mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-04-27 22:49:53 +08:00
Fix script fetch binary response (#407)
This commit is contained in:
66
assets/js/fetch.js
Normal file
66
assets/js/fetch.js
Normal file
@@ -0,0 +1,66 @@
|
||||
function fetch(url, options) {
|
||||
options = options || {};
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
const keys = [];
|
||||
const all = [];
|
||||
const headers = {};
|
||||
|
||||
const response = () => ({
|
||||
ok: (request.status / 100 | 0) === 2, // 200-299
|
||||
statusText: request.statusText,
|
||||
status: request.status,
|
||||
url: request.responseURL,
|
||||
body: request.response.body,
|
||||
text: () => Promise.resolve(request.responseText),
|
||||
json: () => {
|
||||
// TODO: review this handle because it may discard \n from json attributes
|
||||
try {
|
||||
// console.log('RESPONSE TEXT IN FETCH: ' + request.responseText);
|
||||
return Promise.resolve(JSON.parse(request.responseText));
|
||||
} catch (e) {
|
||||
// console.log('ERROR on fetch parsing JSON: ' + e.message);
|
||||
return Promise.resolve(request.responseText);
|
||||
}
|
||||
},
|
||||
|
||||
blob: () => Promise.resolve(request.response.body),
|
||||
clone: response,
|
||||
headers: {
|
||||
keys: () => keys,
|
||||
entries: () => all,
|
||||
get: n => headers[n.toLowerCase()],
|
||||
has: n => n.toLowerCase() in headers
|
||||
}
|
||||
});
|
||||
|
||||
request.open(options.method || 'get', url, true);
|
||||
|
||||
request.onload = () => {
|
||||
request.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, (m, key, value) => {
|
||||
keys.push(key = key.toLowerCase());
|
||||
all.push([key, value]);
|
||||
headers[key] = headers[key] ? `${headers[key]},${value}` : value;
|
||||
});
|
||||
resolve(response());
|
||||
};
|
||||
|
||||
request.onerror = reject;
|
||||
|
||||
request.withCredentials = options.credentials == 'include';
|
||||
|
||||
if (options.headers) {
|
||||
if (options.headers.constructor.name == 'Object') {
|
||||
for (const i in options.headers) {
|
||||
request.setRequestHeader(i, options.headers[i]);
|
||||
}
|
||||
} else { // if it is some Headers pollyfill, the way to iterate is through for of
|
||||
for (const header of options.headers) {
|
||||
request.setRequestHeader(header[0], header[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request.send(options.body || null);
|
||||
});
|
||||
}
|
||||
437
lib/network/components/js/xhr.dart
Normal file
437
lib/network/components/js/xhr.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_js/javascript_runtime.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:proxypin/network/util/file_read.dart';
|
||||
import 'package:proxypin/network/util/logger.dart';
|
||||
|
||||
/*
|
||||
* Based on bits and pieces from different OSS sources
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
var _XHR_DEBUG = false;
|
||||
|
||||
setXhrDebug(bool value) => _XHR_DEBUG = value;
|
||||
|
||||
const HTTP_GET = "get";
|
||||
const HTTP_POST = "post";
|
||||
const HTTP_PATCH = "patch";
|
||||
const HTTP_DELETE = "delete";
|
||||
const HTTP_PUT = "put";
|
||||
const HTTP_HEAD = "head";
|
||||
|
||||
enum HttpMethod { put, get, post, delete, patch, head }
|
||||
|
||||
String _debugSendNativeCallback() {
|
||||
if (_XHR_DEBUG) {
|
||||
return """console.log("XMLHttpRequest._send_native_callback");
|
||||
console.log("arguments");
|
||||
console.log(arguments);
|
||||
console.log(responseInfo);
|
||||
console.log(responseText);
|
||||
console.log(error);""";
|
||||
} else
|
||||
return "";
|
||||
}
|
||||
|
||||
final String xhrJsCode = """
|
||||
function XMLHttpRequest() {
|
||||
this._send_native = XMLHttpRequestExtension_send_native;
|
||||
this._httpMethod = null;
|
||||
this._url = null;
|
||||
this._requestHeaders = [];
|
||||
this._responseHeaders = [];
|
||||
this.response = null;
|
||||
this.responseText = null;
|
||||
this.responseXML = null;
|
||||
this.onreadystatechange = null;
|
||||
this.onloadstart = null;
|
||||
this.onprogress = null;
|
||||
this.onabort = null;
|
||||
this.onerror = null;
|
||||
this.onload = null;
|
||||
this.onloadend = null;
|
||||
this.ontimeout = null;
|
||||
this.readyState = 0;
|
||||
this.status = 0;
|
||||
this.statusText = "";
|
||||
this.withCredentials = null;
|
||||
};
|
||||
// readystate enum
|
||||
XMLHttpRequest.UNSENT = 0;
|
||||
XMLHttpRequest.OPENED = 1;
|
||||
XMLHttpRequest.HEADERS = 2;
|
||||
XMLHttpRequest.LOADING = 3;
|
||||
XMLHttpRequest.DONE = 4;
|
||||
XMLHttpRequest.prototype.constructor = XMLHttpRequest;
|
||||
XMLHttpRequest.prototype.open = function(httpMethod, url) {
|
||||
this._httpMethod = httpMethod;
|
||||
this._url = url;
|
||||
this.readyState = XMLHttpRequest.OPENED;
|
||||
if (typeof this.onreadystatechange === "function") {
|
||||
//console.log("Calling onreadystatechange(OPENED)...");
|
||||
this.onreadystatechange();
|
||||
}
|
||||
};
|
||||
XMLHttpRequest.prototype.send = function(data) {
|
||||
this.readyState = XMLHttpRequest.LOADING;
|
||||
if (typeof this.onreadystatechange === "function") {
|
||||
//console.log("Calling onreadystatechange(LOADING)...");
|
||||
this.onreadystatechange();
|
||||
}
|
||||
if (typeof this.onloadstart === "function") {
|
||||
//console.log("Calling onloadstart()...");
|
||||
this.onloadstart();
|
||||
}
|
||||
var that = this;
|
||||
this._send_native(this._httpMethod, this._url, this._requestHeaders, data || null, function(responseInfo, responseText, error) {
|
||||
that._send_native_callback(responseInfo, responseText, error);
|
||||
}, this);
|
||||
};
|
||||
XMLHttpRequest.prototype.abort = function() {
|
||||
this.readyState = XMLHttpRequest.UNSENT;
|
||||
// Note: this.onreadystatechange() is not supposed to be called according to the XHR specs
|
||||
}
|
||||
// responseInfo: {statusCode, statusText, responseHeaders}
|
||||
XMLHttpRequest.prototype._send_native_callback = function(responseInfo, responseText, error) {
|
||||
${_debugSendNativeCallback()}
|
||||
if (this.readyState === XMLHttpRequest.UNSENT) {
|
||||
console.log("XHR native callback ignored because the request has been aborted");
|
||||
if (typeof this.onabort === "function") {
|
||||
//console.log("Calling onabort()...");
|
||||
this.onabort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.readyState != XMLHttpRequest.LOADING) {
|
||||
// Request was not expected
|
||||
console.log("XHR native callback ignored because the current state is not LOADING");
|
||||
return;
|
||||
}
|
||||
// Response info
|
||||
// TODO: responseXML?
|
||||
this.responseURL = this._url;
|
||||
this.status = responseInfo.statusCode;
|
||||
this.statusText = responseInfo.statusText;
|
||||
this.responseBody = responseInfo.body;
|
||||
this._responseHeaders = responseInfo.responseHeaders || [];
|
||||
this.readyState = XMLHttpRequest.DONE;
|
||||
// Response
|
||||
this.response = null;
|
||||
this.responseText = null;
|
||||
this.responseXML = null;
|
||||
if (error) {
|
||||
this.responseText = error;
|
||||
} else {
|
||||
this.responseText = responseText;
|
||||
this.response = {
|
||||
body: responseInfo.body,
|
||||
}
|
||||
// console.log('RESPONSE TEXT: ' + responseText);
|
||||
}
|
||||
this.readyState = XMLHttpRequest.DONE;
|
||||
if (typeof this.onreadystatechange === "function") {
|
||||
//console.log("Calling onreadystatechange(DONE)...");
|
||||
this.onreadystatechange();
|
||||
}
|
||||
if (error === "timeout") {
|
||||
// Timeout
|
||||
console.warn("Got XHR timeout");
|
||||
if (typeof this.ontimeout === "function") {
|
||||
//console.log("Calling ontimeout()...");
|
||||
this.ontimeout();
|
||||
}
|
||||
} else if (error) {
|
||||
// Error
|
||||
console.warn("Got XHR error:", error);
|
||||
if (typeof this.onerror === "function") {
|
||||
//console.log("Calling onerror()...");
|
||||
this.onerror();
|
||||
}
|
||||
} else {
|
||||
// Success
|
||||
//console.log("XHR success: response =", this.response);
|
||||
if (typeof this.onload === "function") {
|
||||
//console.log("Calling onload()...");
|
||||
this.onload();
|
||||
}
|
||||
}
|
||||
if (typeof this.onloadend === "function") {
|
||||
//console.log("Calling onloadend()...");
|
||||
this.onloadend();
|
||||
}
|
||||
};
|
||||
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
|
||||
this._requestHeaders.push([header, value]);
|
||||
};
|
||||
XMLHttpRequest.prototype.getAllResponseHeaders = function() {
|
||||
var ret = "";
|
||||
for (var i = 0; i < this._responseHeaders.length; i++) {
|
||||
var keyValue = this._responseHeaders[i];
|
||||
ret += keyValue[0] + ": " + keyValue[1] + "\\r\\n";
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
XMLHttpRequest.prototype.getResponseHeader = function(name) {
|
||||
var ret = "";
|
||||
for (var i = 0; i < this._responseHeaders.length; i++) {
|
||||
var keyValue = this._responseHeaders[i];
|
||||
if (keyValue[0] !== name) continue;
|
||||
if (ret === "") ret += ", ";
|
||||
ret += keyValue[1];
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
// XMLHttpRequest.prototype.overrideMimeType = function() {
|
||||
// // TODO
|
||||
// };
|
||||
this.XMLHttpRequest = XMLHttpRequest;""";
|
||||
|
||||
RegExp regexpHeader = RegExp("^([\\w-])+:(?!\\s*\$).+\$");
|
||||
|
||||
class XhrPendingCall {
|
||||
int? idRequest;
|
||||
String? method;
|
||||
String? url;
|
||||
Map<String, String> headers;
|
||||
String? body;
|
||||
|
||||
XhrPendingCall({
|
||||
required this.idRequest,
|
||||
required this.method,
|
||||
required this.url,
|
||||
required this.headers,
|
||||
required this.body,
|
||||
});
|
||||
}
|
||||
|
||||
const XHR_PENDING_CALLS_KEY = "xhrPendingCalls";
|
||||
|
||||
http.Client? httpClient;
|
||||
|
||||
xhrSetHttpClient(http.Client client) {
|
||||
httpClient = client;
|
||||
}
|
||||
|
||||
extension JavascriptRuntimeXhrExtension on JavascriptRuntime {
|
||||
List<dynamic>? getPendingXhrCalls() {
|
||||
return dartContext[XHR_PENDING_CALLS_KEY];
|
||||
}
|
||||
|
||||
bool hasPendingXhrCalls() => getPendingXhrCalls()!.length > 0;
|
||||
|
||||
void clearXhrPendingCalls() {
|
||||
dartContext[XHR_PENDING_CALLS_KEY] = [];
|
||||
}
|
||||
|
||||
Future<void> enableFetch2() async {
|
||||
enableXhr2();
|
||||
final fetchPolyfill = await FileRead.readAsString('assets/js/fetch.js');
|
||||
final evalFetchResult = evaluate(fetchPolyfill);
|
||||
logger.d('Eval Fetch Result: $evalFetchResult');
|
||||
}
|
||||
|
||||
|
||||
JavascriptRuntime enableXhr2() {
|
||||
httpClient = httpClient ?? http.Client();
|
||||
dartContext[XHR_PENDING_CALLS_KEY] = [];
|
||||
|
||||
Timer.periodic(Duration(milliseconds: 40), (timer) {
|
||||
// exits if there is no pending call to remote
|
||||
if (!hasPendingXhrCalls()) return;
|
||||
|
||||
// collect the pending calls into a local variable making copies
|
||||
List<dynamic> pendingCalls = List<dynamic>.from(getPendingXhrCalls()!);
|
||||
// clear the global pending calls list
|
||||
clearXhrPendingCalls();
|
||||
|
||||
// for each pending call, calls the remote http service
|
||||
pendingCalls.forEach((element) async {
|
||||
XhrPendingCall pendingCall = element as XhrPendingCall;
|
||||
HttpMethod eMethod = HttpMethod.values
|
||||
.firstWhere((e) => e.toString().toLowerCase() == ("HttpMethod.${pendingCall.method}".toLowerCase()));
|
||||
late http.Response response;
|
||||
switch (eMethod) {
|
||||
case HttpMethod.head:
|
||||
response = await httpClient!.head(
|
||||
Uri.parse(pendingCall.url!),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
case HttpMethod.get:
|
||||
response = await httpClient!.get(
|
||||
Uri.parse(pendingCall.url!),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
case HttpMethod.post:
|
||||
response = await httpClient!.post(
|
||||
Uri.parse(pendingCall.url!),
|
||||
body: (pendingCall.body is String) ? pendingCall.body : jsonEncode(pendingCall.body),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
case HttpMethod.put:
|
||||
response = await httpClient!.put(
|
||||
Uri.parse(pendingCall.url!),
|
||||
body: (pendingCall.body is String) ? pendingCall.body : jsonEncode(pendingCall.body),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
case HttpMethod.patch:
|
||||
response = await httpClient!.patch(
|
||||
Uri.parse(pendingCall.url!),
|
||||
body: (pendingCall.body is String) ? pendingCall.body : jsonEncode(pendingCall.body),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
case HttpMethod.delete:
|
||||
response = await httpClient!.delete(
|
||||
Uri.parse(pendingCall.url!),
|
||||
headers: pendingCall.headers,
|
||||
);
|
||||
break;
|
||||
}
|
||||
// assuming request was successfully executed
|
||||
String? responseText;
|
||||
List<int>? body;
|
||||
try {
|
||||
responseText = utf8.decode(response.bodyBytes);
|
||||
responseText = jsonEncode(json.decode(responseText));
|
||||
} on Exception {
|
||||
// responseText = response.body;
|
||||
body = response.bodyBytes;
|
||||
}
|
||||
|
||||
// logger.d('RESPONSE TEXT: $responseText');
|
||||
final xhrResult = XmlHttpRequestResponse(
|
||||
responseText: responseText,
|
||||
responseInfo: XhtmlHttpResponseInfo(statusCode: 200, statusText: "OK", body: body),
|
||||
);
|
||||
|
||||
final responseInfo = jsonEncode(xhrResult.responseInfo);
|
||||
//final responseText = xhrResult.responseText; //.replaceAll("\\n", "\\\n");
|
||||
final error = xhrResult.error;
|
||||
// send back to the javascript environment the
|
||||
// response for the http pending callback
|
||||
this.evaluate(
|
||||
"globalThis.xhrRequests[${pendingCall.idRequest}].callback($responseInfo, `$responseText`, $error);",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
this.evaluate("""
|
||||
var xhrRequests = {};
|
||||
var idRequest = -1;
|
||||
function XMLHttpRequestExtension_send_native() {
|
||||
idRequest += 1;
|
||||
var cb = arguments[4];
|
||||
var context = arguments[5];
|
||||
xhrRequests[idRequest] = {
|
||||
callback: function(responseInfo, responseText, error) {
|
||||
cb(responseInfo, responseText, error);
|
||||
}
|
||||
};
|
||||
var args = [];
|
||||
args[0] = arguments[0];
|
||||
args[1] = arguments[1];
|
||||
args[2] = arguments[2];
|
||||
args[3] = arguments[3];
|
||||
args[4] = idRequest;
|
||||
sendMessage('SendNative', JSON.stringify(args));
|
||||
}
|
||||
""");
|
||||
|
||||
final evalXhrResult = this.evaluate(xhrJsCode);
|
||||
|
||||
if (_XHR_DEBUG) print('RESULT evalXhrResult: $evalXhrResult');
|
||||
|
||||
this.onMessage('SendNative', (arguments) {
|
||||
try {
|
||||
String? method = arguments[0];
|
||||
String? url = arguments[1];
|
||||
dynamic headersList = arguments[2];
|
||||
String? body = arguments[3];
|
||||
int? idRequest = arguments[4];
|
||||
|
||||
Map<String, String> headers = {};
|
||||
headersList.forEach((header) {
|
||||
// final headerMatch = regexpHeader.allMatches(value).first;
|
||||
// String? headerName = headerMatch.group(0);
|
||||
// String? headerValue = headerMatch.group(1);
|
||||
// if (headerName != null) {
|
||||
// headers[headerName] = headerValue ?? '';
|
||||
// }
|
||||
String headerKey = header[0];
|
||||
headers[headerKey] = header[1];
|
||||
});
|
||||
(dartContext[XHR_PENDING_CALLS_KEY] as List<dynamic>).add(
|
||||
XhrPendingCall(
|
||||
idRequest: idRequest,
|
||||
method: method,
|
||||
url: url,
|
||||
headers: headers,
|
||||
body: body,
|
||||
),
|
||||
);
|
||||
} on Error catch (e) {
|
||||
if (_XHR_DEBUG) print('ERROR calling sendNative on Dart: >>>> $e');
|
||||
} on Exception catch (e) {
|
||||
if (_XHR_DEBUG) print('Exception calling sendNative on Dart: >>>> $e');
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class XhtmlHttpResponseInfo {
|
||||
final int? statusCode;
|
||||
final String? statusText;
|
||||
final List<int>? body;
|
||||
final List<List<String>> responseHeaders = [];
|
||||
|
||||
XhtmlHttpResponseInfo({
|
||||
this.body,
|
||||
this.statusCode,
|
||||
this.statusText,
|
||||
});
|
||||
|
||||
void addResponseHeaders(String name, String value) {
|
||||
responseHeaders.add([name, value]);
|
||||
}
|
||||
|
||||
Map<String, Object?> toJson() {
|
||||
return {
|
||||
"statusCode": statusCode,
|
||||
"statusText": statusText,
|
||||
"body": body,
|
||||
"responseHeaders": jsonEncode(responseHeaders)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class XmlHttpRequestResponse {
|
||||
final String? responseText;
|
||||
final String? error; // should be timeout in case of timeout
|
||||
final XhtmlHttpResponseInfo? responseInfo;
|
||||
|
||||
XmlHttpRequestResponse({this.responseText, this.responseInfo, this.error});
|
||||
|
||||
Map<String, Object?> toJson() {
|
||||
return {'responseText': responseText, 'responseInfo': responseInfo!.toJson(), 'error': error};
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:proxypin/network/components/js/file.dart';
|
||||
import 'package:proxypin/network/components/js/md5.dart';
|
||||
import 'package:proxypin/network/components/js/xhr.dart';
|
||||
import 'package:proxypin/network/http/http.dart';
|
||||
import 'package:proxypin/network/http/http.dart' as http;
|
||||
import 'package:proxypin/network/http/http_headers.dart';
|
||||
import 'package:proxypin/network/util/lang.dart';
|
||||
import 'package:proxypin/network/util/logger.dart';
|
||||
@@ -69,7 +71,7 @@ async function onResponse(context, request, response) {
|
||||
|
||||
final Map<ScriptItem, String> _scriptMap = {};
|
||||
|
||||
static JavascriptRuntime flutterJs = getJavascriptRuntime();
|
||||
static JavascriptRuntime flutterJs = getJavascriptRuntime(xhr: false);
|
||||
|
||||
static String? deviceId;
|
||||
|
||||
@@ -89,6 +91,9 @@ async function onResponse(context, request, response) {
|
||||
deviceId = await DeviceUtils.deviceId();
|
||||
Md5Bridge.registerMd5(flutterJs);
|
||||
FileBridge.registerFile(flutterJs);
|
||||
|
||||
flutterJs.enableFetch2();
|
||||
|
||||
logger.d('init script manager $deviceId');
|
||||
}
|
||||
return _instance!;
|
||||
@@ -347,7 +352,7 @@ async function onResponse(context, request, response) {
|
||||
//http request
|
||||
HttpRequest convertHttpRequest(HttpRequest request, Map<dynamic, dynamic> map) {
|
||||
request.headers.clear();
|
||||
request.method = HttpMethod.values.firstWhere((element) => element.name == map['method']);
|
||||
request.method = http.HttpMethod.values.firstWhere((element) => element.name == map['method']);
|
||||
String query = UriUtils.mapToQuery(map['queries']);
|
||||
|
||||
var requestUri = request.requestUri!.replace(path: map['path'], query: query);
|
||||
@@ -362,7 +367,7 @@ async function onResponse(context, request, response) {
|
||||
request.headers.addValues(key, value.map((e) => e.toString()).toList());
|
||||
return;
|
||||
}
|
||||
request.headers.add(key, value);
|
||||
request.headers.set(key, value);
|
||||
});
|
||||
|
||||
//判断是否是二进制
|
||||
@@ -389,7 +394,7 @@ async function onResponse(context, request, response) {
|
||||
return;
|
||||
}
|
||||
|
||||
response.headers.add(key, value);
|
||||
response.headers.set(key, value);
|
||||
});
|
||||
|
||||
response.headers.remove(HttpHeaders.CONTENT_ENCODING);
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:highlight/languages/javascript.dart';
|
||||
import 'package:proxypin/network/components/js/file.dart';
|
||||
import 'package:proxypin/network/components/js/md5.dart';
|
||||
import 'package:proxypin/network/components/js/xhr.dart';
|
||||
|
||||
class JavaScript extends StatefulWidget {
|
||||
const JavaScript({super.key});
|
||||
@@ -40,13 +41,14 @@ class _JavaScriptState extends State<JavaScript> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (resetEnvironment || flutterJs == null) {
|
||||
flutterJs = getJavascriptRuntime();
|
||||
flutterJs = getJavascriptRuntime(xhr: false);
|
||||
}
|
||||
// register channel callback
|
||||
final channelCallbacks = JavascriptRuntime.channelFunctionsRegistered[flutterJs!.getEngineInstanceId()];
|
||||
channelCallbacks!["ConsoleLog"] = consoleLog;
|
||||
Md5Bridge.registerMd5(flutterJs!);
|
||||
FileBridge.registerFile(flutterJs!);
|
||||
flutterJs?.enableFetch2();
|
||||
|
||||
code = CodeController(language: javascript, text: 'console.log("Hello, World!")');
|
||||
}
|
||||
@@ -145,7 +147,8 @@ class _JavaScriptState extends State<JavaScript> {
|
||||
))))),
|
||||
const SizedBox(height: 10),
|
||||
Row(children: [
|
||||
Text("${localizations.output}:", style: TextStyle(fontSize: 16, color: primaryColor, fontWeight: FontWeight.w500)),
|
||||
Text("${localizations.output}:",
|
||||
style: TextStyle(fontSize: 16, color: primaryColor, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(width: 15),
|
||||
//copy
|
||||
IconButton(
|
||||
|
||||
@@ -59,3 +59,4 @@ flutter:
|
||||
- assets/certs/ca.crt
|
||||
- assets/certs/ca_key.pem
|
||||
- assets/icon.png
|
||||
- assets/js/
|
||||
Reference in New Issue
Block a user