diff --git a/.factory/LICENSE.txt b/.factory/LICENSE.txt new file mode 100644 index 0000000..95d16bc --- /dev/null +++ b/.factory/LICENSE.txt @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2006, Ivan Sagalaev. +Copyright (c) %(year), Taufik Nurrohman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/.factory/index.html.pug b/.factory/index.html.pug new file mode 100644 index 0000000..e58b107 --- /dev/null +++ b/.factory/index.html.pug @@ -0,0 +1,62 @@ +html + head + meta(content='width=device-width' name='viewport') + meta(charset='utf-8') + title Line Number Plugin for Highlight.js + link(href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css' rel='stylesheet') + body + header + h1 Line Number Plugin for #[a(href='https://highlightjs.org' rel='nofollow' target='_blank') Highlight.js] + p Works for all themes. No configuration needed. Number and border color will inherit to your theme color. + main + h2 CSS + pre: code.hljs.language-css + | * { + | box-sizing: border-box; + | color: inherit; + | font: inherit; + | margin: 0; + | padding: 0; + | } + | + | :root { + | background: #fff; + | color: #000; + | font: normal normal 16px/1.4 sans-serif; + | } + | + | body { + | padding: 2em; + | } + | + | #main { + | margin: 0 auto; + | max-width: 600px; + | } + h2 HTML + pre: code.hljs.language-html + | <main id="main"> + | <p> + | Loading&hellip; + | </p> + | </main> + h2 JavaScript + pre: code.hljs.language-js + | let controller = new AbortController; + | + | fetch('https://example.com/rest/index.json', { + | signal: controller.signal + | }).then(response => response.json()).then(json => { + | let out = ""; + | for (let v of json) { + | out += '<article>'; + | out += '<h2>' + v.title + '</h2>'; + | out += '<p>' + v.description + '</p>'; + | out += '</article>'; + | } + | document.querySelector('#main').innerHTML = out; + | }); + script(src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js') + script(src='index.min.js') + script + | hljs.highlightAll(); \ No newline at end of file diff --git a/.factory/index.js.mjs b/.factory/index.js.mjs new file mode 100644 index 0000000..4fb7b54 --- /dev/null +++ b/.factory/index.js.mjs @@ -0,0 +1,53 @@ +import {getName, getParent, getStyle, setClass, setElement, setHTML, setStyles, W} from '@taufik-nurrohman/document'; +import {isDefined} from '@taufik-nurrohman/is'; + +function getColorFrom(node) { + let color = getStyle(node, 'color') || "", c; + // + if (c = color.match(/^rgba\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]|0?\.\d+)\s*\)$/i)) { + return [+c[1], +c[2], +c[3], +c[4]]; + } + if (c = color.match(/^rgb\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*\)$/i)) { + return [+c[1], +c[2], +c[3], 1]; + } + return [0, 0, 0, 1]; +} + +const LineNumberPlugin = { + 'after:highlightElement': function ({el, result, text}) { + let theCode = el, + theCodeLines = setElement(getName(el)), + theCodeNumbers = [], + theCodeParent = getParent(el), + theCodeParentIsValid = theCodeParent && 'pre' === getName(theCodeParent); + if (theCodeParentIsValid) { + for (let i = 0, j = text.split(/\n/).length; i < j; ++i) { + theCodeNumbers.push('' + (i + 1) + ''); + } + theCodeParent.insertBefore(theCodeLines, theCode); + setStyles(theCode, { + 'flex': '1' + }); + setStyles(theCodeParent, { + 'direction': 'ltr', + 'display': 'flex' + }); + setHTML(theCodeLines, theCodeNumbers.join('\n')); + setClass(theCodeLines, 'hljs'); // Inherit `background` and `padding` value(s) from the style sheet + let [r, g, b, a] = getColorFrom(theCodeLines); + setStyles(theCodeLines, { + 'border-right': '2px solid rgba(' + r + ',' + g + ',' + b + ',' + (a / 10) + ')', + 'text-align': 'right', + 'user-select': 'none' // Disable selection on number(s) + }); + } + return {el, result, text}; + } +} + +// Is a web browser +if (isDefined(W) && isDefined(W.hljs)) { + W.hljs.addPlugin(LineNumberPlugin); +} + +export default LineNumberPlugin; \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..28e0335 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* linguist-vendored +*.js linguist-vendored=false +.factory export-ignore +.gitattributes export-ignore +.github export-ignore +.gitignore export-ignore +README.md export-ignore \ No newline at end of file diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index 0190951..0000000 --- a/.github/workflows/nodejs.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Publish - -on: - release: - types: [published] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 12 - registry-url: https://registry.npmjs.org/ - - run: yarn install - - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml new file mode 100644 index 0000000..eb50732 --- /dev/null +++ b/.github/workflows/npm.yml @@ -0,0 +1,16 @@ +name: 'Publish to NPM' +on: + release: + types: [published] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: 'actions/checkout@v1' + - uses: 'actions/setup-node@v1' + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: 'npm publish --access=public' + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/LICENSE b/LICENSE index a2b5438..96c01a1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ BSD 3-Clause License Copyright (c) 2006, Ivan Sagalaev. -Copyright (c) 2020, Taufik Nurrohman +Copyright (c) 2024, Taufik Nurrohman All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,4 +27,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index ec1ba64..3df61f9 100644 --- a/README.md +++ b/README.md @@ -8,31 +8,47 @@ Line Number Plugin for [Highlight.js](https://github.com/highlightjs/highlight.j Usage ----- -### Browsers +### Browser -~~~ .html +~~~ html - + ~~~ -### ES6 Modules (TODO) +### Node.js -~~~ .js +~~~ js import hljs from 'highlight.js/lib/core'; import css from 'highlight.js/lib/languages/css'; -import {highlightWithLineNumbers} from 'highlight.ln.js'; +import LineNumberPlugin from '@taufik-nurrohman/highlight.ln.js'; + +hljs.addPlugin(LineNumberPlugin); + +hljs.registerLanguage('css', css); + +hljs.highlightAll(); +~~~ + +### CommonJS + +~~~ js +const hljs = require('highlight.js/lib/core'); +const css = require('highlight.js/lib/languages/css'); + +const LineNumberPlugin = require('@taufik-nurrohman/highlight.ln.js').default; + +hljs.addPlugin(LineNumberPlugin); hljs.registerLanguage('css', css); -const highlighted = hljs.highlight('css', '#foo { bar: baz; }'); -const highlightedMarkup = highlightWithLineNumbers(highlighted); +hljs.highlightAll(); ~~~ License ------- -BSD +BSD \ No newline at end of file diff --git a/highlight.ln.js b/highlight.ln.js deleted file mode 100644 index 28ff89a..0000000 --- a/highlight.ln.js +++ /dev/null @@ -1,35 +0,0 @@ -((win, doc, hljs) => { - function getColorParts(el) { - let color = win.getComputedStyle(el).color || "", c; - // - if (c = color.match(/^rgba\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]|0?\.\d+)\s*\)$/i)) { - return [+c[1], +c[2], +c[3], +c[4]]; - } - if (c = color.match(/^rgb\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*\)$/i)) { - return [+c[1], +c[2], +c[3], 1]; - } - return [0, 0, 0, 1]; - } - hljs.addPlugin({ - 'after:highlightBlock': ({block}) => { - let blockParent = block.parentNode, - blockHasParent = blockParent && 'pre' === blockParent.nodeName.toLowerCase(), - lines = doc.createElement('code'), - numbers = []; - if (blockHasParent) { - for (let i = 0, j = block.textContent.split(/\n/).length; i < j; ++i) { - numbers.push(i + 1); - } - blockParent.insertBefore(lines, block); - blockParent.style.display = 'flex'; - lines.innerHTML = numbers.join('\n'); - lines.style.textAlign = 'right'; - lines.style.userSelect = 'none'; // Disable selection - lines.className = 'hljs'; // Inherit `background` and `padding` from the style sheet - let rgba = getColorParts(lines); - lines.style.borderRight = '2px solid rgba(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ',' + (rgba[3] / 10) + ')'; - block.style.flex = 1; - } - } - }); -})(window, document, hljs); diff --git a/highlight.ln.min.js b/highlight.ln.min.js deleted file mode 100644 index f661e60..0000000 --- a/highlight.ln.min.js +++ /dev/null @@ -1 +0,0 @@ -((e,t,s)=>{s.addPlugin({"after:highlightBlock":({block:s})=>{let l=s.parentNode,n=l&&"pre"===l.nodeName.toLowerCase(),o=t.createElement("code"),r=[];if(n){for(let e=0,t=s.textContent.split(/\n/).length;e' + generateLineNumbers(value) + '' + value + ''; -} - -export {generateLineNumbers, highlightWithLineNumbers}; diff --git a/index.html b/index.html index bde74ae..4bd41b3 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,10 @@ - + Line Number Plugin for Highlight.js - +
@@ -13,18 +13,18 @@

Line Number Plugin for

CSS

-
* {
+      
* {
+  box-sizing: border-box;
+  color: inherit;
+  font: inherit;
   margin: 0;
   padding: 0;
-  font: inherit;
-  color: inherit;
-  box-sizing: border-box;
 }
 
-html {
+:root {
   background: #fff;
-  font: normal normal 16px/1.4 sans-serif;
   color: #000;
+  font: normal normal 16px/1.4 sans-serif;
 }
 
 body {
@@ -32,18 +32,21 @@ 

CSS

} #main { - max-width: 600px; margin: 0 auto; + max-width: 600px; }

HTML

-
<main id="main">
-  <p>Loading&hellip;</p>
+      
<main id="main">
+  <p>
+    Loading&hellip;
+  </p>
 </main>

JavaScript

-
let controller = new AbortController;
-fetch('https://example.com/api/list.json', {
+      
let controller = new AbortController;
+
+fetch('https://example.com/rest/index.json', {
     signal: controller.signal
-}).then(response => response.json()).then(json => {
+}).then(response => response.json()).then(json => {
     let out = "";
     for (let v of json) {
         out += '<article>';
@@ -54,10 +57,10 @@ 

JavaScript

document.querySelector('#main').innerHTML = out; });
- - + + - + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..f77fbff --- /dev/null +++ b/index.js @@ -0,0 +1,254 @@ +(function (g, f) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = f() : typeof define === 'function' && define.amd ? define(f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, (g.hljs = g.hljs || {}, g.hljs.LineNumberPlugin = f())); +})(this, (function () { + 'use strict'; + var isArray = function isArray(x) { + return Array.isArray(x); + }; + var isDefined = function isDefined(x) { + return 'undefined' !== typeof x; + }; + var isInstance = function isInstance(x, of) { + return x && isSet(of) && x instanceof of ; + }; + var isNull = function isNull(x) { + return null === x; + }; + var isNumber = function isNumber(x) { + return 'number' === typeof x; + }; + var isNumeric = function isNumeric(x) { + return /^-?(?:\d*.)?\d+$/.test(x + ""); + }; + var isObject = function isObject(x, isPlain) { + if (isPlain === void 0) { + isPlain = true; + } + if ('object' !== typeof x) { + return false; + } + return isPlain ? isInstance(x, Object) : true; + }; + var isSet = function isSet(x) { + return isDefined(x) && !isNull(x); + }; + var isString = function isString(x) { + return 'string' === typeof x; + }; + var toCaseCamel = function toCaseCamel(x) { + return x.replace(/[-_.](\w)/g, function (m0, m1) { + return toCaseUpper(m1); + }); + }; + var toCaseLower = function toCaseLower(x) { + return x.toLowerCase(); + }; + var toCaseUpper = function toCaseUpper(x) { + return x.toUpperCase(); + }; + var toNumber = function toNumber(x, base) { + if (base === void 0) { + base = 10; + } + return base ? parseInt(x, base) : parseFloat(x); + }; + var toValue = function toValue(x) { + if (isArray(x)) { + return x.map(function (v) { + return toValue(v); + }); + } + if (isNumeric(x)) { + return toNumber(x); + } + if (isObject(x)) { + for (var k in x) { + x[k] = toValue(x[k]); + } + return x; + } + if ('false' === x) { + return false; + } + if ('null' === x) { + return null; + } + if ('true' === x) { + return true; + } + return x; + }; + var fromValue = function fromValue(x) { + if (isArray(x)) { + return x.map(function (v) { + return fromValue(x); + }); + } + if (isObject(x)) { + for (var k in x) { + x[k] = fromValue(x[k]); + } + return x; + } + if (false === x) { + return 'false'; + } + if (null === x) { + return 'null'; + } + if (true === x) { + return 'true'; + } + return "" + x; + }; + var D = document; + var W = window; + var getName = function getName(node) { + return toCaseLower(node && node.nodeName || "") || null; + }; + var getParent = function getParent(node, query) { + return node.parentNode || null; + }; + var getStyle = function getStyle(node, style, parseValue) { + if (parseValue === void 0) { + parseValue = true; + } + var value = W.getComputedStyle(node).getPropertyValue(style); + if (parseValue) { + value = toValue(value); + } + return value || "" === value || 0 === value ? value : null; + }; + var hasState = function hasState(node, state) { + return state in node; + }; + var letAttribute = function letAttribute(node, attribute) { + return node.removeAttribute(attribute), node; + }; + var letStyle = function letStyle(node, style) { + return node.style[toCaseCamel(style)] = null, node; + }; + var setAttribute = function setAttribute(node, attribute, value) { + if (true === value) { + value = attribute; + } + return node.setAttribute(attribute, fromValue(value)), node; + }; + var setAttributes = function setAttributes(node, attributes) { + var value; + for (var attribute in attributes) { + value = attributes[attribute]; + if (value || "" === value || 0 === value) { + setAttribute(node, attribute, value); + } else { + letAttribute(node, attribute); + } + } + return node; + }; + var setClass = function setClass(node, value) { + return node.classList.add(value), node; + }; + var setElement = function setElement(node, content, attributes) { + node = isString(node) ? D.createElement(node) : node; + if (isObject(content)) { + attributes = content; + content = false; + } + if (isString(content)) { + setHTML(node, content); + } + if (isObject(attributes)) { + setAttributes(node, attributes); + } + return node; + }; + var setHTML = function setHTML(node, content, trim) { + if (trim === void 0) { + trim = true; + } + if (null === content) { + return node; + } + var state = 'innerHTML'; + return hasState(node, state) && (node[state] = trim ? content.trim() : content), node; + }; + var setStyle = function setStyle(node, style, value) { + if (isNumber(value)) { + value += 'px'; + } + return node.style[toCaseCamel(style)] = fromValue(value), node; + }; + var setStyles = function setStyles(node, styles) { + var value; + for (var style in styles) { + value = styles[style]; + if (value || "" === value || 0 === value) { + setStyle(node, style, value); + } else { + letStyle(node, style); + } + } + return node; + }; + + function getColorFrom(node) { + var color = getStyle(node, 'color') || "", + c; + // + if (c = color.match(/^rgba\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]|0?\.\d+)\s*\)$/i)) { + return [+c[1], +c[2], +c[3], +c[4]]; + } + if (c = color.match(/^rgb\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*\)$/i)) { + return [+c[1], +c[2], +c[3], 1]; + } + return [0, 0, 0, 1]; + } + var LineNumberPlugin = { + 'after:highlightElement': function afterHighlightElement(_ref) { + var el = _ref.el, + result = _ref.result, + text = _ref.text; + var theCode = el, + theCodeLines = setElement(getName(el)), + theCodeNumbers = [], + theCodeParent = getParent(el), + theCodeParentIsValid = theCodeParent && 'pre' === getName(theCodeParent); + if (theCodeParentIsValid) { + for (var i = 0, j = text.split(/\n/).length; i < j; ++i) { + theCodeNumbers.push('' + (i + 1) + ''); + } + theCodeParent.insertBefore(theCodeLines, theCode); + setStyles(theCode, { + 'flex': '1' + }); + setStyles(theCodeParent, { + 'direction': 'ltr', + 'display': 'flex' + }); + setHTML(theCodeLines, theCodeNumbers.join('\n')); + setClass(theCodeLines, 'hljs'); // Inherit `background` and `padding` value(s) from the style sheet + var _getColorFrom = getColorFrom(theCodeLines), + r = _getColorFrom[0], + g = _getColorFrom[1], + b = _getColorFrom[2], + a = _getColorFrom[3]; + setStyles(theCodeLines, { + 'border-right': '2px solid rgba(' + r + ',' + g + ',' + b + ',' + a / 10 + ')', + 'text-align': 'right', + 'user-select': 'none' // Disable selection on number(s) + }); + } + return { + el: el, + result: result, + text: text + }; + } + }; + // Is a web browser + if (isDefined(W) && isDefined(W.hljs)) { + W.hljs.addPlugin(LineNumberPlugin); + } + return LineNumberPlugin; +})); \ No newline at end of file diff --git a/index.min.js b/index.min.js new file mode 100644 index 0000000..295f357 --- /dev/null +++ b/index.min.js @@ -0,0 +1 @@ +!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):((n="undefined"!=typeof globalThis?globalThis:n||self).hljs=n.hljs||{},n.hljs.LineNumberPlugin=t())}(this,(function(){"use strict";var n=function(n){return Array.isArray(n)},t=function(n){return void 0!==n},r=function(n,t){return void 0===t&&(t=!0),"object"==typeof n&&(!t||function(n,t){return n&&e(t)&&n instanceof t}(n,Object))},e=function(n){return t(n)&&!function(n){return null===n}(n)},u=function(n){return"string"==typeof n},i=function(n){return n.replace(/[-_.](\w)/g,(function(n,t){return o(t)}))},o=function(n){return n.toUpperCase()},f=function t(e){if(n(e))return e.map((function(n){return t(n)}));if(function(n){return/^-?(?:\d*.)?\d+$/.test(n+"")}(e))return function(n,t){return void 0===t&&(t=10),t?parseInt(n,t):parseFloat(n)}(e);if(r(e)){for(var u in e)e[u]=t(e[u]);return e}return"false"!==e&&("null"===e?null:"true"===e||e)},l=function t(e){if(n(e))return e.map((function(n){return t(e)}));if(r(e)){for(var u in e)e[u]=t(e[u]);return e}return!1===e?"false":null===e?"null":!0===e?"true":""+e},s=document,c=window,a=function(n){return(n&&n.nodeName||"").toLowerCase()||null},d=function(n,t){return n.removeAttribute(t),n},p=function(n,t){return n.style[i(t)]=null,n},v=function(n,t,r){return!0===r&&(r=t),n.setAttribute(t,l(r)),n},h=function(n,t,e){return n=u(n)?s.createElement(n):n,r(t)&&(e=t,t=!1),u(t)&&g(n,t),r(e)&&function(n,t){var r;for(var e in t)(r=t[e])||""===r||0===r?v(n,e,r):d(n,e)}(n,e),n},g=function(n,t,r){if(void 0===r&&(r=!0),null===t)return n;var e="innerHTML";return function(n,t){return t in n}(n,e)&&(n[e]=r?t.trim():t),n},m=function(n,t,r){return"number"==typeof r&&(r+="px"),n.style[i(t)]=l(r),n},y=function(n,t){var r;for(var e in t)(r=t[e])||""===r||0===r?m(n,e,r):p(n,e);return n};function b(n){var t,r=function(n,t,r){void 0===r&&(r=!0);var e=c.getComputedStyle(n).getPropertyValue(t);return r&&(e=f(e)),e||""===e||0===e?e:null}(n,"color")||"";return(t=r.match(/^rgba\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]|0?\.\d+)\s*\)$/i))?[+t[1],+t[2],+t[3],+t[4]]:(t=r.match(/^rgb\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*\)$/i))?[+t[1],+t[2],+t[3],1]:[0,0,0,1]}var j={"after:highlightElement":function(n){var t=n.el,r=n.result,e=n.text,u=t,i=h(a(t)),o=[],f=t.parentNode||null;if(f&&"pre"===a(f)){for(var l=0,s=e.split(/\n/).length;l"+(l+1)+"");f.insertBefore(i,u),y(u,{flex:"1"}),y(f,{direction:"ltr",display:"flex"}),g(i,o.join("\n")),function(n,t){n.classList.add(t)}(i,"hljs");var c=b(i),d=c[0],p=c[1],v=c[2],m=c[3];y(i,{"border-right":"2px solid rgba("+d+","+p+","+v+","+m/10+")","text-align":"right","user-select":"none"})}return{el:t,result:r,text:e}}};return t(c)&&t(c.hljs)&&c.hljs.addPlugin(j),j})); \ No newline at end of file diff --git a/index.mjs b/index.mjs new file mode 100644 index 0000000..4fb7b54 --- /dev/null +++ b/index.mjs @@ -0,0 +1,53 @@ +import {getName, getParent, getStyle, setClass, setElement, setHTML, setStyles, W} from '@taufik-nurrohman/document'; +import {isDefined} from '@taufik-nurrohman/is'; + +function getColorFrom(node) { + let color = getStyle(node, 'color') || "", c; + // + if (c = color.match(/^rgba\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]|0?\.\d+)\s*\)$/i)) { + return [+c[1], +c[2], +c[3], +c[4]]; + } + if (c = color.match(/^rgb\s*\(\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*[, ]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*\)$/i)) { + return [+c[1], +c[2], +c[3], 1]; + } + return [0, 0, 0, 1]; +} + +const LineNumberPlugin = { + 'after:highlightElement': function ({el, result, text}) { + let theCode = el, + theCodeLines = setElement(getName(el)), + theCodeNumbers = [], + theCodeParent = getParent(el), + theCodeParentIsValid = theCodeParent && 'pre' === getName(theCodeParent); + if (theCodeParentIsValid) { + for (let i = 0, j = text.split(/\n/).length; i < j; ++i) { + theCodeNumbers.push('' + (i + 1) + ''); + } + theCodeParent.insertBefore(theCodeLines, theCode); + setStyles(theCode, { + 'flex': '1' + }); + setStyles(theCodeParent, { + 'direction': 'ltr', + 'display': 'flex' + }); + setHTML(theCodeLines, theCodeNumbers.join('\n')); + setClass(theCodeLines, 'hljs'); // Inherit `background` and `padding` value(s) from the style sheet + let [r, g, b, a] = getColorFrom(theCodeLines); + setStyles(theCodeLines, { + 'border-right': '2px solid rgba(' + r + ',' + g + ',' + b + ',' + (a / 10) + ')', + 'text-align': 'right', + 'user-select': 'none' // Disable selection on number(s) + }); + } + return {el, result, text}; + } +} + +// Is a web browser +if (isDefined(W) && isDefined(W.hljs)) { + W.hljs.addPlugin(LineNumberPlugin); +} + +export default LineNumberPlugin; \ No newline at end of file diff --git a/package.json b/package.json index 00b13b3..1e417cf 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,47 @@ -{ - "name": "@taufik-nurrohman/highlight.ln.js", - "description": "Line number plugin for Highlight.js", - "version": "1.0.0", - "main": "highlight.ln.mjs", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/taufik-nurrohman/highlight.ln.js.git" - }, - "keywords": [ - "highlight", - "highlightjs", - "plugin", - "syntax" - ], - "author": "Taufik Nurrohman", - "license": "BSD", - "bugs": { - "url": "https://github.com/taufik-nurrohman/highlight.ln.js/issues" - }, - "homepage": "https://taufik-nurrohman.github.io/highlight.ln.js/index.html" -} +{ + "author": "Taufik Nurrohman", + "browser": "index.min.js", + "bugs": "https://github.com/taufik-nurrohman/highlight.ln.js/issues", + "dependencies": { + "@taufik-nurrohman/document": "^1.0.1", + "@taufik-nurrohman/is": "^1.0.1" + }, + "description": "Line number plugin for Highlight.js", + "devDependencies": { + "@taufik-nurrohman/factory": "^2.1.0" + }, + "exports": { + "import": "./index.mjs", + "require": "./index.js" + }, + "files": [ + "index.js", + "index.min.js", + "index.mjs" + ], + "funding": { + "url": "https://paypal.me/tatautaufik" + }, + "homepage": "https://taufik-nurrohman.js.org/highlight.ln.js/index.html", + "keywords": [ + "highlight", + "highlighter", + "highlightjs", + "line", + "number", + "plugin", + "syntax" + ], + "license": "BSD", + "main": "index.js", + "module": "index.mjs", + "name": "@taufik-nurrohman/highlight.ln.js", + "repository": { + "type": "git", + "url": "git+https://github.com/taufik-nurrohman/highlight.ln.js.git" + }, + "scripts": { + "pack": "pack --clean=false --from=.factory --js-format=umd --js-name=hljs.LineNumberPlugin --mjs=true --to=." + }, + "version": "2.0.0" +} \ No newline at end of file