Projekt

Obecné

Profil

Stáhnout (24.7 KB) Statistiky
| Větev: | Tag: | Revize:
1
using Core.Entities;
2
using Core.GraphUtils;
3
using Ganss.XSS;
4
using HtmlAgilityPack;
5
using Models.Tags;
6
using System;
7
using System.Collections.Generic;
8
using System.Linq;
9
using System.Text;
10
using System.Threading.Tasks;
11

    
12
namespace Core.Services
13
{
14
    public class HTMLService : IHTMLService
15
    {
16

    
17
        private const string TAG_ID_ATTRIBUTE_NAME = "aswi-tag-id";
18
        private const string TAG_INSTANCE_ATTRIBUTE_NAME = "aswi-tag-instance";
19
        private const string TAG_EF_ID_ATTRIBUTE_NAME = "aswi-tag-ef-id";
20

    
21
        public class CachedInfo
22
        {
23
            public List<int> TagStartPositions = new();
24
            public List<int> TagStartLengths = new();
25
            public List<int> TagClosingPositions = new();
26
            public List<int> TagClosingLengths = new();
27
            public Dictionary<HtmlNode, HtmlNode> NodeDict = new();
28
            public List<TagInstanceCSSInfo> TagInstanceCSS = new();
29
        }
30

    
31
        private List<int> TagStartPositions = new();
32
        private List<int> TagStartLengths = new();
33
        private List<int> TagClosingPositions = new();
34
        private List<int> TagClosingLengths = new();
35
        private Dictionary<HtmlNode, HtmlNode> NodeDict = new();
36
        private List<TagInstanceCSSInfo> TagInstanceCSS = new();
37

    
38
        private void UnpackCachedInfo(CachedInfo cachedInfo)
39
        {
40
            TagStartPositions = cachedInfo.TagStartPositions;
41
            TagStartLengths = cachedInfo.TagStartLengths;
42
            TagClosingPositions = cachedInfo.TagClosingPositions;
43
            TagClosingLengths = cachedInfo.TagClosingLengths;
44
            NodeDict = cachedInfo.NodeDict;
45
            TagInstanceCSS = cachedInfo.TagInstanceCSS;
46
        }
47

    
48

    
49
        /*
50
         *      Full HTML Preprocessing -------------------------------------------------------------------------------
51
         */
52

    
53
        public (string, CachedInfo) FullPreprocessHTML(string htmlSource, List<AnnotationTagGeneric> tags)
54
        {
55
            var docOriginal = new HtmlDocument();
56
            docOriginal.LoadHtml(htmlSource);
57
            var docToEdit = new HtmlDocument();
58
            docToEdit.LoadHtml(htmlSource);
59

    
60
            var descendantsOriginal = docOriginal.DocumentNode.DescendantsAndSelf();
61
            var descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
62

    
63
            int currentId = 0;
64

    
65
            FillNodeDict(descendantsOriginal, descendantsToEdit);
66
            AssignIdsToOriginalDocument(descendantsOriginal, ref currentId);
67

    
68
            WrapTextInSpan(descendantsOriginal, docToEdit);
69

    
70
            descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
71
            int descCount = descendantsToEdit.Count;
72

    
73
            foreach (var tag in tags)
74
            {
75
                int i = 0;
76
                List<HtmlNode> addedForSelection = new();
77
                while (i < descCount)
78
                {
79
                    for (; i < descCount; i++)
80
                    {
81
                        var node = descendantsToEdit.ElementAt(i);
82
                        if (!node.Name.Contains("#text") || addedForSelection.Contains(node) || addedForSelection.Contains(node.ParentNode) || node.ParentNode.Name == "style")
83
                        {
84
                            continue;
85
                        }
86

    
87
                        int nodeId = node.ParentNode.GetAttributeValue(TAG_ID_ATTRIBUTE_NAME, -1);
88

    
89
                        var start = TagStartPositions[nodeId] + TagStartLengths[nodeId];
90
                        var end = TagClosingPositions[nodeId];
91

    
92
                        int selectionStart = tag.Position;
93
                        int selectionEnd = tag.Position + tag.Length;
94

    
95
                        if (selectionStart < end && selectionEnd > start)
96
                        {
97
                            if (selectionStart <= start && selectionEnd >= end)
98
                            {
99
                                addedForSelection.Add(SolveFullFill(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
100
                            }
101
                            else if (selectionStart <= start)
102
                            {
103
                                addedForSelection.AddRange(SolveRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
104
                            }
105
                            else if (selectionEnd >= end)
106
                            {
107
                                addedForSelection.AddRange(SolveLeftGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
108
                            }
109
                            else
110
                            {
111
                                addedForSelection.AddRange(SolveLeftRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
112
                            }
113
                            descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
114
                            descCount = descendantsToEdit.Count;
115
                            break;
116
                        }
117
                    }
118
                }
119

    
120
            }
121

    
122
            ModifyLinks(descendantsToEdit);
123
            string docToRender = docToEdit.DocumentNode.OuterHtml;
124
            HtmlSanitizer sanitizer = new HtmlSanitizer();
125
            sanitizer.AllowedAttributes.Clear();
126
            sanitizer.AllowedAttributes.Add(TAG_ID_ATTRIBUTE_NAME);
127
            sanitizer.AllowedAttributes.Add(TAG_INSTANCE_ATTRIBUTE_NAME);
128
            sanitizer.AllowedAttributes.Add(TAG_EF_ID_ATTRIBUTE_NAME);
129
            sanitizer.AllowedAttributes.Add("href");
130
            sanitizer.AllowedAttributes.Add("end");
131
            sanitizer.AllowedAttributes.Add("start");
132
            sanitizer.AllowedAttributes.Add("class");
133
            sanitizer.AllowedAttributes.Add("target");
134
            sanitizer.AllowedAttributes.Add("id");
135
            if (sanitizer.AllowedTags.Contains("script"))
136
            {
137
                sanitizer.AllowedTags.Remove("script");
138
            }
139
            if (!sanitizer.AllowedTags.Contains("style"))
140
            {
141
                sanitizer.AllowedTags.Add("style");
142
            }
143
            sanitizer.AllowedTags.Add("a");
144
            docToRender = sanitizer.Sanitize(docToRender);
145
            GenerateCSS(tags);
146

    
147
            return (docToRender, new CachedInfo()
148
            {
149
                TagStartPositions = TagStartPositions,
150
                TagClosingPositions = TagClosingPositions,
151
                TagStartLengths = TagStartLengths,
152
                TagClosingLengths = TagClosingLengths,
153
                TagInstanceCSS = TagInstanceCSS,
154
                NodeDict = NodeDict
155
            });
156
        }
157

    
158
        private HtmlNode SolveFullFill(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit, AnnotationTagGeneric tag)
159
        {
160
            // full fill
161
            string textSelected = node.InnerText;
162

    
163
            var parentNode = node.ParentNode;
164
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
165
            parentNode.ChildNodes.RemoveAt(nodeIndex);
166

    
167
            EPosition markerPosition = EPosition.MARK_NONE;
168
            if (selectionEnd == end && selectionStart == start)
169
            {
170
                markerPosition = EPosition.MARK_LEFT_RIGHT;
171
            }
172

    
173
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TagStartPositions.Count, tag.Instance, tag.Id, start, markerPosition);
174
            parentNode.ChildNodes.Insert(nodeIndex, spanSelected);
175

    
176
            return spanSelected;
177
        }
178

    
179
        private List<HtmlNode> SolveRightGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
180
                                             AnnotationTagGeneric tag)
181
        {
182
            // partial fill, end gap
183
            string text = node.InnerText;
184
            string textAfter = text.Substring(Math.Min(selectionStart - start + tag.Length, text.Length));
185
            string textSelected = text.Substring(0, selectionEnd - start);
186

    
187
            var parentNode = node.ParentNode;
188
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
189
            parentNode.ChildNodes.RemoveAt(nodeIndex);
190

    
191
            int spanSelectedStart = start;
192
            int spanAfterStart = start + textSelected.Length;
193

    
194
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TagStartPositions.Count, tag.Instance, tag.Id, spanSelectedStart, EPosition.MARK_RIGHT);
195
            parentNode.ChildNodes.Insert(nodeIndex, spanSelected);
196

    
197
            HtmlNode spanAfter = CreateSpan(docToEdit, textAfter, TagStartPositions.Count, null, null, spanAfterStart);
198
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanAfter);
199

    
200
            return new() { spanSelected, spanAfter };
201
        }
202

    
203
        private List<HtmlNode> SolveLeftGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
204
                                             AnnotationTagGeneric tag)
205
        {
206
            // partial fill, start gap
207
            string text = node.InnerText;
208
            string textBefore = text.Substring(0, selectionStart - start);
209
            string textSelected = text.Substring(selectionStart - start, Math.Min(tag.Length, text.Length - textBefore.Length));
210

    
211
            var parentNode = node.ParentNode;
212
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
213
            parentNode.ChildNodes.RemoveAt(nodeIndex);
214

    
215
            int spanBeforeStart = start;
216
            int spanSelectedStart = start + textBefore.Length;
217

    
218
            HtmlNode spanBefore = CreateSpan(docToEdit, textBefore, TagStartPositions.Count, null, null, spanBeforeStart);
219
            parentNode.ChildNodes.Insert(nodeIndex, spanBefore);
220

    
221
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TagStartPositions.Count, tag.Instance, tag.Id, spanSelectedStart, EPosition.MARK_LEFT);
222
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanSelected);
223

    
224
            return new() { spanSelected, spanBefore };
225
        }
