···8989 return spans
9090}
91919292-export function make_windows(spans) {
9393- // at begin, push its type to stack
9494- // at end, pop
9595- // make list of begins and ends
9696- // sort
9797- // iterate
9898- let edges = []
9999- for (let x of spans) {
100100- edges.push([x.start, x])
101101- edges.push([x.end, x])
102102- }
103103- edges.sort(([a],[b])=>a-b)
104104- let open = new Set()
105105- let windows = []
106106- let last = 0
107107- for (let [index,span] of edges) {
108108- if (index > last && open.size) {
109109- windows.push({
110110- start: last,
111111- end: index,
112112- spans: [...open],
113113- })
114114- }
115115- open.delete(span) || open.add(span) // toggle
116116- last = index
117117- }
118118- return windows
119119-}
120120-// note "group" is perpendicular to "span" (need better name)
121121-12292function index_to_byteindex(string, index) {
12393 return unescape(encodeURIComponent(string.slice(0,index))).length
12494}
12595126126-export function propagate_strip(windows) {
127127- for (let win of windows) {
128128- win.spans = win.spans.map(span=>{
129129- let length = win.end-win.start
130130- function clamp(x) {
131131- if (x<0)
132132- return 0
133133- if (x>length)
134134- return length
135135- return x
136136- }
137137- let inset_left = win.start - span.start
138138- let inset_right = span.end - win.end
139139- let strip_left = clamp(span.strip[0] - inset_left)
140140- let strip_right = clamp(span.strip[1] - inset_right)
141141- let span2 = {
142142- feature: span.feature,
143143- strip: [strip_left, strip_right],
144144- }
145145- return span2
146146- })
147147- }
148148-}
149149-15096function has_client_strip(feature) {
15197 return feature.$type=="com.example.richtext.facet#markup"
15298}
15399100100+// todo: exclusion like "dont allow anything overlapping this one" -> delete all reflections of a span if anything overlaps one of its reflections, or vice versa.
154101155155-export function apply_strips(obj) {
156156- let text = obj.text
157157- let shift = 0
158158- function cut(index, amount) {
159159- console.log(cut, index, amount)
160160- text = text.slice(0, index) + text.slice(index+amount)
161161- shift += amount
102102+export class MarkedText {
103103+ constructor(text) {
104104+ this.text = text
105105+ this.spans = []
106106+ this.windows = null
162107 }
163163-164164- for (let i=0; i<obj.windows.length; i++) {
165165- let win = obj.windows[i]
166166- let lstrip = 0
167167- let rstrip = 0
168168- // find the max strip we have to remove
169169- for (let span of win.spans) {
170170- if (!has_client_strip(span.feature)) {
171171- lstrip = Math.max(lstrip, span.strip[0])
172172- rstrip = Math.max(rstrip, span.strip[1])
173173- }
108108+ make_windows() {
109109+ let edges = []
110110+ for (let span of this.spans) {
111111+ edges.push([span.start, span])
112112+ edges.push([span.end, span])
113113+ }
114114+ edges.sort(([a],[b])=>a-b)
115115+ let open = new Set()
116116+ this.windows = []
117117+ let last = 0
118118+ for (let [index,span] of edges) {
119119+ if (index > last && open.size)
120120+ this.windows.push({start: last, end: index, spans: [...open]})
121121+ open.delete(span) || open.add(span) // toggle
122122+ last = index
174123 }
175175- let len = win.end-win.start
176176- win.start -= shift
177177- win.end -= shift
178178- if (lstrip+rstrip > len) {
179179- // invalid
180180- throw new Error("heck darn")
181181- } else if (lstrip+rstrip == len) {
182182- // cut out the entire thing
183183- cut(win.start, len)
184184- obj.windows.splice(i, 1)
185185- i--
186186- } else {
187187- if (lstrip) {
188188- cut(win.start, lstrip)
189189- win.end -= lstrip
190190- }
191191- if (rstrip) {
192192- cut(win.end-rstrip, rstrip)
193193- win.end -= rstrip
194194- }
195195-196196- // decrease strip amounts
124124+ // propagate strips
125125+ for (let win of this.windows) {
126126+ win.spans = win.spans.map(span=>{
127127+ let length = win.end-win.start
128128+ function clamp(x) {
129129+ if (x<0)
130130+ return 0
131131+ if (x>length)
132132+ return length
133133+ return x
134134+ }
135135+ let inset_left = win.start - span.start
136136+ let inset_right = span.end - win.end
137137+ let strip_left = clamp(span.strip[0] - inset_left)
138138+ let strip_right = clamp(span.strip[1] - inset_right)
139139+ let span2 = {
140140+ feature: span.feature,
141141+ strip: [strip_left, strip_right],
142142+ }
143143+ return span2
144144+ })
145145+ }
146146+ }
147147+ create_facets() {
148148+ let facets = []
149149+150150+ let text = this.text
151151+ let shift = 0
152152+ function cut(index, amount) {
153153+ text = text.slice(0, index) + text.slice(index+amount)
154154+ shift += amount
155155+ }
156156+157157+ for (let win of this.windows) {
158158+ // PRE-APPLY STRIP to facets which don't support that:
159159+ let lstrip = 0
160160+ let rstrip = 0
161161+ // find the max strip we have to remove
197162 for (let span of win.spans) {
198198- span.strip[0] = Math.max(0, span.strip[0]-lstrip)
199199- span.strip[1] = Math.max(0, span.strip[1]-rstrip)
163163+ if (!has_client_strip(span.feature)) {
164164+ lstrip = Math.max(lstrip, span.strip[0])
165165+ rstrip = Math.max(rstrip, span.strip[1])
166166+ }
167167+ }
168168+ let start = win.start-shift
169169+ let end = win.end-shift
170170+ let len = end-start
171171+ if (lstrip+rstrip > len) {
172172+ // invalid
173173+ throw new Error("heck darn")
174174+ } else if (lstrip+rstrip == len) {
175175+ // cut out the entire thing
176176+ cut(start, len)
177177+ } else {
178178+ if (lstrip) {
179179+ cut(start, lstrip)
180180+ end -= lstrip
181181+ }
182182+ if (rstrip) {
183183+ cut(end-rstrip, rstrip)
184184+ end -= rstrip
185185+ }
186186+ // now push the facet
187187+ facets.push({
188188+ index: {
189189+ byteStart: index_to_byteindex(text, start),
190190+ byteEnd: index_to_byteindex(text, end),
191191+ },
192192+ features: win.spans.map(span=>{
193193+ let feature = {...span.feature}
194194+ if (has_client_strip(feature)) {
195195+ feature.strip = [
196196+ Math.max(0, span.strip[0]-lstrip),
197197+ Math.max(0, span.strip[1]-rstrip),
198198+ ]
199199+ }
200200+ return feature
201201+ }),
202202+ })
200203 }
201204 }
205205+ return {text, facets}
202206 }
203203- obj.text = text
204207}
205205-206206-export function finalize_facets(text, windows) {
207207- let facets = windows.map(x=>({
208208- index: {
209209- byteStart:index_to_byteindex(text, x.start),
210210- byteEnd:index_to_byteindex(text, x.end),
211211- },
212212- features: x.spans.map(x=>{
213213- let x2 = {...x.feature}
214214- if (has_client_strip(x2)) {
215215- x2.strip = x.strip
216216- }
217217- return x2
218218- }),
219219- }))
220220- return facets
221221-}
222222-223223-/*spans = [
224224- {type:"abc", start: 1, end: 5},
225225- {type:"def", start: 3, end: 5},
226226- {type:"zzz", start: 6, end: 10},
227227-]
228228-229229- .aaaa.....
230230- ...dd.zzzz
231231-232232-output = [
233233- {start: 1, end: 3, types: ["abc"]},
234234- {start: 3, end: 5, types: ["abc","def"]},
235235- {start: 6, end: 10, types: ["zzz"]},
236236-]
237237-*/
+8-15
test3.html
···11<!doctype html>
2233<script type=module>
44- import {apply_strips,make_windows,markup_italic,markup1,finalize_facets,propagate_strip} from './facet.js'
55-66- let text = "abc /123 #test/ http://example.com[abc]"
44+ import {MarkedText,markup_italic,markup1} from './facet.js'
7588- let spans = [
99- ...markup_italic(text),
1010- ...markup1.text_to_spans(text),
66+ let text = new MarkedText("abc /123 #test/ http://example.com")
77+ text.spans = [
88+ ...markup_italic(text.text),
99+ ...markup1.text_to_spans(text.text),
1110 ]
1212- // todo: exclusion like "dont allow anything overlapping this one" -> delete all reflections of a span if anything overlaps one of its reflections, or vice versa.
1313- let windows = make_windows(spans)
1414- propagate_strip(windows)
1515- console.log(windows)
16111717- let x = {text, windows}
1818- apply_strips(x)
1919-2020- let facets = finalize_facets(x.text, x.windows)
2121- console.log(x.text, facets)
1212+ text.make_windows()
1313+ let f = text.create_facets()
1414+ console.log(f)
2215</script>