Plugins: Toolbar & Copy to Clipboard (#891) * Add prism-toolbar plugin This plugin exposes a `registerButton` method, which other plugins can use to add buttons to the toolbar. Comes with styles. * Add demo file for toolbar plugin Registers a "Hello World!" tag with the toolbar. * Make `toolbar.registerButton` polymorphic This allows developers to provide either a callback or an object with a `text` string and an optional `onClick` function to create a new button. * Add Toolbar & Copy to Clipboard to components.js * Add Copy to Clipboard plugin * Switch `innerHTML` to `textContent` This ensures additional HTML can't be passed to the toolbar via the `text` property, ensuring a consistent display for the buttons. * Use `call` to bind `this` to the `onClick` method This provides access to the clicked element, which is what `this` is usually bound to on event listeners. * Add hover animation to toolbar * Add drop shadow to toolbar buttons * Add `clipboard` to `optionalDependencies` This will install Clipboard.js when installing from `npm`, but won't fail the build if the installation of Clipboard.js fails. * Load Clipboard.js from CDN if not present * Display plugin code using data-src * Recompile prism-toolbar * Update Show Languages to be a Toolbar button Show Languages now registers a callback with the toolbar plugin to return an element with the language in it. * Add basic HTML API & documentation The Toolbar will now be able to read a `data-label` attribute and add it to the code snippet. * Switch a -> button when only providing onClick Also adds a `url` property which creats an anchor tag and sets the href. Adds some styles to override the button defaults. * Add support for data-url to create anchor tag This allows the HTML API to create links in the Toolbar. * Update toolbar to allow order controlled via HTML Uses a data-attribute on the `body` tag to update the order, should the user choose to do so. * Allow template element to provide content to label This provides one of several options a user can implement in order to get a custom button. Also fixes some bugs in the documentation. * Fix bug when combined with the autoloader plugin The autoloader will rehighlight the element after the language arrives. This means the complete hook can run multiple times. Without a check, multiple toolbars can get added to an element.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
diff --git a/components.js b/components.js
index 0a49d3c..6501117 100644
--- a/components.js
+++ b/components.js
@@ -587,7 +587,9 @@ var components = {
},
"show-language": {
"title": "Show Language",
- "owner": "nauzilus"
+ "owner": "nauzilus",
+ "noCSS": true,
+ "require": "toolbar"
},
"jsonp-highlight": {
"title": "JSONP Highlight",
@@ -659,6 +661,16 @@ var components = {
"title": "Data-URI Highlight",
"owner": "Golmote",
"noCSS": true
+ },
+ "toolbar": {
+ "title": "Toolbar",
+ "owner": "mAAdhaTTah"
+ },
+ "copy-to-clipboard": {
+ "title": "Copy to Clipboard Button",
+ "owner": "mAAdhaTTah",
+ "require": "toolbar",
+ "noCSS": true
}
}
};
diff --git a/package.json b/package.json
index 4ae0326..a51a2a9 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,9 @@
"author": "Lea Verou",
"license": "MIT",
"readmeFilename": "README.md",
+ "optionalDependencies": {
+ "clipboard": "^1.5.5"
+ },
"devDependencies": {
"chai": "^2.3.0",
"gulp": "^3.8.6",
diff --git a/plugins/copy-to-clipboard/index.html b/plugins/copy-to-clipboard/index.html
new file mode 100644
index 0000000..abd5e57
--- /dev/null
+++ b/plugins/copy-to-clipboard/index.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+
+ <meta charset="utf-8" />
+ <link rel="shortcut icon" href="favicon.png" />
+ <title>Copy to Clipboard ▲ Prism plugins</title>
+ <base href="../.." />
+ <link rel="stylesheet" href="style.css" />
+ <link rel="stylesheet" href="themes/prism.css" data-noprefix />
+ <link rel="stylesheet" href="plugins/toolbar/prism-toolbar.css" data-noprefix />
+ <script src="prefixfree.min.js"></script>
+
+ <script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
+ <script src="http://www.google-analytics.com/ga.js" async></script>
+</head>
+<body class="language-markup">
+
+<header>
+ <div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>
+
+ <h2>Copy to Clipboard</h2>
+ <p>Add a button that copies the code block to the clipboard when clicked.</p>
+</header>
+
+<section>
+ <h1>How to use</h1>
+ <p>In addition to including the plugin file with your PrismJS build, ensure <a href="https://clipboardjs.com/">Clipboard.js</a> is loaded before the plugin.</p>
+
+ <p>The simplest way to include Clipboard.js is to use any of the
+ <a href="https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers">recommended CDNs</a>. If you're using Browserify, Clipboard.js will be loaded auotmatically
+ if it's included in your <code>package.json</code>.
+ If you don't load Clipboard.js yourself, the plugin will load it from a CDN for you.</p>
+
+ <pre data-src="plugins/copy-to-clipboard/prism-copy-to-clipboard.js"></pre>
+</section>
+
+<footer data-src="templates/footer.html" data-type="text/html"></footer>
+
+<script src="prism.js"></script>
+<script src="plugins/toolbar/prism-toolbar.js"></script>
+<script src="plugins/copy-to-clipboard/prism-copy-to-clipboard.js"></script>
+<script src="utopia.js"></script>
+<script src="components.js"></script>
+<script src="code.js"></script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/copy-to-clipboard/prism-copy-to-clipboard.js b/plugins/copy-to-clipboard/prism-copy-to-clipboard.js
new file mode 100644
index 0000000..3739766
--- /dev/null
+++ b/plugins/copy-to-clipboard/prism-copy-to-clipboard.js
@@ -0,0 +1,75 @@
+(function(){
+ if (typeof self === 'undefined' || !self.Prism || !self.document) {
+ return;
+ }
+
+ if (!Prism.plugins.toolbar) {
+ console.warn('Copy to Clipboard plugin loaded before Toolbar plugin.');
+
+ return;
+ }
+
+ var Clipboard = window.Clipboard || undefined;
+
+ if (!Clipboard && typeof require === 'function') {
+ Clipboard = require('clipboard');
+ }
+
+ var callbacks = [];
+
+ if (!Clipboard) {
+ var script = document.createElement('script');
+ var head = document.querySelector('head');
+
+ script.onload = function() {
+ Clipboard = window.Clipboard;
+
+ if (Clipboard) {
+ while (callbacks.length) {
+ callbacks.pop()();
+ }
+ }
+ };
+
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.8/clipboard.min.js';
+ head.appendChild(script);
+ }
+
+ Prism.plugins.toolbar.registerButton('copy-to-clipboard', function (env) {
+ var linkCopy = document.createElement('a');
+ linkCopy.textContent = 'Copy';
+
+ if (!Clipboard) {
+ callbacks.push(registerClipboard);
+ } else {
+ registerClipboard();
+ }
+
+ return linkCopy;
+
+ function registerClipboard() {
+ var clip = new Clipboard(linkCopy, {
+ 'text': function () {
+ return env.code;
+ }
+ });
+
+ clip.on('success', function() {
+ linkCopy.textContent = 'Copied!';
+
+ resetText();
+ });
+ clip.on('error', function () {
+ linkCopy.textContent = 'Press Ctrl+C to copy';
+
+ resetText();
+ });
+ }
+
+ function resetText() {
+ setTimeout(function () {
+ linkCopy.textContent = 'Copy';
+ }, 5000);
+ }
+ });
+})();
diff --git a/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js b/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js
new file mode 100644
index 0000000..5153ba6
--- /dev/null
+++ b/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js
@@ -0,0 +1 @@
+!function(){if("undefined"!=typeof self&&self.Prism&&self.document){if(!Prism.plugins.toolbar)return console.warn("Copy to Clipboard plugin loaded before Toolbar plugin."),void 0;var o=window.Clipboard||void 0;o||"function"!=typeof require||(o=require("clipboard"));var e=[];if(!o){var t=document.createElement("script"),n=document.querySelector("head");t.onload=function(){if(o=window.Clipboard)for(;e.length;)e.pop()()},t.src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.8/clipboard.min.js",n.appendChild(t)}Prism.plugins.toolbar.registerButton("copy-to-clipboard",function(t){function n(){var e=new o(i,{text:function(){return t.code}});e.on("success",function(){i.textContent="Copied!",r()}),e.on("error",function(){i.textContent="Press Ctrl+C to copy",r()})}function r(){setTimeout(function(){i.textContent="Copy"},5e3)}var i=document.createElement("a");return i.textContent="Copy",o?n():e.push(n),i})}}();
\ No newline at end of file
diff --git a/plugins/show-language/index.html b/plugins/show-language/index.html
index bbec50a..95ae3a8 100644
--- a/plugins/show-language/index.html
+++ b/plugins/show-language/index.html
@@ -8,7 +8,7 @@
<base href="../.." />
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
-<link rel="stylesheet" href="plugins/show-language/prism-show-language.css" data-noprefix />
+<link rel="stylesheet" href="plugins/toolbar/prism-toolbar.css" data-noprefix />
<script src="prefixfree.min.js"></script>
<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
@@ -25,7 +25,7 @@
<section>
<h1>Examples</h1>
-
+
<h2>JavaScript</h2>
<pre data-src="plugins/show-language/prism-show-language.js"></pre>
@@ -43,6 +43,7 @@
<footer data-src="templates/footer.html" data-type="text/html"></footer>
<script src="prism.js"></script>
+<script src="plugins/toolbar/prism-toolbar.js"></script>
<script src="plugins/show-language/prism-show-language.js"></script>
<script src="utopia.js"></script>
<script src="components.js"></script>
diff --git a/plugins/show-language/prism-show-language.css b/plugins/show-language/prism-show-language.css
deleted file mode 100644
index 3ada3eb..0000000
--- a/plugins/show-language/prism-show-language.css
+++ /dev/null
@@ -1,27 +0,0 @@
-div.prism-show-language {
- position: relative;
-}
-
-div.prism-show-language > div.prism-show-language-label {
- color: black;
- background-color: #CFCFCF;
- display: inline-block;
- position: absolute;
- bottom: auto;
- left: auto;
- top: 0;
- right: 0;
- width: auto;
- height: auto;
- font-size: 0.9em;
- border-radius: 0 0 0 5px;
- padding: 0 0.5em;
- text-shadow: none;
- z-index: 1;
- box-shadow: none;
- -webkit-transform: none;
- -moz-transform: none;
- -ms-transform: none;
- -o-transform: none;
- transform: none;
-}
diff --git a/plugins/show-language/prism-show-language.js b/plugins/show-language/prism-show-language.js
index 10bdff1..4038ffb 100644
--- a/plugins/show-language/prism-show-language.js
+++ b/plugins/show-language/prism-show-language.js
@@ -4,35 +4,25 @@ if (typeof self === 'undefined' || !self.Prism || !self.document) {
return;
}
+if (!Prism.plugins.toolbar) {
+ console.warn('Show Languages plugin loaded before Toolbar plugin.');
+
+ return;
+}
+
// The languages map is built automatically with gulp
var Languages = /*languages_placeholder[*/{"html":"HTML","xml":"XML","svg":"SVG","mathml":"MathML","css":"CSS","clike":"C-like","javascript":"JavaScript","abap":"ABAP","actionscript":"ActionScript","apacheconf":"Apache Configuration","apl":"APL","applescript":"AppleScript","asciidoc":"AsciiDoc","aspnet":"ASP.NET (C#)","autoit":"AutoIt","autohotkey":"AutoHotkey","basic":"BASIC","csharp":"C#","cpp":"C++","coffeescript":"CoffeeScript","css-extras":"CSS Extras","fsharp":"F#","glsl":"GLSL","graphql":"GraphQL","http":"HTTP","inform7":"Inform 7","json":"JSON","latex":"LaTeX","livescript":"LiveScript","lolcode":"LOLCODE","matlab":"MATLAB","mel":"MEL","nasm":"NASM","nginx":"nginx","nsis":"NSIS","objectivec":"Objective-C","ocaml":"OCaml","parigp":"PARI/GP","php":"PHP","php-extras":"PHP Extras","powershell":"PowerShell","properties":".properties","protobuf":"Protocol Buffers","jsx":"React JSX","rest":"reST (reStructuredText)","sas":"SAS","sass":"Sass (Sass)","scss":"Sass (Scss)","sql":"SQL","typescript":"TypeScript","vhdl":"VHDL","vim":"vim","wiki":"Wiki markup","xojo":"Xojo (REALbasic)","yaml":"YAML"}/*]*/;
-Prism.hooks.add('before-highlight', function(env) {
+Prism.plugins.toolbar.registerButton('show-language', function(env) {
var pre = env.element.parentNode;
if (!pre || !/pre/i.test(pre.nodeName)) {
return;
}
var language = pre.getAttribute('data-language') || Languages[env.language] || (env.language.substring(0, 1).toUpperCase() + env.language.substring(1));
- /* check if the divs already exist */
- var sib = pre.previousSibling;
- var div, div2;
- if (sib && /\s*\bprism-show-language\b\s*/.test(sib.className) &&
- sib.firstChild &&
- /\s*\bprism-show-language-label\b\s*/.test(sib.firstChild.className)) {
- div2 = sib.firstChild;
- } else {
- div = document.createElement('div');
- div2 = document.createElement('div');
+ var element = document.createElement('span');
+ element.innerHTML = language;
- div2.className = 'prism-show-language-label';
-
- div.className = 'prism-show-language';
- div.appendChild(div2);
-
- pre.parentNode.insertBefore(div, pre);
- }
-
- div2.innerHTML = language;
+ return element;
});
})();
diff --git a/plugins/show-language/prism-show-language.min.js b/plugins/show-language/prism-show-language.min.js
index afa963c..14c744b 100644
--- a/plugins/show-language/prism-show-language.min.js
+++ b/plugins/show-language/prism-show-language.min.js
@@ -1 +1 @@
-!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var e={html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",css:"CSS",clike:"C-like",javascript:"JavaScript",abap:"ABAP",actionscript:"ActionScript",apacheconf:"Apache Configuration",apl:"APL",applescript:"AppleScript",asciidoc:"AsciiDoc",aspnet:"ASP.NET (C#)",autoit:"AutoIt",autohotkey:"AutoHotkey",basic:"BASIC",csharp:"C#",cpp:"C++",coffeescript:"CoffeeScript","css-extras":"CSS Extras",fsharp:"F#",glsl:"GLSL",graphql:"GraphQL",http:"HTTP",inform7:"Inform 7",json:"JSON",latex:"LaTeX",livescript:"LiveScript",lolcode:"LOLCODE",matlab:"MATLAB",mel:"MEL",nasm:"NASM",nginx:"nginx",nsis:"NSIS",objectivec:"Objective-C",ocaml:"OCaml",parigp:"PARI/GP",php:"PHP","php-extras":"PHP Extras",powershell:"PowerShell",properties:".properties",protobuf:"Protocol Buffers",jsx:"React JSX",rest:"reST (reStructuredText)",sas:"SAS",sass:"Sass (Sass)",scss:"Sass (Scss)",sql:"SQL",typescript:"TypeScript",vhdl:"VHDL",vim:"vim",wiki:"Wiki markup",xojo:"Xojo (REALbasic)",yaml:"YAML"};Prism.hooks.add("before-highlight",function(s){var a=s.element.parentNode;if(a&&/pre/i.test(a.nodeName)){var t,i,r=a.getAttribute("data-language")||e[s.language]||s.language.substring(0,1).toUpperCase()+s.language.substring(1),p=a.previousSibling;p&&/\s*\bprism-show-language\b\s*/.test(p.className)&&p.firstChild&&/\s*\bprism-show-language-label\b\s*/.test(p.firstChild.className)?i=p.firstChild:(t=document.createElement("div"),i=document.createElement("div"),i.className="prism-show-language-label",t.className="prism-show-language",t.appendChild(i),a.parentNode.insertBefore(t,a)),i.innerHTML=r}})}}();
\ No newline at end of file
+!function(){if("undefined"!=typeof self&&self.Prism&&self.document){if(!Prism.plugins.toolbar)return console.warn("Show Languages plugin loaded before Toolbar plugin."),void 0;var e={html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",css:"CSS",clike:"C-like",javascript:"JavaScript",abap:"ABAP",actionscript:"ActionScript",apacheconf:"Apache Configuration",apl:"APL",applescript:"AppleScript",asciidoc:"AsciiDoc",aspnet:"ASP.NET (C#)",autoit:"AutoIt",autohotkey:"AutoHotkey",basic:"BASIC",csharp:"C#",cpp:"C++",coffeescript:"CoffeeScript","css-extras":"CSS Extras",fsharp:"F#",glsl:"GLSL",graphql:"GraphQL",http:"HTTP",inform7:"Inform 7",json:"JSON",latex:"LaTeX",livescript:"LiveScript",lolcode:"LOLCODE",matlab:"MATLAB",mel:"MEL",nasm:"NASM",nginx:"nginx",nsis:"NSIS",objectivec:"Objective-C",ocaml:"OCaml",parigp:"PARI/GP",php:"PHP","php-extras":"PHP Extras",powershell:"PowerShell",properties:".properties",protobuf:"Protocol Buffers",jsx:"React JSX",rest:"reST (reStructuredText)",sas:"SAS",sass:"Sass (Sass)",scss:"Sass (Scss)",sql:"SQL",typescript:"TypeScript",vhdl:"VHDL",vim:"vim",wiki:"Wiki markup",xojo:"Xojo (REALbasic)",yaml:"YAML"};Prism.plugins.toolbar.registerButton("show-language",function(t){var a=t.element.parentNode;if(a&&/pre/i.test(a.nodeName)){var s=a.getAttribute("data-language")||e[t.language]||t.language.substring(0,1).toUpperCase()+t.language.substring(1),r=document.createElement("span");return r.innerHTML=s,r}})}}();
\ No newline at end of file
diff --git a/plugins/toolbar/index.html b/plugins/toolbar/index.html
new file mode 100644
index 0000000..64d8383
--- /dev/null
+++ b/plugins/toolbar/index.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+
+ <meta charset="utf-8" />
+ <link rel="shortcut icon" href="favicon.png" />
+ <title>Toolbar ▲ Prism plugins</title>
+ <base href="../.." />
+ <link rel="stylesheet" href="style.css" />
+ <link rel="stylesheet" href="themes/prism.css" data-noprefix />
+ <link rel="stylesheet" href="plugins/toolbar/prism-toolbar.css" data-noprefix />
+ <script src="prefixfree.min.js"></script>
+
+ <script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
+ <script src="http://www.google-analytics.com/ga.js" async></script>
+</head>
+<body class="language-markup" data-toolbar-order="select-code,hello-world,label">
+
+<header>
+ <div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>
+
+ <h2>Toolbar</h2>
+ <p>Attach a toolbar for plugins to easily register buttons on the top of a code block.</p>
+</header>
+
+<section>
+ <h1>How to use</h1>
+ <p>The Toolbar plugin allows for several methods to register your button, using the <code>Prism.plugins.toolbar.registerButton</code> function.</p>
+
+ <p>The simplest method is through the HTML API. Add a <code>data-label</code> attribute to the <code>pre</code> element, and the Toolbar
+ plugin will read the value of that attribute and append a label to the code snippet.</p>
+
+ <pre data-label="Hello World!"><code class="language-markup"><pre data-src="plugins/toolbar/prism-toolbar.js" data-label="Hello World!"></pre></code></pre>
+
+ <p>If you want to provide arbitrary HTML to the label, create a <code>template</code> element with the HTML you want in the label, and provide the
+ <code>template</code> element's <code>id</code> to <code>data-label</code>. The Toolbar plugin will use the template's content for the button.
+ You can also use to declare your event handlers inline:</p>
+
+ <pre data-label="my-label-button"><code class="language-markup"><pre data-src="plugins/toolbar/prism-toolbar.js" data-label="my-label-button"></pre></code></pre>
+
+ <pre><code><template id="my-label-button"><button onclick="console.log('This is an inline-handler');">My button</button></template></code></pre>
+
+ <p>For more flexibility, the Toolbar exposes a JavaScript function that can be used to register new buttons or labels to the Toolbar,
+ <code>Prism.plugins.toolbar.registerButton</code>.</p>
+
+ <p>The function accepts a key for the button and an object with a <code>text</code> property string and an optional
+ <code>onClick</code> function or <code>url</code> string. The <code>onClick</code> function will be called when the button is clicked, while the
+ <code>url</code> property will be set to the anchor tag's <code>href</code>.</p>
+
+ <pre><code class="language-javascript">Prism.plugins.toolbar.registerButton('hello-world', {
+ text: 'Hello World!', // required
+ onClick: function (env) { // optional
+ alert('This code snippet is written in ' + env.language + '.');
+ }
+});</code></pre>
+
+ <p>See how the above code registers the <code>Hello World!</code> button? You can use this in your plugins to register your own buttons with the toolbar.</p>
+
+ <p>If you need more control, you can provide a function to <code>registerButton</code> that returns either a <code>span</code>, <code>a</code>, or
+ <code>button</code> element.</p>
+
+ <pre><code class="language-javascript">Prism.plugins.toolbar.registerButton('select-code', function() {
+ var button = document.createElement('button');
+ button.innerHTML = 'Select Code';
+
+ button.addEventListener('click', function () {
+ // Source: http://stackoverflow.com/a/11128179/2757940
+ if (document.body.createTextRange) { // ms
+ var range = document.body.createTextRange();
+ range.moveToElementText(env.element);
+ range.select();
+ } else if (window.getSelection) { // moz, opera, webkit
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.selectNodeContents(env.element);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ });
+
+ return button;
+});</code></pre>
+
+ <p>The above function creates the Select Code button you see, and when you click it, the code gets highlighted.</p>
+
+ <p>By default, the buttons will be added to the code snippet in the order they were registered. If more control over
+ the order is needed, an HTML attribute can be added to the <code>body</code> tag with a comma-separated string indicating the
+ order.</p>
+
+ <pre><code><body data-toolbar-order="select-code,hello-world,label"></code></pre>
+</section>
+
+<footer data-src="templates/footer.html" data-type="text/html"></footer>
+<template id="my-label-button"><button onclick="console.log('This is an inline-handler');">My button</button></template>
+
+<script src="prism.js"></script>
+<script src="plugins/toolbar/prism-toolbar.js"></script>
+<script src="utopia.js"></script>
+<script src="components.js"></script>
+<script>
+ Prism.plugins.toolbar.registerButton('hello-world', {
+ text: 'Hello World!', // required
+ onClick: function (env) { // optional
+ alert('This code snippet is written in ' + env.language + '.');
+ }
+ });
+</script>
+<script>
+ Prism.plugins.toolbar.registerButton('select-code', function(env) {
+ var button = document.createElement('button');
+ button.innerHTML = 'Select Code';
+
+ button.addEventListener('click', function () {
+ // Source: http://stackoverflow.com/a/11128179/2757940
+ if (document.body.createTextRange) { // ms
+ var range = document.body.createTextRange();
+ range.moveToElementText(env.element);
+ range.select();
+ } else if (window.getSelection) { // moz, opera, webkit
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.selectNodeContents(env.element);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ });
+
+ return button;
+ });
+</script>
+<script src="code.js"></script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/toolbar/prism-toolbar.css b/plugins/toolbar/prism-toolbar.css
new file mode 100644
index 0000000..6d1afb9
--- /dev/null
+++ b/plugins/toolbar/prism-toolbar.css
@@ -0,0 +1,58 @@
+pre.code-toolbar {
+ position: relative;
+}
+
+pre.code-toolbar > .toolbar {
+ position: absolute;
+ top: .3em;
+ right: .2em;
+ transition: opacity 0.3s ease-in-out;
+ opacity: 0;
+}
+
+pre.code-toolbar:hover > .toolbar {
+ opacity: 1;
+}
+
+pre.code-toolbar > .toolbar .toolbar-item {
+ display: inline-block;
+}
+
+pre.code-toolbar > .toolbar a {
+ cursor: pointer;
+}
+
+pre.code-toolbar > .toolbar button {
+ background: none;
+ border: 0;
+ color: inherit;
+ font: inherit;
+ line-height: normal;
+ overflow: visible;
+ padding: 0;
+ -webkit-user-select: none; /* for button */
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+pre.code-toolbar > .toolbar a,
+pre.code-toolbar > .toolbar button,
+pre.code-toolbar > .toolbar span {
+ color: #bbb;
+ font-size: .8em;
+ padding: 0 .5em;
+ background: #f5f2f0;
+ background: rgba(224, 224, 224, 0.2);
+ box-shadow: 0 2px 0 0 rgba(0,0,0,0.2);
+ border-radius: .5em;
+}
+
+pre.code-toolbar > .toolbar a:hover,
+pre.code-toolbar > .toolbar a:focus,
+pre.code-toolbar > .toolbar button:hover,
+pre.code-toolbar > .toolbar button:focus,
+pre.code-toolbar > .toolbar span:hover,
+pre.code-toolbar > .toolbar span:focus {
+ color: inherit;
+ text-decoration: none;
+}
diff --git a/plugins/toolbar/prism-toolbar.js b/plugins/toolbar/prism-toolbar.js
new file mode 100644
index 0000000..f4cd28e
--- /dev/null
+++ b/plugins/toolbar/prism-toolbar.js
@@ -0,0 +1,133 @@
+(function(){
+ if (typeof self === 'undefined' || !self.Prism || !self.document) {
+ return;
+ }
+
+ var callbacks = [];
+ var map = {};
+ var noop = function() {};
+
+ Prism.plugins.toolbar = {};
+
+ /**
+ * Register a button callback with the toolbar.
+ *
+ * @param {string} key
+ * @param {Object|Function} opts
+ */
+ var registerButton = Prism.plugins.toolbar.registerButton = function (key, opts) {
+ var callback;
+
+ if (typeof opts === 'function') {
+ callback = opts;
+ } else {
+ callback = function (env) {
+ var element;
+
+ if (typeof opts.onClick === 'function') {
+ element = document.createElement('button');
+ element.type = 'button';
+ element.addEventListener('click', function () {
+ opts.onClick.call(this, env);
+ });
+ } else if (typeof opts.url === 'string') {
+ element = document.createElement('a');
+ element.href = opts.url;
+ } else {
+ element = document.createElement('span');
+ }
+
+ element.textContent = opts.text;
+
+ return element;
+ };
+ }
+
+ callbacks.push(map[key] = callback);
+ };
+
+ /**
+ * Post-highlight Prism hook callback.
+ *
+ * @param env
+ */
+ var hook = Prism.plugins.toolbar.hook = function (env) {
+ // Check if inline or actual code block (credit to line-numbers plugin)
+ var pre = env.element.parentNode;
+ if (!pre || !/pre/i.test(pre.nodeName)) {
+ return;
+ }
+
+ // Autoloader rehighlights, so only do this once.
+ if (pre.classList.contains('code-toolbar')) {
+ return;
+ }
+
+ pre.classList.add('code-toolbar');
+
+ // Setup the toolbar
+ var toolbar = document.createElement('div');
+ toolbar.classList.add('toolbar');
+
+ if (document.body.hasAttribute('data-toolbar-order')) {
+ callbacks = document.body.getAttribute('data-toolbar-order').split(',').map(function(key) {
+ return map[key] || noop;
+ });
+ }
+
+ callbacks.forEach(function(callback) {
+ var element = callback(env);
+
+ if (!element) {
+ return;
+ }
+
+ var item = document.createElement('div');
+ item.classList.add('toolbar-item');
+
+ item.appendChild(element);
+ toolbar.appendChild(item);
+ });
+
+ // Add our toolbar to the <pre> tag
+ pre.appendChild(toolbar);
+ };
+
+ registerButton('label', function(env) {
+ var pre = env.element.parentNode;
+ if (!pre || !/pre/i.test(pre.nodeName)) {
+ return;
+ }
+
+ if (!pre.hasAttribute('data-label')) {
+ return;
+ }
+
+ var element, template;
+ var text = pre.getAttribute('data-label');
+ try {
+ // Any normal text will blow up this selector.
+ template = document.querySelector('template#' + text);
+ } catch (e) {}
+
+ if (template) {
+ element = template.content;
+ } else {
+ if (pre.hasAttribute('data-url')) {
+ element = document.createElement('a');
+ element.href = pre.getAttribute('data-url');
+ } else {
+ element = document.createElement('span');
+ }
+
+ element.innerHTML = text;
+ }
+
+ return element;
+ });
+
+ /**
+ * Register the toolbar with Prism.
+ */
+ Prism.hooks.add('complete', hook);
+})();
diff --git a/plugins/toolbar/prism-toolbar.min.js b/plugins/toolbar/prism-toolbar.min.js
new file mode 100644
index 0000000..c069c1e
--- /dev/null
+++ b/plugins/toolbar/prism-toolbar.min.js
@@ -0,0 +1 @@
+!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var t=[],e={},n=function(){};Prism.plugins.toolbar={};var a=Prism.plugins.toolbar.registerButton=function(n,a){var o;o="function"==typeof a?a:function(t){var e;return"function"==typeof a.onClick?(e=document.createElement("button"),e.type="button",e.addEventListener("click",function(){a.onClick.call(this,t)})):"string"==typeof a.url?(e=document.createElement("a"),e.href=a.url):e=document.createElement("span"),e.textContent=a.text,e},t.push(e[n]=o)},o=Prism.plugins.toolbar.hook=function(a){var o=a.element.parentNode;if(o&&/pre/i.test(o.nodeName)){o.classList.add("code-toolbar");var r=document.createElement("div");r.classList.add("toolbar"),document.body.hasAttribute("data-toolbar-order")&&(t=document.body.getAttribute("data-toolbar-order").split(",").map(function(t){return e[t]||n})),t.forEach(function(t){var e=t(a);if(e){var n=document.createElement("div");n.classList.add("toolbar-item"),n.appendChild(e),r.appendChild(n)}}),o.appendChild(r)}};a("label",function(t){var e=t.element.parentNode;if(e&&/pre/i.test(e.nodeName)&&e.hasAttribute("data-label")){var n,a=e.getAttribute("data-label"),o=document.querySelector("template#"+a);return o?n=o.content:(e.hasAttribute("data-url")?(n=document.createElement("a"),n.href=e.getAttribute("data-url")):n=document.createElement("span"),n.innerHTML=a),n}}),Prism.hooks.add("complete",o)}}();
\ No newline at end of file