# HG changeset patch # User Ludovic Chabant # Date 1366005956 25200 # Node ID 6d962a238c030f7339ce838f1b6c49e53a4aab8f # Parent 30784c036eadf7f7b8b7a7c02e9f3c3044f0c6d2 Added full preview button for page editing. diff -r 30784c036ead -r 6d962a238c03 static/js/wikked/views.js --- a/static/js/wikked/views.js Sun Apr 14 23:05:19 2013 -0700 +++ b/static/js/wikked/views.js Sun Apr 14 23:05:56 2013 -0700 @@ -37,6 +37,31 @@ var exports = {}; + // JQuery feature for watching size changes in a DOM element. + jQuery.fn.watch = function(id, fn) { + return this.each(function() { + var self = this; + var oldVal = self[id]; + $(self).data( + 'watch_timer', + setInterval( + function() { + if (self[id] !== oldVal) { + fn.call(self, id, oldVal, self[id]); + oldVal = self[id]; + } + }, + 100 + ) + ); + }); + }; + jQuery.fn.unwatch = function( id ) { + return this.each(function() { + clearInterval($(this).data('watch_timer')); + }); + }; + var PageView = exports.PageView = Backbone.View.extend({ tagName: 'div', className: 'wrapper', @@ -277,12 +302,21 @@ var PageEditView = exports.PageEditView = MasterPageView.extend({ defaultTemplateSource: tplEditPage, + dispose: function() { + PageEditView.__super__.dispose.apply(this, arguments); + this._removePreview(); + }, renderCallback: function() { PageEditView.__super__.renderCallback.apply(this, arguments); if (this.isError) { return; } + // Cache some stuff. + this._ctrlInput = $('#wmd-input'); + this._ctrlPreview = $('#wmd-preview'); + this._originalInputHeight = this._ctrlInput.height(); + // Create the Markdown editor. var formatter = new Client.PageFormatter(); formatter.baseUrl = this.model.get('path').match(/.*\//); @@ -290,17 +324,21 @@ converter.hooks.chain("preConversion", function(text) { return formatter.formatText(text); }); + var $view = this; var editor = new Markdown.Editor(converter); //TODO: pass options + editor.hooks.chain("onPreviewRefresh", function() { + $view._updateUI(true); + }); editor.run(); // Setup UI. this._updateUI(); $('#wmd-preview-wrapper').hide(); - this.originalHeight = $('#wmd-input').height(); }, events: { "mousedown #wmd-input-grip": "_inputGripMouseDown", "click #wmd-preview-button": "_togglePreview", + "click #wmd-full-preview-button": "_toggleFullPreview", "submit #page-edit": "_submitEditedPage" }, _inputGripMouseDown: function(e) { @@ -320,8 +358,32 @@ _togglePreview: function(e) { // Show/hide live preview. var w = $('body').width() - 40; - if ($('#wmd-preview').is(":visible")) { + if (this._ctrlPreview.is(":visible")) { + this._removePreview(); + } else { + this._addPreview(); + } + e.preventDefault(); + return false; + }, + _addPreview: function() { + $('#app') + .removeClass('container') + .addClass('container-fluid'); + $('#page-edit') + .removeClass('row') + .addClass('row-fluid'); $('#wmd-form-wrapper') + .removeClass('span12') + .addClass('span6'); + $('#wmd-preview-wrapper') + .show() + .addClass('span6'); + + this._updateUI(true); + }, + _removePreview: function() { + $('#wmd-form-wrapper') .removeClass('span6') .addClass('span12'); $('#wmd-preview-wrapper') @@ -333,31 +395,37 @@ $('#app') .removeClass('container-fluid') .addClass('container'); - $('#wmd-input').height(this.originalHeight); - this._updateUI(); - } else { - $('#app') - .removeClass('container') - .addClass('container-fluid'); - $('#page-edit') - .removeClass('row') - .addClass('row-fluid'); - $('#wmd-form-wrapper') - .removeClass('span12') - .addClass('span6'); - $('#wmd-preview-wrapper') - .show() - .addClass('span6'); - $('#wmd-input').height($('#wmd-preview').height()); - this._updateUI(); - } + + this._ctrlInput.height(this._originalInputHeight); + this._updateUI(); + }, + _toggleFullPreview: function(e) { + var $view = this; + var previewData = { + url: this.model.get('path'), + text: $('#wmd-input').val() + }; + $.post('/api/preview', previewData) + .success(function(data) { + $('#wmd-preview').html(data.text); + $view._updateUI(true); + }) + .error(function() { + $('#wmd-preview').html("Error running preview."); + }); e.preventDefault(); return false; }, - _updateUI: function() { + _updateUI: function(setHeight) { var inputWidth = $('#wmd-input-wrapper').innerWidth(); - $('#wmd-input') - .outerWidth(inputWidth); + this._ctrlInput.outerWidth(inputWidth); + + if (setHeight === true) { + var maxHeight = Math.max( + this._ctrlPreview.height(), + this._ctrlInput.height()); + this._ctrlInput.height(maxHeight); + } }, _submitEditedPage: function(e) { // Make the model submit the form. diff -r 30784c036ead -r 6d962a238c03 static/tpl/edit-page.html --- a/static/tpl/edit-page.html Sun Apr 14 23:05:19 2013 -0700 +++ b/static/tpl/edit-page.html Sun Apr 14 23:05:56 2013 -0700 @@ -37,6 +37,7 @@

