diff --git a/.gitignore b/.gitignore index ea5cfc7e8e..d96411817f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ hide-*.js node_modules .idea/ -.DS_Store \ No newline at end of file +.DS_Store +yarn.lock diff --git a/components.js b/components.js index 0a49d3c511..650111759f 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/components/prism-core.js b/components/prism-core.js index bc5d1319c0..5ec69fc73d 100644 --- a/components/prism-core.js +++ b/components/prism-core.js @@ -289,7 +289,7 @@ var _ = _self.Prism = { pattern = pattern.pattern || pattern; // Don’t cache length as it changes during the loop - for (var i=0, pos = 0; i= p) { ++i; @@ -409,7 +409,7 @@ var Token = _.Token = function(type, content, alias, matchedStr, greedy) { this.content = content; this.alias = alias; // Copy of the full string this token was created from - this.matchedStr = matchedStr || null; + this.length = (matchedStr || "").length|0; this.greedy = !!greedy; }; diff --git a/components/prism-core.min.js b/components/prism-core.min.js index d75e9bd9d8..9e56873952 100644 --- a/components/prism-core.min.js +++ b/components/prism-core.min.js @@ -1 +1 @@ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(v instanceof a)){u.lastIndex=0;var b=u.exec(v),k=1;if(!b&&h&&m!=r.length-1){if(u.lastIndex=y,b=u.exec(e),!b)break;for(var w=b.index+(g?b[1].length:0),_=b.index+b[0].length,A=m,S=y,P=r.length;P>A&&_>S;++A)S+=(r[A].matchedStr||r[A]).length,w>=S&&(++m,y=S);if(r[m]instanceof a||r[A-1].greedy)continue;k=A-m,v=e.slice(y,S),b.index-=y}if(b){g&&(f=b[1].length);var w=b.index+f,b=b[0].slice(f),_=w+b.length,x=v.slice(0,w),O=v.slice(_),j=[m,k];x&&j.push(x);var N=new a(l,c?n.tokenize(b,c):b,d,b,h);j.push(N),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=(o?" ":"")+s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(v instanceof a)){u.lastIndex=0;var b=u.exec(v),k=1;if(!b&&h&&m!=r.length-1){if(u.lastIndex=y,b=u.exec(e),!b)break;for(var w=b.index+(c?b[1].length:0),_=b.index+b[0].length,A=m,P=y,x=r.length;x>A&&_>P;++A)P+=r[A].length,w>=P&&(++m,y=P);if(r[m]instanceof a||r[A-1].greedy)continue;k=A-m,v=e.slice(y,P),b.index-=y}if(b){c&&(f=b[1].length);var w=b.index+f,b=b[0].slice(f),_=w+b.length,O=v.slice(0,w),S=v.slice(_),j=[m,k];O&&j.push(O);var N=new a(l,g?n.tokenize(b,g):b,d,b,h);j.push(N),S&&j.push(S),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=(o?" ":"")+s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file diff --git a/components/prism-groovy.js b/components/prism-groovy.js index 7ecef42994..c6f749e330 100644 --- a/components/prism-groovy.js +++ b/components/prism-groovy.js @@ -49,7 +49,7 @@ Prism.hooks.add('wrap', function(env) { } // To prevent double HTML-encoding we have to decode env.content first - env.content = env.content.replace(/&/g, '&').replace(/</g, '<'); + env.content = env.content.replace(/</g, '<').replace(/&/g, '&'); env.content = Prism.highlight(env.content, { 'expression': { diff --git a/components/prism-groovy.min.js b/components/prism-groovy.min.js index ed59332acf..cb07547481 100644 --- a/components/prism-groovy.min.js +++ b/components/prism-groovy.min.js @@ -1 +1 @@ -Prism.languages.groovy=Prism.languages.extend("clike",{keyword:/\b(as|def|in|abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,string:[{pattern:/("""|''')[\W\w]*?\1|(\$\/)(\$\/\$|[\W\w])*?\/\$/,greedy:!0},{pattern:/("|'|\/)(?:\\?.)*?\1/,greedy:!0}],number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.{1,2}(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),Prism.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),Prism.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(setup|given|when|then|and|cleanup|expect|where):/}),Prism.languages.insertBefore("groovy","function",{annotation:{alias:"punctuation",pattern:/(^|[^.])@\w+/,lookbehind:!0}}),Prism.hooks.add("wrap",function(e){if("groovy"===e.language&&"string"===e.type){var t=e.content[0];if("'"!=t){var n=/([^\\])(\$(\{.*?\}|[\w\.]+))/;"$"===t&&(n=/([^\$])(\$(\{.*?\}|[\w\.]+))/),e.content=e.content.replace(/&/g,"&").replace(/</g,"<"),e.content=Prism.highlight(e.content,{expression:{pattern:n,lookbehind:!0,inside:Prism.languages.groovy}}),e.classes.push("/"===t?"regex":"gstring")}}}); \ No newline at end of file +Prism.languages.groovy=Prism.languages.extend("clike",{keyword:/\b(as|def|in|abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,string:[{pattern:/("""|''')[\W\w]*?\1|(\$\/)(\$\/\$|[\W\w])*?\/\$/,greedy:!0},{pattern:/("|'|\/)(?:\\?.)*?\1/,greedy:!0}],number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.{1,2}(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),Prism.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),Prism.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(setup|given|when|then|and|cleanup|expect|where):/}),Prism.languages.insertBefore("groovy","function",{annotation:{alias:"punctuation",pattern:/(^|[^.])@\w+/,lookbehind:!0}}),Prism.hooks.add("wrap",function(e){if("groovy"===e.language&&"string"===e.type){var t=e.content[0];if("'"!=t){var n=/([^\\])(\$(\{.*?\}|[\w\.]+))/;"$"===t&&(n=/([^\$])(\$(\{.*?\}|[\w\.]+))/),e.content=e.content.replace(/</g,"<").replace(/&/g,"&"),e.content=Prism.highlight(e.content,{expression:{pattern:n,lookbehind:!0,inside:Prism.languages.groovy}}),e.classes.push("/"===t?"regex":"gstring")}}}); \ No newline at end of file diff --git a/components/prism-ini.js b/components/prism-ini.js index e6a3852203..6747ad6c2a 100644 --- a/components/prism-ini.js +++ b/components/prism-ini.js @@ -1,6 +1,6 @@ Prism.languages.ini= { 'comment': /^[ \t]*;.*$/m, - 'important': /\[.*?\]/, + 'selector': /^[ \t]*\[.*?\]/m, 'constant': /^[ \t]*[^\s=]+?(?=[ \t]*=)/m, 'attr-value': { pattern: /=.*/, diff --git a/components/prism-ini.min.js b/components/prism-ini.min.js index b78bc95188..bcc3c466ef 100644 --- a/components/prism-ini.min.js +++ b/components/prism-ini.min.js @@ -1 +1 @@ -Prism.languages.ini={comment:/^[ \t]*;.*$/m,important:/\[.*?\]/,constant:/^[ \t]*[^\s=]+?(?=[ \t]*=)/m,"attr-value":{pattern:/=.*/,inside:{punctuation:/^[=]/}}}; \ No newline at end of file +Prism.languages.ini={comment:/^[ \t]*;.*$/m,selector:/^[ \t]*\[.*?\]/m,constant:/^[ \t]*[^\s=]+?(?=[ \t]*=)/m,"attr-value":{pattern:/=.*/,inside:{punctuation:/^[=]/}}}; \ No newline at end of file diff --git a/components/prism-ruby.js b/components/prism-ruby.js index eb3e42939b..dfd0a0fbfe 100644 --- a/components/prism-ruby.js +++ b/components/prism-ruby.js @@ -71,12 +71,14 @@ Prism.languages.ruby.string = [ { pattern: /%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/, + greedy: true, inside: { 'interpolation': interpolation } }, { pattern: /%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/, + greedy: true, inside: { 'interpolation': interpolation } @@ -84,24 +86,28 @@ { // Here we need to specifically allow interpolation pattern: /%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/, + greedy: true, inside: { 'interpolation': interpolation } }, { pattern: /%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/, + greedy: true, inside: { 'interpolation': interpolation } }, { pattern: /%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/, + greedy: true, inside: { 'interpolation': interpolation } }, { pattern: /("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/, + greedy: true, inside: { 'interpolation': interpolation } diff --git a/components/prism-ruby.min.js b/components/prism-ruby.min.js index a87d311cdc..270b779b2d 100644 --- a/components/prism-ruby.min.js +++ b/components/prism-ruby.min.js @@ -1 +1 @@ -!function(e){e.languages.ruby=e.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.util.clone(e.languages.ruby)}};e.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:n}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,inside:{interpolation:n}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,inside:{interpolation:n}}]}(Prism); \ No newline at end of file +!function(e){e.languages.ruby=e.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.util.clone(e.languages.ruby)}};e.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:n}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:n}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,greedy:!0,inside:{interpolation:n}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,greedy:!0,inside:{interpolation:n}}]}(Prism); \ No newline at end of file diff --git a/components/prism-typescript.js b/components/prism-typescript.js index 7f7b58235e..dde12ced8d 100755 --- a/components/prism-typescript.js +++ b/components/prism-typescript.js @@ -1,3 +1,5 @@ Prism.languages.typescript = Prism.languages.extend('javascript', { - 'keyword': /\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield|module|declare|constructor|string|Function|any|number|boolean|Array|enum)\b/ + 'keyword': /\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield|module|declare|constructor|string|Function|any|number|boolean|Array|enum)\b/ }); + +Prism.languages.ts = Prism.languages.typescript; \ No newline at end of file diff --git a/components/prism-typescript.min.js b/components/prism-typescript.min.js index 8b6575ef55..9a0e4cc58f 100755 --- a/components/prism-typescript.min.js +++ b/components/prism-typescript.min.js @@ -1 +1 @@ -Prism.languages.typescript=Prism.languages.extend("javascript",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield|module|declare|constructor|string|Function|any|number|boolean|Array|enum)\b/}); \ No newline at end of file +Prism.languages.typescript=Prism.languages.extend("javascript",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield|module|declare|constructor|string|Function|any|number|boolean|Array|enum)\b/}),Prism.languages.ts=Prism.languages.typescript; \ No newline at end of file diff --git a/package.json b/package.json index 4ae0326080..a51a2a96cc 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 0000000000..abd5e57e4a --- /dev/null +++ b/plugins/copy-to-clipboard/index.html @@ -0,0 +1,48 @@ + + + + + + + Copy to Clipboard ▲ Prism plugins + + + + + + + + + + + +
+
+ +

Copy to Clipboard

+

Add a button that copies the code block to the clipboard when clicked.

+
+ +
+

How to use

+

In addition to including the plugin file with your PrismJS build, ensure Clipboard.js is loaded before the plugin.

+ +

The simplest way to include Clipboard.js is to use any of the + recommended CDNs. If you're using Browserify, Clipboard.js will be loaded auotmatically + if it's included in your package.json. + If you don't load Clipboard.js yourself, the plugin will load it from a CDN for you.

+ +

+
+ + + + + + + + + + + + \ 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 0000000000..373976669b --- /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 0000000000..5153ba68b1 --- /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 bbec50a8bf..95ae3a8cc0 100644 --- a/plugins/show-language/index.html +++ b/plugins/show-language/index.html @@ -8,7 +8,7 @@ - + @@ -25,7 +25,7 @@

Show Language

Examples

- +

JavaScript


 
@@ -43,6 +43,7 @@ 

SVG

+ diff --git a/plugins/show-language/prism-show-language.css b/plugins/show-language/prism-show-language.css deleted file mode 100644 index 3ada3eb98a..0000000000 --- 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 10bdff15e2..4038ffb21b 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 afa963ce48..14c744b77d 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 0000000000..64d8383098 --- /dev/null +++ b/plugins/toolbar/index.html @@ -0,0 +1,134 @@ + + + + + + + Toolbar ▲ Prism plugins + + + + + + + + + + + +
+
+ +

Toolbar

+

Attach a toolbar for plugins to easily register buttons on the top of a code block.

+
+ +
+

How to use

+

The Toolbar plugin allows for several methods to register your button, using the Prism.plugins.toolbar.registerButton function.

+ +

The simplest method is through the HTML API. Add a data-label attribute to the pre element, and the Toolbar + plugin will read the value of that attribute and append a label to the code snippet.

+ +
<pre data-src="plugins/toolbar/prism-toolbar.js" data-label="Hello World!"></pre>
+ +

If you want to provide arbitrary HTML to the label, create a template element with the HTML you want in the label, and provide the + template element's id to data-label. The Toolbar plugin will use the template's content for the button. + You can also use to declare your event handlers inline:

+ +
<pre data-src="plugins/toolbar/prism-toolbar.js" data-label="my-label-button"></pre>
+ +
<template id="my-label-button"><button onclick="console.log('This is an inline-handler');">My button</button></template>
+ +

For more flexibility, the Toolbar exposes a JavaScript function that can be used to register new buttons or labels to the Toolbar, + Prism.plugins.toolbar.registerButton.

+ +

The function accepts a key for the button and an object with a text property string and an optional + onClick function or url string. The onClick function will be called when the button is clicked, while the + url property will be set to the anchor tag's href.

+ +
Prism.plugins.toolbar.registerButton('hello-world', {
+	text: 'Hello World!', // required
+	onClick: function (env) { // optional
+		alert('This code snippet is written in ' + env.language + '.');
+	}
+});
+ +

See how the above code registers the Hello World! button? You can use this in your plugins to register your own buttons with the toolbar.

+ +

If you need more control, you can provide a function to registerButton that returns either a span, a, or + button element.

+ +
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;
+});
+ +

