Javascript, not working

HI.

So I was going through my first webview tutorial to learn the basics and see how javascript works.

https://appinventor.mit.edu/explore/ai2/webview-javascript

I used the above tutorial and followed it with the current version of AI2.
(home url is defined in design view).
Used “file:///appinventor_asset/javascript.html”

Copied and pasted the Javascript.

The text is being returned without being reversed.
John is returning in the notifier back as John. I was expecting "nhoJ"

//
var result = new Date().toString();
var appInventorInput = window.AppInventor.getWebViewString();
    function processJavascript() {
    if (appInventorInput.length > 0) {
        document.write( "WebView InputString received FROM app:
" + appInventorInput );
        result = appInventorInput.split("").reverse().join("");
        document.write( "<p/>WebView InputString sending BACK TO app:<br/>" +
        result );
    } else {
        document.write( "No WebView InputString set by app at: <br/>" + result 

);
    }
    window.AppInventor.setWebViewString( result );
}
//  Thats the core of the script used

It is an odd script....

You are not calling the function processJavascript.

var result = new Date().toString();
var appInventorInput = window.AppInventor.getWebViewString();

// run the function here
processJavascript();

function processJavascript() {
	if (appInventorInput.length > 0) {
		document.write( "WebView InputString received FROM app:" + appInventorInput );

		result = appInventorInput.split("").reverse().join("");
		document.write( "" + result );
	} else {
		document.write( "No WebView InputString set by app at:" + result );
	}
		window.AppInventor.setWebViewString( result );
	}

This returns:

WebView INputString received FROM app:JohnnhoJ
in the web view

and
nhoJ
as the webviewstring

1 Like

WOW. Updated the script with your recommendation. Its working now and it feels like magic. Wow. Thank you so so much.

I know you are testing javascript, but there is also a reverse string block in the Text palette :wink:

1 Like

So why the tutorial is wrong? https://appinventor.mit.edu/explore/ai2/webview-javascript

It isn't.

All I could see was a part of the html page

I also became interested in the possibilities of javascript. I built an application identical to the tutorial. I copied the script 1: 1. I type in the 1234 box. Unfortunately, after running the script, I get 1234 TIMEOUT. Your script works.
test_java.aia (4.3 KB)

1 Like

Missing: "processJavascript();"

1 Like

Could be the html needed a bit of tidying up after copying, and of course things have changed now we have the webviewstringChange block.

This html works for me:

<!doctype html>
<head>
<title>WebView Javascript Processor</title>
</head>

<body onload="processJavascript();">
<b>This page includes a javascript function that reverses text.</b>
<p>The javascript function incorporates a special App Inventor feature called <i>window.AppInventor.getWebViewString()</i>, which allows App Inventor apps to communicate with the WebViewer component's internal processing of javascript.
<p>This simple example shows how to use the <i>window.AppInventor.getWebViewString()</i> function to pass data to and from the WebViewer component, and thereby an App Inventor app.

<script>
    var result = new Date().toString();
    var appInventorInput = window.AppInventor.getWebViewString();
    function processJavascript() {
        if (appInventorInput.length > 0) {
            document.write( "WebView InputString received FROM app:<br><br>" + appInventorInput );
            result = appInventorInput.split("").reverse().join("");
            document.write( "<p/>WebView InputString sending BACK TO app:<br><br>" + result );
        } else {
            document.write( "No WebView InputString set by app at: <br>" + result );
        }
        window.AppInventor.setWebViewString( result );
    }
</script>

</body>
</html>

with these blocks
blocks (13)

1 Like

That WebView Javascript Processor for App Inventor tutorial is out of date and the block images are fuzzy on my monitor.
It would be short work to post an updated copy to this board with draggable block images, suitable for Yandex cross-translation too given the source .aia file.

Does this part still hold, given the file system changes for recent Android versions?

This block sets the WebViewer1.HomeUrl property to either file:///mnt/sdcard/AppInventor/assets/javascriptWebViewProcessor.html (when debugMode is TRUE), or file:///android_asset/javascriptWebViewProcessor.html (when debugMode is FALSE).

1 Like

It works for me on Android 9, after compiling the APK.
file: ///android_asset/javascriptWebViewProcessor.html

1 Like

Could this utf8 script be rewritten to work with appinventor? I would like appinvent to send a letter to it and receive Utf8 and vice versa. I look at it and think but my knowledge is too little to know where to enter "window.AppInventor.getWebViewString ()" and "window.AppInventor.setWebViewString ()"

<textarea ></textarea>

<textarea ></textarea>
<script>


(function(n){var g=typeof exports=="object"&&exports;
var t=typeof module=="object"&&module&&module.exports==g&&module;
var m=typeof global=="object"&&global;
if(m.global===m||m.window===m){n=m}var l=String.fromCharCode;
function f(x){var w=[];
var v=0;
var y=x.length;
var z;
var u;
while(v<y){z=x.charCodeAt(v++);
if(z>=55296&&z<=56319&&v<y){u=x.charCodeAt(v++);
if((u&64512)==56320){w.push(((z&1023)<<10)+(u&1023)+65536)}else{w.push(z);
v--}}else{w.push(z)}}return w}function q(y){var w=y.length;
var v=-1;
var x;
var u="";
while(++v<w){x=y[v];
if(x>65535){x-=65536;u+=l(x>>>10&1023|55296);
x=56320|x&1023}u+=l(x)}return u}function e(v,u){return l(((v>>u)&63)|128)}function p(u){if((u&4294967168)==0){return l(u)}var v="";
if((u&4294965248)==0){v=l(((u>>6)&31)|192)}else{if((u&4294901760)==0){v=l(((u>>12)&15)|224);
v+=e(u,6)}else{if((u&4292870144)==0){v=l(((u>>18)&7)|240);
v+=e(u,12);
v+=e(u,6)}}}v+=l((u&63)|128);
return v}function o(x){var w=f(x);
var y=w.length;
var v=-1;
var u;
var z="";
while(++v<y){u=w[v];
z+=p(u)}return z}function a(){if(j>=i){throw Error("Invalid byte index")}var u=k[j]&255;
j++;
if((u&192)==128){return u&63}throw Error("Invalid continuation byte")}function c(){var v;
var u;
var y;
var x;
var w;
if(j>i){throw Error("Invalid byte index")}if(j==i){return false}v=k[j]&255;
j++;
if((v&128)==0){return v}if((v&224)==192){var u=a();
w=((v&31)<<6)|u;
if(w>=128){return w}else{throw Error("Invalid continuation byte")}}if((v&240)==224){u=a();
y=a();
w=((v&15)<<12)|(u<<6)|y;
if(w>=2048){return w}else{throw Error("Invalid continuation byte")}}if((v&248)==240){u=a();
y=a();
x=a();
w=((v&15)<<18)|(u<<12)|(y<<6)|x;if(w>=65536&&w<=1114111){return w}}throw Error("Invalid UTF-8 detected")}var k;
var i;
var j;
function b(w){k=f(w);i=k.length;
j=0;
var u=[];
var v;
while((v=c())!==false){u.push(v)}return q(u)}var d={version:"2.0.0",encode:o,decode:b};
if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return d})}else{if(g&&!g.nodeType){if(t){t.exports=d}else{var r={};
var h=r.hasOwnProperty;for(var s in d){h.call(d,s)&&(g[s]=d[s])}}}else{n.utf8=d}}}(this));

