├── .github └── FUNDING.yml ├── .prettierrc.yml ├── .prettierrc ├── public ├── cat1.jpg ├── cat2.jpg ├── favicon.ico ├── forest1.jpg ├── forest2.jpg ├── favicon.1.ico └── index.html ├── babel.config.js ├── vue.config.js ├── src ├── main.js ├── assets │ ├── prism.css │ └── prism.js ├── App.vue └── VueCompareImage.vue ├── CHANGELOG.md ├── .gitignore ├── .all-contributorsrc ├── package.json └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: junkboy0315 2 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | trailingComma: all -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | trailingComma: "all" 2 | tabWidth: 2 3 | singleQuote: true 4 | -------------------------------------------------------------------------------- /public/cat1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/cat1.jpg -------------------------------------------------------------------------------- /public/cat2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/cat2.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/forest1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/forest1.jpg -------------------------------------------------------------------------------- /public/forest2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/forest2.jpg -------------------------------------------------------------------------------- /public/favicon.1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tam315/vue-compare-image/HEAD/public/favicon.1.ico -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // bundle css into commonjs bundle 3 | css: { extract: false }, 4 | }; 5 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app'); 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.1.5] - 2019-10-23 4 | 5 | ### New Features 6 | 7 | - Add a feature to display labels. 8 | 9 | ## [0.1.4] - 2019-10-21 10 | 11 | ### New Features 12 | 13 | - Add `leftImageAlt` and `rightImageAlt` props. 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-compare-image 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "junkboy0315", 10 | "name": "Shota Tamura", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/10986861?v=4", 12 | "profile": "https://www.yuuniworks.com/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "lukasirsak", 19 | "name": "Lukáš Irsák", 20 | "avatar_url": "https://avatars2.githubusercontent.com/u/30669262?v=4", 21 | "profile": "https://github.com/lukasirsak", 22 | "contributions": [ 23 | "code" 24 | ] 25 | }, 26 | { 27 | "login": "jimmyangel", 28 | "name": "Ricardo Morin", 29 | "avatar_url": "https://avatars0.githubusercontent.com/u/6842945?v=4", 30 | "profile": "https://morinricardo.com", 31 | "contributions": [ 32 | "code" 33 | ] 34 | } 35 | ], 36 | "contributorsPerLine": 7, 37 | "projectName": "vue-compare-image", 38 | "projectOwner": "junkboy0315", 39 | "repoType": "github", 40 | "repoHost": "https://github.com" 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-compare-image", 3 | "version": "0.2.0", 4 | "description": "Vue component to compare two images using slider.", 5 | "author": "Shota Tamura", 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint", 10 | "build:lib": "vue-cli-service build --target lib --name vueCompareImage ./src/VueCompareImage.vue", 11 | "prepublishOnly": "npm run build:lib" 12 | }, 13 | "main": "dist/vueCompareImage.common.js", 14 | "files": [ 15 | "dist/*", 16 | "src/*", 17 | "public/*", 18 | "*.json", 19 | "*.js" 20 | ], 21 | "dependencies": { 22 | "core-js": "^3.1.2", 23 | "css-element-queries": "^1.0.2", 24 | "vue": "^2.5.17" 25 | }, 26 | "devDependencies": { 27 | "@vue/cli-plugin-babel": "^4.0.4", 28 | "@vue/cli-plugin-eslint": "^4.0.4", 29 | "@vue/cli-service": "^4.0.4", 30 | "@vue/eslint-config-airbnb": "^4.0.1", 31 | "babel-eslint": "^10.0.3", 32 | "eslint": "^6.5.1", 33 | "eslint-plugin-vue": "^5.2.3", 34 | "node-sass": "^4.9.0", 35 | "sass-loader": "^8.0.0", 36 | "vue-template-compiler": "^2.5.17" 37 | }, 38 | "eslintConfig": { 39 | "root": true, 40 | "env": { 41 | "node": true 42 | }, 43 | "extends": [ 44 | "plugin:vue/essential", 45 | "@vue/airbnb" 46 | ], 47 | "rules": { 48 | "no-mixed-operators": 0, 49 | "no-irregular-whitespace": 0, 50 | "prefer-template": 0 51 | }, 52 | "parserOptions": { 53 | "parser": "babel-eslint" 54 | } 55 | }, 56 | "postcss": { 57 | "plugins": { 58 | "autoprefixer": {} 59 | } 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not ie <= 8" 65 | ], 66 | "keywords": [ 67 | "picture comparison", 68 | "image comparison", 69 | "slider", 70 | "react", 71 | "twentytwenty" 72 | ], 73 | "license": "MIT", 74 | "repository": { 75 | "type": "git", 76 | "url": "junkboy0315/vue-compare-image" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/assets/prism.css: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /* PrismJS 1.14.0 4 | http://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+clike+javascript+jsx */ 5 | /** 6 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML 7 | * Based on https://github.com/chriskempson/tomorrow-theme 8 | * @author Rose Pritchard 9 | */ 10 | 11 | code[class*='language-'], 12 | pre[class*='language-'] { 13 | color: #ccc; 14 | background: none; 15 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 16 | text-align: left; 17 | white-space: pre; 18 | word-spacing: normal; 19 | word-break: normal; 20 | word-wrap: normal; 21 | line-height: 1.5; 22 | 23 | -moz-tab-size: 4; 24 | -o-tab-size: 4; 25 | tab-size: 4; 26 | 27 | -webkit-hyphens: none; 28 | -moz-hyphens: none; 29 | -ms-hyphens: none; 30 | hyphens: none; 31 | } 32 | 33 | /* Code blocks */ 34 | pre[class*='language-'] { 35 | padding: 1em; 36 | margin: 0.5em 0; 37 | overflow: auto; 38 | } 39 | 40 | :not(pre) > code[class*='language-'], 41 | pre[class*='language-'] { 42 | background: #2d2d2d; 43 | } 44 | 45 | /* Inline code */ 46 | :not(pre) > code[class*='language-'] { 47 | padding: 0.1em; 48 | border-radius: 0.3em; 49 | white-space: normal; 50 | } 51 | 52 | .token.comment, 53 | .token.block-comment, 54 | .token.prolog, 55 | .token.doctype, 56 | .token.cdata { 57 | color: #999; 58 | } 59 | 60 | .token.punctuation { 61 | color: #ccc; 62 | } 63 | 64 | .token.tag, 65 | .token.attr-name, 66 | .token.namespace, 67 | .token.deleted { 68 | color: #e2777a; 69 | } 70 | 71 | .token.function-name { 72 | color: #6196cc; 73 | } 74 | 75 | .token.boolean, 76 | .token.number, 77 | .token.function { 78 | color: #f08d49; 79 | } 80 | 81 | .token.property, 82 | .token.class-name, 83 | .token.constant, 84 | .token.symbol { 85 | color: #f8c555; 86 | } 87 | 88 | .token.selector, 89 | .token.important, 90 | .token.atrule, 91 | .token.keyword, 92 | .token.builtin { 93 | color: #cc99cd; 94 | } 95 | 96 | .token.string, 97 | .token.char, 98 | .token.attr-value, 99 | .token.regex, 100 | .token.variable { 101 | color: #7ec699; 102 | } 103 | 104 | .token.operator, 105 | .token.entity, 106 | .token.url { 107 | color: #67cdcc; 108 | } 109 | 110 | .token.important, 111 | .token.bold { 112 | font-weight: bold; 113 | } 114 | .token.italic { 115 | font-style: italic; 116 | } 117 | 118 | .token.entity { 119 | cursor: help; 120 | } 121 | 122 | .token.inserted { 123 | color: green; 124 | } 125 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 121 | 122 | 127 | 128 | 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Compare Image 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors) 4 | 5 | Simple Vue.js component to compare two images using slider. 6 | 7 | ![img](https://user-images.githubusercontent.com/10986861/67158760-0f02a480-f377-11e9-9b83-75bc8005693a.gif) 8 | 9 | NOTE: [React Version](https://github.com/junkboy0315/react-compare-image) is also available! 10 | 11 | ## Demo 12 | 13 | [DEMO](https://vue-compare-image.yuuniworks.com/) 14 | 15 | ## Features 16 | 17 | - Simple 18 | - Responsive (fit to the parent width) 19 | - Size difference between two images handled correctly. Element size determined by following two factors: 20 | - width of the parent 21 | - right image's aspect ratio 22 | 23 | ## How to use 24 | 25 | In the shell: 26 | 27 | ```bash 28 | yarn add vue-compare-image 29 | 30 | // or 31 | 32 | npm install --save vue-compare-image 33 | ``` 34 | 35 | In your component file: 36 | 37 | ```js 38 | import VueCompareImage from 'vue-compare-image'; 39 | 40 | export default { 41 | name: 'app', 42 | components: { VueCompareImage }, 43 | }; 44 | ``` 45 | 46 | ```xml 47 | 50 | ``` 51 | 52 | ## Props 53 | 54 | | Prop (\* required) | type | default | description | 55 | | ------------------------ | -------------- | :-----: | ------------------------------------ | 56 | | handleSize | number (px) | 40 | diameter of slider handle (by pixel) | 57 | | hover | boolean | false | Whether to slide at hover | 58 | | leftImage \* | string | null | left image's url | 59 | | leftImageAlt | string | null | left image's alt | 60 | | leftLabel | string | null | Left image text label | 61 | | rightImage \* | string | null | right image's url | 62 | | rightImageAlt | string | null | right image's alt | 63 | | rightLabel | string | null | Right image text label | 64 | | sliderLineWidth | number (px) | 2 | line width of slider (by pixel) | 65 | | sliderPositionPercentage | number (float) | 0.5 | Starting line position (from 0 to 1) | 66 | 67 | ## Dependencies 68 | 69 | - [css-element-queries](https://github.com/marcj/css-element-queries) to detect element resize event. 70 | 71 | ## Contributors 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
Shota Tamura
Shota Tamura