The above function creates the Select Code button you see, and when you click it, the code gets highlighted.

+ +

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 body tag with a comma-separated string indicating the + order.

+ +
<body data-toolbar-order="select-code,hello-world,label">
+
+ +
+ + + + + + + + + + + + \ 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 0000000000..6d1afb9dfe --- /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 0000000000..f4cd28ef08 --- /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
 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 0000000000..c069c1ebec
--- /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
diff --git a/prism.js b/prism.js
index 992a5404a8..5826e67a25 100644
--- a/prism.js
+++ b/prism.js
@@ -294,7 +294,7 @@ var _ = _self.Prism = {
 				pattern = pattern.pattern || pattern;
 
 				// Don’t cache length as it changes during the loop
-				for (var i=0, pos = 0; i= p) {
 								++i;
@@ -414,7 +414,7 @@ var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
 	this.content = content;
 	this.alias = alias;
 	// Copy of the full string this token was created from
-	this.matchedStr = matchedStr || null;
+	this.length = (matchedStr || "").length|0;
 	this.greedy = !!greedy;
 };
 
diff --git a/tests/languages/groovy/issue1049.js b/tests/languages/groovy/issue1049.js
new file mode 100644
index 0000000000..2ce81537a2
--- /dev/null
+++ b/tests/languages/groovy/issue1049.js
@@ -0,0 +1,8 @@
+module.exports = {
+	'"&"': '"&amp;"',
+	'"&&"': '"&amp;&amp;"',
+	'"<"': '"&lt;"',
+	'"<<"': '"&lt;&lt;"',
+	'"&lt;"': '"&amp;lt;"',
+	'">"': '"&gt;"',
+};
diff --git a/tests/languages/ini/important_feature.test b/tests/languages/ini/selector_feature.test
similarity index 70%
rename from tests/languages/ini/important_feature.test
rename to tests/languages/ini/selector_feature.test
index b30ffc3e39..3158a665da 100644
--- a/tests/languages/ini/important_feature.test
+++ b/tests/languages/ini/selector_feature.test
@@ -4,8 +4,8 @@
 ----------------------------------------------------
 
 [
-	["important", "[owner]"],
-	["important", "[foobar]"]
+	["selector", "[owner]"],
+	["selector", "[foobar]"]
 ]
 
 ----------------------------------------------------
diff --git a/tests/languages/ruby/builtin_feature.test b/tests/languages/ruby/builtin_feature.test
index e1bb32e934..a8f9c75313 100644
--- a/tests/languages/ruby/builtin_feature.test
+++ b/tests/languages/ruby/builtin_feature.test
@@ -2,7 +2,7 @@ Array Bignum Binding
 Class;
 Continuation Dir Exception
 FalseClass File Stat File
-Fixnum Fload Hash Integer
+Fixnum Float Hash Integer
 IO MatchData Method Module
 NilClass Numeric Object
 Proc Range Regexp String
@@ -16,7 +16,7 @@ Thread Time TrueClass
 	["builtin", "Class"], ["punctuation", ";"],
 	["builtin", "Continuation"], ["builtin", "Dir"], ["builtin", "Exception"],
 	["builtin", "FalseClass"], ["builtin", "File"], ["builtin", "Stat"], ["builtin", "File"],
-	["builtin", "Fixnum"], ["builtin", "Fload"], ["builtin", "Hash"], ["builtin", "Integer"],
+	["builtin", "Fixnum"], ["builtin", "Float"], ["builtin", "Hash"], ["builtin", "Integer"],
 	["builtin", "IO"], ["builtin", "MatchData"], ["builtin", "Method"], ["builtin", "Module"],
 	["builtin", "NilClass"], ["builtin", "Numeric"], ["builtin", "Object"],
 	["builtin", "Proc"], ["builtin", "Range"], ["builtin", "Regexp"], ["builtin", "String"],
diff --git a/tests/languages/ruby/string_feature.test b/tests/languages/ruby/string_feature.test
index 810a844798..2703abee39 100644
--- a/tests/languages/ruby/string_feature.test
+++ b/tests/languages/ruby/string_feature.test
@@ -7,6 +7,7 @@ bar'
 "foo\
 bar"
 
+"foo #bar"
 "foo #{ 42 } bar"
 
 %!foo #{ 42 }!
@@ -44,6 +45,7 @@ bar"
 	["string", ["\"foo\""]],
 	["string", ["'foo\\\r\nbar'"]],
 	["string", ["\"foo\\\r\nbar\""]],
+	["string", ["\"foo #bar\""]],
 	["string", [
         "\"foo ",
         ["interpolation", [