(function(window,document){var textareas=document.getElementsByTagName("textarea"),decoded=textareas[0],encoded=textareas[1],permalink=document.getElementById("permalink"),storage=(function(){var uid=new Date,storage,result;
try{(storage=window.localStorage).setItem(uid,uid);
result=storage.getItem(uid)==uid;
storage.removeItem(uid);
return result&&storage}catch(e){}}()),stringFromCharCode=String.fromCharCode;
function encode(string){return encodeURIComponent(string).replace(/['()_*]/g,function(character){return"%"+character.charCodeAt().toString(16)})}function hexEscape(string){var length=string.length;var index=-1;
var result="";
var hex;
while(++index<length){hex=string.charCodeAt(index).toString(16).toUpperCase();
result+="\\x"+("00"+hex).slice(-2)}return result}function update(){var shouldDecode=this==encoded;
var value;
if(shouldDecode){try{value=utf8.decode(eval("'"+encoded.value+"'"));
decoded.value=value;
decoded.className=encoded.className=""}catch(e){decoded.value="ERROR: invalid input";
decoded.className=encoded.className="invalid"}}else{value=utf8.encode(decoded.value);
encoded.value=hexEscape(value);decoded.className=encoded.className=""}value=decoded.value;
permalink.hash=encode(value);
storage&&(storage.utf8=value)}decoded.onkeyup=encoded.onkeyup=update;
decoded.oninput=encoded.oninput=function(){decoded.onkeyup=encoded.onkeyup=null;update.call(this)};
if(storage){storage.utf8&&(decoded.value=storage.utf8);
update()}window.onhashchange=function(){decoded.value=decodeURIComponent(location.hash.slice(1))
;update()};
if(location.hash){window.onhashchange()}}(this,document));
window._gaq=[["_setAccount","UA-6065217-60"],["_trackPageview"]];
(function(e,a){var c=e.createElement(a),b=e.getElementsByTagName(a)[0];
c.src="//www.google-analytics.com/ga.js";
b.parentNode.insertBefore(c,b)}(document,"script"))
</script>
1 Like

HI. :slight_smile: I had the same question in mind.
file: ///android_asset/javascriptWebViewProcessor.html works for both debug and compiled modes.

Are you sure it worked in debug mode - it shouldn't.

Anyway, now, since the last AI2 update, you can use http://localhost/file.html for both.

Have to have a look at this and find the inputs and outputs.....

@Patryk_F

Does this do it for you ?

1 Like

(edit finished, linked to from FAQs on Javascript and Webview. - ABG...)

WebView Javascript Processor for App Inventor

This tutorial was originally written by Rich Interdonato, MIT Master Trainer and posted to https://appinventor.mit.edu/explore/ai2/webview-javascript
This revision uses the newer WebViewer.WebViewStringChange event and new (November 2020) Media file addressing for the webViewer component for simplicity. - ABG

App Inventor allows users to write powerful programs using blocks instead of traditional programming languages. It also allows advanced users to include traditional Java code in their apps by means of its new Extensions feature. What you might not know is that there is another, even older way for you to incorporate traditional programming into your apps. The technique involves using the WebViewer component as a javascript processor, and this blog post will show you how it can be done using a simple example. In it, an HTML file is sent input text from an App Inventor program, it reverses the text using javascript, and then it sends the result back to the App Inventor program as output that the app can use (show to the user in a Message Dialog). Our example requires an HTML file that includes javascript that can reverse text. The file is included below, and the javascript command that reverses the text is highlighted in yellow. The javascript in this file is like the other javascript you might find on the Web, except in one way. It includes two special App Inventor Only functions, window.AppInventor.getWebViewString() and window.AppInventor.setWebViewString(). It is these two, App Inventor Only functions that allow your apps to communicate with the javascript that runs inside the WebViewer component. By using them creatively, you can leverage the vast number of javascript programs that are freely available on the Web, and some of them can be really useful. For now, lets keep things simple and create a text file called javascriptWebViewProcessor.html. The complete text of the file follows:

HTML file (included as media/asset: javascriptWebViewProcessor.html)

<!doctype html>
 <head> <title>WebView Javascript Processor</title> </head>
 <body onload="processJavascript();"> 
<b>This page includes a javascript function that reverses text.</b> 
<p>The javascript function incorporates a special App Inventor feature called 
 <i>window.AppInventor.getWebViewString()</i>, 
which allows App Inventor apps to communicate with 
the WebViewer component's internal processing of javascript. 
<p>This simple example shows how to use the 
<i>window.AppInventor.getWebViewString()</i>
 function to pass data to and from the WebViewer component, 
and thereby an App Inventor app. 
<script> var result = new Date().toString();
 var appInventorInput = window.AppInventor.getWebViewString();
 function processJavascript() {
  if (appInventorInput.length > 0) { 
    document.write( "WebView InputString received FROM app: " + appInventorInput ); 
    result = appInventorInput.split("").reverse().join(""); 
   document.write( "<p/>WebView InputString sending BACK TO app:<br/>" + result ); } 
 else {
  document.write( "No WebView InputString set by app at: <br/>" + result ); } 
 window.AppInventor.setWebViewString( result ); }
 </script> 
</body> 
</html>

javascriptWebViewProcessor.html.txt (1.2 KB) (drop the trailing .txt after downloading this and before uploading it to the Media folder. I had to add the .txt file name suffix to satisfy this board's file naming restrictions - ABG)

Once the javascriptWebViewProcessor.html has been created, make a new App Inventor app called SimpleWebviewerJavascriptProcessor, and upload the HTML file as an app media asset.

Designer

Designer Components

Add a HorizontalArrangement with a:

  • TextBox named StringToBeProcessedByJavascriptFile
  • Button named btnProcess

Also, using the default names and properties for each, add a:

  • WebViewer component
  • Notifier component

Blocks Overview

As you can see, the blocks for this app are quite simple. There are no variables and 2 event handlers. Each will be explained in individual sections.

Variables

With the availability of the WebViewer.WebViewStringChange event, there is no need for a Clock to poll for a change in the WebViewString value, or for variables to track the elapsed time. In the interest of simplicity, I have removed the Clock entirely, at the expense of removing the timeout capability of the original tutorial.

If you want timeout detection, add a Clock, set it to disabled and nonrepeating, and name it Timeout Clock, with duration equal to your patience in milliseconds. Have that Clock.Timer announce the timeout.

Also missing from this tutorial is the global debugMode variable, which was needed to address the html file in both the Companion and in Built apps. (Thanks to @TimAI2 for the new URI)

Initialization

This version leaves out the Screen1.Initialize block, with its former initialization of WebViewer1.HomeURL, deferring that to the btnProcess Click event. I did this because I noticed the WebViewer1.WebViewStringChange event firing at app startup, and wanted an easy way to avoid that.

when btnProcess.Click


This block sets the WebViewer1.HomeUrl property to http://localhost/javascriptWebViewProcessor.html
The javascriptWebViewProcessor.html file processing begins once the user activates the app. This activation is captured by the btnProcess.Click event handler block, which sets the WebViewer1.WebViewString component to the value of the StringToBeProcessedByJavascriptFile.Text . Then the WebViewer1.GoHome block is called, which causes the WebViewer to load the javascriptWebViewProcessor.html page, thereby starting the javascript processing.

when WebViewer1.WebViewStringChange

When the result is available from the WebViewer, the when WebViewer1.WebViewStringChange event will fire, to display a message to the user in a Message Dialog that includes the result of the javascript processing. The output of the processing is available to the app in the WebViewer1.WebViewString property, which is set inside the javascript processing file. Inside the event block, there is a variable value available with that value, for convenience.
The app will reset the StringToBeProcessedByJavascriptFile.Text to an empty string () to be ready for the next input

Test Runs

radar

Conclusion

In this article, we have explored how the WebViewer component can be used to process javascript in your App Inventor apps. Our example was simple, but it demonstrated the fact that App Inventor can communicate with running javascript, and this provides you with a foundation for more advanced and useful javascript processing. There is a LOT of javascript code available on the web, and some of it can be used immediately by you to implement advanced features in your apps. In my next blog post, I will explore an advanced application of WebView javascript processing that you can use right away to freely make your apps much more secure. Until then, keep inventing!

3 Likes

I have a script that almost works. In this version, I substitute characters in the script:

    <!DOCTYPE html>
    <html>
    <body>
        <script>
        var outapp;
        var inapp = '\xc4\x85';
        var stringFromCharCode = String.fromCharCode;

    utf8decode();

    	// Taken from https://mths.be/punycode
    	function ucs2decode(string) {
    		var output = [];
    		var counter = 0;
    		var length = string.length;
    		var value;
    		var extra;
    		while (counter < length) {
    			value = string.charCodeAt(counter++);
    			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
    				// high surrogate, and there is a next character
    				extra = string.charCodeAt(counter++);
    				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
    					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
    				} else {
    					// unmatched surrogate; only append this code unit, in case the next
    					// code unit is the high surrogate of a surrogate pair
    					output.push(value);
    					counter--;
    				}
    			} else {
    				output.push(value);
    			}
    		}
    		return output;
    	}

    	// Taken from https://mths.be/punycode
    	function ucs2encode(array) {
    		var length = array.length;
    		var index = -1;
    		var value;
    		var output = '';
    		while (++index < length) {
    			value = array[index];
    			if (value > 0xFFFF) {
    				value -= 0x10000;
    				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
    				value = 0xDC00 | value & 0x3FF;
    			}
    			output += stringFromCharCode(value);
    		}
    		return output;
    	}

    	function checkScalarValue(codePoint) {
    		if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
    			throw Error(
    				'Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
    				' is not a scalar value'
    			);
    		}
    	}
    	/*--------------------------------------------------------------------------*/

    	function createByte(codePoint, shift) {
    		return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
    	}

    	function encodeCodePoint(codePoint) {
    		if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
    			return stringFromCharCode(codePoint);
    		}
    		var symbol = '';
    		if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
    			symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
    		}
    		else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
    			checkScalarValue(codePoint);
    			symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
    			symbol += createByte(codePoint, 6);
    		}
    		else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
    			symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
    			symbol += createByte(codePoint, 12);
    			symbol += createByte(codePoint, 6);
    		}
    		symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
    		return symbol;
    	}

    	function utf8encode(string) {
    		var codePoints = ucs2decode(string);
    		var length = codePoints.length;
    		var index = -1;
    		var codePoint;
    		var byteString = '';
    		while (++index < length) {
    			codePoint = codePoints[index];
    			byteString += encodeCodePoint(codePoint);
    		}
    		return byteString;
    	}

    	/*--------------------------------------------------------------------------*/

    	function readContinuationByte() {
    		if (byteIndex >= byteCount) {
    			throw Error('Invalid byte index');
    		}

    		var continuationByte = byteArray[byteIndex] & 0xFF;
    		byteIndex++;

    		if ((continuationByte & 0xC0) == 0x80) {
    			return continuationByte & 0x3F;
    		}

    		// If we end up here, it’s not a continuation byte
    		throw Error('Invalid continuation byte');
    	}

    	function decodeSymbol() {
    		var byte1;
    		var byte2;
    		var byte3;
    		var byte4;
    		var codePoint;

    		if (byteIndex > byteCount) {
    			throw Error('Invalid byte index');
    		}

    		if (byteIndex == byteCount) {
    			return false;
    		}

    		// Read first byte
    		byte1 = byteArray[byteIndex] & 0xFF;
    		byteIndex++;

    		// 1-byte sequence (no continuation bytes)
    		if ((byte1 & 0x80) == 0) {
    			return byte1;
    		}

    		// 2-byte sequence
    		if ((byte1 & 0xE0) == 0xC0) {
    			byte2 = readContinuationByte();
    			codePoint = ((byte1 & 0x1F) << 6) | byte2;
    			if (codePoint >= 0x80) {
    				return codePoint;
    			} else {
    				throw Error('Invalid continuation byte');
    			}
    		}

    		// 3-byte sequence (may include unpaired surrogates)
    		if ((byte1 & 0xF0) == 0xE0) {
    			byte2 = readContinuationByte();
    			byte3 = readContinuationByte();
    			codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
    			if (codePoint >= 0x0800) {
    				checkScalarValue(codePoint);
    				return codePoint;
    			} else {
    				throw Error('Invalid continuation byte');
    			}
    		}

    		// 4-byte sequence
    		if ((byte1 & 0xF8) == 0xF0) {
    			byte2 = readContinuationByte();
    			byte3 = readContinuationByte();
    			byte4 = readContinuationByte();
    			codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) |
    				(byte3 << 0x06) | byte4;
    			if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
    				return codePoint;
    			}
    		}

    		throw Error('Invalid UTF-8 detected');
    	}

    	var byteArray;
    	var byteCount;
    	var byteIndex;
    	function utf8decode() {
    		byteArray = ucs2decode(inapp);
    		byteCount = byteArray.length;
    		byteIndex = 0;
    		var codePoints = [];
    		var tmp;
    		while ((tmp = decodeSymbol()) !== false) {
    			codePoints.push(tmp);
    		}
                    outapp = ucs2encode(codePoints);
                   
                    document.write(outapp);
                    
    		

    }

        </script>
    </body>
    </html>

