Commits

Anonymous committed a8d6cab

now use session to manage preferneces. 1 cookie for me, plz.

  • Participants
  • Parent commits 2fac117

Comments (0)

Files changed (12)

build/friendpaste.yml

         - colors.css
 javascript:
     friendpaste.js:
+        - lib/json2.js
         - src/base.js
         - src/friendpaste.js
     

friendpaste/application.py

 
 
 from friendpaste import settings
+from friendpaste import context_processors
 from friendpaste.urls import urls_map, all_views
 from friendpaste.utils import local, local_manager
 from friendpaste.http import FPRequest, session_store

friendpaste/context_processors.py

+# -*- coding: utf-8 -
+#
+# Copyright 2008 by Benoît Chesneau <benoitc@e-engura.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from friendpaste.template import register_contextprocessor
+from friendpaste.utils import local
+
+@register_contextprocessor
+def settings(request):
+    return {
+        'theme': request.session.get('theme', 'default'),
+        'showlinenos': request.session.get('showlinenos', True)
+    }

friendpaste/template.py

 from pygments.styles import get_all_styles
 
 from friendpaste import settings
-from friendpaste.http import FPResponse
+from friendpaste.http import FPResponse, FPRequest
 from friendpaste.utils import local, timesince, datetimestr_topython
 
 template_env = Environment(loader=FileSystemLoader(settings.TEMPLATES_PATH))
 template_env.charset = 'utf-8'
 
 ALL_COLORSHEME = list(get_all_styles())
