diff options
Diffstat (limited to 'pecan')
-rw-r--r-- | pecan/__init__.py | 25 | ||||
-rw-r--r-- | pecan/core.py | 4 | ||||
-rw-r--r-- | pecan/middleware/__init__.py | 1 | ||||
-rw-r--r-- | pecan/middleware/debug.py | 420 | ||||
-rw-r--r-- | pecan/middleware/resources/XRegExp.js | 664 | ||||
-rw-r--r-- | pecan/middleware/resources/__init__.py | 28 | ||||
-rw-r--r-- | pecan/middleware/resources/brush-python.js | 64 | ||||
-rw-r--r-- | pecan/middleware/resources/pecan.png | bin | 4454 -> 0 bytes | |||
-rw-r--r-- | pecan/middleware/resources/shCore.js | 1731 | ||||
-rw-r--r-- | pecan/middleware/resources/syntax.css | 226 | ||||
-rw-r--r-- | pecan/middleware/resources/theme.css | 117 | ||||
-rw-r--r-- | pecan/tests/middleware/test_debug.py | 83 | ||||
-rw-r--r-- | pecan/tests/test_base.py | 35 |
13 files changed, 111 insertions, 3287 deletions
diff --git a/pecan/__init__.py b/pecan/__init__.py index 3987892..1adcc71 100644 --- a/pecan/__init__.py +++ b/pecan/__init__.py @@ -20,7 +20,6 @@ try: except ImportError: from logutils.dictconfig import dictConfig as load_logging_config # noqa -import six import warnings @@ -44,8 +43,6 @@ def make_app(root, **kw): debug mode is set. :param debug: A flag to enable debug mode. This enables the debug middleware and serving static files. - :param debugger: A callable to start debugging, defaulting to the Python - debugger entry point ``pdb.post_mortem``. :param wrap_app: A function or middleware class to wrap the Pecan app. This must either be a wsgi middleware class or a function that returns a wsgi application. This wrapper @@ -101,19 +98,19 @@ def make_app(root, **kw): # Included for internal redirect support app = middleware.recursive.RecursiveMiddleware(app) - # When in debug mode, load our exception dumping middleware + # When in debug mode, load exception debugging middleware static_root = kw.get('static_root', None) if debug: - debugger = kw.get('debugger', None) - debugger_kwargs = {} - if six.callable(debugger): - debugger_kwargs['debugger'] = debugger - elif debugger: - warnings.warn( - "`app.debugger` is not callable, ignoring", - RuntimeWarning - ) - app = middleware.debug.DebugMiddleware(app, **debugger_kwargs) + debug_kwargs = getattr(conf, 'debug', {}) + debug_kwargs.setdefault('context_injectors', []).append( + lambda environ: { + 'request': environ.get('pecan.locals', {}).get('request') + } + ) + app = DebugMiddleware( + app, + **debug_kwargs + ) # Support for serving static files (for development convenience) if static_root: diff --git a/pecan/core.py b/pecan/core.py index 050687c..854d948 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -662,6 +662,10 @@ class PecanBase(object): req = self.request_cls(environ) resp = self.response_cls() state = RoutingState(req, resp, self) + environ['pecan.locals'] = { + 'request': req, + 'response': resp + } controller = None # handle the request diff --git a/pecan/middleware/__init__.py b/pecan/middleware/__init__.py index d95b453..0c82911 100644 --- a/pecan/middleware/__init__.py +++ b/pecan/middleware/__init__.py @@ -1,4 +1,3 @@ -from . import debug from . import errordocument from . import recursive from . import static diff --git a/pecan/middleware/debug.py b/pecan/middleware/debug.py index ec2faa0..1a6a482 100644 --- a/pecan/middleware/debug.py +++ b/pecan/middleware/debug.py @@ -1,324 +1,96 @@ -from traceback import print_exc -from pprint import pformat -import pdb - -from six.moves import cStringIO as StringIO - -from mako.template import Template -from webob import Response - -from .resources import (pecan_image, xregexp_js, syntax_js, syntax_css, theme, - brush) - - -debug_template_raw = '''<html> - <head> - <title>Pecan - Application Error</title> - - <link rel="stylesheet" type="text/css" href="${syntax_css}" /> - <link rel="stylesheet" type="text/css" href="${theme}" /> - - <script type="text/javascript" src="${xregexp_js}"></script> - <script type="text/javascript" src="${syntax_js}"> - /** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - </script> - <script type="text/javascript" src="${brush}"> - /** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - </script> - - <style type="text/css"> - body { - color: #000; - background: #FFF; - font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif; - font-size: 12px; - padding: 0; - margin: 0; - } - - a { - color: #FAFF78; - } - - h1, h2, h3, h4, h5, h6 { - font-family: 'Helvetica', sans-serif; - } - - h1 { - margin: 0; - padding: .75em 1.5em 1em 1.5em; - color: #F90; - font-size: 14px; - font-weight: bold; - } - - h1 img { - padding-right: 5px; - } - - h2 { - color: #311F00; - } - - header { - width: 100%; - background: #311F00; - } - - div#error-content { - padding: 0 2em; - } - - .syntaxhighlighter a, - .syntaxhighlighter div, - .syntaxhighlighter code, - .syntaxhighlighter table, - .syntaxhighlighter table td, - .syntaxhighlighter table tr, - .syntaxhighlighter table tbody, - .syntaxhighlighter table thead, - .syntaxhighlighter table caption, - .syntaxhighlighter textarea { - font-family: monospace !important; - } - - .syntaxhighlighter .container { - background: #FDF6E3 !important; - padding: 1em !important; - } - - .syntaxhighlighter .container .line { - background: #FDF6E3 !important; - } - - .syntaxhighlighter .container .line .python.string { - color: #C70 !important; - } - - #debug { - background: #FDF6E3; - padding: 10px !important; - margin-top: 10px; - font-family: monospace; - } - - </style> - <script type="text/javascript"> - SyntaxHighlighter.defaults['gutter'] = false; - SyntaxHighlighter.defaults['toolbar'] = false; - SyntaxHighlighter.all() - </script> - - <script type="text/javascript"> - function get_request() { - /* ajax sans jquery makes me sad */ - var request = false; - - // Mozilla/Safari - if (window.XMLHttpRequest) { - request = new XMLHttpRequest(); - } - - // IE - else if (window.ActiveXObject) { - request = new ActiveXObject("Microsoft.XMLHTTP"); - } - - return request; - } - - function debug_request(btn) { - btn.disabled = true; - - request = get_request(); - request.open('GET', '/__pecan_initiate_pdb__', true); - request.onreadystatechange = function() { - if (request.readyState == 4) { - btn.disabled = false; - } - } - request.send(''); - - /* automatically timeout after 5 minutes, re-enabling the button */ - setTimeout(function() { - request.abort(); - }, 5 * 60 * 1000); - } - </script> - </head> - <body> - - <header> - <h1> - <img style="padding-top: 7px" - align="center" alt="pecan logo" - height="25" - src="${pecan_image}" /> - application error - </h1> - </header> - - <div id="error-content"> - - <p> - <b>To disable this interface, set </b> - <pre class="brush: python">conf.app.debug = False</pre> - </p> - - <h2>Traceback</h2> - <div id="traceback"> - <pre class="brush: python">${traceback}</pre> - </div> - - % if not debugging: - <b>Want to debug this request?</b> - <div id="debug"> - You can <button onclick="debug_request(this)"> - repeat this request - </button> with a Python debugger breakpoint. - </div> - % endif - - <h2>WSGI Environment</h2> - <div id="environ"> - <pre class="brush: python">${environment}</pre> - </div> - </div> - </body> -</html> -''' - -debug_template = Template(debug_template_raw) -__debug_environ__ = None - - -class PdbMiddleware(object): - def __init__(self, app, debugger): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - try: - return self.app(environ, start_response) - except: - self.debugger() - - -class DebugMiddleware(object): - """A WSGI middleware that provides debugging assistance for development - environments. - - To enable the debugging middleware, simply set the ``debug`` flag to - ``True`` in your configuration file:: - - app = { - ... - 'debug': True, - ... - } - - Once enabled, the middleware will automatically catch exceptions raised by - your application, and display the Python stack trace and WSGI environment - in your browser for easy debugging. - - To further aid in debugging, the middleware includes the ability to repeat - the offending request, automatically inserting a breakpoint, and dropping - your console into the Python debugger, ``pdb.post_mortem``. - - You can also use any debugger with a suitable ``post_mortem`` entry point - such as the `PuDB Debugger <http://pypi.python.org/pypi/pudb>`_, - - For more information, refer to the `documentation for pdb - <http://docs.python.org/library/pdb.html>`_ available on the Python - website. - - :param app: the application to wrap. - :param debugger: a callable to start debugging, defaulting to the Python - debugger entry point ``pdb.post_mortem``. - """ - - def __init__(self, app, debugger=pdb.post_mortem): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - if environ['wsgi.multiprocess']: - raise RuntimeError( - "The DebugMiddleware middleware is not usable in a " - "multi-process environment" - ) - - if environ.get('paste.testing'): - return self.app(environ, start_response) - - # initiate a PDB session if requested - global __debug_environ__ - debugging = environ['PATH_INFO'] == '/__pecan_initiate_pdb__' - if debugging: - PdbMiddleware(self.app, self.debugger)( - __debug_environ__, start_response - ) - environ = __debug_environ__ - - try: - return self.app(environ, start_response) - except: - # save the environ for debugging - if not debugging: - __debug_environ__ = environ - - # get a formatted exception - out = StringIO() - print_exc(file=out) - - # get formatted WSGI environment - formatted_environ = pformat(environ) - - # render our template - result = debug_template.render( - traceback=out.getvalue(), - environment=formatted_environ, - pecan_image=pecan_image, - xregexp_js=xregexp_js, - syntax_js=syntax_js, - brush=brush, - syntax_css=syntax_css, - theme=theme, - debugging=debugging - ) - - # construct and return our response - response = Response() - response.status_int = 400 - response.unicode_body = result - return response(environ, start_response) +__CONFIG_HELP__ = ''' +<div class="traceback"> + <b>To disable this interface, set </b> + <a target="window" + href="https://pecan.readthedocs.org/en/latest/deployment.html#disabling-debug-mode"> + <pre>conf.app.debug = False</pre> + </a> +</div> +''' # noqa + +try: + import re + from backlash.debug import DebuggedApplication + + class DebugMiddleware(DebuggedApplication): + + body_re = re.compile('(<body[^>]*>)', re.I) + + def debug_application(self, environ, start_response): + for part in super(DebugMiddleware, self).debug_application( + environ, start_response + ): + yield self.body_re.sub('\g<1>%s' % __CONFIG_HELP__, part) + + +except ImportError: + from traceback import print_exc + from pprint import pformat + + from mako.template import Template + from six.moves import cStringIO as StringIO + from webob import Response + from webob.exc import HTTPException + + debug_template_raw = '''<html> + <head> + <title>Pecan - Application Error</title> + <body> + <header> + <h1> + An error occurred! + </h1> + </header> + <div id="error-content"> + <p> + %(config_help)s + Pecan offers support for interactive debugging by installing the <a href="https://pypi.python.org/pypi/backlash" target="window">backlash</a> package: + <br /> + <b><pre>pip install backlash</pre></b> + ...and reloading this page. + </p> + <h2>Traceback</h2> + <div id="traceback"> + <pre>${traceback}</pre> + </div> + <h2>WSGI Environment</h2> + <div id="environ"> + <pre>${environment}</pre> + </div> + </div> + </body> + </html> + ''' % {'config_help': __CONFIG_HELP__} # noqa + + debug_template = Template(debug_template_raw) + + class DebugMiddleware(object): + + def __init__(self, app, *args, **kwargs): + self.app = app + + def __call__(self, environ, start_response): + try: + return self.app(environ, start_response) + except Exception as exc: + # get a formatted exception + out = StringIO() + print_exc(file=out) + + # get formatted WSGI environment + formatted_environ = pformat(environ) + + # render our template + result = debug_template.render( + traceback=out.getvalue(), + environment=formatted_environ + ) + + # construct and return our response + response = Response() + if isinstance(exc, HTTPException): + response.status_int = exc.status + else: + response.status_int = 500 + response.unicode_body = result + return response(environ, start_response) diff --git a/pecan/middleware/resources/XRegExp.js b/pecan/middleware/resources/XRegExp.js deleted file mode 100644 index ebdb9c9..0000000 --- a/pecan/middleware/resources/XRegExp.js +++ /dev/null @@ -1,664 +0,0 @@ -// XRegExp 1.5.1 -// (c) 2007-2012 Steven Levithan -// MIT License -// <http://xregexp.com> -// Provides an augmented, extensible, cross-browser implementation of regular expressions, -// including support for additional syntax, flags, and methods - -var XRegExp; - -if (XRegExp) { - // Avoid running twice, since that would break references to native globals - throw Error("can't load XRegExp twice in the same frame"); -} - -// Run within an anonymous function to protect variables and avoid new globals -(function (undefined) { - - //--------------------------------- - // Constructor - //--------------------------------- - - // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native - // regular expression in that additional syntax and flags are supported and cross-browser - // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and - // converts to type XRegExp - XRegExp = function (pattern, flags) { - var output = [], - currScope = XRegExp.OUTSIDE_CLASS, - pos = 0, - context, tokenResult, match, chr, regex; - - if (XRegExp.isRegExp(pattern)) { - if (flags !== undefined) - throw TypeError("can't supply flags when constructing one RegExp from another"); - return clone(pattern); - } - // Tokens become part of the regex construction process, so protect against infinite - // recursion when an XRegExp is constructed within a token handler or trigger - if (isInsideConstructor) - throw Error("can't call the XRegExp constructor within token definition functions"); - - flags = flags || ""; - context = { // `this` object for custom tokens - hasNamedCapture: false, - captureNames: [], - hasFlag: function (flag) {return flags.indexOf(flag) > -1;}, - setFlag: function (flag) {flags += flag;} - }; - - while (pos < pattern.length) { - // Check for custom tokens at the current position - tokenResult = runTokens(pattern, pos, currScope, context); - - if (tokenResult) { - output.push(tokenResult.output); - pos += (tokenResult.match[0].length || 1); - } else { - // Check for native multicharacter metasequences (excluding character classes) at - // the current position - if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) { - output.push(match[0]); - pos += match[0].length; - } else { - chr = pattern.charAt(pos); - if (chr === "[") - currScope = XRegExp.INSIDE_CLASS; - else if (chr === "]") - currScope = XRegExp.OUTSIDE_CLASS; - // Advance position one character - output.push(chr); - pos++; - } - } - } - - regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, "")); - regex._xregexp = { - source: pattern, - captureNames: context.hasNamedCapture ? context.captureNames : null - }; - return regex; - }; - - - //--------------------------------- - // Public properties - //--------------------------------- - - XRegExp.version = "1.5.1"; - - // Token scope bitflags - XRegExp.INSIDE_CLASS = 1; - XRegExp.OUTSIDE_CLASS = 2; - - - //--------------------------------- - // Private variables - //--------------------------------- - - var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g, - flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags - quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/, - isInsideConstructor = false, - tokens = [], - // Copy native globals for reference ("native" is an ES3 reserved keyword) - nativ = { - exec: RegExp.prototype.exec, - test: RegExp.prototype.test, - match: String.prototype.match, - replace: String.prototype.replace, - split: String.prototype.split - }, - compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups - compliantLastIndexIncrement = function () { - var x = /^/g; - nativ.test.call(x, ""); - return !x.lastIndex; - }(), - hasNativeY = RegExp.prototype.sticky !== undefined, - nativeTokens = {}; - - // `nativeTokens` match native multicharacter metasequences only (including deprecated octals, - // excluding character classes) - nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/; - nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/; - - - //--------------------------------- - // Public methods - //--------------------------------- - - // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by - // the XRegExp library and can be used to create XRegExp plugins. This function is intended for - // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can - // be disabled by `XRegExp.freezeTokens` - XRegExp.addToken = function (regex, handler, scope, trigger) { - tokens.push({ - pattern: clone(regex, "g" + (hasNativeY ? "y" : "")), - handler: handler, - scope: scope || XRegExp.OUTSIDE_CLASS, - trigger: trigger || null - }); - }; - - // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag - // combination has previously been cached, the cached copy is returned; otherwise the newly - // created regex is cached - XRegExp.cache = function (pattern, flags) { - var key = pattern + "/" + (flags || ""); - return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags)); - }; - - // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh - // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global` - // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve - // special properties required for named capture - XRegExp.copyAsGlobal = function (regex) { - return clone(regex, "g"); - }; - - // Accepts a string; returns the string with regex metacharacters escaped. The returned string - // can safely be used at any point within a regex to match the provided literal string. Escaped - // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace - XRegExp.escape = function (str) { - return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - }; - - // Accepts a string to search, regex to search with, position to start the search within the - // string (default: 0), and an optional Boolean indicating whether matches must start at-or- - // after the position or at the specified position only. This function ignores the `lastIndex` - // of the provided regex in its own handling, but updates the property for compatibility - XRegExp.execAt = function (str, regex, pos, anchored) { - var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")), - match; - r2.lastIndex = pos = pos || 0; - match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (anchored && match && match.index !== pos) - match = null; - if (regex.global) - regex.lastIndex = match ? r2.lastIndex : 0; - return match; - }; - - // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing - // syntax and flag changes. Should be run after XRegExp and any plugins are loaded - XRegExp.freezeTokens = function () { - XRegExp.addToken = function () { - throw Error("can't run addToken after freezeTokens"); - }; - }; - - // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object. - // Note that this is also `true` for regex literals and regexes created by the `XRegExp` - // constructor. This works correctly for variables created in another frame, when `instanceof` - // and `constructor` checks would fail to work as intended - XRegExp.isRegExp = function (o) { - return Object.prototype.toString.call(o) === "[object RegExp]"; - }; - - // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to - // iterate over regex matches compared to the traditional approaches of subverting - // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop - XRegExp.iterate = function (str, regex, callback, context) { - var r2 = clone(regex, "g"), - i = -1, match; - while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (regex.global) - regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback` - callback.call(context, match, ++i, str, regex); - if (r2.lastIndex === match.index) - r2.lastIndex++; - } - if (regex.global) - regex.lastIndex = 0; - }; - - // Accepts a string and an array of regexes; returns the result of using each successive regex - // to search within the matches of the previous regex. The array of regexes can also contain - // objects with `regex` and `backref` properties, in which case the named or numbered back- - // references specified are passed forward to the next regex or returned. E.g.: - // var xregexpImgFileNames = XRegExp.matchChain(html, [ - // {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes - // {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values - // {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths - // /[^\/]+$/ // filenames (strip directory paths) - // ]); - XRegExp.matchChain = function (str, chain) { - return function recurseChain (values, level) { - var item = chain[level].regex ? chain[level] : {regex: chain[level]}, - regex = clone(item.regex, "g"), - matches = [], i; - for (i = 0; i < values.length; i++) { - XRegExp.iterate(values[i], regex, function (match) { - matches.push(item.backref ? (match[item.backref] || "") : match[0]); - }); - } - return ((level === chain.length - 1) || !matches.length) ? - matches : recurseChain(matches, level + 1); - }([str], 0); - }; - - - //--------------------------------- - // New RegExp prototype methods - //--------------------------------- - - // Accepts a context object and arguments array; returns the result of calling `exec` with the - // first value in the arguments array. the context is ignored but is accepted for congruity - // with `Function.prototype.apply` - RegExp.prototype.apply = function (context, args) { - return this.exec(args[0]); - }; - - // Accepts a context object and string; returns the result of calling `exec` with the provided - // string. the context is ignored but is accepted for congruity with `Function.prototype.call` - RegExp.prototype.call = function (context, str) { - return this.exec(str); - }; - - - //--------------------------------- - // Overriden native methods - //--------------------------------- - - // Adds named capture support (with backreferences returned as `result.name`), and fixes two - // cross-browser issues per ES3: - // - Captured values for nonparticipating capturing groups should be returned as `undefined`, - // rather than the empty string. - // - `lastIndex` should not be incremented after zero-length matches. - RegExp.prototype.exec = function (str) { - var match, name, r2, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.apply(this, arguments); - if (match) { - // Fix browsers whose `exec` methods don't consistently return `undefined` for - // nonparticipating capturing groups - if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { - r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", "")); - // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed - // matching due to characters outside the match - nativ.replace.call((str + "").slice(match.index), r2, function () { - for (var i = 1; i < arguments.length - 2; i++) { - if (arguments[i] === undefined) - match[i] = undefined; - } - }); - } - // Attach named capture properties - if (this._xregexp && this._xregexp.captureNames) { - for (var i = 1; i < match.length; i++) { - name = this._xregexp.captureNames[i - 1]; - if (name) - match[name] = match[i]; - } - } - // Fix browsers that increment `lastIndex` after zero-length matches - if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - } - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return match; - }; - - // Fix browser bugs in native method - RegExp.prototype.test = function (str) { - // Use the native `exec` to skip some processing overhead, even though the altered - // `exec` would take care of the `lastIndex` fixes - var match, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.call(this, str); - // Fix browsers that increment `lastIndex` after zero-length matches - if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return !!match; - }; - - // Adds named capture support and fixes browser bugs in native method - String.prototype.match = function (regex) { - if (!XRegExp.isRegExp(regex)) - regex = RegExp(regex); // Native `RegExp` - if (regex.global) { - var result = nativ.match.apply(this, arguments); - regex.lastIndex = 0; // Fix IE bug - return result; - } - return regex.exec(this); // Run the altered `exec` - }; - - // Adds support for `${n}` tokens for named and numbered backreferences in replacement text, - // and provides named backreferences to replacement functions as `arguments[0].name`. Also - // fixes cross-browser differences in replacement text syntax when performing a replacement - // using a nonregex search value, and the value of replacement regexes' `lastIndex` property - // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary - // third (`flags`) parameter - String.prototype.replace = function (search, replacement) { - var isRegex = XRegExp.isRegExp(search), - captureNames, result, str, origLastIndex; - - // There are too many combinations of search/replacement types/values and browser bugs that - // preclude passing to native `replace`, so don't try - //if (...) - // return nativ.replace.apply(this, arguments); - - if (isRegex) { - if (search._xregexp) - captureNames = search._xregexp.captureNames; // Array or `null` - if (!search.global) - origLastIndex = search.lastIndex; - } else { - search = search + ""; // Type conversion - } - - if (Object.prototype.toString.call(replacement) === "[object Function]") { - result = nativ.replace.call(this + "", search, function () { - if (captureNames) { - // Change the `arguments[0]` string primitive to a String object which can store properties - arguments[0] = new String(arguments[0]); - // Store named backreferences on `arguments[0]` - for (var i = 0; i < captureNames.length; i++) { - if (captureNames[i]) - arguments[0][captureNames[i]] = arguments[i + 1]; - } - } - // Update `lastIndex` before calling `replacement` (fix browsers) - if (isRegex && search.global) - search.lastIndex = arguments[arguments.length - 2] + arguments[0].length; - return replacement.apply(null, arguments); - }); - } else { - str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`) - result = nativ.replace.call(str, search, function () { - var args = arguments; // Keep this function's `arguments` available through closure - return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) { - // Numbered backreference (without delimiters) or special variable - if ($1) { - switch ($1) { - case "$": return "$"; - case "&": return args[0]; - case "`": return args[args.length - 1].slice(0, args[args.length - 2]); - case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length); - // Numbered backreference - default: - // What does "$10" mean? - // - Backreference 10, if 10 or more capturing groups exist - // - Backreference 1 followed by "0", if 1-9 capturing groups exist - // - Otherwise, it's the string "$10" - // Also note: - // - Backreferences cannot be more than two digits (enforced by `replacementToken`) - // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01" - // - There is no "$0" token ("$&" is the entire match) - var literalNumbers = ""; - $1 = +$1; // Type conversion; drop leading zero - if (!$1) // `$1` was "0" or "00" - return $0; - while ($1 > args.length - 3) { - literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers; - $1 = Math.floor($1 / 10); // Drop the last digit - } - return ($1 ? args[$1] || "" : "$") + literalNumbers; - } - // Named backreference or delimited numbered backreference - } else { - // What does "${n}" mean? - // - Backreference to numbered capture n. Two differences from "$n": - // - n can be more than two digits - // - Backreference 0 is allowed, and is the entire match - // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture - // - Otherwise, it's the string "${n}" - var n = +$2; // Type conversion; drop leading zeros - if (n <= args.length - 3) - return args[n]; - n = captureNames ? indexOf(captureNames, $2) : -1; - return n > -1 ? args[n + 1] : $0; - } - }); - }); - } - - if (isRegex) { - if (search.global) - search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows) - else - search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - } - - return result; - }; - - // A consistent cross-browser, ES3 compliant `split` - String.prototype.split = function (s /* separator */, limit) { - // If separator `s` is not a regex, use the native `split` - if (!XRegExp.isRegExp(s)) - return nativ.split.apply(this, arguments); - - var str = this + "", // Type conversion - output = [], - lastLastIndex = 0, - match, lastLength; - - // Behavior for `limit`: if it's... - // - `undefined`: No limit - // - `NaN` or zero: Return an empty array - // - A positive number: Use `Math.floor(limit)` - // - A negative number: No limit - // - Other: Type-convert, then use the above rules - if (limit === undefined || +limit < 0) { - limit = Infinity; - } else { - limit = Math.floor(+limit); - if (!limit) - return []; - } - - // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero - // and restore it to its original value when we're done using the regex - s = XRegExp.copyAsGlobal(s); - - while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (s.lastIndex > lastLastIndex) { - output.push(str.slice(lastLastIndex, match.index)); - - if (match.length > 1 && match.index < str.length) - Array.prototype.push.apply(output, match.slice(1)); - - lastLength = match[0].length; - lastLastIndex = s.lastIndex; - - if (output.length >= limit) - break; - } - - if (s.lastIndex === match.index) - s.lastIndex++; - } - - if (lastLastIndex === str.length) { - if (!nativ.test.call(s, "") || lastLength) - output.push(""); - } else { - output.push(str.slice(lastLastIndex)); - } - - return output.length > limit ? output.slice(0, limit) : output; - }; - - - //--------------------------------- - // Private helper functions - //--------------------------------- - - // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp` - // instance with a fresh `lastIndex` (set to zero), preserving properties required for named - // capture. Also allows adding new flags in the process of copying the regex - function clone (regex, additionalFlags) { - if (!XRegExp.isRegExp(regex)) - throw TypeError("type RegExp expected"); - var x = regex._xregexp; - regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || "")); - if (x) { - regex._xregexp = { - source: x.source, - captureNames: x.captureNames ? x.captureNames.slice(0) : null - }; - } - return regex; - } - - function getNativeFlags (regex) { - return (regex.global ? "g" : "") + - (regex.ignoreCase ? "i" : "") + - (regex.multiline ? "m" : "") + - (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 - (regex.sticky ? "y" : ""); - } - - function runTokens (pattern, index, scope, context) { - var i = tokens.length, - result, match, t; - // Protect against constructing XRegExps within token handler and trigger functions - isInsideConstructor = true; - // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws - try { - while (i--) { // Run in reverse order - t = tokens[i]; - if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) { - t.pattern.lastIndex = index; - match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc. - if (match && match.index === index) { - result = { - output: t.handler.call(context, match, scope), - match: match - }; - break; - } - } - } - } catch (err) { - throw err; - } finally { - isInsideConstructor = false; - } - return result; - } - - function indexOf (array, item, from) { - if (Array.prototype.indexOf) // Use the native array method if available - return array.indexOf(item, from); - for (var i = from || 0; i < array.length; i++) { - if (array[i] === item) - return i; - } - return -1; - } - - - //--------------------------------- - // Built-in tokens - //--------------------------------- - - // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the - // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS` - - // Comment pattern: (?# ) - XRegExp.addToken( - /\(\?#[^)]*\)/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - } - ); - - // Capturing group (match the opening parenthesis only). - // Required for support of named capturing groups - XRegExp.addToken( - /\((?!\?)/, - function () { - this.captureNames.push(null); - return "("; - } - ); - - // Named capturing group (match the opening delimiter only): (?<name> - XRegExp.addToken( - /\(\?<([$\w]+)>/, - function (match) { - this.captureNames.push(match[1]); - this.hasNamedCapture = true; - return "("; - } - ); - - // Named backreference: \k<name> - XRegExp.addToken( - /\\k<([\w$]+)>/, - function (match) { - var index = indexOf(this.captureNames, match[1]); - // Keep backreferences separate from subsequent literal numbers. Preserve back- - // references to named groups that are undefined at this point as literal strings - return index > -1 ? - "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") : - match[0]; - } - ); - - // Empty character class: [] or [^] - XRegExp.addToken( - /\[\^?]/, - function (match) { - // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S]. - // (?!) should work like \b\B, but is unreliable in Firefox - return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]"; - } - ); - - // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx) - // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc. - XRegExp.addToken( - /^\(\?([imsx]+)\)/, - function (match) { - this.setFlag(match[1]); - return ""; - } - ); - - // Whitespace and comments, in free-spacing (aka extended) mode only - XRegExp.addToken( - /(?:\s+|#.*)+/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - }, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("x");} - ); - - // Dot, in dotall (aka singleline) mode only - XRegExp.addToken( - /\./, - function () {return "[\\s\\S]";}, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("s");} - ); - - - //--------------------------------- - // Backward compatibility - //--------------------------------- - - // Uncomment the following block for compatibility with XRegExp 1.0-1.2: - /* - XRegExp.matchWithinChain = XRegExp.matchChain; - RegExp.prototype.addFlags = function (s) {return clone(this, s);}; - RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;}; - RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);}; - RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;}; - */ - -})(); - diff --git a/pecan/middleware/resources/__init__.py b/pecan/middleware/resources/__init__.py deleted file mode 100644 index 2d8c627..0000000 --- a/pecan/middleware/resources/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -from mimetypes import guess_type -from contextlib import closing -from base64 import b64encode - -from pecan.compat import quote - - -def load_resource(filename): - with closing(open( - os.path.join( - os.path.dirname(__file__), - filename - ), - 'rb' - )) as f: - data = f.read() - return 'data:%s;base64,%s' % ( - guess_type(filename)[0], - quote(b64encode(data)) - ) - -pecan_image = load_resource('pecan.png') -xregexp_js = load_resource('XRegExp.js') -syntax_js = load_resource('shCore.js') -syntax_css = load_resource('syntax.css') -theme = load_resource('theme.css') -brush = load_resource('brush-python.js') diff --git a/pecan/middleware/resources/brush-python.js b/pecan/middleware/resources/brush-python.js deleted file mode 100644 index 30f0a8f..0000000 --- a/pecan/middleware/resources/brush-python.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -(function() -{ - // CommonJS - typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; - - function Brush() - { - // Contributed by Gheorghe Milas and Ahmad Sherif - - var keywords = 'and assert break class continue def del elif else ' + - 'except exec finally for from global if import in is ' + - 'lambda not or pass print raise return try yield while'; - - var funcs = '__import__ abs all any apply basestring bin bool buffer callable ' + - 'chr classmethod cmp coerce compile complex delattr dict dir ' + - 'divmod enumerate eval execfile file filter float format frozenset ' + - 'getattr globals hasattr hash help hex id input int intern ' + - 'isinstance issubclass iter len list locals long map max min next ' + - 'object oct open ord pow print property range raw_input reduce ' + - 'reload repr reversed round set setattr slice sorted staticmethod ' + - 'str sum super tuple type type unichr unicode vars xrange zip'; - - var special = 'None True False self cls class_'; - - this.regexList = [ - { regex: SyntaxHighlighter.regexLib.singleLinePerlComments, css: 'comments' }, - { regex: /^\s*@\w+/gm, css: 'decorator' }, - { regex: /(['\"]{3})([^\1])*?\1/gm, css: 'comments' }, - { regex: /"(?!")(?:\.|\\\"|[^\""\n])*"/gm, css: 'string' }, - { regex: /'(?!')(?:\.|(\\\')|[^\''\n])*'/gm, css: 'string' }, - { regex: /\+|\-|\*|\/|\%|=|==/gm, css: 'keyword' }, - { regex: /\b\d+\.?\w*/g, css: 'value' }, - { regex: new RegExp(this.getKeywords(funcs), 'gmi'), css: 'functions' }, - { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, - { regex: new RegExp(this.getKeywords(special), 'gm'), css: 'color1' } - ]; - - this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags); - }; - - Brush.prototype = new SyntaxHighlighter.Highlighter(); - Brush.aliases = ['py', 'python']; - - SyntaxHighlighter.brushes.Python = Brush; - - // CommonJS - typeof(exports) != 'undefined' ? exports.Brush = Brush : null; -})(); diff --git a/pecan/middleware/resources/pecan.png b/pecan/middleware/resources/pecan.png Binary files differdeleted file mode 100644 index 16f2490..0000000 --- a/pecan/middleware/resources/pecan.png +++ /dev/null diff --git a/pecan/middleware/resources/shCore.js b/pecan/middleware/resources/shCore.js deleted file mode 100644 index 320e0b5..0000000 --- a/pecan/middleware/resources/shCore.js +++ /dev/null @@ -1,1731 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * @VERSION@ (@DATE@) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - -// -// Begin anonymous function. This is used to contain local scope variables without polutting global scope. -// -if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() { - -// CommonJS -if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined') -{ - XRegExp = require('XRegExp').XRegExp; -} - -// Shortcut object which will be assigned to the SyntaxHighlighter variable. -// This is a shorthand for local reference in order to avoid long namespace -// references to SyntaxHighlighter.whatever... -var sh = { - defaults : { - /** Additional CSS class names to be added to highlighter elements. */ - 'class-name' : '', - - /** First line number. */ - 'first-line' : 1, - - /** - * Pads line numbers. Possible values are: - * - * false - don't pad line numbers. - * true - automaticaly pad numbers with minimum required number of leading zeroes. - * [int] - length up to which pad line numbers. - */ - 'pad-line-numbers' : false, - - /** Lines to highlight. */ - 'highlight' : null, - - /** Title to be displayed above the code block. */ - 'title' : null, - - /** Enables or disables smart tabs. */ - 'smart-tabs' : true, - - /** Gets or sets tab size. */ - 'tab-size' : 4, - - /** Enables or disables gutter. */ - 'gutter' : true, - - /** Enables or disables toolbar. */ - 'toolbar' : true, - - /** Enables quick code copy and paste from double click. */ - 'quick-code' : true, - - /** Forces code view to be collapsed. */ - 'collapse' : false, - - /** Enables or disables automatic links. */ - 'auto-links' : true, - - /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */ - 'light' : false, - - 'unindent' : true, - - 'html-script' : false - }, - - config : { - space : ' ', - - /** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */ - useScriptTags : true, - - /** Blogger mode flag. */ - bloggerMode : false, - - stripBrs : false, - - /** Name of the tag that SyntaxHighlighter will automatically look for. */ - tagName : 'pre', - - strings : { - expandSource : 'expand source', - help : '?', - alert: 'SyntaxHighlighter\n\n', - noBrush : 'Can\'t find brush for: ', - brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ', - - // this is populated by the build script - aboutDialog : '@ABOUT@' - } - }, - - /** Internal 'global' variables. */ - vars : { - discoveredBrushes : null, - highlighters : {} - }, - - /** This object is populated by user included external brush files. */ - brushes : {}, - - /** Common regular expressions. */ - regexLib : { - multiLineCComments : /\/\*[\s\S]*?\*\//gm, - singleLineCComments : /\/\/.*$/gm, - singleLinePerlComments : /#.*$/gm, - doubleQuotedString : /"([^\\"\n]|\\.)*"/g, - singleQuotedString : /'([^\\'\n]|\\.)*'/g, - multiLineDoubleQuotedString : new XRegExp('"([^\\\\"]|\\\\.)*"', 'gs'), - multiLineSingleQuotedString : new XRegExp("'([^\\\\']|\\\\.)*'", 'gs'), - xmlComments : /(<|<)!--[\s\S]*?--(>|>)/gm, - url : /\w+:\/\/[\w-.\/?%&=:@;#]*/g, - - /** <?= ?> tags. */ - phpScriptTags : { left: /(<|<)\?(?:=|php)?/g, right: /\?(>|>)/g, 'eof' : true }, - - /** <%= %> tags. */ - aspScriptTags : { left: /(<|<)%=?/g, right: /%(>|>)/g }, - - /** <script> tags. */ - scriptScriptTags : { left: /(<|<)\s*script.*?(>|>)/gi, right: /(<|<)\/\s*script\s*(>|>)/gi } - }, - - toolbar: { - /** - * Generates HTML markup for the toolbar. - * @param {Highlighter} highlighter Highlighter instance. - * @return {String} Returns HTML markup. - */ - getHtml: function(highlighter) - { - var html = '<div class="toolbar">', - items = sh.toolbar.items, - list = items.list - ; - - function defaultGetHtml(highlighter, name) - { - return sh.toolbar.getButtonHtml(highlighter, name, sh.config.strings[name]); - }; - - for (var i = 0; i < list.length; i++) - html += (items[list[i]].getHtml || defaultGetHtml)(highlighter, list[i]); - - html += '</div>'; - - return html; - }, - - /** - * Generates HTML markup for a regular button in the toolbar. - * @param {Highlighter} highlighter Highlighter instance. - * @param {String} commandName Command name that would be executed. - * @param {String} label Label text to display. - * @return {String} Returns HTML markup. - */ - getButtonHtml: function(highlighter, commandName, label) - { - return '<span><a href="#" class="toolbar_item' - + ' command_' + commandName - + ' ' + commandName - + '">' + label + '</a></span>' - ; - }, - - /** - * Event handler for a toolbar anchor. - */ - handler: function(e) - { - var target = e.target, - className = target.className || '' - ; - - function getValue(name) - { - var r = new RegExp(name + '_(\\w+)'), - match = r.exec(className) - ; - - return match ? match[1] : null; - }; - - var highlighter = getHighlighterById(findParentElement(target, '.syntaxhighlighter').id), - commandName = getValue('command') - ; - - // execute the toolbar command - if (highlighter && commandName) - sh.toolbar.items[commandName].execute(highlighter); - - // disable default A click behaviour - e.preventDefault(); - }, - - /** Collection of toolbar items. */ - items : { - // Ordered lis of items in the toolbar. Can't expect `for (var n in items)` to be consistent. - list: ['expandSource', 'help'], - - expandSource: { - getHtml: function(highlighter) - { - if (highlighter.getParam('collapse') != true) - return ''; - - var title = highlighter.getParam('title'); - return sh.toolbar.getButtonHtml(highlighter, 'expandSource', title ? title : sh.config.strings.expandSource); - }, - - execute: function(highlighter) - { - var div = getHighlighterDivById(highlighter.id); - removeClass(div, 'collapsed'); - } - }, - - /** Command to display the about dialog window. */ - help: { - execute: function(highlighter) - { - var wnd = popup('', '_blank', 500, 250, 'scrollbars=0'), - doc = wnd.document - ; - - doc.write(sh.config.strings.aboutDialog); - doc.close(); - wnd.focus(); - } - } - } - }, - - /** - * Finds all elements on the page which should be processes by SyntaxHighlighter. - * - * @param {Object} globalParams Optional parameters which override element's - * parameters. Only used if element is specified. - * - * @param {Object} element Optional element to highlight. If none is - * provided, all elements in the current document - * are returned which qualify. - * - * @return {Array} Returns list of <code>{ target: DOMElement, params: Object }</code> objects. - */ - findElements: function(globalParams, element) - { - var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)), - conf = sh.config, - result = [] - ; - - // support for <SCRIPT TYPE="syntaxhighlighter" /> feature - if (conf.useScriptTags) - elements = elements.concat(getSyntaxHighlighterScriptTags()); - - if (elements.length === 0) - return result; - - for (var i = 0; i < elements.length; i++) - { - var item = { - target: elements[i], - // local params take precedence over globals - params: merge(globalParams, parseParams(elements[i].className)) - }; - - if (item.params['brush'] == null) - continue; - - result.push(item); - } - - return result; - }, - - /** - * Shorthand to highlight all elements on the page that are marked as - * SyntaxHighlighter source code. - * - * @param {Object} globalParams Optional parameters which override element's - * parameters. Only used if element is specified. - * - * @param {Object} element Optional element to highlight. If none is - * provided, all elements in the current document - * are highlighted. - */ - highlight: function(globalParams, element) - { - var elements = this.findElements(globalParams, element), - propertyName = 'innerHTML', - highlighter = null, - conf = sh.config - ; - - if (elements.length === 0) - return; - - for (var i = 0; i < elements.length; i++) - { - var element = elements[i], - target = element.target, - params = element.params, - brushName = params.brush, - code - ; - - if (brushName == null) - continue; - - // Instantiate a brush - if (params['html-script'] == 'true' || sh.defaults['html-script'] == true) - { - highlighter = new sh.HtmlScript(brushName); - brushName = 'htmlscript'; - } - else - { - var brush = findBrush(brushName); - - if (brush) - highlighter = new brush(); - else - continue; - } - - code = target[propertyName]; - - // remove CDATA from <SCRIPT/> tags if it's present - if (conf.useScriptTags) - code = stripCData(code); - - // Inject title if the attribute is present - if ((target.title || '') != '') - params.title = target.title; - - params['brush'] = brushName; - highlighter.init(params); - element = highlighter.getDiv(code); - - // carry over ID - if ((target.id || '') != '') - element.id = target.id; - - target.parentNode.replaceChild(element, target); - } - }, - - /** - * Main entry point for the SyntaxHighlighter. - * @param {Object} params Optional params to apply to all highlighted elements. - */ - all: function(params) - { - attachEvent( - window, - 'load', - function() { sh.highlight(params); } - ); - } -}; // end of sh - -/** - * Checks if target DOM elements has specified CSS class. - * @param {DOMElement} target Target DOM element to check. - * @param {String} className Name of the CSS class to check for. - * @return {Boolean} Returns true if class name is present, false otherwise. - */ -function hasClass(target, className) -{ - return target.className.indexOf(className) != -1; -}; - -/** - * Adds CSS class name to the target DOM element. - * @param {DOMElement} target Target DOM element. - * @param {String} className New CSS class to add. - */ -function addClass(target, className) -{ - if (!hasClass(target, className)) - target.className += ' ' + className; -}; - -/** - * Removes CSS class name from the target DOM element. - * @param {DOMElement} target Target DOM element. - * @param {String} className CSS class to remove. - */ -function removeClass(target, className) -{ - target.className = target.className.replace(className, ''); -}; - -/** - * Converts the source to array object. Mostly used for function arguments and - * lists returned by getElementsByTagName() which aren't Array objects. - * @param {List} source Source list. - * @return {Array} Returns array. - */ -function toArray(source) -{ - var result = []; - - for (var i = 0; i < source.length; i++) - result.push(source[i]); - - return result; -}; - -/** - * Splits block of text into lines. - * @param {String} block Block of text. - * @return {Array} Returns array of lines. - */ -function splitLines(block) -{ - return block.split(/\r?\n/); -} - -/** - * Generates HTML ID for the highlighter. - * @param {String} highlighterId Highlighter ID. - * @return {String} Returns HTML ID. - */ -function getHighlighterId(id) -{ - var prefix = 'highlighter_'; - return id.indexOf(prefix) == 0 ? id : prefix + id; -}; - -/** - * Finds Highlighter instance by ID. - * @param {String} highlighterId Highlighter ID. - * @return {Highlighter} Returns instance of the highlighter. - */ -function getHighlighterById(id) -{ - return sh.vars.highlighters[getHighlighterId(id)]; -}; - -/** - * Finds highlighter's DIV container. - * @param {String} highlighterId Highlighter ID. - * @return {Element} Returns highlighter's DIV element. - */ -function getHighlighterDivById(id) -{ - return document.getElementById(getHighlighterId(id)); -}; - -/** - * Stores highlighter so that getHighlighterById() can do its thing. Each - * highlighter must call this method to preserve itself. - * @param {Highilghter} highlighter Highlighter instance. - */ -function storeHighlighter(highlighter) -{ - sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter; -}; - -/** - * Looks for a child or parent node which has specified classname. - * Equivalent to jQuery's $(container).find(".className") - * @param {Element} target Target element. - * @param {String} search Class name or node name to look for. - * @param {Boolean} reverse If set to true, will go up the node tree instead of down. - * @return {Element} Returns found child or parent element on null. - */ -function findElement(target, search, reverse /* optional */) -{ - if (target == null) - return null; - - var nodes = reverse != true ? target.childNodes : [ target.parentNode ], - propertyToFind = { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName', - expectedValue, - found - ; - - expectedValue = propertyToFind != 'nodeName' - ? search.substr(1) - : search.toUpperCase() - ; - - // main return of the found node - if ((target[propertyToFind] || '').indexOf(expectedValue) != -1) - return target; - - for (var i = 0; nodes && i < nodes.length && found == null; i++) - found = findElement(nodes[i], search, reverse); - - return found; -}; - -/** - * Looks for a parent node which has specified classname. - * This is an alias to <code>findElement(container, className, true)</code>. - * @param {Element} target Target element. - * @param {String} className Class name to look for. - * @return {Element} Returns found parent element on null. - */ -function findParentElement(target, className) -{ - return findElement(target, className, true); -}; - -/** - * Finds an index of element in the array. - * @ignore - * @param {Object} searchElement - * @param {Number} fromIndex - * @return {Number} Returns index of element if found; -1 otherwise. - */ -function indexOf(array, searchElement, fromIndex) -{ - fromIndex = Math.max(fromIndex || 0, 0); - - for (var i = fromIndex; i < array.length; i++) - if(array[i] == searchElement) - return i; - - return -1; -}; - -/** - * Generates a unique element ID. - */ -function guid(prefix) -{ - return (prefix || '') + Math.round(Math.random() * 1000000).toString(); -}; - -/** - * Merges two objects. Values from obj2 override values in obj1. - * Function is NOT recursive and works only for one dimensional objects. - * @param {Object} obj1 First object. - * @param {Object} obj2 Second object. - * @return {Object} Returns combination of both objects. - */ -function merge(obj1, obj2) -{ - var result = {}, name; - - for (name in obj1) - result[name] = obj1[name]; - - for (name in obj2) - result[name] = obj2[name]; - - return result; -}; - -/** - * Attempts to convert string to boolean. - * @param {String} value Input string. - * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise. - */ -function toBoolean(value) -{ - var result = { "true" : true, "false" : false }[value]; - return result == null ? value : result; -}; - -/** - * Opens up a centered popup window. - * @param {String} url URL to open in the window. - * @param {String} name Popup name. - * @param {int} width Popup width. - * @param {int} height Popup height. - * @param {String} options window.open() options. - * @return {Window} Returns window instance. - */ -function popup(url, name, width, height, options) -{ - var x = (screen.width - width) / 2, - y = (screen.height - height) / 2 - ; - - options += ', left=' + x + - ', top=' + y + - ', width=' + width + - ', height=' + height - ; - options = options.replace(/^,/, ''); - - var win = window.open(url, name, options); - win.focus(); - return win; -}; - -/** - * Adds event handler to the target object. - * @param {Object} obj Target object. - * @param {String} type Name of the event. - * @param {Function} func Handling function. - */ -function attachEvent(obj, type, func, scope) -{ - function handler(e) - { - e = e || window.event; - - if (!e.target) - { - e.target = e.srcElement; - e.preventDefault = function() - { - this.returnValue = false; - }; - } - - func.call(scope || window, e); - }; - - if (obj.attachEvent) - { - obj.attachEvent('on' + type, handler); - } - else - { - obj.addEventListener(type, handler, false); - } -}; - -/** - * Displays an alert. - * @param {String} str String to display. - */ -function alert(str) -{ - window.alert(sh.config.strings.alert + str); -}; - -/** - * Finds a brush by its alias. - * - * @param {String} alias Brush alias. - * @param {Boolean} showAlert Suppresses the alert if false. - * @return {Brush} Returns bursh constructor if found, null otherwise. - */ -function findBrush(alias, showAlert) -{ - var brushes = sh.vars.discoveredBrushes, - result = null - ; - - if (brushes == null) - { - brushes = {}; - - // Find all brushes - for (var brush in sh.brushes) - { - var info = sh.brushes[brush], - aliases = info.aliases - ; - - if (aliases == null) - continue; - - // keep the brush name - info.brushName = brush.toLowerCase(); - - for (var i = 0; i < aliases.length; i++) - brushes[aliases[i]] = brush; - } - - sh.vars.discoveredBrushes = brushes; - } - - result = sh.brushes[brushes[alias]]; - - if (result == null && showAlert) - alert(sh.config.strings.noBrush + alias); - - return result; -}; - -/** - * Executes a callback on each line and replaces each line with result from the callback. - * @param {Object} str Input string. - * @param {Object} callback Callback function taking one string argument and returning a string. - */ -function eachLine(str, callback) -{ - var lines = splitLines(str); - - for (var i = 0; i < lines.length; i++) - lines[i] = callback(lines[i], i); - - // include \r to enable copy-paste on windows (ie8) without getting everything on one line - return lines.join('\r\n'); -}; - -/** - * This is a special trim which only removes first and last empty lines - * and doesn't affect valid leading space on the first line. - * - * @param {String} str Input string - * @return {String} Returns string without empty first and last lines. - */ -function trimFirstAndLastLines(str) -{ - return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, ''); -}; - -/** - * Parses key/value pairs into hash object. - * - * Understands the following formats: - * - name: word; - * - name: [word, word]; - * - name: "string"; - * - name: 'string'; - * - * For example: - * name1: value; name2: [value, value]; name3: 'value' - * - * @param {String} str Input string. - * @return {Object} Returns deserialized object. - */ -function parseParams(str) -{ - var match, - result = {}, - arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"), - regex = new XRegExp( - "(?<name>[\\w-]+)" + - "\\s*:\\s*" + - "(?<value>" + - "[\\w-%#]+|" + // word - "\\[.*?\\]|" + // [] array - '".*?"|' + // "" string - "'.*?'" + // '' string - ")\\s*;?", - "g" - ) - ; - - while ((match = regex.exec(str)) != null) - { - var value = match.value - .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings - ; - - // try to parse array value - if (value != null && arrayRegex.test(value)) - { - var m = arrayRegex.exec(value); - value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : []; - } - - result[match.name] = value; - } - - return result; -}; - -/** - * Wraps each line of the string into <code/> tag with given style applied to it. - * - * @param {String} str Input string. - * @param {String} css Style name to apply to the string. - * @return {String} Returns input string with each line surrounded by <span/> tag. - */ -function wrapLinesWithCode(str, css) -{ - if (str == null || str.length == 0 || str == '\n') - return str; - - str = str.replace(/</g, '<'); - - // Replace two or more sequential spaces with leaving last space untouched. - str = str.replace(/ {2,}/g, function(m) - { - var spaces = ''; - - for (var i = 0; i < m.length - 1; i++) - spaces += sh.config.space; - - return spaces + ' '; - }); - - // Split each line and apply <span class="...">...</span> to them so that - // leading spaces aren't included. - if (css != null) - str = eachLine(str, function(line) - { - if (line.length == 0) - return ''; - - var spaces = ''; - - line = line.replace(/^( | )+/, function(s) - { - spaces = s; - return ''; - }); - - if (line.length == 0) - return spaces; - - return spaces + '<code class="' + css + '">' + line + '</code>'; - }); - - return str; -}; - -/** - * Pads number with zeros until it's length is the same as given length. - * - * @param {Number} number Number to pad. - * @param {Number} length Max string length with. - * @return {String} Returns a string padded with proper amount of '0'. - */ -function padNumber(number, length) -{ - var result = number.toString(); - - while (result.length < length) - result = '0' + result; - - return result; -}; - -/** - * Replaces tabs with spaces. - * - * @param {String} code Source code. - * @param {Number} tabSize Size of the tab. - * @return {String} Returns code with all tabs replaces by spaces. - */ -function processTabs(code, tabSize) -{ - var tab = ''; - - for (var i = 0; i < tabSize; i++) - tab += ' '; - - return code.replace(/\t/g, tab); -}; - -/** - * Replaces tabs with smart spaces. - * - * @param {String} code Code to fix the tabs in. - * @param {Number} tabSize Number of spaces in a column. - * @return {String} Returns code with all tabs replaces with roper amount of spaces. - */ -function processSmartTabs(code, tabSize) -{ - var lines = splitLines(code), - tab = '\t', - spaces = '' - ; - - // Create a string with 1000 spaces to copy spaces from... - // It's assumed that there would be no indentation longer than that. - for (var i = 0; i < 50; i++) - spaces += ' '; // 20 spaces * 50 - - // This function inserts specified amount of spaces in the string - // where a tab is while removing that given tab. - function insertSpaces(line, pos, count) - { - return line.substr(0, pos) - + spaces.substr(0, count) - + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab - ; - }; - - // Go through all the lines and do the 'smart tabs' magic. - code = eachLine(code, function(line) - { - if (line.indexOf(tab) == -1) - return line; - - var pos = 0; - - while ((pos = line.indexOf(tab)) != -1) - { - // This is pretty much all there is to the 'smart tabs' logic. - // Based on the position within the line and size of a tab, - // calculate the amount of spaces we need to insert. - var spaces = tabSize - pos % tabSize; - line = insertSpaces(line, pos, spaces); - } - - return line; - }); - - return code; -}; - -/** - * Performs various string fixes based on configuration. - */ -function fixInputString(str) -{ - var br = /<br\s*\/?>|<br\s*\/?>/gi; - - if (sh.config.bloggerMode == true) - str = str.replace(br, '\n'); - - if (sh.config.stripBrs == true) - str = str.replace(br, ''); - - return str; -}; - -/** - * Removes all white space at the beginning and end of a string. - * - * @param {String} str String to trim. - * @return {String} Returns string without leading and following white space characters. - */ -function trim(str) -{ - return str.replace(/^\s+|\s+$/g, ''); -}; - -/** - * Unindents a block of text by the lowest common indent amount. - * @param {String} str Text to unindent. - * @return {String} Returns unindented text block. - */ -function unindent(str) -{ - var lines = splitLines(fixInputString(str)), - indents = new Array(), - regex = /^\s*/, - min = 1000 - ; - - // go through every line and check for common number of indents - for (var i = 0; i < lines.length && min > 0; i++) - { - var line = lines[i]; - - if (trim(line).length == 0) - continue; - - var matches = regex.exec(line); - - // In the event that just one line doesn't have leading white space - // we can't unindent anything, so bail completely. - if (matches == null) - return str; - - min = Math.min(matches[0].length, min); - } - - // trim minimum common number of white space from the beginning of every line - if (min > 0) - for (var i = 0; i < lines.length; i++) - lines[i] = lines[i].substr(min); - - return lines.join('\n'); -}; - -/** - * Callback method for Array.sort() which sorts matches by - * index position and then by length. - * - * @param {Match} m1 Left object. - * @param {Match} m2 Right object. - * @return {Number} Returns -1, 0 or -1 as a comparison result. - */ -function matchesSortCallback(m1, m2) -{ - // sort matches by index first - if(m1.index < m2.index) - return -1; - else if(m1.index > m2.index) - return 1; - else - { - // if index is the same, sort by length - if(m1.length < m2.length) - return -1; - else if(m1.length > m2.length) - return 1; - } - - return 0; -}; - -/** - * Executes given regular expression on provided code and returns all - * matches that are found. - * - * @param {String} code Code to execute regular expression on. - * @param {Object} regex Regular expression item info from <code>regexList</code> collection. - * @return {Array} Returns a list of Match objects. - */ -function getMatches(code, regexInfo) -{ - function defaultAdd(match, regexInfo) - { - return match[0]; - }; - - var index = 0, - match = null, - matches = [], - func = regexInfo.func ? regexInfo.func : defaultAdd - ; - - while((match = regexInfo.regex.exec(code)) != null) - { - var resultMatch = func(match, regexInfo); - - if (typeof(resultMatch) == 'string') - resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)]; - - matches = matches.concat(resultMatch); - } - - return matches; -}; - -/** - * Turns all URLs in the code into <a/> tags. - * @param {String} code Input code. - * @return {String} Returns code with </a> tags. - */ -function processUrls(code) -{ - var gt = /(.*)((>|<).*)/; - - return code.replace(sh.regexLib.url, function(m) - { - var suffix = '', - match = null - ; - - // We include < and > in the URL for the common cases like <http://google.com> - // The problem is that they get transformed into <http://google.com> - // Where as > easily looks like part of the URL string. - - if (match = gt.exec(m)) - { - m = match[1]; - suffix = match[2]; - } - - return '<a href="' + m + '">' + m + '</a>' + suffix; - }); -}; - -/** - * Finds all <SCRIPT TYPE="syntaxhighlighter" /> elementss. - * @return {Array} Returns array of all found SyntaxHighlighter tags. - */ -function getSyntaxHighlighterScriptTags() -{ - var tags = document.getElementsByTagName('script'), - result = [] - ; - - for (var i = 0; i < tags.length; i++) - if (tags[i].type == 'syntaxhighlighter') - result.push(tags[i]); - - return result; -}; - -/** - * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used - * there in most cases for XHTML compliance. - * @param {String} original Input code. - * @return {String} Returns code without leading <![CDATA[]]> tags. - */ -function stripCData(original) -{ - var left = '<![CDATA[', - right = ']]>', - // for some reason IE inserts some leading blanks here - copy = trim(original), - changed = false, - leftLength = left.length, - rightLength = right.length - ; - - if (copy.indexOf(left) == 0) - { - copy = copy.substring(leftLength); - changed = true; - } - - var copyLength = copy.length; - - if (copy.indexOf(right) == copyLength - rightLength) - { - copy = copy.substring(0, copyLength - rightLength); - changed = true; - } - - return changed ? copy : original; -}; - - -/** - * Quick code mouse double click handler. - */ -function quickCodeHandler(e) -{ - var target = e.target, - highlighterDiv = findParentElement(target, '.syntaxhighlighter'), - container = findParentElement(target, '.container'), - textarea = document.createElement('textarea'), - highlighter - ; - - if (!container || !highlighterDiv || findElement(container, 'textarea')) - return; - - highlighter = getHighlighterById(highlighterDiv.id); - - // add source class name - addClass(highlighterDiv, 'source'); - - // Have to go over each line and grab it's text, can't just do it on the - // container because Firefox loses all \n where as Webkit doesn't. - var lines = container.childNodes, - code = [] - ; - - for (var i = 0; i < lines.length; i++) - code.push(lines[i].innerText || lines[i].textContent); - - // using \r instead of \r or \r\n makes this work equally well on IE, FF and Webkit - code = code.join('\r'); - - // For Webkit browsers, replace nbsp with a breaking space - code = code.replace(/\u00a0/g, " "); - - // inject <textarea/> tag - textarea.appendChild(document.createTextNode(code)); - container.appendChild(textarea); - - // preselect all text - textarea.focus(); - textarea.select(); - - // set up handler for lost focus - attachEvent(textarea, 'blur', function(e) - { - textarea.parentNode.removeChild(textarea); - removeClass(highlighterDiv, 'source'); - }); -}; - -/** - * Match object. - */ -sh.Match = function(value, index, css) -{ - this.value = value; - this.index = index; - this.length = value.length; - this.css = css; - this.brushName = null; -}; - -sh.Match.prototype.toString = function() -{ - return this.value; -}; - -/** - * Simulates HTML code with a scripting language embedded. - * - * @param {String} scriptBrushName Brush name of the scripting language. - */ -sh.HtmlScript = function(scriptBrushName) -{ - var brushClass = findBrush(scriptBrushName), - scriptBrush, - xmlBrush = new sh.brushes.Xml(), - bracketsRegex = null, - ref = this, - methodsToExpose = 'getDiv getHtml init'.split(' ') - ; - - if (brushClass == null) - return; - - scriptBrush = new brushClass(); - - for(var i = 0; i < methodsToExpose.length; i++) - // make a closure so we don't lose the name after i changes - (function() { - var name = methodsToExpose[i]; - - ref[name] = function() - { - return xmlBrush[name].apply(xmlBrush, arguments); - }; - })(); - - if (scriptBrush.htmlScript == null) - { - alert(sh.config.strings.brushNotHtmlScript + scriptBrushName); - return; - } - - xmlBrush.regexList.push( - { regex: scriptBrush.htmlScript.code, func: process } - ); - - function offsetMatches(matches, offset) - { - for (var j = 0; j < matches.length; j++) - matches[j].index += offset; - } - - function process(match, info) - { - var code = match.code, - matches = [], - regexList = scriptBrush.regexList, - offset = match.index + match.left.length, - htmlScript = scriptBrush.htmlScript, - result - ; - - // add all matches from the code - for (var i = 0; i < regexList.length; i++) - { - result = getMatches(code, regexList[i]); - offsetMatches(result, offset); - matches = matches.concat(result); - } - - // add left script bracket - if (htmlScript.left != null && match.left != null) - { - result = getMatches(match.left, htmlScript.left); - offsetMatches(result, match.index); - matches = matches.concat(result); - } - - // add right script bracket - if (htmlScript.right != null && match.right != null) - { - result = getMatches(match.right, htmlScript.right); - offsetMatches(result, match.index + match[0].lastIndexOf(match.right)); - matches = matches.concat(result); - } - - for (var j = 0; j < matches.length; j++) - matches[j].brushName = brushClass.brushName; - - return matches; - } -}; - -/** - * Main Highlither class. - * @constructor - */ -sh.Highlighter = function() -{ - // not putting any code in here because of the prototype inheritance -}; - -sh.Highlighter.prototype = { - /** - * Returns value of the parameter passed to the highlighter. - * @param {String} name Name of the parameter. - * @param {Object} defaultValue Default value. - * @return {Object} Returns found value or default value otherwise. - */ - getParam: function(name, defaultValue) - { - var result = this.params[name]; - return toBoolean(result == null ? defaultValue : result); - }, - - /** - * Shortcut to document.createElement(). - * @param {String} name Name of the element to create (DIV, A, etc). - * @return {HTMLElement} Returns new HTML element. - */ - create: function(name) - { - return document.createElement(name); - }, - - /** - * Applies all regular expression to the code and stores all found - * matches in the `this.matches` array. - * @param {Array} regexList List of regular expressions. - * @param {String} code Source code. - * @return {Array} Returns list of matches. - */ - findMatches: function(regexList, code) - { - var result = []; - - if (regexList != null) - for (var i = 0; i < regexList.length; i++) - // BUG: length returns len+1 for array if methods added to prototype chain (oising@gmail.com) - if (typeof (regexList[i]) == "object") - result = result.concat(getMatches(code, regexList[i])); - - // sort and remove nested the matches - return this.removeNestedMatches(result.sort(matchesSortCallback)); - }, - - /** - * Checks to see if any of the matches are inside of other matches. - * This process would get rid of highligted strings inside comments, - * keywords inside strings and so on. - */ - removeNestedMatches: function(matches) - { - // Optimized by Jose Prado (http://joseprado.com) - for (var i = 0; i < matches.length; i++) - { - if (matches[i] === null) - continue; - - var itemI = matches[i], - itemIEndPos = itemI.index + itemI.length - ; - - for (var j = i + 1; j < matches.length && matches[i] !== null; j++) - { - var itemJ = matches[j]; - - if (itemJ === null) - continue; - else if (itemJ.index > itemIEndPos) - break; - else if (itemJ.index == itemI.index && itemJ.length > itemI.length) - matches[i] = null; - else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos) - matches[j] = null; - } - } - - return matches; - }, - - /** - * Creates an array containing integer line numbers starting from the 'first-line' param. - * @return {Array} Returns array of integers. - */ - figureOutLineNumbers: function(code) - { - var lines = [], - firstLine = parseInt(this.getParam('first-line')) - ; - - eachLine(code, function(line, index) - { - lines.push(index + firstLine); - }); - - return lines; - }, - - /** - * Determines if specified line number is in the highlighted list. - */ - isLineHighlighted: function(lineNumber) - { - var list = this.getParam('highlight', []); - - if (typeof(list) != 'object' && list.push == null) - list = [ list ]; - - return indexOf(list, lineNumber.toString()) != -1; - }, - - /** - * Generates HTML markup for a single line of code while determining alternating line style. - * @param {Integer} lineNumber Line number. - * @param {String} code Line HTML markup. - * @return {String} Returns HTML markup. - */ - getLineHtml: function(lineIndex, lineNumber, code) - { - var classes = [ - 'line', - 'number' + lineNumber, - 'index' + lineIndex, - 'alt' + (lineNumber % 2 == 0 ? 1 : 2).toString() - ]; - - if (this.isLineHighlighted(lineNumber)) - classes.push('highlighted'); - - if (lineNumber == 0) - classes.push('break'); - - return '<div class="' + classes.join(' ') + '">' + code + '</div>'; - }, - - /** - * Generates HTML markup for line number column. - * @param {String} code Complete code HTML markup. - * @param {Array} lineNumbers Calculated line numbers. - * @return {String} Returns HTML markup. - */ - getLineNumbersHtml: function(code, lineNumbers) - { - var html = '', - count = splitLines(code).length, - firstLine = parseInt(this.getParam('first-line')), - pad = this.getParam('pad-line-numbers') - ; - - if (pad == true) - pad = (firstLine + count - 1).toString().length; - else if (isNaN(pad) == true) - pad = 0; - - for (var i = 0; i < count; i++) - { - var lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i, - code = lineNumber == 0 ? sh.config.space : padNumber(lineNumber, pad) - ; - - html += this.getLineHtml(i, lineNumber, code); - } - - return html; - }, - - /** - * Splits block of text into individual DIV lines. - * @param {String} code Code to highlight. - * @param {Array} lineNumbers Calculated line numbers. - * @return {String} Returns highlighted code in HTML form. - */ - getCodeLinesHtml: function(html, lineNumbers) - { - html = trim(html); - - var lines = splitLines(html), - padLength = this.getParam('pad-line-numbers'), - firstLine = parseInt(this.getParam('first-line')), - html = '', - brushName = this.getParam('brush') - ; - - for (var i = 0; i < lines.length; i++) - { - var line = lines[i], - indent = /^( |\s)+/.exec(line), - spaces = null, - lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i; - ; - - if (indent != null) - { - spaces = indent[0].toString(); - line = line.substr(spaces.length); - spaces = spaces.replace(' ', sh.config.space); - } - - line = trim(line); - - if (line.length == 0) - line = sh.config.space; - - html += this.getLineHtml( - i, - lineNumber, - (spaces != null ? '<code class="' + brushName + ' spaces">' + spaces + '</code>' : '') + line - ); - } - - return html; - }, - - /** - * Returns HTML for the table title or empty string if title is null. - */ - getTitleHtml: function(title) - { - return title ? '<caption>' + title + '</caption>' : ''; - }, - - /** - * Finds all matches in the source code. - * @param {String} code Source code to process matches in. - * @param {Array} matches Discovered regex matches. - * @return {String} Returns formatted HTML with processed mathes. - */ - getMatchesHtml: function(code, matches) - { - var pos = 0, - result = '', - brushName = this.getParam('brush', '') - ; - - function getBrushNameCss(match) - { - var result = match ? (match.brushName || brushName) : brushName; - return result ? result + ' ' : ''; - }; - - // Finally, go through the final list of matches and pull the all - // together adding everything in between that isn't a match. - for (var i = 0; i < matches.length; i++) - { - var match = matches[i], - matchBrushName - ; - - if (match === null || match.length === 0) - continue; - - matchBrushName = getBrushNameCss(match); - - result += wrapLinesWithCode(code.substr(pos, match.index - pos), matchBrushName + 'plain') - + wrapLinesWithCode(match.value, matchBrushName + match.css) - ; - - pos = match.index + match.length + (match.offset || 0); - } - - // don't forget to add whatever's remaining in the string - result += wrapLinesWithCode(code.substr(pos), getBrushNameCss() + 'plain'); - - return result; - }, - - /** - * Generates HTML markup for the whole syntax highlighter. - * @param {String} code Source code. - * @return {String} Returns HTML markup. - */ - getHtml: function(code) - { - var html = '', - classes = [ 'syntaxhighlighter' ], - tabSize, - matches, - lineNumbers - ; - - // process light mode - if (this.getParam('light') == true) - this.params.toolbar = this.params.gutter = false; - - className = 'syntaxhighlighter'; - - if (this.getParam('collapse') == true) - classes.push('collapsed'); - - if ((gutter = this.getParam('gutter')) == false) - classes.push('nogutter'); - - // add custom user style name - classes.push(this.getParam('class-name')); - - // add brush alias to the class name for custom CSS - classes.push(this.getParam('brush')); - - code = trimFirstAndLastLines(code) - .replace(/\r/g, ' ') // IE lets these buggers through - ; - - tabSize = this.getParam('tab-size'); - - // replace tabs with spaces - code = this.getParam('smart-tabs') == true - ? processSmartTabs(code, tabSize) - : processTabs(code, tabSize) - ; - - // unindent code by the common indentation - if (this.getParam('unindent')) - code = unindent(code); - - if (gutter) - lineNumbers = this.figureOutLineNumbers(code); - - // find matches in the code using brushes regex list - matches = this.findMatches(this.regexList, code); - // processes found matches into the html - html = this.getMatchesHtml(code, matches); - // finally, split all lines so that they wrap well - html = this.getCodeLinesHtml(html, lineNumbers); - - // finally, process the links - if (this.getParam('auto-links')) - html = processUrls(html); - - if (typeof(navigator) != 'undefined' && navigator.userAgent && navigator.userAgent.match(/MSIE/)) - classes.push('ie'); - - html = - '<div id="' + getHighlighterId(this.id) + '" class="' + classes.join(' ') + '">' - + (this.getParam('toolbar') ? sh.toolbar.getHtml(this) : '') - + '<table border="0" cellpadding="0" cellspacing="0">' - + this.getTitleHtml(this.getParam('title')) - + '<tbody>' - + '<tr>' - + (gutter ? '<td class="gutter">' + this.getLineNumbersHtml(code) + '</td>' : '') - + '<td class="code">' - + '<div class="container">' - + html - + '</div>' - + '</td>' - + '</tr>' - + '</tbody>' - + '</table>' - + '</div>' - ; - - return html; - }, - - /** - * Highlights the code and returns complete HTML. - * @param {String} code Code to highlight. - * @return {Element} Returns container DIV element with all markup. - */ - getDiv: function(code) - { - if (code === null) - code = ''; - - this.code = code; - - var div = this.create('div'); - - // create main HTML - div.innerHTML = this.getHtml(code); - - // set up click handlers - if (this.getParam('toolbar')) - attachEvent(findElement(div, '.toolbar'), 'click', sh.toolbar.handler); - - if (this.getParam('quick-code')) - attachEvent(findElement(div, '.code'), 'dblclick', quickCodeHandler); - - return div; - }, - - /** - * Initializes the highlighter/brush. - * - * Constructor isn't used for initialization so that nothing executes during necessary - * `new SyntaxHighlighter.Highlighter()` call when setting up brush inheritance. - * - * @param {Hash} params Highlighter parameters. - */ - init: function(params) - { - this.id = guid(); - - // register this instance in the highlighters list - storeHighlighter(this); - - // local params take precedence over defaults - this.params = merge(sh.defaults, params || {}) - - // process light mode - if (this.getParam('light') == true) - this.params.toolbar = this.params.gutter = false; - }, - - /** - * Converts space separated list of keywords into a regular expression string. - * @param {String} str Space separated keywords. - * @return {String} Returns regular expression string. - */ - getKeywords: function(str) - { - str = str - .replace(/^\s+|\s+$/g, '') - .replace(/\s+/g, '|') - ; - - return '\\b(?:' + str + ')\\b'; - }, - - /** - * Makes a brush compatible with the `html-script` functionality. - * @param {Object} regexGroup Object containing `left` and `right` regular expressions. - */ - forHtmlScript: function(regexGroup) - { - var regex = { 'end' : regexGroup.right.source }; - - if(regexGroup.eof) - regex.end = "(?:(?:" + regex.end + ")|$)"; - - this.htmlScript = { - left : { regex: regexGroup.left, css: 'script' }, - right : { regex: regexGroup.right, css: 'script' }, - code : new XRegExp( - "(?<left>" + regexGroup.left.source + ")" + - "(?<code>.*?)" + - "(?<right>" + regex.end + ")", - "sgi" - ) - }; - } -}; // end of Highlighter - -return sh; -}(); // end of anonymous function - -// CommonJS -typeof(exports) != 'undefined' ? exports.SyntaxHighlighter = SyntaxHighlighter : null; diff --git a/pecan/middleware/resources/syntax.css b/pecan/middleware/resources/syntax.css deleted file mode 100644 index 4f0021e..0000000 --- a/pecan/middleware/resources/syntax.css +++ /dev/null @@ -1,226 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -.syntaxhighlighter a, -.syntaxhighlighter div, -.syntaxhighlighter code, -.syntaxhighlighter table, -.syntaxhighlighter table td, -.syntaxhighlighter table tr, -.syntaxhighlighter table tbody, -.syntaxhighlighter table thead, -.syntaxhighlighter table caption, -.syntaxhighlighter textarea { - -moz-border-radius: 0 0 0 0 !important; - -webkit-border-radius: 0 0 0 0 !important; - background: none !important; - border: 0 !important; - bottom: auto !important; - float: none !important; - height: auto !important; - left: auto !important; - line-height: 1.1em !important; - margin: 0 !important; - outline: 0 !important; - overflow: visible !important; - padding: 0 !important; - position: static !important; - right: auto !important; - text-align: left !important; - top: auto !important; - vertical-align: baseline !important; - width: auto !important; - box-sizing: content-box !important; - font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; - font-weight: normal !important; - font-style: normal !important; - font-size: 1em !important; - min-height: inherit !important; - min-height: auto !important; -} - -.syntaxhighlighter { - width: 100% !important; - margin: 1em 0 1em 0 !important; - position: relative !important; - overflow: auto !important; - font-size: 1em !important; -} -.syntaxhighlighter.source { - overflow: hidden !important; -} -.syntaxhighlighter .bold { - font-weight: bold !important; -} -.syntaxhighlighter .italic { - font-style: italic !important; -} -.syntaxhighlighter .line { - white-space: pre !important; -} -.syntaxhighlighter table { - width: 100% !important; -} -.syntaxhighlighter table caption { - text-align: left !important; - padding: .5em 0 0.5em 1em !important; -} -.syntaxhighlighter table td.code { - width: 100% !important; -} -.syntaxhighlighter table td.code .container { - position: relative !important; -} -.syntaxhighlighter table td.code .container textarea { - box-sizing: border-box !important; - position: absolute !important; - left: 0 !important; - top: 0 !important; - width: 100% !important; - height: 100% !important; - border: none !important; - background: white !important; - padding-left: 1em !important; - overflow: hidden !important; - white-space: pre !important; -} -.syntaxhighlighter table td.gutter .line { - text-align: right !important; - padding: 0 0.5em 0 1em !important; -} -.syntaxhighlighter table td.code .line { - padding: 0 1em !important; -} -.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { - padding-left: 0em !important; -} -.syntaxhighlighter.show { - display: block !important; -} -.syntaxhighlighter.collapsed table { - display: none !important; -} -.syntaxhighlighter.collapsed .toolbar { - padding: 0.1em 0.8em 0em 0.8em !important; - font-size: 1em !important; - position: static !important; - width: auto !important; - height: auto !important; -} -.syntaxhighlighter.collapsed .toolbar span { - display: inline !important; - margin-right: 1em !important; -} -.syntaxhighlighter.collapsed .toolbar span a { - padding: 0 !important; - display: none !important; -} -.syntaxhighlighter.collapsed .toolbar span a.expandSource { - display: inline !important; -} -.syntaxhighlighter .toolbar { - position: absolute !important; - right: 1px !important; - top: 1px !important; - width: 11px !important; - height: 11px !important; - font-size: 10px !important; - z-index: 10 !important; -} -.syntaxhighlighter .toolbar span.title { - display: inline !important; -} -.syntaxhighlighter .toolbar a { - display: block !important; - text-align: center !important; - text-decoration: none !important; - padding-top: 1px !important; -} -.syntaxhighlighter .toolbar a.expandSource { - display: none !important; -} -.syntaxhighlighter.ie { - font-size: .9em !important; - padding: 1px 0 1px 0 !important; -} -.syntaxhighlighter.ie .toolbar { - line-height: 8px !important; -} -.syntaxhighlighter.ie .toolbar a { - padding-top: 0px !important; -} -.syntaxhighlighter.printing .line.alt1 .content, -.syntaxhighlighter.printing .line.alt2 .content, -.syntaxhighlighter.printing .line.highlighted .number, -.syntaxhighlighter.printing .line.highlighted.alt1 .content, -.syntaxhighlighter.printing .line.highlighted.alt2 .content { - background: none !important; -} -.syntaxhighlighter.printing .line .number { - color: #bbbbbb !important; -} -.syntaxhighlighter.printing .line .content { - color: black !important; -} -.syntaxhighlighter.printing .toolbar { - display: none !important; -} -.syntaxhighlighter.printing a { - text-decoration: none !important; -} -.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { - color: black !important; -} -.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { - color: #008200 !important; -} -.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { - color: blue !important; -} -.syntaxhighlighter.printing .keyword { - color: #006699 !important; - font-weight: bold !important; -} -.syntaxhighlighter.printing .preprocessor { - color: gray !important; -} -.syntaxhighlighter.printing .variable { - color: #aa7700 !important; -} -.syntaxhighlighter.printing .value { - color: #009900 !important; -} -.syntaxhighlighter.printing .functions { - color: #ff1493 !important; -} -.syntaxhighlighter.printing .constants { - color: #0066cc !important; -} -.syntaxhighlighter.printing .script { - font-weight: bold !important; -} -.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { - color: gray !important; -} -.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { - color: #ff1493 !important; -} -.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { - color: red !important; -} -.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { - color: black !important; -} diff --git a/pecan/middleware/resources/theme.css b/pecan/middleware/resources/theme.css deleted file mode 100644 index 8a870e4..0000000 --- a/pecan/middleware/resources/theme.css +++ /dev/null @@ -1,117 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -.syntaxhighlighter { - background-color: white !important; -} -.syntaxhighlighter .line.alt1 { - background-color: white !important; -} -.syntaxhighlighter .line.alt2 { - background-color: white !important; -} -.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { - background-color: #e0e0e0 !important; -} -.syntaxhighlighter .line.highlighted.number { - color: black !important; -} -.syntaxhighlighter table caption { - color: black !important; -} -.syntaxhighlighter .gutter { - color: #afafaf !important; -} -.syntaxhighlighter .gutter .line { - border-right: 3px solid #6ce26c !important; -} -.syntaxhighlighter .gutter .line.highlighted { - background-color: #6ce26c !important; - color: white !important; -} -.syntaxhighlighter.printing .line .content { - border: none !important; -} -.syntaxhighlighter.collapsed { - overflow: visible !important; -} -.syntaxhighlighter.collapsed .toolbar { - color: blue !important; - background: white !important; - border: 1px solid #6ce26c !important; -} -.syntaxhighlighter.collapsed .toolbar a { - color: blue !important; -} -.syntaxhighlighter.collapsed .toolbar a:hover { - color: red !important; -} -.syntaxhighlighter .toolbar { - color: white !important; - background: #6ce26c !important; - border: none !important; -} -.syntaxhighlighter .toolbar a { - color: white !important; -} -.syntaxhighlighter .toolbar a:hover { - color: black !important; -} -.syntaxhighlighter .plain, .syntaxhighlighter .plain a { - color: black !important; -} -.syntaxhighlighter .comments, .syntaxhighlighter .comments a { - color: #008200 !important; -} -.syntaxhighlighter .string, .syntaxhighlighter .string a { - color: blue !important; -} -.syntaxhighlighter .keyword { - color: #006699 !important; -} -.syntaxhighlighter .preprocessor { - color: gray !important; -} -.syntaxhighlighter .variable { - color: #aa7700 !important; -} -.syntaxhighlighter .value { - color: #009900 !important; -} -.syntaxhighlighter .functions { - color: #ff1493 !important; -} -.syntaxhighlighter .constants { - color: #0066cc !important; -} -.syntaxhighlighter .script { - font-weight: bold !important; - color: #006699 !important; - background-color: none !important; -} -.syntaxhighlighter .color1, .syntaxhighlighter .color1 a { - color: gray !important; -} -.syntaxhighlighter .color2, .syntaxhighlighter .color2 a { - color: #ff1493 !important; -} -.syntaxhighlighter .color3, .syntaxhighlighter .color3 a { - color: red !important; -} - -.syntaxhighlighter .keyword { - font-weight: bold !important; -} diff --git a/pecan/tests/middleware/test_debug.py b/pecan/tests/middleware/test_debug.py deleted file mode 100644 index bc9d65f..0000000 --- a/pecan/tests/middleware/test_debug.py +++ /dev/null @@ -1,83 +0,0 @@ -from wsgiref.util import setup_testing_defaults - -from webtest import TestApp -from six import b as b_ - -from pecan.middleware.debug import DebugMiddleware -from pecan.tests import PecanTestCase - - -class StripPasteVar(object): - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - environ.pop('paste.testing') - return self.app(environ, start_response) - - -class TestDebugMiddleware(PecanTestCase): - - def setUp(self): - super(TestDebugMiddleware, self).setUp() - - def conditional_error_app(environ, start_response): - setup_testing_defaults(environ) - if environ['PATH_INFO'] == '/error': - assert 1 == 2 - start_response("200 OK", [('Content-type', 'text/plain')]) - return [b_('requested page returned')] - self.app = TestApp(StripPasteVar(DebugMiddleware( - conditional_error_app - ))) - - def test_middleware_passes_through_when_no_exception_raised(self): - r = self.app.get('/') - assert r.status_int == 200 - assert r.body == b_('requested page returned') - - def test_middleware_gives_stack_trace_on_errors(self): - r = self.app.get('/error', expect_errors=True) - assert r.status_int == 400 - assert b_('AssertionError') in r.body - - def test_middleware_complains_in_multi_process_environment(self): - - class MultiProcessApp(object): - - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - environ['wsgi.multiprocess'] = True - return self.app(environ, start_response) - - def conditional_error_app(environ, start_response): - start_response("200 OK", [('Content-type', 'text/plain')]) - return ['Hello, World!'] - - app = TestApp(MultiProcessApp(DebugMiddleware(conditional_error_app))) - self.assertRaises( - RuntimeError, - app.get, - '/' - ) - - def test_middlware_allows_for_post_mortem_debugging(self): - def patch_debugger(d): - def _patched_debug_request(): - d.append(True) - return _patched_debug_request - - debugger = [] - - app = TestApp(StripPasteVar(DebugMiddleware( - self.app, - patch_debugger(debugger) - ))) - - r = app.get('/error', expect_errors=True) - assert r.status_int == 400 - - r = app.get('/__pecan_initiate_pdb__', expect_errors=True) - assert len(debugger) > 0 diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py index 44f87db..e42f1da 100644 --- a/pecan/tests/test_base.py +++ b/pecan/tests/test_base.py @@ -1683,41 +1683,6 @@ class TestNonCanonical(PecanTestCase): assert len(wrapped_apps) == 1 -class TestDebugging(PecanTestCase): - def test_debugger_setup(self): - class RootController(object): - pass - - def debugger(): - pass - - app_conf = dict( - debug=True, - debugger=debugger - ) - with mock.patch('pecan.middleware.debug.DebugMiddleware') \ - as patched_debug_middleware: - app = make_app(RootController(), **app_conf) - args, kwargs = patched_debug_middleware.call_args - assert kwargs.get('debugger') == debugger - - def test_invalid_debugger_setup(self): - class RootController(object): - pass - - debugger = 'not_a_valid_entry_point' - - app_conf = dict( - debug=True, - debugger=debugger - ) - with mock.patch('pecan.middleware.debug.DebugMiddleware') \ - as patched_debug_middleware: - app = make_app(RootController(), **app_conf) - args, kwargs = patched_debug_middleware.call_args - assert kwargs.get('debugger') is None - - class TestLogging(PecanTestCase): def test_logging_setup(self): |