💻
Lukáš Irsák
Lukáš Irsák

💻
Ricardo Morin
Ricardo Morin

💻
82 | 83 | 84 | -------------------------------------------------------------------------------- /src/VueCompareImage.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 206 | 207 | 208 | 284 | -------------------------------------------------------------------------------- /src/assets/prism.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /* PrismJS 1.14.0 4 | http://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+clike+javascript+jsx */ 5 | var _self = 6 | 'undefined' != typeof window 7 | ? window 8 | : 'undefined' != typeof WorkerGlobalScope && 9 | self instanceof WorkerGlobalScope 10 | ? self 11 | : {}, 12 | Prism = (function() { 13 | var e = /\blang(?:uage)?-([\w-]+)\b/i, 14 | t = 0, 15 | n = (_self.Prism = { 16 | manual: _self.Prism && _self.Prism.manual, 17 | disableWorkerMessageHandler: 18 | _self.Prism && _self.Prism.disableWorkerMessageHandler, 19 | util: { 20 | encode: function(e) { 21 | return e instanceof r 22 | ? new r(e.type, n.util.encode(e.content), e.alias) 23 | : 'Array' === n.util.type(e) 24 | ? e.map(n.util.encode) 25 | : e 26 | .replace(/&/g, '&') 27 | .replace(/ e.length) return; 207 | if (!(w instanceof s)) { 208 | if (m && b != t.length - 1) { 209 | h.lastIndex = k; 210 | var _ = h.exec(e); 211 | if (!_) break; 212 | for ( 213 | var j = _.index + (d ? _[1].length : 0), 214 | P = _.index + _[0].length, 215 | A = b, 216 | x = k, 217 | O = t.length; 218 | O > A && (P > x || (!t[A].type && !t[A - 1].greedy)); 219 | ++A 220 | ) 221 | (x += t[A].length), j >= x && (++b, (k = x)); 222 | if (t[b] instanceof s) continue; 223 | (I = A - b), (w = e.slice(k, x)), (_.index -= k); 224 | } else { 225 | h.lastIndex = 0; 226 | var _ = h.exec(w), 227 | I = 1; 228 | } 229 | if (_) { 230 | d && (p = _[1] ? _[1].length : 0); 231 | var j = _.index + p, 232 | _ = _[0].slice(p), 233 | P = j + _.length, 234 | N = w.slice(0, j), 235 | S = w.slice(P), 236 | C = [b, I]; 237 | N && (++b, (k += N.length), C.push(N)); 238 | var E = new s(u, f ? n.tokenize(_, f) : _, y, _, m); 239 | if ( 240 | (C.push(E), 241 | S && C.push(S), 242 | Array.prototype.splice.apply(t, C), 243 | 1 != I && n.matchGrammar(e, t, r, b, k, !0, u), 244 | i) 245 | ) 246 | break; 247 | } else if (i) break; 248 | } 249 | } 250 | } 251 | } 252 | }, 253 | tokenize: function(e, t) { 254 | var r = [e], 255 | a = t.rest; 256 | if (a) { 257 | for (var l in a) t[l] = a[l]; 258 | delete t.rest; 259 | } 260 | return n.matchGrammar(e, r, t, 0, 0, !1), r; 261 | }, 262 | hooks: { 263 | all: {}, 264 | add: function(e, t) { 265 | var r = n.hooks.all; 266 | (r[e] = r[e] || []), r[e].push(t); 267 | }, 268 | run: function(e, t) { 269 | var r = n.hooks.all[e]; 270 | if (r && r.length) for (var a, l = 0; (a = r[l++]); ) a(t); 271 | }, 272 | }, 273 | }), 274 | r = (n.Token = function(e, t, n, r, a) { 275 | (this.type = e), 276 | (this.content = t), 277 | (this.alias = n), 278 | (this.length = 0 | (r || '').length), 279 | (this.greedy = !!a); 280 | }); 281 | if ( 282 | ((r.stringify = function(e, t, a) { 283 | if ('string' == typeof e) return e; 284 | if ('Array' === n.util.type(e)) 285 | return e 286 | .map(function(n) { 287 | return r.stringify(n, t, e); 288 | }) 289 | .join(''); 290 | var l = { 291 | type: e.type, 292 | content: r.stringify(e.content, t, a), 293 | tag: 'span', 294 | classes: ['token', e.type], 295 | attributes: {}, 296 | language: t, 297 | parent: a, 298 | }; 299 | if (e.alias) { 300 | var i = 'Array' === n.util.type(e.alias) ? e.alias : [e.alias]; 301 | Array.prototype.push.apply(l.classes, i); 302 | } 303 | n.hooks.run('wrap', l); 304 | var o = Object.keys(l.attributes) 305 | .map(function(e) { 306 | return ( 307 | e + '="' + (l.attributes[e] || '').replace(/"/g, '"') + '"' 308 | ); 309 | }) 310 | .join(' '); 311 | return ( 312 | '<' + 313 | l.tag + 314 | ' class="' + 315 | l.classes.join(' ') + 316 | '"' + 317 | (o ? ' ' + o : '') + 318 | '>' + 319 | l.content + 320 | '' 323 | ); 324 | }), 325 | !_self.document) 326 | ) 327 | return _self.addEventListener 328 | ? (n.disableWorkerMessageHandler || 329 | _self.addEventListener( 330 | 'message', 331 | function(e) { 332 | var t = JSON.parse(e.data), 333 | r = t.language, 334 | a = t.code, 335 | l = t.immediateClose; 336 | _self.postMessage(n.highlight(a, n.languages[r], r)), 337 | l && _self.close(); 338 | }, 339 | !1, 340 | ), 341 | _self.Prism) 342 | : _self.Prism; 343 | var a = 344 | document.currentScript || 345 | [].slice.call(document.getElementsByTagName('script')).pop(); 346 | return ( 347 | a && 348 | ((n.filename = a.src), 349 | n.manual || 350 | a.hasAttribute('data-manual') || 351 | ('loading' !== document.readyState 352 | ? window.requestAnimationFrame 353 | ? window.requestAnimationFrame(n.highlightAll) 354 | : window.setTimeout(n.highlightAll, 16) 355 | : document.addEventListener('DOMContentLoaded', n.highlightAll))), 356 | _self.Prism 357 | ); 358 | })(); 359 | 'undefined' != typeof module && module.exports && (module.exports = Prism), 360 | 'undefined' != typeof global && (global.Prism = Prism); 361 | (Prism.languages.markup = { 362 | comment: //, 363 | prolog: /<\?[\s\S]+?\?>/, 364 | doctype: //i, 365 | cdata: //i, 366 | tag: { 367 | pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i, 368 | greedy: !0, 369 | inside: { 370 | tag: { 371 | pattern: /^<\/?[^\s>\/]+/i, 372 | inside: { punctuation: /^<\/?/, namespace: /^[^\s>\/:]+:/ }, 373 | }, 374 | 'attr-value': { 375 | pattern: /=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i, 376 | inside: { 377 | punctuation: [/^=/, { pattern: /(^|[^\\])["']/, lookbehind: !0 }], 378 | }, 379 | }, 380 | punctuation: /\/?>/, 381 | 'attr-name': { 382 | pattern: /[^\s>\/]+/, 383 | inside: { namespace: /^[^\s>\/:]+:/ }, 384 | }, 385 | }, 386 | }, 387 | entity: /&#?[\da-z]{1,8};/i, 388 | }), 389 | (Prism.languages.markup.tag.inside['attr-value'].inside.entity = 390 | Prism.languages.markup.entity), 391 | Prism.hooks.add('wrap', function(a) { 392 | 'entity' === a.type && 393 | (a.attributes.title = a.content.replace(/&/, '&')); 394 | }), 395 | (Prism.languages.xml = Prism.languages.markup), 396 | (Prism.languages.html = Prism.languages.markup), 397 | (Prism.languages.mathml = Prism.languages.markup), 398 | (Prism.languages.svg = Prism.languages.markup); 399 | Prism.languages.clike = { 400 | comment: [ 401 | { pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, lookbehind: !0 }, 402 | { pattern: /(^|[^\\:])\/\/.*/, lookbehind: !0, greedy: !0 }, 403 | ], 404 | string: { 405 | pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, 406 | greedy: !0, 407 | }, 408 | 'class-name': { 409 | pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i, 410 | lookbehind: !0, 411 | inside: { punctuation: /[.\\]/ }, 412 | }, 413 | keyword: /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, 414 | boolean: /\b(?:true|false)\b/, 415 | function: /[a-z0-9_]+(?=\()/i, 416 | number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, 417 | operator: /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, 418 | punctuation: /[{}[\];(),.:]/, 419 | }; 420 | (Prism.languages.javascript = Prism.languages.extend('clike', { 421 | keyword: /\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/, 422 | number: /\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/, 423 | function: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i, 424 | operator: /-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/, 425 | })), 426 | Prism.languages.insertBefore('javascript', 'keyword', { 427 | regex: { 428 | pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^\/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/, 429 | lookbehind: !0, 430 | greedy: !0, 431 | }, 432 | 'function-variable': { 433 | pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i, 434 | alias: 'function', 435 | }, 436 | constant: /\b[A-Z][A-Z\d_]*\b/, 437 | }), 438 | Prism.languages.insertBefore('javascript', 'string', { 439 | 'template-string': { 440 | pattern: /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/, 441 | greedy: !0, 442 | inside: { 443 | interpolation: { 444 | pattern: /\${[^}]+}/, 445 | inside: { 446 | 'interpolation-punctuation': { 447 | pattern: /^\${|}$/, 448 | alias: 'punctuation', 449 | }, 450 | rest: null, 451 | }, 452 | }, 453 | string: /[\s\S]+/, 454 | }, 455 | }, 456 | }), 457 | (Prism.languages.javascript[ 458 | 'template-string' 459 | ].inside.interpolation.inside.rest = Prism.languages.javascript), 460 | Prism.languages.markup && 461 | Prism.languages.insertBefore('markup', 'tag', { 462 | script: { 463 | pattern: /()[\s\S]*?(?=<\/script>)/i, 464 | lookbehind: !0, 465 | inside: Prism.languages.javascript, 466 | alias: 'language-javascript', 467 | greedy: !0, 468 | }, 469 | }), 470 | (Prism.languages.js = Prism.languages.javascript); 471 | !(function(t) { 472 | var n = t.util.clone(t.languages.javascript); 473 | (t.languages.jsx = t.languages.extend('markup', n)), 474 | (t.languages.jsx.tag.pattern = /<\/?(?:[\w.:-]+\s*(?:\s+(?:[\w.:-]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s{'">=]+|\{(?:\{(?:\{[^}]*\}|[^{}])*\}|[^{}])+\}))?|\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}))*\s*\/?)?>/i), 475 | (t.languages.jsx.tag.inside.tag.pattern = /^<\/?[^\s>\/]*/i), 476 | (t.languages.jsx.tag.inside[ 477 | 'attr-value' 478 | ].pattern = /=(?!\{)(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">]+)/i), 479 | t.languages.insertBefore( 480 | 'inside', 481 | 'attr-name', 482 | { 483 | spread: { 484 | pattern: /\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}/, 485 | inside: { punctuation: /\.{3}|[{}.]/, 'attr-value': /\w+/ }, 486 | }, 487 | }, 488 | t.languages.jsx.tag, 489 | ), 490 | t.languages.insertBefore( 491 | 'inside', 492 | 'attr-value', 493 | { 494 | script: { 495 | pattern: /=(\{(?:\{(?:\{[^}]*\}|[^}])*\}|[^}])+\})/i, 496 | inside: { 497 | 'script-punctuation': { pattern: /^=(?={)/, alias: 'punctuation' }, 498 | rest: t.languages.jsx, 499 | }, 500 | alias: 'language-javascript', 501 | }, 502 | }, 503 | t.languages.jsx.tag, 504 | ); 505 | var e = function(t) { 506 | return t 507 | ? 'string' == typeof t 508 | ? t 509 | : 'string' == typeof t.content 510 | ? t.content 511 | : t.content.map(e).join('') 512 | : ''; 513 | }, 514 | a = function(n) { 515 | for (var s = [], g = 0; g < n.length; g++) { 516 | var o = n[g], 517 | i = !1; 518 | if ( 519 | ('string' != typeof o && 520 | ('tag' === o.type && o.content[0] && 'tag' === o.content[0].type 521 | ? ' 0 && 523 | s[s.length - 1].tagName === e(o.content[0].content[1]) && 524 | s.pop() 525 | : '/>' === o.content[o.content.length - 1].content || 526 | s.push({ 527 | tagName: e(o.content[0].content[1]), 528 | openedBraces: 0, 529 | }) 530 | : s.length > 0 && 'punctuation' === o.type && '{' === o.content 531 | ? s[s.length - 1].openedBraces++ 532 | : s.length > 0 && 533 | s[s.length - 1].openedBraces > 0 && 534 | 'punctuation' === o.type && 535 | '}' === o.content 536 | ? s[s.length - 1].openedBraces-- 537 | : (i = !0)), 538 | (i || 'string' == typeof o) && 539 | s.length > 0 && 540 | 0 === s[s.length - 1].openedBraces) 541 | ) { 542 | var p = e(o); 543 | g < n.length - 1 && 544 | ('string' == typeof n[g + 1] || 'plain-text' === n[g + 1].type) && 545 | ((p += e(n[g + 1])), n.splice(g + 1, 1)), 546 | g > 0 && 547 | ('string' == typeof n[g - 1] || 'plain-text' === n[g - 1].type) && 548 | ((p = e(n[g - 1]) + p), n.splice(g - 1, 1), g--), 549 | (n[g] = new t.Token('plain-text', p, null, p)); 550 | } 551 | o.content && 'string' != typeof o.content && a(o.content); 552 | } 553 | }; 554 | t.hooks.add('after-tokenize', function(t) { 555 | ('jsx' === t.language || 'tsx' === t.language) && a(t.tokens); 556 | }); 557 | })(Prism); 558 | --------------------------------------------------------------------------------