226

    
227
        private List<HtmlNode> SolveLeftRightGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
228
                                                 AnnotationTagGeneric tag)
229
        {
230
            // partial fill, start gap end gap
231
            string text = node.InnerText;
232
            string textBefore = text.Substring(0, selectionStart - start);
233
            string textAfter = text.Substring(selectionStart - start + tag.Length);
234
            string textSelected = text.Substring(selectionStart - start, tag.Length);
235

    
236
            var parentNode = node.ParentNode;
237
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
238
            parentNode.ChildNodes.RemoveAt(nodeIndex);
239

    
240
            int spanBeforeStart = start;
241
            int spanSelectedStart = start + textBefore.Length;
242
            int spanAfterStart = start + textBefore.Length + textSelected.Length;
243

    
244
            HtmlNode spanBefore = CreateSpan(docToEdit, textBefore, TagStartPositions.Count, null, null, spanBeforeStart);
245
            parentNode.ChildNodes.Insert(nodeIndex, spanBefore);
246

    
247
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TagStartPositions.Count, tag.Instance, tag.Id, spanSelectedStart, EPosition.MARK_LEFT_RIGHT);
248
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanSelected);
249

    
250
            HtmlNode spanAfter = CreateSpan(docToEdit, textAfter, TagStartPositions.Count, null, null, spanAfterStart);
