From fcab3f96513c759ca67281e871b970d5691348af Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 17 Sep 2021 20:12:00 -0400
Subject: fixed making link to selected region in video

---
 src/client/views/nodes/VideoBox.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/VideoBox.tsx')

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 90de3227f..e3ba638b3 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -30,6 +30,7 @@ import { DragManager } from "../../util/DragManager";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { Tooltip } from "@material-ui/core";
+import { AnchorMenu } from "../pdf/AnchorMenu";
 const path = require('path');
 
 type VideoDocument = makeInterface<[typeof documentSchema]>;
@@ -74,7 +75,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     getAnchor = () => {
         const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined) || this.rootDoc;
+        const marquee = AnchorMenu.Instance.GetAnchor?.();
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
     }
 
     videoLoad = () => {
-- 
cgit v1.2.3-70-g09d2


From dab30019adba202ba2266acf046ad3c41f90c6e2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Sep 2021 14:06:51 -0400
Subject: videoBox fitsWidth properly in lightbox.  documentdecorations less
 hacky for deciding when to allow nativewidth/height changes by adding props
 to document type templates.  fixed height of properties pane & updated CSS
 for leftflyout & properties pane.  marquees no longer oveflow image/video
 boxes.

---
 src/client/documents/Documents.ts        | 12 +++++++++---
 src/client/views/DocumentDecorations.tsx | 29 +++++++++++------------------
 src/client/views/GlobalKeyHandler.ts     |  2 +-
 src/client/views/MainView.scss           |  9 ++++-----
 src/client/views/MainView.tsx            | 12 ++++++------
 src/client/views/nodes/ImageBox.scss     |  1 +
 src/client/views/nodes/ImageBox.tsx      |  4 ++--
 src/client/views/nodes/VideoBox.scss     | 15 ++++++++++++++-
 src/client/views/nodes/VideoBox.tsx      | 22 +++++++++++++++-------
 src/client/views/nodes/WebBox.tsx        |  8 ++++----
 10 files changed, 67 insertions(+), 47 deletions(-)

(limited to 'src/client/views/nodes/VideoBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8ac647b99..403620bdd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -352,7 +352,7 @@ export namespace Docs {
         const TemplateMap: TemplateMap = new Map([
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
-                options: { _height: 150, _xMargin: 10, _yMargin: 10, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.SEARCH, {
                 layout: { view: SearchBox, dataField: defaultDataKey },
@@ -372,7 +372,7 @@ export namespace Docs {
             }],
             [DocumentType.WEB, {
                 layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.COL, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
@@ -392,7 +392,7 @@ export namespace Docs {
             }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: defaultDataKey },
-                options: { _curPage: 1, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.IMPORT, {
                 layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -506,6 +506,12 @@ export namespace Docs {
             const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
             // fetch the actual prototype documents from the server
             const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds);
+            Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB
+            Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true;
+            Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true;
+            Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB
+            Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true;
+            Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true;
 
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e9a54d6a5..bd61c9f60 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -319,12 +319,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
                 const scale = docView.props.ScreenToLocalTransform().Scale;
-                const canModifyNativeDim = e.ctrlKey || doc.allowReflow;
+                const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
                 if (nwidth && nheight) {
                     if (nwidth / nheight !== width / height && !dragBottom) {
                         height = nheight / nwidth * width;
                     }
-                    if (canModifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
+                    if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
                         if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
                         else dW = dH * nwidth / nheight;
                     }
@@ -334,34 +334,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = (nwidth && nheight);
-                if (canModifyNativeDim && [DocumentType.IMG, DocumentType.SCREENSHOT, DocumentType.VID].includes(doc.type as DocumentType)) {
-                    dW !== 0 && runInAction(() => {
-                        const dataDoc = doc[DataSym];
-                        const nw = Doc.NativeWidth(dataDoc);
-                        const nh = Doc.NativeHeight(dataDoc);
-                        Doc.SetNativeWidth(dataDoc, nw + (dW > 0 ? 10 : -10));
-                        Doc.SetNativeHeight(dataDoc, nh + (dW > 0 ? 10 : -10) * nh / nw);
-                    });
-                }
-                else if (fixedAspect) {
-                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !canModifyNativeDim)) || dragRight) {
-                        if (dragRight && canModifyNativeDim) {
+                if (fixedAspect) {
+                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
+                        if (dragRight && modifyNativeDim) {
                             doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
                         } else {
                             if (!doc._fitWidth) doc._height = nheight / nwidth * actualdW;
-                            else if (!canModifyNativeDim || dragBotRight) doc._height = actualdH;
+                            else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
                         }
                         doc._width = actualdW;
                     }
                     else {
-                        if (dragBottom && (canModifyNativeDim || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used
+                        if (dragBottom && (modifyNativeDim ||
+                            (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
                             doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
                             doc._autoHeight = false;
                         } else {
                             if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH;
-                            else if (!canModifyNativeDim || dragBotRight) doc._width = actualdW;
+                            else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
                         }
-                        doc._height = actualdH;
+                        if (!modifyNativeDim) doc._height = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
+                        else doc._height = actualdH;
                     }
                 } else {
                     dH && (doc._height = actualdH);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 574d28b8f..364bf05e2 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -141,7 +141,7 @@ export class KeyManager {
             case "delete":
             case "backspace":
                 if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
-                    const selected = SelectionManager.Views().filter(dv => !dv.topMost);;
+                    const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     UndoManager.RunInBatch(() => {
                         SelectionManager.DeselectAll();
                         selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document));
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 7fa841002..c3cdb7dde 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -109,9 +109,9 @@
 
 .properties-container {
     height: 100%;
-    position: relative;
-    left: 100%;
-    top: calc(-100% - 36px);
+    position: absolute;
+    right: 0;
+    top: 0;
     z-index: 3000;
 }
 
@@ -298,9 +298,8 @@
     width: var(--flyoutHandleWidth);
     height: 55px;
     top: 50%;
-    left: -10px;
     border-radius: 8px;
-    position: relative;
+    position: absolute;
     z-index: 41; // lm_maximised has a z-index of 40 and this needs to be above that
     touch-action: none;
     cursor: grab;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7edcd6217..3f7df705f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -426,23 +426,23 @@ export class MainView extends React.Component {
     }
 
     @computed get mainInnerContent() {
-        const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth();
-        const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined;
+        const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
+        const width = this.propertiesWidth() + leftMenuFlyoutWidth;
         return <>
             {this.leftMenuPanel}
             <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
                 {this.flyout}
-                <div className="mainView-libraryHandle" style={{ display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+                <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
                     <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
                 </div>
-                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
+                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
 
                     {this.dockingContent}
 
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._leftMenuFlyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
+                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
                         <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
-                    <div className="properties-container">
+                    <div className="properties-container" style={{ width: this.propertiesWidth() }}>
                         {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                     </div>
                 </div>
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9142a68ed..4238f6d29 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -13,6 +13,7 @@
         width: 100%;
         pointer-events: none;
         mix-blend-mode: multiply; // bcz: makes text fuzzy!
+        overflow: hidden;
     }
 
     .imageBox-fader {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b927e98db..89f70985c 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -354,15 +354,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 renderDepth={this.props.renderDepth + 1}
-                isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
                 CollectionView={undefined}
+                isAnnotationOverlay={true}
                 annotationLayerHostsContent={true}
                 PanelWidth={this.props.PanelWidth}
                 PanelHeight={this.props.PanelHeight}
                 ScreenToLocalTransform={this.screenToLocalTransform}
-                scaling={returnOne}
                 select={emptyFunction}
+                scaling={returnOne}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index cdd36eb3b..4871599b8 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -14,6 +14,9 @@
     height: 100%;
     position: relative;
     .videoBox-viewer {
+        display:flex;
+        flex-direction: column;
+        height: 100%;
         border-radius: inherit;
         opacity: 0.99;  // hack!  overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
     }
@@ -26,7 +29,17 @@
     .videoBox-stackPanel {
         z-index: -1;
         width: 100%;
-        position: absolute; 
+        position: relative; 
+    }
+
+    .videoBox-annotationLayer {
+        position: relative;
+        transform-origin: left top;
+        top: 0;
+        width: 100%;
+        pointer-events: none;
+        mix-blend-mode: multiply; // bcz: makes text fuzzy!
+        overflow: hidden;
     }
 }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e3ba638b3..440ccf638 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -550,7 +550,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     @computed get annotationLayer() {
-        return <div className="imageBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
+        return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
     }
 
     marqueeDown = (e: React.PointerEvent) => {
@@ -568,10 +568,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.props.select(true);
     });
 
+    @computed get fitWidth() { return this.props.docViewPath?.().lastElement().fitWidth; }
     contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
     scaling = () => this.props.scaling?.() || 1;
-    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+    panelWidth = (): number => this.fitWidth ? this.props.PanelWidth() : (Doc.NativeAspect(this.rootDoc) || 1) * this.panelHeight();
+    panelHeight = (): number => this.fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.heightPercent / 100 * this.props.PanelHeight();
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
         return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
@@ -585,10 +586,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
             style={{
                 pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
-                borderRadius
+                borderRadius,
+                overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
             <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                <div style={{ position: "absolute", transition: this.transition, width: this.panelWidth(), height: this.panelHeight(), top: 0, left: `${(100 - this.heightPercent) / 2}%` }}>
+                <div style={{
+                    position: "absolute", transition: this.transition,
+                    width: this.panelWidth(),
+                    height: this.panelHeight(),
+                    top: 0,
+                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+                }}>
                     <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                         renderDepth={this.props.renderDepth + 1}
                         fieldKey={this.annotationKey}
@@ -608,9 +616,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         {this.contentFunc}
                     </CollectionFreeFormView>
                 </div>
-                {this.uIButtons}
                 {this.annotationLayer}
-                {this.renderTimeline}
                 {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
                     <MarqueeAnnotator
                         rootDoc={this.rootDoc}
@@ -625,6 +631,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         annotationLayer={this._annotationLayer.current}
                         mainCont={this._mainCont.current}
                     />}
+                {this.renderTimeline}
+                {this.uIButtons}
             </div>
         </div >);
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8e6586735..37d716993 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -430,9 +430,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!cm.findByDescription("Options...")) {
             !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
             funcs.push({
-                description: (!this.layoutDoc.allowReflow ? "Allow" : "Prevent") + " Reflow", event: () => {
-                    const nw = !this.layoutDoc.allowReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
-                    this.layoutDoc.allowReflow = !nw;
+                description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => {
+                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
+                    this.layoutDoc.forceReflow = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true);
                     }
@@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
-            style={{ width: !this.layoutDoc.allowReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
+            style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
     }
-- 
cgit v1.2.3-70-g09d2

