class View { constructor() { this.navigationState = { prevPage: { p: undefined, s: undefined, }, prevSubPage: { p: undefined, s: undefined, }, nextPage: { p: undefined, s: undefined, }, nextSubPage: { p: undefined, s: undefined, }, currentPage: { p: undefined, s: undefined, }, }; this.colorArray = { 0: [0, 0, 0], 1: [1, 0, 0], 2: [0, 1, 0], 3: [1, 1, 0], 4: [0, 0, 1], 5: [1, 0, 1], 6: [0, 1, 1], 7: [1, 1, 1], 16: [0, 0, 0], 17: [1, 0, 0], 18: [0, 1, 0], 19: [1, 1, 0], 20: [0, 0, 1], 21: [1, 0, 1], 22: [0, 1, 1], 23: [1, 1, 1], }; } start() { g.clear(); if (this.nextStartPage) { this.show(this.nextStartPage); this.nextStartPage = undefined; } else { if (this.navigationState.currentPage.p) { this.show(this.navigationState.currentPage.p); } else { this.show(101); //load default } } } split_at_fourty(res, value) { res.push(value.substring(0, 40)); if (value.length > 40) { // at least two rows return this.split_at_fourty(res, value.substring(40)); } else { return res; } } // strToUtf8Bytes(str) { // const utf8 = []; // for (let ii = 0; ii < str.length; ii++) { // let charCode = str.charCodeAt(ii); // if (charCode < 0x80) utf8.push(charCode); // else if (charCode < 0x800) { // utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f)); // } else if (charCode < 0xd800 || charCode >= 0xe000) { // utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f)); // } else { // ii++; // // Surrogate pair: // // UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and // // splitting the 20 bits of 0x0-0xFFFFF into two halves // charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff)); // utf8.push( // 0xf0 | (charCode >> 18), // 0x80 | ((charCode >> 12) & 0x3f), // 0x80 | ((charCode >> 6) & 0x3f), // 0x80 | (charCode & 0x3f), // ); // } // } // return utf8; // } loadPrevPage() { if (this.navigationState.prevPage.p) { this.show(this.navigationState.prevPage.p, this.navigationState.prevPage.s); } } loadNextPage() { if (this.navigationState.nextPage.p) { this.show(this.navigationState.nextPage.p, this.navigationState.nextPage.s); } } loadPrevSubPage() { if (this.navigationState.prevSubPage.p) { this.show(this.navigationState.prevSubPage.p, this.navigationState.prevSubPage.s); } } loadNextSubPage() { if (this.navigationState.nextSubPage.p) { this.show(this.navigationState.nextSubPage.p, this.navigationState.nextSubPage.s); } } handleSwipe(lr, ud){ if (lr == -1 && ud == 0) { this.loadNextPage(); } if (lr == 1 && ud == 0) { this.loadPrevPage(); } if (lr == 0 && ud == 1) { this.loadPrevSubPage(); } if (lr == 0 && ud == -1) { this.loadNextSubPage(); } } show(pageId, subPageId) { if(!subPageId){ subPageId = 1; } if (Bangle.http) { Bangle.http('https://teletekst-data.nos.nl/page/' + pageId + '-' + subPageId).then((data) => { const res = data.resp; g.clear(); this.navigationState = { prevPage: { p: undefined, s: undefined, }, prevSubPage: { p: undefined, s: undefined, }, nextPage: { p: undefined, s: undefined, }, nextSubPage: { p: undefined, s: undefined, }, currentPage: { p: pageId, s: subPageId, }, }; // set next -, previous -, next sub - and previous sub page let navNIndex = res.indexOf('pn=n_'); if (navNIndex > -1) { this.navigationState.nextPage.p = parseInt(res.substring(navNIndex + 5, navNIndex + 8)); this.navigationState.nextPage.s = parseInt(res.substring(navNIndex + 9, navNIndex + 10)); } let navPIndex = res.indexOf('pn=p_'); if (navPIndex > -1) { this.navigationState.prevPage.p = parseInt(res.substring(navPIndex + 5, navPIndex + 8)); this.navigationState.prevPage.s = parseInt(res.substring(navPIndex + 9, navPIndex + 10)); } let navPSIndex = res.indexOf('pn=ps'); if (navPSIndex > -1) { this.navigationState.prevSubPage.p = parseInt(res.substring(navPSIndex + 5, navPSIndex + 8)); this.navigationState.prevSubPage.s = parseInt(res.substring(navPSIndex + 9, navPSIndex + 10)); } let navNSIndex = res.indexOf('pn=ns'); if (navNSIndex > -1) { this.navigationState.nextSubPage.p = parseInt(res.substring(navNSIndex + 5, navNSIndex + 8)); this.navigationState.nextSubPage.s = parseInt(res.substring(navNSIndex + 9, navNSIndex + 10)); } let split = E.toString(res.split('
')[1].split('
')[0]); this.render(split); }); } } render(source) { g.setFontAlign(-1, -1); g.setFont('4x6'); const bytes = E.toUint8Array(E.decodeUTF8(source)); let rowIndex = 0; let totalIndex = 0; let charIndex = 0; for (let charByte of bytes) { { if ((charByte >= 0 && charByte <= 7) || (charByte >= 16 && charByte <= 23)) { const color = this.colorArray[charByte]; g.setColor(color[0], color[1], color[2]); } } g.drawString(source[totalIndex], (charIndex * 4) + 6, rowIndex * 7); charIndex++; totalIndex++; if (charIndex == 40) { rowIndex++; charIndex = 0; g.flip(); } } } } const BUTTON_BORDER_WITH = 2; class Button { // position; // value; // highlightTimeoutId; constructor(position, value) { this.position = position; this.value = value; } draw(highlight) { g.setColor(g.theme.fg); g.fillRect( this.position.x1, this.position.y1, this.position.x2, this.position.y2 ); if (highlight) { g.setColor(g.theme.bgH); } else { g.setColor(g.theme.bg); } g.fillRect( this.position.x1 + BUTTON_BORDER_WITH, this.position.y1 + BUTTON_BORDER_WITH, this.position.x2 - BUTTON_BORDER_WITH, this.position.y2 - BUTTON_BORDER_WITH ); g.setColor(g.theme.fg); g.setFontAlign(0, 0); g.setFont("Vector", 35); g.drawString( this.value, this.position.x1 + (this.position.x2 - this.position.x1) / 2 + 2, this.position.y1 + (this.position.y2 - this.position.y1) / 2 + 2 ); } handleTouchInput(n, e) { if ( e.x >= this.position.x1 && e.x <= this.position.x2 && e.y >= this.position.y1 && e.y <= this.position.y2 ) { this.draw(true); // draw to highlight this.highlightTimeoutId = setTimeout(() => { this.draw(); this.highlightTimeoutId = undefined; }, 100); return this.value; } else { return undefined; } } disable() { // disable button if (this.highlightTimeoutId) { clearTimeout(this.highlightTimeoutId); this.highlightTimeoutId = undefined; } } } class Input { constructor(callback) { this.inputCallback = callback; this.inputVal = ""; let button1 = new Button({ x1: 1, y1: 35, x2: 58, y2: 70 }, '1'); let button2 = new Button({ x1: 60, y1: 35, x2: 116, y2: 70 }, '2'); let button3 = new Button({ x1: 118, y1: 35, x2: 174, y2: 70 }, '3'); let button4 = new Button({ x1: 1, y1: 72, x2: 58, y2: 105 }, '4'); let button5 = new Button({ x1: 60, y1: 72, x2: 116, y2: 105 }, '5'); let button6 = new Button({ x1: 118, y1: 72, x2: 174, y2: 105 }, '6'); let button7 = new Button({ x1: 1, y1: 107, x2: 58, y2: 140 }, '7'); let button8 = new Button({ x1: 60, y1: 107, x2: 116, y2: 140 }, '8'); let button9 = new Button({ x1: 118, y1: 107, x2: 174, y2: 140 }, '9'); let buttonOK = new Button({ x1: 1, y1: 142, x2: 58, y2: 174 }, "OK"); let button0 = new Button({ x1: 60, y1: 142, x2: 116, y2: 174 }, "0"); let buttonDelete = new Button({ x1: 118, y1: 142, x2: 174, y2: 174 }, "<-"); this.inputButtons = [ button1, button2, button3, button4, button5, button6, button7, button8, button9, buttonOK, button0, buttonDelete, ]; } handleTouchInput(n, e) { let res = 'none'; for (let button of this.inputButtons) { const touchResult = button.handleTouchInput(n, e); if (touchResult) { res = touchResult; } } switch (res) { case 'OK': if(this.inputVal.length == 3){ this.inputCallback(parseInt(this.inputVal)); } break; case '<-': this.removeNumber(); this.drawField(); break; case 'none': break; default: this.appendNumber(parseInt(res)); this.drawField(); } } hide() { for (let button of this.inputButtons) { button.disable(); } } start(preset) { if (preset) { this.inputVal = preset.toString(); } else { this.inputVal = ''; } this.draw(); } appendNumber(number) { if (number === 0 && this.inputVal.length === 0) { return; } if (this.inputVal.length <= 2) { this.inputVal = this.inputVal + number; } } removeNumber() { if (this.inputVal.length > 0) { this.inputVal = this.inputVal.slice(0, -1); } } reset() { this.inputVal = ""; } draw() { g.clear(); this.drawButtons(); this.drawField(); } drawButtons() { for (let button of this.inputButtons) { button.draw(); } } drawField() { g.clearRect(0, 0, 176, 34); g.setColor(g.theme.fg); g.setFontAlign(-1, -1); g.setFont("Vector:26x40"); g.drawString(this.inputVal, 2, 0); } } // require('./Input'); class NOSTeletekstApp { constructor() { console.log("this is the teletekst app!"); this.isLeaving = false; this.viewMode= 'VIEW'; this.view = new View(); this.input = new Input((newVal)=>this.inputHandler(newVal)); this.view.start(); Bangle.setUI({ mode: "custom", remove: () => { this.isLeaving = true; console.log("teletext app: i am packing my stuff, goodbye"); require("widget_utils").show(); // re-show widgets }, touch: (n, e) => { if (this.viewMode == 'VIEW') { // we need to go to input mode this.setViewMode('INPUT'); return; } if (this.viewMode == 'INPUT') { this.input.handleTouchInput(n, e); return; } }, swipe: (lr, ud) => { if (this.viewMode == 'VIEW') { this.view.handleSwipe(lr,ud); } if (this.viewMode == 'INPUT') { if(lr == 1 && ud == 0){ this.setViewMode('VIEW'); } } } }); } inputHandler(input){ // set viewMode back to view this.view.nextStartPage = input; this.setViewMode('VIEW'); } setViewMode(newViewMode){ this.viewMode = newViewMode; if(newViewMode=='INPUT'){ this.input.start(); } if(newViewMode=='VIEW'){ this.input.hide(); this.view.start(); } } } new NOSTeletekstApp();