I'm using a WKWebView on iOS to render an SVG. The SVG is created like this simplified example in a third-party library:
var svgBlob = new Blob(['<?xml version="1.0" standalone="no"?>\r\n' + '<svg height="100" width="100"><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /></svg>'], { type: "image/svg+xml" });
My code then tries to obtain the raw SVG text like this:
var svgText = await svgBlob.text();
On Safari 15 and higher, everything works as expected—-the SVG data is returned—-but on Safari 14, I get the error svgBlob.text() is not a function. Upon further inspection, svgBlob is in fact a blob, has a size, a type, and a slice function, but no other properties. The text property/function doesn't even exist.
MDN indicates that this API has existed on Safari on iOS since v14, and there are no caveats listed.
Is there some sort of trick to making this work?
Related
I am trying to build a webpage where users can zoom in to a photo and look at the image in greater detail. The idea is that I will have a "zoom in" button, that, when clicked, will show a higher resolution version of the image. Use case: historical/archival image gallery
I recently came upon this article written by Cloudinary on how to create JavaScript functions for zooming in and out of photos. However, the article did not actually provide an example of using the Cloudinary service.
Article: Cool Tricks for Resizing Images in JavaScript (cloudinary.com)
https://cloudinary.com/blog/cool_tricks_for_resizing_images_in_javascript
Does anyone have advice/tips on how to make this work with Cloudinary stored images? I want to ensure I'm serving the highest quality/resolution version of the original image when zoomed in.
I have a codesandbox demo: https://codesandbox.io/s/cloudinary-zoom-image-542b1?file=/src/App.vue
The best practice is to upload the images to Cloudinary in the best quality/resolution. Assuming that the images would be scaled down when displayed on your site, once the zoom button is clicked, you can use Cloudinary transformations to request the images in larger dimensions, and possibly even their original dimensions.
For instance, in the following example, the image is originally scaled down to a width of 400px (c_scale,w_400). Once the "Zoom In" button is clicked, I'm changing the transformation URL in a way that crops a size of 400px width of the original image (height is implicitly set to retain the image aspect ratio) - c_crop,w_400. This way the zoomed-in image is in it's original resolution/quality:
function zoomin() {
var myImg = document.getElementById("my-image");
var src = myImg.src;
var splitted = src.split("c_scale,w_400/");
splitted[0] += "c_crop,w_400";
myImg.src = splitted.join("/");
// results in https://res.cloudinary.com/demo/image/upload/q_auto/c_crop,w_400/sample.jpg
}
function zoomout() {
var myImg = document.getElementById("my-image");
myImg.src = "https://res.cloudinary.com/demo/image/upload/q_auto/c_scale,w_400/sample.jpg";
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
<button type="button" onclick="zoomin()">Zoom In</button>
<button type="button" onclick="zoomout()">Zoom Out</button>
<img src="https://res.cloudinary.com/demo/image/upload/q_auto/c_scale,w_400/sample.jpg" id="my-image" width="400px" alt="Flowers and Bee">
That's just a simple example. There are many ways you could implement a zoom-in effect in general, and by using Cloudinary transformations in particular.
In SVG you can have things which are only clickable at the stroke, e.g. because there is no fill or because of pointer-events: stroke.
Example:
document.querySelector("#path1").addEventListener("click", function(e) {
console.log("Click1!")
})
document.querySelector("#path2").addEventListener("click", function(e) {
console.log("Click2!")
})
#path1:hover {
stroke: red;
}
#path2:hover {
stroke: green;
}
<svg height="300" width="300">
<path d="M64,116 C100,100 400,100 96,39" stroke="blue" stroke-width="7" fill="none" id="path1" />
<path d="M134,186 C100,100 400,100 126,69" stroke="blue" stroke-width="7" fill="none" id="path2" />
</svg>
In my project I want to write Selenium tests for I have a dynamically generated SVG <path>s which I want to click at using Selenium, the problem is that the center of the element1 is not necessarily clickable (since only the stroke of the path is).
Some ideas I had:
Clicking at a fixed offset: A possibility but since the generated path is highly dynamic it would mean a lot of tinkering with the offset value to get it right and possibly having to change the test a lot of the future.
Triggering click event through code: Would work but make the test less useful since this way it would not test whether the stroke of the path is indeed clickable. Certain bugs could evade being detected by the test this way.
Setting a non-none fill through code or replacing/adding filled a rect around the <path>: Setting fill might not guarantee either that the center is clickable. A <rect> would work but then the clickable areas of multiple paths would overlap which could mean that the wrong path gets the click.
None of these approaches are ideal. Are there any other possibilities?
(I am using Selenium for Python but I am OK with solutions with Selenium for other programming languages since normally it's easy to port.)
1) This is the default position Selenium clicks at if using the function where no offset is specified, or rather it's the center of the visible area of the element since the newest version of the WebDriver protocol but in my use case everything can be assumed to be fully visible.
P.S.
Demo showing why setting fill to something other than none wouldn't help (or pointer-events to all):
document.querySelector("#path1").addEventListener("click", function(e) {
console.log("Click1!")
})
document.querySelector("#path2").addEventListener("click", function(e) {
console.log("Click2!")
})
#path1:hover {
stroke: red;
}
#path2:hover {
stroke: green;
}
<svg height="300" width="300">
<path d="M64,116 C100,100 400,100 96,39" stroke="blue" stroke-width="7" fill="turquoise" id="path1" />
<path d="M134,186 C100,100 400,100 126,69" stroke="blue" stroke-width="7" fill="yellow" id="path2" />
</svg>
I've loaded a series of svg images by creating and storing a CanvasSvgDocument for each, and using its LoadAsync() method to load the svg:
nextSvg = CanvasSvgDocument(resourceCreator.Device());
auto fileStream = co_await nextFile.OpenReadAsync();
co_await nextSvg.LoadAsync(resourceCreator.Device(),fileStream);
This appears to load the svg, but when I use that stored svg in a drawing session nothing appears in the CanvasControl. Other items draw fine: shapes, lines, etc. - just not svgs:
session.DrawSvg(m_svg, boxSize, top, left);
In an attempt to discover the problem I've tried using GetXml() on the svg document in the hope that would show me its contents. The result is an abbreviated svg with no drawing information. I provide that here in case it's a hint:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
Perhaps that is all that GetXml() is supposed to return?
I have successfully drawn svgs if they loaded as imagesources; the resulting bitmap image works. But is it possible that session.DrawSvg() is just not completely implemented yet in Win2D? I'd rather render the svg directly this way if it can be done.
[Update] p.s. The svgs are version 1.1 svgs converted from pdfs by one of the online conversion services. As I mentioned, they render fine if opened in Edge or other browsers.
[Update2] Thinking perhaps there is something in the svgs that Win2D doesn't like, I tried creating a simple one using as a model an online example - it just has a rectangle and a circle, as follows. But it also does not draw:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 140"
preserveAspectRatio="xMidYMid meet">
<title>Scalable Vector Graphics (SVG) Demo</title>
<circle cx="100" cy="100" r="50" stroke="#000" stroke-width="1" fill="#ccc" />
<rect x="200" y="50" width="250" height="100" rx="5" ry="5"
stroke="#000" stroke-width="1" fill="#ccc" />
</svg>
[Update] It appears that the problem lies in the way the svg is read from the Storage File. I'll post my own answer as soon as I have enough that it might be useful to someone.
The answer is that my code contains an egregious error: As was pointed out to me at GitHub, LoadAsync() is a static method. In my first line above, I create a CanvasSvgDocument, but in the third line I ignore the results of LoadAsync. LoadASync is also a constructor, so the proper code is
auto fileStream = co_await nextFile.OpenReadAsync();
nextSvg = co_await CanvasSvgDocument::LoadAsync(resourceCreator.Device(),fileStream);
That works! Only trouble is that what I really want is to use LoadFromXaml(), another static method, and there is a different issue with that to be addressed later. Probably also my error.
I'm writing a Windows 8 app and am trying to get live tiles working. Here's what I have so far (all in App.xaml.cs):
In OnLaunched():
Window.Current.VisibilityChanged += Current_VisibilityChanged;
The event handler for that:
void Current_VisibilityChanged(object sender, Windows.UI.Core.VisibilityChangedEventArgs e)
{
// create a string with the tile template xml
string tileXmlString = "<tile><visual><binding template=\"TileSquareBlock\">" + App.VM.GetImageLinks() + "</binding></visual></tile>";
// create a DOM
Windows.Data.Xml.Dom.XmlDocument tileDOM = new Windows.Data.Xml.Dom.XmlDocument();
// load the xml string into the DOM, catching any invalid xml characters
tileDOM.LoadXml(tileXmlString);
// create a tile notification
TileNotification tile = new TileNotification(tileDOM);
// send the notification to the app's application tile
TileUpdateManager.CreateTileUpdaterForApplication().Update(tile);
}
App.VM.GetImageLinks() returns:
<tile>
<visual>
<binding template=\"TileSquareBlock\">
<image id=\"1\">Assets/Img1.jpg</image>
<image id=\"2\">Assets/Img2.jpg</image>
<image id=\"3\">Assets/Img3.jpg</image>
</binding>
</visual>
</tile>
At the minute, I'm basically trying to just get these images to show on the start screen. My guess is that VisibilityChanged is the wrong event, because it seems to occur too often.
The XML being used is invalid, as the TileSquareBlock template doesn't contain any images. See the tile template catalog to see the various visual templates and their corresponding XML.
The NotificationsExtensions library (found in the MSDN tiles sample) provides an object model that maps the image and text fields to named properties. It provides more validation and may simplify your code.
I have a gadget that is a glossary with a number of different pages. users can upload new words to the data source and those words will be pulled into the glossary through an AJAX call.
I want to resize the gadget window everytime the window is re-sized OR a new letter is selected and the page height changes (ie the gadget html block height).
Google developers has posted this on their website. However, this clearly is not working for me. The scrolling is not registering on the iframe and the height is not adjusting when the window is resized.
Here are my ModulePrefs
title="Climate Policy and Science Glossary"
description="Paragraph format"
height="300"
scrolling="true">
<Require feature="dynamic-height"/>
<Require feature="opensocial-0.8" />
Here is the gadget's script telling it to adjust:
window.onresize = adjust;
function adjust() {
var wndwH = gadgets.window.getViewportDimensions().height,
wgtH = $('#_glossary').closest('html').height,
h = Math.min(wndwH, wgtH);
gadgets.window.adjustHeight(h);
}
gadgets.util.registerOnLoadHandler(adjust);
What's going on? Am I doing something wrong or is there anyone else out there having trouble with Google's dynamic height??
The adjust function really only needs:
function adjust() {
gadgets.window.adjustHeight();
}
That should fit everything automatically.