-
+_context_processors = []
 
 
 def url_for(endpoint, _external=False, **values):
                 l = change['base']['offset']
                 for line in change['base']['lines']:
                     rst = rst + "<tr class=\"unmod\"><th class=\"linenos\">%s</th>\
-                    <th class=\"linenos\">%s</th><td></td><td>%s</td>" % (
+                    <th class=\"linenos\">%s</th><td></td><td>%s</td></tr>" % (
                         l,
                         l,
                         line
                 pos = 1
                 for line in change['base']['lines']:       
                     rst = rst + '<tr class="%s"><th class=\"linenos\">%s</th>\
-                    <th class=\"linenos\"></th><th class="diffm">-</th><td class="c wrap %s">%s</td>' % (
+                    <th class=\"linenos\"></th><th class="diffm">-</th><td class="c wrap %s">%s</td></tr>' % (
                         class_,
                         change['base']['offset'],
                         class_start,
                 pos = 1
                 for line in change['changed']['lines']:
                     rst = rst + "<tr class=\"%s\"><th class=\"linenos\"></th><th class=\"linenos\">%s</th>\
-                    <th class=\"diffp\">+</th><td class=\"c wrap %s\">%s</td>" % (
+                    <th class=\"diffp\">+</th><td class=\"c wrap %s\">%s</td></tr>" % (
                         class_,
                         change['base']['offset'],
                         class_end,
                     pos = pos + 1
                     if pos == nb_lines:
                         class_end = "last"
-            rst = rst + "<tr><th class=\"linenos\">...<th><td colspan=\"3\"></td></tr>"
-    rst = "<table class=\"difftabular\">%s</table>" % rst
+            rst = rst + "<tr><th class=\"linenos\">...</th><td colspan=\"3\"></td></tr>"
+    rst = "<table id=\"tableDiff\" class=\"difftabular\">%s</table>" % rst
     return rst
 template_env.filters['tabular'] = do_blockdiff
 
 
 def render_template(template_name, _stream=False, **kwargs):
     tmpl = template_env.get_template(template_name)
+    request = getattr(local, 'request', None)
+    
+    if  request is not None and isinstance(request, FPRequest):
+        context = {}
+        for processor in _context_processors:
+            context.update(processor(request))
+        context.update(kwargs)
+    else:
+        context = kwargs
     if _stream:
-        return tmpl.stream(kwargs)
-    return tmpl.render(kwargs)
+        return tmpl.stream(context)
+    return tmpl.render(context)
 
+def register_contextprocessor(func):
+    _context_processors.append(func)
+    return func
 
-

friendpaste/urls.py

         'paste/changeset': views.view_changeset,
         'paste/revisions': views.view_revisions,
         'paste/rss': views.view_rss,
+        'settings': views.save_settings,
         'paste/all_languages': views.get_all_languages,
         'generic/about': views.about,
         'generic/services': views.services
     Rule('/about', endpoint='generic/about'),
     Rule('/services/<page>', endpoint='generic/services'),
     Rule('/services', endpoint='generic/services'),
+    Rule('/settings', endpoint='settings'),
     Rule('/_all_languages', endpoint='paste/all_languages'),
     Rule('/<id>_<rev>/raw', endpoint='paste/raw'),
     Rule('/<id>_<rev>/original', endpoint='paste/original'),

friendpaste/views.py

 
     # get theme
     theme = request.cookies.get('theme', 'default')
-    return render_response('paste/view.html', snippet=s, theme=theme, form=form, rev=rev)
+    return render_response('paste/view.html', snippet=s, form=form, rev=rev)
 
 
 
         return response
 
     theme = request.cookies.get('theme', 'default')
-    return render_response('paste/diff.html', unidiff=unidiff, difft=tabular, theme=theme,
+    return render_response('paste/diff.html', unidiff=unidiff, difft=tabular,
             snippet=snippet, rev=rev, old_rev=old_rev)
 
 def get_all_languages(request):
     languages.sort()
     return send_json(dict(languages))
 
+def save_settings(request):
+    if request.method == "POST":
+        try:
+            data = json.loads(request.data)
+        except ValueError:
+            return send_json({'ok': False})
+            
+        print data
+        for attrname, value in data.items():
+            request.session[attrname] = value
+        
+    return send_json({"ok": True})
 
 # generic views
 def about(request):

static/js/lib/json2.js

+/*
+    http://www.JSON.org/json2.js
+    2008-05-25
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects without a toJSON
+                        method. It can be a function or an array.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the object holding the key.
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array, then it will be used to
+            select the members to be serialized. It filters the results such
+            that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+*/
+
+/*jslint evil: true */
+
+/*global JSON */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
+    charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
+    getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
+    parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
+    test, toJSON, toString
+*/
+
+if (!this.JSON) {
+
+// Create a JSON object only if one does not already exist. We create the
+// object in a closure to avoid creating global variables.
+
+    JSON = function () {
+
+        function f(n) {
+            // Format integers to have at least two digits.
+            return n < 10 ? '0' + n : n;
+        }
+
+        Date.prototype.toJSON = function (key) {
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+        var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+            escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+            gap,
+            indent,
+            meta = {    // table of character substitutions
+                '\b': '\\b',
+                '\t': '\\t',
+                '\n': '\\n',
+                '\f': '\\f',
+                '\r': '\\r',
+                '"' : '\\"',
+                '\\': '\\\\'
+            },
+            rep;
+
+
+        function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+            escapeable.lastIndex = 0;
+            return escapeable.test(string) ?
+                '"' + string.replace(escapeable, function (a) {
+                    var c = meta[a];
+                    if (typeof c === 'string') {
+                        return c;
+                    }
+                    return '\\u' + ('0000' +
+                            (+(a.charCodeAt(0))).toString(16)).slice(-4);
+                }) + '"' :
+                '"' + string + '"';
+        }
+
+
+        function str(key, holder) {
+
+// Produce a string from holder[key].
+
+            var i,          // The loop counter.
+                k,          // The member key.
+                v,          // The member value.
+                length,
+                mind = gap,
+                partial,
+                value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+            if (value && typeof value === 'object' &&
+                    typeof value.toJSON === 'function') {
+                value = value.toJSON(key);
+            }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+            if (typeof rep === 'function') {
+                value = rep.call(holder, key, value);
+            }
+
+// What happens next depends on the value's type.
+
+            switch (typeof value) {
+            case 'string':
+                return quote(value);
+
+            case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+                return isFinite(value) ? String(value) : 'null';
+
+            case 'boolean':
+            case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+                return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+            case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+                if (!value) {
+                    return 'null';
+                }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+                gap += indent;
+                partial = [];
+
+// If the object has a dontEnum length property, we'll treat it as an array.
+
+                if (typeof value.length === 'number' &&
+                        !(value.propertyIsEnumerable('length'))) {
+
+// The object is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                    length = value.length;
+                    for (i = 0; i < length; i += 1) {
+                        partial[i] = str(i, value) || 'null';
+                    }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                    v = partial.length === 0 ? '[]' :
+                        gap ? '[\n' + gap +
+                                partial.join(',\n' + gap) + '\n' +
+                                    mind + ']' :
+                              '[' + partial.join(',') + ']';
+                    gap = mind;
+                    return v;
+                }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+                if (rep && typeof rep === 'object') {
+                    length = rep.length;
+                    for (i = 0; i < length; i += 1) {
+                        k = rep[i];
+                        if (typeof k === 'string') {
+                            v = str(k, value, rep);
+                            if (v) {
+                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                            }
+                        }
+                    }
+                } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = str(k, value, rep);
+                            if (v) {
+                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                            }
+                        }
+                    }
+                }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+                v = partial.length === 0 ? '{}' :
+                    gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                            mind + '}' : '{' + partial.join(',') + '}';
+                gap = mind;
+                return v;
+            }
+        }
+
+// Return the JSON object containing the stringify and parse methods.
+
+        return {
+            stringify: function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+                var i;
+                gap = '';
+                indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+                if (typeof space === 'number') {
+                    for (i = 0; i < space; i += 1) {
+                        indent += ' ';
+                    }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+                } else if (typeof space === 'string') {
+                    indent = space;
+                }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+                rep = replacer;
+                if (replacer && typeof replacer !== 'function' &&
+                        (typeof replacer !== 'object' ||
+                         typeof replacer.length !== 'number')) {
+                    throw new Error('JSON.stringify');
+                }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+                return str('', {'': value});
+            },
+
+
+            parse: function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+                var j;
+
+                function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                    var k, v, value = holder[key];
+                    if (value && typeof value === 'object') {
+                        for (k in value) {
+                            if (Object.hasOwnProperty.call(value, k)) {
+                                v = walk(value, k);
+                                if (v !== undefined) {
+                                    value[k] = v;
+                                } else {
+                                    delete value[k];
+                                }
+                            }
+                        }
+                    }
+                    return reviver.call(holder, key, value);
+                }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+                cx.lastIndex = 0;
+                if (cx.test(text)) {
+                    text = text.replace(cx, function (a) {
+                        return '\\u' + ('0000' +
+                                (+(a.charCodeAt(0))).toString(16)).slice(-4);
+                    });
+                }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+                if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                    j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                    return typeof reviver === 'function' ?
+                        walk({'': j}, '') : j;
+                }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+                throw new SyntaxError('JSON.parse');
+            }
+        };
+    }();
+}

