diff --git a/sourcing/static/js/xanaedit.js b/sourcing/static/js/xanaedit.js new file mode 100644 index 0000000..3e117fe --- /dev/null +++ b/sourcing/static/js/xanaedit.js @@ -0,0 +1,112 @@ +'use strict'; + +function common_start(a, b) { + for(var i = 0; i < a.length; i++) + if(a[i] != b[i]) + return i; + return a.length +} + +function common_end(a, b) { + var min_len = Math.min(a.length, b.length); + + for(var i = 1; i < min_len + 1; i++) + if(a[a.length - i] != b[b.length - i]) + return i - 1; + return min_len; +} + +var editor = document.getElementById("editor"); +var changes = document.getElementById("changes"); + +var edits = []; + +editor.value = text;; + +function update_list_of_edits() { + changes.innerHTML = null; + edits.forEach(function(edit) { + var update; + var start = edit.start + 1; + if (edit.op == 'insert') { + update = start + ' insert: [' + edit.new + ']'; + } else if (edit.op == 'delete') { + update = start + ' delete: [' + edit.old + ']'; + } else if (edit.op == 'replace') { + update = start + ' replace: [' + edit.old + '] with [' + edit.new + ']'; + } + var div = document.createElement("div"); + div.innerHTML = update; + changes.append(div); + }); +} + +function update_buttons () { + if(edits.length) { + $('.save-btn').prop('disabled', false); + $('#cancel-btn').text('cancel edits'); + } else { + $('.save-btn').prop('disabled', true); + $('#cancel-btn').text('back to view page'); + } +} + +function update(e) { + var current = editor.value; + if (current == text) // no change + return + + var start = common_start(text, current); + var end = common_end(text.substring(start), current.substring(start)); + + var old_text = text.slice(start, text.length - end); + var new_text = current.slice(start, current.length - end); + text = current; + + // var div = document.createElement("div"); + var prev_edit = edits[edits.length - 1]; + if (old_text && !new_text) { // delete + if (prev_edit && prev_edit.op == 'delete' && prev_edit.start == start + 1) { + prev_edit.start = start; + prev_edit.old = old_text + prev_edit.old; + } else if (prev_edit && prev_edit.op == 'delete' && prev_edit.start == start) { + prev_edit.old += old_text; + } else if (prev_edit && prev_edit.op == 'insert' && + start - prev_edit.new.length + old_text.length == prev_edit.start && + prev_edit.new.endsWith(old_text)) { + if (prev_edit.new == old_text) { + edits.splice(-1, 1); + } else { + prev_edit.new = prev_edit.new.slice(0, prev_edit.new.length - old_text.length); + } + } else { + edits.push({'op': 'delete', 'start': start, 'old': old_text}); + } + // div.innerHTML = start + ' delete: [' + old_text + ']'; + } else if (!old_text && new_text) { // insert + if (prev_edit && prev_edit.op == 'insert' && prev_edit.start == start - prev_edit.new.length) { + prev_edit.new += new_text; + } else { + edits.push({'op': 'insert', 'start': start, 'new': new_text}); + } + // div.innerHTML = start + ' insert: [' + new_text + ']'; + } else { // replace + edits.push({'op': 'replace', 'start': start, 'new': new_text, 'old': old_text}); + // div.innerHTML = start + ' replace [' + old_text + '] with [' + new_text + ']'; + } + // changes.append(div); + + + $('input#edits').val(JSON.stringify(edits)); + + update_list_of_edits(); + update_buttons(); +} + +update_buttons(); +editor.addEventListener("input", update, false); + +$('#save-btn').click(function(e) { + e.preventDefault(); + console.log('save'); +}); diff --git a/sourcing/templates/view.html b/sourcing/templates/view.html index b69e5a5..e47dd8e 100644 --- a/sourcing/templates/view.html +++ b/sourcing/templates/view.html @@ -113,7 +113,7 @@

Edit EDL - Xanaedit text + Xanaedit text Reorder paragraphs

diff --git a/sourcing/templates/xanaedit.html b/sourcing/templates/xanaedit.html new file mode 100644 index 0000000..be2f2ee --- /dev/null +++ b/sourcing/templates/xanaedit.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% from "form/controls.html" import render_field %} + +{% block title %}xanaedit {{ doc.title() }}{% endblock %} + +{% block content %} + +

{{ doc.title() }}

+ +

view xanadoc (cancel edit)

+ +
+ + +
+
+ +

+ + + back to view page +

+ + +
+
+
+
+ +
+ +{% endblock %} + +{% block scripts %} + + +{% endblock %} diff --git a/sourcing/view.py b/sourcing/view.py index f69b767..3264b24 100644 --- a/sourcing/view.py +++ b/sourcing/view.py @@ -8,7 +8,9 @@ from .forms import (LoginForm, SignupForm, AccountSettingsForm, from .model import User, SourceDoc, Item, XanaDoc, XanaLink from .url import get_url from .mail import send_mail -from .edl import fulfil_edl_with_sources, fulfil_edl +from .edl import fulfil_edl_with_sources, fulfil_edl, parse_edl +from .span import Span +from .edit import apply_edits from .database import session from .text import iter_lines, add_highlight from werkzeug.debug.tbtools import get_current_traceback @@ -17,7 +19,9 @@ from functools import wraps from .utils import nbsp_at_start from itsdangerous import URLSafeTimedSerializer from sqlalchemy_continuum import version_class +from pprint import pformat +import json import re login_manager = LoginManager() @@ -218,7 +222,6 @@ def realize_edl(username, hashid): item = get_xanadoc(username, hashid) spans = list(fulfil_edl(item.text)) - doc_text = ''.join(span['text'] for span in spans) return render_template('realize.html', @@ -298,8 +301,36 @@ def history(username, hashid): @bp.route('///xanaedit') def xanaedit_item(username, hashid): - obj = get_item(username, hashid) - return render_template('xanaedit.html', doc=obj) + doc = get_xanadoc(username, hashid) + + spans = list(fulfil_edl(doc.text)) + doc_text = ''.join(span['text'] for span in spans) + + return render_template('xanaedit.html', doc=doc, doc_text=doc_text) + +@bp.route('///xanaedit', methods=['POST']) +def save_xanaedit(username, hashid): + # doc = get_xanadoc(username, hashid) + edits = json.loads(request.form['edits']) + return jsonify(edits=edits) + +@bp.route('///finish', methods=['POST']) +def finish_xanaedit(username, hashid): + doc = get_xanadoc(username, hashid) + current_edl = parse_edl(doc.text) + + spans = [Span(*span) for span in current_edl['spans']] + edits = json.loads(request.form['edits']) + new_text = '' + new_text_pos = 0 + for edit in edits: + if edit['op'] != 'insert': + continue + new_text += edit['new'] + edit['span'] = Span('placeholder', new_text_pos, len(edit['new'])) + new_text_pos += len(edit['new']) + spans = apply_edits(spans, edits) + return Response(pformat(spans), mimetype='text/plain') @bp.route('///edit', methods=['GET', 'POST']) def edit_item(username, hashid):