changeset 40:81333391792d

Moved client code from HTML template to `views.js`. Better handle relative vs. absolute page URLs in wiki links.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 07 Jan 2013 15:31:17 -0800
parents 7ec7cf21840a
children ef9cf617d76f
files wikked/static/js/wikked.js wikked/static/js/wikked/client.js wikked/static/js/wikked/views.js wikked/static/tpl/edit-page.html wikked/wiki.py
diffstat 5 files changed, 125 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/static/js/wikked.js	Sun Jan 06 23:17:16 2013 -0800
+++ b/wikked/static/js/wikked.js	Mon Jan 07 15:31:17 2013 -0800
@@ -37,17 +37,16 @@
 /**
  * Entry point: run Backbone!
  *
- * We also import scripts like `handlebars` and `client` that
- * are not used directly by anybody, but need to be evaluated.
+ * We also import scripts like `handlebars` that are not used directly
+ * by anybody, but need to be evaluated.
  */
 require([
-        'wikked/app', 
-        'wikked/handlebars', 
-        'wikked/client', 
+        'wikked/app',
+        'wikked/handlebars',
         'backbone',
         'bootstrap'
         ],
-    function(app, hb, client, Backbone) {
+    function(app, hb, Backbone) {
 
     var router = new app.Router();
     Backbone.history.start();//{ pushState: true });
--- a/wikked/static/js/wikked/client.js	Sun Jan 06 23:17:16 2013 -0800
+++ b/wikked/static/js/wikked/client.js	Mon Jan 07 15:31:17 2013 -0800
@@ -1,8 +1,16 @@
 /**
  * Client-side Wikked.
  */
-define(function() {
+define([
+        'jquery',
+        'underscore'
+        ],
+    function($, _) {
 
+    /**
+     * Lookup table and function to remove accentuated/alternate characters
+     * with their ASCII equivalent.
+     */
     var defaultDiacriticsRemovalMap = [
         {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},
         {'base':'AA','letters':/[\uA732]/g},
@@ -99,9 +107,61 @@
         return str;
     }
 
-    var PageFormatter = {
+    /**
+     * Function to normalize an array of path parts (remove/simplify `./` and
+     * `../` elements).
+     */
+    function normalizeArray(parts, keepBlanks) {
+        var directories = [], prev;
+        for (var i = 0, l = parts.length - 1; i <= l; i++) {
+            var directory = parts[i];
+
+            // if it's blank, but it's not the first thing, and not the last thing, skip it.
+            if (directory === "" && i !== 0 && i !== l && !keepBlanks)
+                continue;
+
+            // if it's a dot, and there was some previous dir already, then skip it.
+            if (directory === "." && prev !== undefined)
+                continue;
+
+            // if it starts with "", and is a . or .., then skip it.
+            if (directories.length === 1 && directories[0] === "" && (
+                directory === "." || directory === ".."))
+                continue;
+
+                if (
+                    directory === ".." && directories.length && prev !== ".." && prev !== "." && prev !== undefined && (prev !== "" || keepBlanks)) {
+                    directories.pop();
+                prev = directories.slice(-1)[0]
+            } else {
+                if (prev === ".") directories.pop();
+                directories.push(directory);
+                prev = directory;
+            }
+        }
+        return directories;
+    }
+
+    /**
+     * Client-side page formatter, with support for previewing meta properties
+     * in the live preview window.
+     */
+    var PageFormatter = function(baseUrl) {
+        this.baseUrl = baseUrl;
+        if (baseUrl === undefined) {
+            this.baseUrl = '';
+        }
+    };
+    _.extend(PageFormatter.prototype, {
         formatLink: function(link) {
-            ansi_link = removeDiacritics(link);
+            var abs_link = link;
+            if (link[0] == '/') {
+                abs_link = link.substring(1);
+            } else {
+                raw_abs_link = this.baseUrl + link
+                abs_link = normalizeArray(raw_abs_link.split('/')).join('/');
+            }
+            ansi_link = removeDiacritics(abs_link);
             return ansi_link.toLowerCase().replace(/[^a-z0-9_\.\-\(\)\/]+/g, '-');
         },
         formatText: function(text) {
@@ -126,11 +186,7 @@
             });
             return text;
         }
-    };
-    //TODO: remove this and move the JS code from the template to the view.
-    if (!window.Wikked)
-        window.Wikked = {};
-    window.Wikked.PageFormatter = PageFormatter;
+    });
 
     return {
         PageFormatter: PageFormatter
--- a/wikked/static/js/wikked/views.js	Sun Jan 06 23:17:16 2013 -0800
+++ b/wikked/static/js/wikked/views.js	Mon Jan 07 15:31:17 2013 -0800
@@ -6,10 +6,11 @@
         'underscore',
         'backbone',
         'handlebars',
+        './client',
         './models',
         './util'
         ],
-    function($, _, Backbone, Handlebars, Models, Util) {
+    function($, _, Backbone, Handlebars, Client, Models, Util) {
 
     var exports = {};
 
@@ -229,6 +230,49 @@
         },
         renderCallback: function(view, model) {
             PageEditView.__super__.renderCallback.apply(this, arguments);
+
+            // Create the Markdown editor.
+            var formatter = new Client.PageFormatter();
+            formatter.baseUrl = this.model.get('path').match(/.*\//);
+            var converter = new Markdown.Converter();
+            converter.hooks.chain("preConversion", function(text) {
+                return formatter.formatText(text);
+            });
+            var editor = new Markdown.Editor(converter); //TODO: pass options
+            editor.run();
+            var editor_control = this.$('textarea#wmd-input');
+            editor_control.outerWidth(this.$('.wmd-input-wrapper').innerWidth());
+
+            // Input area resizing with the grip.
+            var last_pageY;
+            this.$(".wmd-input-grip")
+                .mousedown(function(e) {
+                    last_pageY = e.pageY;
+                    $('body')
+                        .on('mousemove.wikked.editor_resize', function(e) {
+                            editor_control.height(editor_control.height() + e.pageY - last_pageY);
+                            last_pageY = e.pageY;
+                        })
+                        .on('mouseup.wikked.editor_resize mouseleave.wikked.editor_resize', function(e) {
+                            $('body').off('.wikked.editor_resize');
+                        });
+                });
+
+            // Show/hide live preview.
+            this.$('.wmd-preview-wrapper>h3>a').on('click', function(e) {
+                $('#wmd-preview').fadeToggle(function() {
+                    var icon = $('.wmd-preview-wrapper>h3>a i');
+                    if (icon.hasClass('icon-minus')) {
+                        icon.removeClass('icon-minus');
+                        icon.addClass('icon-plus');
+                    } else {
+                        icon.removeClass('icon-plus');
+                        icon.addClass('icon-minus');
+                    }
+                });
+            });
+
+            // Make the model submit the form.
             this.$('#page-edit').submit(function(e) {
                 e.preventDefault();
                 model.doEdit(this);
--- a/wikked/static/tpl/edit-page.html	Sun Jan 06 23:17:16 2013 -0800
+++ b/wikked/static/tpl/edit-page.html	Mon Jan 07 15:31:17 2013 -0800
@@ -39,49 +39,3 @@
 <script type="text/javascript" src="/js/pagedown/Markdown.Converter.js"></script>
 <script type="text/javascript" src="/js/pagedown/Markdown.Sanitizer.js"></script>
 <script type="text/javascript" src="/js/pagedown/Markdown.Editor.js"></script>
-<script type="text/javascript">
-    (function() {
-            var formatter = _.extend(this.Wikked.PageFormatter, {});
-            var converter = new Markdown.Converter();
-            converter.hooks.chain("preConversion", function(text) {
-                return formatter.formatText(text);
-            });
-            
-            //var help = function () { alert("Do you need help?"); }
-            //var options = {
-            //    helpButton: { handler: help },
-            //    strings: { quoteexample: "whatever you're quoting, put it right here" }
-            //};
-            var editor = new Markdown.Editor(converter); //TODO: pass options
-            editor.run();
-
-            var editor_control = $('textarea#wmd-input');
-            editor_control.outerWidth($('.wmd-input-wrapper').innerWidth());
-
-            var last_pageY;
-            $(".wmd-input-grip")
-                .mousedown(function(e) {
-                    last_pageY = e.pageY;
-                    $('body')
-                        .on('mousemove.wikked.editor_resize', function(e) {
-                            editor_control.height(editor_control.height() + e.pageY - last_pageY);
-                            last_pageY = e.pageY;
-                        })
-                        .on('mouseup.wikked.editor_resize mouseleave.wikked.editor_resize', function(e) { 
-                            $('body').off('.wikked.editor_resize');
-                        });
-                });
-            $('.wmd-preview-wrapper>h3>a').on('click', function(e) {
-                $('#wmd-preview').fadeToggle(function() {
-                    var icon = $('.wmd-preview-wrapper>h3>a i');
-                    if (icon.hasClass('icon-minus')) {
-                        icon.removeClass('icon-minus');
-                        icon.addClass('icon-plus');
-                    } else {
-                        icon.removeClass('icon-plus');
-                        icon.addClass('icon-minus');
-                    }
-                });
-            });
-        })();
-</script>
--- a/wikked/wiki.py	Sun Jan 06 23:17:16 2013 -0800
+++ b/wikked/wiki.py	Mon Jan 07 15:31:17 2013 -0800
@@ -95,7 +95,16 @@
         return text
 
     def _formatWikiLink(self, ctx, display, url):
-        slug = Page.title_to_url(os.path.join(ctx.urldir, url))
+        if url.startswith('/'):
+            # Absolute page URL.
+            abs_url = url[1:]
+        else:
+            # Relative page URL. Let's normalize all `..` in it,
+            # which could also replace forward slashes by backslashes
+            # on Windows, so we need to convert that back.
+            raw_abs_url = os.path.join(ctx.urldir, url)
+            abs_url = os.path.normpath(raw_abs_url).replace('\\', '/')
+        slug = Page.title_to_url(abs_url)
         ctx.out_links.append(slug)
 
         css_class = 'wiki-link'
@@ -260,7 +269,7 @@
         self.fs = FileSystem(root, slugify=Page.title_to_url)
         self.auth = UserManager(self.config, logger=self.logger)
         self.index = WhooshWikiIndex(os.path.join(root, '.index'), logger=self.logger)
-        
+
         scm_type = 'hg'
         if self.config.has_option('wiki', 'scm'):
             scm_type = self.config.get('wiki', 'scm')