├── .gitignore ├── .vscode └── launch.json ├── .vscodeignore ├── README.md ├── changelog.md ├── demo ├── 1.png ├── 2.png ├── 3.png └── usage.gif ├── icon.png ├── jsconfig.json ├── package.json ├── src └── extension.js ├── webview ├── dom2image.js ├── index.html ├── index.js └── vivus.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | *.vsix 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Polacode", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 10 | "stopOnEntry": false 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | demo 3 | *.vsix 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <p> 2 | <h2 align="center">Polacode — Polaroid for your code 📸</h2> 3 | </p> 4 | 5 | <!--  --> 6 |  7 | 8 | ## Why? 9 | 10 | You have spent countless hours finding the perfect [JavaScript grammar](https://marketplace.visualstudio.com/search?term=javascript%20grammar&target=VSCode&category=All%20categories&sortBy=Relevance), matching it with a [sleek-looking VS Code theme](https://marketplace.visualstudio.com/search?target=VSCode&category=Themes&sortBy=Downloads), trying out all the [best programming fonts](https://www.slant.co/topics/67/~best-programming-fonts). 11 | 12 | You take three days porting over [your theme](https://github.com/wesbos/cobalt2-vscode) before starting to use VS Code. 13 | You shell out $200 for [italic cursive html attributes](https://www.typography.com/blog/introducing-operator). 14 | 15 | The code has to look right. 16 | 17 | ## Tips 18 | 19 | - Resize the snippet / container by dragging the lowerright corner 20 | - Use `polacode.target`, `polacode.shadow`, `polacode.transparentBackground` and `polacode.backgroundColor` to control image appearance 21 | 22 | ## Demo 23 | 24 | [Nord](https://github.com/arcticicestudio/nord-visual-studio-code) + [Input Mono](http://input.fontbureau.com) 25 | 26 |  27 | 28 | [Monokai Pro](https://marketplace.visualstudio.com/items?itemName=monokai.theme-monokai-pro-vscode) + [Operator Mono](https://www.typography.com/blog/introducing-operator) 29 | 30 |  31 | 32 | [Material Theme Palenight](https://marketplace.visualstudio.com/items?itemName=Equinusocio.vsc-material-theme) + [Fira Code](https://github.com/tonsky/FiraCode) 33 | 34 |  35 | 36 | ## Credit 37 | 38 | Thanks to [@tsayen](https://github.com/tsayen) for making [dom-to-image](https://github.com/tsayen/dom-to-image), which Polacode is using for generating the images. 39 | 40 | Thanks to [Dawn Labs](https://dawnlabs.io) for making [Carbon](https://carbon.now.sh) that inspired Polacode. 41 | 42 | Many color are taken from the elegant [Nord](https://github.com/arcticicestudio/nord) theme by [@arcticicestudio](https://github.com/arcticicestudio). 43 | 44 | Download button animation is made with [Vivus](https://github.com/maxwellito/vivus). 45 | 46 | ## Contribution 47 | 48 | Contribution is not very welcome. 49 | Please open an issue first so I can stop you from complicating the UX. 50 | 51 | ## License 52 | 53 | MIT -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### 0.3.4 | 2019-09-27 4 | 5 | - Fix Polacode not loading background color correctly. [#125](https://github.com/octref/polacode/issues/125). 6 | 7 | ### 0.3.3 | 2019-09-24 8 | 9 | - 🙌 Fix Polacode not loading `font-family` correctly. Thanks to contribution from [@kufii](https://github.com/kufii). [#121](https://github.com/octref/polacode/pull/121). 10 | 11 | ### 0.3.2 | 2019-05-10 12 | 13 | - Vertically center code in the snippet. #108. 14 | 15 | ### 0.3.1 | 2019-05-07 16 | 17 | - Dispose selection sync after closing Polacode window so selection no longer jams clipboard. #107. 18 | 19 | ### 0.3.0 | 2019-05-07 20 | 21 | - Major rewrite. See features in [#105](https://github.com/octref/polacode/pull/105). 22 | - Long token wrapping. Thanks to [OhYee](https://github.com/OhYee). [#104](https://github.com/octref/polacode/pull/104). 23 | - Improved vertical alignment of snippet. Thanks to [vxna](https://github.com/vxna). [#106](https://github.com/octref/polacode/pull/106). 24 | 25 | ### 0.2.2 | 2018-02-19 26 | 27 | - Remove "Pasted content is invalid" check. Will do proper HTML validation in [#30](https://github.com/octref/polacode/issues/30). 28 | 29 | ### 0.2.1 | 2018-02-19 30 | 31 | - Fix an issue where Polacode incorrectly reports "Pasted content is invalid". 32 | 33 | ### 0.2.0 | 2018-02-16 34 | 35 | - Add warning when pasted content is not valid HTML copy-pasted from VS Code. 36 | - Remove backdrop for snippet with light background. 37 | - Find the line with smallest indentation and detract that indentation from all lines. 38 | - Add `polacode.shoot` command that you can bind command to. 39 | - Remember last used image save path. 40 | - Wrap when code is longer than Polacode preview. 41 | - Initialize with correct fontFamily. 42 | 43 | ### 0.1.2 | 2018-02-16 44 | 45 | - Update readme with some tip and explanation. 46 | - Make default filename `code.png`. 47 | 48 | ### 0.1.1 | 2018-02-15 49 | 50 | - Fix a Windows path issue. 51 | - Use correct background for snippet when pasting. 52 | 53 | ### 0.1.0 | 2018-02-15 54 | 55 | - Initial release -------------------------------------------------------------------------------- /demo/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/1.png -------------------------------------------------------------------------------- /demo/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/2.png -------------------------------------------------------------------------------- /demo/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/3.png -------------------------------------------------------------------------------- /demo/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/demo/usage.gif -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octref/polacode/8d17e92c63ad6785283d142fe4da82e9d840f557/icon.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "commonjs", 5 | "moduleResolution": "node" 6 | } 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polacode", 3 | "displayName": "Polacode", 4 | "description": "📸 Polaroid for your code", 5 | "version": "0.3.4", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/octref/polacode.git" 9 | }, 10 | "publisher": "pnp", 11 | "keywords": [ 12 | "polacode", 13 | "polaroid", 14 | "screenshot", 15 | "snippet", 16 | "share" 17 | ], 18 | "galleryBanner": { 19 | "color": "#fbfbfb", 20 | "theme": "light" 21 | }, 22 | "icon": "icon.png", 23 | "categories": [ 24 | "Other" 25 | ], 26 | "engines": { 27 | "vscode": "^1.32.0" 28 | }, 29 | "activationEvents": [ 30 | "onCommand:polacode.activate", 31 | "onWebviewPanel:polacode" 32 | ], 33 | "main": "./src/extension", 34 | "contributes": { 35 | "commands": [ 36 | { 37 | "command": "polacode.activate", 38 | "title": "Polacode 📸" 39 | } 40 | ], 41 | "configuration": { 42 | "title": "Polacode", 43 | "properties": { 44 | "polacode.shadow": { 45 | "type": "string", 46 | "description": "Shadow of the snippet node. Use any value for CSS `box-shadow`", 47 | "default": "rgba(0, 0, 0, 0.55) 0px 20px 68px" 48 | }, 49 | "polacode.transparentBackground": { 50 | "type": "boolean", 51 | "description": "Transparent background for containers", 52 | "default": false 53 | }, 54 | "polacode.backgroundColor": { 55 | "type": "string", 56 | "description": "Background color of snippet container. Use any value for CSS `background-color`", 57 | "format": "color-hex", 58 | "default": "#f2f2f2" 59 | }, 60 | "polacode.target": { 61 | "type": "string", 62 | "description": "Shoot with or without container", 63 | "default": "container", 64 | "enum": [ 65 | "container", 66 | "snippet" 67 | ], 68 | "enumDescriptions": [ 69 | "Shoot with the container.", 70 | "Shoot with the snippet alone. If you want transparent padding, use `container` with `\"polacode.transparentBackground\": true`" 71 | ] 72 | } 73 | } 74 | } 75 | }, 76 | "devDependencies": { 77 | "@types/node": "^11.12.0", 78 | "@types/vscode": "^1.32.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode') 2 | const fs = require('fs') 3 | const path = require('path') 4 | const { homedir } = require('os') 5 | 6 | const writeSerializedBlobToFile = (serializeBlob, fileName) => { 7 | const bytes = new Uint8Array(serializeBlob.split(',')) 8 | fs.writeFileSync(fileName, Buffer.from(bytes)) 9 | } 10 | 11 | const P_TITLE = 'Polacode 📸' 12 | 13 | /** 14 | * @param {vscode.ExtensionContext} context 15 | */ 16 | function activate(context) { 17 | const htmlPath = path.resolve(context.extensionPath, 'webview/index.html') 18 | 19 | let lastUsedImageUri = vscode.Uri.file(path.resolve(homedir(), 'Desktop/code.png')) 20 | let panel 21 | 22 | vscode.window.registerWebviewPanelSerializer('polacode', { 23 | async deserializeWebviewPanel(_panel, state) { 24 | panel = _panel 25 | panel.webview.html = getHtmlContent(htmlPath) 26 | panel.webview.postMessage({ 27 | type: 'restore', 28 | innerHTML: state.innerHTML, 29 | bgColor: context.globalState.get('polacode.bgColor', '#2e3440') 30 | }) 31 | const selectionListener = setupSelectionSync() 32 | panel.onDidDispose(() => { 33 | selectionListener.dispose() 34 | }) 35 | setupMessageListeners() 36 | } 37 | }) 38 | 39 | vscode.commands.registerCommand('polacode.activate', () => { 40 | panel = vscode.window.createWebviewPanel('polacode', P_TITLE, 2, { 41 | enableScripts: true, 42 | localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'webview'))] 43 | }) 44 | 45 | panel.webview.html = getHtmlContent(htmlPath) 46 | 47 | const selectionListener = setupSelectionSync() 48 | panel.onDidDispose(() => { 49 | selectionListener.dispose() 50 | }) 51 | 52 | setupMessageListeners() 53 | 54 | const fontFamily = vscode.workspace.getConfiguration('editor').fontFamily 55 | const bgColor = context.globalState.get('polacode.bgColor', '#2e3440') 56 | panel.webview.postMessage({ 57 | type: 'init', 58 | fontFamily, 59 | bgColor 60 | }) 61 | 62 | syncSettings() 63 | }) 64 | 65 | vscode.workspace.onDidChangeConfiguration(e => { 66 | if (e.affectsConfiguration('polacode') || e.affectsConfiguration('editor')) { 67 | syncSettings() 68 | } 69 | }) 70 | 71 | function setupMessageListeners() { 72 | panel.webview.onDidReceiveMessage(({ type, data }) => { 73 | switch (type) { 74 | case 'shoot': 75 | vscode.window 76 | .showSaveDialog({ 77 | defaultUri: lastUsedImageUri, 78 | filters: { 79 | Images: ['png'] 80 | } 81 | }) 82 | .then(uri => { 83 | if (uri) { 84 | writeSerializedBlobToFile(data.serializedBlob, uri.fsPath) 85 | lastUsedImageUri = uri 86 | } 87 | }) 88 | break 89 | case 'getAndUpdateCacheAndSettings': 90 | panel.webview.postMessage({ 91 | type: 'restoreBgColor', 92 | bgColor: context.globalState.get('polacode.bgColor', '#2e3440') 93 | }) 94 | 95 | syncSettings() 96 | break 97 | case 'updateBgColor': 98 | context.globalState.update('polacode.bgColor', data.bgColor) 99 | break 100 | case 'invalidPasteContent': 101 | vscode.window.showInformationMessage( 102 | 'Pasted content is invalid. Only copy from VS Code and check if your shortcuts for copy/paste have conflicts.' 103 | ) 104 | break 105 | } 106 | }) 107 | } 108 | 109 | function syncSettings() { 110 | const settings = vscode.workspace.getConfiguration('polacode') 111 | const editorSettings = vscode.workspace.getConfiguration('editor', null) 112 | panel.webview.postMessage({ 113 | type: 'updateSettings', 114 | shadow: settings.get('shadow'), 115 | transparentBackground: settings.get('transparentBackground'), 116 | backgroundColor: settings.get('backgroundColor'), 117 | target: settings.get('target'), 118 | ligature: editorSettings.get('fontLigatures') 119 | }) 120 | } 121 | 122 | function setupSelectionSync() { 123 | return vscode.window.onDidChangeTextEditorSelection(e => { 124 | if (e.selections[0] && !e.selections[0].isEmpty) { 125 | vscode.commands.executeCommand('editor.action.clipboardCopyWithSyntaxHighlightingAction') 126 | panel.postMessage({ 127 | type: 'update' 128 | }) 129 | } 130 | }) 131 | } 132 | } 133 | 134 | function getHtmlContent(htmlPath) { 135 | const htmlContent = fs.readFileSync(htmlPath, 'utf-8') 136 | return htmlContent.replace(/script src="([^"]*)"/g, (match, src) => { 137 | const realSource = 'vscode-resource:' + path.resolve(htmlPath, '..', src) 138 | return `script src="${realSource}"` 139 | }) 140 | } 141 | 142 | exports.activate = activate 143 | -------------------------------------------------------------------------------- /webview/dom2image.js: -------------------------------------------------------------------------------- 1 | /*! dom-to-image 10-06-2017 */ 2 | !function(a){"use strict";function b(a,b){function c(a){return b.bgcolor&&(a.style.backgroundColor=b.bgcolor),b.width&&(a.style.width=b.width+"px"),b.height&&(a.style.height=b.height+"px"),b.style&&Object.keys(b.style).forEach(function(c){a.style[c]=b.style[c]}),a}return b=b||{},g(b),Promise.resolve(a).then(function(a){return i(a,b.filter,!0)}).then(j).then(k).then(c).then(function(c){return l(c,b.width||q.width(a),b.height||q.height(a))})}function c(a,b){return h(a,b||{}).then(function(b){return b.getContext("2d").getImageData(0,0,q.width(a),q.height(a)).data})}function d(a,b){return h(a,b||{}).then(function(a){return a.toDataURL()})}function e(a,b){return b=b||{},h(a,b).then(function(a){return a.toDataURL("image/jpeg",b.quality||1)})}function f(a,b){return h(a,b||{}).then(q.canvasToBlob)}function g(a){"undefined"==typeof a.imagePlaceholder?v.impl.options.imagePlaceholder=u.imagePlaceholder:v.impl.options.imagePlaceholder=a.imagePlaceholder,"undefined"==typeof a.cacheBust?v.impl.options.cacheBust=u.cacheBust:v.impl.options.cacheBust=a.cacheBust}function h(a,c){function d(a){var b=document.createElement("canvas");if(b.width=c.width||q.width(a),b.height=c.height||q.height(a),c.bgcolor){var d=b.getContext("2d");d.fillStyle=c.bgcolor,d.fillRect(0,0,b.width,b.height)}return b}return b(a,c).then(q.makeImage).then(q.delay(100)).then(function(b){var c=d(a);return c.getContext("2d").drawImage(b,0,0),c})}function i(a,b,c){function d(a){return a instanceof HTMLCanvasElement?q.makeImage(a.toDataURL()):a.cloneNode(!1)}function e(a,b,c){function d(a,b,c){var d=Promise.resolve();return b.forEach(function(b){d=d.then(function(){return i(b,c)}).then(function(b){b&&a.appendChild(b)})}),d}var e=a.childNodes;return 0===e.length?Promise.resolve(b):d(b,q.asArray(e),c).then(function(){return b})}function f(a,b){function c(){function c(a,b){function c(a,b){q.asArray(a).forEach(function(c){b.setProperty(c,a.getPropertyValue(c),a.getPropertyPriority(c))})}a.cssText?b.cssText=a.cssText:c(a,b)}c(window.getComputedStyle(a),b.style)}function d(){function c(c){function d(a,b,c){function d(a){var b=a.getPropertyValue("content");return a.cssText+" content: "+b+";"}function e(a){function b(b){return b+": "+a.getPropertyValue(b)+(a.getPropertyPriority(b)?" !important":"")}return q.asArray(a).map(b).join("; ")+";"}var f="."+a+":"+b,g=c.cssText?d(c):e(c);return document.createTextNode(f+"{"+g+"}")}var e=window.getComputedStyle(a,c),f=e.getPropertyValue("content");if(""!==f&&"none"!==f){var g=q.uid();b.className=b.className+" "+g;var h=document.createElement("style");h.appendChild(d(g,c,e)),b.appendChild(h)}}[":before",":after"].forEach(function(a){c(a)})}function e(){a instanceof HTMLTextAreaElement&&(b.innerHTML=a.value),a instanceof HTMLInputElement&&b.setAttribute("value",a.value)}function f(){b instanceof SVGElement&&(b.setAttribute("xmlns","http://www.w3.org/2000/svg"),b instanceof SVGRectElement&&["width","height"].forEach(function(a){var c=b.getAttribute(a);c&&b.style.setProperty(a,c)}))}return b instanceof Element?Promise.resolve().then(c).then(d).then(e).then(f).then(function(){return b}):b}return c||!b||b(a)?Promise.resolve(a).then(d).then(function(c){return e(a,c,b)}).then(function(b){return f(a,b)}):Promise.resolve()}function j(a){return s.resolveAll().then(function(b){var c=document.createElement("style");return a.appendChild(c),c.appendChild(document.createTextNode(b)),a})}function k(a){return t.inlineAll(a).then(function(){return a})}function l(a,b,c){return Promise.resolve(a).then(function(a){return a.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(a)}).then(q.escapeXhtml).then(function(a){return'<foreignObject x="0" y="0" width="100%" height="100%">'+a+"</foreignObject>"}).then(function(a){return'<svg xmlns="http://www.w3.org/2000/svg" width="'+b+'" height="'+c+'">'+a+"</svg>"}).then(function(a){return"data:image/svg+xml;charset=utf-8,"+a})}function m(){function a(){var a="application/font-woff",b="image/jpeg";return{woff:a,woff2:a,ttf:"application/font-truetype",eot:"application/vnd.ms-fontobject",png:"image/png",jpg:b,jpeg:b,gif:"image/gif",tiff:"image/tiff",svg:"image/svg+xml"}}function b(a){var b=/\.([^\.\/]*?)$/g.exec(a);return b?b[1]:""}function c(c){var d=b(c).toLowerCase();return a()[d]||""}function d(a){return a.search(/^(data:)/)!==-1}function e(a){return new Promise(function(b){for(var c=window.atob(a.toDataURL().split(",")[1]),d=c.length,e=new Uint8Array(d),f=0;f<d;f++)e[f]=c.charCodeAt(f);b(new Blob([e],{type:"image/png"}))})}function f(a){return a.toBlob?new Promise(function(b){a.toBlob(b)}):e(a)}function g(a,b){var c=document.implementation.createHTMLDocument(),d=c.createElement("base");c.head.appendChild(d);var e=c.createElement("a");return c.body.appendChild(e),d.href=b,e.href=a,e.href}function h(){var a=0;return function(){function b(){return("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)}return"u"+b()+a++}}function i(a){return new Promise(function(b,c){var d=new Image;d.onload=function(){b(d)},d.onerror=c,d.src=a})}function j(a){var b=3e4;return v.impl.options.cacheBust&&(a+=(/\?/.test(a)?"&":"?")+(new Date).getTime()),new Promise(function(c){function d(){if(4===g.readyState){if(200!==g.status)return void(h?c(h):f("cannot fetch resource: "+a+", status: "+g.status));var b=new FileReader;b.onloadend=function(){var a=b.result.split(/,/)[1];c(a)},b.readAsDataURL(g.response)}}function e(){h?c(h):f("timeout of "+b+"ms occured while fetching resource: "+a)}function f(a){console.error(a),c("")}var g=new XMLHttpRequest;g.onreadystatechange=d,g.ontimeout=e,g.responseType="blob",g.timeout=b,g.open("GET",a,!0),g.send();var h;if(v.impl.options.imagePlaceholder){var i=v.impl.options.imagePlaceholder.split(/,/);i&&i[1]&&(h=i[1])}})}function k(a,b){return"data:"+b+";base64,"+a}function l(a){return a.replace(/([.*+?^${}()|\[\]\/\\])/g,"\\$1")}function m(a){return function(b){return new Promise(function(c){setTimeout(function(){c(b)},a)})}}function n(a){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}function o(a){return a.replace(/#/g,"%23").replace(/\n/g,"%0A")}function p(a){var b=r(a,"border-left-width"),c=r(a,"border-right-width");return a.scrollWidth+b+c}function q(a){var b=r(a,"border-top-width"),c=r(a,"border-bottom-width");return a.scrollHeight+b+c}function r(a,b){var c=window.getComputedStyle(a).getPropertyValue(b);return parseFloat(c.replace("px",""))}return{escape:l,parseExtension:b,mimeType:c,dataAsUrl:k,isDataUrl:d,canvasToBlob:f,resolveUrl:g,getAndEncode:j,uid:h(),delay:m,asArray:n,escapeXhtml:o,makeImage:i,width:p,height:q}}function n(){function a(a){return a.search(e)!==-1}function b(a){for(var b,c=[];null!==(b=e.exec(a));)c.push(b[1]);return c.filter(function(a){return!q.isDataUrl(a)})}function c(a,b,c,d){function e(a){return new RegExp("(url\\(['\"]?)("+q.escape(a)+")(['\"]?\\))","g")}return Promise.resolve(b).then(function(a){return c?q.resolveUrl(a,c):a}).then(d||q.getAndEncode).then(function(a){return q.dataAsUrl(a,q.mimeType(b))}).then(function(c){return a.replace(e(b),"$1"+c+"$3")})}function d(d,e,f){function g(){return!a(d)}return g()?Promise.resolve(d):Promise.resolve(d).then(b).then(function(a){var b=Promise.resolve(d);return a.forEach(function(a){b=b.then(function(b){return c(b,a,e,f)})}),b})}var e=/url\(['"]?([^'"]+?)['"]?\)/g;return{inlineAll:d,shouldProcess:a,impl:{readUrls:b,inline:c}}}function o(){function a(){return b(document).then(function(a){return Promise.all(a.map(function(a){return a.resolve()}))}).then(function(a){return a.join("\n")})}function b(){function a(a){return a.filter(function(a){return a.type===CSSRule.FONT_FACE_RULE}).filter(function(a){return r.shouldProcess(a.style.getPropertyValue("src"))})}function b(a){var b=[];return a.forEach(function(a){try{q.asArray(a.cssRules||[]).forEach(b.push.bind(b))}catch(c){console.log("Error while reading CSS rules from "+a.href,c.toString())}}),b}function c(a){return{resolve:function(){var b=(a.parentStyleSheet||{}).href;return r.inlineAll(a.cssText,b)},src:function(){return a.style.getPropertyValue("src")}}}return Promise.resolve(q.asArray(document.styleSheets)).then(b).then(a).then(function(a){return a.map(c)})}return{resolveAll:a,impl:{readAll:b}}}function p(){function a(a){function b(b){return q.isDataUrl(a.src)?Promise.resolve():Promise.resolve(a.src).then(b||q.getAndEncode).then(function(b){return q.dataAsUrl(b,q.mimeType(a.src))}).then(function(b){return new Promise(function(c,d){a.onload=c,a.onerror=d,a.src=b})})}return{inline:b}}function b(c){function d(a){var b=a.style.getPropertyValue("background");return b?r.inlineAll(b).then(function(b){a.style.setProperty("background",b,a.style.getPropertyPriority("background"))}).then(function(){return a}):Promise.resolve(a)}return c instanceof Element?d(c).then(function(){return c instanceof HTMLImageElement?a(c).inline():Promise.all(q.asArray(c.childNodes).map(function(a){return b(a)}))}):Promise.resolve(c)}return{inlineAll:b,impl:{newImage:a}}}var q=m(),r=n(),s=o(),t=p(),u={imagePlaceholder:void 0,cacheBust:!1},v={toSvg:b,toPng:d,toJpeg:e,toBlob:f,toPixelData:c,impl:{fontFaces:s,images:t,util:q,inliner:r,options:{}}};"undefined"!=typeof module?module.exports=v:a.domtoimage=v}(this); -------------------------------------------------------------------------------- /webview/index.html: -------------------------------------------------------------------------------- 1 | <html> 2 | <head> 3 | <meta 4 | http-equiv="Content-Security-Policy" 5 | content="img-src vscode-resource: data: https:; script-src vscode-resource:; style-src 'unsafe-inline' vscode-resource:;" 6 | /> 7 | <style> 8 | html { 9 | box-sizing: border-box; 10 | padding-top: 32px; 11 | } 12 | body { 13 | display: flex; 14 | flex-flow: column; 15 | align-items: center; 16 | } 17 | 18 | #snippet-container { 19 | display: flex; 20 | justify-content: center; 21 | align-items: center; 22 | overflow: hidden; 23 | resize: both; 24 | background-color: #f2f2f2; 25 | border-radius: 4px; 26 | opacity: 0; 27 | transition: opacity .4s; 28 | width: calc(100% - 4rem); 29 | } 30 | #snippet { 31 | overflow: hidden; 32 | resize: both; 33 | display: flex; 34 | padding: 18px; 35 | margin-top: 64px; 36 | margin-bottom: 64px; 37 | border-radius: 5px; 38 | box-shadow: rgba(0, 0, 0, 0.55) 0px 20px 68px; 39 | width: calc(100% - 8rem); 40 | } 41 | #snippet > div { 42 | display: flex; 43 | flex-flow: column nowrap; 44 | justify-content: center; 45 | max-width: 100%; 46 | white-space: pre-wrap !important; 47 | word-break: break-all; 48 | } 49 | #snippet > div > div { 50 | display: flex; 51 | flex-wrap: wrap; 52 | } 53 | 54 | #save-container { 55 | margin-top: 40px; 56 | margin-bottom: 60px; 57 | text-align: center; 58 | } 59 | .obturateur { 60 | width: 64px; 61 | height: 64px; 62 | } 63 | .obturateur * { 64 | transition: stroke .4s; 65 | } 66 | .obturateur:not(.filling) path { 67 | opacity: .5; 68 | } 69 | </style> 70 | </head> 71 | 72 | <body> 73 | <div id="snippet-container"> 74 | <div id="snippet" style="color: #d8dee9;background-color: #2e3440;font-family: SFMono-Regular,Consolas,DejaVu Sans Mono,Ubuntu Mono,Liberation Mono,Menlo,Courier,monospace;font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><meta charset="utf-8"><div style="color: #d8dee9;background-color: #2e3440;font-family: Input Mono;font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">0. Run command `Polacode 📸 `</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">1. Copy some code</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">2. Paste into Polacode view</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">3. Click the button 📸 </span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div></div></div> 75 | </div> 76 | 77 | <div id="save-container"> 78 | 79 | <svg id="save" class="obturateur" width="132px" height="132px" viewBox="0 0 132 132" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 80 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"> 81 | <g id="obturateur" transform="translate(2.000000, 2.000000)" stroke-width="3"> 82 | <circle id="Oval" stroke="#4C566A" cx="64" cy="64" r="64"></circle> 83 | <circle id="Oval" stroke="#4C566A" cx="64" cy="64" r="60.9706667"></circle> 84 | <circle id="Oval" stroke="#4C566A" cx="64" cy="64" r="51.8734222"></circle> 85 | <circle id="Oval" stroke="#D8DEE9" cx="64" cy="64" r="28.2595556"></circle> 86 | <path d="M17.0965333,86.1788444 L40.5667556,48.1998222" id="Shape" stroke="#EBCB8B"></path> 87 | <path d="M15.1509333,46.5180444 L58.6026667,36.2574222" id="Shape" stroke="#A3BE8C"></path> 88 | <path d="M41.8204444,17.0965333 L79.8001778,40.5660444" id="Shape" stroke="#8FBCBB"></path> 89 | <path d="M81.4819556,15.1502222 L91.7425778,58.6019556" id="Shape" stroke="#88C0D0"></path> 90 | <path d="M110.902756,41.8197333 L87.4332444,79.8001778" id="Shape" stroke="#81A1C1"></path> 91 | <path d="M112.848356,81.4819556 L69.3973333,91.7418667" id="Shape" stroke="#B48EAD"></path> 92 | <path d="M86.1795556,110.902756 L48.1998222,87.4332444" id="Shape" stroke="#BF616A"></path> 93 | <path d="M46.5187556,112.848356 L36.2574222,69.3973333" id="Shape" stroke="#D08770"></path> 94 | </g> 95 | </g> 96 | </svg> 97 | </div> 98 | 99 | <script src="./dom2image.js"></script> 100 | <script src="./vivus.js"></script> 101 | <script src="./index.js"></script> 102 | </body> 103 | </html> -------------------------------------------------------------------------------- /webview/index.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | const vscode = acquireVsCodeApi() 3 | 4 | let target = 'container' 5 | let transparentBackground = false 6 | let backgroundColor = '#f2f2f2' 7 | 8 | vscode.postMessage({ 9 | type: 'getAndUpdateCacheAndSettings' 10 | }) 11 | 12 | const snippetNode = document.getElementById('snippet') 13 | const snippetContainerNode = document.getElementById('snippet-container') 14 | const obturateur = document.getElementById('save') 15 | 16 | snippetContainerNode.style.opacity = '1' 17 | const oldState = vscode.getState(); 18 | if (oldState && oldState.innerHTML) { 19 | snippetNode.innerHTML = oldState.innerHTML 20 | } 21 | 22 | const getInitialHtml = fontFamily => { 23 | const cameraWithFlashEmoji = String.fromCodePoint(128248) 24 | const monoFontStack = `${fontFamily},SFMono-Regular,Consolas,DejaVu Sans Mono,Ubuntu Mono,Liberation Mono,Menlo,Courier,monospace` 25 | return `<meta charset="utf-8"><div style="color: #d8dee9;background-color: #2e3440; font-family: ${monoFontStack};font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">0. Run command \`Polacode ${cameraWithFlashEmoji}\`</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">1. Copy some code</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">2. Paste into Polacode view</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div><div><span style="color: #8fbcbb;">console</span><span style="color: #eceff4;">.</span><span style="color: #88c0d0;">log</span><span style="color: #d8dee9;">(</span><span style="color: #eceff4;">'</span><span style="color: #a3be8c;">3. Click the button ${cameraWithFlashEmoji}</span><span style="color: #eceff4;">'</span><span style="color: #d8dee9;">)</span></div></div></div>` 26 | } 27 | 28 | const serializeBlob = (blob, cb) => { 29 | const fileReader = new FileReader() 30 | 31 | fileReader.onload = () => { 32 | const bytes = new Uint8Array(fileReader.result) 33 | cb(Array.from(bytes).join(',')) 34 | } 35 | function getBrightness(color) { 36 | const rgb = this.toRgb() 37 | return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 38 | } 39 | 40 | fileReader.readAsArrayBuffer(blob) 41 | } 42 | 43 | function shoot(serializedBlob) { 44 | vscode.postMessage({ 45 | type: 'shoot', 46 | data: { 47 | serializedBlob 48 | } 49 | }) 50 | } 51 | 52 | function getBrightness(hexColor) { 53 | const rgb = parseInt(hexColor.slice(1), 16) 54 | const r = (rgb >> 16) & 0xff 55 | const g = (rgb >> 8) & 0xff 56 | const b = (rgb >> 0) & 0xff 57 | return (r * 299 + g * 587 + b * 114) / 1000 58 | } 59 | function isDark(hexColor) { 60 | return getBrightness(hexColor) < 128 61 | } 62 | function getSnippetBgColor(html) { 63 | const match = html.match(/background-color: (#[a-fA-F0-9]+)/) 64 | return match ? match[1] : undefined; 65 | } 66 | 67 | function updateEnvironment(snippetBgColor) { 68 | // update snippet bg color 69 | document.getElementById('snippet').style.backgroundColor = snippetBgColor 70 | 71 | // update backdrop color 72 | if (isDark(snippetBgColor)) { 73 | snippetContainerNode.style.backgroundColor = '#f2f2f2' 74 | } else { 75 | snippetContainerNode.style.background = 'none' 76 | } 77 | } 78 | 79 | function getMinIndent(code) { 80 | const arr = code.split('\n') 81 | 82 | let minIndentCount = Number.MAX_VALUE 83 | for (let i = 0; i < arr.length; i++) { 84 | const wsCount = arr[i].search(/\S/) 85 | if (wsCount !== -1) { 86 | if (wsCount < minIndentCount) { 87 | minIndentCount = wsCount 88 | } 89 | } 90 | } 91 | 92 | return minIndentCount 93 | } 94 | 95 | function stripInitialIndent(html, indent) { 96 | const doc = new DOMParser().parseFromString(html, 'text/html') 97 | const initialSpans = doc.querySelectorAll('div > div span:first-child') 98 | for (let i = 0; i < initialSpans.length; i++) { 99 | initialSpans[i].textContent = initialSpans[i].textContent.slice(indent) 100 | } 101 | return doc.body.innerHTML 102 | } 103 | 104 | document.addEventListener('paste', e => { 105 | const innerHTML = e.clipboardData.getData('text/html') 106 | 107 | const code = e.clipboardData.getData('text/plain') 108 | const minIndent = getMinIndent(code) 109 | 110 | const snippetBgColor = getSnippetBgColor(innerHTML) 111 | if (snippetBgColor) { 112 | vscode.postMessage({ 113 | type: 'updateBgColor', 114 | data: { 115 | bgColor: snippetBgColor 116 | } 117 | }) 118 | updateEnvironment(snippetBgColor) 119 | } 120 | 121 | if (minIndent !== 0) { 122 | snippetNode.innerHTML = stripInitialIndent(innerHTML, minIndent) 123 | } else { 124 | snippetNode.innerHTML = innerHTML 125 | } 126 | 127 | vscode.setState({ innerHTML }) 128 | }) 129 | 130 | obturateur.addEventListener('click', () => { 131 | if (target === 'container') { 132 | shootAll() 133 | } else { 134 | shootSnippet() 135 | } 136 | }) 137 | 138 | function shootAll() { 139 | const width = snippetContainerNode.offsetWidth * 2 140 | const height = snippetContainerNode.offsetHeight * 2 141 | const config = { 142 | width, 143 | height, 144 | style: { 145 | transform: 'scale(2)', 146 | 'transform-origin': 'center', 147 | background: getRgba(backgroundColor, transparentBackground) 148 | } 149 | } 150 | 151 | // Hide resizer before capture 152 | snippetNode.style.resize = 'none' 153 | snippetContainerNode.style.resize = 'none' 154 | 155 | domtoimage.toBlob(snippetContainerNode, config).then(blob => { 156 | snippetNode.style.resize = '' 157 | snippetContainerNode.style.resize = '' 158 | serializeBlob(blob, serializedBlob => { 159 | shoot(serializedBlob) 160 | }) 161 | }) 162 | } 163 | 164 | function shootSnippet() { 165 | const width = snippetNode.offsetWidth * 2 166 | const height = snippetNode.offsetHeight * 2 167 | const config = { 168 | width, 169 | height, 170 | style: { 171 | transform: 'scale(2)', 172 | 'transform-origin': 'center', 173 | padding: 0, 174 | background: 'none' 175 | } 176 | } 177 | 178 | // Hide resizer before capture 179 | snippetNode.style.resize = 'none' 180 | snippetContainerNode.style.resize = 'none' 181 | 182 | domtoimage.toBlob(snippetContainerNode, config).then(blob => { 183 | snippetNode.style.resize = '' 184 | snippetContainerNode.style.resize = '' 185 | serializeBlob(blob, serializedBlob => { 186 | shoot(serializedBlob) 187 | }) 188 | }) 189 | } 190 | 191 | let isInAnimation = false 192 | 193 | obturateur.addEventListener('mouseover', () => { 194 | if (!isInAnimation) { 195 | isInAnimation = true 196 | 197 | new Vivus( 198 | 'save', 199 | { 200 | duration: 40, 201 | onReady: () => { 202 | obturateur.className = 'obturateur filling' 203 | } 204 | }, 205 | () => { 206 | setTimeout(() => { 207 | isInAnimation = false 208 | obturateur.className = 'obturateur' 209 | }, 700) 210 | } 211 | ) 212 | } 213 | }) 214 | 215 | window.addEventListener('message', e => { 216 | if (e) { 217 | if (e.data.type === 'init') { 218 | const { fontFamily, bgColor } = e.data 219 | 220 | const initialHtml = getInitialHtml(fontFamily) 221 | snippetNode.innerHTML = initialHtml 222 | vscode.setState({ innerHTML: initialHtml }) 223 | 224 | // update backdrop color, using bgColor from last pasted snippet 225 | // cannot deduce from initialHtml since it's always using Nord color 226 | if (isDark(bgColor)) { 227 | snippetContainerNode.style.backgroundColor = '#f2f2f2' 228 | } else { 229 | snippetContainerNode.style.background = 'none' 230 | } 231 | 232 | } else if (e.data.type === 'update') { 233 | document.execCommand('paste') 234 | } else if (e.data.type === 'restore') { 235 | snippetNode.innerHTML = e.data.innerHTML 236 | updateEnvironment(e.data.bgColor) 237 | } else if (e.data.type === 'restoreBgColor') { 238 | updateEnvironment(e.data.bgColor) 239 | } else if (e.data.type === 'updateSettings') { 240 | snippetNode.style.boxShadow = e.data.shadow 241 | target = e.data.target 242 | transparentBackground = e.data.transparentBackground 243 | snippetContainerNode.style.backgroundColor = e.data.backgroundColor 244 | backgroundColor = e.data.backgroundColor 245 | if (e.data.ligature) { 246 | snippetNode.style.fontVariantLigatures = 'normal' 247 | } else { 248 | snippetNode.style.fontVariantLigatures = 'none' 249 | } 250 | } 251 | } 252 | }) 253 | })() 254 | 255 | function getRgba(hex, transparentBackground) { 256 | const bigint = parseInt(hex.slice(1), 16); 257 | const r = (bigint >> 16) & 255; 258 | const g = (bigint >> 8) & 255; 259 | const b = bigint & 255; 260 | const a = transparentBackground ? 0 : 1 261 | return `rgba(${r}, ${g}, ${b}, ${a})` 262 | } -------------------------------------------------------------------------------- /webview/vivus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * vivus - JavaScript library to make drawing animation on SVG 3 | * @version v0.4.2 4 | * @link https://github.com/maxwellito/vivus 5 | * @license MIT 6 | */ 7 | "use strict";!function(){function t(t){if("undefined"==typeof t)throw new Error('Pathformer [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');if(!(t instanceof window.SVGElement||t instanceof window.SVGGElement||/^svg$/i.test(t.nodeName)))throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');this.el=t,this.scan(t)}function e(t,e,n){r(),this.isReady=!1,this.setElement(t,e),this.setOptions(e),this.setCallback(n),this.isReady&&this.init()}t.prototype.TYPES=["line","ellipse","circle","polygon","polyline","rect"],t.prototype.ATTR_WATCH=["cx","cy","points","r","rx","ry","x","x1","x2","y","y1","y2"],t.prototype.scan=function(t){for(var e,r,n,i,a=t.querySelectorAll(this.TYPES.join(",")),o=0;o<a.length;o++)r=a[o],e=this[r.tagName.toLowerCase()+"ToPath"],n=e(this.parseAttr(r.attributes)),i=this.pathMaker(r,n),r.parentNode.replaceChild(i,r)},t.prototype.lineToPath=function(t){var e={},r=t.x1||0,n=t.y1||0,i=t.x2||0,a=t.y2||0;return e.d="M"+r+","+n+"L"+i+","+a,e},t.prototype.rectToPath=function(t){var e={},r=parseFloat(t.x)||0,n=parseFloat(t.y)||0,i=parseFloat(t.width)||0,a=parseFloat(t.height)||0;if(t.rx||t.ry){var o=parseInt(t.rx,10)||-1,s=parseInt(t.ry,10)||-1;o=Math.min(Math.max(0>o?s:o,0),i/2),s=Math.min(Math.max(0>s?o:s,0),a/2),e.d="M "+(r+o)+","+n+" L "+(r+i-o)+","+n+" A "+o+","+s+",0,0,1,"+(r+i)+","+(n+s)+" L "+(r+i)+","+(n+a-s)+" A "+o+","+s+",0,0,1,"+(r+i-o)+","+(n+a)+" L "+(r+o)+","+(n+a)+" A "+o+","+s+",0,0,1,"+r+","+(n+a-s)+" L "+r+","+(n+s)+" A "+o+","+s+",0,0,1,"+(r+o)+","+n}else e.d="M"+r+" "+n+" L"+(r+i)+" "+n+" L"+(r+i)+" "+(n+a)+" L"+r+" "+(n+a)+" Z";return e},t.prototype.polylineToPath=function(t){var e,r,n={},i=t.points.trim().split(" ");if(-1===t.points.indexOf(",")){var a=[];for(e=0;e<i.length;e+=2)a.push(i[e]+","+i[e+1]);i=a}for(r="M"+i[0],e=1;e<i.length;e++)-1!==i[e].indexOf(",")&&(r+="L"+i[e]);return n.d=r,n},t.prototype.polygonToPath=function(e){var r=t.prototype.polylineToPath(e);return r.d+="Z",r},t.prototype.ellipseToPath=function(t){var e={},r=parseFloat(t.rx)||0,n=parseFloat(t.ry)||0,i=parseFloat(t.cx)||0,a=parseFloat(t.cy)||0,o=i-r,s=a,h=parseFloat(i)+parseFloat(r),l=a;return e.d="M"+o+","+s+"A"+r+","+n+" 0,1,1 "+h+","+l+"A"+r+","+n+" 0,1,1 "+o+","+l,e},t.prototype.circleToPath=function(t){var e={},r=parseFloat(t.r)||0,n=parseFloat(t.cx)||0,i=parseFloat(t.cy)||0,a=n-r,o=i,s=parseFloat(n)+parseFloat(r),h=i;return e.d="M"+a+","+o+"A"+r+","+r+" 0,1,1 "+s+","+h+"A"+r+","+r+" 0,1,1 "+a+","+h,e},t.prototype.pathMaker=function(t,e){var r,n,i=document.createElementNS("http://www.w3.org/2000/svg","path");for(r=0;r<t.attributes.length;r++)n=t.attributes[r],-1===this.ATTR_WATCH.indexOf(n.name)&&i.setAttribute(n.name,n.value);for(r in e)i.setAttribute(r,e[r]);return i},t.prototype.parseAttr=function(t){for(var e,r={},n=0;n<t.length;n++){if(e=t[n],-1!==this.ATTR_WATCH.indexOf(e.name)&&-1!==e.value.indexOf("%"))throw new Error("Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into 'path' tags. Please use 'viewBox'.");r[e.name]=e.value}return r};var r,n,i,a;e.LINEAR=function(t){return t},e.EASE=function(t){return-Math.cos(t*Math.PI)/2+.5},e.EASE_OUT=function(t){return 1-Math.pow(1-t,3)},e.EASE_IN=function(t){return Math.pow(t,3)},e.EASE_OUT_BOUNCE=function(t){var e=-Math.cos(.5*t*Math.PI)+1,r=Math.pow(e,1.5),n=Math.pow(1-t,2),i=-Math.abs(Math.cos(2.5*r*Math.PI))+1;return 1-n+i*n},e.prototype.setElement=function(t,e){if("undefined"==typeof t)throw new Error('Vivus [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');if(this.parentEl=t,e&&e.file){var r=document.createElement("object");r.setAttribute("type","image/svg+xml"),r.setAttribute("data",e.file),r.setAttribute("built-by-vivus","true"),t.appendChild(r),t=r}switch(t.constructor){case window.SVGSVGElement:case window.SVGElement:case window.SVGGElement:this.el=t,this.isReady=!0;break;case window.HTMLObjectElement:var n,i;i=this,n=function(e){if(!i.isReady){if(i.el=t.contentDocument&&t.contentDocument.querySelector("svg"),!i.el&&e)throw new Error("Vivus [constructor]: object loaded does not contain any SVG");return i.el?(t.getAttribute("built-by-vivus")&&(i.parentEl.insertBefore(i.el,t),i.parentEl.removeChild(t),i.el.setAttribute("width","100%"),i.el.setAttribute("height","100%")),i.isReady=!0,i.init(),!0):void 0}},n()||t.addEventListener("load",n);break;default:throw new Error('Vivus [constructor]: "element" parameter is not valid (or miss the "file" attribute)')}},e.prototype.setOptions=function(t){var r=["delayed","sync","async","nsync","oneByOne","scenario","scenario-sync"],n=["inViewport","manual","autostart"];if(void 0!==t&&t.constructor!==Object)throw new Error('Vivus [constructor]: "options" parameter must be an object');if(t=t||{},t.type&&-1===r.indexOf(t.type))throw new Error("Vivus [constructor]: "+t.type+" is not an existing animation `type`");if(this.type=t.type||r[0],t.start&&-1===n.indexOf(t.start))throw new Error("Vivus [constructor]: "+t.start+" is not an existing `start` option");if(this.start=t.start||n[0],this.isIE=-1!==window.navigator.userAgent.indexOf("MSIE")||-1!==window.navigator.userAgent.indexOf("Trident/")||-1!==window.navigator.userAgent.indexOf("Edge/"),this.duration=a(t.duration,120),this.delay=a(t.delay,null),this.dashGap=a(t.dashGap,1),this.forceRender=t.hasOwnProperty("forceRender")?!!t.forceRender:this.isIE,this.reverseStack=!!t.reverseStack,this.selfDestroy=!!t.selfDestroy,this.onReady=t.onReady,this.map=[],this.frameLength=this.currentFrame=this.delayUnit=this.speed=this.handle=null,this.ignoreInvisible=t.hasOwnProperty("ignoreInvisible")?!!t.ignoreInvisible:!1,this.animTimingFunction=t.animTimingFunction||e.LINEAR,this.pathTimingFunction=t.pathTimingFunction||e.LINEAR,this.delay>=this.duration)throw new Error("Vivus [constructor]: delay must be shorter than duration")},e.prototype.setCallback=function(t){if(t&&t.constructor!==Function)throw new Error('Vivus [constructor]: "callback" parameter must be a function');this.callback=t||function(){}},e.prototype.mapping=function(){var t,e,r,n,i,o,s,h;for(h=o=s=0,e=this.el.querySelectorAll("path"),t=0;t<e.length;t++)r=e[t],this.isInvisible(r)||(i={el:r,length:Math.ceil(r.getTotalLength())},isNaN(i.length)?window.console&&console.warn&&console.warn("Vivus [mapping]: cannot retrieve a path element length",r):(this.map.push(i),r.style.strokeDasharray=i.length+" "+(i.length+2*this.dashGap),r.style.strokeDashoffset=i.length+this.dashGap,i.length+=this.dashGap,o+=i.length,this.renderPath(t)));for(o=0===o?1:o,this.delay=null===this.delay?this.duration/3:this.delay,this.delayUnit=this.delay/(e.length>1?e.length-1:1),this.reverseStack&&this.map.reverse(),t=0;t<this.map.length;t++){switch(i=this.map[t],this.type){case"delayed":i.startAt=this.delayUnit*t,i.duration=this.duration-this.delay;break;case"oneByOne":i.startAt=s/o*this.duration,i.duration=i.length/o*this.duration;break;case"sync":case"async":case"nsync":i.startAt=0,i.duration=this.duration;break;case"scenario-sync":r=i.el,n=this.parseAttr(r),i.startAt=h+(a(n["data-delay"],this.delayUnit)||0),i.duration=a(n["data-duration"],this.duration),h=void 0!==n["data-async"]?i.startAt:i.startAt+i.duration,this.frameLength=Math.max(this.frameLength,i.startAt+i.duration);break;case"scenario":r=i.el,n=this.parseAttr(r),i.startAt=a(n["data-start"],this.delayUnit)||0,i.duration=a(n["data-duration"],this.duration),this.frameLength=Math.max(this.frameLength,i.startAt+i.duration)}s+=i.length,this.frameLength=this.frameLength||this.duration}},e.prototype.drawer=function(){var t=this;if(this.currentFrame+=this.speed,this.currentFrame<=0)this.stop(),this.reset();else{if(!(this.currentFrame>=this.frameLength))return this.trace(),this.handle=n(function(){t.drawer()}),void 0;this.stop(),this.currentFrame=this.frameLength,this.trace(),this.selfDestroy&&this.destroy()}this.callback(this),this.instanceCallback&&(this.instanceCallback(this),this.instanceCallback=null)},e.prototype.trace=function(){var t,e,r,n;for(n=this.animTimingFunction(this.currentFrame/this.frameLength)*this.frameLength,t=0;t<this.map.length;t++)r=this.map[t],e=(n-r.startAt)/r.duration,e=this.pathTimingFunction(Math.max(0,Math.min(1,e))),r.progress!==e&&(r.progress=e,r.el.style.strokeDashoffset=Math.floor(r.length*(1-e)),this.renderPath(t))},e.prototype.renderPath=function(t){if(this.forceRender&&this.map&&this.map[t]){var e=this.map[t],r=e.el.cloneNode(!0);e.el.parentNode.replaceChild(r,e.el),e.el=r}},e.prototype.init=function(){this.frameLength=0,this.currentFrame=0,this.map=[],new t(this.el),this.mapping(),this.starter(),this.onReady&&this.onReady(this)},e.prototype.starter=function(){switch(this.start){case"manual":return;case"autostart":this.play();break;case"inViewport":var t=this,e=function(){t.isInViewport(t.parentEl,1)&&(t.play(),window.removeEventListener("scroll",e))};window.addEventListener("scroll",e),e()}},e.prototype.getStatus=function(){return 0===this.currentFrame?"start":this.currentFrame===this.frameLength?"end":"progress"},e.prototype.reset=function(){return this.setFrameProgress(0)},e.prototype.finish=function(){return this.setFrameProgress(1)},e.prototype.setFrameProgress=function(t){return t=Math.min(1,Math.max(0,t)),this.currentFrame=Math.round(this.frameLength*t),this.trace(),this},e.prototype.play=function(t,e){if(this.instanceCallback=null,t&&"function"==typeof t)this.instanceCallback=t,t=null;else if(t&&"number"!=typeof t)throw new Error("Vivus [play]: invalid speed");return e&&"function"==typeof e&&!this.instanceCallback&&(this.instanceCallback=e),this.speed=t||1,this.handle||this.drawer(),this},e.prototype.stop=function(){return this.handle&&(i(this.handle),this.handle=null),this},e.prototype.destroy=function(){this.stop();var t,e;for(t=0;t<this.map.length;t++)e=this.map[t],e.el.style.strokeDashoffset=null,e.el.style.strokeDasharray=null,this.renderPath(t)},e.prototype.isInvisible=function(t){var e,r=t.getAttribute("data-ignore");return null!==r?"false"!==r:this.ignoreInvisible?(e=t.getBoundingClientRect(),!e.width&&!e.height):!1},e.prototype.parseAttr=function(t){var e,r={};if(t&&t.attributes)for(var n=0;n<t.attributes.length;n++)e=t.attributes[n],r[e.name]=e.value;return r},e.prototype.isInViewport=function(t,e){var r=this.scrollY(),n=r+this.getViewportH(),i=t.getBoundingClientRect(),a=i.height,o=r+i.top,s=o+a;return e=e||0,n>=o+a*e&&s>=r},e.prototype.getViewportH=function(){var t=this.docElem.clientHeight,e=window.innerHeight;return e>t?e:t},e.prototype.scrollY=function(){return window.pageYOffset||this.docElem.scrollTop},r=function(){e.prototype.docElem||(e.prototype.docElem=window.document.documentElement,n=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),i=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t)}}())},a=function(t,e){var r=parseInt(t,10);return r>=0?r:e},"function"==typeof define&&define.amd?define([],function(){return e}):"object"==typeof exports?module.exports=e:window.Vivus=e}(); -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@^11.12.0": 6 | version "11.12.0" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.0.tgz#ec5594728811dc2797e42396cfcdf786f2052c12" 8 | integrity sha512-Lg00egj78gM+4aE0Erw05cuDbvX9sLJbaaPwwRtdCdAMnIudqrQZ0oZX98Ek0yiSK/A2nubHgJfvII/rTT2Dwg== 9 | 10 | "@types/vscode@^1.32.0": 11 | version "1.32.0" 12 | resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.32.0.tgz#e0a57de5fc8690a8a3a473996a6b6dfbccc28fbd" 13 | integrity sha512-fpHR6iE38V3+2ezMwopt726uRYKK9c89OQDO+t8VEUXNJCaYvnMFbHYgxXvQ/jOvP2ZanlL6r6joRZlsUowzoA== 14 | --------------------------------------------------------------------------------