Adding a fallback images in markdown - html

I have been replacing my existing site images with .webp. And in HTML it's relatively easy to add fallback support for instance in which .webp isn't supported. However, I'm struggling to find an equivalent for markdown?
HTML
<img src="img.webp" />
HTML with fallback support
<picture>
<source srcset="img.webp" type="image/webp">
<source srcset="img.jpg" type="image/jpeg">
<source srcset="img.png" type="image/png">
</picture>
For Example
Markdown
![](img.webp)
Markdown with fallback support
![](img.webp,img.png)

I don't think there's a spec for fallback images like that in markdown, but if you're using markdown-it (which is the default markdown parser in Eleventy) you can add custom plugins to extend the syntax. You can also just put HTML in your markdown, or you can even use shortcodes in your markdown if you're using Eleventy.
Using shortcodes
You can define custom shortcodes in Eleventy and then use them in your markdown. This could be easier and more flexible than setting up markdown-it plugins, but has the drawback of putting non-markdown things in your markdown.
Using a markdown-it plugin
If you already have pre-generated images that you want to use, you may want to use something like markdown-it-picture and modify it to set the type attribute, either by parsing the file extension or changing the media query/title spot to accept the type instead. You'll want to copy the code into a new file in your project, make your modifications, and register it like so:
// .eleventy.js
module.exports = function(eleventyConfig) {
const mdPicture = require('/path/to/plugin.js')
const md = require('markdown-it')()
md.use(mdPicture)
eleventyConfig.setLibrary('md', md)
// ...
};
With eleventy-image
Alternatively, if you don't have images already generated and would like to generate them as part of the 11ty build step, you can use eleventy-image (Docs). This Twitter thread has a discussion on creating a custom markdown-it renderer, which I've modified to use with eleventy-image below.
// you might want to put this in another file
// such as ./utils/markdown.js
// responsive images with 11ty image
// this overrides the default image renderer
// titles are also used for size setting (optional)
//
// ![alt text](/path/to/img.png '(max-width: 768px) 100vw, 768px')
const md = require('markdown-it')();
const Image = require('#11ty/eleventy-img')
md.renderer.rules.image = function (tokens, idx, options, env, self) {
const token = tokens[idx]
let imgSrc = token.attrGet('src')
const imgAlt = token.content
// you can modify the default sizes, or omit
const imgSize = token.attrGet('title') || '(max-width: 768px) 100vw, 768px'
const widths = [250, 426, 580, 768] // choose your own widths, or [null] to disable resize
const imgOpts = {
widths: widths
.concat(widths.map((w) => w * 2)) // generate 2x sizes for retina displays
.filter((v, i, s) => s.indexOf(v) === i), // dedupe widths
formats: ['webp', 'jpeg'], // choose your own formats (see docs)
urlPath: '/assets/img/', // src path in HTML output
outputDir: './_site/assets/img/' // where the generated images will go
}
// generate the images
// see https://www.11ty.dev/docs/plugins/image/#synchronous-usage
Image(imgSrc, imgOpts)
const metadata = Image.statsSync(imgSrc, imgOpts)
return Image.generateHTML(metadata, {
alt: imgAlt,
sizes: imgSize,
loading: 'lazy',
decoding: 'async'
})
}
// in your .eleventy.js
module.exports = function(eleventyConfig) {
// you may need to `require` the file with
// your markdown config with the custom renderer
// const md = require('./utils/markdown.js')
eleventyConfig.setLibrary('md', md)
// ...
};
This renderer will automatically generate different sizes of your image, as well as different formats, and then output the <picture> tags in your HTML. Since markdown-it doesn't play well with async functions, we use the syncronous pattern of eleventy-image. More information about configuration can be found in the docs.
One thing you might have to be careful about is that the image path in your markdown should be where the image is located in your website source, NOT the eventual published URL. For example, my image may be located at src/images/img.png, but published at https://example.com/images/img.png. The markdown should be ![alt text](src/images/img.png) and NOT ![alt text](/images/img.png). You can also do additional parsing in the renderer function.

