diff --git a/AstConv/HtmlStruct.fs b/AstConv/HtmlStruct.fs index 3fdc899..32045c9 100644 --- a/AstConv/HtmlStruct.fs +++ b/AstConv/HtmlStruct.fs @@ -3,20 +3,17 @@ open AstAccess open System.Xml open System +open System.Xml.Linq +open System.IO + /// 展现节点 ========================================================================================= module Present = - /// 可访问页面 - type PageAccess(hrefs: string) = - class - member this.accessLink(): string = hrefs - end /// 内容元素:分卷、章节、引用节点、故事线、情节、定义节点、引用节点 type IDomUnit = interface abstract member name:unit -> string abstract member object:unit -> AstImport.AstObject - abstract member getHtmlWith: pnode:XmlElement -> XmlElement end /// 容器节点:分卷、章节、故事线、情节 @@ -26,25 +23,46 @@ open System abstract member children:unit -> IDomUnit list end + /// 可访问页面 + type PageAccess(hrefs: string) = + class + member this.pageURL(): string = hrefs + end + /// 内容定义元素 ================================================================================= type Forwards(item: AstImport.AstObject) = class - let mutable assemble_page_url: string = "" + let mutable defines_page: Option = None + let mutable assemble_page: Option = None member this.elementID(): string = item.address() - member this.setAssembleURL(url: string) = - assemble_page_url <- url - member this.assembleURL():string = - assemble_page_url - end + /// 节点定义页面,本元素完整URL + member this.definedURL(): string = + match defines_page with + | Some v -> $"{v.pageURL()}#{this.elementID()}" + | None -> failwith "节点元素定义页面配置错误" + member this.setDefines(page: PageAccess) = + defines_page <- Some(page) + /// 节点汇集页面,本元素完整URL + member this.assembleURL():string = + match assemble_page with + | Some v -> $"{v.pageURL()}#{this.elementID()}" + | None -> failwith "节点元素汇集页面配置错误" + member this.setAssemble(page: PageAccess) = + assemble_page <- Some(page) + end + /// 回访定义元素 ================================================================================= - type Backwards(bind_item: Forwards, bind_page: PageAccess) = + type Backwards(bind_item: Forwards, refer_anchor: string) = class + member this.defsElement() = + bind_item member this.elementID(): string = bind_item.elementID() member this.backwardsLink(): string = - $"{bind_page.accessLink()}#{bind_item.elementID()}" + bind_item.definedURL() + member this.referAnchor(): string = refer_anchor end @@ -58,8 +76,6 @@ open System member this.name():string = volume.name() member this.object() = volume.object() - member this.getHtmlWith(p: XmlElement): XmlElement = - failwith "getHtmlWith" interface IContainer with member this.children(): IDomUnit list = childs @@ -76,8 +92,6 @@ open System member this.name():string = story.name() member this.object() = story.object() - member this.getHtmlWith(p: XmlElement): XmlElement = - failwith "getHtmlWith" interface IContainer with member this.children(): IDomUnit list = childs @@ -89,12 +103,13 @@ open System class inherit PageAccess(page_hrefs) new(page_hrefs, point) = PointPage(page_hrefs, point, []) + + member this.defines() = + point interface IDomUnit with member this.name():string = point.name() member this.object() = point.object() - member this.getHtmlWith(p: XmlElement): XmlElement = - failwith "getHtmlWith" interface IContainer with member this.children(): IDomUnit list = refer_list @@ -110,8 +125,6 @@ open System interface Present.IDomUnit with member this.name(): string = "" member this.object() = word - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" end @@ -125,8 +138,6 @@ open System interface Present.IDomUnit with member this.name(): string = ref_signature member this.object() = refs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = items @@ -147,8 +158,6 @@ open System interface Present.IDomUnit with member this.name(): string = defs.name() member this.object() = defs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = items @@ -161,15 +170,16 @@ open System /// 具有回退功能的元素 - type AssembleRefer<'T when 'T :> Present.Forwards and 'T :> Present.IDomUnit and 'T:> Present.IContainer>(from: string,item: 'T, page: Present.PageAccess) = + type AssembleRefer<'T when + 'T :> Present.Forwards and + 'T :> Present.IDomUnit and + 'T:> Present.IContainer>(item: 'T, refer_anchor: string) = class - inherit Present.Backwards(item, page) + inherit Present.Backwards(item, refer_anchor) interface Present.IDomUnit with - member this.name() = from + member this.name() = refer_anchor member this.object() = item.object() - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = item.children() @@ -183,8 +193,6 @@ open System interface Present.IDomUnit with member this.name(): string = defs.name() member this.object() = defs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = items @@ -209,8 +217,6 @@ open System member this.name(): string = defs.name() member this.object() = defs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = childs @@ -234,8 +240,6 @@ open System member this.name(): string = defs.name() member this.object() = defs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = childs @@ -259,8 +263,6 @@ open System member this.name(): string = defs.name() member this.object() = defs - member this.getHtmlWith(pnode: XmlElement): XmlElement = - failwith "getHtmlWith" interface Present.IContainer with member this.children(): Present.IDomUnit list = childs @@ -293,19 +295,19 @@ open System let datas = match obj with | :? AstImport.TextItem as text -> - (refs_depth, TextContent(text) :> Present.IDomUnit)::result_nodes + result_nodes@[(refs_depth, TextContent(text) :> Present.IDomUnit)] | :? AstImport.PointRef as refs -> - (refs_depth, PointRefer(refs, childs))::result_nodes + result_nodes@[(refs_depth, PointRefer(refs, childs))] | :? AstImport.PointDef as defs -> - (refs_depth, PointDefine(defs, childs))::result_nodes + result_nodes@[(refs_depth, PointDefine(defs, childs))] | :? AstImport.SliceDef as defs -> - (refs_depth, SliceDefine(defs, childs))::result_nodes + result_nodes@[(refs_depth, SliceDefine(defs, childs))] | :? AstImport.StoryDef as defs -> - (refs_depth, StoryDefine(defs, childs))::result_nodes + result_nodes@[(refs_depth, StoryDefine(defs, childs))] | :? AstImport.ArticleDef as defs -> - (refs_depth, ArticleDefine(defs, childs))::result_nodes + result_nodes@[(refs_depth, ArticleDefine(defs, childs))] | :? AstImport.VolumeDef as defs -> - (refs_depth, VolumeDefine(defs, childs))::result_nodes + result_nodes@[(refs_depth, VolumeDefine(defs, childs))] | :? AstImport.Program -> result_nodes | _ -> failwith "match error" @@ -424,40 +426,327 @@ open System /// 构建节点汇总页面 /// param nodes volume-pages@story-pages - let rec point_page_assemble(nodes: Present.IDomUnit list) = - let refers_about = point_peers_extract(nodes) - let pages = refers_about |> List.map ( - fun (page_defs, slice_defs, point_defs) -> - match point_defs.Value with - | :? Content.PointDefine as defs -> - let name_seqs = $"{page_defs.Value.name()}&{slice_defs.Value.name()}&{point_defs.Value.name()}" - let refer_point = Content.AssembleRefer(name_seqs, defs, page_defs.Value :?> Present.StoryPage) - let page_assemble = Present.PointPage(page_address_make(defs), refer_point) - defs.setAssembleURL(page_assemble.accessLink()) - Some(page_assemble) - | _ -> None + let rec point_page_assemble(pages: Present.IDomUnit list) = + let checked_list = pages |> List.filter( + fun it -> + match it with + | :? Present.PageAccess -> true + | _ -> false ) - |> List.filter (fun d -> d <> None) + if checked_list.Length <> pages.Length then + failwith "传入的参数pages必须是PageAccess的子类集合" - let refer_nodes = refers_about |> List.filter( + + let points_about= point_peers_extract(pages) + // 所有的节点关联定义页面 + points_about |> List.iter ( + fun (page, midx, deref) -> + match deref.Value with + | :? Present.Forwards as forwards_elm -> + let page_def = page.Value :?> Present.PageAccess + forwards_elm.setDefines(page_def) + | _ -> failwith "point_peers_extract结果类型错误" + ) + let refer_nodes = points_about |> List.filter( fun (_, _, n) -> match n.Value with | :? Content.PointRefer -> true | _ -> false ) |> List.map( - fun (page_defs, _, point_refs) -> - let refer_object = point_refs.Value.object() :?> AstImport.PointRef - let refer_name = $"{refer_object.storyRef()}&{refer_object.sliceRef()}&{refer_object.pointRef()}" + fun (page_defs, mid_defs, point_refs) -> + let refn = point_refs.Value.object() :?> AstImport.PointRef + let refer_name = $"{page_defs.Value.name()}&{mid_defs.Value.name()}@" let refs = point_refs.Value :?> Content.PointRefer - Content.AssembleRefer(refer_name, refs, page_defs.Value :?> Present.PageAccess) + $"{refn.storyRef()}&{refn.sliceRef()}&{refn.pointRef()}", Content.AssembleRefer(refs, refer_name) ) + // 构建节点汇集页面 + let pages = points_about |> List.filter( + fun (_, _, n) -> + match n.Value with + | :? Content.PointDefine -> true + | _ -> false + ) + |> List.map ( + fun (page_defs, slice_defs, point_defs) -> + let defs = point_defs.Value :?> Content.PointDefine + let name_seqs = $"{page_defs.Value.name()}&{slice_defs.Value.name()}&{point_defs.Value.name()}" + let refer_point = Content.AssembleRefer(defs, name_seqs) + let page_assemble = Present.PointPage(page_address_make(defs), refer_point) + defs.setAssemble(page_assemble) + name_seqs,page_assemble + ) - pages |> List.map( - fun page_one -> - let page_old = page_one.Value :> Present.IContainer - let page_dom = page_one.Value :> Present.IDomUnit - let nodes = refer_nodes |> List.filter(fun d -> (d:>Present.IDomUnit).name() = page_dom.name()) - page_old.append(nodes |> List.map, Present.IDomUnit> (fun x->x)) - ) \ No newline at end of file + pages |> List.map( + fun (key, page_one) -> + let page_old = page_one :> Present.IContainer + let page_dom = page_one :> Present.IDomUnit + let nodes = refer_nodes |> List.filter(fun (k, _) -> k = key) + nodes |> List.iter (fun (_, data) -> data.defsElement().setAssemble(page_one)) + let p = page_old.append(page_dom::(nodes |> List.map(fun (_, x)->x :> Present.IDomUnit))) + p :?> Present.IDomUnit + ) + + type PageText = + interface + abstract member getHtmlText:unit -> string + end + + type PageMaker<'T when + 'T :> Present.PageAccess and + 'T :> Present.IDomUnit and + 'T :> Present.IContainer>(page: 'T) = + class + member this.getHtmlDocument(): XmlDocument = + let doc = XmlDocument() + + let doc_type = doc.CreateDocumentType("html", null, null, null) + doc.AppendChild(doc_type) |> ignore + + let html = doc.CreateElement("html") + let head = doc.CreateElement("head") + let charset = doc.CreateElement("meta") + charset.SetAttribute("charset", "UTF-8") + head.AppendChild(charset) |> ignore + + let refresh = doc.CreateElement("meta") + refresh.SetAttribute("http-equiv", "refresh") + refresh.SetAttribute("content", "5") + head.AppendChild(refresh) |> ignore + + let title = doc.CreateElement("title") + title.AppendChild(doc.CreateTextNode(page.name())) |> ignore + head.AppendChild(title) |> ignore + + let style = doc.CreateElement("style") + style.AppendChild(doc.CreateTextNode("""body{ + background-color:lightgray; + } + + h1 { + margin-left:300px; + margin-top: 50px; + margin-bottom: 30px; + } + div.outline + { + max-width:90%; + #outline:green dotted thick; + margin:auto; + display:flex; + background-color:#f0f0f0; + } + div#left-nav + { + min-width:30%; + max-width:30%; + + # border:1px solid red; + # outline:green dotted thick; + overflow:clip; + padding:20px; + background-image: linear-gradient( + to right, + #e0e0e0 0%, + #ffffff 100% + ); + } + div#left-nav p { + word-wrap:break-word; + text-align:right; + } + + div.content { + padding: 5px; + padding-left: 20px; + max-width: 90%; + outline: #e0e0e0 solid 1px; + box-shadow: 2px 2px 2px gray; + } + .content p { + word-wrap:break-word; + } + """)) |> ignore + head.AppendChild(style) |> ignore + + html.AppendChild(head) |> ignore + doc.AppendChild(html) |> ignore + + let body = doc.CreateElement("body") + html.AppendChild(body) |> ignore + + let h1 = doc.CreateElement("h1") + h1.AppendChild(doc.CreateTextNode(page.name())) |> ignore + body.AppendChild(h1) |> ignore + + let div_outline = doc.CreateElement("div") + div_outline.SetAttribute("class", "outline") + body.AppendChild(div_outline) |> ignore + + let div_nav = doc.CreateElement("div") + div_nav.SetAttribute("id", "left-nav") + div_outline.AppendChild(div_nav) |> ignore + this.initNavigate(div_nav) |> ignore + + let div_content = doc.CreateElement("div") + div_content.SetAttribute("id", "content") + div_content.SetAttribute("class", "content") + div_outline.AppendChild(div_content) |> ignore + this.contentAppend(div_content, page.children()) + + doc + + member this.initNavigate(nav: XmlElement):XmlElement = + let rec nav_items_expand(nav: XmlElement, items: (Present.IDomUnit option*Present.IDomUnit option*Present.IDomUnit option) list) = + match items with + | [] -> () + | _ -> + let doc_o = nav.OwnerDocument + let (_, _, head) = items.Head + match head.Value with + | :? Present.Forwards as fx -> + let p = doc_o.CreateElement("p") + let a = doc_o.CreateElement("a") + a.SetAttribute("href", $"#{fx.elementID()}") + a.AppendChild(doc_o.CreateTextNode(head.Value.name())) |> ignore + p.AppendChild(a) |> ignore + nav.AppendChild(p) |> ignore + | :? Present.Backwards as bx -> + let p = doc_o.CreateElement("p") + let a = doc_o.CreateElement("a") + a.SetAttribute("href", $"#{bx.elementID()}") + a.AppendChild(doc_o.CreateTextNode(head.Value.name())) |> ignore + p.AppendChild(a) |> ignore + nav.AppendChild(p) |> ignore + | _ -> () + + nav_items_expand(nav, items.Tail) + + let objp = page :> obj + let point_peers = if (objp :? Present.PointPage) then + (objp :?>Present.PointPage).defines()::page.children() |> List.map(fun x-> None, None, Some(x)) + else + point_peers_extract(page.children()) + + match point_peers with + | [] -> + let empty = nav.OwnerDocument.CreateElement("p") + empty.AppendChild(nav.OwnerDocument.CreateTextNode("当前文档无索引")) |> ignore + nav.AppendChild(empty) |> ignore + | _ -> + nav_items_expand(nav, point_peers) + + nav + + member this.contentAppend(pnode: XmlElement, contents: Present.IDomUnit list) = + let vdoc = pnode.OwnerDocument + match contents with + | [] -> () + | _ -> + match contents.Head with + | :? Content.TextContent -> + let text_ast = contents.Head.object() :?> AstImport.TextItem + let text_line = contents.Tail |> List.filter( + fun content_item-> + match content_item with + | :? Content.TextContent -> + let curr_ast = content_item.object() :?> AstImport.TextItem + curr_ast.row() = text_ast.row() + | _ -> false + ) + let one_line = contents.Head::text_line + let p = vdoc.CreateElement("p") + for t in one_line do + let text_content = t.object() :?> AstImport.TextItem + let content = text_content.content() + p.AppendChild(vdoc.CreateTextNode(content)) |> ignore + pnode.AppendChild(p) |> ignore + this.contentAppend(pnode, List.skip one_line.Length contents) + + | :? Content.SliceDefine as slice_defs-> + let div_slice = vdoc.CreateElement("div") + div_slice.SetAttribute("data-type", "slice") + div_slice.SetAttribute("class", "content") + let slice_title = vdoc.CreateElement("h3") + slice_title.AppendChild(vdoc.CreateTextNode(contents.Head.name())) |> ignore + div_slice.AppendChild(slice_title) |> ignore + pnode.AppendChild(div_slice) |> ignore + let slice_con = slice_defs :> Present.IContainer + this.contentAppend(div_slice, slice_con.children()) + this.contentAppend(pnode, contents.Tail) + + | :? Content.ArticleDefine as article_defs -> + let div_article = vdoc.CreateElement("div") + div_article.SetAttribute("data-type", "article") + div_article.SetAttribute("class", "content") + let article_title = vdoc.CreateElement("h3") + article_title.AppendChild(vdoc.CreateTextNode(contents.Head.name())) |> ignore + div_article.AppendChild(article_title) |> ignore + pnode.AppendChild(div_article) |> ignore + let article_con = article_defs :> Present.IContainer + this.contentAppend(div_article, article_con.children()) + this.contentAppend(pnode, contents.Tail) + + | :? Present.PointPage as page_point -> + let page_defs = page_point.defines() :?> Content.AssembleRefer + this.contentAppend(pnode, [page_defs]) + this.contentAppend(pnode, contents.Tail) + + + | :? Present.Forwards as object_defs -> + let type_name = match object_defs with + | :? Content.PointDefine -> "point" + | :? Content.PointRefer -> "refer" + | _ -> failwith "type mismatch" + + let div_forwards = vdoc.CreateElement("div") + div_forwards.SetAttribute("data-type", type_name) + div_forwards.SetAttribute("class", "content") + + let define_title = vdoc.CreateElement("h3") + define_title.AppendChild(vdoc.CreateTextNode(contents.Head.name())) |> ignore + div_forwards.AppendChild(define_title) |> ignore + let define_a = vdoc.CreateElement("a") + define_a.SetAttribute("id", object_defs.elementID()) + define_a.SetAttribute("href", object_defs.assembleURL()) + define_a.AppendChild(define_title) |> ignore + div_forwards.AppendChild(define_a) |> ignore + + pnode.AppendChild(div_forwards) |> ignore + let point_con = contents.Head :?> Present.IContainer + this.contentAppend(div_forwards, point_con.children()) + this.contentAppend(pnode, contents.Tail) + + | :? Present.Backwards as object_backs -> + let div_backwards = vdoc.CreateElement("div") + div_backwards.SetAttribute("data-type", "assemble") + div_backwards.SetAttribute("class", "content") + + let define_title = vdoc.CreateElement("h3") + define_title.AppendChild(vdoc.CreateTextNode(contents.Head.name())) |> ignore + div_backwards.AppendChild(define_title) |> ignore + let define_a = vdoc.CreateElement("a") + define_a.SetAttribute("id", object_backs.elementID()) + define_a.SetAttribute("href", object_backs.backwardsLink()) + define_a.AppendChild(define_title) |> ignore + div_backwards.AppendChild(define_a) |> ignore + + pnode.AppendChild(div_backwards) |> ignore + let point_con = contents.Head :?> Present.IContainer + this.contentAppend(div_backwards, point_con.children()) + this.contentAppend(pnode, contents.Tail) + + | _ -> () + + interface PageText with + member this.getHtmlText(): string = + let doc = this.getHtmlDocument() + + let out = new StringWriter() + let writer = new XmlTextWriter(out) + writer.Formatting = Formatting.Indented |> ignore + doc.WriteTo(writer) + writer.Flush() + out.ToString() + end \ No newline at end of file diff --git a/AstConv/Program.fs b/AstConv/Program.fs index e6ac7f9..79abfe7 100644 --- a/AstConv/Program.fs +++ b/AstConv/Program.fs @@ -19,5 +19,20 @@ let volume_pages = volume_page_assemble(visitor.contents()) |> List.map List.map (fun x->x) let point_pages = point_page_assemble(volume_pages @ story_pages) +let maker = volume_pages @ story_pages @ point_pages + |> List.map(fun page_unit -> + match page_unit with + | :? PointPage as point -> + PageMaker(point) :> PageText + | :? VolumePage as vol -> + PageMaker(vol) + | :? StoryPage as story -> + PageMaker(story) + | _ -> failwith "" + ) +let docx0 = maker.Item(5).getHtmlText() +let docx1 = maker.Item(18).getHtmlText() +let docx2 = maker.Item(22).getHtmlText() + for refs in point_pages do System.Console.WriteLine(refs.ToString()) \ No newline at end of file