251
            parentNode.ChildNodes.Insert(nodeIndex + 2, spanAfter);
252

    
253
            return new() { spanSelected, spanBefore, spanAfter };
254
        }
255

    
256
        private HtmlNode CreateSpan(HtmlDocument doc, string text, int tagId, Guid? instanceId, Guid? entityId, int startPosition, EPosition position = EPosition.MARK_NONE)
257
        {
258
            HtmlNode span = doc.CreateElement("span");
259
            span.InnerHtml = text;
260
            TagStartPositions.Add(startPosition);
261
            TagStartLengths.Add(0);
262
            TagClosingPositions.Add(startPosition + text.Length);
263
            TagClosingLengths.Add(0);
264
            span.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, tagId.ToString());
265

    
266
            if (instanceId != null)
267
            {
268
                span.AddClass("annotation");
269
                span.Attributes.Add(TAG_INSTANCE_ATTRIBUTE_NAME, instanceId.Value.ToString());
270
                span.Attributes.Add(TAG_EF_ID_ATTRIBUTE_NAME, entityId.ToString());
271
                span.Attributes.Add("id", entityId.ToString());
272

    
273
                if (position == EPosition.MARK_LEFT || position == EPosition.MARK_LEFT_RIGHT)
274
                {
275
                    span.Attributes.Add("start", "1");
276
                }
277
                if (position == EPosition.MARK_RIGHT || position == EPosition.MARK_LEFT_RIGHT)
278
                {
279
                    span.Attributes.Add("end", "1");
280
                }
281
            }
