From be338ed439270df55c4e3a28115b8ab54f989f82 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Fri, 26 Sep 2014 09:49:21 -0400 Subject: [PATCH 01/23] don't allow classes to be passed through on pod html --- lib/MetaCPAN/Web/Controller/Pod.pm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/MetaCPAN/Web/Controller/Pod.pm b/lib/MetaCPAN/Web/Controller/Pod.pm index 26015d3c9dd..9f1fffb160d 100644 --- a/lib/MetaCPAN/Web/Controller/Pod.pm +++ b/lib/MetaCPAN/Web/Controller/Pod.pm @@ -125,18 +125,18 @@ sub view : Private { img => [qw( alt border height width src style title / )], li => ['id'], ol => [], - p => [qw(class style)], - pre => [qw(id class style)], + p => [qw(style)], + pre => [qw(id style)], span => [qw(style)], strong => [], sub => [], sup => [], - table => [qw( style class border cellspacing cellpadding align )], - tbody => [], - td => [qw(style class)], - tr => [qw(style class)], - u => [], - ul => ['id'], + table => [qw( style border cellspacing cellpadding align )], + tbody => [], + td => [qw(style)], + tr => [qw(style)], + u => [], + ul => ['id'], } ); From d4a64fb826598abbfd28dcd1ca84ec0ed2a5c149 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 02:22:39 -0400 Subject: [PATCH 02/23] icons for source view --- root/source.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/root/source.html b/root/source.html index 5323edb3b25..0c82609a236 100644 --- a/root/source.html +++ b/root/source.html @@ -16,36 +16,36 @@
  • - Release Info + Release Info
  • <% IF module.documentation %>
  • - Module Documentation + Module Documentation
  • <% ELSIF module.slop %>
  • - Documentation View + Documentation View
  • <% END %>
  • - Author + Author
  •  
  • -
  • Raw code
  • +
  • Raw code
  • - Download + Download
  • <% IF module.sloc > 0 %> -
  • Toggle pod
  • +
  • Toggle pod
  • <% END %>
  • <% module.sloc %> lines of code
  • From 2365aafcca0c7d80d317e733264e73ae8405e620 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 15 Sep 2014 08:40:03 -0400 Subject: [PATCH 03/23] move syntax highlighter code to separate file --- app.psgi | 1 + root/static/js/cpan.js | 89 ---------------------------- root/static/js/syntaxhighlighter.js | 90 +++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 89 deletions(-) create mode 100644 root/static/js/syntaxhighlighter.js diff --git a/app.psgi b/app.psgi index 5eb85d73118..5847ac19738 100644 --- a/app.psgi +++ b/app.psgi @@ -145,6 +145,7 @@ my @js_files = map {"/static/js/$_.js"} ( bootstrap/bootstrap-tooltip bootstrap/bootstrap-affix bootstrap-slidepanel + syntaxhighlighter ), ); my @css_files diff --git a/root/static/js/cpan.js b/root/static/js/cpan.js index 94c32d1d72e..829d9979ef8 100644 --- a/root/static/js/cpan.js +++ b/root/static/js/cpan.js @@ -83,95 +83,6 @@ function toggleTOC() { $(document).ready(function () { $(".ttip").tooltip(); - SyntaxHighlighter.defaults['quick-code'] = false; - SyntaxHighlighter.defaults['tab-size'] = 8; - - // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, - SyntaxHighlighter.regexLib['url'] = /\w+:\/\/[\w-.\/?%&=:@;#~]*/g; - - /** - * Turns all package names into metacpan.org links within tags. - * @param {String} code Input code. - * @return {String} Returns code with tags. - */ - function processPackages(code) - { - var destination = document.location.href.match(/\/source\//) ? 'source' : 'pod', - strip_delimiters = /((?:q[qw]?)?.)([A-Za-z0-9\:]+)(.*)/ - ; - - code = code.replace(/((?:with|extends|use<\/code> (?:parent|base|aliased))\s*<\/code>\s*)(.+?)(<\/code>)/g, function(m,prefix,pkg,suffix) - { - var match = null, - mcpan_url - ; - - if ( match = strip_delimiters.exec(pkg) ) - { - prefix = prefix + match[1]; - pkg = match[2]; - suffix = match[3] + suffix; - } - - mcpan_url = '' + pkg + ''; - return prefix + mcpan_url + suffix; - }); - - // Link our dependencies - return code.replace(/((use|package|require)<\/code> )([A-Za-z0-9\:]+)(.*?<\/code>)/g, '$1$3$4'); - }; - - var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; - SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml = function(html, lineNumbers) { - html = html.replace(/^ /, " "); - html = getCodeLinesHtml.call(this, html, lineNumbers); - return processPackages(html); - }; - - var getLineNumbersHtml = SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml; - SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml = function() { - var html = getLineNumbersHtml.apply(this, arguments); - html = html.replace(/(]*>\s*)(\d+)(\s*<\/div>)/g, '$1$2$3'); - return html; - }; - - - var source = $("#source"); - // if this is a source-code view with destination anchor - if (source.length && source.html().length > 500000) { - source.removeClass(); - } - else if (source[0] && document.location.hash) { - // check for 'L{number}' anchor in URL and highlight and jump - // to that line. - var lineMatch = document.location.hash.match(/^#L(\d+)$/); - if (lineMatch) { - SyntaxHighlighter.defaults['highlight'] = [lineMatch[1]]; - } - else { - // check for 'P{encoded_package_name}' anchor, convert to - // line number (if possible), and then highlight and jump - // as long as the matching line is not the first line in - // the code. - var packageMatch = document.location.hash.match(/^#P(\S+)$/); - if (packageMatch) { - var decodedPackageMatch = decodeURIComponent(packageMatch[1]); - var leadingSource = source.html().split("package " + decodedPackageMatch + ";"); - var lineCount = leadingSource[0].split("\n").length; - if (leadingSource.length > 1 && lineCount > 1) { - SyntaxHighlighter.defaults['highlight'] = [lineCount]; - document.location.hash = "#L" + lineCount; - } - else { - // reset the anchor portion of the URL (it just looks neater). - document.location.hash = ''; - } - } - } - } - - SyntaxHighlighter.highlight(); - $('#signin-button').mouseenter(function () { $('#signin').show() }); $('#signin').mouseleave(function () { $('#signin').hide() }); diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js new file mode 100644 index 00000000000..5a59d55aef5 --- /dev/null +++ b/root/static/js/syntaxhighlighter.js @@ -0,0 +1,90 @@ +$(function () { + SyntaxHighlighter.defaults['quick-code'] = false; + SyntaxHighlighter.defaults['tab-size'] = 8; + + // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, + SyntaxHighlighter.regexLib['url'] = /\w+:\/\/[\w-.\/?%&=:@;#~]*/g; + + /** + * Turns all package names into metacpan.org links within tags. + * @param {String} code Input code. + * @return {String} Returns code with tags. + */ + function processPackages(code) + { + var destination = document.location.href.match(/\/source\//) ? 'source' : 'pod', + strip_delimiters = /((?:q[qw]?)?.)([A-Za-z0-9\:]+)(.*)/ + ; + + code = code.replace(/((?:with|extends|use<\/code> (?:parent|base|aliased))\s*<\/code>\s*)(.+?)(<\/code>)/g, function(m,prefix,pkg,suffix) + { + var match = null, + mcpan_url + ; + + if ( match = strip_delimiters.exec(pkg) ) + { + prefix = prefix + match[1]; + pkg = match[2]; + suffix = match[3] + suffix; + } + + mcpan_url = '' + pkg + ''; + return prefix + mcpan_url + suffix; + }); + + // Link our dependencies + return code.replace(/((use|package|require)<\/code> )([A-Za-z0-9\:]+)(.*?<\/code>)/g, '$1$3$4'); + }; + + var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; + SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml = function(html, lineNumbers) { + html = html.replace(/^ /, " "); + html = getCodeLinesHtml.call(this, html, lineNumbers); + return processPackages(html); + }; + + var getLineNumbersHtml = SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml; + SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml = function() { + var html = getLineNumbersHtml.apply(this, arguments); + html = html.replace(/(]*>\s*)(\d+)(\s*<\/div>)/g, '$1$2$3'); + return html; + }; + + + var source = $("#source"); + // if this is a source-code view with destination anchor + if (source.length && source.html().length > 500000) { + source.removeClass(); + } + else if (source[0] && document.location.hash) { + // check for 'L{number}' anchor in URL and highlight and jump + // to that line. + var lineMatch = document.location.hash.match(/^#L(\d+)$/); + if (lineMatch) { + SyntaxHighlighter.defaults['highlight'] = [lineMatch[1]]; + } + else { + // check for 'P{encoded_package_name}' anchor, convert to + // line number (if possible), and then highlight and jump + // as long as the matching line is not the first line in + // the code. + var packageMatch = document.location.hash.match(/^#P(\S+)$/); + if (packageMatch) { + var decodedPackageMatch = decodeURIComponent(packageMatch[1]); + var leadingSource = source.html().split("package " + decodedPackageMatch + ";"); + var lineCount = leadingSource[0].split("\n").length; + if (leadingSource.length > 1 && lineCount > 1) { + SyntaxHighlighter.defaults['highlight'] = [lineCount]; + document.location.hash = "#L" + lineCount; + } + else { + // reset the anchor portion of the URL (it just looks neater). + document.location.hash = ''; + } + } + } + } + + SyntaxHighlighter.highlight(); +}); From 06caa48dbebe8542065c635d0c212298f731b83e Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 15 Sep 2014 09:41:30 -0400 Subject: [PATCH 04/23] use long filetype names for syntax brushes --- lib/MetaCPAN/Web/Controller/Source.pm | 8 ++++---- root/pod.html | 2 +- root/static/js/syntaxhighlighter.js | 4 ++-- t/controller/pod.t | 2 +- t/controller/source.t | 21 +++++++++++---------- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/MetaCPAN/Web/Controller/Source.pm b/lib/MetaCPAN/Web/Controller/Source.pm index 31c90cc60e4..dcbe5371a2e 100644 --- a/lib/MetaCPAN/Web/Controller/Source.pm +++ b/lib/MetaCPAN/Web/Controller/Source.pm @@ -79,13 +79,13 @@ sub detect_filetype { local $_ = $file->{path}; # No separate pod brush as of 2011-08-04. - return 'pl' if /\. ( p[ml] | psgi | pod ) $/ix; + return 'perl' if /\. ( p[ml] | psgi | pod ) $/ix; - return 'pl' if /^ cpanfile $/ix; + return 'perl' if /^ cpanfile $/ix; return 'yaml' if /\. ya?ml $/ix; - return 'js' if /\. js(on)? $/ix; + return 'javascript' if /\. js(on)? $/ix; return 'c' if /\. ( c | h | xs ) $/ix; @@ -97,7 +97,7 @@ sub detect_filetype { if ( defined( $file->{mime} ) ) { local $_ = $file->{mime}; - return 'pl' if /perl/; + return 'perl' if /perl/; } # Default to plain text. diff --git a/root/pod.html b/root/pod.html index 5d37713dc16..d14afc1c9ff 100644 --- a/root/pod.html +++ b/root/pod.html @@ -52,7 +52,7 @@
    <% IF pod %> -<% pod.replace(/
    /, '
    ').replace(/<\/code><\/pre>/, '
    ') | none %> +<% pod.replace(/
    /, '
    ').replace(/<\/code><\/pre>/, '
    ') | none %> <% ELSIF pod_error %>

    Error rendering POD for <% module.name %> - <% pod_error %>

    <% ELSE %> diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 5a59d55aef5..dd472d38ade 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -16,7 +16,7 @@ $(function () { strip_delimiters = /((?:q[qw]?)?.)([A-Za-z0-9\:]+)(.*)/ ; - code = code.replace(/((?:with|extends|use<\/code> (?:parent|base|aliased))\s*<\/code>\s*)(.+?)(<\/code>)/g, function(m,prefix,pkg,suffix) + code = code.replace(/((?:with|extends|use<\/code> (?:parent|base|aliased))\s*<\/code>\s*)(.+?)(<\/code>)/g, function(m,prefix,pkg,suffix) { var match = null, mcpan_url @@ -34,7 +34,7 @@ $(function () { }); // Link our dependencies - return code.replace(/((use|package|require)<\/code> )([A-Za-z0-9\:]+)(.*?<\/code>)/g, '$1$3$4'); + return code.replace(/((use|package|require)<\/code> )([A-Za-z0-9\:]+)(.*?<\/code>)/g, '$1$3$4'); }; var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; diff --git a/t/controller/pod.t b/t/controller/pod.t index 1c1d6045d86..bb6b712c9da 100644 --- a/t/controller/pod.t +++ b/t/controller/pod.t @@ -32,7 +32,7 @@ test_psgi app, sub { ); like $tx->find_value('//div[contains(@class, "pod")]//pre/@class'), - qr/^brush: pl; .+; metacpan-verbatim$/, + qr/^brush: perl; .+; metacpan-verbatim$/, 'verbatim pre tag has syn-hi class'; # Request with lowercase author redirects to uppercase author. diff --git a/t/controller/source.t b/t/controller/source.t index 11f03a82dae..b9161abe4d4 100644 --- a/t/controller/source.t +++ b/t/controller/source.t @@ -30,10 +30,11 @@ test_psgi app, sub { ok( my $res = $cb->( GET $uri ), "GET $uri" ); is( $res->code, 200, 'code 200' ); my $tx = tx($res); - ok( - my $source = $tx->find_value( - qq{//div[\@class="content"]/pre[starts-with(\@class, "brush: $type; ")]} + like( + $tx->find_value( + q{//div[@class="content"]/pre/@class} ), + qr/^brush: perl;/, 'has pre-block with expected syntax brush' ); } @@ -44,12 +45,12 @@ test_psgi app, sub { # Test filetype detection. This is based on file attributes so we don't # need to do the API hits to test each type. my @tests = ( - [ pl => 'lib/Template/Manual.pod' ], # pod - [ pl => 'lib/Dist/Zilla.pm' ], - [ pl => 'Makefile.PL' ], + [ perl => 'lib/Template/Manual.pod' ], # pod + [ perl => 'lib/Dist/Zilla.pm' ], + [ perl => 'Makefile.PL' ], - [ js => 'META.json' ], - [ js => 'script.js' ], + [ javascript => 'META.json' ], + [ javascript => 'script.js' ], [ yaml => 'META.yml' ], [ yaml => 'config.yaml' ], @@ -60,11 +61,11 @@ test_psgi app, sub { [ cpanchanges => 'Changes' ], - [ pl => { path => 'bin/dzil', mime => 'text/x-script.perl' } ], + [ perl => { path => 'bin/dzil', mime => 'text/x-script.perl' } ], # There wouldn't normally be a file with no path # but that doesn't mean this shouldn't work. - [ pl => { mime => 'text/x-script.perl' } ], + [ perl => { mime => 'text/x-script.perl' } ], [ plain => 'README' ], ); From 5e88468d5a5bd88014f46660680e943b7ce1b22c Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 04:07:44 -0400 Subject: [PATCH 05/23] improve module regex in syntax highlighter --- root/static/js/syntaxhighlighter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index dd472d38ade..435cb34c4f5 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -34,7 +34,7 @@ $(function () { }); // Link our dependencies - return code.replace(/((use|package|require)<\/code> )([A-Za-z0-9\:]+)(.*?<\/code>)/g, '$1$3$4'); + return code.replace(/((use|package|require)<\/code> )([A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*)(.*?<\/code>)/g, '$1$3$4'); }; var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; From 124d73cfce9336948af225920a07e5086775a8a7 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 15 Sep 2014 08:52:39 -0400 Subject: [PATCH 06/23] initialize syntax highlighter with real classes and data attributes This makes the classes used for highlighting match up with the HTML5 recommendations, and removes the use of the ugly braced config stuffed into the class attribute. It also opens up the option of the pod->html converter providing syntax highlighting configuration in a reasonable format. --- root/diff.html | 2 +- root/pod.html | 2 +- root/source.html | 2 +- root/static/js/syntaxhighlighter.js | 115 +++++++++++++++++++++------- t/controller/pod.t | 4 - t/controller/source.t | 6 +- 6 files changed, 91 insertions(+), 40 deletions(-) diff --git a/root/diff.html b/root/diff.html index 5ca005f717d..2269257e2c4 100644 --- a/root/diff.html +++ b/root/diff.html @@ -73,7 +73,7 @@ -
    <% parts = file.diff.split("\n"); WHILE parts; line = parts.shift; LAST IF line.match( '^\+' ); END; parts.join("\n") %>
    +
    <% parts = file.diff.split("\n"); WHILE parts; line = parts.shift; LAST IF line.match( '^\+' ); END; parts.join("\n") %>
    <% END %> diff --git a/root/pod.html b/root/pod.html index d14afc1c9ff..8e653e69c49 100644 --- a/root/pod.html +++ b/root/pod.html @@ -52,7 +52,7 @@
    <% IF pod %> -<% pod.replace(/
    /, '
    ').replace(/<\/code><\/pre>/, '
    ') | none %> +<% pod | none %> <% ELSIF pod_error %>

    Error rendering POD for <% module.name %> - <% pod_error %>

    <% ELSE %> diff --git a/root/source.html b/root/source.html index 0c82609a236..99c85e3f1a9 100644 --- a/root/source.html +++ b/root/source.html @@ -55,7 +55,7 @@
    <% IF !module.binary %> -
    <% source %>
    +
    <% source %>
    <% ELSE %> This file cannot be displayed inline. Try the raw file. <% END %> diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 435cb34c4f5..fb05f780492 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -1,7 +1,4 @@ $(function () { - SyntaxHighlighter.defaults['quick-code'] = false; - SyntaxHighlighter.defaults['tab-size'] = 8; - // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, SyntaxHighlighter.regexLib['url'] = /\w+:\/\/[\w-.\/?%&=:@;#~]*/g; @@ -53,38 +50,98 @@ $(function () { var source = $("#source"); - // if this is a source-code view with destination anchor - if (source.length && source.html().length > 500000) { - source.removeClass(); + if (source.length) { + var lineMatch; + var packageMatch + if (source.html().length > 500000) { + source.children('code').removeClass(); + } + else if ( lineMatch = document.location.hash.match(/^#L(\d+)$/) ) { + source.attr('data-line', lineMatch[1]); + } + // check for 'P{encoded_package_name}' anchor, convert to + // line number (if possible), and then highlight and jump + // as long as the matching line is not the first line in + // the code. + else if ( packageMatch = document.location.hash.match(/^#P(\S+)$/) ) { + var decodedPackageMatch = decodeURIComponent(packageMatch[1]); + var leadingSource = source.text().split("package " + decodedPackageMatch + ";"); + var lineCount = leadingSource[0].split("\n").length; + if (leadingSource.length > 1 && lineCount > 1) { + source.attr('data-line', lineCount); + document.location.hash = "#L" + lineCount; + } + else { + // reset the anchor portion of the URL (it just looks neater). + document.location.hash = ''; + } + } } - else if (source[0] && document.location.hash) { - // check for 'L{number}' anchor in URL and highlight and jump - // to that line. - var lineMatch = document.location.hash.match(/^#L(\d+)$/); - if (lineMatch) { - SyntaxHighlighter.defaults['highlight'] = [lineMatch[1]]; + + /* set perl as the default type in pod */ + $(".pod pre > code").each(function(index, source) { + var have_lang; + if (source.className) { + var classes = source.className.split(/\s+/); + for (var i = 0; i < classes.length; i++) { + if (classes[i].match(/^language-(.*)/)) { + return; + } + } } - else { - // check for 'P{encoded_package_name}' anchor, convert to - // line number (if possible), and then highlight and jump - // as long as the matching line is not the first line in - // the code. - var packageMatch = document.location.hash.match(/^#P(\S+)$/); - if (packageMatch) { - var decodedPackageMatch = decodeURIComponent(packageMatch[1]); - var leadingSource = source.html().split("package " + decodedPackageMatch + ";"); - var lineCount = leadingSource[0].split("\n").length; - if (leadingSource.length > 1 && lineCount > 1) { - SyntaxHighlighter.defaults['highlight'] = [lineCount]; - document.location.hash = "#L" + lineCount; + source.className = 'language-perl'; + }); + + $(".content pre > code").each(function(index, source) { + var code = $(source); + var pre = code.parent(); + + var config = { + 'gutter' : false, + 'toolbar' : false, + 'quick-code' : false, + 'tab-size' : 8 + }; + if (source.className) { + var classes = source.className.split(/\s+/); + for (var i = 0; i < classes.length; i++) { + var res = classes[i].match(/^language-(.*)/); + if (res) { + config.brush = res[1]; + } + } + } + if (!config.brush) { + return; + } + + if (pre.hasClass('line-numbers')) { + config.gutter = true; + } + var first_line = pre.attr('data-start'); + if (first_line) { + config['first-line'] = first_line; + } + var lines = pre.attr('data-line'); + if (lines) { + lines = lines.split(/,/); + var all_lines = []; + for (var i = 0; i < lines.length; i++) { + var line = lines[i].trim(); + var res = line.match(/(\d+)-(\d+)/); + if (res) { + for (var l = res[1]; l <= res[2]; l++) { + all_lines.push(l); + } } else { - // reset the anchor portion of the URL (it just looks neater). - document.location.hash = ''; + all_lines.push(line); } } + config.highlight = all_lines; } - } - SyntaxHighlighter.highlight(); + $(source).unwrap(); + SyntaxHighlighter.highlight(config, source); + }); }); diff --git a/t/controller/pod.t b/t/controller/pod.t index bb6b712c9da..79e36280a99 100644 --- a/t/controller/pod.t +++ b/t/controller/pod.t @@ -31,10 +31,6 @@ test_psgi app, sub { 'content of both urls is exactly the same' ); - like $tx->find_value('//div[contains(@class, "pod")]//pre/@class'), - qr/^brush: perl; .+; metacpan-verbatim$/, - 'verbatim pre tag has syn-hi class'; - # Request with lowercase author redirects to uppercase author. ( my $lc_this = $this ) =~ s{(/pod/release/)([^/]+)}{$1\L$2}; # lc author name diff --git a/t/controller/source.t b/t/controller/source.t index b9161abe4d4..3b1456c3d92 100644 --- a/t/controller/source.t +++ b/t/controller/source.t @@ -31,10 +31,8 @@ test_psgi app, sub { is( $res->code, 200, 'code 200' ); my $tx = tx($res); like( - $tx->find_value( - q{//div[@class="content"]/pre/@class} - ), - qr/^brush: perl;/, + $tx->find_value(q{//div[@class="content"]/pre/code/@class}), + qr/\blanguage-perl\b/, 'has pre-block with expected syntax brush' ); } From dd8dcc6ea64f870f00fe75bbffc68c331d120ecf Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 15 Sep 2014 09:15:23 -0400 Subject: [PATCH 07/23] use normal pre styles for syntax highlighter Instead of applying styles to syntax highlighter blocks and trying to match verbatim sections to that, just use the normal
     styles.  This
    simplifies the rules needed, and removes the need to extend the syntax
    highlighter's css rules.
    ---
     .../SyntaxHighlighter => css}/shCore.css      |  0
     .../shThemeDefault.css                        |  0
     root/static/js/syntaxhighlighter.js           |  1 -
     root/static/less/pod.less                     |  3 ---
     root/static/less/syntaxhighlighter.less       | 23 +++++--------------
     5 files changed, 6 insertions(+), 21 deletions(-)
     rename root/static/{less/SyntaxHighlighter => css}/shCore.css (100%)
     rename root/static/{less/SyntaxHighlighter => css}/shThemeDefault.css (100%)
    
    diff --git a/root/static/less/SyntaxHighlighter/shCore.css b/root/static/css/shCore.css
    similarity index 100%
    rename from root/static/less/SyntaxHighlighter/shCore.css
    rename to root/static/css/shCore.css
    diff --git a/root/static/less/SyntaxHighlighter/shThemeDefault.css b/root/static/css/shThemeDefault.css
    similarity index 100%
    rename from root/static/less/SyntaxHighlighter/shThemeDefault.css
    rename to root/static/css/shThemeDefault.css
    diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js
    index fb05f780492..6a72b55cfa3 100644
    --- a/root/static/js/syntaxhighlighter.js
    +++ b/root/static/js/syntaxhighlighter.js
    @@ -141,7 +141,6 @@ $(function () {
                 config.highlight = all_lines;
             }
     
    -        $(source).unwrap();
             SyntaxHighlighter.highlight(config, source);
         });
     });
    diff --git a/root/static/less/pod.less b/root/static/less/pod.less
    index 33a185db271..f8fc4601db5 100644
    --- a/root/static/less/pod.less
    +++ b/root/static/less/pod.less
    @@ -133,9 +133,6 @@ ul#index, #index ul {
         }
     }
     
    -.nogutter, .pod pre {
    -    padding-left: 10px;
    -}
     .pod p.pod-error {
         border-left: 1px solid #f32;
         margin-left: -16px;
    diff --git a/root/static/less/syntaxhighlighter.less b/root/static/less/syntaxhighlighter.less
    index 86ccf52228a..d4767c093b1 100644
    --- a/root/static/less/syntaxhighlighter.less
    +++ b/root/static/less/syntaxhighlighter.less
    @@ -1,20 +1,10 @@
    -@import (less) "SyntaxHighlighter/shCore.css";
    -@import (less) "SyntaxHighlighter/shThemeDefault.css";
    -
     body .syntaxhighlighter {
    -  font-size: 90% !important;
    -  *, * *, * * * {
    -    font-family: @font-family-monospace !important;
    -  }
    -}
    -
    -.syntaxhighlighter {
    -    border: 1px solid #e9e9e9;
    -    width: auto !important;
    -    overflow-y: hidden !important;
    -    background-color: #fafafa;
    -    padding: 10px;
    -    -webkit-text-size-adjust: 100%; /* for iPhone , issue #107 */
    +    font-size: 100% !important;
    +    margin: 0 !important;
    +    /* needs higher specificity than the syntax highligher's rules */
    +    &, *, * *, * * *, * * * * {
    +        font-family: inherit !important;
    +    }
     }
     
     /* work around incompatibility between bootstrap and syntaxhighlighter
    @@ -23,4 +13,3 @@ body .syntaxhighlighter {
     .syntaxhighlighter .container:after {
         content: none !important;
     }
    -
    
    From faac8b4be89c6cfdf0a3ad535ae7f7cee4198c26 Mon Sep 17 00:00:00 2001
    From: Graham Knop 
    Date: Tue, 16 Sep 2014 00:57:16 -0400
    Subject: [PATCH 08/23] hide/show pod by class toggle
    
    ---
     root/source.html                        |  5 +++--
     root/static/js/cpan.js                  | 20 --------------------
     root/static/js/syntaxhighlighter.js     | 17 +++++++++++++++++
     root/static/less/global.less            |  1 -
     root/static/less/syntaxhighlighter.less |  4 ++++
     5 files changed, 24 insertions(+), 23 deletions(-)
    
    diff --git a/root/source.html b/root/source.html
    index 99c85e3f1a9..e34b1b4c8b7 100644
    --- a/root/source.html
    +++ b/root/source.html
    @@ -45,7 +45,7 @@
         
    <% IF module.sloc > 0 %> -
  • +
  • <% END %>
  • <% module.sloc %> lines of code
  • @@ -67,7 +67,8 @@ //SyntaxHighlighter.all(); $(document).ready(function() { <% IF module.sloc > 0 %> -togglePod(<% module.pod_lines.json %>); + setPodLines(<% module.pod_lines.json %>); + togglePod(); <% END %> if(document.location.hash) document.location.href = document.location.href; diff --git a/root/static/js/cpan.js b/root/static/js/cpan.js index 829d9979ef8..5861260b7a0 100644 --- a/root/static/js/cpan.js +++ b/root/static/js/cpan.js @@ -28,26 +28,6 @@ $.extend({ } }); -var podVisible = false; - -function togglePod(lines) { - var toggle = podVisible ? 'none' : 'block'; - podVisible = !podVisible; - if (!lines || !lines.length) return; - for (var i = 0; i < lines.length; i++) { - var start = lines[i][0], - length = lines[i][1]; - var sourceC = $('.container')[0].children; - var linesC = $('.gutter')[0].children; - var x; - for (x = start; x < start + length; x++) { - sourceC[x].style.display = toggle; - linesC[x].style.display = toggle; - } - - } -} - function togglePanel(side) { var panel = $('#' + side + '-panel'); var shower = $('#show-' + side + '-panel'); diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 6a72b55cfa3..7f3320629a1 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -144,3 +144,20 @@ $(function () { SyntaxHighlighter.highlight(config, source); }); }); + +function setPodLines (lines) { + if (!lines || !lines.length) return; + for (var i = 0; i < lines.length; i++) { + var start = lines[i][0]; + var length = lines[i][1]; + var selectors = []; + for (var x = start; x < start + length; x++) { + selectors.push('.number' + (x+1)); + } + $('#source .syntaxhighlighter .line').filter(selectors.join(', ')).addClass('pod-line'); + } +} + +function togglePod() { + $('.syntaxhighlighter').toggleClass('pod-hidden'); +} diff --git a/root/static/less/global.less b/root/static/less/global.less index 140966cc916..4c4dc845544 100644 --- a/root/static/less/global.less +++ b/root/static/less/global.less @@ -245,7 +245,6 @@ ul { /* Contributors list on release pages * see /release/Plack for example */ - #contributors { min-height: 40px; diff --git a/root/static/less/syntaxhighlighter.less b/root/static/less/syntaxhighlighter.less index d4767c093b1..1fbce9ffd11 100644 --- a/root/static/less/syntaxhighlighter.less +++ b/root/static/less/syntaxhighlighter.less @@ -13,3 +13,7 @@ body .syntaxhighlighter { .syntaxhighlighter .container:after { content: none !important; } + +.pod-hidden .pod-line { + display: none; +} From 718f00cbb6c9019091feb0b8db434b39b9f97128 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 02:04:11 -0400 Subject: [PATCH 09/23] provide pod lines via data attribute rather than javascript --- root/source.html | 5 +-- root/static/js/syntaxhighlighter.js | 55 ++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/root/source.html b/root/source.html index e34b1b4c8b7..73178856a7d 100644 --- a/root/source.html +++ b/root/source.html @@ -55,7 +55,9 @@
    <% IF !module.binary %> -
    <% source %>
    +
    "><% source %>
    <% ELSE %> This file cannot be displayed inline. Try the raw file. <% END %> @@ -67,7 +69,6 @@ //SyntaxHighlighter.all(); $(document).ready(function() { <% IF module.sloc > 0 %> - setPodLines(<% module.pod_lines.json %>); togglePod(); <% END %> if(document.location.hash) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 7f3320629a1..7d4b7475446 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -1,4 +1,21 @@ $(function () { + function parseLines (lines) { + lines = lines.split(/\s*,\s*/); + var all_lines = []; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var res = line.match(/^\s*(\d+)\s*(-\s*(\d+)\s*)?$/); + if (res) { + var start = res[1]*1; + var end = (res[3] || res[1])*1; + for (var l = start; l <= end; l++) { + all_lines.push(l); + } + } + } + return all_lines; + } + // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, SyntaxHighlighter.regexLib['url'] = /\w+:\/\/[\w-.\/?%&=:@;#~]*/g; @@ -124,39 +141,21 @@ $(function () { } var lines = pre.attr('data-line'); if (lines) { - lines = lines.split(/,/); - var all_lines = []; - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim(); - var res = line.match(/(\d+)-(\d+)/); - if (res) { - for (var l = res[1]; l <= res[2]; l++) { - all_lines.push(l); - } - } - else { - all_lines.push(line); - } - } - config.highlight = all_lines; + config.highlight = parseLines(lines); } SyntaxHighlighter.highlight(config, source); - }); -}); -function setPodLines (lines) { - if (!lines || !lines.length) return; - for (var i = 0; i < lines.length; i++) { - var start = lines[i][0]; - var length = lines[i][1]; - var selectors = []; - for (var x = start; x < start + length; x++) { - selectors.push('.number' + (x+1)); + var pod_lines = pre.attr('data-pod-lines'); + if (pod_lines) { + var selector = $.map( + parseLines(pod_lines), + function (e, i) { return '.number' + e } + ).join(', '); + pre.find('.syntaxhighlighter .line').filter(selector).addClass('pod-line'); } - $('#source .syntaxhighlighter .line').filter(selectors.join(', ')).addClass('pod-line'); - } -} + }); +}); function togglePod() { $('.syntaxhighlighter').toggleClass('pod-hidden'); From 59d0329499ad0b08d58e6f27e874ae89a8c2d6de Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 02:43:40 -0400 Subject: [PATCH 10/23] add line numbers after highlighting rather than monkey patching --- root/static/js/syntaxhighlighter.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 7d4b7475446..0e14830cbed 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -58,13 +58,6 @@ $(function () { return processPackages(html); }; - var getLineNumbersHtml = SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml; - SyntaxHighlighter.Highlighter.prototype.getLineNumbersHtml = function() { - var html = getLineNumbersHtml.apply(this, arguments); - html = html.replace(/(]*>\s*)(\d+)(\s*<\/div>)/g, '$1$2$3'); - return html; - }; - var source = $("#source"); if (source.length) { @@ -154,6 +147,14 @@ $(function () { ).join(', '); pre.find('.syntaxhighlighter .line').filter(selector).addClass('pod-line'); } + + pre.find('.syntaxhighlighter .gutter .line').each(function(i, el) { + var res; + if (res = el.className.match(/(^|\s)number(\d+)(\s|$)/)) { + var id = 'L' + res[2]; + $(el).contents().wrap(''); + } + }); }); }); From 9f78c095ff5c5611017b301219ab5bdda8630164 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 02:50:16 -0400 Subject: [PATCH 11/23] use hide/show labels for pod toggle --- root/source.html | 2 +- root/static/js/syntaxhighlighter.js | 1 + root/static/less/syntaxhighlighter.less | 14 +++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/root/source.html b/root/source.html index 73178856a7d..e4c8218c9fa 100644 --- a/root/source.html +++ b/root/source.html @@ -45,7 +45,7 @@
    <% IF module.sloc > 0 %> -
  • +
  • <% END %>
  • <% module.sloc %> lines of code
  • diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 0e14830cbed..1ac2d8b4f04 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -160,4 +160,5 @@ $(function () { function togglePod() { $('.syntaxhighlighter').toggleClass('pod-hidden'); + $('.pod-toggle').toggleClass('pod-hidden'); } diff --git a/root/static/less/syntaxhighlighter.less b/root/static/less/syntaxhighlighter.less index 1fbce9ffd11..48b981a8d9e 100644 --- a/root/static/less/syntaxhighlighter.less +++ b/root/static/less/syntaxhighlighter.less @@ -14,6 +14,18 @@ body .syntaxhighlighter { content: none !important; } -.pod-hidden .pod-line { +.pod-hidden { + .pod-line { + display: none; + } + .hide-pod { + display: none; + } + .show-pod { + display: inline; + } +} + +.show-pod { display: none; } From d2d1fd5048327c11eb29250d9ad83e5012741881 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 03:11:39 -0400 Subject: [PATCH 12/23] allow highlighting multiple lines --- root/static/js/syntaxhighlighter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 1ac2d8b4f04..f935d7df33f 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -66,7 +66,7 @@ $(function () { if (source.html().length > 500000) { source.children('code').removeClass(); } - else if ( lineMatch = document.location.hash.match(/^#L(\d+)$/) ) { + else if ( lineMatch = document.location.hash.match(/^#L(\d+(-\d+)?(,\d+(-\d+)?)*)$/) ) { source.attr('data-line', lineMatch[1]); } // check for 'P{encoded_package_name}' anchor, convert to From d25f10297d6ee19b6284c1d472e531d8ecb85661 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 03:34:28 -0400 Subject: [PATCH 13/23] only link line numbers on source page --- root/static/js/syntaxhighlighter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index f935d7df33f..1de091458ef 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -147,15 +147,17 @@ $(function () { ).join(', '); pre.find('.syntaxhighlighter .line').filter(selector).addClass('pod-line'); } + }); - pre.find('.syntaxhighlighter .gutter .line').each(function(i, el) { + if (source.length) { + source.find('.syntaxhighlighter .gutter .line').each(function(i, el) { var res; if (res = el.className.match(/(^|\s)number(\d+)(\s|$)/)) { var id = 'L' + res[2]; $(el).contents().wrap(''); } }); - }); + } }); function togglePod() { From f37119d9e59eea21bd5321f53147fe9ae76bbb80 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 03:35:25 -0400 Subject: [PATCH 14/23] scroll to highlighted element for ranges too --- root/source.html | 3 --- root/static/js/syntaxhighlighter.js | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/root/source.html b/root/source.html index e4c8218c9fa..cd35a64e998 100644 --- a/root/source.html +++ b/root/source.html @@ -71,7 +71,4 @@ <% IF module.sloc > 0 %> togglePod(); <% END %> -if(document.location.hash) - document.location.href = document.location.href; -}); diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 1de091458ef..39e82d2913c 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -157,6 +157,12 @@ $(function () { $(el).contents().wrap(''); } }); + + var res; + if (res = document.location.hash.match(/^(#L\d+)(-|,|$)/)) { + var el = $(res[1]); + $('html, body').scrollTop(el.offset().top); + } } }); From 8911ac15ccb4e30d5750a699ed9050461082db8b Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 03:44:09 -0400 Subject: [PATCH 15/23] auto-hide pod via class attribute --- root/source.html | 14 ++------------ root/static/js/syntaxhighlighter.js | 1 - 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/root/source.html b/root/source.html index cd35a64e998..c6b43017e79 100644 --- a/root/source.html +++ b/root/source.html @@ -45,7 +45,7 @@
    <% IF module.sloc > 0 %> -
  • +
  • <% END %>
  • <% module.sloc %> lines of code
  • @@ -55,20 +55,10 @@
    <% IF !module.binary %> -
    " data-pod-lines="<%
       module.pod_lines.map(->(lines){ lines.0+1 _ "-" _ (lines.0+lines.1) }).join(', ')
     %>"><% source %>
    <% ELSE %> This file cannot be displayed inline. Try the raw file. <% END %>
    - - diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 39e82d2913c..cebe41a45d7 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -167,6 +167,5 @@ $(function () { }); function togglePod() { - $('.syntaxhighlighter').toggleClass('pod-hidden'); $('.pod-toggle').toggleClass('pod-hidden'); } From cfbcdfe7680570aa5fd4c2535900cf45f59d8169 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 05:42:40 -0400 Subject: [PATCH 16/23] re-highlight rows when location hash changes --- root/static/js/syntaxhighlighter.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index cebe41a45d7..21d7dca69eb 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -163,6 +163,19 @@ $(function () { var el = $(res[1]); $('html, body').scrollTop(el.offset().top); } + + $(window).on('hashchange', function() { + var lineMatch; + if (lineMatch = document.location.hash.match(/^#L(\d+(-\d+)?(,\d+(-\d+)?)*)$/) ) { + source.attr('data-line', lineMatch[1]); + var selector = $.map( + parseLines(lineMatch[1]), + function (e, i) { return '.number' + e } + ).join(', '); + source.find('.highlighted').removeClass('highlighted'); + source.find('.syntaxhighlighter .line').filter(selector).addClass('highlighted'); + } + }); } }); From b6dc99c86c694301a1f8a8d8434f12f056f80702 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Tue, 16 Sep 2014 05:43:07 -0400 Subject: [PATCH 17/23] don't scroll when clicking on line numbers --- root/static/js/syntaxhighlighter.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 21d7dca69eb..7a57d438bd3 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -151,10 +151,19 @@ $(function () { if (source.length) { source.find('.syntaxhighlighter .gutter .line').each(function(i, el) { + var line = $(el); var res; - if (res = el.className.match(/(^|\s)number(\d+)(\s|$)/)) { - var id = 'L' + res[2]; - $(el).contents().wrap(''); + if (res = line.attr('class').match(/(^|\s)number(\d+)(\s|$)/)) { + var linenr = res[2]; + var id = 'L' + linenr; + line.contents().wrap(''); + var link = line.children('a'); + link.click(function(e) { + e.preventDefault(); + link.removeAttr('id'); + document.location.hash = '#' + id; + link.attr('id', id); + }); } }); From 7ac339e851e0631e263aba06b67286deb8fd96c7 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Wed, 17 Sep 2014 18:38:25 -0400 Subject: [PATCH 18/23] allow syntax highlighter config through HTML filter --- lib/MetaCPAN/Web/Controller/Pod.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/MetaCPAN/Web/Controller/Pod.pm b/lib/MetaCPAN/Web/Controller/Pod.pm index 9f1fffb160d..779c541966e 100644 --- a/lib/MetaCPAN/Web/Controller/Pod.pm +++ b/lib/MetaCPAN/Web/Controller/Pod.pm @@ -109,7 +109,7 @@ sub view : Private { br => [], caption => [], center => [], - code => [], + code => [ { class => qr/^language-\S+$/ } ], dd => ['id'], div => [qw(id style)], dl => ['id'], @@ -126,7 +126,11 @@ sub view : Private { li => ['id'], ol => [], p => [qw(style)], - pre => [qw(id style)], + pre => [qw(id style), { + class => qr/^line-numbers$/, + 'data-line' => qr/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/, + 'data-start' => qr/^\d+$/, + }], span => [qw(style)], strong => [], sub => [], From 34bbf5a0beb03317aab867222cff9b9e0ac500a2 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Sun, 21 Sep 2014 21:24:11 -0400 Subject: [PATCH 19/23] move duplicate regex into variable --- root/static/js/syntaxhighlighter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 7a57d438bd3..e2b66e689a3 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -16,6 +16,8 @@ $(function () { return all_lines; } + var hashLines = /^#L(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*)$/; + // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, SyntaxHighlighter.regexLib['url'] = /\w+:\/\/[\w-.\/?%&=:@;#~]*/g; @@ -66,7 +68,7 @@ $(function () { if (source.html().length > 500000) { source.children('code').removeClass(); } - else if ( lineMatch = document.location.hash.match(/^#L(\d+(-\d+)?(,\d+(-\d+)?)*)$/) ) { + else if ( lineMatch = document.location.hash.match(hashLines) ) { source.attr('data-line', lineMatch[1]); } // check for 'P{encoded_package_name}' anchor, convert to @@ -175,7 +177,7 @@ $(function () { $(window).on('hashchange', function() { var lineMatch; - if (lineMatch = document.location.hash.match(/^#L(\d+(-\d+)?(,\d+(-\d+)?)*)$/) ) { + if (lineMatch = document.location.hash.match(hashLines) ) { source.attr('data-line', lineMatch[1]); var selector = $.map( parseLines(lineMatch[1]), From cfba3bf1a3bf8f5fe119c2d1ad6fe65af6e5f496 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 22 Sep 2014 04:29:46 -0400 Subject: [PATCH 20/23] prevent syntax highlighter from stripping leading tabs as well --- root/static/js/syntaxhighlighter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index e2b66e689a3..82387475257 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -56,6 +56,7 @@ $(function () { var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml = function(html, lineNumbers) { html = html.replace(/^ /, " "); + html = html.replace(/^\t/, " "); html = getCodeLinesHtml.call(this, html, lineNumbers); return processPackages(html); }; From b5cd9424154c1908b720ea2b1af14fc4cb392799 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 22 Sep 2014 04:31:37 -0400 Subject: [PATCH 21/23] simplify and reduce duplication in syntax highlighter code --- root/static/js/syntaxhighlighter.js | 57 ++++++++++++----------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 82387475257..81118bd52af 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -4,10 +4,10 @@ $(function () { var all_lines = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; - var res = line.match(/^\s*(\d+)\s*(-\s*(\d+)\s*)?$/); + var res = line.match(/^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/); if (res) { var start = res[1]*1; - var end = (res[3] || res[1])*1; + var end = (res[2] || res[1])*1; for (var l = start; l <= end; l++) { all_lines.push(l); } @@ -16,6 +16,14 @@ $(function () { return all_lines; } + function findLines (el, lines) { + var selector = $.map( + parseLines(lines), + function (i, line) { return '.number' + line } + ).join(', '); + return el.find('.syntaxhighlighter .line').filter(selector); + } + var hashLines = /^#L(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*)$/; // Allow tilde in url (#1118). Orig: /\w+:\/\/[\w-.\/?%&=:@;#]*/g, @@ -65,7 +73,7 @@ $(function () { var source = $("#source"); if (source.length) { var lineMatch; - var packageMatch + var packageMatch; if (source.html().length > 500000) { source.children('code').removeClass(); } @@ -92,22 +100,16 @@ $(function () { } /* set perl as the default type in pod */ - $(".pod pre > code").each(function(index, source) { + $(".pod pre > code").each(function(index, code) { var have_lang; - if (source.className) { - var classes = source.className.split(/\s+/); - for (var i = 0; i < classes.length; i++) { - if (classes[i].match(/^language-(.*)/)) { - return; - } - } + if (code.className && code.className.match(/(?:\s|^)language-\S+/)) { + return; } - source.className = 'language-perl'; + $(code).addClass('language-perl'); }); - $(".content pre > code").each(function(index, source) { - var code = $(source); - var pre = code.parent(); + $(".content pre > code").each(function(index, code) { + var pre = $(code).parent(); var config = { 'gutter' : false, @@ -115,13 +117,10 @@ $(function () { 'quick-code' : false, 'tab-size' : 8 }; - if (source.className) { - var classes = source.className.split(/\s+/); - for (var i = 0; i < classes.length; i++) { - var res = classes[i].match(/^language-(.*)/); - if (res) { - config.brush = res[1]; - } + if (code.className) { + var res = code.className.match(/(?:\s|^)language-(\S+)/); + if (res) { + config.brush = res[1]; } } if (!config.brush) { @@ -140,15 +139,11 @@ $(function () { config.highlight = parseLines(lines); } - SyntaxHighlighter.highlight(config, source); + SyntaxHighlighter.highlight(config, code); var pod_lines = pre.attr('data-pod-lines'); if (pod_lines) { - var selector = $.map( - parseLines(pod_lines), - function (e, i) { return '.number' + e } - ).join(', '); - pre.find('.syntaxhighlighter .line').filter(selector).addClass('pod-line'); + findLines(pre, pod_lines).addClass('pod-line'); } }); @@ -180,12 +175,8 @@ $(function () { var lineMatch; if (lineMatch = document.location.hash.match(hashLines) ) { source.attr('data-line', lineMatch[1]); - var selector = $.map( - parseLines(lineMatch[1]), - function (e, i) { return '.number' + e } - ).join(', '); source.find('.highlighted').removeClass('highlighted'); - source.find('.syntaxhighlighter .line').filter(selector).addClass('highlighted'); + findLines(source, lineMatch[1]).addClass('highlighted'); } }); } From d3d4d210e0a040287f96377fe4bad1a43273eb86 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Mon, 22 Sep 2014 04:46:35 -0400 Subject: [PATCH 22/23] more comments in syntax highlighter code --- root/static/js/syntaxhighlighter.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/root/static/js/syntaxhighlighter.js b/root/static/js/syntaxhighlighter.js index 81118bd52af..3f83d27eb8b 100644 --- a/root/static/js/syntaxhighlighter.js +++ b/root/static/js/syntaxhighlighter.js @@ -1,4 +1,5 @@ $(function () { + // convert a string like "1,3-5,7" into an array [1,3,4,5,7] function parseLines (lines) { lines = lines.split(/\s*,\s*/); var all_lines = []; @@ -63,6 +64,8 @@ $(function () { var getCodeLinesHtml = SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml; SyntaxHighlighter.Highlighter.prototype.getCodeLinesHtml = function(html, lineNumbers) { + // the syntax highlighter has a bug that strips spaces from the first line. + // replace any leading whitespace with an entity, preventing that. html = html.replace(/^ /, " "); html = html.replace(/^\t/, " "); html = getCodeLinesHtml.call(this, html, lineNumbers); @@ -74,9 +77,12 @@ $(function () { if (source.length) { var lineMatch; var packageMatch; + // avoid highlighting excessively large blocks of code as they will take + // too long, causing browsers to lag and offer to kill the script if (source.html().length > 500000) { source.children('code').removeClass(); } + // save highlighted lines in an attribute, to be used later else if ( lineMatch = document.location.hash.match(hashLines) ) { source.attr('data-line', lineMatch[1]); } @@ -99,7 +105,7 @@ $(function () { } } - /* set perl as the default type in pod */ + // on pod pages, set the language to perl if no other language is set $(".pod pre > code").each(function(index, code) { var have_lang; if (code.className && code.className.match(/(?:\s|^)language-\S+/)) { @@ -130,10 +136,12 @@ $(function () { if (pre.hasClass('line-numbers')) { config.gutter = true; } + // starting line number can be provided by an attribute var first_line = pre.attr('data-start'); if (first_line) { config['first-line'] = first_line; } + // highlighted lines can be provided by an attribute var lines = pre.attr('data-line'); if (lines) { config.highlight = parseLines(lines); @@ -148,6 +156,7 @@ $(function () { }); if (source.length) { + // on the source page, make line numbers into links source.find('.syntaxhighlighter .gutter .line').each(function(i, el) { var line = $(el); var res; @@ -157,6 +166,9 @@ $(function () { line.contents().wrap(''); var link = line.children('a'); link.click(function(e) { + // normally the browser would update the url and scroll to + // the the link. instead, update the hash ourselves, but + // unset the id first so it doesn't scroll e.preventDefault(); link.removeAttr('id'); document.location.hash = '#' + id; @@ -165,12 +177,16 @@ $(function () { } }); + // the line ids are added by javascript, so the browser won't have + // scrolled to it. also, highlight ranges don't correspond to exact + // ids. do the initial scroll ourselves. var res; if (res = document.location.hash.match(/^(#L\d+)(-|,|$)/)) { var el = $(res[1]); $('html, body').scrollTop(el.offset().top); } + // if someone changes the url hash manually, update the highlighted lines $(window).on('hashchange', function() { var lineMatch; if (lineMatch = document.location.hash.match(hashLines) ) { From 6746cd771f02b7894d5da70446e8179c3778ba97 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Fri, 26 Sep 2014 09:56:25 -0400 Subject: [PATCH 23/23] tidy pod html filter config --- lib/MetaCPAN/Web/Controller/Pod.pm | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/MetaCPAN/Web/Controller/Pod.pm b/lib/MetaCPAN/Web/Controller/Pod.pm index 779c541966e..e659e8c7ae6 100644 --- a/lib/MetaCPAN/Web/Controller/Pod.pm +++ b/lib/MetaCPAN/Web/Controller/Pod.pm @@ -126,21 +126,24 @@ sub view : Private { li => ['id'], ol => [], p => [qw(style)], - pre => [qw(id style), { - class => qr/^line-numbers$/, - 'data-line' => qr/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/, - 'data-start' => qr/^\d+$/, - }], - span => [qw(style)], - strong => [], - sub => [], - sup => [], - table => [qw( style border cellspacing cellpadding align )], - tbody => [], - td => [qw(style)], - tr => [qw(style)], - u => [], - ul => ['id'], + pre => [ + qw(id style), + { + class => qr/^line-numbers$/, + 'data-line' => qr/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/, + 'data-start' => qr/^\d+$/, + } + ], + span => [qw(style)], + strong => [], + sub => [], + sup => [], + table => [qw( style border cellspacing cellpadding align )], + tbody => [], + td => [qw(style)], + tr => [qw(style)], + u => [], + ul => ['id'], } );