Widget with Webview

Hey there!

I am currently trying to create my first widget. As a first test I would like to display the server load of a computer game. I get the data from the page www.newworldstatus.com. However, the corresponding data is only loaded when the page is visited, so I already found out that I have to use Webview.

That’s my initial approach, based on my research so far:


let url = "https://www.newworldstatus.com/";

async function loadSite() {

  let wbv = new WebView()
  await wbv.loadURL(url)

  let jsc = `
  var server = document
    .getElementsByTagName("tbody")[1]
    .getElementsByTagName("td")[106]
    .innerText
  `
  
  await wbv.evaluateJavaScript(jsc)
  console.log(server)
}

The goal is to output the queue from the server Ravenal.

Unfortunately, I’m not getting anywhere… The problem is, among other things, that I only want to evaluate the data from a specific server (Ravenal). My idea was to query the elements (using for loop) until this one is equal to “Ravenal”. But how does this work? Or is there another way?

Thank you very much!

This should allow you to get just the Ravenal row.
The ravenal variable corresponds to whole <tr>.
Your challenge would be how to wait for whole page to load. wbv.waitForLoad() doesn’t seem to help.

var rows = Array.from(document.querySelector('#db76b9e516bd tbody').rows)
var ravenal = rows.filter( row => row.children[1].innerText=='Ravenal')[0]
2 Likes

Thank you very much for your help. Unfortunately, I don’t get any values. Even the variable rows is completely empty:


[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]

Is this because the page has not yet loaded? Is there no possibility that, for example, wait 15 seconds?

HTML elements don’t print anything to the console. If the object is there, then the element exists. You would actually have to check/print the content with .innerText.

Thanks for the tip. It works now with the output in the widget. Is there any way to access the individual values in the “Ravenal” row? Or do I have to use the substring function?


let widget = new ListWidget()
widget.setPadding(16, 16, 16, 16)

const webview = new WebView();
await webview.loadURL("https://newworldstatus.com");

var getData = `
  function runit() {
    var rows = Array.from(document.querySelector("#db76b9e516bd tbody").rows);
    var ravenal = rows.filter( row => row.children[1].innerText=='Ravenal')[0];  
    return ravenal.innerText;
  }
  runit();
`

widget.addText(await webview.evaluateJavaScript(getData, false));

if (!config.runsInWidget) widget.presentSmall()
Script.setWidget(widget)
Script.complete()

image

var ravenal = rows.filter( row => row.children[1].innerText=='Ravenal')[0];  
var cells = Array.from(ravenal.children).map( td => td.innerText )
return cells;
// returns an array ["LIVE", "Ravenal", "Vanaheim Rho (DE/EN)", "EU (Frankfurt)", "1341", "0", "No wait"] 

Thank you @supermamon and @schl3ck!

This is my result and I am very satisfied.

image


let widget = new ListWidget();
widget.setPadding(16, 16, 16, 16);

const gradient = new LinearGradient()
  gradient.locations = [0, 1]
  gradient.colors = [
    new Color("1e3f66"),
    new Color("2e5984")
  ]
  widget.backgroundGradient = gradient;

let color1 = new Color("fffafa");

let goodIcon = SFSymbol.named("checkmark.circle.fill").image;
let goodIconColor = Color.green();
let badIcon = SFSymbol.named("exclamationmark.triangle.fill").image;
let badIconColor = Color.red();
let iconSize = new Size(12, 12);

let fm = FileManager.iCloud();
let imagePath = fm.documentsDirectory() + "/NW-logo.png";
await fm.downloadFileFromiCloud(imagePath);

const webview = new WebView();
await webview.loadURL("https://newworldstatus.com");

var getData = `
  function runit() {
    var rows = Array.from(document.querySelector("#db76b9e516bd tbody").rows);
    var ravenal = rows.filter( row => row.children[1].innerText=='Ravenal')[0];   
    var cells = Array.from(ravenal.children).map( td => td.innerText )  
    return cells;
  }
  runit();
`

let data = await webview.evaluateJavaScript(getData, false);

let header = widget.addStack();
header.addImage(fm.readImage(imagePath));
widget.addSpacer(10);

let playerStack = widget.addStack();
playerStack.layoutHorizontally();
playerStack.centerAlignContent();
widget.addSpacer(10);
let serverStack = widget.addStack();
serverStack.layoutHorizontally();
serverStack.centerAlignContent();
widget.addSpacer(5);
let serverStack2 = widget.addStack();
serverStack2.layoutVertically();

let players = playerStack.addText(data[4]);
players.textColor = color1;
players.font = Font.blackSystemFont(24);
playerStack.addSpacer(5);
let queue = playerStack.addText("(+ " + data[5] + ")");
queue.textColor = color1;
queue.font = Font.semiboldSystemFont(12);

let status = badIcon
if (data[0] === "LIVE") {
  status = goodIcon;
} else {
  status = badIcon;
}

let icon = serverStack.addImage(status);
icon.imageSize = iconSize;
if (data[0] === "LIVE") {
  icon.tintColor = goodIconColor;
} else {
  icon.tintColor = badIconColor;
}

serverStack.addSpacer(5);
let server = serverStack.addText(data[1]);
server.textColor = color1;
server.font = Font.semiboldSystemFont(14);

let worldset = serverStack2.addText(data[2]);
worldset.textColor = color1;
worldset.font = Font.systemFont(8);
let location = serverStack2.addText(data[3]);
location.textColor = color1;
location.font = Font.systemFont(8);

if (!config.runsInWidget) widget.presentSmall();
Script.setWidget(widget);
Script.complete();

I am happy about optimization suggestions. Otherwise, thanks again for your help!