From d4fd33ea337b1136344b73b92e9d5494ff94f090 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 3 Mar 2020 10:49:22 -0500
Subject: fixes to allow navigation from hashtag tag to pivot/schema/stacking
 views

---
 .../views/collections/CollectionStackingView.tsx   | 30 +++++++++++-----------
 1 file changed, 15 insertions(+), 15 deletions(-)

(limited to 'src/client/views/collections/CollectionStackingView.tsx')

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d1f45af90..f84b0af20 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -31,21 +31,21 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _heightDisposer?: IReactionDisposer;
-    _sectionFilterDisposer?: IReactionDisposer;
+    _pivotFieldDisposer?: IReactionDisposer;
     _docXfs: any[] = [];
     _columnStart: number = 0;
     @observable _heightMap = new Map<string, number>();
     @observable _cursor: CursorProperty = "grab";
     @observable _scroll = 0; // used to force the document decoration to update when scrolling
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
-    @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
+    @computed get pivotField() { return StrCast(this.props.Document._pivotField); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
     @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
     @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
-    @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); }
+    @computed get showAddAGroup() { return (this.pivotField && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); }
     @computed get columnWidth() {
         return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
             this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
@@ -73,7 +73,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     get Sections() {
-        if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+        if (!this.pivotField || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
         if (this.sectionHeaders === undefined) {
             setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
@@ -83,18 +83,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
         let changed = false;
         this.filteredChildren.map(d => {
-            const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
+            const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
             // the next five lines ensures that floating point rounding errors don't create more than one section -syip
             const parsed = parseInt(sectionValue.toString());
             const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
             // look for if header exists already
-            const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
+            const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
             }
             else {
-                const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
+                const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
                 sectionHeaders.push(newSchemaHeader);
                 changed = true;
@@ -134,15 +134,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         );
 
         // reset section headers when a new filter is inputted
-        this._sectionFilterDisposer = reaction(
-            () => this.sectionFilter,
+        this._pivotFieldDisposer = reaction(
+            () => this.pivotField,
             () => this.props.Document.sectionHeaders = new List()
         );
     }
     componentWillUnmount() {
         super.componentWillUnmount();
-        this._heightDisposer && this._heightDisposer();
-        this._sectionFilterDisposer && this._sectionFilterDisposer();
+        this._heightDisposer?.();
+        this._pivotFieldDisposer?.();
     }
 
     @action
@@ -278,7 +278,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
     headings = () => Array.from(this.Sections.keys());
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        const key = this.sectionFilter;
+        const key = this.pivotField;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
         const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
@@ -313,7 +313,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        const key = this.sectionFilter;
+        const key = this.pivotField;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
         const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
@@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (value && this.sectionHeaders) {
             const schemaHdrField = new SchemaHeaderField(value);
             this.sectionHeaders.push(schemaHdrField);
-            Doc.addEnumerationToTextField(undefined, this.sectionFilter, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]);
+            Doc.addEnumerationToTextField(undefined, this.pivotField, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]);
             return true;
         }
         return false;
@@ -370,7 +370,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get renderedSections() {
         TraceMobx();
         let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
-        if (this.sectionFilter) {
+        if (this.pivotField) {
             const entries = Array.from(this.Sections.entries());
             sections = entries.sort(this.sortFunc);
         }
-- 
cgit v1.2.3-70-g09d2


From a88608f33c15a79fecda08e3fddb5bc7690af127 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 3 Mar 2020 21:19:20 -0500
Subject: fixed enumerations a bit.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/RichTextSchema.tsx                 | 57 ++++++++++------------
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/new_fields/Doc.ts                              | 35 ++++---------
 .../authentication/models/current_user_utils.ts    | 16 +++---
 5 files changed, 44 insertions(+), 67 deletions(-)

(limited to 'src/client/views/collections/CollectionStackingView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6b25b3897..b06ff5465 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -108,6 +108,7 @@ export interface DocumentOptions {
     _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor )
     color?: string; // foreground color data doc
     _color?: string;  // foreground color for each template layout doc (overrides color)
+    caption?: RichTextField;
     ignoreClick?: boolean;
     lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2c3714310..649908317 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -887,32 +887,10 @@ export class DashFieldView {
 
         this._enumerables.onpointerdown = async (e) => {
             e.stopPropagation();
-            const collview = await Doc.addEnumerationToTextField(self._textBoxDoc, node.attrs.fieldKey, [Docs.Create.TextDocument(self._fieldSpan.innerText, { title: self._fieldSpan.innerText })]);
+            const collview = await Doc.addFieldEnumerations(self._textBoxDoc, node.attrs.fieldKey, [{ title: self._fieldSpan.innerText }]);
             collview instanceof Doc && tbox.props.addDocTab(collview, "onRight");
         }
-
-        this._fieldSpan = document.createElement("div");
-        this._fieldSpan.id = Utils.GenerateGuid();
-        this._fieldSpan.contentEditable = "true";
-        this._fieldSpan.style.position = "relative";
-        this._fieldSpan.style.display = "inline-block";
-        this._fieldSpan.style.minWidth = "5px";
-        this._fieldSpan.style.backgroundColor = "rgba(155, 155, 155, 0.24)";
-        this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); };
-        this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldSpan.onmousedown = function (e: any) {
-            e.stopPropagation();
-            self._enumerables.style.display = "inline-block";
-        };
-        this._fieldSpan.oncontextmenu = function (e: any) {
-            ContextMenu.Instance.addItem({
-                description: "Show Enumeration Templates", event: () => {
-                    e.stopPropagation();
-                    DocServer.GetRefField(node.attrs.fieldKey).then(collview => collview instanceof Doc && tbox.props.addDocTab(collview, "onRight"));
-                }, icon: "expand-arrows-alt"
-            });
-        };
-        this._fieldSpan.onblur = function (e: any) {
+        const updateText = (forceMatch: boolean) => {
             self._enumerables.style.display = "none";
             let newText = self._fieldSpan.innerText.startsWith(":=") ? ":=-computed-" : self._fieldSpan.innerText;
 
@@ -920,8 +898,12 @@ export class DashFieldView {
             // holds the different enumerated values for the field in the titles of its collected documents.
             // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
             DocServer.GetRefField(node.attrs.fieldKey).then(options => {
-                (options instanceof Doc) && DocListCast(options.data).forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title)));
-                self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = newText;
+                let modText = "";
+                (options instanceof Doc) && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
+                if (modText) {
+                    self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = modText;
+                    Doc.addFieldEnumerations(self._textBoxDoc, node.attrs.fieldKey, []);
+                }
 
                 // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
                 if (newText.startsWith(":=") && self._dashDoc && e.data === null && !e.inputType.includes("delete")) {
@@ -930,10 +912,22 @@ export class DashFieldView {
             });
         }
 
+        this._fieldSpan = document.createElement("div");
+        this._fieldSpan.id = Utils.GenerateGuid();
+        this._fieldSpan.contentEditable = "true";
+        this._fieldSpan.style.position = "relative";
+        this._fieldSpan.style.display = "inline-block";
+        this._fieldSpan.style.minWidth = "5px";
+        this._fieldSpan.style.backgroundColor = "rgba(155, 155, 155, 0.24)";
+        this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); };
+        this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); };
+        this._fieldSpan.onmousedown = function (e: any) { e.stopPropagation(); self._enumerables.style.display = "inline-block"; };
+        this._fieldSpan.onblur = function (e: any) { updateText(false); }
+
         const setDashDoc = (doc: Doc) => {
             self._dashDoc = doc;
-            if (this._dashDoc && self._options?.length && !this._dashDoc[node.attrs.fieldKey]) {
-                this._dashDoc[node.attrs.fieldKey] = StrCast(self._options[0].title);
+            if (self._dashDoc && self._options?.length && !self._dashDoc[node.attrs.fieldKey]) {
+                self._dashDoc[node.attrs.fieldKey] = StrCast(self._options[0].title);
             }
         }
         this._fieldSpan.onkeydown = function (e: any) {
@@ -949,10 +943,8 @@ export class DashFieldView {
             }
             if (e.key === "Enter") {
                 e.preventDefault();
-                if (e.ctrlKey) {
-                    Doc.addEnumerationToTextField(self._textBoxDoc, node.attrs.fieldKey, [Docs.Create.TextDocument(self._fieldSpan.innerText, { title: self._fieldSpan.innerText })]);
-                }
-                self._fieldSpan.onblur?.(undefined as any);
+                e.ctrlKey && Doc.addFieldEnumerations(self._textBoxDoc, node.attrs.fieldKey, [{ title: self._fieldSpan.innerText }]);
+                updateText(true);
             }
         };
 
@@ -997,6 +989,7 @@ export class DashFieldView {
         this._fieldWrapper.appendChild(this._fieldSpan);
         this._fieldWrapper.appendChild(this._enumerables);
         (this as any).dom = this._fieldWrapper;
+        updateText(false);
     }
     destroy() {
         this._reactionDisposer?.();
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f84b0af20..3f7f0a352 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         if (value && this.sectionHeaders) {
             const schemaHdrField = new SchemaHeaderField(value);
             this.sectionHeaders.push(schemaHdrField);
-            Doc.addEnumerationToTextField(undefined, this.pivotField, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]);
+            Doc.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
             return true;
         }
         return false;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6d94f050c..ce69d95b7 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -847,40 +847,23 @@ export namespace Doc {
         return id;
     }
 
-    // setup a document to use enumerated values for a specified field name:
-    //  doc: text document
-    //  layoutString: species which text field receives the document's main text (e.g., FormattedTextBox.LayoutString("Todo") )
-    //  enumeratedFieldKey : specifies which enumerated field of the document is displayed in the caption (e.g., taskStatus)
-    //  captionKey: specifies which field holds the caption template (e.g., caption) -- ideally this wouldn't be needed but would be derived from the layoutString's target field key
-    //
-    export function enumeratedTextTemplate(doc: Doc, layoutString: string, enumeratedFieldKey: string, enumeratedDocs: Doc[], captionKey: string = "caption") {
-        doc.caption = RichTextField.DashField(enumeratedFieldKey);
-        doc._showCaption = captionKey;
-        doc.layout = layoutString;
-
-        Doc.addEnumerationToTextField(doc, enumeratedFieldKey, enumeratedDocs);
-    }
-
-    export async function getEnumerationTextField(enumeratedFieldKey: string) {
-        return (await DocServer.GetRefField(enumeratedFieldKey)) as Doc;
-    }
-
-    export async function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
+    export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
         let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
         if (!(optionsCollection instanceof Doc)) {
             optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
             Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
         }
         const options = optionsCollection as Doc;
-        doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
-        doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
-        enumeratedDocs.map(enumeratedDoc => {
-            const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
+        const targetDoc = doc && Doc.GetProto(Cast(doc.expandedTemplate, Doc, null) || doc);
+        targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this)["${enumeratedFieldKey}"])?._backgroundColor || "white"`, undefined, { options }));
+        targetDoc && (targetDoc.color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this)["${enumeratedFieldKey}"]).color || "black"`, undefined, { options }));
+        enumerations.map(enumeration => {
+            const found = DocListCast(options.data).find(d => d.title === enumeration.title);
             if (found) {
-                found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
-                found._color = enumeratedDoc._color || found._color;
+                found._backgroundColor = enumeration._backgroundColor || found._backgroundColor;
+                found._color = enumeration.color || found._color;
             } else {
-                Doc.AddDocToList(options, "data", enumeratedDoc);
+                Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration));
             }
         });
         return optionsCollection;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 0f8d8fec8..f672da085 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -37,20 +37,20 @@ export class CurrentUserUtils {
     @observable public static GuestMobile: Doc | undefined;
 
     static setupDefaultDocTemplates(doc: Doc, buttons?: string[]) {
-        const taskStatusValues = [
-            Docs.Create.TextDocument("todo", { title: "todo", _backgroundColor: "blue", color: "white" }),
-            Docs.Create.TextDocument("in progress", { title: "in progress", _backgroundColor: "yellow", color: "black" }),
-            Docs.Create.TextDocument("completed", { title: "completed", _backgroundColor: "green", color: "white" })
+        const taskStatusValues = [  { title: "todo", _backgroundColor: "blue", color: "white" },
+                                    { title: "in progress", _backgroundColor: "yellow", color: "black" },
+                                    { title: "completed", _backgroundColor: "green", color: "white" }
         ];
         const noteTemplates = [
             Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }),
             Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }),
             Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }),
             Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption" })
+            Docs.Create.TextDocument("", { title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange",_autoHeight: false, 
+                layout:FormattedTextBox.LayoutString("Todo"), _height: 100, _showCaption: "caption",caption: RichTextField.DashField("taskStatus") })
         ];
         doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
-        Doc.enumeratedTextTemplate(Doc.GetProto(noteTemplates[4]), FormattedTextBox.LayoutString("Todo"), "taskStatus", taskStatusValues);
+        Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
         doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Types", _height: 75 }));
     }
 
@@ -268,9 +268,9 @@ export class CurrentUserUtils {
             ],
             { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false });
         slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
-        const descriptionTemplate = Docs.Create.TextDocument("", { title: "descriptionView", _height: 100, _showTitle: "title" });
+        const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
         Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
-        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate);
+        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
 
         const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("setNativeView(this)") });
         Doc.GetProto(iconDoc).data = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
-- 
cgit v1.2.3-70-g09d2