static/js/src/base.js

   return function() {
     return __method.apply(object, args.concat($A(arguments)));
   }
-}
+}
+
+
+// get form data from a form element
+function get_formdata(form) {  
+    var form_data = {};
+    $("*", form).each(function(index, el) {
+        if (!(!el.name || el.disabled || (el.type == "reset") ||
+                (el.type == "button") && 
+                (el.type == "checkbox" || el.type == "radio" && !el.checked) || 
+                (el.type == 'submit') || (el.type == 'image') ||
+                (el.tagName.toLowerCase() == "select" && el.selectedIndex == -1))) {
+        
+            if (el.tagName.toLowerCase() == "select") {
+                var index = el.selectedIndex;
+                if (index > 0) {
+                    var  a = [];
+                    for (var i=index; i< el.options.length; i++) {
+                        op = el.options[i];
+                        var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
+                        a.push(v);
+                    }
+                    form_data[el.name] = a
+                }
+            
+            } else {
+                form_data[el.name] = el.value;
+            }
+        }
+    });
+    
+    return form_data;
+}

static/js/src/friendpaste.js

         if (history) 
             history.click(this.do_history.bindAsEventListener(this));
 
+        $("#change-theme").change(this.setTheme.bindAsEventListener(this));
+        $("#show-linenos").click(this.toggleLineNumber.bindAsEventListener(this));
+            
         this.snippet = $('#snippet');
         this.snippet_edit = $('#snippet_edit');
         this.revisions = $('#revisions');
         
+        
     },
     
     do_edit: function(e) {
 
     toggleLineNumber: function(e) {
         $("table.sourcetable tbody").toggleClass("hidenos");
+        this.saveSettings();
     },
 
+    saveSettings: function() {
+        var form = $("#factions");
+        var showlinenos = true;
+        if ( $('#show-linenos:checked').val() != 1) {
+            showlinenos = false;
+        }
+        
+        $.ajax({
+            url: form.attr('action'),
+            dataType: "json",
+            contentType: "application/json",
+            type: "POST",
+            data: this.toJSON({
+                "theme": $("#change-theme").val(),
+                "showlinenos": showlinenos
+            }),
+            success: function(data) {
+                
+            }
+        });
+    },
 
-    /*setTheme: function(event) {
-        setCookie("theme", this.value, 60);
-        document.location.reload();
+    setTheme: function(e) {
+         var theme = e.target.value;
+         $('link[@rel*=style][@title]').each(function(i) {
+             if (this.getAttribute('title') == "syntax_theme") {
+                 this.href = "/static/css/"+theme+".css"
+             }
+         });
+         
+         this.saveSettings();
+         
     },
-*/
+
     do_history: function(e) {
         url = "/" + snippet_id + "/revisions";
         if (typeof(revid) != "undefined") {
             self.revisions.removeClass("hidden");
         });
         return false;
+    },
+    
+    toJSON: function(obj) {
+        return obj !== null ? JSON.stringify(obj) : null;
     }
 
     
          
         this.sourcetable.addClass("hidden");
         
