Commit 2400c5f4 authored by Michael Murtaugh's avatar Michael Murtaugh

stylerun

parent d1c4a96a
......@@ -4,6 +4,12 @@
<meta charset="utf-8">
<title>changesets</title>
<style type="text/css">
body {
background-color: gray;
}
h3 {
font-size: 100%;
}
.insert {
background: yellow;
color: black;
......@@ -40,6 +46,24 @@
.toggle_compacted .toggle_trigger:before {
content: '\25B8';
}
#content_text {
white-space: pre-wrap;
background-color: white;
}
span.attr0 {
background: hsla(0,100%,50%,1.00);
}
span.attr1 {
background: hsla(180,100%,50%,1.00);
}
span.attr2 {
background: hsla(90,100%,50%,0.05);
}
span.attr3 {
background: hsla(270,100%,50%,0.05);
}
</style>
</head>
<body>
......@@ -60,7 +84,7 @@
<h3 class="toggle_trigger">raw</h3>
<div id="content_raw" class="toggle_body"></div>
</div>
<div class="toggle">
<div class="toggle toggle_compacted">
<h3 class="toggle_trigger">general</h3>
<div id="content_summary" class="toggle_body"></div>
</div>
......@@ -68,10 +92,14 @@
<h3 class="toggle_trigger">ops</h3>
<div id="content_ops" class="toggle_body"></div>
</div>
<div class="toggle">
<div class="toggle toggle_compacted">
<h3 class="toggle_trigger">bank</h3>
<div id="content_bank" class="toggle_body"></div>
</div>
<div class="toggle">
<h3 class="toggle_trigger">text</h3>
<div id="content_text" class="toggle_body"></div>
</div>
</div>
<script src="dist/etherpad.js"></script>
......@@ -79,6 +107,7 @@
let content_raw = document.getElementById("content_raw"),
content_summary = document.getElementById("content_summary"),
content_bank = document.getElementById("content_bank"),
content_text = document.getElementById("content_text"),
content_ops = document.getElementById("content_ops"),
rev = document.getElementById("rev"),
total_revs = document.getElementById("total_revs"),
......@@ -134,6 +163,8 @@ function show_changeset (n) {
content_summary.innerHTML = "";
content_summary.appendChild(changeset_summary_html(cs));
content_ops.innerHTML = "";
// content_text.innerHTML = pad.get_revision(n).get_html();
content_text.innerHTML = pad.curtext.get_html();
for (var i=0, l=cs.ops.length; i<l; i++) {
content_ops.appendChild(htmlify_changeset_op(cs.ops[i]));
}
......@@ -141,10 +172,12 @@ function show_changeset (n) {
}
function do_next () {
if (current_rev < pad.last_rev) { show_changeset(++current_rev); }
// if (current_rev < pad.last_rev) { show_changeset(++current_rev); }
pad.next();
show_changeset(pad.rev);
}
function do_prev () {
if (current_rev > 0) { show_changeset(--current_rev); }
// if (current_rev > 0) { show_changeset(--current_rev); }
}
next.addEventListener("click", do_next);
......
This diff is collapsed.
import { StyleRun } from './stylerun.js';
async function wait (time) {
return new Promise(resolve => {
......@@ -51,92 +52,30 @@ function changeset_parse (c) {
return ret;
}
class Attributes {
constructor (attr) {
this.attributes = attr || [];
// an attribute entry has the form
// {
// start: set,
// end: set
// }
}
insert (startpos, endpos, attributes) {
// scan over the affected characters
// read current attributes at startpos
// extend / add as needed
// arrange state per attribute index...
// each attribute index is either on or off at any given position...
// Maintain "style runs" for each attribute as
// ordered lists of pairs of starting and ending (character) indexes
// load current state at startpos... advance each "style run" to a certain character position
var attrs = set();
for (let i=0; i<startpos; i++) {
let d = this.attributes[i];
if (d) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
let ee = d.end.entries(),
e = ee.next();
while (!e.done) {
attrs.delete(e.value[1]);
e = ee.next();
}
ee = d.start.entries();
e = ee.next();
while (!e.done) {
attrs.add(e.value[1]);
e = ee.next();
}
}
}
// assert
for (let i=0; i<numchars; i++) {
let p = pos+i,
attr = this.attributes[p];
if (attr === undefined) {
this.attributes[p] = attr = new Set();
}
attributes.forEach(x => { attr.add(x) });
}
}
delete (pos, numchars) {
this.attributes.splice(pos, numchars);
}
copy () {
return new Attributes(this.attributes.slice());
}
get_runs (startpos, endpos) {
}
}
class Text {
constructor (text, attr) {
this.text = text;
this.attributes = attr;
if (attr == undefined) {
this.attributes = new Attributes();
}
this.text = text || '\n';
this.attributes = attr || [];
}
perform_changeset (c) {
// process the given change set returning a new Text object
// process the given change set -- modifying this object in place
let textpos = 0,
textline = 0,
bank = c.bank,
bankpos = 0,
newtext = '',
// newattributes = this.attributes.slice(),
current_attributes = [];
// loop through the operations
// rebuilding the final text
for (let i=0, ops=c.ops.length; i<ops; i++) {
let op = c.ops[i];
if (op.op != "attr") {
if (op.op == "attr") {
// console.log(op);
// record this attribute at current
// add op to current attibutes to be applied to subsequent inserts
// console.log("attr", op.index);
current_attributes.push(op.index);
// attributes.set(textpos, op.index)
}
......@@ -146,12 +85,41 @@ class Text {
newtext += insertion_text;
// advance positions
bankpos += op.chars;
this.attributes.insert(newtextposition, op.chars, current_attributes);
current_attributes.forEach(a => {
// console.log("ATTR", a);
if (this.attributes[a] === undefined) {
this.attributes[a] = new StyleRun();
}
// this.attributes[a].insert(newtextposition, op.chars);
});
for (var a in this.attributes) {
console.log("INSERTATTR", a);
if (this.attributes.hasOwnProperty(a)) {
let sr = this.attributes[a];
console.log("sr", sr);
if (current_attributes.indexOf(parseInt(a)) > -1) {
console.log("INSERT");
sr.insert(newtextposition, op.chars);
} else {
console.log("BLANK")
sr.insert_blank(newtextposition, op.chars);
}
}
}
// importantly, on an insert, the (original/old/previous) textpos
// does *not* increment...
} else if (op.op == "delete") {
let newtextposition = newtext.length; // is this right?
// todo: delete attributes as well
this.attributes.delete(textpos, op.chars);
current_attributes.forEach(a => {
if (this.attributes[a] === undefined) {
return;
}
this.attributes[a].delete(newtextposition, op.chars);
});
textpos += op.chars;
} else if (op.op == "hold") {
newtext += this.text.substring(textpos, textpos+op.chars);
......@@ -160,7 +128,14 @@ class Text {
}
// append rest of old text...
newtext += this.text.substring(textpos);
return new Text(newtext, this.attributes.slice());
this.text = newtext;
// return new Text(newtext, newattributes);
}
get_text () {
return this.text;
}
get_html () {
return StyleRun.render_runs(this.attributes, this.text);
}
}
......@@ -181,8 +156,30 @@ export class Etherpad {
this.padname = padname;
this.text = "\n";
this.changesets = null;
//this.revs = new Array();
this.curtext = new Text("\n");
this.rev = -1;
}
next () {
if (this.rev < this.last_rev) {
this.rev += 1;
this.curtext.perform_changeset(this.changesets[this.rev]);
}
}
// calculate_revisions () {
// var text = new Text("\n");
// for (var i=0; i<=this.last_rev; i++) {
// text = this.revs[i] = text.perform_changeset(this.changesets[i]);
// }
// console.log("calculated revisions", this.revs);
// }
// get_revision (n) {
// return this.revs[n];
// }
async animate (callback, steptime) {
var curtext = "\n";
for (var i=0; i<=this.last_rev; i++) {
......@@ -196,6 +193,23 @@ export class Etherpad {
//console.log("CHECK", (curtext == pad_data.atext.text))
}
async animate2 (callback, steptime) {
var text = new Text("\n");
for (var i=0; i<=this.last_rev; i++) {
let newtext = text.perform_changeset(this.changesets[i]);
// change.rev = i;
callback({
'html': newtext.get_html(),
'rev': i
});
text = newtext;
// console.log(curtext);
await wait(steptime||100);
}
return text;
//console.log("CHECK", (curtext == pad_data.atext.text))
}
parse_json (data) {
let pad_data = data[`pad:${this.padname}`],
last_rev = pad_data.head;
......
class StyleRun {
export class StyleRun {
static render_runs (obj, text) {
var runs = [];
var allintervals = [];
var by_pos = {};
for (var key in obj) {
let run = obj[key];
console.log("run", key);
console.log(run.unparse());
console.log("---");
runs.push(run);
for (var i=0, len=run.runs.length; i<len; i++) {
let interval = run.runs[i];
interval.attr = key;
if (by_pos[interval.start] === undefined) {
by_pos[interval.start] = {starts: [], ends: [], value: interval.start}
}
by_pos[interval.start].starts.push(interval);
if (by_pos[interval.end] === undefined) {
by_pos[interval.end] = {starts: [], ends: [], value: interval.end }
}
by_pos[interval.end].ends.push(interval);
allintervals.push(interval);
}
}
var nodes = [];
for (var key in by_pos) {
nodes.push(by_pos[key]);
}
nodes.sort((a, b) => {
return a.value - b.value;
});
function to_list (set) {
return Array.from(set);
// return [...set];
// let array = [];
// set.forEach(x => array.push(x));
}
console.log("render_runs", text.length, text);
console.log("nodes", nodes);
var r = '',
last_node_pos = undefined,
attrs = new Set(),
spans = [];
for (var i=0, len=nodes.length; i<len; i++) {
let node = nodes[i];
if (last_node_pos!== undefined) {
spans.push({
text: text.substring(last_node_pos, node.value),
classlist: to_list(attrs).map(x => `attr${x}`).join(" ")
});
}
last_node_pos = node.value;
node.ends.forEach(x => attrs.delete(x.attr));
node.starts.forEach(x => attrs.add(x.attr));
}
console.log("rendering", spans);
return spans.map(x => `<span class="${x.classlist}">${x.text}</span>`).join("");
// console.log("got", runs.length, "runs", allintervals.length, "total intervals");
// allintervals.sort((a, b) => {
// return a.start - b.start;
// });
// console.log("allintervals", allintervals);
}
constructor () {
this.runs = [];
}
......@@ -50,6 +122,7 @@ class StyleRun {
// insertion point is fully before
// insert new interval
this.runs.splice(i, 0, {start: start, end: start+chars});
len += 1;
// shift subsequent intervals
for (let j=i+1; j<len; j++) {
this.runs[j].start += chars;
......@@ -71,6 +144,44 @@ class StyleRun {
// add new interval clean at the end
this.runs.push({start: start, end: start+chars});
}
insert_blank (start, chars) {
// insert a sequence of characters *not* with the given attribute
// (use when an inserting *another* attribute -- keeps attributes in parallel)
let i=0,
len = this.runs.length,
run;
for (; i<len; i++) {
run = this.runs[i];
if (start <= run.start) {
// insertion point is before/at this interval
// shift this & subsequent intervals
for (let j=i; j<len; j++) {
this.runs[j].start += chars;
this.runs[j].end += chars;
}
return;
} else if (start < run.end) {
// start > run.start
// insertion point inside this interval, nb start != run.end
// split this interval at start
this.runs.splice(i+1, 0, {start: start+chars, end: run.end+chars});
len += 1;
i+=1;
// end current interval at the start of the split
run.end = start;
// this should happen above ?!
// // and shift all subsequent...
// for (let j=i+1; j<len; j++) {
// this.runs[j].start += chars;
// this.runs[j].end += chars;
// }
// return;
}
}
// add new interval clean at the end
// this.runs.push({start: start, end: start+chars});
}
delete (start, chars) {
let end = start + chars,
i=0,
......@@ -132,11 +243,18 @@ class StyleRun {
}
// var sr = new StyleRun();
// sr.insert(10, 10);
// console.log("---");
// console.log(sr.unparse());
/*
var sr = new StyleRun();
sr.insert(0, 100);
sr.insert(200, 10);
console.log("---");
console.log(sr.unparse());
console.log("---");
sr.insert_blank(100, 5);
console.log(sr.unparse());
*/
// var pos = 1,
// del = 18;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment