javascript embedded in html not running in wkwebview - javascript

I am implementing wkwebview in an app that was using UIWebView. I am unable to get javascript to execute when pointing to a local html file that has the javascript embedded within it. The javascript is stripped to do a simple alert and load a basic google map. None of that is getting executed. Do I need to run a local server? GCDWebserver..
I should add, the html/javascript works in safari, google browser no problem.
Solutions attempted include:
1. AppTransportSecuritySettings AllowArbitrary loads.
2. ViewController.swift webview.configuration.preferences.javaScriptEnabled = true
3. This question addresses the issue and says is was fixed in iOS 10.3 Load Javascript files through HTML file on WKWebView in iOS The simulator is running 12.1
4. This question also addresses the issue with an answer of requiring GCDWebserver to be able to execute javascript using wkwebview. WKWebView not executing any js code This however was also solved in a laster version of iOS. Here is some code:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
//#IBOutlet var googleMap: WKWebView!
var webview: WKWebView!
override func loadView() {
webview = WKWebView()
webview.navigationDelegate = self
view = webview
}
override func viewDidLoad() {
super.viewDidLoad()
//let url = URL(string: "https://schallerf1.com")!
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www")!
webview.load(URLRequest(url: url))
webview.allowsBackForwardNavigationGestures = true
let request = URLRequest(url: url)
webview.configuration.preferences.javaScriptEnabled = true
webview.load(request)
}
}
<!DOCTYPE html>
<html>
<head>
<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<b>WHYYYYYYYYYY!!!!</b>
<div style="height:100%;width:100%;" id="map"></div>
<script type="text/javascript">
var name = "TESTTESTTEST";
alert('code: ' + name + '\n');
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 39.976068, lng: -83.003297},
zoom: 8
});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxx&callback=initMap"></script>
</body>
</html>
None of the javascript works, I should get an alert and a simple google map should display. Do I need to look into the local web server GCDWebserver?

You should be calling your javascript in this WKNavigationDelegate method, which is called when the webview finishes loading.
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("initMap()", completionHandler: { (value, err) in
// Handle response here.
})
}
Also, I'm not sure why you're calling two different webview.load() requests - maybe don't do that?

Related

Unable to load tilesets and maps into my Phaser3 game

