changeset 296:d1e7f4c35afd

Frontend code cleanup, optimization, and remembering the menu state.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 28 Sep 2014 23:24:44 -0700
parents 3faee3c34d0c
children eb4897943264
files wikked/assets/js/wikked/app.js wikked/assets/js/wikked/models.js wikked/assets/js/wikked/views.js wikked/assets/tpl/nav.html
diffstat 4 files changed, 133 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/assets/js/wikked/app.js	Sun Sep 28 23:23:51 2014 -0700
+++ b/wikked/assets/js/wikked/app.js	Sun Sep 28 23:24:44 2014 -0700
@@ -31,12 +31,12 @@
 
             if (view) {
                 this._currentView = view;
-                this.el.html(view.el);
                 if (autoFetch || autoFetch === undefined) {
                     view.model.fetch();
                 } else {
                     view.render();
                 }
+                this.el.empty().append(view.el);
             }
 
             return this;
@@ -80,14 +80,15 @@
             'special/orphans':       "showOrphans"
         },
         readPage: function(path) {
-            path_clean = this.stripQuery(path);
-            no_redirect = this.getQueryVariable('no_redirect', path);
+            var path_clean = this.stripQuery(path);
+            var no_redirect = this.getQueryVariable('no_redirect', path);
+            var model_attrs = { path: path_clean };
+            if (no_redirect) model_attrs.no_redirect = true;
+
             var view = new Views.PageReadView({
-                model: new Models.PageReadModel({ path: path_clean })
+                model: new Models.PageReadModel(model_attrs)
             });
-            if (no_redirect) {
-                view.model.set('no_redirect', true);
-            }
+
             this.viewManager.switchView(view);
             this.navigate('/read/' + path);
         },
--- a/wikked/assets/js/wikked/models.js	Sun Sep 28 23:23:51 2014 -0700
+++ b/wikked/assets/js/wikked/models.js	Sun Sep 28 23:24:44 2014 -0700
@@ -16,22 +16,18 @@
         defaults: function() {
             return {
                 path: "",
-                action: "read",
                 username: false,
+                urls: [],
                 url_extras: [
                     { name: 'Special Pages', url: '/#/special', icon: 'dashboard' }
                 ]
             };
         },
         initialize: function() {
-            this.on('change:path', function(model, path) {
-                model._onChangePath(path);
-            });
-            this._onChangePath(this.get('path'));
-            this.on('change:user', function(model, auth) {
-                model._onChangeUser(auth);
-            });
-            this._onChangeUser(this.get('user'));
+            this.on('change:path', this._onChangePath, this);
+            this.on('change:user', this._onChangeUser, this);
+            this._onChangePath(this, this.get('path'));
+            this._onChangeUser(this, this.get('user'));
             return this;
         },
         url: function() {
@@ -76,14 +72,17 @@
         doNewPage: function(form) {
             this.navigate('/create/', { trigger: true });
         },
-        _onChangePath: function(path) {
-            this.set({
-                url_home: '/',
-                url_read: '/#/read/' + path,
-                url_edit: '/#/edit/' + path,
-                url_hist: '/#/changes/' + path,
-                url_search: '/search'
-            });
+        _onChangePath: function(model, path) {
+            var attrs ={
+                url_home: '/#/'};
+            var urls = this.get('urls');
+            if (_.contains(urls, 'read'))
+                attrs.url_read = '/#/read/' + path;
+            if (_.contains(urls, 'edit'))
+                attrs.url_edit = '/#/edit/' + path;
+            if (_.contains(urls, 'history'))
+                attrs.url_hist = '/#/changes/' + path;
+            this.set(attrs);
         },
         _isSearching: false,
         _pendingQuery: null,
@@ -97,7 +96,7 @@
                 this.doPreviewSearch(q, c);
             }
         },
