view static/js/wikked/models.js @ 134:f47b047c9414

Changed URL schemes around meta pages: - They're now handled the same as normal pages. - Added endpoint to get the main page. - Reduced a bit the amount of redundant unquoting/URL massaging.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 05 Dec 2013 08:22:55 -0800
parents 9d22cf4d2412
children f02e049d6546
line wrap: on
line source

/**
 * Wikked models.
 */
define([
        'require',
        'jquery',
        'underscore',
        'backbone',
        'handlebars'
        ],
    function(require, $, _, Backbone, Handlebars) {

    var exports = {};

    var NavigationModel = exports.NavigationModel = Backbone.Model.extend({
        idAttribute: 'path',
        defaults: function() {
            return {
                path: "",
                action: "read",
                user: false
            };
        },
        initialize: function() {
            this.on('change:path', function(model, path) {
                model._onChangePath(path);
            });
            this._onChangePath(this.get('path'));
            this.on('change:auth', function(model, auth) {
                model._onChangeAuth(auth);
            });
            this._onChangeAuth(this.get('auth'));
            return this;
        },
        doPreviewSearch: function(query, callback) {
            if (this._isSearching) {
                return;
            }
            this._isSearching = true;
            var $model = this;
            $.getJSON('/api/search', { q: query })
                .success(function (data) {
                    $model._isSearching = false;
                    callback(data);
                })
                .error(function() {
                    $model._isSearching = false;
                });
        },
        doSearch: function(form) {
            this.navigate('/search/' + $(form.q).val(), { trigger: true });
        },
        _onChangePath: function(path) {
            this.set({
                url_home: '/',
                url_read: '/#/read/' + path,
                url_edit: '/#/edit/' + path,
                url_hist: '/#/changes/' + path,
                url_search: '/search'
            });
        },
        _isSearching: false,
        _onChangeAuth: function(auth) {
            if (auth) {
                this.set({
                    url_login: false,
                    url_logout: '/#/logout',
                    username: auth.username
                });
            } else {
                this.set({
                    url_login: '/#/login',
                    url_logout: false,
                    username: false
                });
            }
        }
    });

    var FooterModel = exports.FooterModel = Backbone.Model.extend({
        defaults: function() {
            return {
                url_extras: [
                    { name: 'Special Pages', url: '/#/special' }
                ]
            };
        },
        clearExtraUrls: function() {
            this.get('url_extras').length = 0;
        },
        addExtraUrl: function(name, url, index) {
            if (index === undefined) {
                this.get('url_extras').push({ name: name, url: url });
            } else {
                this.get('url_extras').splice(index, 0, { name: name, url: url });
            }
        }
    });

    var LoginModel = exports.LoginModel = Backbone.Model.extend({
        setApp: function(app) {
            this.app = app;
        },
        doLogin: function(form) {
            var $model = this;
            $.post('/api/user/login', $(form).serialize())
                .success(function() {
                    $model.navigate('/', { trigger: true });
                })
                .error(function() {
                    $model.set('has_error', true);
                });
        }
    });

    var PageModel = exports.PageModel = Backbone.Model.extend({
        idAttribute: 'path',
        defaults: function() {
            return {
                path: "main-page"
            };
        },
        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('');
            return this;
        },
        url: function() {
            var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || Backbone.urlError();
            if (this.isNew()) return base;
            return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + this.id;
        },
        title: function() {
            return this.getMeta('title');
        },
        getMeta: function(key) {
            var meta = this.get('meta');
            if (meta === undefined) {
                return undefined;
            }
            return meta[key];
        },
        setApp: function(app) {
            this.app = app;
            if (this._onAppSet !== undefined) {
                this._onAppSet(app);
            }
        },
        _onChangePath: function(path) {
        },
        _onChangeText: function(text) {
            this.set('content', new Handlebars.SafeString(text));
        }
    });

    var PageStateModel = exports.PageStateModel = PageModel.extend({
        urlRoot: '/api/state/',
        _onChangePath: function(path) {
            PageStateModel.__super__._onChangePath.apply(this, arguments);
            this.set('url_edit', '/#/edit/' + path);
        }
    });

    var MasterPageModel = exports.MasterPageModel = PageModel.extend({
        initialize: function() {
            this.nav = new NavigationModel({ id: this.id });
            this.footer = new FooterModel();
            MasterPageModel.__super__.initialize.apply(this, arguments);
            this.on('change:auth', function(model, auth) {
                model._onChangeAuth(auth);
            });
            this.on('error', this._onError, this);
            if (this.action !== undefined) {
                this.nav.set('action', this.action);
                this.footer.set('action', this.action);
            }
            return this;
        },
        _onAppSet: function(app) {
            this.nav.app = app;
            this.footer.app = app;
        },
        _onChangePath: function(path) {
            MasterPageModel.__super__._onChangePath.apply(this, arguments);
            this.nav.set('path', path);
        },
        _onChangeAuth: function(auth) {
            this.nav.set('auth', auth);
        },
        _onError: function(model, resp) {
            var setmap = {
                has_error: true,
                error_code: resp.status
            };
            switch (resp.status) {
                case 401:
                    setmap.error = 'Unauthorized';
                    break;
                case 404:
                    setmap.error = 'Not Found';
                    break;
                default:
                    setmap.error = 'Error';
                    break;
            }
            this.set(setmap);
        }
    });

    var PageReadModel = exports.PageReadModel = MasterPageModel.extend({
        urlRoot: '/api/read/',
        action: 'read',
        initialize: function() {
            PageReadModel.__super__.initialize.apply(this, arguments);
            this.on('change', this._onChange, this);

            // Add extra links to the footer.
            var model = this;
            this.footer.addExtraUrl('Pages Linking Here', function() { return '/#/inlinks/' + model.id; }, 1);
            this.footer.addExtraUrl('JSON', function() { return '/api/read/' + model.id; });
        },
        checkStatePath: function() {
            return this.get('path');
        },
        _onChange: function() {
            if (this.getMeta('redirect')) {
                // Handle redirects.
                var newPath = this.getMeta('redirect').replace(/^\//, "");
                if (this.get('no_redirect')) {
                    this.set({ 'redirects_to': newPath }, { 'silent': true });
                } else {
                    var oldPath = this.get('path');
                    this.set({
                        'path': newPath,
                        'redirected_from': oldPath
                    }, {
                        'silent': true
                    });
                    this.fetch();
                    this.navigate('/read/' + newPath, { replace: true, trigger: false });
                }
            }
        }
    });

    var PageSourceModel = exports.PageSourceModel = MasterPageModel.extend({
        urlRoot: '/api/raw/',
        action: 'source'
    });

    var PageEditModel = exports.PageEditModel = MasterPageModel.extend({
        action: 'edit',
        urlRoot: '/api/edit/',
        doEdit: function(form) {
            var $model = this;
            $.post(this.url(), $(form).serialize())
                .success(function(data) {
                    $model._onEditSuccess();
                })
                .error(function() {
                    alert('Error saving page...');
                });
        },
        _onChangePath: function(path) {
            PageEditModel.__super__._onChangePath.apply(this, arguments);
            this.set('url_read', this._getReadPath(path));
        },
        _onEditSuccess: function() {
            this.navigate('/read/' + this.get('path'), { trigger: true });
        },
        _getReadPath: function(path) {
            return '/#/read/' + path;
        }
    });

    var PageHistoryModel = exports.PageHistoryModel = MasterPageModel.extend({
        urlRoot: '/api/history/',
        action: 'history',
        initialize: function() {
            PageHistoryModel.__super__.initialize.apply(this, arguments);
            var model = this;
            this.footer.addExtraUrl('JSON', function() { return '/api/history/' + model.id; });
        },
        doDiff: function(form) {
            var rev1 = $('input[name=rev1]:checked', form).val();
            var rev2 = $('input[name=rev2]:checked', form).val();
            this.navigate('/diff/r/' + this.get('path') + '/' + rev1 + '/' + rev2, { trigger: true });
        },
        _onChangePath: function(path) {
            PageHistoryModel.__super__._onChangePath.apply(this, arguments);
            this.set({
                url_read: '/#/read/' + path,
                url_edit: '/#/edit/' + path,
                url_rev: '/#/revision/' + path,
                url_diffc: '/#/diff/c/' + path,
                url_diffr: '/#/diff/r/' + path
            });
        }
    });

    var PageRevisionModel = exports.PageRevisionModel = MasterPageModel.extend({
        action: 'revision',
        defaults: function() {
            return {
                path: "main-page",
                rev: "tip"
            };
        },
        url: function() {
            return '/api/revision/' + this.get('path') + '?rev=' + this.get('rev');
        },
        initialize: function() {
            PageRevisionModel.__super__.initialize.apply(this, arguments);
            this.on('change:rev', function(model, rev) {
                model._onChangeRev(rev);
            });
            this._onChangeRev(this.get('rev'));
            return this;
        },
        doRevert: function(form) {
            var $model = this;
            var path = this.get('path');
            $.post('/api/revert/' + path, $(form).serialize())
                .success(function(data) {
                    $model.navigate('/read/' + path, { trigger: true });
                })
                .error(function() {
                    alert('Error reverting page...');
                });
        },
        _onChangeRev: function(rev) {
            var setmap = { disp_rev: rev };
            if (rev.match(/[a-f0-9]{40}/)) {
                setmap.disp_rev = rev.substring(0, 8);
            }
            this.set(setmap);
        }
    });

    var PageDiffModel = exports.PageDiffModel = MasterPageModel.extend({
        action: 'diff',
        defaults: function() {
            return {
                path: "main-page",
                rev1: "tip",
                rev2: ""
            };
        },
        url: function() {
            var apiUrl = '/api/diff/' + this.get('path') + '?rev1=' + this.get('rev1');
            if (this.get('rev2')) {
                apiUrl += '&rev2=' + this.get('rev2');
            }
            return apiUrl;
        },
        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'));
            return this;
        },
        _onChangeRev1: function(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) {
            var setmap = { disp_rev2:  rev2 };
            if (rev2 !== undefined && rev2.match(/[a-f0-9]{40}/)) {
                setmap.disp_rev2 = rev2.substring(0, 8);
            }
            this.set(setmap);
        }
    });

    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);
        }
    });

    var WikiSearchModel = exports.WikiSearchModel = MasterPageModel.extend({
        urlRoot: '/api/search/',
        action: 'search',
        title: function() {
            return 'Search';
        },
        execute: function(query) {
            var $model = this;
            $.getJSON('/api/search', { q: query })
                .success(function (data) {
                    $model.set('hits', data.hits);
                })
                .error(function() {
                    alert("Error searching...");
                });
        }
    });

    var SpecialPagesModel = exports.SpecialPagesModel = MasterPageModel.extend({
        action: 'special',
        title: function() {
            return 'Special Pages';
        },
        initialize: function() {
            SpecialPagesModel.__super__.initialize.apply(this, arguments);
            this.footer.clearExtraUrls();
        }
    });

    var SpecialPageModel = exports.SpecialPageModel = MasterPageModel.extend({
        action: 'special',
        initialize: function() {
            SpecialPageModel.__super__.initialize.apply(this, arguments);
            this.footer.clearExtraUrls();
        }
    });

    var SpecialChangesModel = exports.SpecialChangesModel = SpecialPageModel.extend({
        title: "Wiki History",
        url: '/api/history'
    });

    var SpecialOrphansModel = exports.SpecialOrphansModel = SpecialPageModel.extend({
        title: "Orphaned Pages",
        url: '/api/orphans'
    });

    return exports;
});