282

    
283
            return span;
284
        }
285

    
286
        private enum EPosition
287
        {
288
            MARK_LEFT = 5,
289
            MARK_RIGHT = 3,
290
            MARK_LEFT_RIGHT = 2,
291
            MARK_NONE = 0
292
        }
293

    
294
        private void ModifyLinks(IEnumerable<HtmlNode> descendantsOriginal)
295
        {
296
            foreach (var descendant in descendantsOriginal)
297
            {
298
                if (descendant.Name == "a")
299
                {
300
                    if (descendant.Attributes.Contains("href"))
301
                    {
302
                        descendant.SetAttributeValue("href", "/link?url=" + descendant.Attributes["href"].Value);
303
                        descendant.SetAttributeValue("target", "_blank");
304
                    }
305
                }
306
            }
307
        }
308

    
309
        private void WrapTextInSpan(IEnumerable<HtmlNode> descendantsOriginal, HtmlDocument docToEdit)
310
        {
311
            // Special case for non-html documents
312
            if (descendantsOriginal.Count() == 2)
313
            {
314
                var documentNode = descendantsOriginal.ElementAt(0);
315
                var childNode = descendantsOriginal.ElementAt(1);
316
                if (documentNode.Name == "#document" && childNode.Name == "#text")
317
                {
318
                    HtmlNode coveringSpan = docToEdit.CreateElement("span");
319
                    coveringSpan.InnerHtml = childNode.InnerHtml;
320
                    TagStartPositions.Add(childNode.InnerStartIndex);
321
                    TagStartLengths.Add(0);
322
                    TagClosingPositions.Add(childNode.InnerStartIndex + childNode.InnerLength);
323
                    TagClosingLengths.Add(0);
324
                    coveringSpan.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, (TagStartPositions.Count - 1).ToString());
325

    
326
                    var parent = NodeDict[documentNode];
327

    
328
                    parent.ChildNodes.RemoveAt(0);
329
                    parent.ChildNodes.Add(coveringSpan);
330

    
331
                    return;
332
                }
333
            }
334

    
335
            foreach (var node in descendantsOriginal)
336
            {
337
                var originalNode = node;
338
                var toEditNode = NodeDict[node];
339

    
340
                if (originalNode.Name.Contains("#"))
341
                {
342
                    continue;
343
                }
344
                else
345
                {
346
                    bool onlyText = true;
347
                    bool onlySubtags = true;
348

    
349
                    foreach (var child in node.ChildNodes)
350
                    {
351
                        if (child.Name.Contains("#"))
352
                        {
353
                            onlySubtags = false;
354
                        }
355
                        else
356
                        {
357
                            onlyText = false;
358
                        }
359
                    }
360

    
361
                    if (onlyText || onlySubtags)
362
                    {
363
                        continue;
364
                    }
365
                    else
366
                    {
367

    
368
                        foreach (var child in node.ChildNodes)
369
                        {
370
                            if (child.Name.Contains("#text"))
371
                            {
372
                                HtmlNode coveringSpan = docToEdit.CreateElement("span");
373
                                coveringSpan.InnerHtml = child.InnerHtml;
374
                                TagStartPositions.Add(child.InnerStartIndex);
375
                                TagStartLengths.Add(0);
376
                                TagClosingPositions.Add(child.InnerStartIndex + child.InnerLength);
377
                                TagClosingLengths.Add(0);
378
                                coveringSpan.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, (TagStartPositions.Count - 1).ToString());
379

    
380
                                var parent = NodeDict[node];
381
                                var index = parent.ChildNodes.IndexOf(NodeDict[child]);
382

    
383
                                parent.ChildNodes.RemoveAt(index);
384
                                parent.ChildNodes.Insert(index, coveringSpan);
385
                            }
386
                        }
387
                    }
388
                }