-        this.switcher.html('<span class="stab active">Tabular</span'+
-        '<span class="suni">Unified</span');
+        this.switcher.html('<span class="stab active">Tabular</span><span class="suni">Unified</span>');
         
         // set  event handlers
         this.stab = $(".stab", this.switcher);

templates/base.html

         {% if DEBUG %}
         <link rel="stylesheet" href="/static/css/src/debug.css" type="text/css" />
         <script type="text/javascript" src="/static/js/lib/jquery-1.2.6.js"></script>
+        <script type="text/javascript" src="/static/js/lib/json2.js"></script>
         <script type="text/javascript" src="/static/js/src/base.js"></script>
         <script type="text/javascript" src="/static/js/src/friendpaste.js"></script>
         {% else %}

templates/paste/diff.html

         </table>
     </div>
     <div id="actions">            
-        <form id="factions" action="/{{ snippet['_id'] }}/changeset?rev={{ rev }}" method="post">
+        <form id="factions" action="{{ url_for('settings') }}" method="post">
                 <ul>
                     <li class="first">
                         <select name="theme" id="change-theme">
                         {% endfor %}
                         </select>
                     </li>
-                    <li><input type="checkbox" id="show-linenos" name="linen" value="1" checked="checked" /> <label for="show-linenos">Show line numbers</label></li>
+                    <li><input type="checkbox" id="show-linenos" name="linen" value="1"{% if showlinenos %} checked="checked"{% endif %} /> <label for="show-linenos">Show line numbers</label></li>
                 </ul>
             </form>
         </div>
     <div id="diff">
-    <div id="switch"></div>
-    <div class="highlight">
-        {{ difft|tabular }}
-        {{ unidiff|highlight("diff") }}
-    </div>
+        <div id="switch"></div>
+        <div class="highlight">
+            {{ difft|tabular }}
+            {{ unidiff|highlight("diff") }}
+        </div>
     </div>
 </div>
 <br />

templates/paste/view.html

 {% block head %}
 <link rel="alternate" href="/{{ snippet.id }}/rss" type="application/rss+xml" class="rss" title="RSS Feed" />
 
-<link rel="stylesheet" href="/static/css/{{theme }}.css" type="text/css" />
+<link rel="stylesheet" href="/static/css/{{theme }}.css" type="text/css" title="syntax_theme" />
 
 <script type="text/javascript">
     snippet_id = "{{ snippet.id }}";
         
     </div>
     <div id="info">
-        <p>Revision <a href="/{{ snippet.id }}?rev={{ snippet.revid }}">{{ snippet.revid }}</a> ({{ snippet.updated|timesince }}) - <a href="/{{ snippet.id }}/changeset?rev={{ snippet.revid }}">Diff</a>
+        <p>Revision <a href="/{{ snippet.id }}?rev={{ snippet.revid }}">{{ snippet.revid }}</a> ({{ snippet.updated|timesince }}) - <a href="{{ url_for('paste/changeset', id=snippet.id, rev=snippet.revid) }}">Diff</a>
         </p>
         <p class="lsnippet">Link to this snippet : <a href="/{{ snippet.id }}">http://friendpaste.com/{{ snippet.id  }}</a></p>
     </div>
         <div id="actions">
-            <form id="factions" action="/{{ snippet.id }}" method="post">
+            <form id="factions" action="{{ url_for('settings') }}" method="post">
                 <ul>
                     <li class="first">
                         <select name="theme" id="change-theme">
                         {% endfor %}
                         </select>
                     </li>
-                    <li><input type="checkbox" id="show-linenos" value="1" checked="checked" /> <label for="show-linenos">Show line numbers</label></li>
+                    <li><input type="checkbox" id="show-linenos" value="1"{% if showlinenos %} checked="checked"{% endif %} /> <label for="show-linenos">Show line numbers</label></li>
                 </ul>
             </form>
         </div>