Not sure if this will work, but you can try it out:
[![my image](img.webp)](image_url)

Related

Can i load data referenced by a Web Component dynamically, with caching?

I'm currently learning Web Components and I wonder if it is possible to have a Component load its own data dynamically, similar to how <img> does from its src attribute, i.e. something like this:
<my-fancy-thingy src='/stuff.json'></my-fancy-thingy>
Obviously this functionality would be useful if stuff.json could be rather large, so it should also be possible to make use of the browser's caching mechanism so the referenced file doesn't get reloaded every time we request the page, unless changed.
Can this be done?
Sure, take inspiration from <load-file> See Dev.to Post
/*
defining the <load-file> Web Component,
yes! the documenation is longer than the code
License: https://unlicense.org/
*/
customElements.define("load-file", class extends HTMLElement {
// declare default connectedCallback as sync so await can be used
async connectedCallback(
// attach a shadowRoot if none exists (prevents displaying error when moving Nodes)
// declare as parameter to save 4 Bytes: 'let '
shadowRoot = this.shadowRoot || this.attachShadow({mode:"open"})
) {
// load SVG file from src="" async, parse to text, add to shadowRoot.innerHTML
shadowRoot.innerHTML = await (await fetch(this.getAttribute("src"))).text()
// append optional <tag [shadowRoot]> Elements from inside <load-svg> after parsed <svg>
shadowRoot.append(...this.querySelectorAll("[shadowRoot]"))
// if "replaceWith" attribute
// then replace <load-svg> with loaded content <load-svg>
// childNodes instead of children to include #textNodes also
this.hasAttribute("replaceWith") && this.replaceWith(...shadowRoot.childNodes)
}
})
Change .text() to .json() and it parses JSON files
Caching can be done by storing the String in localStorage (but a 5MB limit total, I think):
https://en.wikipedia.org/wiki/Web_storage
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
You need to come up with "data has changed" strategy; as the Client has no clue when data actually was changed. Maybe an extra semaphore file/endpoint that provides info if the (large) JSON file was changed.
This works like a charm
export class MonElement extends HTMLElement {
constructor(){
super();
this.attachShadow({mode:'open'});
(...)
this.shadowRoot.appendChild(atemplate);
}
connectedCallback(){...}
static get observedAttributes(){
return ['src'];
}
attributeChangedCallback(nameattr,oldval,newval)
{
if (nameattr==='src') {
this[nameattr]=newval;
here do the fetch for the src value which is newval then update what you got in the innerdom
}
(...)

IPython notebook markdown -- what are the list of allowed HTML tags and how can I change them?

I'm using IPython Notebook markdown to create a document and it does allow some HTML like <img> for example but I added <object> to include an SVG file and it does not show up in the IPython Notebook output. I double-checked the HTML source of the IPython Notebook and the <object> tag is not served by the IPython Notebook server. Looks like the <iframe> tag is sanitized also.
Whereas when I convert it myself outside of IPython Notebook to an HTML file, it works fine. Is there something I need to do to add <object> and <iframe> to the list of allowed HTML tags?
I found Jupyter's security.js but not sure whether this runs on the server or the client, and not sure how to alter it.
I also tried File → Trust Notebook but that didn't fix it either.
I monkeypatched my local copy of site-packages/notebook/static/notebook/js/main.min.js to have this:
var sanitize_html = function (html, allow_css) {
/**
* sanitize HTML
* if allow_css is true (default: false), CSS is sanitized as well.
* otherwise, CSS elements and attributes are simply removed.
*/
var html4 = caja.html4;
if (allow_css) {
// allow sanitization of style tags,
// not just scrubbing
html4.ELEMENTS.style &= ~html4.eflags.UNSAFE;
html4.ATTRIBS.style = html4.atype.STYLE;
} else {
// scrub all CSS
html4.ELEMENTS.style |= html4.eflags.UNSAFE;
html4.ATTRIBS.style = html4.atype.SCRIPT;
}
/* BEGIN NEW CODE */
var whitelist_key = html.match(/<span class="whitelist:([0-9A-Za-z-_]+)"\s+\/?>/)
if (whitelist_key && whitelist_key[1]== /* password goes here */)
{
console.log("whitelist ok");
html4.ELEMENTS.object &= ~html4.eflags.UNSAFE;
html4.ATTRIBS['iframe::src'] = 0;
html4.ATTRIBS['object::data'] = 0;
html4.ATTRIBS['object::type'] = 0;
}
/* END NEW CODE */
and then in the cells of my notebook I add <span class="whitelist:mypassword" />
It works for now, I've opened issue #2614 on github.

Jekyll - where is full-img class coming from?

When I use image markdown like ![alt text](/assets/images/foo.png), for some images Jekyll is adding class="full-img", making the image larger than it should be.
I observe the same behavior with both redcarpet and kramdown.
Why is that happening / how do I stop it?
I have the similar issue.
In my case I use theme kasper .
I found that class full-img was not added by markdown engine, but the JavaScript code that author wrote in the assets/js/index.js file.
There's a function saying
function updateImageWidth() {
var $this = $(this),
contentWidth = $postContent.outerWidth(), // Width of the content
imageWidth = this.naturalWidth; // Original image resolution
if (imageWidth >= contentWidth) {
$this.addClass('full-img');
} else {
$this.removeClass('full-img');
}
}
After commenting every picture works fine as the original class.
That's my case.
Hopes that works for you.

Use local files with Browser extensions (kango framework)

I'm working on a "browser extension" using "Kango Framework" (http://kangoextensions.com/)
When i want to link a css file i have to use external source (href='http://mysite.com/folder/mysite.css), how should i change the href to make is source from the plugin folder ? (ex: href='mylocalpluginfolder/localfile.css')
i've tried 'localfile.css' and putting the file in the same folder as the JS file.
$("head").append("");
How should i change the json file to make it work ? Should i declare the files as "extended_scripts" or "content_scripts" ?
I've a hard time finding support for this framework, even though the admins are awesome !
Thanks for your help. (please do not suggest to use other solutions, because i won't be able to code plugins for IE and Kango is my only option for this). I didn't find any samples matching my need as the only example available on their site is linking to outside content (christmas tree).
If you want to add CSS in page from content script you should:
Get CSS file contents
Inject CSS code in page
function addStyle(cssCode, id) {
if (id && document.getElementById(id))
return;
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (id)
styleElement.id = id;
if (styleElement.styleSheet){
styleElement.styleSheet.cssText = cssCode;
}else{
styleElement.appendChild(document.createTextNode(cssCode));
}
var father = null;
var heads = document.getElementsByTagName("head");
if (heads.length>0){
father = heads[0];
}else{
if (typeof document.documentElement!='undefined'){
father = document.documentElement
}else{
var bodies = document.getElementsByTagName("body");
if (bodies.length>0){
father = bodies[0];
}
}
}
if (father!=null)
father.appendChild(styleElement);
}
var details = {
url: 'styles.css',
method: 'GET',
async: true,
contentType: 'text'
};
kango.xhr.send(details, function(data) {
var content = data.response;
kango.console.log(content);
addStyle(content);
});
I do it another way.
I have a JSON containing the styling for specified web sites, when i should change the css.
Using jQuery's CSS gives an advantage on applying CSS, as you may know css() applying in-line css and inline css have a priority over classes and IDs defined in default web pages files and in case of inline CSS it will override them. I find it fine for my needs, you should try.
Using jQuery:
// i keep info in window so making it globally accessible
function SetCSS(){
$.each(window.skinInfo.css, function(tagName, cssProps){
$(tagName).css(cssProps);
});
return;
}
// json format
{
"css":{
"body":{"backgroundColor":"#f0f0f0"},
"#main_feed .post":{"borderBottom":"1px solid #000000"}
}
}
As per kango framework structure, resources must be placed in common/res directory.
Create 'res' folder under src/common folder
Add your css file into it and then access that file using
kango.io.getResourceUrl("res/style.css");
You must add this file into head element of the DOM.
This is done by following way.
// Common function to load local css into head element.
function addToHead (element) {
'use strict';
var head = document.getElementsByTagName('head')[0];
if (head === undefined) {
head = document.createElement('head');
document.getElementsByTagName('html')[0].appendChild(head);
}
head.appendChild(element);
}
// Common function to create css link element dynamically.
function addCss(url) {
var css_tag = document.createElement('link');
css_tag.setAttribute('type', 'text/css');
css_tag.setAttribute('rel', 'stylesheet');
css_tag.setAttribute('href', url);
addToHead(css_tag);
}
And then you can call common function to add your local css file with kango api
// Add css.
addCss(kango.io.getResourceUrl('res/style.css'));

LibTiff.NET append mode bug?

I've started using LibTiff.NET for writing tiff IPTC tags lately and discovered strange behavior on some files that i have here. I'm using sample code that ships with LibTiff.NET binaries, and it works fine with most of the images, but some files are having image data corruption after these lines:
class Program
{
private const TiffTag TIFFTAG_GDAL_METADATA = (TiffTag)42112;
private static Tiff.TiffExtendProc m_parentExtender;
public static void TagExtender(Tiff tif)
{
TiffFieldInfo[] tiffFieldInfo =
{
new TiffFieldInfo(TIFFTAG_GDAL_METADATA, -1, -1, TiffType.ASCII,
FieldBit.Custom, true, false, "GDALMetadata"),
};
tif.MergeFieldInfo(tiffFieldInfo, tiffFieldInfo.Length);
if (m_parentExtender != null)
m_parentExtender(tif);
}
public static void Main(string[] args)
{
// Register the extender callback
// It's a good idea to keep track of the previous tag extender (if any) so that we can call it
// from our extender allowing a chain of customizations to take effect.
m_parentExtender = Tiff.SetTagExtender(TagExtender);
string destFile = #"d:\00000641(tiffed).tif";
File.Copy(#"d:\00000641.tif", destFile);
//Console.WriteLine("Hello World!");
// TODO: Implement Functionality Here
using (Tiff image = Tiff.Open(destFile, "a"))
{
// we should rewind to first directory (first image) because of append mode
image.SetDirectory(0);
// set the custom tag
string value = "<GDALMetadata>\n<Item name=\"IMG_GUID\">" +
"817C0168-0688-45CD-B799-CF8C4DE9AB2B</Item>\n<Item" +
" name=\"LAYER_TYPE\" sample=\"0\">athematic</Item>\n</GDALMetadata>";
image.SetField(TIFFTAG_GDAL_METADATA, value);
// rewrites directory saving new tag
image.CheckpointDirectory();
}
// restore previous tag extender
Tiff.SetTagExtender(m_parentExtender);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
After opening i see mostly blank white image or multiple black and white lines instead of text that have been written there (i don't need to read\write tags to produce this behavior). I noticed this happens when image already has a custom tag (console window alerts about it) or one of tags have got 'bad value' (console window in this case says 'vsetfield:%pathToTiffFile%: bad value 0 for "%TagName%" tag').
Original image: http://dl.dropbox.com/u/1476402/00000641.tif
Image after LibTiff.NET: http://dl.dropbox.com/u/1476402/00000641%28tiffed%29.tif
I would be grateful for any help provided.
You probably should not use CheckpointDirectory method for files opened in append mode. Try using RewriteDirectory method instead.
It will rewrite the directory, but instead of place it at it's old
location (as WriteDirectory() would) it will place them at the end of
the file, correcting the pointer from the preceeding directory or file
header to point to it's new location. This is particularly important
in cases where the size of the directory and pointed to data has
grown, so it won’t fit in the space available at the old location.
Note that this will result in the loss of the previously used
directory space.