389
            }
390
        }
391

    
392
        private void GenerateCSS(List<AnnotationTagGeneric> tags)
393
        {
394
            /*string inner = "span.annotation {border-bottom: 2px solid;}";
395
            inner += "span {line-height: 30px}\n";*/
396

    
397
            var tagPaddingDict = Intersections.ColorGraph(Intersections.FindIntersections(tags));
398
            foreach (var tag in tags.DistinctBy(t => t.Instance))
399
            {
400
                var padding = (tagPaddingDict[tag] + 1) * 2;
401
                TagInstanceCSS.Add(new()
402
                {
403
                    InstanceId = tag.Instance,
404
                    Color = tag.Tag.Color,
405
                    Padding = padding
406
                });
407
            }
408
        }
409

    
410
        private void AssignIdsToOriginalDocument(IEnumerable<HtmlNode> descendantsOriginal, ref int currentId)
411
        {
412
            foreach (var node in descendantsOriginal)
413
            {
414
                var originalNode = node;
415
                var toEditNode = NodeDict[node];
416

    
417
                if (originalNode.Name.Contains("#"))
418
                {
419
                    continue;
420
                }
421
                else
422
                {
423
                    TagStartPositions.Add(originalNode.OuterStartIndex);
424
                    TagStartLengths.Add(originalNode.InnerStartIndex - originalNode.OuterStartIndex);
425
                    currentId = TagStartPositions.Count - 1;
426
                    toEditNode.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, currentId.ToString());
427

    
428
                    TagClosingPositions.Add(originalNode.InnerStartIndex + originalNode.InnerLength);
429
                    TagClosingLengths.Add((originalNode.OuterStartIndex + originalNode.OuterLength) - (originalNode.InnerStartIndex + originalNode.InnerLength));
430
                }
431
            }
432
        }
433

    
434
        private void FillNodeDict(IEnumerable<HtmlNode> descendantsOriginal, IEnumerable<HtmlNode> descendantsToEdit)
435
        {
436
            var zipped = descendantsOriginal.Zip(descendantsToEdit, (orig, toEdit) => new
437
            {
438
                Original = orig,
439
                ToEdit = toEdit
440
            });
441
            foreach (var node in zipped)
442
            {
443
                var originalNode = node.Original;
444
                var toEditNode = node.ToEdit;
445
                NodeDict.Add(originalNode, toEditNode);
446
            }
447
        }
448

    
449

    
450
        /*
451
         *      Full HTML Preprocessing -------------------------------------------------------------------------------
452
         */
453

    
454
        /*
455
         *      Partial HTML Preprocessing ----------------------------------------------------------------------------
456
         */
457

    
458
        public (string, CachedInfo) PartialPreprocessHTMLAddTag(string htmlToEdit, string htmlOriginal, AnnotationTagGeneric tagToAdd, List<AnnotationTagGeneric> tags, CachedInfo cachedInfo)