I am new to phaser and game development.
I followed the below tutorial.
https://medium.com/#michaelwesthadley/modular-game-worlds-in-phaser-3-tilemaps-1-958fc7e6bbd6
I downloaded and Tiled software and made a simple map with a tileset I got from OpenGameArt.org. Unfortunately, nothing gets loaded on the browser screen, I just see a black rectangle instead of the map. I find no errors in the console. I am running this using XAMPP in Windows 10.
I will paste all my code here, let me know if you find anything wrong.
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/phaser#3.15.1/dist/phaser-arcade-physics.min.js">
</script>
</head>
<body>
<script src="index.js" type="text/javascript"></script>
</body>
</html>
The is the index.js file
const config = {
type: Phaser.AUTO, // Which renderer to use
width: 100, // Canvas width in pixels
height: 100, // Canvas height in pixels
parent: "game-container", // ID of the DOM element to add the canvas to
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
function preload() {
// Runs once, loads up assets like images and audio
this.load.image("tiles", "assets/tilesets/GoldBricks.png");
this.load.tilemapTiledJSON("map", "assets/tilemaps/mario.json");
}
function create() {
// Runs once, after all assets in preload are loaded
const map = this.make.tilemap({ key: "map" });
const tileset = map.addTilesetImage("GoldBricks", "tiles");
// Parameters: layer name (or index) from Tiled, tileset, x, y
const belowLayer = map.createStaticLayer("Tile Layer 1", tileset, 0, 0);
}
function update(time, delta) {
// Runs once per frame for the duration of the scene
}
EDIT: Below is the json file
{ "compressionlevel":-1,
"height":100,
"infinite":false,
"layers":[
{
"compression":"",
"data":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAAAWAAAAFwAAABgAAAAZAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAVAAAAFgAAABcAAAAYAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAkQAAAJIAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAA==",
"encoding":"base64",
"height":100,
"id":1,
"name":"Tile Layer 1",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":100,
"x":0,
"y":0
}],
"nextlayerid":2,
"nextobjectid":1,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.3.2",
"tileheight":32,
"tilesets":[
{
"columns":16,
"firstgid":1,
"image":"..\/..\/..\/..\/..\/Users\/Shashank A C\/Downloads\/Goldbricksandgrass\/GoldBricks.png",
"imageheight":512,
"imagewidth":512,
"margin":0,
"name":"GoldBricks",
"spacing":0,
"tilecount":256,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":1.2,
"width":100
}
I am also seeing and error in the console now.
Uncaught TypeError: Cannot read property '0' of undefined
at StaticTilemapLayer.upload (phaser.js:74806)
at StaticTilemapLayerWebGLRenderer [as renderWebGL] (phaser.js:122959)
at WebGLRenderer.render (phaser.js:65133)
at CameraManager.render (phaser.js:114533)
at Systems.render (phaser.js:27184)
at SceneManager.render (phaser.js:46818)
at Game.step (phaser.js:109346)
at TimeStep.step (phaser.js:106091)
at step (phaser.js:66488)
UPDATE: Check this file structure --
https://next.plnkr.co/edit/OqywHzLC80aZMGeF
======
Need to see the JSON file to completely understand the issue, but I will just try to speculate. Make sure your JSON file has below settings correctly:
"tilesets":[
{
"image":"path/to/GoldBricks.png",
"name":"GoldBricks"
...
}
]
In some cases Tiled includes wrong/different path to the image file, so make sure to check that part. If there is no image path, embed it in Tiled.
In addition, the name value should match the first parameter of map.addTilesetImage(). Hope it helps!
I had a similar problem myself, the solution was going back to Tiled software and check: 'Embed tileset' on each tileset of the map.
Alright, I asked in the phaser community forum itself and got some help.
The tilemap layer is taller than the game canvas so the visible tiles are out of sight. The solution is to add the below code in the create function.
this.cameras.main.centerOn(800, 1300);

How to display an Esri Map on MVC view from an HTML file?

I would like to know how to display an Esri Map on a ASP.Net MVC view from an HTML file.
I have an HTML file that contains a JavaScript code. This file can display the map and the widgets when I open it with any internet browser. It is working fine. But now I want to display that map on an ASP.Net MVC view. This is what I have done:
<h2 class="panel-title" data-toggle="collapse" href="#projectmap">Project Map</h2>
<div id="projectmap" class="panel-collapse collapse">
<div class="panel-body">
<div class="form-group">
#Html.Raw(File.ReadAllText(Server.MapPath("~/Views/Application/default.html")))
</div>
</div>
</div>
But it displays only the Sketch widget, it does not display the map. I have also tried to write the JavaScript code manually on the .cshtml file and use the div container to display the map, it still displays the Sketch widget only. I have also tried the partial views, it does not work.
Below is the HTML code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Load a basic WebMap - 4.14</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet"
href="https://js.arcgis.com/4.14/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.14/"></script>
<script>
var x;
var y;
require(["esri/views/MapView", "esri/WebMap", "esri/config", "esri/widgets/Sketch", "esri/layers/GraphicsLayer"], function (MapView, WebMap, esriConfig, Sketch, GraphicsLayer) {
/************************************************************
* Creates a new WebMap instance. A WebMap must reference
* a PortalItem ID that represents a WebMap saved to
* arcgis.com or an on-premise portal.
*
* To load a WebMap from an on-premise portal, set the portal
* url with esriConfig.portalUrl.
************************************************************/
esriConfig.portalUrl = "https://portal.environment.gov.za/portal";
var webmap = new WebMap({
portalItem: {
// autocasts as new PortalItem()
id: "04582be14885483da48f29398960f653"
}
});
var graphicsLayer = new GraphicsLayer();
/************************************************************
* Set the WebMap instance to the map property in a MapView.
************************************************************/
var view = new MapView({
map: webmap,
container: "viewDiv"
});
webmap.layers.add(graphicsLayer);
var sketch = new Sketch({
layer: graphicsLayer,
view: view,
container: "viewDiv"
});
view.ui.add(sketch, "top-left");
// Listen to sketch widget's create event.
sketch.on("create", function (event) {
// check if the create event's state has changed to complete indicating
// the graphic create operation is completed.
console.log(view.zoom);
//if (event.state === "complete") {
// // remove the graphic from the layer. Sketch adds
// // the completed graphic to the layer by default.
// graphicsLayer.remove(event.graphic);
// // use the graphic.geometry to query features that intersect it
// selectFeatures(event.graphic.geometry);
//}
console.info(event.graphic.geometry);
x = event.graphic.geometry.x;
y = event.graphic.geometry.y;
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>

Download image returned by JavaScript (html2canvas)

I'm trying to download an image from a WebPage, which is returned by a JavaScript (using html2canvas) immediately after calling it. Therefore I'm using the library HTMLUnit, but I haven't been successful until now.
Unfortunately only a faulty png-File is downloaded, which has around 140kb. It can't be opened by Windows (e.g. paint or preview).
Code-Snippet for my HTML-Page (executed immediately after div-element #div is loaded:
function saveMap() {
var element = $("#div");
html2canvas(element, {
useCORS: true,
onrendered: function(canvas) {
var dataUrl= canvas.toDataURL("image/png");
var a = $("<a>")
.attr("href", dataUrl)
.attr("download", "test.png")
.appendTo("body");
a[0].click();
a.remove();
}
});
}
Java-Code trying to download the returned png-File:
WebClient webClient = new WebClient(BrowserVersion.CHROME);
try {
HtmlPage page1 = webClient.getPage( new URI("file:///D:/path/to/page/sample.html").toURL() );
webClient.waitForBackgroundJavaScript(5000);
InputStream is = page1.getWebResponse().getContentAsStream();
File f = new File("test.png");
OutputStream os = new FileOutputStream(f);
byte[] bytes = new byte[2048];
int b = 0;
while ((b = is.read()) != -1)
{
os.write(bytes, 0, b);
}
os.close();
is.close();
} catch (FailingHttpStatusCodeException | IOException | URISyntaxException e) {
e.printStackTrace();
}
Full HTML-Page:
<!DOCTYPE html>
<html>
<head>
<style>
html, body, #div {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="html2canvas.js"></script>
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
</head>
<body>
<div id="div"></div>
<script>
// Some init stuff for div and after completion the following:
saveMap();
function saveMap() {
var element = $("#div");
html2canvas(element, {
useCORS: true,
onrendered: function(canvas) {
var dataUrl= canvas.toDataURL("image/png");
var a = $("<a>")
.attr("href", dataUrl)
.attr("download", "test.png")
.appendTo("body");
a[0].click();
a.remove();
}
});
}
</script>
</body>
</html>
Thanks for the code. Have already done some tests with the samples available on the html2canvas web page. There is bug in the current version of HtmlUnit that stops the javascript from working.
I think i have done a fix also, but sourceforge is down at the moment. If they are back i will commit the fix and prepare a new snapshot. Will inform you and also have a look at your sample.
BTW: Do not expect nice screenshots from this. HtmlUnit is a headless browser and most of the layout functions are doing only a basic job. But you are welcome to provide better implementations.
Your code works (with some fixes) with the latest snapshot.
But to get reasonable results you have to provide a width and height for the result. I guess there is some layouting in HtmlUnit that returns otherwise 1x1 as result size. If this is a problem for you might have a look inside the code and try to point to the problematic place.
html2canvas(element, {
useCORS: true,
width: 300,
height: 300,
onrendered: function(canvas) {
Now to your java code
HtmlPage page1 = webClient.getPage( new URI("file:///D:/path/to/page/sample.html").toURL() );
webClient.waitForBackgroundJavaScript(5000);
The tricky part here is the async execution of the rendering inside your browser. From HtmlUnit point of view the browsers will, after the loading of the page was done, replace the content of the current window with the png image. Andd you have to deal with this in your code. Because there is a replace your page1 is still the old page returned (sync).
After the wait you have to reget the current content to have the png in hands
Page image = webClient.getCurrentWindow().getEnclosedPage();
InputStream is = image.getWebResponse().getContentAsStream();
And finally there is a small issue with your image writing code
Instead of
while ((b = is.read()) != -1)
you have to write
while ((b = is.read(bytes)) != -1)
Otherwise you will end up with a file of null bytes.
Hope that helps.

Vue.js not rendering when trying to generate a PDF using Phantom.js

In this simple example with hardcoded url my Vue.js components not rendering, plain html get rendered but all places i have a component appear blank.
Phantom.js should work normally with Vue.js?
var webPage = require('webpage');
var page = webPage.create();
page.viewportSize = { width: 1920, height: 1080 };
page.open("-----------", function start(status) {
page.render('test.jpeg', {format: 'jpeg', quality: '100'});
phantom.exit();
});
Quick vue code for who want to help and do the test.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js" charset="utf-8"></script>
<style media="screen"> body { background-color: grey; } </style>
</head>
<body>
plain text before vue
<div id="app" v-text="title" />
plain text after vue
<script type="text/javascript">
const app = new Vue({ el : '#app', data () { return { title : 'Vue Title' } } });
</script>
</body>
</html>
Check your phantomjs version. Phantomjs Version 2.x would work just fine with vuejs.
It's possible that phantomjs is running too quick that the element is not yet available when it reach that part of the code. Use waitForElement or wait to induce a delay before trying to access the element.

Get access to Google Maps API from a Chrome extension

Say I'd like to find addresses on any webpage and have a click on each one of them insert a small Google Maps below the address.
The problem I'm running into is that the GMaps library must be loaded via a < script> tag. But because anything loaded via < script> is out of the Chrome extension execution context, the "google.maps" object won't be available to my content scripts.
Any thoughts on a workaround?
What you could do is create an iframe that contains a page to your map viewer. Then you will belong in the context of Content Scripts and you have full access to Chrome's Message Passing.
Since I have created hundreds of extensions, I have done exactly this in tons of them, and some of them are available in my github.com/mohamedmansour page. I will show an example that I just did for this problem below, unfortunately it may contain bugs. Check my github page above for a more complete example.
What I would do
Create a map_viewer.html page in your extension.
Include within the <head> tag the <script src="http://maps.google.com/maps?file=api.....
Use Chrome Message Passing to pass data between content scripts.
For example
map_viewer.html
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="/css/map_viewer.css" />
<script type="text/javascript" src="/js/map_viewer.js"></script>
</head>
<body>
<div id="map_canvas"></div>
</body>
</html>
map_viewer.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == 'RenderMap') {
renderMap(request.data.latitude, request.data.longitude);
}
});
function renderMap(latitude, latitude) {
var map = new GMap2(document.getElementById('map_canvas'));
map.setCenter(new GLatLng(latitude, latitude), 13);
var marker = new GMarker(new GPoint(lng, lat));
map.addOverlay(marker);
}
webpage_content_script.js
...
// Unique ID to differentiate this content script from the rest of the web.
// or use the extension id from ##__extension_id__, I recall there was a bug, haven't
// checked if it got resolved though.
var UNIQUE_MAP_VIEWER_ID = 'crx_myextension_iframe';
var latitude = -1;
var longitude = -1;
/**
* Here is where you want to render a latitude and longitude. We create an iframe so we
* we can inject it. We just want to maintain a single instance of it though.
*/
function onRenderMap() {
var mapViewerDOM = document.getElementById(UNIQUE_MAP_VIEWER_ID);
if (mapViewerDOM) {
mapViewerDOM.parentNode.removeChild(mapViewerDOM);
}
mapViewerDOM = document.createElement('iframe');
mapViewerDOM.setAttribute('id', UNIQUE_MAP_VIEWER_ID);
mapViewerDOM.setAttribute('src', chrome.extension.getURL('map_viewer.html');
mapViewerDOM.setAttribute('frameBorder', '0');
mapViewerDOM.setAttribute('width', '99.90%');
mapViewerDOM.setAttribute('height', '100%');
mapViewerDOM.setAttribute('style', 'position: fixed; top: 0; left: 0; overflow: hidden; z-index: 99999');
mapViewerDOM.onload = function(e) {
sendResponse({
method: 'RenderMap',
data: {
latitude: latitude,
longitude: longitude
}
});
}
document.body.appendChild(mapViewerDOM);
}
...
I hope this would steer you in the right direction.
Thanks,
Mohamed Mansour

Categories