Skip to content

Instantly share code, notes, and snippets.

@dcatanzaro
Last active September 1, 2024 22:06
Show Gist options
  • Save dcatanzaro/a5c4206fbf222a485ef5a006b2c3630f to your computer and use it in GitHub Desktop.
Save dcatanzaro/a5c4206fbf222a485ef5a006b2c3630f to your computer and use it in GitHub Desktop.

Revisions

  1. dcatanzaro revised this gist Sep 22, 2020. 1 changed file with 12 additions and 531 deletions.
    543 changes: 12 additions & 531 deletions widget-html-css-scriptable-jobs.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    //Twitter: https://twitter.com/DamianCatanzaro

    const html = "<html> \
    <head> \
    <style> \
    @@ -27,537 +29,14 @@ const html = "<html> \
    </body> \
    </html>";

    const js = `(function (global) {
    "use strict";
    var util = newUtil();
    // Default impl options
    var defaultOptions = {
    // Default is to fail on error, no placeholder
    imagePlaceholder: undefined,
    // Default cache bust is false, it will use the cache
    cacheBust: false,
    };
    var domtoimage = {
    toSvg: toSvg,
    toPng: toPng,
    toJpeg: toJpeg,
    toBlob: toBlob,
    toPixelData: toPixelData,
    impl: {
    options: {},
    util: util,
    },
    };
    if (typeof module !== "undefined") module.exports = domtoimage;
    else global.domtoimage = domtoimage;
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options
    * @param {Function} options.filter - Should return true if passed node should be included in the output
    * (excluding node means excluding it's children as well). Not called on the root node.
    * @param {String} options.bgcolor - color for the background, any valid CSS color value.
    * @param {Number} options.width - width to be applied to node before rendering.
    * @param {Number} options.height - height to be applied to node before rendering.
    * @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
    * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
    defaults to 1.0.
    * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
    * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
    * @return {Promise} - A promise that is fulfilled with a SVG image data URL
    * */
    function toSvg(node, options) {
    options = options || {};
    copyOptions(options);
    return (
    Promise.resolve(node)
    .then(function (node) {
    return cloneNode(node, options.filter, true);
    })
    //.then(embedFonts)
    //.then(inlineImages)
    .then(applyOptions)
    .then(function (clone) {
    return makeSvgDataUri(
    clone,
    options.width || util.width(node),
    options.height || util.height(node)
    );
    })
    );
    function applyOptions(clone) {
    if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
    if (options.width) clone.style.width = options.width + "px";
    if (options.height) clone.style.height = options.height + "px";
    if (options.style)
    Object.keys(options.style).forEach(function (property) {
    clone.style[property] = options.style[property];
    });
    return clone;
    }
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
    * */
    function toPixelData(node, options) {
    return draw(node, options || {}).then(function (canvas) {
    return canvas
    .getContext("2d")
    .getImageData(0, 0, util.width(node), util.height(node)).data;
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a PNG image data URL
    * */
    function toPng(node, options) {
    return draw(node, options || {}).then(function (canvas) {
    return canvas.toDataURL();
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a JPEG image data URL
    * */
    function toJpeg(node, options) {
    options = options || {};
    return draw(node, options).then(function (canvas) {
    return canvas.toDataURL("image/jpeg", options.quality || 1.0);
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a PNG image blob
    * */
    function toBlob(node, options) {
    return draw(node, options || {}).then(util.canvasToBlob);
    }
    function copyOptions(options) {
    // Copy options to impl options for use in impl
    if (typeof options.imagePlaceholder === "undefined") {
    domtoimage.impl.options.imagePlaceholder =
    defaultOptions.imagePlaceholder;
    } else {
    domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
    }
    if (typeof options.cacheBust === "undefined") {
    domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
    } else {
    domtoimage.impl.options.cacheBust = options.cacheBust;
    }
    }
    function draw(domNode, options) {
    return toSvg(domNode, options)
    .then(util.makeImage)
    .then(util.delay(100))
    .then(function (image) {
    var canvas = newCanvas(domNode);
    canvas.getContext("2d").drawImage(image, 0, 0);
    return canvas;
    });
    function newCanvas(domNode) {
    var canvas = document.createElement("canvas");
    canvas.width = options.width || util.width(domNode);
    canvas.height = options.height || util.height(domNode);
    if (options.bgcolor) {
    var ctx = canvas.getContext("2d");
    ctx.fillStyle = options.bgcolor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
    return canvas;
    }
    }
    function cloneNode(node, filter, root) {
    if (!root && filter && !filter(node)) return Promise.resolve();
    return Promise.resolve(node)
    .then(makeNodeCopy)
    .then(function (clone) {
    return cloneChildren(node, clone, filter);
    })
    .then(function (clone) {
    return processClone(node, clone);
    });
    function makeNodeCopy(node) {
    if (node instanceof HTMLCanvasElement)
    return util.makeImage(node.toDataURL());
    return node.cloneNode(false);
    }
    function cloneChildren(original, clone, filter) {
    var children = original.childNodes;
    if (children.length === 0) return Promise.resolve(clone);
    return cloneChildrenInOrder(
    clone,
    util.asArray(children),
    filter
    ).then(function () {
    return clone;
    });
    function cloneChildrenInOrder(parent, children, filter) {
    var done = Promise.resolve();
    children.forEach(function (child) {
    done = done
    .then(function () {
    return cloneNode(child, filter);
    })
    .then(function (childClone) {
    if (childClone) parent.appendChild(childClone);
    });
    });
    return done;
    }
    }
    function processClone(original, clone) {
    if (!(clone instanceof Element)) return clone;
    return Promise.resolve()
    .then(cloneStyle)
    .then(clonePseudoElements)
    .then(copyUserInput)
    .then(fixSvg)
    .then(function () {
    return clone;
    });
    function cloneStyle() {
    copyStyle(window.getComputedStyle(original), clone.style);
    function copyStyle(source, target) {
    if (source.cssText) target.cssText = source.cssText;
    else copyProperties(source, target);
    function copyProperties(source, target) {
    util.asArray(source).forEach(function (name) {
    target.setProperty(
    name,
    source.getPropertyValue(name),
    source.getPropertyPriority(name)
    );
    });
    }
    }
    }
    function clonePseudoElements() {
    [":before", ":after"].forEach(function (element) {
    clonePseudoElement(element);
    });
    function clonePseudoElement(element) {
    var style = window.getComputedStyle(original, element);
    var content = style.getPropertyValue("content");
    if (content === "" || content === "none") return;
    var className = util.uid();
    clone.className = clone.className + " " + className;
    var styleElement = document.createElement("style");
    styleElement.appendChild(
    formatPseudoElementStyle(className, element, style)
    );
    clone.appendChild(styleElement);
    function formatPseudoElementStyle(
    className,
    element,
    style
    ) {
    var selector = "." + className + ":" + element;
    var cssText = style.cssText
    ? formatCssText(style)
    : formatCssProperties(style);
    return document.createTextNode(
    selector + "{" + cssText + "}"
    );
    function formatCssText(style) {
    var content = style.getPropertyValue("content");
    return style.cssText + " content: " + content + ";";
    }
    function formatCssProperties(style) {
    return (
    util
    .asArray(style)
    .map(formatProperty)
    .join("; ") + ";"
    );
    function formatProperty(name) {
    return (
    name +
    ": " +
    style.getPropertyValue(name) +
    (style.getPropertyPriority(name)
    ? " !important"
    : "")
    );
    }
    }
    }
    }
    }
    function copyUserInput() {
    if (original instanceof HTMLTextAreaElement)
    clone.innerHTML = original.value;
    if (original instanceof HTMLInputElement)
    clone.setAttribute("value", original.value);
    }
    function fixSvg() {
    if (!(clone instanceof SVGElement)) return;
    clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    if (!(clone instanceof SVGRectElement)) return;
    ["width", "height"].forEach(function (attribute) {
    var value = clone.getAttribute(attribute);
    if (!value) return;
    clone.style.setProperty(attribute, value);
    });
    }
    }
    }
    function embedFonts(node) {
    return fontFaces.resolveAll().then(function (cssText) {
    var styleNode = document.createElement("style");
    node.appendChild(styleNode);
    styleNode.appendChild(document.createTextNode(cssText));
    return node;
    });
    }
    function inlineImages(node) {
    return images.inlineAll(node).then(function () {
    return node;
    });
    }
    function makeSvgDataUri(node, width, height) {
    return Promise.resolve(node)
    .then(function (node) {
    node.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
    return new XMLSerializer().serializeToString(node);
    })
    .then(util.escapeXhtml)
    .then(function (xhtml) {
    return (
    '<foreignObject x="0" y="0" width="100%" height="100%">' +
    xhtml +
    "</foreignObject>"
    );
    })
    .then(function (foreignObject) {
    return (
    '<svg xmlns="http://www.w3.org/2000/svg" width="' +
    width +
    '" height="' +
    height +
    '">' +
    foreignObject +
    "</svg>"
    );
    })
    .then(function (svg) {
    return "data:image/svg+xml;charset=utf-8," + svg;
    });
    }
    function newUtil() {
    return {
    escape: escape,
    parseExtension: parseExtension,
    mimeType: mimeType,
    dataAsUrl: dataAsUrl,
    isDataUrl: isDataUrl,
    canvasToBlob: canvasToBlob,
    resolveUrl: resolveUrl,
    getAndEncode: getAndEncode,
    uid: uid(),
    delay: delay,
    asArray: asArray,
    escapeXhtml: escapeXhtml,
    makeImage: makeImage,
    width: width,
    height: height,
    };
    function mimes() {
    /*
    * Only WOFF and EOT mime types for fonts are 'real'
    * see http://www.iana.org/assignments/media-types/media-types.xhtml
    */
    var WOFF = "application/font-woff";
    var JPEG = "image/jpeg";
    return {
    woff: WOFF,
    woff2: WOFF,
    ttf: "application/font-truetype",
    eot: "application/vnd.ms-fontobject",
    png: "image/png",
    jpg: JPEG,
    jpeg: JPEG,
    gif: "image/gif",
    tiff: "image/tiff",
    svg: "image/svg+xml",
    };
    }
    function parseExtension(url) {
    return "png";
    }
    function mimeType(url) {
    var extension = parseExtension(url).toLowerCase();
    return mimes()[extension] || "";
    }
    function isDataUrl(url) {
    return url.search(/^(data:)/) !== -1;
    }
    function toBlob(canvas) {
    return new Promise(function (resolve) {
    var binaryString = window.atob(
    canvas.toDataURL().split(",")[1]
    );
    var length = binaryString.length;
    var binaryArray = new Uint8Array(length);
    for (var i = 0; i < length; i++)
    binaryArray[i] = binaryString.charCodeAt(i);
    resolve(
    new Blob([binaryArray], {
    type: "image/png",
    })
    );
    });
    }
    function canvasToBlob(canvas) {
    if (canvas.toBlob)
    return new Promise(function (resolve) {
    canvas.toBlob(resolve);
    });
    return toBlob(canvas);
    }
    function resolveUrl(url, baseUrl) {
    var doc = document.implementation.createHTMLDocument();
    var base = doc.createElement("base");
    doc.head.appendChild(base);
    var a = doc.createElement("a");
    doc.body.appendChild(a);
    base.href = baseUrl;
    a.href = url;
    return a.href;
    }
    function uid() {
    var index = 0;
    return function () {
    return "u" + fourRandomChars() + index++;
    function fourRandomChars() {
    /* see http://stackoverflow.com/a/6248722/2519373 */
    return (
    "0000" +
    ((Math.random() * Math.pow(36, 4)) << 0).toString(36)
    ).slice(-4);
    }
    };
    }
    function makeImage(uri) {
    return new Promise(function (resolve, reject) {
    var image = new Image();
    image.onload = function () {
    resolve(image);
    };
    image.onerror = reject;
    image.src = uri;
    });
    }
    function getAndEncode(url) {
    var TIMEOUT = 30000;
    return new Promise(function (resolve) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = done;
    request.ontimeout = timeout;
    request.responseType = "blob";
    request.timeout = TIMEOUT;
    request.open("GET", url, true);
    request.send();
    var placeholder;
    if (domtoimage.impl.options.imagePlaceholder) {
    var split = domtoimage.impl.options.imagePlaceholder.split(
    /,/
    );
    if (split && split[1]) {
    placeholder = split[1];
    }
    }
    function done() {
    if (request.readyState !== 4) return;
    if (request.status !== 200) {
    if (placeholder) {
    resolve(placeholder);
    } else {
    fail(
    "cannot fetch resource: " +
    url +
    ", status: " +
    request.status
    );
    }
    return;
    }
    var encoder = new FileReader();
    encoder.onloadend = function () {
    var content = encoder.result.split(/,/)[1];
    resolve(content);
    };
    encoder.readAsDataURL(request.response);
    }
    function timeout() {
    if (placeholder) {
    resolve(placeholder);
    } else {
    fail(
    "timeout of " +
    TIMEOUT +
    "ms occured while fetching resource: " +
    url
    );
    }
    }
    function fail(message) {
    console.error(message);
    resolve("");
    }
    });
    }
    function dataAsUrl(content, type) {
    return "data:" + type + ";base64," + content;
    }
    function escape(string) {
    return "";
    }
    function delay(ms) {
    return function (arg) {
    return new Promise(function (resolve) {
    setTimeout(function () {
    resolve(arg);
    }, ms);
    });
    };
    }
    function asArray(arrayLike) {
    var array = [];
    var length = arrayLike.length;
    for (var i = 0; i < length; i++) array.push(arrayLike[i]);
    return array;
    }
    function escapeXhtml(string) {
    return string;
    }
    function width(node) {
    var leftBorder = px(node, "border-left-width");
    var rightBorder = px(node, "border-right-width");
    return node.scrollWidth + leftBorder + rightBorder;
    }
    function height(node) {
    var topBorder = px(node, "border-top-width");
    var bottomBorder = px(node, "border-bottom-width");
    return node.scrollHeight + topBorder + bottomBorder;
    }
    function px(node, styleProperty) {
    var value = window
    .getComputedStyle(node)
    .getPropertyValue(styleProperty);
    return parseFloat(value.replace("px", ""));
    }
    }
    })(this);
    const js = `!function(t){"use strict";var g={parseExtension:function(t){return"png"},canvasToBlob:function(e){return e.toBlob?new Promise(function(t){e.toBlob(t)}):function(i){return new Promise(function(t){for(var e=window.atob(i.toDataURL().split(",")[1]),n=e.length,r=new Uint8Array(n),o=0;o<n;o++)r[o]=e.charCodeAt(o);t(new Blob([r],{type:"image/png"}))})}(e)},uid:function(){var t=0;return function(){return"u"+("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)+t++}}(),delay:function(n){return function(e){return new Promise(function(t){setTimeout(function(){t(e)},n)})}},asArray:function(t){for(var e=[],n=t.length,r=0;r<n;r++)e.push(t[r]);return e},escapeXhtml:function(t){return t},makeImage:function(r){return new Promise(function(t,e){var n=new Image;n.onload=function(){t(n)},n.onerror=e,n.src=r})},width:function(t){var e=r(t,"border-left-width"),n=r(t,"border-right-width");return t.scrollWidth+e+n},height:function(t){var e=r(t,"border-top-width"),n=r(t,"border-bottom-width");return t.scrollHeight+e+n}};function r(t,e){var n=window.getComputedStyle(t).getPropertyValue(e);return parseFloat(n.replace("px",""))}var e={imagePlaceholder:void 0,cacheBust:!1},n={toSvg:o,toBlob:function(t,e){return function(n,r){return o(n,r).then(g.makeImage).then(g.delay(100)).then(function(t){var e=function(t){var e=document.createElement("canvas");{var n;e.width=r.width||g.width(t),e.height=r.height||g.height(t),r.bgcolor&&((n=e.getContext("2d")).fillStyle=r.bgcolor,n.fillRect(0,0,e.width,e.height))}return e}(n);return e.getContext("2d").drawImage(t,0,0),e})}(t,e||{}).then(g.canvasToBlob)},impl:{options:{},util:g}};function o(o,i){return function(t){void 0===t.imagePlaceholder?n.impl.options.imagePlaceholder=e.imagePlaceholder:n.impl.options.imagePlaceholder=t.imagePlaceholder;void 0===t.cacheBust?n.impl.options.cacheBust=e.cacheBust:n.impl.options.cacheBust=t.cacheBust}(i=i||{}),Promise.resolve(o).then(function(t){return function i(e,n,t){if(!t&&n&&!n(e))return Promise.resolve();return Promise.resolve(e).then(r).then(function(t){return o(e,t,n)}).then(function(t){return u(e,t)});function r(t){return t instanceof HTMLCanvasElement?g.makeImage(t.toDataURL()):t.cloneNode(!1)}function o(t,e,n){var r=t.childNodes;return 0===r.length?Promise.resolve(e):o(e,g.asArray(r),n).then(function(){return e});function o(e,t,n){var r=Promise.resolve();return t.forEach(function(t){r=r.then(function(){return i(t,n)}).then(function(t){t&&e.appendChild(t)})}),r}}function u(f,d){return d instanceof Element?Promise.resolve().then(t).then(e).then(n).then(r).then(function(){return d}):d;function t(){function t(t,e){var n,r;t.cssText?e.cssText=t.cssText:(n=t,r=e,g.asArray(n).forEach(function(t){r.setProperty(t,n.getPropertyValue(t),n.getPropertyPriority(t))}))}t(window.getComputedStyle(f),d.style)}function e(){function e(t){var e,n,r,o,i,u,c,a,h=window.getComputedStyle(f,t),l=h.getPropertyValue("content");function s(t){return t+": "+r.getPropertyValue(t)+(r.getPropertyPriority(t)?" !important":"")}""!==l&&"none"!==l&&(e=g.uid(),d.className=d.className+" "+e,(n=document.createElement("style")).appendChild((c="."+e+":"+t,a=(u=h).cssText?(i=(o=o=u).getPropertyValue("content"),o.cssText+" content: "+i+";"):(r=u,g.asArray(r).map(s).join("; ")+";"),document.createTextNode(c+"{"+a+"}"))),d.appendChild(n))}[":before",":after"].forEach(function(t){e(t)})}function n(){f instanceof HTMLTextAreaElement&&(d.innerHTML=f.value),f instanceof HTMLInputElement&&d.setAttribute("value",f.value)}function r(){d instanceof SVGElement&&(d.setAttribute("xmlns","http://www.w3.org/2000/svg"),d instanceof SVGRectElement&&["width","height"].forEach(function(t){var e=d.getAttribute(t);e&&d.style.setProperty(t,e)}))}}}(t,i.filter,!0)}).then(function(e){i.bgcolor&&(e.style.backgroundColor=i.bgcolor);i.width&&(e.style.width=i.width+"px");i.height&&(e.style.height=i.height+"px");i.style&&Object.keys(i.style).forEach(function(t){e.style[t]=i.style[t]});return e}).then(function(t){return e=t,n=i.width||g.width(o),r=i.height||g.height(o),Promise.resolve(e).then(function(t){return t.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(t)}).then(g.escapeXhtml).then(function(t){return'<foreignObject x="0" y="0" width="100%" height="100%">'+t+"</foreignObject>"}).then(function(t){return'<svg xmlns="http://www.w3.org/2000/svg" width="'+n+'" height="'+r+'">'+t+"</svg>"}).then(function(t){return"data:image/svg+xml;charset=utf-8,"+t});var e,n,r})}"undefined"!=typeof module?module.exports=n:t.domtoimage=n}(this);
    const iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    const iframedoc = iframe.contentDocument || iframe.contentWindow.document;
    iframedoc.body.innerHTML = "${html}";
    const blobToBase64 = (blob) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    @@ -567,18 +46,20 @@ const blobToBase64 = (blob) => {
    };
    });
    };
    setTimeout(function() {
    domtoimage.toBlob(iframedoc.body).then(async (blob) => {
    setTimeout(function () {
    domtoimage.toBlob(iframedoc.body).then(async (blob) => {
    completion(await blobToBase64(blob));
    });
    }, 1)`;
    }, 1);
    `;

    const wv = new WebView();
    const test = await wv.evaluateJavaScript(js, true);
    const base64Image = await wv.evaluateJavaScript(js, true);

    const widget = new ListWidget();

    const imgReqBq = await new Request(test);
    const imgReqBq = await new Request(base64Image);
    const imgBq = await imgReqBq.loadImage();

    widget.backgroundImage = imgBq;
  2. dcatanzaro revised this gist Sep 22, 2020. 1 changed file with 30 additions and 113 deletions.
    143 changes: 30 additions & 113 deletions widget-html-css-scriptable-jobs.js
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,42 @@
    const html = "<html> \
    <head> \
    <style> \
    body { \
    margin: 0; \
    background-color: cyan; \
    width: 500px; \
    height: 500px; \
    } \
    header { \
    width: 100%; \
    height: 100px; \
    background-color: red; \
    } \
    .container { \
    width: 100%; \
    height: 400px; \
    background-color: white; \
    text-align: center; \
    font-size: 50px; \
    } \
    </style> \
    </head> \
    <body> \
    <header></header> \
    <div class='container'>Widget construido con HTML y CSS, en iOS, si. En tu cara Jobs.</div> \
    </body> \
    </html>";

    const js = `(function (global) {
    "use strict";
    var util = newUtil();
    // Default impl options
    var defaultOptions = {
    // Default is to fail on error, no placeholder
    imagePlaceholder: undefined,
    // Default cache bust is false, it will use the cache
    cacheBust: false,
    };
    var domtoimage = {
    toSvg: toSvg,
    toPng: toPng,
    @@ -22,10 +48,8 @@ const js = `(function (global) {
    util: util,
    },
    };
    if (typeof module !== "undefined") module.exports = domtoimage;
    else global.domtoimage = domtoimage;
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options
    @@ -60,22 +84,17 @@ const js = `(function (global) {
    );
    })
    );
    function applyOptions(clone) {
    if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
    if (options.width) clone.style.width = options.width + "px";
    if (options.height) clone.style.height = options.height + "px";
    if (options.style)
    Object.keys(options.style).forEach(function (property) {
    clone.style[property] = options.style[property];
    });
    return clone;
    }
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    @@ -88,7 +107,6 @@ const js = `(function (global) {
    .getImageData(0, 0, util.width(node), util.height(node)).data;
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    @@ -99,7 +117,6 @@ const js = `(function (global) {
    return canvas.toDataURL();
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    @@ -111,7 +128,6 @@ const js = `(function (global) {
    return canvas.toDataURL("image/jpeg", options.quality || 1.0);
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    @@ -120,7 +136,6 @@ const js = `(function (global) {
    function toBlob(node, options) {
    return draw(node, options || {}).then(util.canvasToBlob);
    }
    function copyOptions(options) {
    // Copy options to impl options for use in impl
    if (typeof options.imagePlaceholder === "undefined") {
    @@ -129,14 +144,12 @@ const js = `(function (global) {
    } else {
    domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
    }
    if (typeof options.cacheBust === "undefined") {
    domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
    } else {
    domtoimage.impl.options.cacheBust = options.cacheBust;
    }
    }
    function draw(domNode, options) {
    return toSvg(domNode, options)
    .then(util.makeImage)
    @@ -146,25 +159,20 @@ const js = `(function (global) {
    canvas.getContext("2d").drawImage(image, 0, 0);
    return canvas;
    });
    function newCanvas(domNode) {
    var canvas = document.createElement("canvas");
    canvas.width = options.width || util.width(domNode);
    canvas.height = options.height || util.height(domNode);
    if (options.bgcolor) {
    var ctx = canvas.getContext("2d");
    ctx.fillStyle = options.bgcolor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
    return canvas;
    }
    }
    function cloneNode(node, filter, root) {
    if (!root && filter && !filter(node)) return Promise.resolve();
    return Promise.resolve(node)
    .then(makeNodeCopy)
    .then(function (clone) {
    @@ -173,25 +181,21 @@ const js = `(function (global) {
    .then(function (clone) {
    return processClone(node, clone);
    });
    function makeNodeCopy(node) {
    if (node instanceof HTMLCanvasElement)
    return util.makeImage(node.toDataURL());
    return node.cloneNode(false);
    }
    function cloneChildren(original, clone, filter) {
    var children = original.childNodes;
    if (children.length === 0) return Promise.resolve(clone);
    return cloneChildrenInOrder(
    clone,
    util.asArray(children),
    filter
    ).then(function () {
    return clone;
    });
    function cloneChildrenInOrder(parent, children, filter) {
    var done = Promise.resolve();
    children.forEach(function (child) {
    @@ -206,10 +210,8 @@ const js = `(function (global) {
    return done;
    }
    }
    function processClone(original, clone) {
    if (!(clone instanceof Element)) return clone;
    return Promise.resolve()
    .then(cloneStyle)
    .then(clonePseudoElements)
    @@ -218,14 +220,11 @@ const js = `(function (global) {
    .then(function () {
    return clone;
    });
    function cloneStyle() {
    copyStyle(window.getComputedStyle(original), clone.style);
    function copyStyle(source, target) {
    if (source.cssText) target.cssText = source.cssText;
    else copyProperties(source, target);
    function copyProperties(source, target) {
    util.asArray(source).forEach(function (name) {
    target.setProperty(
    @@ -237,26 +236,21 @@ const js = `(function (global) {
    }
    }
    }
    function clonePseudoElements() {
    [":before", ":after"].forEach(function (element) {
    clonePseudoElement(element);
    });
    function clonePseudoElement(element) {
    var style = window.getComputedStyle(original, element);
    var content = style.getPropertyValue("content");
    if (content === "" || content === "none") return;
    var className = util.uid();
    clone.className = clone.className + " " + className;
    var styleElement = document.createElement("style");
    styleElement.appendChild(
    formatPseudoElementStyle(className, element, style)
    );
    clone.appendChild(styleElement);
    function formatPseudoElementStyle(
    className,
    element,
    @@ -269,20 +263,17 @@ const js = `(function (global) {
    return document.createTextNode(
    selector + "{" + cssText + "}"
    );
    function formatCssText(style) {
    var content = style.getPropertyValue("content");
    return style.cssText + " content: " + content + ";";
    }
    function formatCssProperties(style) {
    return (
    util
    .asArray(style)
    .map(formatProperty)
    .join("; ") + ";"
    );
    function formatProperty(name) {
    return (
    name +
    @@ -297,29 +288,24 @@ const js = `(function (global) {
    }
    }
    }
    function copyUserInput() {
    if (original instanceof HTMLTextAreaElement)
    clone.innerHTML = original.value;
    if (original instanceof HTMLInputElement)
    clone.setAttribute("value", original.value);
    }
    function fixSvg() {
    if (!(clone instanceof SVGElement)) return;
    clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    if (!(clone instanceof SVGRectElement)) return;
    ["width", "height"].forEach(function (attribute) {
    var value = clone.getAttribute(attribute);
    if (!value) return;
    clone.style.setProperty(attribute, value);
    });
    }
    }
    }
    function embedFonts(node) {
    return fontFaces.resolveAll().then(function (cssText) {
    var styleNode = document.createElement("style");
    @@ -328,13 +314,11 @@ const js = `(function (global) {
    return node;
    });
    }
    function inlineImages(node) {
    return images.inlineAll(node).then(function () {
    return node;
    });
    }
    function makeSvgDataUri(node, width, height) {
    return Promise.resolve(node)
    .then(function (node) {
    @@ -364,7 +348,6 @@ const js = `(function (global) {
    return "data:image/svg+xml;charset=utf-8," + svg;
    });
    }
    function newUtil() {
    return {
    escape: escape,
    @@ -383,15 +366,13 @@ const js = `(function (global) {
    width: width,
    height: height,
    };
    function mimes() {
    /*
    * Only WOFF and EOT mime types for fonts are 'real'
    * see http://www.iana.org/assignments/media-types/media-types.xhtml
    */
    var WOFF = "application/font-woff";
    var JPEG = "image/jpeg";
    return {
    woff: WOFF,
    woff2: WOFF,
    @@ -405,48 +386,39 @@ const js = `(function (global) {
    svg: "image/svg+xml",
    };
    }
    function parseExtension(url) {
    return "png";
    }
    function mimeType(url) {
    var extension = parseExtension(url).toLowerCase();
    return mimes()[extension] || "";
    }
    function isDataUrl(url) {
    return url.search(/^(data:)/) !== -1;
    }
    function toBlob(canvas) {
    return new Promise(function (resolve) {
    var binaryString = window.atob(
    canvas.toDataURL().split(",")[1]
    );
    var length = binaryString.length;
    var binaryArray = new Uint8Array(length);
    for (var i = 0; i < length; i++)
    binaryArray[i] = binaryString.charCodeAt(i);
    resolve(
    new Blob([binaryArray], {
    type: "image/png",
    })
    );
    });
    }
    function canvasToBlob(canvas) {
    if (canvas.toBlob)
    return new Promise(function (resolve) {
    canvas.toBlob(resolve);
    });
    return toBlob(canvas);
    }
    function resolveUrl(url, baseUrl) {
    var doc = document.implementation.createHTMLDocument();
    var base = doc.createElement("base");
    @@ -457,13 +429,10 @@ const js = `(function (global) {
    a.href = url;
    return a.href;
    }
    function uid() {
    var index = 0;
    return function () {
    return "u" + fourRandomChars() + index++;
    function fourRandomChars() {
    /* see http://stackoverflow.com/a/6248722/2519373 */
    return (
    @@ -473,7 +442,6 @@ const js = `(function (global) {
    }
    };
    }
    function makeImage(uri) {
    return new Promise(function (resolve, reject) {
    var image = new Image();
    @@ -484,20 +452,16 @@ const js = `(function (global) {
    image.src = uri;
    });
    }
    function getAndEncode(url) {
    var TIMEOUT = 30000;
    return new Promise(function (resolve) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = done;
    request.ontimeout = timeout;
    request.responseType = "blob";
    request.timeout = TIMEOUT;
    request.open("GET", url, true);
    request.send();
    var placeholder;
    if (domtoimage.impl.options.imagePlaceholder) {
    var split = domtoimage.impl.options.imagePlaceholder.split(
    @@ -507,10 +471,8 @@ const js = `(function (global) {
    placeholder = split[1];
    }
    }
    function done() {
    if (request.readyState !== 4) return;
    if (request.status !== 200) {
    if (placeholder) {
    resolve(placeholder);
    @@ -522,18 +484,15 @@ const js = `(function (global) {
    request.status
    );
    }
    return;
    }
    var encoder = new FileReader();
    encoder.onloadend = function () {
    var content = encoder.result.split(/,/)[1];
    resolve(content);
    };
    encoder.readAsDataURL(request.response);
    }
    function timeout() {
    if (placeholder) {
    resolve(placeholder);
    @@ -546,22 +505,18 @@ const js = `(function (global) {
    );
    }
    }
    function fail(message) {
    console.error(message);
    resolve("");
    }
    });
    }
    function dataAsUrl(content, type) {
    return "data:" + type + ";base64," + content;
    }
    function escape(string) {
    return "";
    }
    function delay(ms) {
    return function (arg) {
    return new Promise(function (resolve) {
    @@ -571,30 +526,25 @@ const js = `(function (global) {
    });
    };
    }
    function asArray(arrayLike) {
    var array = [];
    var length = arrayLike.length;
    for (var i = 0; i < length; i++) array.push(arrayLike[i]);
    return array;
    }
    function escapeXhtml(string) {
    return string;
    }
    function width(node) {
    var leftBorder = px(node, "border-left-width");
    var rightBorder = px(node, "border-right-width");
    return node.scrollWidth + leftBorder + rightBorder;
    }
    function height(node) {
    var topBorder = px(node, "border-top-width");
    var bottomBorder = px(node, "border-bottom-width");
    return node.scrollHeight + topBorder + bottomBorder;
    }
    function px(node, styleProperty) {
    var value = window
    .getComputedStyle(node)
    @@ -604,41 +554,10 @@ const js = `(function (global) {
    }
    })(this);
    const html = "<html> \
    <head> \
    <style> \
    body { \
    margin: 0; \
    background-color: cyan; \
    width: 500px; \
    height: 500px; \
    } \
    header { \
    width: 100%; \
    height: 100px; \
    background-color: red; \
    } \
    .container { \
    width: 100%; \
    height: 400px; \
    background-color: white; \
    text-align: center; \
    font-size: 50px; \
    } \
    </style> \
    </head> \
    <body> \
    <header></header> \
    <div class='container'>Widget construido con HTML y CSS, en iOS, si. En tu cara Jobs.</div> \
    </body> \
    </html>";
    const iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    const iframedoc = iframe.contentDocument || iframe.contentWindow.document;
    iframedoc.body.innerHTML = html;
    iframedoc.body.innerHTML = "${html}";
    const blobToBase64 = (blob) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    @@ -648,7 +567,6 @@ const blobToBase64 = (blob) => {
    };
    });
    };
    setTimeout(function() {
    domtoimage.toBlob(iframedoc.body).then(async (blob) => {
    completion(await blobToBase64(blob));
    @@ -657,7 +575,6 @@ setTimeout(function() {

    const wv = new WebView();
    const test = await wv.evaluateJavaScript(js, true);
    log(test);

    const widget = new ListWidget();

  3. dcatanzaro created this gist Sep 22, 2020.
    674 changes: 674 additions & 0 deletions widget-html-css-scriptable-jobs.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,674 @@
    const js = `(function (global) {
    "use strict";
    var util = newUtil();
    // Default impl options
    var defaultOptions = {
    // Default is to fail on error, no placeholder
    imagePlaceholder: undefined,
    // Default cache bust is false, it will use the cache
    cacheBust: false,
    };
    var domtoimage = {
    toSvg: toSvg,
    toPng: toPng,
    toJpeg: toJpeg,
    toBlob: toBlob,
    toPixelData: toPixelData,
    impl: {
    options: {},
    util: util,
    },
    };
    if (typeof module !== "undefined") module.exports = domtoimage;
    else global.domtoimage = domtoimage;
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options
    * @param {Function} options.filter - Should return true if passed node should be included in the output
    * (excluding node means excluding it's children as well). Not called on the root node.
    * @param {String} options.bgcolor - color for the background, any valid CSS color value.
    * @param {Number} options.width - width to be applied to node before rendering.
    * @param {Number} options.height - height to be applied to node before rendering.
    * @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
    * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
    defaults to 1.0.
    * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
    * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
    * @return {Promise} - A promise that is fulfilled with a SVG image data URL
    * */
    function toSvg(node, options) {
    options = options || {};
    copyOptions(options);
    return (
    Promise.resolve(node)
    .then(function (node) {
    return cloneNode(node, options.filter, true);
    })
    //.then(embedFonts)
    //.then(inlineImages)
    .then(applyOptions)
    .then(function (clone) {
    return makeSvgDataUri(
    clone,
    options.width || util.width(node),
    options.height || util.height(node)
    );
    })
    );
    function applyOptions(clone) {
    if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
    if (options.width) clone.style.width = options.width + "px";
    if (options.height) clone.style.height = options.height + "px";
    if (options.style)
    Object.keys(options.style).forEach(function (property) {
    clone.style[property] = options.style[property];
    });
    return clone;
    }
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
    * */
    function toPixelData(node, options) {
    return draw(node, options || {}).then(function (canvas) {
    return canvas
    .getContext("2d")
    .getImageData(0, 0, util.width(node), util.height(node)).data;
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a PNG image data URL
    * */
    function toPng(node, options) {
    return draw(node, options || {}).then(function (canvas) {
    return canvas.toDataURL();
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a JPEG image data URL
    * */
    function toJpeg(node, options) {
    options = options || {};
    return draw(node, options).then(function (canvas) {
    return canvas.toDataURL("image/jpeg", options.quality || 1.0);
    });
    }
    /**
    * @param {Node} node - The DOM Node object to render
    * @param {Object} options - Rendering options, @see {@link toSvg}
    * @return {Promise} - A promise that is fulfilled with a PNG image blob
    * */
    function toBlob(node, options) {
    return draw(node, options || {}).then(util.canvasToBlob);
    }
    function copyOptions(options) {
    // Copy options to impl options for use in impl
    if (typeof options.imagePlaceholder === "undefined") {
    domtoimage.impl.options.imagePlaceholder =
    defaultOptions.imagePlaceholder;
    } else {
    domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
    }
    if (typeof options.cacheBust === "undefined") {
    domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
    } else {
    domtoimage.impl.options.cacheBust = options.cacheBust;
    }
    }
    function draw(domNode, options) {
    return toSvg(domNode, options)
    .then(util.makeImage)
    .then(util.delay(100))
    .then(function (image) {
    var canvas = newCanvas(domNode);
    canvas.getContext("2d").drawImage(image, 0, 0);
    return canvas;
    });
    function newCanvas(domNode) {
    var canvas = document.createElement("canvas");
    canvas.width = options.width || util.width(domNode);
    canvas.height = options.height || util.height(domNode);
    if (options.bgcolor) {
    var ctx = canvas.getContext("2d");
    ctx.fillStyle = options.bgcolor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
    return canvas;
    }
    }
    function cloneNode(node, filter, root) {
    if (!root && filter && !filter(node)) return Promise.resolve();
    return Promise.resolve(node)
    .then(makeNodeCopy)
    .then(function (clone) {
    return cloneChildren(node, clone, filter);
    })
    .then(function (clone) {
    return processClone(node, clone);
    });
    function makeNodeCopy(node) {
    if (node instanceof HTMLCanvasElement)
    return util.makeImage(node.toDataURL());
    return node.cloneNode(false);
    }
    function cloneChildren(original, clone, filter) {
    var children = original.childNodes;
    if (children.length === 0) return Promise.resolve(clone);
    return cloneChildrenInOrder(
    clone,
    util.asArray(children),
    filter
    ).then(function () {
    return clone;
    });
    function cloneChildrenInOrder(parent, children, filter) {
    var done = Promise.resolve();
    children.forEach(function (child) {
    done = done
    .then(function () {
    return cloneNode(child, filter);
    })
    .then(function (childClone) {
    if (childClone) parent.appendChild(childClone);
    });
    });
    return done;
    }
    }
    function processClone(original, clone) {
    if (!(clone instanceof Element)) return clone;
    return Promise.resolve()
    .then(cloneStyle)
    .then(clonePseudoElements)
    .then(copyUserInput)
    .then(fixSvg)
    .then(function () {
    return clone;
    });
    function cloneStyle() {
    copyStyle(window.getComputedStyle(original), clone.style);
    function copyStyle(source, target) {
    if (source.cssText) target.cssText = source.cssText;
    else copyProperties(source, target);
    function copyProperties(source, target) {
    util.asArray(source).forEach(function (name) {
    target.setProperty(
    name,
    source.getPropertyValue(name),
    source.getPropertyPriority(name)
    );
    });
    }
    }
    }
    function clonePseudoElements() {
    [":before", ":after"].forEach(function (element) {
    clonePseudoElement(element);
    });
    function clonePseudoElement(element) {
    var style = window.getComputedStyle(original, element);
    var content = style.getPropertyValue("content");
    if (content === "" || content === "none") return;
    var className = util.uid();
    clone.className = clone.className + " " + className;
    var styleElement = document.createElement("style");
    styleElement.appendChild(
    formatPseudoElementStyle(className, element, style)
    );
    clone.appendChild(styleElement);
    function formatPseudoElementStyle(
    className,
    element,
    style
    ) {
    var selector = "." + className + ":" + element;
    var cssText = style.cssText
    ? formatCssText(style)
    : formatCssProperties(style);
    return document.createTextNode(
    selector + "{" + cssText + "}"
    );
    function formatCssText(style) {
    var content = style.getPropertyValue("content");
    return style.cssText + " content: " + content + ";";
    }
    function formatCssProperties(style) {
    return (
    util
    .asArray(style)
    .map(formatProperty)
    .join("; ") + ";"
    );
    function formatProperty(name) {
    return (
    name +
    ": " +
    style.getPropertyValue(name) +
    (style.getPropertyPriority(name)
    ? " !important"
    : "")
    );
    }
    }
    }
    }
    }
    function copyUserInput() {
    if (original instanceof HTMLTextAreaElement)
    clone.innerHTML = original.value;
    if (original instanceof HTMLInputElement)
    clone.setAttribute("value", original.value);
    }
    function fixSvg() {
    if (!(clone instanceof SVGElement)) return;
    clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    if (!(clone instanceof SVGRectElement)) return;
    ["width", "height"].forEach(function (attribute) {
    var value = clone.getAttribute(attribute);
    if (!value) return;
    clone.style.setProperty(attribute, value);
    });
    }
    }
    }
    function embedFonts(node) {
    return fontFaces.resolveAll().then(function (cssText) {
    var styleNode = document.createElement("style");
    node.appendChild(styleNode);
    styleNode.appendChild(document.createTextNode(cssText));
    return node;
    });
    }
    function inlineImages(node) {
    return images.inlineAll(node).then(function () {
    return node;
    });
    }
    function makeSvgDataUri(node, width, height) {
    return Promise.resolve(node)
    .then(function (node) {
    node.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
    return new XMLSerializer().serializeToString(node);
    })
    .then(util.escapeXhtml)
    .then(function (xhtml) {
    return (
    '<foreignObject x="0" y="0" width="100%" height="100%">' +
    xhtml +
    "</foreignObject>"
    );
    })
    .then(function (foreignObject) {
    return (
    '<svg xmlns="http://www.w3.org/2000/svg" width="' +
    width +
    '" height="' +
    height +
    '">' +
    foreignObject +
    "</svg>"
    );
    })
    .then(function (svg) {
    return "data:image/svg+xml;charset=utf-8," + svg;
    });
    }
    function newUtil() {
    return {
    escape: escape,
    parseExtension: parseExtension,
    mimeType: mimeType,
    dataAsUrl: dataAsUrl,
    isDataUrl: isDataUrl,
    canvasToBlob: canvasToBlob,
    resolveUrl: resolveUrl,
    getAndEncode: getAndEncode,
    uid: uid(),
    delay: delay,
    asArray: asArray,
    escapeXhtml: escapeXhtml,
    makeImage: makeImage,
    width: width,
    height: height,
    };
    function mimes() {
    /*
    * Only WOFF and EOT mime types for fonts are 'real'
    * see http://www.iana.org/assignments/media-types/media-types.xhtml
    */
    var WOFF = "application/font-woff";
    var JPEG = "image/jpeg";
    return {
    woff: WOFF,
    woff2: WOFF,
    ttf: "application/font-truetype",
    eot: "application/vnd.ms-fontobject",
    png: "image/png",
    jpg: JPEG,
    jpeg: JPEG,
    gif: "image/gif",
    tiff: "image/tiff",
    svg: "image/svg+xml",
    };
    }
    function parseExtension(url) {
    return "png";
    }
    function mimeType(url) {
    var extension = parseExtension(url).toLowerCase();
    return mimes()[extension] || "";
    }
    function isDataUrl(url) {
    return url.search(/^(data:)/) !== -1;
    }
    function toBlob(canvas) {
    return new Promise(function (resolve) {
    var binaryString = window.atob(
    canvas.toDataURL().split(",")[1]
    );
    var length = binaryString.length;
    var binaryArray = new Uint8Array(length);
    for (var i = 0; i < length; i++)
    binaryArray[i] = binaryString.charCodeAt(i);
    resolve(
    new Blob([binaryArray], {
    type: "image/png",
    })
    );
    });
    }
    function canvasToBlob(canvas) {
    if (canvas.toBlob)
    return new Promise(function (resolve) {
    canvas.toBlob(resolve);
    });
    return toBlob(canvas);
    }
    function resolveUrl(url, baseUrl) {
    var doc = document.implementation.createHTMLDocument();
    var base = doc.createElement("base");
    doc.head.appendChild(base);
    var a = doc.createElement("a");
    doc.body.appendChild(a);
    base.href = baseUrl;
    a.href = url;
    return a.href;
    }
    function uid() {
    var index = 0;
    return function () {
    return "u" + fourRandomChars() + index++;
    function fourRandomChars() {
    /* see http://stackoverflow.com/a/6248722/2519373 */
    return (
    "0000" +
    ((Math.random() * Math.pow(36, 4)) << 0).toString(36)
    ).slice(-4);
    }
    };
    }
    function makeImage(uri) {
    return new Promise(function (resolve, reject) {
    var image = new Image();
    image.onload = function () {
    resolve(image);
    };
    image.onerror = reject;
    image.src = uri;
    });
    }
    function getAndEncode(url) {
    var TIMEOUT = 30000;
    return new Promise(function (resolve) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = done;
    request.ontimeout = timeout;
    request.responseType = "blob";
    request.timeout = TIMEOUT;
    request.open("GET", url, true);
    request.send();
    var placeholder;
    if (domtoimage.impl.options.imagePlaceholder) {
    var split = domtoimage.impl.options.imagePlaceholder.split(
    /,/
    );
    if (split && split[1]) {
    placeholder = split[1];
    }
    }
    function done() {
    if (request.readyState !== 4) return;
    if (request.status !== 200) {
    if (placeholder) {
    resolve(placeholder);
    } else {
    fail(
    "cannot fetch resource: " +
    url +
    ", status: " +
    request.status
    );
    }
    return;
    }
    var encoder = new FileReader();
    encoder.onloadend = function () {
    var content = encoder.result.split(/,/)[1];
    resolve(content);
    };
    encoder.readAsDataURL(request.response);
    }
    function timeout() {
    if (placeholder) {
    resolve(placeholder);
    } else {
    fail(
    "timeout of " +
    TIMEOUT +
    "ms occured while fetching resource: " +
    url
    );
    }
    }
    function fail(message) {
    console.error(message);
    resolve("");
    }
    });
    }
    function dataAsUrl(content, type) {
    return "data:" + type + ";base64," + content;
    }
    function escape(string) {
    return "";
    }
    function delay(ms) {
    return function (arg) {
    return new Promise(function (resolve) {
    setTimeout(function () {
    resolve(arg);
    }, ms);
    });
    };
    }
    function asArray(arrayLike) {
    var array = [];
    var length = arrayLike.length;
    for (var i = 0; i < length; i++) array.push(arrayLike[i]);
    return array;
    }
    function escapeXhtml(string) {
    return string;
    }
    function width(node) {
    var leftBorder = px(node, "border-left-width");
    var rightBorder = px(node, "border-right-width");
    return node.scrollWidth + leftBorder + rightBorder;
    }
    function height(node) {
    var topBorder = px(node, "border-top-width");
    var bottomBorder = px(node, "border-bottom-width");
    return node.scrollHeight + topBorder + bottomBorder;
    }
    function px(node, styleProperty) {
    var value = window
    .getComputedStyle(node)
    .getPropertyValue(styleProperty);
    return parseFloat(value.replace("px", ""));
    }
    }
    })(this);
    const html = "<html> \
    <head> \
    <style> \
    body { \
    margin: 0; \
    background-color: cyan; \
    width: 500px; \
    height: 500px; \
    } \
    header { \
    width: 100%; \
    height: 100px; \
    background-color: red; \
    } \
    .container { \
    width: 100%; \
    height: 400px; \
    background-color: white; \
    text-align: center; \
    font-size: 50px; \
    } \
    </style> \
    </head> \
    <body> \
    <header></header> \
    <div class='container'>Widget construido con HTML y CSS, en iOS, si. En tu cara Jobs.</div> \
    </body> \
    </html>";
    const iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    const iframedoc = iframe.contentDocument || iframe.contentWindow.document;
    iframedoc.body.innerHTML = html;
    const blobToBase64 = (blob) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    return new Promise((resolve) => {
    reader.onloadend = () => {
    resolve(reader.result);
    };
    });
    };
    setTimeout(function() {
    domtoimage.toBlob(iframedoc.body).then(async (blob) => {
    completion(await blobToBase64(blob));
    });
    }, 1)`;

    const wv = new WebView();
    const test = await wv.evaluateJavaScript(js, true);
    log(test);

    const widget = new ListWidget();

    const imgReqBq = await new Request(test);
    const imgBq = await imgReqBq.loadImage();

    widget.backgroundImage = imgBq;

    if (config.runsInWidget) {
    Script.setWidget(widget);
    Script.complete();
    } else {
    widget.presentLarge();
    }