459
        {
460
            UnpackCachedInfo(cachedInfo);
461

    
462
            var docOriginal = new HtmlDocument();
463
            docOriginal.LoadHtml(htmlOriginal);
464
            var docToEdit = new HtmlDocument();
465
            docToEdit.LoadHtml(htmlToEdit);
466

    
467
            var descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
468

    
469
            int i = 0;
470
            List<HtmlNode> addedForSelection = new();
471

    
472
            int descendantsCount = descendantsToEdit.Count();
473
            while (i < descendantsCount)
474
            {
475
                for (; i < descendantsCount; i++)
476
                {
477
                    var node = descendantsToEdit.ElementAt(i);
478
                    if (!node.Name.Contains("#text") || addedForSelection.Contains(node) || addedForSelection.Contains(node.ParentNode) ||
479
                        node.ParentNode.Name == "style" || node.ParentNode.Name.StartsWith("#"))
480
                    {
481
                        continue;
482
                    }
483

    
484
                    int nodeId = node.ParentNode.GetAttributeValue(TAG_ID_ATTRIBUTE_NAME, -1);
485

    
486
                    var start = TagStartPositions[nodeId] + TagStartLengths[nodeId];
487
                    var end = TagClosingPositions[nodeId];
488

    
489
                    int selectionStart = tagToAdd.Position;
490
                    int selectionEnd = tagToAdd.Position + tagToAdd.Length;
491

    
492
                    if (selectionStart < end && selectionEnd > start)
493
                    {
494
                        if (selectionStart <= start && selectionEnd >= end)
495
                        {
496
                            addedForSelection.Add(SolveFullFill(node, selectionStart, selectionEnd, start, end, docToEdit, tagToAdd));
497
                        }
498
                        else if (selectionStart <= start)
499
                        {
500
                            addedForSelection.AddRange(SolveRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tagToAdd));
501
                        }
502
                        else if (selectionEnd >= end)
503
                        {
504
                            addedForSelection.AddRange(SolveLeftGap(node, selectionStart, selectionEnd, start, end, docToEdit, tagToAdd));
505
                        }
506
                        else
507
                        {
508
                            addedForSelection.AddRange(SolveLeftRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tagToAdd));
509
                        }
510
                        descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
511
                        descendantsCount = descendantsToEdit.Count();
512
                        break;
513
                    }
514
                }
515
            }
516

    
517
            GenerateCSS(tags);
518
            return (docToEdit.DocumentNode.OuterHtml, new()
519
            {
520
                TagStartPositions = TagStartPositions,
521
                TagStartLengths = TagStartLengths,
522
                TagClosingPositions = TagClosingPositions,
523
                TagClosingLengths = TagClosingLengths,
524
                TagInstanceCSS = TagInstanceCSS,
525
                NodeDict = NodeDict
526
            });
527
        }
528

    
529

    
530
        public (string, CachedInfo) PartialPreprocessHTMLRemoveTag(string htmlToEdit, string htmlOriginal, AnnotationTagGeneric tagToRemove, List<AnnotationTagGeneric> tags, CachedInfo cachedInfo)
531
        {
532
            UnpackCachedInfo(cachedInfo);
533

    
534
            var docOriginal = new HtmlDocument();
535
            docOriginal.LoadHtml(htmlOriginal);
536
            var docToEdit = new HtmlDocument();
537
            docToEdit.LoadHtml(htmlToEdit);
538

    
539
            var descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
540

    
541
            int i = 0;
542
            int descendantsCount = descendantsToEdit.Count();
543
            while (i < descendantsCount)
544
            {
545
                for (; i < descendantsCount; i++)
546
                {
547
                    var node = descendantsToEdit.ElementAt(i);
548
                    if (!node.Attributes.Contains(TAG_EF_ID_ATTRIBUTE_NAME))
549
                    {
550
                        continue;
551
                    }
552
                    else
553
                    {
554
                        if (node.Attributes[TAG_EF_ID_ATTRIBUTE_NAME].Value != tagToRemove.Id.ToString())
555
                        {
556
                            continue;
557
                        }
558

    
559
                        node.Attributes.Remove(TAG_EF_ID_ATTRIBUTE_NAME);
560
                        node.Attributes.Remove(TAG_INSTANCE_ATTRIBUTE_NAME);
561
                        node.Attributes.Remove("class");
562
                        node.Attributes.Remove("start");
563
                        node.Attributes.Remove("end");
564

    
565
                        descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf().ToList();
566
                        descendantsCount = descendantsToEdit.Count();
567
                        break;
568
                    }
569
                }
570
            }
571

    
572
            GenerateCSS(tags);
573
            return (docToEdit.DocumentNode.OuterHtml, new()
574
            {
575
                TagStartPositions = TagStartPositions,
576
                TagStartLengths = TagStartLengths,
577
                TagClosingPositions = TagClosingPositions,
578
                TagClosingLengths = TagClosingLengths,
579
                TagInstanceCSS = TagInstanceCSS,
580
                NodeDict = NodeDict
581
            });
582
        }
583

    
584
        /*
585
         *      Partial HTML Preprocessing ----------------------------------------------------------------------------
586
         */
587
    }
588
}
(1-1/2)