├── .npmignore ├── examples ├── hero-image.jpg ├── simple.html ├── script.js ├── script.ts └── threejs.html ├── .gitignore ├── src ├── codepen.js ├── index.css └── index.js ├── rollup.config.mjs ├── package.json ├── Readme.md └── dist ├── index.js ├── dependencies.txt └── index.js.map /.npmignore: -------------------------------------------------------------------------------- 1 | *.backup 2 | -------------------------------------------------------------------------------- /examples/hero-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/repalash/example-code-previewer/HEAD/examples/hero-image.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /src/codepen.js: -------------------------------------------------------------------------------- 1 | // https://blog.codepen.io/documentation/prefill/ 2 | export function linkCodepen({html, css, js, ...opts}) { 3 | 4 | html = html.trim(); 5 | css = css.trim(); 6 | js = js.trim(); 7 | 8 | const data = { 9 | title: document.title, 10 | description: "", 11 | html: html, 12 | html_pre_processor: "none", 13 | css: css, 14 | css_pre_processor: "none", 15 | css_starter: "neither", 16 | css_prefix_free: false, 17 | js: js, 18 | js_pre_processor: 'js', 19 | js_modernizr: false, 20 | js_library: "", 21 | html_classes: "", 22 | css_external: "", 23 | js_external: "", 24 | editors: "101", 25 | template: true, 26 | ...opts 27 | }; 28 | 29 | const JSONstring = 30 | JSON.stringify(data) 31 | // Quotes will screw up the JSON 32 | .replace(/"/g, """) 33 | .replace(/'/g, "'"); 34 | 35 | const form = 36 | '
' + 37 | '' + 40 | '' + 41 | '
'; 42 | 43 | return form 44 | } 45 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | 2 | // rollup.config.js 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import terser from '@rollup/plugin-terser'; 6 | import license from 'rollup-plugin-license' 7 | import packageJson from './package.json' assert { type: 'json' }; 8 | import path from 'path' 9 | import { fileURLToPath } from 'url'; 10 | import css from "rollup-plugin-import-css"; 11 | 12 | const __filename = fileURLToPath(import.meta.url); 13 | const __dirname = path.dirname(__filename); 14 | 15 | const { name, version, author, module } = packageJson 16 | const isProduction = process.env.NODE_ENV === 'production' 17 | 18 | const settings = { 19 | globals: { 20 | }, 21 | sourcemap: true 22 | } 23 | 24 | export default { 25 | input: './src/index.js', 26 | output: [{ 27 | file: module, 28 | ...settings, 29 | name: name, 30 | format: 'es', 31 | plugins: [ 32 | isProduction && terser() 33 | ] 34 | }], 35 | external: [ ], 36 | plugins: [ 37 | commonjs({}), 38 | resolve({ 39 | }), 40 | css({ 41 | minify: isProduction, 42 | }), 43 | license({ 44 | banner: ` 45 | @license 46 | ${name} v${version} 47 | Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} 48 | ${packageJson.license} License 49 | `, 50 | thirdParty: { 51 | output: path.join(__dirname, 'dist', 'dependencies.txt'), 52 | includePrivate: true, // Default is false. 53 | }, 54 | 55 | }) 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-code-previewer", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "module": "dist/index.js", 7 | "source": "src/index.js", 8 | "sideEffects": false, 9 | "scripts": { 10 | "build": "NODE_ENV=production rollup -c", 11 | "dev": "rollup -c -w", 12 | "serve": "ws -p 8000", 13 | "prepare": "npm run build" 14 | }, 15 | "files": [ 16 | "dist", 17 | "src", 18 | "examples", 19 | "LICENSE", 20 | "README.md" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/repalash/example-code-previewer.git" 25 | }, 26 | "keywords": [ 27 | "typescript", 28 | "html", 29 | "code-viewer", 30 | "syntax-highlighting", 31 | "code-previewer", 32 | "three.js", 33 | "template", 34 | "javascript", 35 | "library" 36 | ], 37 | "author": "repalash ", 38 | "license": "MIT", 39 | "bugs": { 40 | "url": "https://github.com/repalash/example-code-previewer/issues" 41 | }, 42 | "homepage": "https://github.com/repalash/example-code-previewer#readme", 43 | "devDependencies": { 44 | "@rollup/plugin-commonjs": "^25.0.0", 45 | "@rollup/plugin-node-resolve": "^15.0.1", 46 | "@rollup/plugin-terser": "^0.1.0", 47 | "@speed-highlight/core": "^1.1.11", 48 | "clean-package": "^2.2.0", 49 | "local-web-server": "^5.2.1", 50 | "rollup": "^3.4.0", 51 | "rollup-plugin-import-css": "^3.2.1", 52 | "rollup-plugin-license": "^3.0.1", 53 | "@types/three": "^0.152.0" 54 | }, 55 | "optionalDependencies": { 56 | "win-node-env": "^0.6.1" 57 | }, 58 | "browserslist": [ 59 | "defaults" 60 | ], 61 | "type": "module" 62 | } 63 | -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Code Sample 6 | 7 | 29 | 49 | 50 | 51 |
52 |
53 | Counter: 0

