Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 44 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
# HighlightJs Copy Code Badge Component
# HighlightJs Copy Code Badge-JS (extension) Component
This small JavaScript library complements the [highlightJs Syntax Highligher](https://highlightjs.org/) by providing a badge in the top right corner of highlightJs code snippets.

* Shows active Syntax for the code block
* Allows copying the code block to Clipboard

You can install it [from NPM](https://www.npmjs.com/package/highlightjs-badge):
You can install it [from NPM](https://www.npmjs.com/package/highlightjs-badgejs):

```ps
npm install highlightjs-badge
npm install highlightjs-badgejs
```


* [Codepen Example](https://codepen.io/rstrahl/pen/RwNZGBE)
* [HighlightJs-Badge Blog Post](https://weblog.west-wind.com/posts/2019/Dec/30/A-HighlightJs-Copy-Code-Badge-Component)

Here's what the code badge looks like attached to several highlightjs code blocks:

![](ScreenShot.png)

This small, single JavaScript file component can be loaded after highlightJS has been loaded. It's fully self-contained. Add the script, call the `window.highlightJsBadge()`, and you're up and running without any other configuration or dependencies.

### Usage
To use this library is very simple - you add a script file and call `highlightJsBadge()` after highlightJS has been applied.

Expand All @@ -31,7 +21,7 @@ The following is a typical configuration for both highlightJs and highlightJs-Ba
<script src="scripts/highlightjs/highlight.pack.js"></script>

<!-- then add this badge component -->
<script src="scripts/highlightjs-badge.min.js"></script>
<script src="scripts/highlightjs-badgejs.min.js"></script>

<script>
// apply HighlightJS
Expand All @@ -53,6 +43,17 @@ The following is a typical configuration for both highlightJs and highlightJs-Ba
</script>
```

#### Options

Some other options added from the original:

* <b>title</b> : allow to set a different title on hover
* <b>label</b> : allow to set a prefix on badge name
* <b>clickableBadge</b> : if set "true" allow to have pointer and click event on badge in addition to icon click
* <b>hasLineNumber</b> : if set "true" indicates that content as line number plugin activated
* <b>xmlBeautifier</b> : if set "true" indicates that content (if xml) has been copied as beautif, otherwirse as original content


### Styling
The default script includes default styling that should work great with dark themed syntax, and fairly well with light themed syntax.

Expand Down Expand Up @@ -130,8 +131,8 @@ Alternately you can completely replace the template and styling. If you look at
</style>
<div id="CodeBadgeTemplate" style="display:none">
<div class="code-badge">
<div class="code-badge-language">{{language}}</div>
<div title="Copy to clipboard">
<div class="code-badge-language">{{label}} {{language}}</div>
<div title="{{title}}">
<i class="{{copyIconClass}} code-badge-copy-icon"></i>
</div>
</div>
Expand Down Expand Up @@ -186,15 +187,35 @@ Licensed under the MIT License. There's no charge to use, integrate or modify th

### Version History

### v0.1.5
### v0.0.5

* Bug fix

### v0.0.4

* Bug fix

### v0.0.3

* **XML Beautifier**
* Added option xmlBeautifier
* <b>xmlBeautifier (true/false default : false)</b> : allow to beautify content copied if language is "xml"


### v0.0.2

* **Fixed code**
* Some code fixing in order to improve logic of new options

* **Turn off code badge in Print Media Style**
Added media class so that the code-badge is not shown when printing is active for print or PDF generation.

* **Fix Internet Explorer missing Line Feeds**
Fixed issue where IE 11/10 was not properly picking up line breaks in the copied text content. Still useful for those of us using the Web Browser control in Windows. Fixed by using `textContent` instead of `innerText`.
### v0.0.1

* **Badge Position on Scrolled Content**
Fixed issue where the badge overlay would not properly stay right aligned when a code block was scrolled horizontally. Fixed by moving `position:relative` up to the `<pre>` tag **via code**. Unfortunately this style feature is not directly settable via CSS so the relative style gets hardcoded when the badge is added to the page.
* **Added new options**
* <b>title</b> : allow to set a different title on hover
* <b>label</b> : allow to set a prefix on badge name
* <b>clickableBadge (true/false default : false)</b> : if set "true" allow to have pointer and click event on badge in addition to icon click
* <b>hasLineNumber (true/false default : false)</b> : if set "true" indicates that content as line number plugin activated

* **Fixed bugs**
* Options not correctly initialized

2 changes: 2 additions & 0 deletions highlight.pack.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion highlightjs-badge.min.js

This file was deleted.

90 changes: 70 additions & 20 deletions highlightjs-badge.js → highlightjs-badgejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,17 @@ function highlightJsBadge(opt) {

// CSS class(es) used to render the done icon.
checkIconClass: "fa fa-check text-success",
checkIconContent: ""
checkIconContent: "",
// If pre>code plugin line number is active set to true the boolean
hasLineNumber: "false",
// Title on hover
title: "Copy to clipboard",
// Label in addition to language name on badge
label: "",
// If whole badge is "clickable"
badgeClickable: "false",
// Xml Beatifier
xmlBeautifier: "false"
};

function initialize(opt) {
Expand Down Expand Up @@ -150,19 +160,28 @@ function highlightJsBadge(opt) {
var hudText = document.querySelector(options.templateSelector).innerHTML;

var $codes = document.querySelectorAll("pre>code.hljs");
if(Boolean(options.hasLineNumber)) {
$codes = document.querySelectorAll("pre>code.hljs:not(.hljs-line-numbers)");
}
for (var index = 0; index < $codes.length; index++) {
var el = $codes[index];
if (el.querySelector(".code-badge"))
continue; // already exists

var lang = "";

for (var i = 0; i < el.classList.length; i++) {
var cl = el.classList[i];
// class="hljs language-csharp"
if (el.classList[i].substr(0, 9) === 'language-') {
if (cl.substr(0, 9) === 'language-') {
lang = el.classList[i].replace('language-', '');
break;
}
// class="hljs lang-cs" // docFx
else if (cl.substr(0, 5) === 'lang-') {
lang = el.classList[i].replace('lang-', '');
break;
}
// class="kotlin hljs" (auto detected)
if (!lang) {
for (var j = 0; j < el.classList.length; j++) {
Expand Down Expand Up @@ -191,11 +210,11 @@ function highlightJsBadge(opt) {
else if (lang == "fox")
lang = "foxpro";


var html = hudText.replace("{{language}}", lang)
.replace("{{copyIconClass}}",options.copyIconClass)
.replace("{{title}}",options.title)
.replace("{{label}}",options.label)
.trim();

// insert the Hud panel
var $newHud = document.createElement("div");
$newHud.innerHTML = html;
Expand All @@ -212,33 +231,52 @@ function highlightJsBadge(opt) {
pre.insertBefore($newHud, el);
}

var $content = document.querySelector(options.contentSelector);

// single copy click handler
$content.addEventListener("click",
var $content = document.querySelectorAll(options.contentSelector);
// check selector
if($content != null) {
$( $content ).each(function( index ) {
// single copy click handler on icon
this.addEventListener("click",
function (e) {
var $clicked = e.srcElement;
if ($clicked.classList.contains("code-badge-copy-icon")) {
e.preventDefault();
e.cancelBubble = true;
copyCodeToClipboard(e);
copyCodeToClipboard(e, lang);
}
// on badge
if(Boolean(options.badgeClickable)) {
if ($clicked.className.indexOf('code-badge') != -1) {
e.preventDefault();
e.cancelBubble = true;
copyCodeToClipboard(e, lang);
}
}
return false;
});
}); // each
}
}


function copyCodeToClipboard(e) {

function copyCodeToClipboard(e, lang) {
// walk back up to <pre> tag
var $origCode = e.srcElement.parentElement.parentElement.parentElement;

// select the <code> tag and grab text
// select the <code> tag and grab text
var $code = $origCode.querySelector("pre>code");
if(Boolean(options.hasLineNumber)) {
$code = $origCode.querySelector("pre>code:not(.hljs-line-numbers)");
}
var text = $code.textContent || $code.innerText;

// Create a textblock and assign the text and add to document
var el = document.createElement('textarea');
el.value = text.trim();
// Beautifier
if(Boolean(options.xmlBeautifier) && lang.toUpperCase() == 'xml'.toUpperCase()){
el.value = xmlBeautifier(el.value);
}

document.body.appendChild(el);
el.style.display = "block";

Expand All @@ -258,6 +296,17 @@ function highlightJsBadge(opt) {
swapIcons($origCode);
}

function xmlBeautifier(xml, tab) {
var formatted = '', indent= '';
tab = tab || '\t';
xml.split(/>\s*</).forEach(function(node) {
if (node.match( /^\/\w/ )) indent = indent.substring(tab.length); // decrease indent by one 'tab'
formatted += indent + '<' + node + '>\r\n';
if (node.match( /^<?\w[^>]*[^\/]$/ )) indent += tab; // increase indent
});
return formatted.substring(1, formatted.length-3);
}

function swapIcons($code) {
var copyIcons = options.copyIconClass.split(' ');
var checkIcons = options.checkIconClass.split(' ');
Expand Down Expand Up @@ -299,13 +348,14 @@ function highlightJsBadge(opt) {
" background: transparent;",
" background: #333;",
" color: white;",
" font-size: 0.8em;",
" font-size: 0.875em;",
" opacity: 0.5;",
" transition: opacity linear 0.5s;",
" border-radius: 0 0 0 7px;",
" padding: 5px 8px 5px 8px;",
" position: absolute;",
" right: 0;",
(Boolean(options.badgeClickable) ? 'cursor: pointer;' : ''),
" top: 0;",
" }",
" .code-badge.active {",
Expand Down Expand Up @@ -336,8 +386,8 @@ function highlightJsBadge(opt) {
"</style>",
"<div id=\"CodeBadgeTemplate\" style=\"display:none\">",
" <div class=\"code-badge\">",
" <div class=\"code-badge-language\" >{{language}}</div>",
" <div title=\"Copy to clipboard\">",
" <div class=\"code-badge-language\" >{{label}} {{language}}</div>",
" <div title=\"{{title}}\">",
" <i class=\"{{copyIconClass}} code-badge-copy-icon\"></i></i></a>",
" </div>",
" </div>",
Expand All @@ -351,7 +401,7 @@ function highlightJsBadge(opt) {
return t;
}

initialize();
initialize(opt);
}


Expand Down Expand Up @@ -383,7 +433,7 @@ if (highlightJsBadgeAutoLoad)
background: transparent;
background: #333;
color: white;
font-size: 0.8em;
font-size: 0.875em;
opacity: 0.5;
border-radius: 0 0 0 7px;
padding: 5px 8px 5px 8px;
Expand Down
Loading