-        _onChangeUser: function(user) {
+        _onChangeUser: function(model, user) {
             if (user) {
                 this.set({
                     url_login: false,
@@ -158,14 +157,10 @@
             };
         },
         initialize: function() {
-            this.on('change:path', function(model, path) {
-                model._onChangePath(path);
-            });
-            this.on('change:text', function(model, text) {
-                model._onChangeText(text);
-            });
-            this._onChangePath(this.get('path'));
-            this._onChangeText('');
+            this.on('change:path', this._onChangePath, this);
+            this.on('change:text', this._onChangeText, this);
+            this._onChangePath(this, this.get('path'));
+            this._onChangeText(this, '');
             return this;
         },
         url: function() {
@@ -189,45 +184,53 @@
                 this._onAppSet(app);
             }
         },
-        _onChangePath: function(path) {
+        _onChangePath: function(model, path) {
         },
-        _onChangeText: function(text) {
+        _onChangeText: function(model, text) {
             this.set('content', new Handlebars.SafeString(text));
         }
     });
 
     var PageStateModel = exports.PageStateModel = PageModel.extend({
         urlRoot: '/api/state/',
-        _onChangePath: function(path) {
+        _onChangePath: function(model, path) {
             PageStateModel.__super__._onChangePath.apply(this, arguments);
             this.set('url_edit', '/#/edit/' + path);
         }
     });
 
     var MasterPageModel = exports.MasterPageModel = PageModel.extend({
+        navUrls: [],
         initialize: function() {
-            this.nav = new NavigationModel({ path: this.id });
+            var navAttrs = { path: this.id, urls: this.navUrls };
+            this.nav = new NavigationModel(navAttrs);
             this.footer = new FooterModel();
             MasterPageModel.__super__.initialize.apply(this, arguments);
-            this.on('change:auth', function(model, auth) {
-                model._onChangeAuth(auth);
-            });
+            this._addNavExtraUrls();
+            this._addFooterExtraUrls();
+            this.on('change:auth', this._onChangeAuth, this);
             this.on('error', this._onError, this);
-            if (this.action !== undefined) {
-                this.nav.set('action', this.action);
-                this.footer.set('action', this.action);
-            }
             return this;
         },
+        _addNavExtraUrls: function() {
+        },
+        _addFooterExtraUrls: function() {
+            var model = this;
+            this.footer.addExtraUrl(
+                'JSON',
+                function() { return model.url(); },
+                -1,
+                'cog');
+        },
         _onAppSet: function(app) {
             this.nav.app = app;
             this.footer.app = app;
         },
-        _onChangePath: function(path) {
+        _onChangePath: function(model, path) {
             MasterPageModel.__super__._onChangePath.apply(this, arguments);
             this.nav.set('path', path);
         },
-        _onChangeAuth: function(auth) {
+        _onChangeAuth: function(model, auth) {
             this.nav.set('auth', auth);
         },
         _onError: function(model, resp) {
@@ -252,23 +255,29 @@
 
     var PageReadModel = exports.PageReadModel = MasterPageModel.extend({
         urlRoot: '/api/read/',
-        action: 'read',
+        navUrls: ['edit', 'history'],
         initialize: function() {
             PageReadModel.__super__.initialize.apply(this, arguments);
             this.on('change', this._onChange, this);
-
-            // Add extra links to the footer.
+            return this;
+        },
+        _addNavExtraUrls: function() {
+            PageReadModel.__super__._addNavExtraUrls.apply(this, arguments);
             var model = this;
             this.nav.addExtraUrl(
                 'Pages Linking Here',
                 function() { return '/#/inlinks/' + model.id; },
                 1,
                 'link');
+        },
+        _addFooterExtraUrls: function() {
+            PageReadModel.__super__._addFooterExtraUrls.apply(this, arguments);
+            var model = this;
             this.footer.addExtraUrl(
-                'JSON',
-                function() { return '/api/read/' + model.id; },
+                'RAW',
+                function() { return '/api/raw/' + model.id; },
                 -1,
-                'cog');
+                'wrench');
         },
         url: function() {
             var url = PageReadModel.__super__.url.apply(this, arguments);
@@ -293,13 +302,12 @@
     });
 
     var PageSourceModel = exports.PageSourceModel = MasterPageModel.extend({
-        urlRoot: '/api/raw/',
-        action: 'source'
+        urlRoot: '/api/raw/'
     });
 
     var PageEditModel = exports.PageEditModel = MasterPageModel.extend({
-        action: 'edit',
         urlRoot: '/api/edit/',
+        navUrls: ['read', 'history'],
         doEdit: function(form) {
             if (this.get('is_new')) {
                 this.set('path', $('input[name="title"]', form).val());
@@ -329,16 +337,7 @@
 
     var PageHistoryModel = exports.PageHistoryModel = MasterPageModel.extend({
         urlRoot: '/api/history/',
-        action: 'history',
-        initialize: function() {
-            PageHistoryModel.__super__.initialize.apply(this, arguments);
-            var model = this;
-            this.nav.addExtraUrl(
-                'JSON',
-                function() { return '/api/history/' + model.id; },
-                -1,
-                'road');
-        },
+        navUrls: ['read', 'edit'],
         doDiff: function(form) {
             var rev1 = $('input[name=rev1]:checked', form).val();
             var rev2 = $('input[name=rev2]:checked', form).val();
@@ -357,7 +356,6 @@
     });
 
     var PageRevisionModel = exports.PageRevisionModel = MasterPageModel.extend({
-        action: 'revision',
         defaults: function() {
             return {
                 path: "",
@@ -369,10 +367,8 @@
         },
         initialize: function() {
             PageRevisionModel.__super__.initialize.apply(this, arguments);
-            this.on('change:rev', function(model, rev) {
-                model._onChangeRev(rev);
-            });
-            this._onChangeRev(this.get('rev'));
+            this.on('change:rev', this._onChangeRev, this);
+            this._onChangeRev(this, this.get('rev'));
             return this;
         },
         doRevert: function(form) {
@@ -386,7 +382,7 @@
                     alert('Error reverting page...');
                 });
         },
-        _onChangeRev: function(rev) {
+        _onChangeRev: function(model, rev) {
             var setmap = { disp_rev: rev };
             if (rev.match(/[a-f0-9]{40}/)) {
                 setmap.disp_rev = rev.substring(0, 8);
@@ -396,7 +392,6 @@
     });
 
     var PageDiffModel = exports.PageDiffModel = MasterPageModel.extend({
-        action: 'diff',
         defaults: function() {
             return {
                 path: "",
@@ -413,24 +408,20 @@
         },
         initialize: function() {
             PageDiffModel.__super__.initialize.apply(this, arguments);
-            this.on('change:rev1', function(model, rev1) {
-                model._onChangeRev1(rev1);
-            });
-            this.on('change:rev2', function(model, rev2) {
-                model._onChangeRev2(rev2);
-            });
-            this._onChangeRev1(this.get('rev1'));
-            this._onChangeRev2(this.get('rev2'));
+            this.on('change:rev1', this._onChangeRev1, this);
+            this.on('change:rev2', this._onChangeRev2, this);
+            this._onChangeRev1(this, this.get('rev1'));
+            this._onChangeRev2(this, this.get('rev2'));
             return this;
         },
-        _onChangeRev1: function(rev1) {
+        _onChangeRev1: function(model, rev1) {
             var setmap = { disp_rev1: rev1 };
             if (rev1 !== undefined && rev1.match(/[a-f0-9]{40}/)) {
                 setmap.disp_rev1 = rev1.substring(0, 8);
             }
             this.set(setmap);
         },
-        _onChangeRev2: function(rev2) {
+        _onChangeRev2: function(model, rev2) {
             var setmap = { disp_rev2:  rev2 };
             if (rev2 !== undefined && rev2.match(/[a-f0-9]{40}/)) {
                 setmap.disp_rev2 = rev2.substring(0, 8);
@@ -441,7 +432,6 @@
 
     var IncomingLinksModel = exports.IncomingLinksModel = MasterPageModel.extend({
         urlRoot: '/api/inlinks/',
-        action: 'inlinks',
         _onChangePath: function(path) {
             IncomingLinksModel.__super__._onChangePath.apply(this, arguments);
             this.set('url_read', '/#/read/' + path);
@@ -450,7 +440,6 @@
 
     var WikiSearchModel = exports.WikiSearchModel = MasterPageModel.extend({
         urlRoot: '/api/search',
-        action: 'search',
         title: function() {
             return 'Search';
         },
@@ -460,21 +449,19 @@
     });
 
     var SpecialPagesModel = exports.SpecialPagesModel = MasterPageModel.extend({
-        action: 'special',
         title: function() {
             return 'Special Pages';
         },
         initialize: function() {
             SpecialPagesModel.__super__.initialize.apply(this, arguments);
-            this.nav.clearExtraUrls();
+        },
+        _addFooterExtraUrls: function() {
         }
     });
 
     var SpecialPageModel = exports.SpecialPageModel = MasterPageModel.extend({
-        action: 'special',
         initialize: function() {
             SpecialPageModel.__super__.initialize.apply(this, arguments);
-            this.nav.clearExtraUrls();
         }
     });
 
@@ -482,10 +469,10 @@
         title: "Wiki History",
         initialize: function() {
             SpecialChangesModel.__super__.initialize.apply(this, arguments);
-            this.on('change:history', this._onHistoryChanged);
+            this.on('change:history', this._onHistoryChanged, this);
         },
         url: function() {
-            var url = '/api/history';
+            var url = '/api/site-history';
             if (this.get('after_rev'))
                 url += '?rev=' + this.get('after_rev');
             return url;
--- a/wikked/assets/js/wikked/views.js	Sun Sep 28 23:23:51 2014 -0700
+++ b/wikked/assets/js/wikked/views.js	Sun Sep 28 23:24:44 2014 -0700
@@ -124,10 +124,8 @@
         isMainPage: true,
         initialize: function() {
             PageView.__super__.initialize.apply(this, arguments);
-            if (this.model !== undefined) {
-                var $view = this;
-                this.model.on("change", function() { $view._onModelChange(); });
-            }
+            if (this.model)
+                this.model.on("change", this._onModelChange, this);
             return this;
         },
         dispose: function() {
@@ -145,7 +143,12 @@
                 this.template = Handlebars.compile(_.result(this, 'templateSource'));
             }
             if (this.template !== undefined) {
-                this.renderTemplate(this.template);
+                var markup = this.renderTemplate(this.template);
+                var $markup = $(markup);
+                if (this.preRenderCallback !== undefined) {
+                    this.preRenderCallback($markup);
+                }
+                this.$el.append($markup);
                 if (this.renderCallback !== undefined) {
                     this.renderCallback();
                 }
@@ -156,7 +159,11 @@
             return this;
         },
         renderTemplate: function(tpl) {
-            this.$el.html(tpl(this.model.toJSON()));
+            var ctx = this.renderContext();
+            return tpl(ctx);
+        },
+        renderContext: function() {
+            return this.model.toJSON();
         },
         renderTitle: function(formatter) {
             var title = _.result(this.model, 'title');
@@ -172,26 +179,30 @@
     _.extend(PageView, Backbone.Events);
 
     var NavigationView = exports.NavigationView = PageView.extend({
+        className: 'nav-wrapper',
         templateSource: tplNav,
         isMainPage: false,
         initialize: function() {
             NavigationView.__super__.initialize.apply(this, arguments);
             return this;
         },
-        render: function() {
-            NavigationView.__super__.render.apply(this, arguments);
-
+        renderContext: function() {
+            var ctx = NavigationView.__super__.renderContext.apply(this, arguments);
+            var ima = localStorage.getItem('wikked.nav.isMenuActive');
+            if (ima == 'true') ctx.showMenu = true;
+            return ctx;
+        },
+        renderCallback: function() {
             // Hide the drop-down for the search results.
             this.searchPreviewList = this.$('#search-preview');
             this.searchPreviewList.hide();
             this.activeResultIndex = -1;
 
-            this.wikiMenu = $('#wiki-menu');
-            this.wrapperAndWikiMenu = $('.wrapper, #wiki-menu');
+            // Cache some stuff for handling the menu.
+            this.wikiMenu = this.$('#wiki-menu');
+            this.wrapperAndWikiMenu = this.$('.wrapper, #wiki-menu');
             this.isMenuActive = (this.wikiMenu.css('left') == '0px');
             this.isMenuActiveLocked = false;
-
-            return this;
         },
         events: {
             "click #wiki-menu-shortcut": "_onMenuShortcutClick",
@@ -208,6 +219,7 @@
         },
         _onMenuShortcutClick: function(e) {
             this.isMenuActive = !this.isMenuActive;
+            localStorage.setItem('wikked.nav.isMenuActive', this.isMenuActive);
         },
         _onMenuShortcutHover: function(e) {
             if (!this.isMenuActive && !this.isMenuActiveLocked)
@@ -313,6 +325,7 @@
     });
 
     var FooterView = exports.FooterView = PageView.extend({
+        className: 'footer-wrapper',
         templateSource: tplFooter,
         isMainPage: false,
         initialize: function() {
@@ -338,17 +351,37 @@
     });
 
     var MasterPageView = exports.MasterPageView = PageView.extend({
+        className: function() {
+            var cls = 'wrapper';
+
+            // HACK-ish: we need to know if the menu needs to be shown 
+            //           on init or not. Can't do it later otherwise
+            //           we'll get the CSS animation to play right away.
+            var ima = localStorage.getItem('wikked.nav.isMenuActive');
+            if (ima == 'true') cls += ' wiki-menu-active';
+
+            return cls;
+        },
         initialize: function() {
             MasterPageView.__super__.initialize.apply(this, arguments);
             this.nav = this._createNavigation(this.model.nav);
             this.footer = this._createFooter(this.model.footer);
             return this;
         },
+        dispose: function() {
+            if (this.footer) this.footer.dispose();
+            if (this.nav) this.nav.dispose();
+            MasterPageView.__super__.dispose.apply(this, arguments);
+        },
         renderCallback: function() {
-            this.$el.prepend('<div class="nav-wrapper"></div>');
-            this.$el.append('<div class="footer-wrapper"></div>');
-            this.nav.setElement(this.$('>.nav-wrapper')).render();
-            this.footer.setElement(this.$('>.footer-wrapper')).render();
+            if (this.nav) {
+                this.nav.render();
+                this.$el.prepend(this.nav.el);
+            }
+            if (this.footer) {
+                this.footer.render();
+                this.$el.append(this.footer.el);
+            }
             this.isError = (this.model.get('error_code') !== undefined);
         },
         templateSource: function() {
@@ -588,16 +621,8 @@
         defaultTemplateSource: tplSearchResults
     });
 
-    var SpecialNavigationView = exports.SpecialNavigationView = NavigationView.extend({
-        templateSource: tplSpecialNav
-    });
-
     var SpecialMasterPageView = exports.SpecialMasterPageView = MasterPageView.extend({
-        className: 'wrapper special',
-        _createNavigation: function(model) {
-            model.set('show_root_link', true);
-            return new SpecialNavigationView({ model: model });
-        }
+        className: 'wrapper special'
     });
 
     var SpecialPagesView = exports.SpecialPagesView = SpecialMasterPageView.extend({
--- a/wikked/assets/tpl/nav.html	Sun Sep 28 23:23:51 2014 -0700
+++ b/wikked/assets/tpl/nav.html	Sun Sep 28 23:24:44 2014 -0700
@@ -1,13 +1,13 @@
 <a id="wiki-menu-shortcut" class="wiki-logo">
     <span>W</span>
 </a>
-<nav id="wiki-menu" role="navigation" class="pure-menu pure-menu-open">
+<nav id="wiki-menu" role="navigation" class="pure-menu pure-menu-open{{#if showMenu}} wiki-menu-active{{/if}}">
     <ul class="">
         <li><a href="/"><span class="glyphicon glyphicon-home"></span> Home</a></li>
         <li><a href="/#/create/"><span class="glyphicon glyphicon-file"></span> New Page</a></li>
-        {{#ifneq action to='read'}}<li><a href="{{url_read}}"><span class="glyphicon glyphicon-book"></span> Read</a></li>{{/ifneq}}
-        {{#ifneq action to='edit'}}<li><a href="{{url_edit}}"><span class="glyphicon glyphicon-edit"></span> Edit</a></li>{{/ifneq}}
-        {{#ifneq action to='history'}}<li><a href="{{url_hist}}"><span class="glyphicon glyphicon-road"></span> History</a></li>{{/ifneq}}
+        {{#if url_read}}<li><a href="{{url_read}}"><span class="glyphicon glyphicon-book"></span> Read</a></li>{{/if}}
+        {{#if url_edit}}<li><a href="{{url_edit}}"><span class="glyphicon glyphicon-edit"></span> Edit</a></li>{{/if}}
+        {{#if url_hist}}<li><a href="{{url_hist}}"><span class="glyphicon glyphicon-road"></span> History</a></li>{{/if}}
     </ul>
     <form role="search" id="search" class="pure-form pure-menu-form pure-menu-divider">
         <fieldset>