After running this script in the application, I get the converted letter "ą" in the webview field. If I add to the script "window.AppInventor.setWebViewString (outapp);" I am also getting the correct letter "ą" in the "WebViewString" block.

The problem appears when I add "window.AppInventor.getWebViewString ();" to the script:

<!DOCTYPE html>
<html>
<body>
    <script>
    var outapp;
    var inapp = window.AppInventor.getWebViewString();
    var stringFromCharCode = String.fromCharCode;

utf8decode();

	// Taken from https://mths.be/punycode
	function ucs2decode(string) {
		var output = [];
		var counter = 0;
		var length = string.length;
		var value;
		var extra;
		while (counter < length) {
			value = string.charCodeAt(counter++);
			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
				// high surrogate, and there is a next character
				extra = string.charCodeAt(counter++);
				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
				} else {
					// unmatched surrogate; only append this code unit, in case the next
					// code unit is the high surrogate of a surrogate pair
					output.push(value);
					counter--;
				}
			} else {
				output.push(value);
			}
		}
		return output;
	}

	// Taken from https://mths.be/punycode
	function ucs2encode(array) {
		var length = array.length;
		var index = -1;
		var value;
		var output = '';
		while (++index < length) {
			value = array[index];
			if (value > 0xFFFF) {
				value -= 0x10000;
				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
				value = 0xDC00 | value & 0x3FF;
			}
			output += stringFromCharCode(value);
		}
		return output;
	}

	function checkScalarValue(codePoint) {
		if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
			throw Error(
				'Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
				' is not a scalar value'
			);
		}
	}
	/*--------------------------------------------------------------------------*/

	function createByte(codePoint, shift) {
		return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
	}

	function encodeCodePoint(codePoint) {
		if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
			return stringFromCharCode(codePoint);
		}
		var symbol = '';
		if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
			symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
		}
		else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
			checkScalarValue(codePoint);
			symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
			symbol += createByte(codePoint, 6);
		}
		else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
			symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
			symbol += createByte(codePoint, 12);
			symbol += createByte(codePoint, 6);
		}
		symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
		return symbol;
	}

	function utf8encode(string) {
		var codePoints = ucs2decode(string);
		var length = codePoints.length;
		var index = -1;
		var codePoint;
		var byteString = '';
		while (++index < length) {
			codePoint = codePoints[index];
			byteString += encodeCodePoint(codePoint);
		}
		return byteString;
	}

	/*--------------------------------------------------------------------------*/

	function readContinuationByte() {
		if (byteIndex >= byteCount) {
			throw Error('Invalid byte index');
		}

		var continuationByte = byteArray[byteIndex] & 0xFF;
		byteIndex++;

		if ((continuationByte & 0xC0) == 0x80) {
			return continuationByte & 0x3F;
		}

		// If we end up here, it’s not a continuation byte
		throw Error('Invalid continuation byte');
	}

	function decodeSymbol() {
		var byte1;
		var byte2;
		var byte3;
		var byte4;
		var codePoint;

		if (byteIndex > byteCount) {
			throw Error('Invalid byte index');
		}

		if (byteIndex == byteCount) {
			return false;
		}

		// Read first byte
		byte1 = byteArray[byteIndex] & 0xFF;
		byteIndex++;

		// 1-byte sequence (no continuation bytes)
		if ((byte1 & 0x80) == 0) {
			return byte1;
		}

		// 2-byte sequence
		if ((byte1 & 0xE0) == 0xC0) {
			byte2 = readContinuationByte();
			codePoint = ((byte1 & 0x1F) << 6) | byte2;
			if (codePoint >= 0x80) {
				return codePoint;
			} else {
				throw Error('Invalid continuation byte');
			}
		}

		// 3-byte sequence (may include unpaired surrogates)
		if ((byte1 & 0xF0) == 0xE0) {
			byte2 = readContinuationByte();
			byte3 = readContinuationByte();
			codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
			if (codePoint >= 0x0800) {
				checkScalarValue(codePoint);
				return codePoint;
			} else {
				throw Error('Invalid continuation byte');
			}
		}

		// 4-byte sequence
		if ((byte1 & 0xF8) == 0xF0) {
			byte2 = readContinuationByte();
			byte3 = readContinuationByte();
			byte4 = readContinuationByte();
			codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) |
				(byte3 << 0x06) | byte4;
			if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
				return codePoint;
			}
		}

		throw Error('Invalid UTF-8 detected');
	}

	var byteArray;
	var byteCount;
	var byteIndex;
	function utf8decode() {
		byteArray = ucs2decode(inapp);
		byteCount = byteArray.length;
		byteIndex = 0;
		var codePoints = [];
		var tmp;
		while ((tmp = decodeSymbol()) !== false) {
			codePoints.push(tmp);
		}
                outapp = ucs2encode(codePoints);
               
                document.write(outapp);
                
		

}

    </script>
</body>
</html>

It passes the text "\ xc4 \ x85" to the "WebViewString" block, ie the same that was entered in the script in the first case. Unfortunately, there are already problems here. After executing the script, I get in the "webview" field the same as I sent to the "WebViewString component, it doesn't convert utf8 to letter. Why can this happen?

where do not set the WebViewString after conversion?
Taifun

I did not understand the question. My blocks look like this:

blocks (50)

I am not using "window.AppInventor.setWebViewString ()" in the script. I display the result of the script in the application using "document.write (outapp);". The data "\xc4\x85" passes to the "webviewstring" block from the text field.

It seems to me the problem is with passing a variable to "webviewstring". Is not shipped what I would expect?

You can test this script here:
https://www.w3schools.com/code/tryit.asp?filename=GKGAU8DBB4GX

in your JavaScript you have to set the WebViewString after the conversion to pass it back to App Inventor, see also How does the property Webviewer.WebViewString work?

Taifun