Preview

+
diff -r 30784c036ead -r 6d962a238c03 wikked/resources/defaults.cfg --- a/wikked/resources/defaults.cfg Sun Apr 14 23:05:19 2013 -0700 +++ b/wikked/resources/defaults.cfg Sun Apr 14 23:05:56 2013 -0700 @@ -1,3 +1,4 @@ [wiki] scm=hg auto_update=False +default_extension=md diff -r 30784c036ead -r 6d962a238c03 wikked/views.py --- a/wikked/views.py Sun Apr 14 23:05:19 2013 -0700 +++ b/wikked/views.py Sun Apr 14 23:05:56 2013 -0700 @@ -1,3 +1,4 @@ +import re import time import os.path from flask import render_template, abort, request, g, jsonify @@ -6,9 +7,9 @@ from pygments.lexers import get_lexer_by_name from pygments.formatters import get_formatter_by_name from web import app, login_manager -from wiki import Page +from page import Page, PageData from fs import PageNotFoundError -from formatter import PageFormatter +from formatter import PageFormatter, FormattingContext import scm @@ -21,6 +22,30 @@ } +class DummyPage(Page): + def __init__(self, wiki, url, text): + Page.__init__(self, wiki, url) + self._text = text + + def _loadCachedData(self): + extension = self.wiki.config.get('wiki', 'default_extension') + data = PageData() + data.path = '__preview__.' + extension + data.filename = '__preview__' + data.extension = extension + data.raw_text = self._text + + ctx = FormattingContext(self.url, slugify=Page.title_to_url) + f = PageFormatter(self.wiki) + data.formatted_text = f.formatText(ctx, data.raw_text) + data.local_meta = ctx.meta + data.local_links = ctx.out_links + + data.title = Page.url_to_title(self.url) + + return data + + def get_page_or_none(url): try: page = g.wiki.getPage(url) @@ -333,7 +358,7 @@ links_to_remove |= set(links) app.logger.debug( links_to_remove) orphans = [o for o in orphans if o['path'] not in links_to_remove] - + result = {'orphans': orphans} return make_auth_response(result) @@ -367,6 +392,16 @@ return make_auth_response(result) +@app.route('/api/preview', methods=['POST']) +def api_preview(): + url = request.form.get('url') + text = request.form.get('text') + dummy = DummyPage(g.wiki, url, text) + + result = {'text': dummy.text} + return jsonify(result) + + @app.route('/api/admin/reindex', methods=['POST']) def api_admin_reindex(): if not current_user.is_authenticated() or not current_user.is_admin():