├── .gitignore
├── .jshintrc
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── demo
└── demo.css
├── dist
├── xrayhtml-iframe.js
├── xrayhtml.css
└── xrayhtml.js
├── iframe.html
├── index.html
├── package.json
├── src
├── X-rayHTML-iframe.js
├── X-rayHTML.css
├── X-rayHTML.js
└── lib
│ ├── clipboard.js
│ ├── jquery.js
│ ├── prism.css
│ ├── prism.js
│ └── shoestring.js
└── test
├── X-rayHTML.html
└── X-rayHTML_test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "immed": true,
5 | "latedef": true,
6 | "newcap": true,
7 | "noarg": true,
8 | "sub": true,
9 | "undef": true,
10 | "unused": true,
11 | "boss": true,
12 | "eqnull": true,
13 | "browser": true,
14 | "predef": ["jQuery"]
15 | }
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Important notes
4 | Please don't edit files in the `dist` subdirectory as they are generated via grunt. You'll find source code in the `src` subdirectory!
5 |
6 | ### Code style
7 | Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.**
8 |
9 | ### PhantomJS
10 | While grunt can run the included unit tests via [PhantomJS](http://phantomjs.org/), this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers.
11 |
12 | See the [Why does grunt complain that PhantomJS isn't installed?](https://github.com/gruntjs/grunt/blob/master/docs/faq.md#why-does-grunt-complain-that-phantomjs-isnt-installed) guide in the [Grunt FAQ](https://github.com/gruntjs/grunt/blob/master/docs/faq.md) for help with installing or troubleshooting PhantomJS.
13 |
14 | ## Modifying the code
15 | First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed.
16 |
17 | Test that grunt is installed globally by running `grunt --version` at the command-line. If grunt isn't installed globally, run `npm install -g grunt` to install the latest version. _You may need to run `sudo npm install -g grunt`._
18 |
19 | _Note that in Windows, you may have to run `grunt.cmd` instead of `grunt`._
20 |
21 | 1. Fork and clone the repo.
22 | 1. Run `npm install` to install all dependencies (including grunt).
23 | 1. Run `grunt` to grunt this project.
24 |
25 | Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken.
26 |
27 | ## Submitting pull requests
28 |
29 | 1. Create a new branch, please don't work in your `master` branch directly.
30 | 1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail.
31 | 1. Fix stuff.
32 | 1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done.
33 | 1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere.
34 | 1. Update the documentation to reflect any changes.
35 | 1. Push to your fork and submit a pull request.
36 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | 'use strict';
3 |
4 |
5 | grunt.initConfig({
6 | pkg: grunt.file.readJSON('package.json'),
7 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
8 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
9 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
10 | '* Copyright (c) <%= grunt.template.today("yyyy") %> Filament Group;' +
11 | ' Licensed <%= pkg.license %> */\n',
12 |
13 | concat: {
14 | options: {
15 | banner: '<%= banner %>',
16 | stripBanners: true
17 | },
18 | js: {
19 | src: [
20 | '<%= banner %>',
21 | 'src/X-rayHTML.js'
22 | ],
23 | dest: 'dist/<%= pkg.name %>.js'
24 | },
25 | "iframe-js": {
26 | src: [
27 | '<%= banner %>',
28 | 'src/X-rayHTML-iframe.js'
29 | ],
30 | dest: 'dist/<%= pkg.name %>-iframe.js'
31 | },
32 | css: {
33 | src: [
34 | '<%= banner %>',
35 | 'src/X-rayHTML.css'
36 | ],
37 | dest: 'dist/<%= pkg.name %>.css'
38 | }
39 | },
40 |
41 | copy: {
42 | libs: {
43 | files: [
44 | { expand: true, cwd: 'node_modules/prismjs/themes/', src: [ 'prism.css' ], dest: 'src/lib/' },
45 | { expand: true, cwd: 'node_modules/shoestring/dist/', src: [ 'shoestring.js' ], dest: 'src/lib/' },
46 | { expand: true, cwd: 'node_modules/jquery/dist/', src: [ 'jquery.js' ], dest: 'src/lib/' },
47 | { expand: true, cwd: 'node_modules/prismjs/', src: [ 'prism.js' ], dest: 'src/lib/' },
48 | { expand: true, cwd: 'node_modules/clipboard/dist/', src: [ 'clipboard.js' ], dest: 'src/lib/' }
49 | ]
50 | }
51 | },
52 |
53 | watch: {
54 | files: 'src/**/*',
55 | tasks: 'default'
56 | },
57 |
58 | jshint: {
59 | src: {
60 | options: {
61 | jshintrc: '.jshintrc'
62 | },
63 | src: ['src/*.js']
64 | }
65 | }
66 | });
67 |
68 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
69 |
70 | // Default task.
71 | grunt.registerTask('default', ['jshint', 'copy:libs', 'concat']);
72 | };
73 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Mat Marquis
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :warning: This project is archived and the repository is no longer maintained.
2 |
3 | # X-rayHTML
4 |
5 | [ ](http://www.filamentgroup.com/)
6 |
7 | A little something to help build documentation pages.
8 |
9 | Instead of dropping in a block of markup to render as a demo, then copying and pasting it into a `pre`/`code` block, then escaping it—then going back and updating both the rendered code and the escaped code should something change: now you just wrap the code you’re rendering in a `div` and it generates a copy/pastable source snippet. Credit to [@ugomobi](http://github.com/ugomobi) for the original idea, which is in use on the [jQuery Mobile docs](http://jquerymobile.com/test).
10 |
11 | ## Dependencies
12 |
13 | 1. jQuery or Shoestring (`./libs`)
14 | 1. prism.js (`./libs`) (Optional)
15 |
16 | ## Install
17 |
18 | This plugin is available on npm as [`xrayhtml`](https://www.npmjs.com/package/xrayhtml).
19 |
20 | ```
21 | npm install xrayhtml
22 | ```
23 |
24 | ## Demos
25 | [Here’s the plugin in action](http://filamentgroup.github.com/X-rayHTML/).
26 |
27 | The second set of demos are using the plugin’s “create” event (`create.xrayhtml` by default, but configurable) to bolt on [Prism.js](http://prismjs.com) syntax highlighting.
28 |
29 | ## Getting Started
30 | Download the [production version][min] or the [development version][max], and the [structural CSS][css].
31 |
32 | [min]: https://raw.github.com/filamentgroup/X-rayHTML/master/dist/X-rayHTML.min.js
33 | [max]: https://raw.github.com/filamentgroup/X-rayHTML/master/dist/X-rayHTML.js
34 | [css]: https://raw.github.com/filamentgroup/X-rayHTML/master/dist/X-rayHTML.css
35 |
36 | In your page:
37 |
38 | ```html
39 |
40 |
41 | ```
42 |
43 | and
44 |
45 | ```html
46 |
47 | ```
48 |
49 | There are some config options up at the top of `X-rayHTML.js`:
50 |
51 | ```javascript
52 | var pluginName = "xrayhtml",
53 | o = {
54 | text: {
55 | open: "View Source",
56 | close: "View Demo"
57 | },
58 | classes: {
59 | button: "btn btn-small",
60 | open: "view-source",
61 | sourcepanel: "source-panel"
62 | },
63 | initSelector: "[data-" + pluginName + "]",
64 | defaultReveal: "inline"
65 | }
66 | ```
67 |
68 | By default, functionality is hooked to the `xrayhtml` data attribute.
69 |
70 | `flip` as the value of the `data-xrayhtml` attribute will gives you a snazzy flip-to-reveal animation (browsers without support for 3D tranforms will simply show/hide the code snippet).
71 |
72 | Leaving `data-xrayhtml` valueless or giving it a value of `inline` gives you—predictably enough—code snippets that are visible inline with the rendered code.
73 |
74 | A `pre`/`code` block gets dropped into place, so whitespace inside of the element with that attribute counts the same way. For example, to avoid a bunch of extra whitespace at the start/end of your snippet:
75 |
76 | ```html
77 |
94 | ...
95 | ```
96 |
97 | Critically, the value of the attribute should point to a URL which serves a document that includes all of the necessary assets to handle the example properly.
98 |
99 | That document must also include the `xrayhtml-iframe.js` found in the `dist`
100 | directory. The JS will listen for messages from the `xrayhtml.js` in the parent
101 | page to so that it can communicate dimensions for the iframe during initial load and also during `resize` events.
102 |
103 | Following the example above, you might include the following in `/xray.html`
104 |
105 | ```html
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | ```
116 |
--------------------------------------------------------------------------------
/demo/demo.css:
--------------------------------------------------------------------------------
1 | @charset utf-8;
2 |
3 | /* Demo
4 | -------------------------- */
5 | .sub, h2 {
6 | font-family: sans-serif;
7 | }
8 | .sub {
9 | border-bottom: 1px solid #ddd;
10 | padding: .25em 0;
11 | }
12 | h2 {
13 | font-size: 1.1em;
14 | }
15 |
16 | .btn {
17 | background-color: #fefefe;
18 | background-image: -webkit-linear-gradient( top, rgba( 255,255,255,.1 ) 0%, rgba( 255,255,255,.1 ) 50%, rgba( 170,170,170,.1 ) 55%, rgba( 120,120,120,.15 ) 100% );
19 | background-image: -moz-linear-gradient( top, rgba( 255,255,255,.1 ) 0%, rgba( 255,255,255,.1 ) 50%, rgba( 170,170,170,.1 ) 55%, rgba( 120,120,120,.15 ) 100% );
20 | background-image: linear-gradient( top, rgba( 255,255,255,.1 ) 0%, rgba( 255,255,255,.1 ) 50%, rgba( 170,170,170,.1 ) 55%, rgba( 120,120,120,.15 ) 100% );
21 | border: 1px solid #ccc;
22 | border-radius: .4em;
23 | color: #308bd3;
24 | cursor: pointer;
25 | display: block;
26 | font: bold 14px/1 sans-serif;
27 | padding: .6em 0 .5em 0;
28 | position: relative;
29 | text-align: center;
30 | text-decoration: none;
31 | text-transform: capitalize;
32 | text-shadow: 0 1px 0 #fff;
33 | width: 100%;
34 |
35 | -webkit-box-sizing: border-box;
36 | -moz-box-sizing: border-box;
37 | -ms-box-sizing: border-box;
38 | box-sizing: border-box;
39 | }
40 |
41 |
42 | .method-inline .btn {
43 | width: auto;
44 | display: inline-block;
45 | position: absolute;
46 | right: 0;
47 | bottom: 0;
48 | padding-left: 1em;
49 | padding-right: 1em;
50 | border-radius: 5px 0 0 0;
51 | border-width: 1px 0 0 1px;
52 | }
53 |
54 | .method-flip .btn-copy {
55 | border-width: 0 0 1px 0;
56 | border-radius: 0;
57 | }
58 | .btn-small {
59 | display: inline-block;
60 | font-size: .8em;
61 | padding: .4em 2% .35em 2%;
62 | width: auto;
63 | }
64 | aside {
65 | padding: .5em;
66 | }
67 | blockquote {
68 | font-size: 1.6em;
69 | margin: 0;
70 | position: relative;
71 | }
72 | blockquote:before {
73 | color: #ccc;
74 | content: "“";
75 | font-size: 4em;
76 | left: 0;
77 | line-height: .25;
78 | position: absolute;
79 | top: 0;
80 | z-index: 1;
81 | }
82 | blockquote p {
83 | position: relative;
84 | margin: 1em;
85 | z-index: 2;
86 | }
87 | address {
88 | font-size: 1.2em;
89 | }
90 | cite {
91 | font-size: 1.1em;
92 | }
93 | /* Logo */
94 | .header {
95 | background: #247201 url(http://filamentgroup.com/images/headerbg-new.jpg) no-repeat bottom left;
96 | }
97 | #fg-logo {
98 | text-indent: -9999px;
99 | margin: 0 auto;
100 | width: 287px;
101 | height: 52px;
102 | background-image: url(http://filamentgroup.com/images/fg-logo-icon.png);
103 | }
104 | @media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5){
105 | #fg-logo {
106 | background-size: 287px 52px;
107 | background-image: url(http://filamentgroup.com/images/fg-logo-icon-lrg.png);
108 | }
109 | }
110 |
111 |
--------------------------------------------------------------------------------
/dist/xrayhtml-iframe.js:
--------------------------------------------------------------------------------
1 | /*! X-rayHTML - v2.3.0 - 2019-06-28
2 | * https://github.com/filamentgroup/x-rayhtml
3 | * Copyright (c) 2019 Filament Group; Licensed MIT */
4 | (function(){
5 |
6 | // Empty and exec the ready queue
7 | var ready = false,
8 | readyQueue = [],
9 | runReady = function(){
10 | if( !ready ){
11 | while( readyQueue.length ){
12 | readyQueue.shift().call( document );
13 | }
14 | ready = true;
15 | }
16 | };
17 |
18 | // The following is borrowed wholesale from shoestring
19 | // https://github.com/filamentgroup/shoestring/blob/master/src/core/ready.js
20 | function onReady( callback ){
21 | if( ready && callback ){
22 | callback.call( document );
23 | } else if( callback ){
24 | readyQueue.push( callback );
25 | } else {
26 | runReady();
27 | }
28 |
29 | return [document];
30 | }
31 |
32 |
33 | // Quick IE8 shiv
34 | if( !window.addEventListener ){
35 | window.addEventListener = function( evt, cb ){
36 | return window.attachEvent( "on" + evt, cb );
37 | };
38 | }
39 |
40 | // reset ready state with new content for the iframe
41 | // TODO bind to load event for other items, e.g. stylesheets
42 | function resetReady(imageCount){
43 | ready = false;
44 | var eventCounter = 0;
45 |
46 | if(! imageCount ){
47 | runReady();
48 | return;
49 | }
50 |
51 | var eventIncrement = function(event){
52 | if( event.target.tagName !== 'IMG' ){
53 | return;
54 | }
55 |
56 | eventCounter++;
57 |
58 | // all of the images and the load event
59 | if(eventCounter === imageCount){
60 | runReady();
61 | }
62 | };
63 |
64 | document.body.addEventListener("load", eventIncrement, true);
65 | }
66 |
67 | function sendSize( iframeid ){
68 | window
69 | .parent
70 | .postMessage('{ "iframeid": ' + iframeid + ', "iframeheight" : ' +
71 | document.documentElement.offsetHeight +
72 | '}', "*");
73 | }
74 |
75 | var id;
76 |
77 | window.addEventListener("message", function( event ){
78 | // same host check
79 | var origin = event.origin || event.originalEvent.origin;
80 | var allowedOrigin = new RegExp( "https?:\/\/" + location.host );
81 | if( !origin.match( allowedOrigin ) ){
82 | return;
83 | }
84 |
85 | var data = event.data || event.originalEvent.data;
86 | var elem = document.querySelector(data.selector || "body");
87 | var fragment = document.createElement("div");
88 | fragment.innerHTML = data.html;
89 |
90 | // the document is now not ready and needs to wait until everything
91 | // new has loaded (proxy: when all the images have loaded)
92 | resetReady(fragment.querySelectorAll("img").length);
93 |
94 | // use the passed information to populate the page
95 | for(var x = 0; x < fragment.children.length; x++) {
96 | elem.append(fragment.children[0]);
97 | }
98 |
99 | id = data.id;
100 |
101 | // wait until everything loads to calc the height and communicate it
102 | // TODO it would be better to bind to the load of the styles at least
103 | onReady(function(){
104 | sendSize(id);
105 | });
106 | }, false);
107 |
108 | var minInterval = 300;
109 | var resized = false;
110 | window.addEventListener("resize", function(){
111 | if(resized){ return; }
112 | sendSize(id);
113 | resized = true;
114 | setTimeout(function(){ resized = false; }, minInterval);
115 | });
116 | })();
117 |
--------------------------------------------------------------------------------
/dist/xrayhtml.css:
--------------------------------------------------------------------------------
1 | /*! X-rayHTML - v2.3.0 - 2019-06-28
2 | * https://github.com/filamentgroup/x-rayhtml
3 | * Copyright (c) 2019 Filament Group; Licensed MIT */
4 | .xrayhtml {
5 | border: 1px solid rgba(0,0,0,.1);
6 | border-radius: .3em;
7 | margin: 1.5em 0 2.5em 0;
8 | padding: 1em 1em 2em;
9 | }
10 | .xrayhtml.xray-copy {
11 | position: relative;
12 | }
13 | .xrayhtml .xraytitle {
14 | text-transform: uppercase;
15 | letter-spacing: 1px;
16 | font: .75em sans-serif;
17 | color: rgba(0,0,0,.5);
18 | background-color: #fff;
19 | border-radius: 3px;
20 | display: inline-block;
21 | position: relative;
22 | top: -2.166666667em; /* 26px */
23 | padding-left: .1em;
24 | padding-right: .1em;
25 | z-index: 3;
26 | margin: 0;
27 | }
28 | .xrayhtml.method-flip:before {
29 | background-color: rgba(255,255,255,.6);
30 | }
31 | .xrayhtml .source-panel {
32 | background: #f7f7f7;
33 | margin-top: 2em;
34 | tab-size: 2;
35 | }
36 | .xrayhtml .source-panel pre {
37 | margin: 0;
38 | padding: 16px;
39 | border-radius: 0 0 .3em .3em;
40 | }
41 | .xrayhtml .source-panel code {
42 | white-space: pre-wrap;
43 | }
44 | .xrayhtml.method-flip .source-panel {
45 | margin-top: 0;
46 | border-radius: 0.3em;
47 | }
48 | .xrayhtml.method-inline .source-panel {
49 | margin: 2em -1em -2em -1em !important; /* Prism override. */
50 | border-top: 1px solid rgba(0,0,0,.1);
51 | border-radius: 0 0 .3em .3em;
52 | }
53 | /* Prism override. */
54 | .xrayhtml .source-panel code.language-markup {
55 | white-space: pre-wrap !important;
56 | }
57 |
58 | .xrayhtml.antipattern {
59 | border-color: #C9282D;
60 | }
61 | .xrayhtml.antipattern .xraytitle {
62 | color: #d75e72;
63 | font-weight: 700;
64 | }
65 |
66 | /* Flip Animation */
67 | .xrayhtml.method-flip {
68 | padding: 0;
69 | }
70 | .method-flip {
71 | -webkit-perspective: 2500px;
72 | -moz-perspective: 2500px;
73 | perspective: 2500px;
74 | }
75 | .method-flip .source-panel {
76 | position: absolute;
77 | top: 0;
78 | left: 0;
79 | width: 100%;
80 | height: 100%;
81 | overflow-x: auto;
82 | }
83 | .method-flip .snippet {
84 | margin: 0;
85 | position: relative;
86 | top: 0;
87 | left: 0;
88 | z-index: 2;
89 | min-height: 100%;
90 | background-color: #fff;
91 | padding: 1em;
92 |
93 | -webkit-transform: rotateY(0deg);
94 | -webkit-transform-style: preserve-3d;
95 | -webkit-backface-visibility: hidden;
96 |
97 | -moz-transform: rotateY(0deg);
98 | -moz-transform-style: preserve-3d;
99 | -moz-backface-visibility: hidden;
100 |
101 | -webkit-transition: -webkit-transform .4s ease-in-out;
102 | -moz-transition: -moz-transform .4s ease-in-out;
103 | }
104 | .method-flip.view-source .snippet {
105 | z-index: 1;
106 | -webkit-transform: rotateY(180deg);
107 | -moz-transform: rotateY(180deg);
108 | }
109 | .method-flip .source-panel {
110 | -webkit-transform: rotateY(-180deg);
111 | -webkit-backface-visibility: hidden;
112 |
113 | -moz-transform: rotateY(-180deg);
114 | -moz-backface-visibility: hidden;
115 |
116 | -moz-transition: all .4s ease-in-out;
117 | -webkit-transition: all .4s ease-in-out;
118 | }
119 | .method-flip.view-source .source-panel {
120 | z-index: 2;
121 | -webkit-transform: rotateY(0deg);
122 | -moz-transform: rotateY(0deg);
123 | }
124 |
125 | .method-flip.view-source .xraytitle {
126 | background-color: transparent;
127 | background-image: linear-gradient(
128 | to bottom,
129 | transparent,
130 | transparent 40%,
131 | #ffffff 40%,
132 | transparent);
133 | }
134 |
135 | iframe.xray-iframe {
136 | border: 0;
137 | width: 100%
138 | }
--------------------------------------------------------------------------------
/dist/xrayhtml.js:
--------------------------------------------------------------------------------
1 | /*! X-rayHTML - v2.3.0 - 2019-06-28
2 | * https://github.com/filamentgroup/x-rayhtml
3 | * Copyright (c) 2019 Filament Group; Licensed MIT */
4 | window.jQuery = window.jQuery || window.shoestring;
5 |
6 | (function( $ ) {
7 | var xrayiframeid = 0;
8 | var pluginName = "xrayhtml",
9 | o = {
10 | text: {
11 | open: "View Source",
12 | close: "View Demo",
13 | titlePrefix: "Example",
14 | antipattern: "Do Not Use"
15 | },
16 | classes: {
17 | button: "btn btn-small btn-xrayhtml-flipsource",
18 | open: "view-source",
19 | sourcepanel: "source-panel",
20 | title: "xraytitle",
21 | antipattern: "antipattern"
22 | },
23 | initSelector: "[data-" + pluginName + "]",
24 | defaultReveal: "inline"
25 | },
26 | methods = {
27 | _create: function() {
28 | return $( this ).each(function() {
29 | var init = $( this ).data( "init." + pluginName );
30 |
31 | if( init ) {
32 | return false;
33 | }
34 |
35 | $( this )
36 | .data( "init." + pluginName, true )
37 | [ pluginName ]( "_init" )
38 | .trigger( "create." + pluginName );
39 | });
40 | },
41 | _init: function() {
42 | var $self = $(this);
43 |
44 | $self.data( "id." + pluginName, xrayiframeid++);
45 |
46 | var method = $( this ).attr( "data-" + pluginName ) || o.defaultReveal;
47 |
48 | if( method === "flip" ) {
49 | $( this )[ pluginName ]( "_createButton" );
50 | }
51 |
52 | $( this )
53 | .addClass( pluginName + " " + "method-" + method )
54 | [ pluginName ]( "_createSource" );
55 |
56 | // use an iframe to host the source
57 | if( $(this).is("[data-" + pluginName + "-iframe]") ){
58 |
59 | // grab the snippet html to ship to the iframe
60 | var snippetHTML = $(this).find(".snippet").html();
61 |
62 | // grab the url of the iframe to load
63 | var url = $(this).attr("data-" + pluginName + "-iframe");
64 |
65 | // grab the selector for the element in the iframe to put the html in
66 | var selector = $(this).attr("data-" + pluginName + "-iframe-target");
67 |
68 | // create the iframe element, so we can bind to the load event
69 | var $iframe = $("
");
70 |
71 | // get the scripts and styles to ship to the iframe
72 | // TODO we should support styles/scripts elsewhere in the page
73 | var headHTML = $( "head" ).html();
74 |
75 | // wait until the iframe loads to send the data
76 | $iframe.bind("load",function(){
77 |
78 | // wait for the iframe page to transmit the height of the page
79 | $(window).bind("message", function(event){
80 | var data = JSON.parse(event.data || event.originalEvent.data);
81 |
82 | if( data.iframeid !== $self.data("id." + pluginName) ){
83 | return;
84 | }
85 |
86 | $iframe.attr("height", data.iframeheight);
87 | });
88 |
89 | // send a message to the iframe with the snippet to load and any
90 | // assets that are required to make it look right
91 | $iframe[0].contentWindow.postMessage({
92 | html: snippetHTML,
93 | head: headHTML,
94 | id: $self.data("id." + pluginName),
95 | selector: selector
96 | }, "*");
97 | });
98 |
99 | // style the iframe properly
100 | $iframe.addClass("xray-iframe");
101 |
102 | // replace the snippet which is rendered in the page with the iframe
103 | $(this).find(".snippet").html("").append($iframe);
104 | }
105 | },
106 | _createButton: function() {
107 | var btn = document.createElement( "a" ),
108 | txt = document.createTextNode( o.text.open ),
109 | el = $( this );
110 |
111 | btn.setAttribute( "class", o.classes.button );
112 | btn.href = "#";
113 | btn.appendChild( txt );
114 |
115 | $( btn )
116 | .bind( "click", function( e ) {
117 | var isOpen = el.attr( "class" ).indexOf( o.classes.open ) > -1;
118 |
119 | el[ isOpen ? "removeClass" : "addClass" ]( o.classes.open );
120 | btn.innerHTML = ( isOpen ? o.text.open : o.text.close );
121 |
122 | e.preventDefault();
123 |
124 | })
125 | .insertBefore( el );
126 | },
127 | _createSource: function() {
128 | var el = this;
129 | var getPrefixText = function () {
130 | if( el.className.match( new RegExp( "\\b" + o.classes.antipattern + "\\b", "gi" ) ) ) {
131 | return o.text.antipattern;
132 | }
133 | return o.text.titlePrefix;
134 | };
135 |
136 | var title = el.getElementsByClassName( o.classes.title );
137 | var deprecatedTitle;
138 |
139 | if( title.length ) {
140 | title = title[ 0 ];
141 | title.parentNode.removeChild( title );
142 | title.innerHTML = getPrefixText() + ": " + title.innerHTML;
143 | } else {
144 | deprecatedTitle = el.getAttribute( "data-title" );
145 | title = document.createElement( "div" );
146 | title.className = o.classes.title;
147 | title.innerHTML = getPrefixText() + ( deprecatedTitle ? ": " + deprecatedTitle : "" );
148 | }
149 |
150 | var suppliedsourcepanel = $( el ).find("." + o.classes.sourcepanel );
151 | var sourcepanel = document.createElement( "div" );
152 | var preel = document.createElement( "pre" );
153 | var codeel = document.createElement( "code" );
154 | var wrap = document.createElement( "div" );
155 | var code;
156 | var leadingWhiteSpace;
157 | var source;
158 |
159 | if( suppliedsourcepanel.length ) {
160 | code = suppliedsourcepanel[0].innerHTML;
161 | suppliedsourcepanel.remove();
162 | } else {
163 | code = el.innerHTML;
164 | }
165 |
166 | // remove empty value attributes
167 | code = code.replace( /\=\"\"/g, '' );
168 | leadingWhiteSpace = code.match( /(^[\s]+)/ );
169 |
170 | if( leadingWhiteSpace ) {
171 | code = code.replace( new RegExp( leadingWhiteSpace[ 1 ], "gmi" ), "\n" );
172 | }
173 |
174 | source = document.createTextNode( code );
175 |
176 | wrap.setAttribute( "class", "snippet" );
177 |
178 | $( el ).wrapInner( wrap );
179 |
180 | codeel.appendChild( source );
181 | preel.appendChild( codeel );
182 |
183 | sourcepanel.setAttribute( "class", o.classes.sourcepanel );
184 | sourcepanel.appendChild( preel );
185 |
186 | this.appendChild( sourcepanel );
187 |
188 | this.insertBefore( title, this.firstChild );
189 | }
190 | };
191 |
192 | // Collection method.
193 | $.fn[ pluginName ] = function( arrg, a, b, c ) {
194 | return this.each(function() {
195 |
196 | // if it's a method
197 | if( arrg && typeof( arrg ) === "string" ){
198 | return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c );
199 | }
200 |
201 | // don't re-init
202 | if( $( this ).data( pluginName + "data" ) ){
203 | return $( this );
204 | }
205 |
206 | // otherwise, init
207 | $( this ).data( pluginName + "active", true );
208 | $.fn[ pluginName ].prototype._create.call( this );
209 | });
210 | };
211 |
212 | // add methods
213 | $.extend( $.fn[ pluginName ].prototype, methods );
214 |
215 | // auto-init
216 | var initted;
217 | function init(){
218 | if( !initted ){
219 | $( o.initSelector )[ pluginName ]();
220 | initted = true;
221 | }
222 | }
223 | // init either on beforeenhance event or domready, whichever comes first.
224 | $( document ).bind("beforeenhance", init );
225 | $( init );
226 |
227 |
228 | }( jQuery ));
229 |
--------------------------------------------------------------------------------
/iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 | Basics
30 | By default, functionality is hooked to the data-xrayhtml
attribute.
31 |
32 | Inline
33 | Bolting the data-xrayhtml
attribute on without a value will give you inline code snippets, like so:
34 |
35 |
44 |
45 | Supplies Source Panel in Markup
46 |
65 |
66 | Add a subtitle
67 | Use a heading element with an xraytitle
class to add your own subtitle to the example text.
68 | Also works with a data-title
attribute but that approach is deprecated.
69 |
70 |
71 |
Parapraph
72 |
This is a paragraph with text.
73 |
74 |
75 | Anti-patterns and Bad Examples
76 | Add class="antipattern"
to show a bad example. Also works with subtitles as documented above.
77 |
78 |
79 |
Non-standard HTML
80 | Text
is not a standard HTML element. Do not use this code.
81 |
82 |
83 | Flip
84 | Setting “flip” as the value of the data-xrayhtml
attribute will give you this snazzy flip-to-reveal animation (browsers without support for 3D tranforms will simply show/hide the code snippet).
85 |
86 |
95 |
96 |
97 |
98 |
99 |
100 | Extras
101 | The plugin fires off a create.xrayhtml
event that can be used to attach features like syntax highlighting (using Prism.js ), or "copy to clipboard" (using Clipboard ).
102 |
103 | Prism.js
104 | Add the prism
class to the data-xrayhtml
container to colorize the code snippet.
105 |
106 |
115 |
116 | Copy (clipboard.js)
117 | Add the xray-copy
class to the data-xrayhtml
container to append a "Copy" button to the code snippet; when clicked, it'll copy the entire snippet.
118 |
119 |
120 |
121 |
122 | It is the unofficial force—the Baker Street irregulars.
123 |
124 | Sherlock Holmes
125 | Sign of Four
126 | (Copied from the demo page)
127 |
128 |
129 |
130 | iframe
131 | It's also possible to use an iframe for situations where styles (like media queries) depend on the viewport. Specify the URL for the iframe with data-xrayhtml-iframe="iframe.html"
on the data-xrayhtml
element. You can also tell XRay where in the iframe page to place the included markup using data-xrayhtml-iframe-target="#replace"
. Otherwise it will default to replacing the body
HTML with the code XRay code snippet.
132 |
133 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
190 |
191 |
192 |
193 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xrayhtml",
3 | "title": "X-rayHTML",
4 | "description": "A plugin to easily show examples of components with the markup to use them.",
5 | "homepage": "https://github.com/filamentgroup/x-rayhtml",
6 | "version": "2.3.0",
7 | "author": "Mat Marquis",
8 | "contributors": [
9 | {
10 | "name": "Zach Leatherman",
11 | "company": "Filament Group",
12 | "email": "zach@filamentgroup.com",
13 | "url": "http://filamentgroup.com/"
14 | }
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/filamentgroup/x-rayhtml.git"
19 | },
20 | "bugs": "https://github.com/filamentgroup/x-rayhtml/issues",
21 | "license": "MIT",
22 | "engines": {
23 | "node": ">= 0.6.0"
24 | },
25 | "scripts": {
26 | "test": "grunt qunit"
27 | },
28 | "dependencies": {
29 | "jquery": "^3.2.1",
30 | "shoestring": "^2.0.1",
31 | "prismjs": "^1.6.0",
32 | "clipboard": "^1.7.1"
33 | },
34 | "devDependencies": {
35 | "grunt": "~1.0.1",
36 | "grunt-contrib-concat": "~1.0.1",
37 | "grunt-contrib-copy": "^1.0.0",
38 | "grunt-contrib-jshint": "~1.1.0",
39 | "grunt-contrib-qunit": "~2.0.0",
40 | "grunt-contrib-watch": "~1.0.0",
41 | "matchdep": "^1.0.1"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/X-rayHTML-iframe.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | // Empty and exec the ready queue
4 | var ready = false,
5 | readyQueue = [],
6 | runReady = function(){
7 | if( !ready ){
8 | while( readyQueue.length ){
9 | readyQueue.shift().call( document );
10 | }
11 | ready = true;
12 | }
13 | };
14 |
15 | // The following is borrowed wholesale from shoestring
16 | // https://github.com/filamentgroup/shoestring/blob/master/src/core/ready.js
17 | function onReady( callback ){
18 | if( ready && callback ){
19 | callback.call( document );
20 | } else if( callback ){
21 | readyQueue.push( callback );
22 | } else {
23 | runReady();
24 | }
25 |
26 | return [document];
27 | }
28 |
29 |
30 | // Quick IE8 shiv
31 | if( !window.addEventListener ){
32 | window.addEventListener = function( evt, cb ){
33 | return window.attachEvent( "on" + evt, cb );
34 | };
35 | }
36 |
37 | // reset ready state with new content for the iframe
38 | // TODO bind to load event for other items, e.g. stylesheets
39 | function resetReady(imageCount){
40 | ready = false;
41 | var eventCounter = 0;
42 |
43 | if(! imageCount ){
44 | runReady();
45 | return;
46 | }
47 |
48 | var eventIncrement = function(event){
49 | if( event.target.tagName !== 'IMG' ){
50 | return;
51 | }
52 |
53 | eventCounter++;
54 |
55 | // all of the images and the load event
56 | if(eventCounter === imageCount){
57 | runReady();
58 | }
59 | };
60 |
61 | document.body.addEventListener("load", eventIncrement, true);
62 | }
63 |
64 | function sendSize( iframeid ){
65 | window
66 | .parent
67 | .postMessage('{ "iframeid": ' + iframeid + ', "iframeheight" : ' +
68 | document.documentElement.offsetHeight +
69 | '}', "*");
70 | }
71 |
72 | var id;
73 |
74 | window.addEventListener("message", function( event ){
75 | // same host check
76 | var origin = event.origin || event.originalEvent.origin;
77 | var allowedOrigin = new RegExp( "https?:\/\/" + location.host );
78 | if( !origin.match( allowedOrigin ) ){
79 | return;
80 | }
81 |
82 | var data = event.data || event.originalEvent.data;
83 | var elem = document.querySelector(data.selector || "body");
84 | var fragment = document.createElement("div");
85 | fragment.innerHTML = data.html;
86 |
87 | // the document is now not ready and needs to wait until everything
88 | // new has loaded (proxy: when all the images have loaded)
89 | resetReady(fragment.querySelectorAll("img").length);
90 |
91 | // use the passed information to populate the page
92 | for(var x = 0; x < fragment.children.length; x++) {
93 | elem.append(fragment.children[0]);
94 | }
95 |
96 | id = data.id;
97 |
98 | // wait until everything loads to calc the height and communicate it
99 | // TODO it would be better to bind to the load of the styles at least
100 | onReady(function(){
101 | sendSize(id);
102 | });
103 | }, false);
104 |
105 | var minInterval = 300;
106 | var resized = false;
107 | window.addEventListener("resize", function(){
108 | if(resized){ return; }
109 | sendSize(id);
110 | resized = true;
111 | setTimeout(function(){ resized = false; }, minInterval);
112 | });
113 | })();
114 |
--------------------------------------------------------------------------------
/src/X-rayHTML.css:
--------------------------------------------------------------------------------
1 | .xrayhtml {
2 | border: 1px solid rgba(0,0,0,.1);
3 | border-radius: .3em;
4 | margin: 1.5em 0 2.5em 0;
5 | padding: 1em 1em 2em;
6 | }
7 | .xrayhtml.xray-copy {
8 | position: relative;
9 | }
10 | .xrayhtml .xraytitle {
11 | text-transform: uppercase;
12 | letter-spacing: 1px;
13 | font: .75em sans-serif;
14 | color: rgba(0,0,0,.5);
15 | background-color: #fff;
16 | border-radius: 3px;
17 | display: inline-block;
18 | position: relative;
19 | top: -2.166666667em; /* 26px */
20 | padding-left: .1em;
21 | padding-right: .1em;
22 | z-index: 3;
23 | margin: 0;
24 | }
25 | .xrayhtml.method-flip:before {
26 | background-color: rgba(255,255,255,.6);
27 | }
28 | .xrayhtml .source-panel {
29 | background: #f7f7f7;
30 | margin-top: 2em;
31 | tab-size: 2;
32 | }
33 | .xrayhtml .source-panel pre {
34 | margin: 0;
35 | padding: 16px;
36 | border-radius: 0 0 .3em .3em;
37 | }
38 | .xrayhtml .source-panel code {
39 | white-space: pre-wrap;
40 | }
41 | .xrayhtml.method-flip .source-panel {
42 | margin-top: 0;
43 | border-radius: 0.3em;
44 | }
45 | .xrayhtml.method-inline .source-panel {
46 | margin: 2em -1em -2em -1em !important; /* Prism override. */
47 | border-top: 1px solid rgba(0,0,0,.1);
48 | border-radius: 0 0 .3em .3em;
49 | }
50 | /* Prism override. */
51 | .xrayhtml .source-panel code.language-markup {
52 | white-space: pre-wrap !important;
53 | }
54 |
55 | .xrayhtml.antipattern {
56 | border-color: #C9282D;
57 | }
58 | .xrayhtml.antipattern .xraytitle {
59 | color: #d75e72;
60 | font-weight: 700;
61 | }
62 |
63 | /* Flip Animation */
64 | .xrayhtml.method-flip {
65 | padding: 0;
66 | }
67 | .method-flip {
68 | -webkit-perspective: 2500px;
69 | -moz-perspective: 2500px;
70 | perspective: 2500px;
71 | }
72 | .method-flip .source-panel {
73 | position: absolute;
74 | top: 0;
75 | left: 0;
76 | width: 100%;
77 | height: 100%;
78 | overflow-x: auto;
79 | }
80 | .method-flip .snippet {
81 | margin: 0;
82 | position: relative;
83 | top: 0;
84 | left: 0;
85 | z-index: 2;
86 | min-height: 100%;
87 | background-color: #fff;
88 | padding: 1em;
89 |
90 | -webkit-transform: rotateY(0deg);
91 | -webkit-transform-style: preserve-3d;
92 | -webkit-backface-visibility: hidden;
93 |
94 | -moz-transform: rotateY(0deg);
95 | -moz-transform-style: preserve-3d;
96 | -moz-backface-visibility: hidden;
97 |
98 | -webkit-transition: -webkit-transform .4s ease-in-out;
99 | -moz-transition: -moz-transform .4s ease-in-out;
100 | }
101 | .method-flip.view-source .snippet {
102 | z-index: 1;
103 | -webkit-transform: rotateY(180deg);
104 | -moz-transform: rotateY(180deg);
105 | }
106 | .method-flip .source-panel {
107 | -webkit-transform: rotateY(-180deg);
108 | -webkit-backface-visibility: hidden;
109 |
110 | -moz-transform: rotateY(-180deg);
111 | -moz-backface-visibility: hidden;
112 |
113 | -moz-transition: all .4s ease-in-out;
114 | -webkit-transition: all .4s ease-in-out;
115 | }
116 | .method-flip.view-source .source-panel {
117 | z-index: 2;
118 | -webkit-transform: rotateY(0deg);
119 | -moz-transform: rotateY(0deg);
120 | }
121 |
122 | .method-flip.view-source .xraytitle {
123 | background-color: transparent;
124 | background-image: linear-gradient(
125 | to bottom,
126 | transparent,
127 | transparent 40%,
128 | #ffffff 40%,
129 | transparent);
130 | }
131 |
132 | iframe.xray-iframe {
133 | border: 0;
134 | width: 100%
135 | }
--------------------------------------------------------------------------------
/src/X-rayHTML.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * View Source
4 | * Generates copy/pastable markup from actual rendered markup.
5 | *
6 | * Copyright (c) 2016 Filament Group, Inc.
7 | * Licensed under the MIT, GPL licenses.
8 | */
9 |
10 | window.jQuery = window.jQuery || window.shoestring;
11 |
12 | (function( $ ) {
13 | var xrayiframeid = 0;
14 | var pluginName = "xrayhtml",
15 | o = {
16 | text: {
17 | open: "View Source",
18 | close: "View Demo",
19 | titlePrefix: "Example",
20 | antipattern: "Do Not Use"
21 | },
22 | classes: {
23 | button: "btn btn-small btn-xrayhtml-flipsource",
24 | open: "view-source",
25 | sourcepanel: "source-panel",
26 | title: "xraytitle",
27 | antipattern: "antipattern"
28 | },
29 | initSelector: "[data-" + pluginName + "]",
30 | defaultReveal: "inline"
31 | },
32 | methods = {
33 | _create: function() {
34 | return $( this ).each(function() {
35 | var init = $( this ).data( "init." + pluginName );
36 |
37 | if( init ) {
38 | return false;
39 | }
40 |
41 | $( this )
42 | .data( "init." + pluginName, true )
43 | [ pluginName ]( "_init" )
44 | .trigger( "create." + pluginName );
45 | });
46 | },
47 | _init: function() {
48 | var $self = $(this);
49 |
50 | $self.data( "id." + pluginName, xrayiframeid++);
51 |
52 | var method = $( this ).attr( "data-" + pluginName ) || o.defaultReveal;
53 |
54 | if( method === "flip" ) {
55 | $( this )[ pluginName ]( "_createButton" );
56 | }
57 |
58 | $( this )
59 | .addClass( pluginName + " " + "method-" + method )
60 | [ pluginName ]( "_createSource" );
61 |
62 | // use an iframe to host the source
63 | if( $(this).is("[data-" + pluginName + "-iframe]") ){
64 |
65 | // grab the snippet html to ship to the iframe
66 | var snippetHTML = $(this).find(".snippet").html();
67 |
68 | // grab the url of the iframe to load
69 | var url = $(this).attr("data-" + pluginName + "-iframe");
70 |
71 | // grab the selector for the element in the iframe to put the html in
72 | var selector = $(this).attr("data-" + pluginName + "-iframe-target");
73 |
74 | // create the iframe element, so we can bind to the load event
75 | var $iframe = $("
");
76 |
77 | // get the scripts and styles to ship to the iframe
78 | // TODO we should support styles/scripts elsewhere in the page
79 | var headHTML = $( "head" ).html();
80 |
81 | // wait until the iframe loads to send the data
82 | $iframe.bind("load",function(){
83 |
84 | // wait for the iframe page to transmit the height of the page
85 | $(window).bind("message", function(event){
86 | var data = JSON.parse(event.data || event.originalEvent.data);
87 |
88 | if( data.iframeid !== $self.data("id." + pluginName) ){
89 | return;
90 | }
91 |
92 | $iframe.attr("height", data.iframeheight);
93 | });
94 |
95 | // send a message to the iframe with the snippet to load and any
96 | // assets that are required to make it look right
97 | $iframe[0].contentWindow.postMessage({
98 | html: snippetHTML,
99 | head: headHTML,
100 | id: $self.data("id." + pluginName),
101 | selector: selector
102 | }, "*");
103 | });
104 |
105 | // style the iframe properly
106 | $iframe.addClass("xray-iframe");
107 |
108 | // replace the snippet which is rendered in the page with the iframe
109 | $(this).find(".snippet").html("").append($iframe);
110 | }
111 | },
112 | _createButton: function() {
113 | var btn = document.createElement( "a" ),
114 | txt = document.createTextNode( o.text.open ),
115 | el = $( this );
116 |
117 | btn.setAttribute( "class", o.classes.button );
118 | btn.href = "#";
119 | btn.appendChild( txt );
120 |
121 | $( btn )
122 | .bind( "click", function( e ) {
123 | var isOpen = el.attr( "class" ).indexOf( o.classes.open ) > -1;
124 |
125 | el[ isOpen ? "removeClass" : "addClass" ]( o.classes.open );
126 | btn.innerHTML = ( isOpen ? o.text.open : o.text.close );
127 |
128 | e.preventDefault();
129 |
130 | })
131 | .insertBefore( el );
132 | },
133 | _createSource: function() {
134 | var el = this;
135 | var getPrefixText = function () {
136 | if( el.className.match( new RegExp( "\\b" + o.classes.antipattern + "\\b", "gi" ) ) ) {
137 | return o.text.antipattern;
138 | }
139 | return o.text.titlePrefix;
140 | };
141 |
142 | var title = el.getElementsByClassName( o.classes.title );
143 | var deprecatedTitle;
144 |
145 | if( title.length ) {
146 | title = title[ 0 ];
147 | title.parentNode.removeChild( title );
148 | title.innerHTML = getPrefixText() + ": " + title.innerHTML;
149 | } else {
150 | deprecatedTitle = el.getAttribute( "data-title" );
151 | title = document.createElement( "div" );
152 | title.className = o.classes.title;
153 | title.innerHTML = getPrefixText() + ( deprecatedTitle ? ": " + deprecatedTitle : "" );
154 | }
155 |
156 | var suppliedsourcepanel = $( el ).find("." + o.classes.sourcepanel );
157 | var sourcepanel = document.createElement( "div" );
158 | var preel = document.createElement( "pre" );
159 | var codeel = document.createElement( "code" );
160 | var wrap = document.createElement( "div" );
161 | var code;
162 | var leadingWhiteSpace;
163 | var source;
164 |
165 | if( suppliedsourcepanel.length ) {
166 | code = suppliedsourcepanel[0].innerHTML;
167 | suppliedsourcepanel.remove();
168 | } else {
169 | code = el.innerHTML;
170 | }
171 |
172 | // remove empty value attributes
173 | code = code.replace( /\=\"\"/g, '' );
174 | leadingWhiteSpace = code.match( /(^[\s]+)/ );
175 |
176 | if( leadingWhiteSpace ) {
177 | code = code.replace( new RegExp( leadingWhiteSpace[ 1 ], "gmi" ), "\n" );
178 | }
179 |
180 | source = document.createTextNode( code );
181 |
182 | wrap.setAttribute( "class", "snippet" );
183 |
184 | $( el ).wrapInner( wrap );
185 |
186 | codeel.appendChild( source );
187 | preel.appendChild( codeel );
188 |
189 | sourcepanel.setAttribute( "class", o.classes.sourcepanel );
190 | sourcepanel.appendChild( preel );
191 |
192 | this.appendChild( sourcepanel );
193 |
194 | this.insertBefore( title, this.firstChild );
195 | }
196 | };
197 |
198 | // Collection method.
199 | $.fn[ pluginName ] = function( arrg, a, b, c ) {
200 | return this.each(function() {
201 |
202 | // if it's a method
203 | if( arrg && typeof( arrg ) === "string" ){
204 | return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c );
205 | }
206 |
207 | // don't re-init
208 | if( $( this ).data( pluginName + "data" ) ){
209 | return $( this );
210 | }
211 |
212 | // otherwise, init
213 | $( this ).data( pluginName + "active", true );
214 | $.fn[ pluginName ].prototype._create.call( this );
215 | });
216 | };
217 |
218 | // add methods
219 | $.extend( $.fn[ pluginName ].prototype, methods );
220 |
221 | // auto-init
222 | var initted;
223 | function init(){
224 | if( !initted ){
225 | $( o.initSelector )[ pluginName ]();
226 | initted = true;
227 | }
228 | }
229 | // init either on beforeenhance event or domready, whichever comes first.
230 | $( document ).bind("beforeenhance", init );
231 | $( init );
232 |
233 |
234 | }( jQuery ));
235 |
--------------------------------------------------------------------------------
/src/lib/clipboard.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * clipboard.js v1.7.1
3 | * https://zenorocha.github.io/clipboard.js
4 | *
5 | * Licensed MIT © Zeno Rocha
6 | */
7 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o
0 && arguments[0] !== undefined ? arguments[0] : {};
424 |
425 | this.action = options.action;
426 | this.container = options.container;
427 | this.emitter = options.emitter;
428 | this.target = options.target;
429 | this.text = options.text;
430 | this.trigger = options.trigger;
431 |
432 | this.selectedText = '';
433 | }
434 | }, {
435 | key: 'initSelection',
436 | value: function initSelection() {
437 | if (this.text) {
438 | this.selectFake();
439 | } else if (this.target) {
440 | this.selectTarget();
441 | }
442 | }
443 | }, {
444 | key: 'selectFake',
445 | value: function selectFake() {
446 | var _this = this;
447 |
448 | var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
449 |
450 | this.removeFake();
451 |
452 | this.fakeHandlerCallback = function () {
453 | return _this.removeFake();
454 | };
455 | this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
456 |
457 | this.fakeElem = document.createElement('textarea');
458 | // Prevent zooming on iOS
459 | this.fakeElem.style.fontSize = '12pt';
460 | // Reset box model
461 | this.fakeElem.style.border = '0';
462 | this.fakeElem.style.padding = '0';
463 | this.fakeElem.style.margin = '0';
464 | // Move element out of screen horizontally
465 | this.fakeElem.style.position = 'absolute';
466 | this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
467 | // Move element to the same position vertically
468 | var yPosition = window.pageYOffset || document.documentElement.scrollTop;
469 | this.fakeElem.style.top = yPosition + 'px';
470 |
471 | this.fakeElem.setAttribute('readonly', '');
472 | this.fakeElem.value = this.text;
473 |
474 | this.container.appendChild(this.fakeElem);
475 |
476 | this.selectedText = (0, _select2.default)(this.fakeElem);
477 | this.copyText();
478 | }
479 | }, {
480 | key: 'removeFake',
481 | value: function removeFake() {
482 | if (this.fakeHandler) {
483 | this.container.removeEventListener('click', this.fakeHandlerCallback);
484 | this.fakeHandler = null;
485 | this.fakeHandlerCallback = null;
486 | }
487 |
488 | if (this.fakeElem) {
489 | this.container.removeChild(this.fakeElem);
490 | this.fakeElem = null;
491 | }
492 | }
493 | }, {
494 | key: 'selectTarget',
495 | value: function selectTarget() {
496 | this.selectedText = (0, _select2.default)(this.target);
497 | this.copyText();
498 | }
499 | }, {
500 | key: 'copyText',
501 | value: function copyText() {
502 | var succeeded = void 0;
503 |
504 | try {
505 | succeeded = document.execCommand(this.action);
506 | } catch (err) {
507 | succeeded = false;
508 | }
509 |
510 | this.handleResult(succeeded);
511 | }
512 | }, {
513 | key: 'handleResult',
514 | value: function handleResult(succeeded) {
515 | this.emitter.emit(succeeded ? 'success' : 'error', {
516 | action: this.action,
517 | text: this.selectedText,
518 | trigger: this.trigger,
519 | clearSelection: this.clearSelection.bind(this)
520 | });
521 | }
522 | }, {
523 | key: 'clearSelection',
524 | value: function clearSelection() {
525 | if (this.trigger) {
526 | this.trigger.focus();
527 | }
528 |
529 | window.getSelection().removeAllRanges();
530 | }
531 | }, {
532 | key: 'destroy',
533 | value: function destroy() {
534 | this.removeFake();
535 | }
536 | }, {
537 | key: 'action',
538 | set: function set() {
539 | var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
540 |
541 | this._action = action;
542 |
543 | if (this._action !== 'copy' && this._action !== 'cut') {
544 | throw new Error('Invalid "action" value, use either "copy" or "cut"');
545 | }
546 | },
547 | get: function get() {
548 | return this._action;
549 | }
550 | }, {
551 | key: 'target',
552 | set: function set(target) {
553 | if (target !== undefined) {
554 | if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
555 | if (this.action === 'copy' && target.hasAttribute('disabled')) {
556 | throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
557 | }
558 |
559 | if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
560 | throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
561 | }
562 |
563 | this._target = target;
564 | } else {
565 | throw new Error('Invalid "target" value, use a valid Element');
566 | }
567 | }
568 | },
569 | get: function get() {
570 | return this._target;
571 | }
572 | }]);
573 |
574 | return ClipboardAction;
575 | }();
576 |
577 | module.exports = ClipboardAction;
578 | });
579 |
580 | },{"select":5}],8:[function(require,module,exports){
581 | (function (global, factory) {
582 | if (typeof define === "function" && define.amd) {
583 | define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory);
584 | } else if (typeof exports !== "undefined") {
585 | factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener'));
586 | } else {
587 | var mod = {
588 | exports: {}
589 | };
590 | factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener);
591 | global.clipboard = mod.exports;
592 | }
593 | })(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) {
594 | 'use strict';
595 |
596 | var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
597 |
598 | var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
599 |
600 | var _goodListener2 = _interopRequireDefault(_goodListener);
601 |
602 | function _interopRequireDefault(obj) {
603 | return obj && obj.__esModule ? obj : {
604 | default: obj
605 | };
606 | }
607 |
608 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
609 | return typeof obj;
610 | } : function (obj) {
611 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
612 | };
613 |
614 | function _classCallCheck(instance, Constructor) {
615 | if (!(instance instanceof Constructor)) {
616 | throw new TypeError("Cannot call a class as a function");
617 | }
618 | }
619 |
620 | var _createClass = function () {
621 | function defineProperties(target, props) {
622 | for (var i = 0; i < props.length; i++) {
623 | var descriptor = props[i];
624 | descriptor.enumerable = descriptor.enumerable || false;
625 | descriptor.configurable = true;
626 | if ("value" in descriptor) descriptor.writable = true;
627 | Object.defineProperty(target, descriptor.key, descriptor);
628 | }
629 | }
630 |
631 | return function (Constructor, protoProps, staticProps) {
632 | if (protoProps) defineProperties(Constructor.prototype, protoProps);
633 | if (staticProps) defineProperties(Constructor, staticProps);
634 | return Constructor;
635 | };
636 | }();
637 |
638 | function _possibleConstructorReturn(self, call) {
639 | if (!self) {
640 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
641 | }
642 |
643 | return call && (typeof call === "object" || typeof call === "function") ? call : self;
644 | }
645 |
646 | function _inherits(subClass, superClass) {
647 | if (typeof superClass !== "function" && superClass !== null) {
648 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
649 | }
650 |
651 | subClass.prototype = Object.create(superClass && superClass.prototype, {
652 | constructor: {
653 | value: subClass,
654 | enumerable: false,
655 | writable: true,
656 | configurable: true
657 | }
658 | });
659 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
660 | }
661 |
662 | var Clipboard = function (_Emitter) {
663 | _inherits(Clipboard, _Emitter);
664 |
665 | /**
666 | * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
667 | * @param {Object} options
668 | */
669 | function Clipboard(trigger, options) {
670 | _classCallCheck(this, Clipboard);
671 |
672 | var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));
673 |
674 | _this.resolveOptions(options);
675 | _this.listenClick(trigger);
676 | return _this;
677 | }
678 |
679 | /**
680 | * Defines if attributes would be resolved using internal setter functions
681 | * or custom functions that were passed in the constructor.
682 | * @param {Object} options
683 | */
684 |
685 |
686 | _createClass(Clipboard, [{
687 | key: 'resolveOptions',
688 | value: function resolveOptions() {
689 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
690 |
691 | this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
692 | this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
693 | this.text = typeof options.text === 'function' ? options.text : this.defaultText;
694 | this.container = _typeof(options.container) === 'object' ? options.container : document.body;
695 | }
696 | }, {
697 | key: 'listenClick',
698 | value: function listenClick(trigger) {
699 | var _this2 = this;
700 |
701 | this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
702 | return _this2.onClick(e);
703 | });
704 | }
705 | }, {
706 | key: 'onClick',
707 | value: function onClick(e) {
708 | var trigger = e.delegateTarget || e.currentTarget;
709 |
710 | if (this.clipboardAction) {
711 | this.clipboardAction = null;
712 | }
713 |
714 | this.clipboardAction = new _clipboardAction2.default({
715 | action: this.action(trigger),
716 | target: this.target(trigger),
717 | text: this.text(trigger),
718 | container: this.container,
719 | trigger: trigger,
720 | emitter: this
721 | });
722 | }
723 | }, {
724 | key: 'defaultAction',
725 | value: function defaultAction(trigger) {
726 | return getAttributeValue('action', trigger);
727 | }
728 | }, {
729 | key: 'defaultTarget',
730 | value: function defaultTarget(trigger) {
731 | var selector = getAttributeValue('target', trigger);
732 |
733 | if (selector) {
734 | return document.querySelector(selector);
735 | }
736 | }
737 | }, {
738 | key: 'defaultText',
739 | value: function defaultText(trigger) {
740 | return getAttributeValue('text', trigger);
741 | }
742 | }, {
743 | key: 'destroy',
744 | value: function destroy() {
745 | this.listener.destroy();
746 |
747 | if (this.clipboardAction) {
748 | this.clipboardAction.destroy();
749 | this.clipboardAction = null;
750 | }
751 | }
752 | }], [{
753 | key: 'isSupported',
754 | value: function isSupported() {
755 | var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
756 |
757 | var actions = typeof action === 'string' ? [action] : action;
758 | var support = !!document.queryCommandSupported;
759 |
760 | actions.forEach(function (action) {
761 | support = support && !!document.queryCommandSupported(action);
762 | });
763 |
764 | return support;
765 | }
766 | }]);
767 |
768 | return Clipboard;
769 | }(_tinyEmitter2.default);
770 |
771 | /**
772 | * Helper function to retrieve attribute value.
773 | * @param {String} suffix
774 | * @param {Element} element
775 | */
776 | function getAttributeValue(suffix, element) {
777 | var attribute = 'data-clipboard-' + suffix;
778 |
779 | if (!element.hasAttribute(attribute)) {
780 | return;
781 | }
782 |
783 | return element.getAttribute(attribute);
784 | }
785 |
786 | module.exports = Clipboard;
787 | });
788 |
789 | },{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)
790 | });
--------------------------------------------------------------------------------
/src/lib/prism.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js default theme for JavaScript, CSS and HTML
3 | * Based on dabblet (http://dabblet.com)
4 | * @author Lea Verou
5 | */
6 |
7 | code[class*="language-"],
8 | pre[class*="language-"] {
9 | color: black;
10 | background: none;
11 | text-shadow: 0 1px white;
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | text-align: left;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 | line-height: 1.5;
19 |
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 |
24 | -webkit-hyphens: none;
25 | -moz-hyphens: none;
26 | -ms-hyphens: none;
27 | hyphens: none;
28 | }
29 |
30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
32 | text-shadow: none;
33 | background: #b3d4fc;
34 | }
35 |
36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
37 | code[class*="language-"]::selection, code[class*="language-"] ::selection {
38 | text-shadow: none;
39 | background: #b3d4fc;
40 | }
41 |
42 | @media print {
43 | code[class*="language-"],
44 | pre[class*="language-"] {
45 | text-shadow: none;
46 | }
47 | }
48 |
49 | /* Code blocks */
50 | pre[class*="language-"] {
51 | padding: 1em;
52 | margin: .5em 0;
53 | overflow: auto;
54 | }
55 |
56 | :not(pre) > code[class*="language-"],
57 | pre[class*="language-"] {
58 | background: #f5f2f0;
59 | }
60 |
61 | /* Inline code */
62 | :not(pre) > code[class*="language-"] {
63 | padding: .1em;
64 | border-radius: .3em;
65 | white-space: normal;
66 | }
67 |
68 | .token.comment,
69 | .token.prolog,
70 | .token.doctype,
71 | .token.cdata {
72 | color: slategray;
73 | }
74 |
75 | .token.punctuation {
76 | color: #999;
77 | }
78 |
79 | .namespace {
80 | opacity: .7;
81 | }
82 |
83 | .token.property,
84 | .token.tag,
85 | .token.boolean,
86 | .token.number,
87 | .token.constant,
88 | .token.symbol,
89 | .token.deleted {
90 | color: #905;
91 | }
92 |
93 | .token.selector,
94 | .token.attr-name,
95 | .token.string,
96 | .token.char,
97 | .token.builtin,
98 | .token.inserted {
99 | color: #690;
100 | }
101 |
102 | .token.operator,
103 | .token.entity,
104 | .token.url,
105 | .language-css .token.string,
106 | .style .token.string {
107 | color: #a67f59;
108 | background: hsla(0, 0%, 100%, .5);
109 | }
110 |
111 | .token.atrule,
112 | .token.attr-value,
113 | .token.keyword {
114 | color: #07a;
115 | }
116 |
117 | .token.function {
118 | color: #DD4A68;
119 | }
120 |
121 | .token.regex,
122 | .token.important,
123 | .token.variable {
124 | color: #e90;
125 | }
126 |
127 | .token.important,
128 | .token.bold {
129 | font-weight: bold;
130 | }
131 | .token.italic {
132 | font-style: italic;
133 | }
134 |
135 | .token.entity {
136 | cursor: help;
137 | }
138 |
--------------------------------------------------------------------------------
/src/lib/prism.js:
--------------------------------------------------------------------------------
1 |
2 | /* **********************************************
3 | Begin prism-core.js
4 | ********************************************** */
5 |
6 | var _self = (typeof window !== 'undefined')
7 | ? window // if in browser
8 | : (
9 | (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
10 | ? self // if in worker
11 | : {} // if in node js
12 | );
13 |
14 | /**
15 | * Prism: Lightweight, robust, elegant syntax highlighting
16 | * MIT license http://www.opensource.org/licenses/mit-license.php/
17 | * @author Lea Verou http://lea.verou.me
18 | */
19 |
20 | var Prism = (function(){
21 |
22 | // Private helper vars
23 | var lang = /\blang(?:uage)?-(\w+)\b/i;
24 | var uniqueId = 0;
25 |
26 | var _ = _self.Prism = {
27 | util: {
28 | encode: function (tokens) {
29 | if (tokens instanceof Token) {
30 | return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
31 | } else if (_.util.type(tokens) === 'Array') {
32 | return tokens.map(_.util.encode);
33 | } else {
34 | return tokens.replace(/&/g, '&').replace(/ text.length) {
305 | // Something went terribly wrong, ABORT, ABORT!
306 | break tokenloop;
307 | }
308 |
309 | if (str instanceof Token) {
310 | continue;
311 | }
312 |
313 | pattern.lastIndex = 0;
314 |
315 | var match = pattern.exec(str),
316 | delNum = 1;
317 |
318 | // Greedy patterns can override/remove up to two previously matched tokens
319 | if (!match && greedy && i != strarr.length - 1) {
320 | pattern.lastIndex = pos;
321 | match = pattern.exec(text);
322 | if (!match) {
323 | break;
324 | }
325 |
326 | var from = match.index + (lookbehind ? match[1].length : 0),
327 | to = match.index + match[0].length,
328 | k = i,
329 | p = pos;
330 |
331 | for (var len = strarr.length; k < len && p < to; ++k) {
332 | p += strarr[k].length;
333 | // Move the index i to the element in strarr that is closest to from
334 | if (from >= p) {
335 | ++i;
336 | pos = p;
337 | }
338 | }
339 |
340 | /*
341 | * If strarr[i] is a Token, then the match starts inside another Token, which is invalid
342 | * If strarr[k - 1] is greedy we are in conflict with another greedy pattern
343 | */
344 | if (strarr[i] instanceof Token || strarr[k - 1].greedy) {
345 | continue;
346 | }
347 |
348 | // Number of tokens to delete and replace with the new match
349 | delNum = k - i;
350 | str = text.slice(pos, p);
351 | match.index -= pos;
352 | }
353 |
354 | if (!match) {
355 | continue;
356 | }
357 |
358 | if(lookbehind) {
359 | lookbehindLength = match[1].length;
360 | }
361 |
362 | var from = match.index + lookbehindLength,
363 | match = match[0].slice(lookbehindLength),
364 | to = from + match.length,
365 | before = str.slice(0, from),
366 | after = str.slice(to);
367 |
368 | var args = [i, delNum];
369 |
370 | if (before) {
371 | args.push(before);
372 | }
373 |
374 | var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
375 |
376 | args.push(wrapped);
377 |
378 | if (after) {
379 | args.push(after);
380 | }
381 |
382 | Array.prototype.splice.apply(strarr, args);
383 | }
384 | }
385 | }
386 |
387 | return strarr;
388 | },
389 |
390 | hooks: {
391 | all: {},
392 |
393 | add: function (name, callback) {
394 | var hooks = _.hooks.all;
395 |
396 | hooks[name] = hooks[name] || [];
397 |
398 | hooks[name].push(callback);
399 | },
400 |
401 | run: function (name, env) {
402 | var callbacks = _.hooks.all[name];
403 |
404 | if (!callbacks || !callbacks.length) {
405 | return;
406 | }
407 |
408 | for (var i=0, callback; callback = callbacks[i++];) {
409 | callback(env);
410 | }
411 | }
412 | }
413 | };
414 |
415 | var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
416 | this.type = type;
417 | this.content = content;
418 | this.alias = alias;
419 | // Copy of the full string this token was created from
420 | this.length = (matchedStr || "").length|0;
421 | this.greedy = !!greedy;
422 | };
423 |
424 | Token.stringify = function(o, language, parent) {
425 | if (typeof o == 'string') {
426 | return o;
427 | }
428 |
429 | if (_.util.type(o) === 'Array') {
430 | return o.map(function(element) {
431 | return Token.stringify(element, language, o);
432 | }).join('');
433 | }
434 |
435 | var env = {
436 | type: o.type,
437 | content: Token.stringify(o.content, language, parent),
438 | tag: 'span',
439 | classes: ['token', o.type],
440 | attributes: {},
441 | language: language,
442 | parent: parent
443 | };
444 |
445 | if (env.type == 'comment') {
446 | env.attributes['spellcheck'] = 'true';
447 | }
448 |
449 | if (o.alias) {
450 | var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
451 | Array.prototype.push.apply(env.classes, aliases);
452 | }
453 |
454 | _.hooks.run('wrap', env);
455 |
456 | var attributes = Object.keys(env.attributes).map(function(name) {
457 | return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"';
458 | }).join(' ');
459 |
460 | return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '' + env.tag + '>';
461 |
462 | };
463 |
464 | if (!_self.document) {
465 | if (!_self.addEventListener) {
466 | // in Node.js
467 | return _self.Prism;
468 | }
469 | // In worker
470 | _self.addEventListener('message', function(evt) {
471 | var message = JSON.parse(evt.data),
472 | lang = message.language,
473 | code = message.code,
474 | immediateClose = message.immediateClose;
475 |
476 | _self.postMessage(_.highlight(code, _.languages[lang], lang));
477 | if (immediateClose) {
478 | _self.close();
479 | }
480 | }, false);
481 |
482 | return _self.Prism;
483 | }
484 |
485 | //Get current script and highlight
486 | var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();
487 |
488 | if (script) {
489 | _.filename = script.src;
490 |
491 | if (document.addEventListener && !script.hasAttribute('data-manual')) {
492 | if(document.readyState !== "loading") {
493 | if (window.requestAnimationFrame) {
494 | window.requestAnimationFrame(_.highlightAll);
495 | } else {
496 | window.setTimeout(_.highlightAll, 16);
497 | }
498 | }
499 | else {
500 | document.addEventListener('DOMContentLoaded', _.highlightAll);
501 | }
502 | }
503 | }
504 |
505 | return _self.Prism;
506 |
507 | })();
508 |
509 | if (typeof module !== 'undefined' && module.exports) {
510 | module.exports = Prism;
511 | }
512 |
513 | // hack for components to work correctly in node.js
514 | if (typeof global !== 'undefined') {
515 | global.Prism = Prism;
516 | }
517 |
518 |
519 | /* **********************************************
520 | Begin prism-markup.js
521 | ********************************************** */
522 |
523 | Prism.languages.markup = {
524 | 'comment': //,
525 | 'prolog': /<\?[\w\W]+?\?>/,
526 | 'doctype': //i,
527 | 'cdata': //i,
528 | 'tag': {
529 | pattern: /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,
530 | inside: {
531 | 'tag': {
532 | pattern: /^<\/?[^\s>\/]+/i,
533 | inside: {
534 | 'punctuation': /^<\/?/,
535 | 'namespace': /^[^\s>\/:]+:/
536 | }
537 | },
538 | 'attr-value': {
539 | pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,
540 | inside: {
541 | 'punctuation': /[=>"']/
542 | }
543 | },
544 | 'punctuation': /\/?>/,
545 | 'attr-name': {
546 | pattern: /[^\s>\/]+/,
547 | inside: {
548 | 'namespace': /^[^\s>\/:]+:/
549 | }
550 | }
551 |
552 | }
553 | },
554 | 'entity': /?[\da-z]{1,8};/i
555 | };
556 |
557 | // Plugin to make entity title show the real entity, idea by Roman Komarov
558 | Prism.hooks.add('wrap', function(env) {
559 |
560 | if (env.type === 'entity') {
561 | env.attributes['title'] = env.content.replace(/&/, '&');
562 | }
563 | });
564 |
565 | Prism.languages.xml = Prism.languages.markup;
566 | Prism.languages.html = Prism.languages.markup;
567 | Prism.languages.mathml = Prism.languages.markup;
568 | Prism.languages.svg = Prism.languages.markup;
569 |
570 |
571 | /* **********************************************
572 | Begin prism-css.js
573 | ********************************************** */
574 |
575 | Prism.languages.css = {
576 | 'comment': /\/\*[\w\W]*?\*\//,
577 | 'atrule': {
578 | pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i,
579 | inside: {
580 | 'rule': /@[\w-]+/
581 | // See rest below
582 | }
583 | },
584 | 'url': /url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
585 | 'selector': /[^\{\}\s][^\{\};]*?(?=\s*\{)/,
586 | 'string': {
587 | pattern: /("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,
588 | greedy: true
589 | },
590 | 'property': /(\b|\B)[\w-]+(?=\s*:)/i,
591 | 'important': /\B!important\b/i,
592 | 'function': /[-a-z0-9]+(?=\()/i,
593 | 'punctuation': /[(){};:]/
594 | };
595 |
596 | Prism.languages.css['atrule'].inside.rest = Prism.util.clone(Prism.languages.css);
597 |
598 | if (Prism.languages.markup) {
599 | Prism.languages.insertBefore('markup', 'tag', {
600 | 'style': {
601 | pattern: /(