54 | 55 |
56 |
57 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/script.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; 4 | import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; 5 | import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'; 6 | 7 | let camera, scene, renderer; 8 | 9 | init(); 10 | render(); 11 | 12 | function init() { 13 | 14 | const container = document.getElementById( 'canvas-container' ); 15 | 16 | camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 ); 17 | camera.position.set( - 1.8, 0.6, 2.7 ); 18 | 19 | scene = new THREE.Scene(); 20 | 21 | new RGBELoader() 22 | .setPath( 'https://threejs.org/examples/textures/equirectangular/' ) 23 | .load( 'royal_esplanade_1k.hdr', function ( texture ) { 24 | 25 | texture.mapping = THREE.EquirectangularReflectionMapping; 26 | 27 | scene.background = texture; 28 | scene.environment = texture; 29 | 30 | render(); 31 | 32 | // model 33 | 34 | const loader = new GLTFLoader().setPath( 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/' ); 35 | loader.load( 'DamagedHelmet.gltf', function ( gltf ) { 36 | 37 | scene.add( gltf.scene ); 38 | 39 | render(); 40 | 41 | } ); 42 | 43 | } ); 44 | 45 | renderer = new THREE.WebGLRenderer( { antialias: true, canvas: document.getElementById('mcanvas') } ); 46 | renderer.setPixelRatio( window.devicePixelRatio ); 47 | renderer.setSize( window.innerWidth, window.innerHeight ); 48 | renderer.toneMapping = THREE.ACESFilmicToneMapping; 49 | renderer.toneMappingExposure = 1; 50 | container.appendChild( renderer.domElement ); 51 | 52 | const controls = new OrbitControls( camera, renderer.domElement ); 53 | controls.addEventListener( 'change', render ); // use if there is no animation loop 54 | controls.minDistance = 2; 55 | controls.maxDistance = 10; 56 | controls.target.set( 0, 0, - 0.2 ); 57 | controls.update(); 58 | 59 | window.addEventListener( 'resize', onWindowResize ); 60 | 61 | } 62 | 63 | function onWindowResize() { 64 | 65 | camera.aspect = window.innerWidth / window.innerHeight; 66 | camera.updateProjectionMatrix(); 67 | 68 | renderer.setSize( window.innerWidth, window.innerHeight ); 69 | 70 | render(); 71 | 72 | } 73 | 74 | // 75 | 76 | function render() { 77 | 78 | renderer.render( scene, camera ); 79 | 80 | requestAnimationFrame(render); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Example Code Previewer 2 | 3 | [![NPM Package](https://img.shields.io/npm/v/example-code-previewer.svg)](https://www.npmjs.com/package/example-code-previewer) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | 6 | ![Example code viewer](./examples/hero-image.jpg) 7 | 8 | A minimal code viewer for html with support for syntax highlighting, multiple files, codepen prefill. 9 | 10 | Source code on Github: [https://github.com/repalash/example-code-previewer/](https://github.com/repalash/example-code-previewer/) 11 | 12 | ## Demo 13 | 14 | Check out the [simple](https://repalash.github.io/example-code-previewer/examples/simple.html) and [complex](https://repalash.github.io/example-code-previewer/examples/threejs.html) (with [three.js](https://threejs.org)) live demos 15 | 16 | ## Usage 17 | 18 | Use directly in the HTML file with a CDN link 19 | ```html 20 | 38 | ``` 39 | 40 | or install from `npm` and import 41 | 42 | ```bash 43 | npm install example-code-previewer 44 | ``` 45 | ```js 46 | import {setupCodePreview} from 'example-code-previewer' 47 | ``` 48 | 49 | ## Development 50 | Build for development (Build and watch): 51 | ```bash 52 | npm run dev 53 | ``` 54 | To serve the local build and examples, run 55 | ```bash 56 | npm run serve 57 | ``` 58 | 59 | 60 | Build for production (Build): 61 | ```bash 62 | npm run build 63 | ``` 64 | 65 | Generate a new version 66 | ```bash 67 | npm version 68 | ``` 69 | 70 | ## License 71 | MIT 72 | 73 | ## References 74 | 75 | Syntax highlighting with [Speed highlight JS](https://github.com/speed-highlight/core) 76 | 77 | Generated with [rollup-library-starter](https://github.com/repalash/rollup-library-starter) 78 | -------------------------------------------------------------------------------- /examples/script.ts: -------------------------------------------------------------------------------- 1 | import {PerspectiveCamera, Scene, EquirectangularReflectionMapping, WebGLRenderer, ACESFilmicToneMapping} from 'three'; 2 | 3 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 4 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; 5 | import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'; 6 | 7 | let camera: PerspectiveCamera, scene: Scene, renderer: WebGLRenderer; 8 | 9 | init(); 10 | render(); 11 | 12 | function init() { 13 | 14 | const container = document.getElementById('canvas-container'); 15 | 16 | camera = new PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 ); 17 | camera.position.set( - 1.8, 0.6, 2.7 ); 18 | 19 | scene = new Scene(); 20 | 21 | new RGBELoader() 22 | .setPath( 'https://threejs.org/examples/textures/equirectangular/' ) 23 | .load( 'royal_esplanade_1k.hdr', function ( texture ) { 24 | 25 | texture.mapping = EquirectangularReflectionMapping; 26 | 27 | scene.background = texture; 28 | scene.environment = texture; 29 | 30 | render(); 31 | 32 | // model 33 | 34 | const loader = new GLTFLoader().setPath( 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/' ); 35 | loader.load( 'DamagedHelmet.gltf', function ( gltf ) { 36 | 37 | scene.add( gltf.scene ); 38 | 39 | render(); 40 | 41 | } ); 42 | 43 | } ); 44 | 45 | renderer = new WebGLRenderer( { antialias: true, canvas: document.getElementById('mcanvas') } ); 46 | renderer.setPixelRatio( window.devicePixelRatio ); 47 | renderer.setSize( window.innerWidth, window.innerHeight ); 48 | renderer.toneMapping = ACESFilmicToneMapping; 49 | renderer.toneMappingExposure = 1; 50 | container.appendChild( renderer.domElement ); 51 | 52 | const controls = new OrbitControls( camera, renderer.domElement ); 53 | controls.addEventListener( 'change', render ); // use if there is no animation loop 54 | controls.minDistance = 2; 55 | controls.maxDistance = 10; 56 | controls.target.set( 0, 0, - 0.2 ); 57 | controls.update(); 58 | 59 | window.addEventListener( 'resize', onWindowResize ); 60 | 61 | } 62 | 63 | function onWindowResize() { 64 | 65 | camera.aspect = window.innerWidth / window.innerHeight; 66 | camera.updateProjectionMatrix(); 67 | 68 | renderer.setSize( window.innerWidth, window.innerHeight ); 69 | 70 | render(); 71 | 72 | } 73 | 74 | // 75 | 76 | function render() { 77 | 78 | renderer.render( scene, camera ); 79 | 80 | // requestAnimationFrame(render); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /examples/threejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GLTF Loader 6 | 7 | 15 | 16 | 24 | 67 | 68 | 69 | 70 |
71 | 72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | .code-block { 2 | display: block; 3 | margin: 0; 4 | padding: 0; 5 | position: absolute; 6 | top: 3rem; 7 | left: 3rem; 8 | z-index: 1000; 9 | width: min-content; 10 | height: min-content; 11 | font: 14px Consolas, Courier New, Monaco, Andale Mono, Ubuntu Mono, monospace; 12 | } 13 | 14 | .code-header { 15 | position: absolute; 16 | top: 20px; 17 | color: #58f; 18 | display: flex; 19 | padding: 6px 6px; 20 | background: #58f3; 21 | border-radius: 5px; 22 | user-select: none; 23 | } 24 | 25 | .show-code-btn { 26 | top: 1rem; 27 | user-select: none; 28 | left: 1rem; 29 | position: absolute; 30 | z-index: 1000; 31 | width: min-content; 32 | height: min-content; 33 | padding: 10px; 34 | background: #1a1a1cdd; 35 | border-radius: 50%; 36 | box-shadow: 0 0 5px #0001; 37 | cursor: pointer; 38 | color: #eeeeee; 39 | transition: background-color 0.2s ease-in-out; 40 | font: 18px Consolas, Courier New, Monaco, Andale Mono, Ubuntu Mono, monospace; 41 | } 42 | 43 | .show-code-btn:hover { 44 | background-color: #1a1a1c; 45 | } 46 | 47 | .code-text { 48 | padding: 60px 15px 15px; 49 | max-width: 100%; 50 | max-height: 100%; 51 | height: 100%; 52 | display: block; 53 | overflow-y: scroll; 54 | background-color: #1a1a1cdd; 55 | transition: background-color 0.2s ease-in-out; 56 | } 57 | 58 | .code-block:hover > .code-text { 59 | background-color: #1a1a1c; 60 | } 61 | 62 | .code-text > div { 63 | max-height: 70vh; 64 | max-width: 80vw; 65 | min-width: 500px; 66 | resize: both; 67 | opacity: 1; 68 | } 69 | 70 | .code-header > span { 71 | display: flex; 72 | cursor: pointer; 73 | border-radius: 4px; 74 | padding: 5px 10px; 75 | transition: color 0.2s ease-in-out, background 0.2s ease-in-out; 76 | } 77 | 78 | .code-header > span:hover { 79 | background: #58f3; 80 | color: #fff; 81 | } 82 | 83 | .code-header > span.active { 84 | background: #58f3; 85 | color: #fff; 86 | } 87 | 88 | .code-header-left { 89 | left: 12px; 90 | } 91 | 92 | .code-header-right { 93 | right: 60px; 94 | } 95 | 96 | .code-header-right-2 { 97 | right: 12px; 98 | display: flex; 99 | padding: 0; 100 | background-color: transparent; 101 | } 102 | 103 | .code-header-right > span { 104 | 105 | } 106 | 107 | .btn-icon { 108 | width: 20px; 109 | height: 20px; 110 | } 111 | 112 | .code-header-center { 113 | position: absolute; 114 | left: 50%; 115 | transform: translateX(-50%); 116 | display: flex; 117 | align-items: center; 118 | justify-content: center; 119 | background-color: transparent; 120 | padding: 5px 0; 121 | color: #71d58a; 122 | cursor: text; 123 | font-size: 16px !important; 124 | } 125 | 126 | #code-head-title { 127 | padding: 5px 10px; 128 | background-color: transparent; 129 | } 130 | 131 | [class*=shj-lang-] { 132 | font-size: 16px !important; 133 | } 134 | 135 | /*#code-previewer{*/ 136 | /* position: absolute;*/ 137 | /* top: 0;*/ 138 | /* left: 0;*/ 139 | /* width: 100%;*/ 140 | /* height: 100%;*/ 141 | /*}*/ 142 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {highlightElement} from 'https://unpkg.com/@speed-highlight/core/dist/index.js'; 2 | import themeStyles from './../node_modules/@speed-highlight/core/dist/themes/dark.css'; 3 | import indexStyles from "./index.css"; 4 | import {linkCodepen} from "./codepen.js"; 5 | 6 | export async function setupCodePreview(container, scripts, titles, sourcePrefix, processor, codepenOpts, appendHTML){ 7 | const initHTML = container.outerHTML + '\n' + (appendHTML || ''); 8 | let sources = [] 9 | if(!scripts){ 10 | sources = [document.querySelector('#example-script').textContent, 'js']; 11 | }else { 12 | sources = await Promise.all(scripts.map(async src => [ 13 | typeof src==='string' ? (await (await fetch(src)).text()).trim() : src.textContent.trimEnd(), 14 | typeof src==='string' ? src.split('?')[0].split('.').pop() : 'js'] 15 | )); 16 | } 17 | const previewel = document.createElement('div') 18 | previewel.id = 'code-previewer'; 19 | previewel.innerHTML = ` 20 | 21 | 22 |
<>
23 |
24 | 25 |
26 | ${titles.map((lang) => `${lang}`).join(' ')} 27 |
28 |
29 |
${document.title}
30 |
31 |
32 | copy 33 | source 34 |
35 |
36 |
37 | 38 |

 39 |         
40 | ` 41 | container.appendChild(previewel); 42 | 43 | const codeBlock = document.querySelector('.code-block') 44 | function updateState(){ 45 | window && window.localStorage && window.localStorage.setItem('code-block-state', JSON.stringify({ 46 | open: codeBlock.style.display !== 'none', 47 | fileIndex: currentIndex, 48 | })) 49 | } 50 | 51 | const elem = document.querySelector('.code-text'); 52 | let currentIndex = 0; 53 | function loadScript(title){ 54 | const last = document.querySelector('.code-header-left > span.active') 55 | last && last.classList.remove('active'); 56 | 57 | const index = titles.findIndex(a=>a===title); 58 | currentIndex = index; 59 | const source = sources[index]; 60 | elem.textContent = source[0] 61 | const lang = source[1]; 62 | elem.classList.remove('shj-lang-'+elem.dataset.lang); 63 | elem.classList.add('shj-lang-'+lang); 64 | highlightElement(elem); 65 | const js = processor ? processor(elem.textContent) : elem.textContent; 66 | const head2 = document.querySelector('.code-header-right-2') 67 | head2.querySelector('#codepen-form') && head2.querySelector('#codepen-form').remove(); 68 | if(lang === 'js' || lang === 'ts') 69 | head2.innerHTML += linkCodepen({ 70 | html: initHTML, 71 | js: js, 72 | js_pre_processor: lang === 'ts' ? 'typescript' : 'none', 73 | ...codepenOpts, 74 | }); 75 | const el = document.querySelector('.code-header-left > span:nth-child('+(index+1)+')') 76 | el && el.classList.add('active'); 77 | updateState(); 78 | } 79 | 80 | document.querySelector('.code-header-left').addEventListener('click', (e) => { 81 | if(e.target.tagName === 'SPAN'){ 82 | loadScript(e.target.textContent); 83 | } 84 | }) 85 | document.querySelector('#copy-btn').addEventListener('click', (e) => { 86 | navigator.clipboard && navigator.clipboard.writeText(elem.textContent).then(() => { 87 | e.target.textContent = 'copied'; 88 | setTimeout(() => { 89 | e.target.textContent = 'copy'; 90 | }, 1000) 91 | }) 92 | }) 93 | document.querySelector('#source-btn').addEventListener('click', (e) => { 94 | const ind = currentIndex 95 | if(ind >=0 && ind { 99 | codeBlock.style.display = codeBlock.style.display === 'none' ? 'block' : 'none'; 100 | updateState(); 101 | }) 102 | 103 | const localStorageState = window && window.localStorage.getItem('code-block-state'); 104 | let open_ = false; 105 | let index_ = currentIndex; 106 | if(localStorageState){ 107 | const state = JSON.parse(localStorageState); 108 | if(state.open !== undefined) open_ = state.open; 109 | if(state.fileIndex && state.fileIndex < titles.length) index_ = state.fileIndex; 110 | } 111 | codeBlock.style.display = open_ ? 'block' : 'none'; 112 | loadScript(titles[index_]); 113 | 114 | window.addEventListener('beforeunload', () => { 115 | updateState(); 116 | }) 117 | 118 | return { 119 | close: () => { 120 | codeBlock.style.display = 'none'; 121 | updateState(); 122 | }, 123 | open: () => { 124 | codeBlock.style.display = 'block'; 125 | updateState(); 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * example-code-previewer v0.1.0 4 | * Copyright 2022-2023 repalash 5 | * MIT License 6 | */ 7 | import{highlightElement as e}from"https://unpkg.com/@speed-highlight/core/dist/index.js";async function o(o,n,t,s,r,a,c){const d=o.outerHTML+"\n"+(c||"");let i=[];i=n?await Promise.all(n.map((async e=>["string"==typeof e?(await(await fetch(e)).text()).trim():e.textContent.trimEnd(),"string"==typeof e?e.split("?")[0].split(".").pop():"js"]))):[document.querySelector("#example-script").textContent,"js"];const l=document.createElement("div");l.id="code-previewer",l.innerHTML=`\n \n \n
<>
\n
\n\n
\n ${t.map((e=>`${e}`)).join(" ")}\n
\n
\n
${document.title}
\n
\n
\n copy\n source\n
\n
\n
\n\n
\n        
\n `,o.appendChild(l);const p=document.querySelector(".code-block");function h(){window&&window.localStorage&&window.localStorage.setItem("code-block-state",JSON.stringify({open:"none"!==p.style.display,fileIndex:g}))}const u=document.querySelector(".code-text");let g=0;function f(o){const n=document.querySelector(".code-header-left > span.active");n&&n.classList.remove("active");const s=t.findIndex((e=>e===o));g=s;const c=i[s];u.textContent=c[0];const l=c[1];u.classList.remove("shj-lang-"+u.dataset.lang),u.classList.add("shj-lang-"+l),e(u);const p=r?r(u.textContent):u.textContent,f=document.querySelector(".code-header-right-2");f.querySelector("#codepen-form")&&f.querySelector("#codepen-form").remove(),"js"!==l&&"ts"!==l||(f.innerHTML+=function({html:e,css:o,js:n,...t}){e=e.trim(),o=o.trim(),n=n.trim();const s={title:document.title,description:"",html:e,html_pre_processor:"none",css:o,css_pre_processor:"none",css_starter:"neither",css_prefix_free:!1,js:n,js_pre_processor:"js",js_modernizr:!1,js_library:"",html_classes:"",css_external:"",js_external:"",editors:"101",template:!0,...t};return'
'}({html:d,js:p,js_pre_processor:"ts"===l?"typescript":"none",...a}));const m=document.querySelector(".code-header-left > span:nth-child("+(s+1)+")");m&&m.classList.add("active"),h()}document.querySelector(".code-header-left").addEventListener("click",(e=>{"SPAN"===e.target.tagName&&f(e.target.textContent)})),document.querySelector("#copy-btn").addEventListener("click",(e=>{navigator.clipboard&&navigator.clipboard.writeText(u.textContent).then((()=>{e.target.textContent="copied",setTimeout((()=>{e.target.textContent="copy"}),1e3)}))})),document.querySelector("#source-btn").addEventListener("click",(e=>{const o=g;o>=0&&o{p.style.display="none"===p.style.display?"block":"none",h()}));const m=window&&window.localStorage.getItem("code-block-state");let x=!1,y=g;if(m){const e=JSON.parse(m);void 0!==e.open&&(x=e.open),e.fileIndex&&e.fileIndex{h()})),{close:()=>{p.style.display="none",h()},open:()=>{p.style.display="block",h()}}}export{o as setupCodePreview}; 8 | //# sourceMappingURL=index.js.map 9 | -------------------------------------------------------------------------------- /dist/dependencies.txt: -------------------------------------------------------------------------------- 1 | Name: @speed-highlight/core 2 | Version: 1.1.11 3 | License: CC0-1.0 4 | Private: false 5 | Description: 🌈 Light, fast, and easy to use, dependencies free javascript syntax highlighter, with automatic language detection 6 | Repository: git://github.com/speed-highlight/core.git 7 | Homepage: https://github.com/speed-highlight/core#readme 8 | Author: matubu 9 | License Copyright: 10 | === 11 | 12 | Creative Commons Legal Code 13 | 14 | CC0 1.0 Universal 15 | 16 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 17 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 18 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 19 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 20 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 21 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 22 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 23 | HEREUNDER. 24 | 25 | Statement of Purpose 26 | 27 | The laws of most jurisdictions throughout the world automatically confer 28 | exclusive Copyright and Related Rights (defined below) upon the creator 29 | and subsequent owner(s) (each and all, an "owner") of an original work of 30 | authorship and/or a database (each, a "Work"). 31 | 32 | Certain owners wish to permanently relinquish those rights to a Work for 33 | the purpose of contributing to a commons of creative, cultural and 34 | scientific works ("Commons") that the public can reliably and without fear 35 | of later claims of infringement build upon, modify, incorporate in other 36 | works, reuse and redistribute as freely as possible in any form whatsoever 37 | and for any purposes, including without limitation commercial purposes. 38 | These owners may contribute to the Commons to promote the ideal of a free 39 | culture and the further production of creative, cultural and scientific 40 | works, or to gain reputation or greater distribution for their Work in 41 | part through the use and efforts of others. 42 | 43 | For these and/or other purposes and motivations, and without any 44 | expectation of additional consideration or compensation, the person 45 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 46 | is an owner of Copyright and Related Rights in the Work, voluntarily 47 | elects to apply CC0 to the Work and publicly distribute the Work under its 48 | terms, with knowledge of his or her Copyright and Related Rights in the 49 | Work and the meaning and intended legal effect of CC0 on those rights. 50 | 51 | 1. Copyright and Related Rights. A Work made available under CC0 may be 52 | protected by copyright and related or neighboring rights ("Copyright and 53 | Related Rights"). Copyright and Related Rights include, but are not 54 | limited to, the following: 55 | 56 | i. the right to reproduce, adapt, distribute, perform, display, 57 | communicate, and translate a Work; 58 | ii. moral rights retained by the original author(s) and/or performer(s); 59 | iii. publicity and privacy rights pertaining to a person's image or 60 | likeness depicted in a Work; 61 | iv. rights protecting against unfair competition in regards to a Work, 62 | subject to the limitations in paragraph 4(a), below; 63 | v. rights protecting the extraction, dissemination, use and reuse of data 64 | in a Work; 65 | vi. database rights (such as those arising under Directive 96/9/EC of the 66 | European Parliament and of the Council of 11 March 1996 on the legal 67 | protection of databases, and under any national implementation 68 | thereof, including any amended or successor version of such 69 | directive); and 70 | vii. other similar, equivalent or corresponding rights throughout the 71 | world based on applicable law or treaty, and any national 72 | implementations thereof. 73 | 74 | 2. Waiver. To the greatest extent permitted by, but not in contravention 75 | of, applicable law, Affirmer hereby overtly, fully, permanently, 76 | irrevocably and unconditionally waives, abandons, and surrenders all of 77 | Affirmer's Copyright and Related Rights and associated claims and causes 78 | of action, whether now known or unknown (including existing as well as 79 | future claims and causes of action), in the Work (i) in all territories 80 | worldwide, (ii) for the maximum duration provided by applicable law or 81 | treaty (including future time extensions), (iii) in any current or future 82 | medium and for any number of copies, and (iv) for any purpose whatsoever, 83 | including without limitation commercial, advertising or promotional 84 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 85 | member of the public at large and to the detriment of Affirmer's heirs and 86 | successors, fully intending that such Waiver shall not be subject to 87 | revocation, rescission, cancellation, termination, or any other legal or 88 | equitable action to disrupt the quiet enjoyment of the Work by the public 89 | as contemplated by Affirmer's express Statement of Purpose. 90 | 91 | 3. Public License Fallback. Should any part of the Waiver for any reason 92 | be judged legally invalid or ineffective under applicable law, then the 93 | Waiver shall be preserved to the maximum extent permitted taking into 94 | account Affirmer's express Statement of Purpose. In addition, to the 95 | extent the Waiver is so judged Affirmer hereby grants to each affected 96 | person a royalty-free, non transferable, non sublicensable, non exclusive, 97 | irrevocable and unconditional license to exercise Affirmer's Copyright and 98 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 99 | maximum duration provided by applicable law or treaty (including future 100 | time extensions), (iii) in any current or future medium and for any number 101 | of copies, and (iv) for any purpose whatsoever, including without 102 | limitation commercial, advertising or promotional purposes (the 103 | "License"). The License shall be deemed effective as of the date CC0 was 104 | applied by Affirmer to the Work. Should any part of the License for any 105 | reason be judged legally invalid or ineffective under applicable law, such 106 | partial invalidity or ineffectiveness shall not invalidate the remainder 107 | of the License, and in such case Affirmer hereby affirms that he or she 108 | will not (i) exercise any of his or her remaining Copyright and Related 109 | Rights in the Work or (ii) assert any associated claims and causes of 110 | action with respect to the Work, in either case contrary to Affirmer's 111 | express Statement of Purpose. 112 | 113 | 4. Limitations and Disclaimers. 114 | 115 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 116 | surrendered, licensed or otherwise affected by this document. 117 | b. Affirmer offers the Work as-is and makes no representations or 118 | warranties of any kind concerning the Work, express, implied, 119 | statutory or otherwise, including without limitation warranties of 120 | title, merchantability, fitness for a particular purpose, non 121 | infringement, or the absence of latent or other defects, accuracy, or 122 | the present or absence of errors, whether or not discoverable, all to 123 | the greatest extent permissible under applicable law. 124 | c. Affirmer disclaims responsibility for clearing rights of other persons 125 | that may apply to the Work or any use thereof, including without 126 | limitation any person's Copyright and Related Rights in the Work. 127 | Further, Affirmer disclaims responsibility for obtaining any necessary 128 | consents, permissions or other rights required for any use of the 129 | Work. 130 | d. Affirmer understands and acknowledges that Creative Commons is not a 131 | party to this document and has no duty or obligation with respect to 132 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":["../src/index.js","../src/codepen.js"],"sourcesContent":["import {highlightElement} from 'https://unpkg.com/@speed-highlight/core/dist/index.js';\nimport themeStyles from './../node_modules/@speed-highlight/core/dist/themes/dark.css';\nimport indexStyles from \"./index.css\";\nimport {linkCodepen} from \"./codepen.js\";\n\nexport async function setupCodePreview(container, scripts, titles, sourcePrefix, processor, codepenOpts, appendHTML){\n const initHTML = container.outerHTML + '\\n' + (appendHTML || '');\n let sources = []\n if(!scripts){\n sources = [document.querySelector('#example-script').textContent, 'js'];\n }else {\n sources = await Promise.all(scripts.map(async src => [\n typeof src==='string' ? (await (await fetch(src)).text()).trim() : src.textContent.trimEnd(),\n typeof src==='string' ? src.split('?')[0].split('.').pop() : 'js']\n ));\n }\n const previewel = document.createElement('div')\n previewel.id = 'code-previewer';\n previewel.innerHTML = `\n \n \n
<>
\n
\n\n
\n ${titles.map((lang) => `${lang}`).join(' ')}\n
\n
\n
${document.title}
\n
\n
\n copy\n source\n
\n
\n
\n\n
\n        
\n `\n container.appendChild(previewel);\n\n const codeBlock = document.querySelector('.code-block')\n function updateState(){\n window && window.localStorage && window.localStorage.setItem('code-block-state', JSON.stringify({\n open: codeBlock.style.display !== 'none',\n fileIndex: currentIndex,\n }))\n }\n\n const elem = document.querySelector('.code-text');\n let currentIndex = 0;\n function loadScript(title){\n const last = document.querySelector('.code-header-left > span.active')\n last && last.classList.remove('active');\n\n const index = titles.findIndex(a=>a===title);\n currentIndex = index;\n const source = sources[index];\n elem.textContent = source[0]\n const lang = source[1];\n elem.classList.remove('shj-lang-'+elem.dataset.lang);\n elem.classList.add('shj-lang-'+lang);\n highlightElement(elem);\n const js = processor ? processor(elem.textContent) : elem.textContent;\n const head2 = document.querySelector('.code-header-right-2')\n head2.querySelector('#codepen-form') && head2.querySelector('#codepen-form').remove();\n if(lang === 'js' || lang === 'ts')\n head2.innerHTML += linkCodepen({\n html: initHTML,\n js: js,\n js_pre_processor: lang === 'ts' ? 'typescript' : 'none',\n ...codepenOpts,\n });\n const el = document.querySelector('.code-header-left > span:nth-child('+(index+1)+')')\n el && el.classList.add('active');\n updateState();\n }\n\n document.querySelector('.code-header-left').addEventListener('click', (e) => {\n if(e.target.tagName === 'SPAN'){\n loadScript(e.target.textContent);\n }\n })\n document.querySelector('#copy-btn').addEventListener('click', (e) => {\n navigator.clipboard && navigator.clipboard.writeText(elem.textContent).then(() => {\n e.target.textContent = 'copied';\n setTimeout(() => {\n e.target.textContent = 'copy';\n }, 1000)\n })\n })\n document.querySelector('#source-btn').addEventListener('click', (e) => {\n const ind = currentIndex\n if(ind >=0 && ind {\n codeBlock.style.display = codeBlock.style.display === 'none' ? 'block' : 'none';\n updateState();\n })\n\n const localStorageState = window && window.localStorage.getItem('code-block-state');\n let open_ = false;\n let index_ = currentIndex;\n if(localStorageState){\n const state = JSON.parse(localStorageState);\n if(state.open !== undefined) open_ = state.open;\n if(state.fileIndex && state.fileIndex < titles.length) index_ = state.fileIndex;\n }\n codeBlock.style.display = open_ ? 'block' : 'none';\n loadScript(titles[index_]);\n\n window.addEventListener('beforeunload', () => {\n updateState();\n })\n\n return {\n close: () => {\n codeBlock.style.display = 'none';\n updateState();\n },\n open: () => {\n codeBlock.style.display = 'block';\n updateState();\n }\n }\n\n}\n","// https://blog.codepen.io/documentation/prefill/\nexport function linkCodepen({html, css, js, ...opts}) {\n\n html = html.trim();\n css = css.trim();\n js = js.trim();\n\n const data = {\n title: document.title,\n description: \"\",\n html: html,\n html_pre_processor: \"none\",\n css: css,\n css_pre_processor: \"none\",\n css_starter: \"neither\",\n css_prefix_free: false,\n js: js,\n js_pre_processor: 'js',\n js_modernizr: false,\n js_library: \"\",\n html_classes: \"\",\n css_external: \"\",\n js_external: \"\",\n editors: \"101\",\n template: true,\n ...opts\n };\n\n const JSONstring =\n JSON.stringify(data)\n // Quotes will screw up the JSON\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n\n const form =\n '
' +\n '' +\n '' +\n '
';\n\n return form\n}\n"],"names":["async","setupCodePreview","container","scripts","titles","sourcePrefix","processor","codepenOpts","appendHTML","initHTML","outerHTML","sources","Promise","all","map","src","fetch","text","trim","textContent","trimEnd","split","pop","document","querySelector","previewel","createElement","id","innerHTML","lang","join","title","appendChild","codeBlock","updateState","window","localStorage","setItem","JSON","stringify","open","style","display","fileIndex","currentIndex","elem","loadScript","last","classList","remove","index","findIndex","a","source","dataset","add","highlightElement","js","head2","html","css","opts","data","description","html_pre_processor","css_pre_processor","css_starter","css_prefix_free","js_pre_processor","js_modernizr","js_library","html_classes","css_external","js_external","editors","template","replace","linkCodepen","el","addEventListener","e","target","tagName","navigator","clipboard","writeText","then","setTimeout","ind","length","localStorageState","getItem","open_","index_","state","parse","undefined","close"],"mappings":"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"} --------------------------------------------------------------------------------