aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2019-07-11 15:48:19 -0400
committerGitHub <noreply@github.com>2019-07-11 15:48:19 -0400
commitb4350c48aee6474692330761f96f8d93bfd4b2da (patch)
tree2e5048b21a3360faeaf74b6609b58083f496cd88
parent7006f67b7cf15734fb0b63d917ae392758704f75 (diff)
parent1b203afbd297f8ae9400a47c9379df912f9ef3b5 (diff)
Merge pull request #201 from browngraphicslab/extensionFields
Extension fields
-rw-r--r--src/client/documents/Documents.ts11
-rw-r--r--src/client/util/TooltipTextMenu.tsx2
-rw-r--r--src/client/views/InkingCanvas.tsx2
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx18
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx4
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx14
-rw-r--r--src/client/views/collections/CollectionSubView.tsx7
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx14
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx4
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss43
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx61
-rw-r--r--src/client/views/nodes/DocumentView.tsx44
-rw-r--r--src/client/views/nodes/FieldView.tsx6
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx3
-rw-r--r--src/client/views/nodes/PDFBox.tsx4
-rw-r--r--src/client/views/pdf/Annotation.tsx4
-rw-r--r--src/client/views/pdf/PDFViewer.tsx21
-rw-r--r--src/client/views/pdf/Page.tsx4
-rw-r--r--src/client/views/search/SearchItem.tsx6
-rw-r--r--src/new_fields/Doc.ts9
22 files changed, 143 insertions, 144 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d1c9feb32..335dfcfef 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -157,6 +157,9 @@ export namespace Docs {
pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype();
iconProto = fields[iconProtoId] as Doc || CreateIconPrototype();
importProto = fields[importProtoId] as Doc || CreateImportPrototype();
+ pdfProto.layout = CollectionPDFView.LayoutString("data", "annotations");
+ imageProto.layout = CollectionView.LayoutString("data", "annotations");
+ videoProto.layout = CollectionVideoView.LayoutString("data", "annotations");
});
}
@@ -174,7 +177,7 @@ export namespace Docs {
}
function CreateImagePrototype(): Doc {
- let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"),
+ let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("data", "annotations"),
{ x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0, type: DocTypes.IMG });
return imageProto;
}
@@ -185,7 +188,7 @@ export namespace Docs {
}
function CreateHistogramPrototype(): Doc {
- let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"),
+ let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("data"),
{ x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST });
return histoProto;
}
@@ -200,7 +203,7 @@ export namespace Docs {
return textProto;
}
function CreatePdfPrototype(): Doc {
- let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"),
+ let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("data", "annotations"),
{ x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1, type: DocTypes.PDF });
return pdfProto;
}
@@ -221,7 +224,7 @@ export namespace Docs {
return kvpProto;
}
function CreateVideoPrototype(): Doc {
- let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"),
+ let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("data", "annotations"),
{ x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0, type: DocTypes.VID });
return videoProto;
}
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 81a966923..930379c65 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -450,7 +450,7 @@ export class TooltipTextMenu {
let node = state.doc.nodeAt(from);
node && node.marks.map(m => {
m.type === markType && (curLink = m.attrs.href);
- })
+ });
//toggleMark(markType)(state, dispatch);
//return true;
}
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index fd7e5b07d..2c54054a5 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -61,7 +61,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
}
set inkData(value: Map<string, StrokeData>) {
- Doc.GetProto(this.props.Document)[this.props.inkFieldKey] = new InkField(value);
+ this.props.Document[this.props.inkFieldKey] = new InkField(value);
}
@action
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index e4f9b5058..2eb2a727c 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -74,21 +74,26 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
this.props.whenActiveChanged(isActive);
}
+ @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
+
@action.bound
addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
+ let self = this;
var curPage = NumCast(this.props.Document.curPage, -1);
Doc.GetProto(doc).page = curPage;
if (curPage >= 0) {
Doc.GetProto(doc).annotationOn = this.props.Document;
}
allowDuplicates = true;
- const value = Cast(this.dataDoc[this.dataField], listSpec(Doc));
+ let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
+ let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
+ const value = Cast(targetDataDoc[targetField], listSpec(Doc));
if (value !== undefined) {
if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
value.push(doc);
}
} else {
- Doc.GetProto(this.dataDoc)[this.dataField] = new List([doc]);
+ Doc.GetProto(targetDataDoc)[targetField] = new List([doc]);
}
return true;
}
@@ -98,7 +103,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
//TODO This won't create the field if it doesn't already exist
- const value = Cast(this.dataDoc[this.dataField], listSpec(Doc), []);
+ let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
+ let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
+ let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
let index = value.reduce((p, v, i) => (v instanceof Doc && v[Id] === doc[Id]) ? i : p, -1);
PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined)
@@ -116,7 +123,10 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
@action.bound
moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- if (Doc.AreProtosEqual(this.dataDoc, targetCollection)) {
+ let self = this;
+ let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
+ if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) {
+ //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) {
return true;
}
if (this.removeDocument(doc)) {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 31a73ab36..c97443785 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -47,8 +47,8 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
this._reactionDisposer && this._reactionDisposer();
}
- public static LayoutString(fieldKey: string = "data") {
- return FieldView.LayoutString(CollectionPDFView, fieldKey);
+ public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") {
+ return FieldView.LayoutString(CollectionPDFView, fieldKey, fieldExt);
}
@observable _inThumb = false;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 21a2a7dd0..a55549280 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -401,8 +401,8 @@ interface CollectionSchemaPreviewProps {
Document?: Doc;
DataDocument?: Doc;
childDocs?: Doc[];
- fitToBox?: () => number[];
renderDepth: number;
+ fitToBox?: boolean;
width: () => number;
height: () => number;
showOverlays?: (doc: Doc) => { title?: string, caption?: string };
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 372891923..62968821a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -14,6 +14,7 @@ import { undoBatch } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
import { DocTypes } from "../../documents/Documents";
import { Transform } from "../../util/Transform";
+import { resolve } from "bluebird";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -50,17 +51,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
overlays = (doc: Doc) => {
- return doc.type === DocTypes.IMG ? { title: "title", caption: "caption" } : {}
+ return doc.type === DocTypes.IMG ? { title: "title", caption: "caption" } : {};
}
getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
- let dataDoc = d !== this.props.DataDoc ? this.props.DataDoc : undefined
+ let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+ let dataDoc = d !== this.props.DataDoc ? this.props.DataDoc : undefined;
let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
let height = () => this.getDocHeight(layoutDoc);
let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
return <CollectionSchemaPreview
Document={layoutDoc}
- DataDocument={dataDoc}
+ DataDocument={resolvedDataDoc}
showOverlays={this.overlays}
renderDepth={this.props.renderDepth}
width={width}
@@ -75,7 +77,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
addDocTab={this.props.addDocTab}
setPreviewScript={emptyFunction}
previewScript={undefined}>
- </CollectionSchemaPreview>
+ </CollectionSchemaPreview>;
}
getDocHeight(d: Doc) {
let nw = NumCast(d.nativeWidth);
@@ -176,7 +178,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
}
- })
+ });
}
if (super.drop(e, de)) {
let newDoc = de.data.droppedDocuments[0];
@@ -202,7 +204,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
}
- })
+ });
super.onDrop(e, {}, () => {
if (targInd !== -1) {
let newDoc = this.childDocs[this.childDocs.length - 1];
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 496f7e461..d2b808d4a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -48,16 +48,17 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
+
get childDocs() {
let self = this;
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
- return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
+ return DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
}
get childDocList() {
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
- return Cast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc));
+ return Cast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc));
}
@action
@@ -102,7 +103,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
} else if (de.data.moveDocument) {
let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
added = movedDocs.reduce((added: boolean, d) =>
- de.data.moveDocument(d, this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.addDocument) || added, false);
+ de.data.moveDocument(d, /*this.props.DataDoc ? this.props.DataDoc :*/ this.props.Document, this.props.addDocument) || added, false);
} else {
added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f8e5f72e4..b2ac0fa2e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -75,7 +75,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed get fieldKey() {
let keys = Array.from(Object.keys(this.resolvedDataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set
if (this.resolvedDataDoc.proto instanceof Doc) {
- let arr = Array.from(Object.keys(this.resolvedDataDoc.proto!));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set
+ let arr = Array.from(Object.keys(this.resolvedDataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set
keys.push(...arr);
while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
}
@@ -322,10 +322,10 @@ class TreeView extends React.Component<TreeViewProps> {
return ele;
}
- @computed get docBounds() {
+ @computed get boundsOfCollectionDocument() {
if (StrCast(this.props.document.type).indexOf(DocTypes.COL) === -1) return undefined;
let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
- return Doc.ComputeContentBounds(layoutDoc);
+ return Doc.ComputeContentBounds(DocListCast(layoutDoc.data));
}
docWidth = () => {
let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
@@ -333,7 +333,7 @@ class TreeView extends React.Component<TreeViewProps> {
return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
}
docHeight = () => {
- let bounds = this.docBounds;
+ let bounds = this.boundsOfCollectionDocument;
return Math.min(this.MAX_EMBED_HEIGHT, (() => {
let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
if (aspect) return this.docWidth() * aspect;
@@ -341,10 +341,6 @@ class TreeView extends React.Component<TreeViewProps> {
return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
})());
}
- fitToBox = () => {
- let bounds = this.docBounds!;
- return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.docHeight() / (bounds.b - bounds.y), this.docWidth() / (bounds.r - bounds.x))];
- }
render() {
let contentElement: (JSX.Element | null) = null;
@@ -367,7 +363,7 @@ class TreeView extends React.Component<TreeViewProps> {
Document={layoutDoc}
DataDocument={this.resolvedDataDoc}
renderDepth={this.props.renderDepth}
- fitToBox={this.docBounds && !NumCast(this.props.document.nativeWidth) ? this.fitToBox : undefined}
+ fitToBox={this.boundsOfCollectionDocument !== undefined}
width={this.docWidth}
height={this.docHeight}
getTransform={this.docTransform}
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 1984965ba..16287a658 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -21,8 +21,8 @@ import { Docs, DocUtils } from "../../documents/Documents";
export class CollectionVideoView extends React.Component<FieldViewProps> {
private _videoBox?: VideoBox;
- public static LayoutString(fieldKey: string = "data") {
- return FieldView.LayoutString(CollectionVideoView, fieldKey);
+ public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") {
+ return FieldView.LayoutString(CollectionVideoView, fieldKey, fieldExt);
}
private get uIButtons() {
let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e500e5c70..56750668d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -27,7 +27,7 @@ library.add(faThList);
@observer
export class CollectionView extends React.Component<FieldViewProps> {
- public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(CollectionView, fieldStr); }
+ public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
let props = { ...this.props, ...renderProps };
@@ -43,7 +43,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
return (null);
}
- get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
+ get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
onContextMenu = (e: React.MouseEvent): void => {
if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index ccf261c95..ec0e446e9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -37,8 +37,9 @@
// background-size: 30px 30px;
// }
box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
+ opacity: 0.99;
border: 0px solid $light-color-secondary;
- border-radius: $border-radius;
+ border-radius: inherit;
box-sizing: border-box;
position: absolute;
@@ -52,46 +53,6 @@
height: 100%;
}
-
-.collectionfreeformview-overlay {
- .collectionfreeformview>.jsx-parser {
- position: inherit;
- height: 100%;
- }
-
- >.jsx-parser {
- position: absolute;
- z-index: 0;
- }
-
- .formattedTextBox-cont {
- background: $light-color-secondary;
- overflow: visible;
- }
-
- opacity: 0.99;
- border: 0px solid transparent;
- border-radius: $border-radius;
- box-sizing: border-box;
- position:absolute;
- z-index: -1;
-
- .marqueeView {
- overflow: hidden;
- }
-
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-
- .collectionfreeformview {
- .formattedTextBox-cont {
- background: yellow;
- }
- }
-}
-
// selection border...?
.border {
border-style: solid;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2c9b6b6a9..2b6317541 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -52,13 +52,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private get _pwidth() { return this.props.PanelWidth(); }
private get _pheight() { return this.props.PanelHeight(); }
+ @computed get contentBounds() {
+ let bounds = this.props.fitToBox && !NumCast(this.nativeWidth) ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined;
+ return {
+ panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0,
+ panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0,
+ scale: bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1
+ };
+ }
+
@computed get nativeWidth() { return this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
- public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
+ public get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt')
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0;
- private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0;
- private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox()[2] : this.Document.scale || 1;
+ private panX = () => this.contentBounds.panX;
+ private panY = () => this.contentBounds.panY;
+ private zoomScaling = () => this.contentBounds.scale;
private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
@@ -86,6 +95,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
});
}
+ @computed get fieldExtensionDoc() {
+ return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true");
+ }
+
+
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
@@ -93,10 +107,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (de.data instanceof DragManager.DocumentDragData) {
if (de.data.droppedDocuments.length) {
let dragDoc = de.data.droppedDocuments[0];
- let zoom = NumCast(dragDoc.zoomBasis, 1);
let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
- let x = xp - de.data.xOffset / zoom;
- let y = yp - de.data.yOffset / zoom;
+ let x = xp - de.data.xOffset;
+ let y = yp - de.data.yOffset;
let dropX = NumCast(de.data.droppedDocuments[0].x);
let dropY = NumCast(de.data.droppedDocuments[0].y);
de.data.droppedDocuments.forEach(d => {
@@ -117,10 +130,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
else if (de.data instanceof DragManager.AnnotationDragData) {
if (de.data.dropDocument) {
let dragDoc = de.data.dropDocument;
- let zoom = NumCast(dragDoc.zoomBasis, 1);
let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
- let x = xp - de.data.xOffset / zoom;
- let y = yp - de.data.yOffset / zoom;
+ let x = xp - de.data.xOffset;
+ let y = yp - de.data.yOffset;
let dropX = NumCast(de.data.dropDocument.x);
let dropY = NumCast(de.data.dropDocument.y);
dragDoc.x = x + NumCast(dragDoc.x) - dropX;
@@ -159,18 +171,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (!this.isAnnotationOverlay) {
PDFMenu.Instance.fadeOut(true);
let minx = docs.length ? NumCast(docs[0].x) : 0;
- let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx;
+ let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
let miny = docs.length ? NumCast(docs[0].y) : 0;
- let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis, 1) + miny : miny;
+ let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
let ranges = docs.filter(doc => doc).reduce((range, doc) => {
let x = NumCast(doc.x);
- let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis, 1);
+ let xe = x + NumCast(doc.width);
let y = NumCast(doc.y);
- let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis, 1);
+ let ye = y + NumCast(doc.height);
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
- let ink = Cast(this.extensionDoc.ink, InkField);
+ let ink = Cast(this.fieldExtensionDoc.ink, InkField);
if (ink && ink.inkData) {
ink.inkData.forEach((value: StrokeData, key: string) => {
let bounds = InkingCanvas.StrokeRect(value);
@@ -283,6 +295,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const panY = this.Document.panY;
const id = this.Document[Id];
const state = HistoryUtil.getState();
+ state.initializers = state.initializers || {};
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
if (state.type === "doc" && panX !== undefined && panY !== undefined) {
@@ -299,10 +312,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
SelectionManager.DeselectAll();
- const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2;
- const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2;
+ const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
const newState = HistoryUtil.getState();
- newState.initializers[id] = { panX: newPanX, panY: newPanY };
+ (newState.initializers || (newState.initializers = {}))[id] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
this.setPan(newPanX, newPanY);
@@ -343,7 +356,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
- let resolvedDataDoc = this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+ let self = this;
+ let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
let layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc);
return {
DataDoc: resolvedDataDoc !== layoutDoc && resolvedDataDoc ? resolvedDataDoc : undefined,
@@ -498,22 +512,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
...this.views
]
render() {
- const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
const easing = () => this.props.Document.panTransformType === "Ease";
- if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
+ Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
return (
- <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- style={{ borderRadius: "inherit" }}
+ <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onContextMenu={this.onContextMenu}>
<MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
<CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-
<CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.extensionDoc} inkFieldKey={this.props.fieldExt ? "ink" : this.props.fieldKey + "_ink"} >
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.fieldExtensionDoc} inkFieldKey={"ink"} >
{this.childViews}
</InkingCanvas>
</CollectionFreeFormLinksView>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6f3adb6b5..65dfd5a84 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -73,7 +73,7 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
- fitToBox?: () => number[];
+ fitToBox?: boolean;
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
@@ -530,7 +530,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.addDocument && this.props.addDocument(coll);
this.props.removeDocument && this.props.removeDocument(this.props.Document);
}, icon: "window-restore"
- })
+ });
cm.addItem({
description: "Find aliases", event: async () => {
const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
@@ -542,25 +542,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
type User = { email: string, userDocumentId: string };
- const users: User[] = JSON.parse(await rp.get(DocServer.prepend(RouteStore.getUsers)));
- let usersMenu: ContextMenuProps[] = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
- description: email, event: async () => {
- const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc);
- if (!userDocument) {
- throw new Error(`Couldn't get user document of user ${email}`);
- }
- const notifDoc = await Cast(userDocument.optionalRightCollection, Doc);
- if (notifDoc instanceof Doc) {
- const data = await Cast(notifDoc.data, listSpec(Doc));
- const sharedDoc = Doc.MakeAlias(this.props.Document);
- if (data) {
- data.push(sharedDoc);
- } else {
- notifDoc.data = new List([sharedDoc]);
+ let usersMenu: ContextMenuProps[] = [];
+ try {
+ let stuff = await rp.get(DocServer.prepend(RouteStore.getUsers));
+ const users: User[] = JSON.parse(stuff);
+ usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
+ description: email, event: async () => {
+ const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc);
+ if (!userDocument) {
+ throw new Error(`Couldn't get user document of user ${email}`);
+ }
+ const notifDoc = await Cast(userDocument.optionalRightCollection, Doc);
+ if (notifDoc instanceof Doc) {
+ const data = await Cast(notifDoc.data, listSpec(Doc));
+ const sharedDoc = Doc.MakeAlias(this.props.Document);
+ if (data) {
+ data.push(sharedDoc);
+ } else {
+ notifDoc.data = new List([sharedDoc]);
+ }
}
}
- }
- }));
+ }));
+ } catch {
+
+ }
runInAction(() => {
cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" });
if (!this.topMost) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c5fc6c65a..0c084035a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -31,7 +31,7 @@ export interface FieldViewProps {
fieldKey: string;
fieldExt: string;
leaveNativeSize?: boolean;
- fitToBox?: () => number[];
+ fitToBox?: boolean;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
@@ -55,8 +55,8 @@ export interface FieldViewProps {
@observer
export class FieldView extends React.Component<FieldViewProps> {
- public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") {
- return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`;
+ public static LayoutString(fieldType: { name: string }, fieldStr: string = "data", fieldExt: string = "") {
+ return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} fieldExt={"${fieldExt}"} />`;
}
@computed
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5475aa2b2..084fd4c96 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -232,9 +232,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
field2 => {
- if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) // bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field.
+ if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) { // bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field.
this._editorView && !this._applyingChange &&
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2)));
+ }
}
);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2ff5c089f..771a1b5d6 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -230,6 +230,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
}
+
+ @computed get fieldExtensionDoc() {
+ return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true");
+ }
render() {
// uses mozilla pdf as default
const pdfUrl = Cast(this.props.Document.data, PdfField);
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 0a1661a1a..104241237 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -87,11 +87,11 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
deleteAnnotation = () => {
- let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+ let annotation = DocListCast(this.props.parent.props.parent.fieldExtensionDoc.annotations);
let group = FieldValue(Cast(this.props.document.group, Doc));
if (group && annotation.indexOf(group) !== -1) {
let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
- this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+ this.props.parent.props.parent.fieldExtensionDoc.annotations = new List<Doc>(newAnnotations);
}
if (group) {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 761db7922..b5efabed5 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -125,9 +125,12 @@ export class Viewer extends React.Component<IViewerProps> {
}, { fireImmediately: true });
this._annotationReactionDisposer = reaction(
- () => this.props.parent.Document && DocListCast(this.props.parent.Document.annotations),
- (annotations: Doc[]) =>
- annotations && annotations.length && this.renderAnnotations(annotations, true),
+ () => {
+ return this.props.parent && this.props.parent.fieldExtensionDoc && DocListCast(this.props.parent.fieldExtensionDoc.annotations)
+ },
+ (annotations: Doc[]) => {
+ annotations && annotations.length && this.renderAnnotations(annotations, true);
+ },
{ fireImmediately: true });
this._activeReactionDisposer = reaction(
@@ -156,7 +159,9 @@ export class Viewer extends React.Component<IViewerProps> {
let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
this._script = scriptfield ? scriptfield.script : CompileScript("return true");
if (this.props.parent.props.ContainingCollectionView) {
- let ccvAnnos = DocListCast(this.props.parent.props.ContainingCollectionView.props.Document.annotations);
+ let fieldDoc = Doc.resolvedFieldDataDoc(this.props.parent.props.ContainingCollectionView.props.DataDoc ?
+ this.props.parent.props.ContainingCollectionView.props.DataDoc : this.props.parent.props.ContainingCollectionView.props.Document, this.props.parent.props.ContainingCollectionView.props.fieldKey, "true");
+ let ccvAnnos = DocListCast(fieldDoc.annotations);
ccvAnnos.forEach(d => {
if (this._script && this._script.compiled) {
let run = this._script.run(d);
@@ -270,13 +275,13 @@ export class Viewer extends React.Component<IViewerProps> {
if (de.data instanceof DragManager.LinkDragData) {
let sourceDoc = de.data.linkSourceDocument;
let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red");
- let targetAnnotations = DocListCast(this.props.parent.Document.annotations);
+ let targetAnnotations = DocListCast(this.props.parent.fieldExtensionDoc.annotations);
if (targetAnnotations) {
targetAnnotations.push(destDoc);
- this.props.parent.Document.annotations = new List<Doc>(targetAnnotations);
+ this.props.parent.fieldExtensionDoc.annotations = new List<Doc>(targetAnnotations);
}
else {
- this.props.parent.Document.annotations = new List<Doc>([destDoc]);
+ this.props.parent.fieldExtensionDoc.annotations = new List<Doc>([destDoc]);
}
e.stopPropagation();
}
@@ -588,7 +593,7 @@ export class Viewer extends React.Component<IViewerProps> {
}
return true;
});
- this.Index = Math.min(this.Index + 1, filtered.length - 1)
+ this.Index = Math.min(this.Index + 1, filtered.length - 1);
}
nextResult = () => {
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 49eac71c4..7c843fc0b 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -138,9 +138,9 @@ export default class Page extends React.Component<IPageProps> {
highlight = (targetDoc?: Doc, color: string = "red") => {
// creates annotation documents for current highlights
let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color, false);
- let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
+ let targetAnnotations = Cast(this.props.parent.fieldExtensionDoc.annotations, listSpec(Doc));
if (targetAnnotations === undefined) {
- Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
+ Doc.GetProto(this.props.parent.fieldExtensionDoc).annotations = new List([annotationDoc]);
} else {
targetAnnotations.push(annotationDoc);
}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 87cae5487..c34e4febd 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -104,10 +104,6 @@ export class SearchItem extends React.Component<SearchItemProps> {
@observable _useIcons = true;
@observable _displayDim = 50;
- fitToBox = () => {
- let bounds = Doc.ComputeContentBounds(this.props.doc);
- return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim];
- }
@computed
public get DocumentIcon() {
if (!this._useIcons) {
@@ -119,7 +115,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
onPointerLeave={action(() => this._displayDim = 50)} >
<DocumentView
- fitToBox={StrCast(this.props.doc.type).indexOf(DocTypes.COL) !== -1 ? this.fitToBox : undefined}
+ fitToBox={StrCast(this.props.doc.type).indexOf(DocTypes.COL) !== -1}
Document={this.props.doc}
addDocument={returnFalse}
removeDocument={returnFalse}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 3d46243e0..d28a29e40 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -264,8 +264,11 @@ export namespace Doc {
return true;
}
- export function ComputeContentBounds(doc: Doc) {
- let bounds = DocListCast(doc.data).reduce((bounds, doc) => {
+ //
+ // Computes the bounds of the contents of a set of documents.
+ //
+ export function ComputeContentBounds(docList: Doc[]) {
+ let bounds = docList.reduce((bounds, doc) => {
var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
return {
@@ -372,7 +375,7 @@ export namespace Doc {
}
let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
if (backgroundLayout) {
- layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
+ layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
}
let nw = Cast(fieldTemplate.nativeWidth, "number");