mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-24 14:27:58 -05:00
Subreddit CSS: Make /about/stylesheet show unminified CSS.
This is for non-moderators to be able to view the commented / unminified CSS so they can learn / cherry-pick bits from it. This uses highlight.js from isagalaev/highlight.js@45c03cd045.
This commit is contained in:
@@ -95,7 +95,7 @@ clean_ini:
|
||||
|
||||
#################### CSS file lists
|
||||
SPRITED_STYLESHEETS += reddit.css compact.css
|
||||
OTHER_STYLESHEETS := reddit-ie6-hax.css reddit-ie7-hax.css mobile.css
|
||||
OTHER_STYLESHEETS := reddit-ie6-hax.css reddit-ie7-hax.css mobile.css highlight.css
|
||||
|
||||
#################### Static Files
|
||||
STATIC_ROOT := r2/public
|
||||
|
||||
@@ -617,6 +617,12 @@ class FrontController(RedditController, OAuth2ResourceController):
|
||||
c.allow_styles = True
|
||||
pane = SubredditStylesheet(site = c.site,
|
||||
stylesheet_contents = stylesheet_contents)
|
||||
elif (location == 'stylesheet'
|
||||
and c.site.can_view(c.user)
|
||||
and not g.css_killswitch):
|
||||
stylesheet = (c.site.stylesheet_contents_user or
|
||||
c.site.stylesheet_contents)
|
||||
pane = SubredditStylesheetSource(stylesheet_contents=stylesheet)
|
||||
elif (location in ('reports', 'spam', 'trials', 'modqueue', 'unmoderated')
|
||||
and is_moderator):
|
||||
c.allow_styles = True
|
||||
|
||||
@@ -325,6 +325,11 @@ module["qrcode"] = Module("qrcode.js",
|
||||
"qrcode.js",
|
||||
)
|
||||
|
||||
module["highlight"] = Module("highlight.js",
|
||||
"lib/highlight.pack.js",
|
||||
"highlight.js",
|
||||
)
|
||||
|
||||
def use(*names):
|
||||
return "\n".join(module[name].use() for name in names)
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ menu = MenuHandler(hot = _('hot'),
|
||||
details = _("details"),
|
||||
duplicates = _("other discussions (%(num)s)"),
|
||||
traffic = _("traffic stats"),
|
||||
stylesheet = _("stylesheet"),
|
||||
|
||||
# reddits
|
||||
home = _("home"),
|
||||
|
||||
@@ -1606,6 +1606,11 @@ class SubredditStylesheet(Templated):
|
||||
Templated.__init__(self, site = site,
|
||||
stylesheet_contents = stylesheet_contents)
|
||||
|
||||
class SubredditStylesheetSource(Templated):
|
||||
"""A view of the unminified source of a subreddit's stylesheet."""
|
||||
def __init__(self, stylesheet_contents):
|
||||
Templated.__init__(self, stylesheet_contents=stylesheet_contents)
|
||||
|
||||
class CssError(Templated):
|
||||
"""Rendered error returned to the stylesheet editing page via ajax"""
|
||||
def __init__(self, error):
|
||||
|
||||
238
r2/r2/public/static/css/highlight.css
Normal file
238
r2/r2/public/static/css/highlight.css
Normal file
@@ -0,0 +1,238 @@
|
||||
/* subreddit stylesheet source viewer */
|
||||
.subreddit-stylesheet-source {
|
||||
padding: 0.5em;
|
||||
overflow-x: auto;
|
||||
margin: 10px 7px;
|
||||
font-size: medium;
|
||||
font-family: "Bitstream Vera Sans Mono", Consolas, monospace;
|
||||
}
|
||||
|
||||
/* github.com style (c) Vasily Polovnyov <vast@whiteants.net> - from the highlight.js source */
|
||||
pre {
|
||||
color: #333;
|
||||
background-color: #f8f8ff;
|
||||
}
|
||||
|
||||
pre .comment,
|
||||
pre .template_comment,
|
||||
pre .diff .header,
|
||||
pre .javadoc {
|
||||
color: #998;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
pre .keyword,
|
||||
pre .css .rule .keyword,
|
||||
pre .winutils,
|
||||
pre .javascript .title,
|
||||
pre .nginx .title,
|
||||
pre .subst,
|
||||
pre .request,
|
||||
pre .status {
|
||||
color: #333;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .number,
|
||||
pre .hexcolor,
|
||||
pre .ruby .constant {
|
||||
color: #099;
|
||||
}
|
||||
|
||||
pre .string,
|
||||
pre .tag .value,
|
||||
pre .phpdoc,
|
||||
pre .tex .formula {
|
||||
color: #d14
|
||||
}
|
||||
|
||||
pre .title,
|
||||
pre .id {
|
||||
color: #900;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .javascript .title,
|
||||
pre .lisp .title,
|
||||
pre .clojure .title,
|
||||
pre .subst {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .class .title,
|
||||
pre .haskell .type,
|
||||
pre .vhdl .literal,
|
||||
pre .tex .command {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .tag,
|
||||
pre .tag .title,
|
||||
pre .rules .property,
|
||||
pre .django .tag .keyword {
|
||||
color: #000080;
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .attribute,
|
||||
pre .variable,
|
||||
pre .lisp .body {
|
||||
color: #008080
|
||||
}
|
||||
|
||||
pre .regexp {
|
||||
color: #009926
|
||||
}
|
||||
|
||||
pre .class {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .symbol,
|
||||
pre .ruby .symbol .string,
|
||||
pre .lisp .keyword,
|
||||
pre .tex .special,
|
||||
pre .input_number {
|
||||
color: #990073
|
||||
}
|
||||
|
||||
pre .built_in,
|
||||
pre .lisp .title,
|
||||
pre .clojure .built_in {
|
||||
color: #0086b3
|
||||
}
|
||||
|
||||
pre .preprocessor,
|
||||
pre .pi,
|
||||
pre .doctype,
|
||||
pre .shebang,
|
||||
pre .cdata {
|
||||
color: #999;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .deletion {
|
||||
background: #fdd
|
||||
}
|
||||
|
||||
pre .addition {
|
||||
background: #dfd
|
||||
}
|
||||
|
||||
pre .diff .change {
|
||||
background: #0086b3
|
||||
}
|
||||
|
||||
pre .chunk {
|
||||
color: #aaa
|
||||
}
|
||||
|
||||
/* Monokai style - ported by Luigi Maselli - http://grigio.org - from the highlight.js source */
|
||||
.res-nightmode pre {
|
||||
background: #272822;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.res-nightmode pre .tag,
|
||||
.res-nightmode pre .tag .title,
|
||||
.res-nightmode pre .keyword,
|
||||
.res-nightmode pre .literal,
|
||||
.res-nightmode pre .change,
|
||||
.res-nightmode pre .winutils,
|
||||
.res-nightmode pre .flow,
|
||||
.res-nightmode pre .lisp .title,
|
||||
.res-nightmode pre .clojure .built_in,
|
||||
.res-nightmode pre .nginx .title,
|
||||
.res-nightmode pre .tex .special {
|
||||
color: #F92672;
|
||||
}
|
||||
|
||||
.res-nightmode pre code .constant {
|
||||
color: #66D9EF;
|
||||
}
|
||||
|
||||
.res-nightmode pre .class .title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.res-nightmode pre .attribute,
|
||||
.res-nightmode pre .symbol,
|
||||
.res-nightmode pre .symbol .string,
|
||||
.res-nightmode pre .value,
|
||||
.res-nightmode pre .regexp {
|
||||
color: #BF79DB;
|
||||
}
|
||||
|
||||
.res-nightmode pre .tag .value,
|
||||
.res-nightmode pre .string,
|
||||
.res-nightmode pre .subst,
|
||||
.res-nightmode pre .title,
|
||||
.res-nightmode pre .haskell .type,
|
||||
.res-nightmode pre ..res-nightmode preprocessor,
|
||||
.res-nightmode pre .ruby .class .parent,
|
||||
.res-nightmode pre .built_in,
|
||||
.res-nightmode pre .sql .aggregate,
|
||||
.res-nightmode pre .django .template_tag,
|
||||
.res-nightmode pre .django .variable,
|
||||
.res-nightmode pre .smalltalk .class,
|
||||
.res-nightmode pre .javadoc,
|
||||
.res-nightmode pre .django .filter .argument,
|
||||
.res-nightmode pre .smalltalk .localvars,
|
||||
.res-nightmode pre .smalltalk .array,
|
||||
.res-nightmode pre .attr_selector,
|
||||
.res-nightmode pre .pseudo,
|
||||
.res-nightmode pre .addition,
|
||||
.res-nightmode pre .stream,
|
||||
.res-nightmode pre .envvar,
|
||||
.res-nightmode pre .apache .tag,
|
||||
.res-nightmode pre .apache .cbracket,
|
||||
.res-nightmode pre .tex .command,
|
||||
.res-nightmode pre .input_number {
|
||||
color: #A6E22E;
|
||||
}
|
||||
|
||||
.res-nightmode pre .comment,
|
||||
.res-nightmode pre .java .annotation,
|
||||
.res-nightmode pre .python .decorator,
|
||||
.res-nightmode pre .template_comment,
|
||||
.res-nightmode pre .pi,
|
||||
.res-nightmode pre .doctype,
|
||||
.res-nightmode pre .deletion,
|
||||
.res-nightmode pre .shebang,
|
||||
.res-nightmode pre .apache .sqbracket,
|
||||
.res-nightmode pre .tex .formula {
|
||||
color: #75715E;
|
||||
}
|
||||
|
||||
.res-nightmode pre .keyword,
|
||||
.res-nightmode pre .literal,
|
||||
.res-nightmode pre .css .id,
|
||||
.res-nightmode pre .phpdoc,
|
||||
.res-nightmode pre .title,
|
||||
.res-nightmode pre .haskell .type,
|
||||
.res-nightmode pre .vbscript .built_in,
|
||||
.res-nightmode pre .sql .aggregate,
|
||||
.res-nightmode pre .rsl .built_in,
|
||||
.res-nightmode pre .smalltalk .class,
|
||||
.res-nightmode pre .diff .header,
|
||||
.res-nightmode pre .chunk,
|
||||
.res-nightmode pre .winutils,
|
||||
.res-nightmode pre .bash .variable,
|
||||
.res-nightmode pre .apache .tag,
|
||||
.res-nightmode pre .tex .special,
|
||||
.res-nightmode pre .request,
|
||||
.res-nightmode pre .status {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.res-nightmode pre .coffeescript .javascript,
|
||||
.res-nightmode pre .javascript .xml,
|
||||
.res-nightmode pre .tex .formula,
|
||||
.res-nightmode pre .xml .javascript,
|
||||
.res-nightmode pre .xml .vbscript,
|
||||
.res-nightmode pre .xml .css,
|
||||
.res-nightmode pre .xml .cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
1
r2/r2/public/static/js/highlight.js
Normal file
1
r2/r2/public/static/js/highlight.js
Normal file
@@ -0,0 +1 @@
|
||||
hljs.initHighlightingOnLoad()
|
||||
701
r2/r2/public/static/js/lib/highlight.pack.js
Normal file
701
r2/r2/public/static/js/lib/highlight.pack.js
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
Copyright (c) 2006, Ivan Sagalaev
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of highlight.js nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
var hljs = new function() {
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
function escape(value) {
|
||||
return value.replace(/&/gm, '&').replace(/</gm, '<');
|
||||
}
|
||||
|
||||
function findCode(pre) {
|
||||
for (var node = pre.firstChild; node; node = node.nextSibling) {
|
||||
if (node.nodeName == 'CODE')
|
||||
return node;
|
||||
if (!(node.nodeType == 3 && node.nodeValue.match(/\s+/)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function blockText(block, ignoreNewLines) {
|
||||
return Array.prototype.map.call(block.childNodes, function(node) {
|
||||
if (node.nodeType == 3) {
|
||||
return ignoreNewLines ? node.nodeValue.replace(/\n/g, '') : node.nodeValue;
|
||||
}
|
||||
if (node.nodeName == 'BR') {
|
||||
return '\n';
|
||||
}
|
||||
return blockText(node, ignoreNewLines);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function blockLanguage(block) {
|
||||
var classes = (block.className + ' ' + block.parentNode.className).split(/\s+/);
|
||||
classes = classes.map(function(c) {return c.replace(/^language-/, '')});
|
||||
for (var i = 0; i < classes.length; i++) {
|
||||
if (languages[classes[i]] || classes[i] == 'no-highlight') {
|
||||
return classes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Stream merging */
|
||||
|
||||
function nodeStream(node) {
|
||||
var result = [];
|
||||
(function _nodeStream(node, offset) {
|
||||
for (var child = node.firstChild; child; child = child.nextSibling) {
|
||||
if (child.nodeType == 3)
|
||||
offset += child.nodeValue.length;
|
||||
else if (child.nodeName == 'BR')
|
||||
offset += 1;
|
||||
else if (child.nodeType == 1) {
|
||||
result.push({
|
||||
event: 'start',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
offset = _nodeStream(child, offset);
|
||||
result.push({
|
||||
event: 'stop',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
})(node, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
function mergeStreams(stream1, stream2, value) {
|
||||
var processed = 0;
|
||||
var result = '';
|
||||
var nodeStack = [];
|
||||
|
||||
function selectStream() {
|
||||
if (stream1.length && stream2.length) {
|
||||
if (stream1[0].offset != stream2[0].offset)
|
||||
return (stream1[0].offset < stream2[0].offset) ? stream1 : stream2;
|
||||
else {
|
||||
/*
|
||||
To avoid starting the stream just before it should stop the order is
|
||||
ensured that stream1 always starts first and closes last:
|
||||
|
||||
if (event1 == 'start' && event2 == 'start')
|
||||
return stream1;
|
||||
if (event1 == 'start' && event2 == 'stop')
|
||||
return stream2;
|
||||
if (event1 == 'stop' && event2 == 'start')
|
||||
return stream1;
|
||||
if (event1 == 'stop' && event2 == 'stop')
|
||||
return stream2;
|
||||
|
||||
... which is collapsed to:
|
||||
*/
|
||||
return stream2[0].event == 'start' ? stream1 : stream2;
|
||||
}
|
||||
} else {
|
||||
return stream1.length ? stream1 : stream2;
|
||||
}
|
||||
}
|
||||
|
||||
function open(node) {
|
||||
function attr_str(a) {return ' ' + a.nodeName + '="' + escape(a.value) + '"'};
|
||||
return '<' + node.nodeName + Array.prototype.map.call(node.attributes, attr_str).join('') + '>';
|
||||
}
|
||||
|
||||
while (stream1.length || stream2.length) {
|
||||
var current = selectStream().splice(0, 1)[0];
|
||||
result += escape(value.substr(processed, current.offset - processed));
|
||||
processed = current.offset;
|
||||
if ( current.event == 'start') {
|
||||
result += open(current.node);
|
||||
nodeStack.push(current.node);
|
||||
} else if (current.event == 'stop') {
|
||||
var node, i = nodeStack.length;
|
||||
do {
|
||||
i--;
|
||||
node = nodeStack[i];
|
||||
result += ('</' + node.nodeName.toLowerCase() + '>');
|
||||
} while (node != current.node);
|
||||
nodeStack.splice(i, 1);
|
||||
while (i < nodeStack.length) {
|
||||
result += open(nodeStack[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result + escape(value.substr(processed));
|
||||
}
|
||||
|
||||
/* Initialization */
|
||||
|
||||
function compileLanguage(language) {
|
||||
|
||||
function langRe(value, global) {
|
||||
return RegExp(
|
||||
value,
|
||||
'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')
|
||||
);
|
||||
}
|
||||
|
||||
function compileMode(mode, parent) {
|
||||
if (mode.compiled)
|
||||
return;
|
||||
mode.compiled = true;
|
||||
|
||||
var keywords = []; // used later with beginWithKeyword but filled as a side-effect of keywords compilation
|
||||
if (mode.keywords) {
|
||||
var compiled_keywords = {};
|
||||
|
||||
function flatten(className, str) {
|
||||
str.split(' ').forEach(function(kw) {
|
||||
var pair = kw.split('|');
|
||||
compiled_keywords[pair[0]] = [className, pair[1] ? Number(pair[1]) : 1];
|
||||
keywords.push(pair[0]);
|
||||
});
|
||||
}
|
||||
|
||||
mode.lexemsRe = langRe(mode.lexems || hljs.IDENT_RE, true);
|
||||
if (typeof mode.keywords == 'string') { // string
|
||||
flatten('keyword', mode.keywords)
|
||||
} else {
|
||||
for (var className in mode.keywords) {
|
||||
if (!mode.keywords.hasOwnProperty(className))
|
||||
continue;
|
||||
flatten(className, mode.keywords[className]);
|
||||
}
|
||||
}
|
||||
mode.keywords = compiled_keywords;
|
||||
}
|
||||
if (parent) {
|
||||
if (mode.beginWithKeyword) {
|
||||
mode.begin = '\\b(' + keywords.join('|') + ')\\s';
|
||||
}
|
||||
mode.beginRe = langRe(mode.begin ? mode.begin : '\\B|\\b');
|
||||
if (!mode.end && !mode.endsWithParent)
|
||||
mode.end = '\\B|\\b';
|
||||
if (mode.end)
|
||||
mode.endRe = langRe(mode.end);
|
||||
mode.terminator_end = mode.end || '';
|
||||
if (mode.endsWithParent && parent.terminator_end)
|
||||
mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
|
||||
}
|
||||
if (mode.illegal)
|
||||
mode.illegalRe = langRe(mode.illegal);
|
||||
if (mode.relevance === undefined)
|
||||
mode.relevance = 1;
|
||||
if (!mode.contains) {
|
||||
mode.contains = [];
|
||||
}
|
||||
for (var i = 0; i < mode.contains.length; i++) {
|
||||
if (mode.contains[i] == 'self') {
|
||||
mode.contains[i] = mode;
|
||||
}
|
||||
compileMode(mode.contains[i], mode);
|
||||
}
|
||||
if (mode.starts) {
|
||||
compileMode(mode.starts, parent);
|
||||
}
|
||||
|
||||
var terminators = [];
|
||||
for (var i = 0; i < mode.contains.length; i++) {
|
||||
terminators.push(mode.contains[i].begin);
|
||||
}
|
||||
if (mode.terminator_end) {
|
||||
terminators.push(mode.terminator_end);
|
||||
}
|
||||
if (mode.illegal) {
|
||||
terminators.push(mode.illegal);
|
||||
}
|
||||
mode.terminators = terminators.length ? langRe(terminators.join('|'), true) : {exec: function(s) {return null;}};
|
||||
}
|
||||
|
||||
compileMode(language);
|
||||
}
|
||||
|
||||
/*
|
||||
Core highlighting function. Accepts a language name and a string with the
|
||||
code to highlight. Returns an object with the following properties:
|
||||
|
||||
- relevance (int)
|
||||
- keyword_count (int)
|
||||
- value (an HTML string with highlighting markup)
|
||||
|
||||
*/
|
||||
function highlight(language_name, value) {
|
||||
|
||||
function subMode(lexem, mode) {
|
||||
for (var i = 0; i < mode.contains.length; i++) {
|
||||
var match = mode.contains[i].beginRe.exec(lexem);
|
||||
if (match && match.index == 0) {
|
||||
return mode.contains[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function endOfMode(mode, lexem) {
|
||||
if (mode.end && mode.endRe.test(lexem)) {
|
||||
return mode;
|
||||
}
|
||||
if (mode.endsWithParent) {
|
||||
return endOfMode(mode.parent, lexem);
|
||||
}
|
||||
}
|
||||
|
||||
function isIllegal(lexem, mode) {
|
||||
return mode.illegal && mode.illegalRe.test(lexem);
|
||||
}
|
||||
|
||||
function keywordMatch(mode, match) {
|
||||
var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
|
||||
return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str];
|
||||
}
|
||||
|
||||
function processKeywords() {
|
||||
var buffer = escape(mode_buffer);
|
||||
if (!top.keywords)
|
||||
return buffer;
|
||||
var result = '';
|
||||
var last_index = 0;
|
||||
top.lexemsRe.lastIndex = 0;
|
||||
var match = top.lexemsRe.exec(buffer);
|
||||
while (match) {
|
||||
result += buffer.substr(last_index, match.index - last_index);
|
||||
var keyword_match = keywordMatch(top, match);
|
||||
if (keyword_match) {
|
||||
keyword_count += keyword_match[1];
|
||||
result += '<span class="'+ keyword_match[0] +'">' + match[0] + '</span>';
|
||||
} else {
|
||||
result += match[0];
|
||||
}
|
||||
last_index = top.lexemsRe.lastIndex;
|
||||
match = top.lexemsRe.exec(buffer);
|
||||
}
|
||||
return result + buffer.substr(last_index);
|
||||
}
|
||||
|
||||
function processSubLanguage() {
|
||||
if (top.subLanguage && !languages[top.subLanguage]) {
|
||||
return escape(mode_buffer);
|
||||
}
|
||||
var result = top.subLanguage ? highlight(top.subLanguage, mode_buffer) : highlightAuto(mode_buffer);
|
||||
// Counting embedded language score towards the host language may be disabled
|
||||
// with zeroing the containing mode relevance. Usecase in point is Markdown that
|
||||
// allows XML everywhere and makes every XML snippet to have a much larger Markdown
|
||||
// score.
|
||||
if (top.relevance > 0) {
|
||||
keyword_count += result.keyword_count;
|
||||
relevance += result.relevance;
|
||||
}
|
||||
return '<span class="' + result.language + '">' + result.value + '</span>';
|
||||
}
|
||||
|
||||
function processBuffer() {
|
||||
return top.subLanguage !== undefined ? processSubLanguage() : processKeywords();
|
||||
}
|
||||
|
||||
function startNewMode(mode, lexem) {
|
||||
var markup = mode.className? '<span class="' + mode.className + '">': '';
|
||||
if (mode.returnBegin) {
|
||||
result += markup;
|
||||
mode_buffer = '';
|
||||
} else if (mode.excludeBegin) {
|
||||
result += escape(lexem) + markup;
|
||||
mode_buffer = '';
|
||||
} else {
|
||||
result += markup;
|
||||
mode_buffer = lexem;
|
||||
}
|
||||
top = Object.create(mode, {parent: {value: top}});
|
||||
relevance += mode.relevance;
|
||||
}
|
||||
|
||||
function processModeInfo(buffer, lexem) {
|
||||
mode_buffer += buffer;
|
||||
if (lexem === undefined) {
|
||||
result += processBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
var new_mode = subMode(lexem, top);
|
||||
if (new_mode) {
|
||||
result += processBuffer();
|
||||
startNewMode(new_mode, lexem);
|
||||
return new_mode.returnBegin;
|
||||
}
|
||||
|
||||
var end_mode = endOfMode(top, lexem);
|
||||
if (end_mode) {
|
||||
if (!(end_mode.returnEnd || end_mode.excludeEnd)) {
|
||||
mode_buffer += lexem;
|
||||
}
|
||||
result += processBuffer();
|
||||
do {
|
||||
if (top.className) {
|
||||
result += '</span>';
|
||||
}
|
||||
top = top.parent;
|
||||
} while (top != end_mode.parent);
|
||||
if (end_mode.excludeEnd) {
|
||||
result += escape(lexem);
|
||||
}
|
||||
mode_buffer = '';
|
||||
if (end_mode.starts) {
|
||||
startNewMode(end_mode.starts, '');
|
||||
}
|
||||
return end_mode.returnEnd;
|
||||
}
|
||||
|
||||
if (isIllegal(lexem, top))
|
||||
throw 'Illegal';
|
||||
}
|
||||
|
||||
var language = languages[language_name];
|
||||
compileLanguage(language);
|
||||
var top = language;
|
||||
var mode_buffer = '';
|
||||
var relevance = 0;
|
||||
var keyword_count = 0;
|
||||
var result = '';
|
||||
try {
|
||||
var match, index = 0;
|
||||
while (true) {
|
||||
top.terminators.lastIndex = index;
|
||||
match = top.terminators.exec(value);
|
||||
if (!match)
|
||||
break;
|
||||
var return_lexem = processModeInfo(value.substr(index, match.index - index), match[0]);
|
||||
index = match.index + (return_lexem ? 0 : match[0].length);
|
||||
}
|
||||
processModeInfo(value.substr(index), undefined);
|
||||
return {
|
||||
relevance: relevance,
|
||||
keyword_count: keyword_count,
|
||||
value: result,
|
||||
language: language_name
|
||||
};
|
||||
} catch (e) {
|
||||
if (e == 'Illegal') {
|
||||
return {
|
||||
relevance: 0,
|
||||
keyword_count: 0,
|
||||
value: escape(value)
|
||||
};
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Highlighting with language detection. Accepts a string with the code to
|
||||
highlight. Returns an object with the following properties:
|
||||
|
||||
- language (detected language)
|
||||
- relevance (int)
|
||||
- keyword_count (int)
|
||||
- value (an HTML string with highlighting markup)
|
||||
- second_best (object with the same structure for second-best heuristically
|
||||
detected language, may be absent)
|
||||
|
||||
*/
|
||||
function highlightAuto(text) {
|
||||
var result = {
|
||||
keyword_count: 0,
|
||||
relevance: 0,
|
||||
value: escape(text)
|
||||
};
|
||||
var second_best = result;
|
||||
for (var key in languages) {
|
||||
if (!languages.hasOwnProperty(key))
|
||||
continue;
|
||||
var current = highlight(key, text);
|
||||
current.language = key;
|
||||
if (current.keyword_count + current.relevance > second_best.keyword_count + second_best.relevance) {
|
||||
second_best = current;
|
||||
}
|
||||
if (current.keyword_count + current.relevance > result.keyword_count + result.relevance) {
|
||||
second_best = result;
|
||||
result = current;
|
||||
}
|
||||
}
|
||||
if (second_best.language) {
|
||||
result.second_best = second_best;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Post-processing of the highlighted markup:
|
||||
|
||||
- replace TABs with something more useful
|
||||
- replace real line-breaks with '<br>' for non-pre containers
|
||||
|
||||
*/
|
||||
function fixMarkup(value, tabReplace, useBR) {
|
||||
if (tabReplace) {
|
||||
value = value.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1, offset, s) {
|
||||
return p1.replace(/\t/g, tabReplace);
|
||||
});
|
||||
}
|
||||
if (useBR) {
|
||||
value = value.replace(/\n/g, '<br>');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
Applies highlighting to a DOM node containing code. Accepts a DOM node and
|
||||
two optional parameters for fixMarkup.
|
||||
*/
|
||||
function highlightBlock(block, tabReplace, useBR) {
|
||||
var text = blockText(block, useBR);
|
||||
var language = blockLanguage(block);
|
||||
if (language == 'no-highlight')
|
||||
return;
|
||||
var result = language ? highlight(language, text) : highlightAuto(text);
|
||||
language = result.language;
|
||||
var original = nodeStream(block);
|
||||
if (original.length) {
|
||||
var pre = document.createElement('pre');
|
||||
pre.innerHTML = result.value;
|
||||
result.value = mergeStreams(original, nodeStream(pre), text);
|
||||
}
|
||||
result.value = fixMarkup(result.value, tabReplace, useBR);
|
||||
|
||||
var class_name = block.className;
|
||||
if (!class_name.match('(\\s|^)(language-)?' + language + '(\\s|$)')) {
|
||||
class_name = class_name ? (class_name + ' ' + language) : language;
|
||||
}
|
||||
block.innerHTML = result.value;
|
||||
block.className = class_name;
|
||||
block.result = {
|
||||
language: language,
|
||||
kw: result.keyword_count,
|
||||
re: result.relevance
|
||||
};
|
||||
if (result.second_best) {
|
||||
block.second_best = {
|
||||
language: result.second_best.language,
|
||||
kw: result.second_best.keyword_count,
|
||||
re: result.second_best.relevance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
|
||||
*/
|
||||
function initHighlighting() {
|
||||
if (initHighlighting.called)
|
||||
return;
|
||||
initHighlighting.called = true;
|
||||
Array.prototype.map.call(document.getElementsByTagName('pre'), findCode).
|
||||
filter(Boolean).
|
||||
forEach(function(code){highlightBlock(code, hljs.tabReplace)});
|
||||
}
|
||||
|
||||
/*
|
||||
Attaches highlighting to the page load event.
|
||||
*/
|
||||
function initHighlightingOnLoad() {
|
||||
window.addEventListener('DOMContentLoaded', initHighlighting, false);
|
||||
window.addEventListener('load', initHighlighting, false);
|
||||
}
|
||||
|
||||
var languages = {}; // a shortcut to avoid writing "this." everywhere
|
||||
|
||||
/* Interface definition */
|
||||
|
||||
this.LANGUAGES = languages;
|
||||
this.highlight = highlight;
|
||||
this.highlightAuto = highlightAuto;
|
||||
this.fixMarkup = fixMarkup;
|
||||
this.highlightBlock = highlightBlock;
|
||||
this.initHighlighting = initHighlighting;
|
||||
this.initHighlightingOnLoad = initHighlightingOnLoad;
|
||||
|
||||
// Common regexps
|
||||
this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*';
|
||||
this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*';
|
||||
this.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
|
||||
this.C_NUMBER_RE = '(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float
|
||||
this.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
|
||||
this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
|
||||
|
||||
// Common modes
|
||||
this.BACKSLASH_ESCAPE = {
|
||||
begin: '\\\\[\\s\\S]', relevance: 0
|
||||
};
|
||||
this.APOS_STRING_MODE = {
|
||||
className: 'string',
|
||||
begin: '\'', end: '\'',
|
||||
illegal: '\\n',
|
||||
contains: [this.BACKSLASH_ESCAPE],
|
||||
relevance: 0
|
||||
};
|
||||
this.QUOTE_STRING_MODE = {
|
||||
className: 'string',
|
||||
begin: '"', end: '"',
|
||||
illegal: '\\n',
|
||||
contains: [this.BACKSLASH_ESCAPE],
|
||||
relevance: 0
|
||||
};
|
||||
this.C_LINE_COMMENT_MODE = {
|
||||
className: 'comment',
|
||||
begin: '//', end: '$'
|
||||
};
|
||||
this.C_BLOCK_COMMENT_MODE = {
|
||||
className: 'comment',
|
||||
begin: '/\\*', end: '\\*/'
|
||||
};
|
||||
this.HASH_COMMENT_MODE = {
|
||||
className: 'comment',
|
||||
begin: '#', end: '$'
|
||||
};
|
||||
this.NUMBER_MODE = {
|
||||
className: 'number',
|
||||
begin: this.NUMBER_RE,
|
||||
relevance: 0
|
||||
};
|
||||
this.C_NUMBER_MODE = {
|
||||
className: 'number',
|
||||
begin: this.C_NUMBER_RE,
|
||||
relevance: 0
|
||||
};
|
||||
this.BINARY_NUMBER_MODE = {
|
||||
className: 'number',
|
||||
begin: this.BINARY_NUMBER_RE,
|
||||
relevance: 0
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
this.inherit = function(parent, obj) {
|
||||
var result = {}
|
||||
for (var key in parent)
|
||||
result[key] = parent[key];
|
||||
if (obj)
|
||||
for (var key in obj)
|
||||
result[key] = obj[key];
|
||||
return result;
|
||||
}
|
||||
}();
|
||||
hljs.LANGUAGES['css'] = function(hljs) {
|
||||
var FUNCTION = {
|
||||
className: 'function',
|
||||
begin: hljs.IDENT_RE + '\\(', end: '\\)',
|
||||
contains: [hljs.NUMBER_MODE, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE]
|
||||
};
|
||||
return {
|
||||
case_insensitive: true,
|
||||
illegal: '[=/|\']',
|
||||
contains: [
|
||||
hljs.C_BLOCK_COMMENT_MODE,
|
||||
{
|
||||
className: 'id', begin: '\\#[A-Za-z0-9_-]+'
|
||||
},
|
||||
{
|
||||
className: 'class', begin: '\\.[A-Za-z0-9_-]+',
|
||||
relevance: 0
|
||||
},
|
||||
{
|
||||
className: 'attr_selector',
|
||||
begin: '\\[', end: '\\]',
|
||||
illegal: '$'
|
||||
},
|
||||
{
|
||||
className: 'pseudo',
|
||||
begin: ':(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\"\\\']+'
|
||||
},
|
||||
{
|
||||
className: 'at_rule',
|
||||
begin: '@(font-face|page)',
|
||||
lexems: '[a-z-]+',
|
||||
keywords: 'font-face page'
|
||||
},
|
||||
{
|
||||
className: 'at_rule',
|
||||
begin: '@', end: '[{;]', // at_rule eating first "{" is a good thing
|
||||
// because it doesn’t let it to be parsed as
|
||||
// a rule set but instead drops parser into
|
||||
// the default mode which is how it should be.
|
||||
excludeEnd: true,
|
||||
keywords: 'import page media charset',
|
||||
contains: [
|
||||
FUNCTION,
|
||||
hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE,
|
||||
hljs.NUMBER_MODE
|
||||
]
|
||||
},
|
||||
{
|
||||
className: 'tag', begin: hljs.IDENT_RE,
|
||||
relevance: 0
|
||||
},
|
||||
{
|
||||
className: 'rules',
|
||||
begin: '{', end: '}',
|
||||
illegal: '[^\\s]',
|
||||
relevance: 0,
|
||||
contains: [
|
||||
hljs.C_BLOCK_COMMENT_MODE,
|
||||
{
|
||||
className: 'rule',
|
||||
begin: '[^\\s]', returnBegin: true, end: ';', endsWithParent: true,
|
||||
contains: [
|
||||
{
|
||||
className: 'attribute',
|
||||
begin: '[A-Z\\_\\.\\-]+', end: ':',
|
||||
excludeEnd: true,
|
||||
illegal: '[^\\s]',
|
||||
starts: {
|
||||
className: 'value',
|
||||
endsWithParent: true, excludeEnd: true,
|
||||
contains: [
|
||||
FUNCTION,
|
||||
hljs.NUMBER_MODE,
|
||||
hljs.QUOTE_STRING_MODE,
|
||||
hljs.APOS_STRING_MODE,
|
||||
hljs.C_BLOCK_COMMENT_MODE,
|
||||
{
|
||||
className: 'hexcolor', begin: '\\#[0-9A-F]+'
|
||||
},
|
||||
{
|
||||
className: 'important', begin: '!important'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}(hljs);
|
||||
35
r2/r2/templates/subredditstylesheetsource.html
Normal file
35
r2/r2/templates/subredditstylesheetsource.html
Normal file
@@ -0,0 +1,35 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2012
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib import js
|
||||
from r2.lib.filters import SC_OFF, SC_ON
|
||||
from r2.lib.template_helpers import static
|
||||
%>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${static("highlight.css")}">
|
||||
|
||||
<pre class="subreddit-stylesheet-source">
|
||||
<code class="language-css">${unsafe(SC_OFF)}${thing.stylesheet_contents}${unsafe(SC_ON)}</code>
|
||||
</pre>
|
||||
|
||||
${unsafe(js.use("highlight"))}
|
||||
Reference in New Issue
Block a user