aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/TestBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/TestBox.tsx')
-rw-r--r--src/client/views/nodes/TestBox.tsx467
1 files changed, 0 insertions, 467 deletions
diff --git a/src/client/views/nodes/TestBox.tsx b/src/client/views/nodes/TestBox.tsx
deleted file mode 100644
index de1640027..000000000
--- a/src/client/views/nodes/TestBox.tsx
+++ /dev/null
@@ -1,467 +0,0 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { ObjectField } from '../../../new_fields/ObjectField';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { ComputedField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { AudioField, ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
-import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
-import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { Docs } from '../../documents/Documents';
-import { Networking } from '../../Network';
-import { DragManager } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { undoBatch } from '../../util/UndoManager';
-import { ContextMenu } from "../../views/ContextMenu";
-import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import FaceRectangles from './FaceRectangles';
-import { FieldView, FieldViewProps } from './FieldView';
-import "./ImageBox.scss";
-import React = require("react");
-const requestImageSize = require('../../util/request-image-size');
-const path = require('path');
-const { Howl } = require('howler');
-
-
-library.add(faImage, faEye as any, faPaintBrush, faBrain);
-library.add(faFileAudio, faAsterisk);
-
-
-export const pageSchema = createSchema({
- curPage: "number",
- fitWidth: "boolean",
- googlePhotosUrl: "string",
- googlePhotosTags: "string"
-});
-
-interface Window {
- MediaRecorder: MediaRecorder;
-}
-
-declare class MediaRecorder {
- // whatever MediaRecorder has
- constructor(e: any);
-}
-
-type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
-const ImageDocument = makeInterface(pageSchema, documentSchema);
-
-const uploadIcons = {
- idle: "downarrow.png",
- loading: "loading.gif",
- success: "greencheck.png",
- failure: "redx.png"
-};
-
-@observer
-export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
- protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
- private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
- private _dropDisposer?: DragManager.DragDropDisposer;
- @observable private _audioState = 0;
- @observable static _showControls: boolean;
- @observable uploadIcon = uploadIcons.idle;
-
- protected createDropTarget = (ele: HTMLDivElement) => {
- this._dropDisposer && this._dropDisposer();
- ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
- }
-
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.complete.docDragData) {
- if (de.metaKey) {
- de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => {
- Doc.AddDocToList(this.dataDoc, this.fieldKey + "-alternates", drop);
- e.stopPropagation();
- }));
- } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
- const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
- const targetField = Doc.LayoutFieldKey(layoutDoc);
- const targetDoc = layoutDoc[DataSym];
- if (targetDoc[targetField] instanceof ImageField) {
- this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
- this.dataDoc[this.fieldKey + "-nativeWidth"] = NumCast(targetDoc[targetField + "-nativeWidth"]);
- this.dataDoc[this.fieldKey + "-nativeHeight"] = NumCast(targetDoc[targetField + "-nativeHeight"]);
- e.stopPropagation();
- }
- }
- }
- }
-
- recordAudioAnnotation = () => {
- let gumStream: any;
- let recorder: any;
- const self = this;
- navigator.mediaDevices.getUserMedia({
- audio: true
- }).then(function (stream) {
- gumStream = stream;
- recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async function (e: any) {
- const formData = new FormData();
- formData.append("file", e.data);
- const res = await fetch(Utils.prepend("/uploadFormData"), {
- method: 'POST',
- body: formData
- });
- const files = await res.json();
- const url = Utils.prepend(files[0].path);
- // upload to server with known URL
- const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = "layout";
- const audioAnnos = Cast(this.dataDoc[this.fieldKey + "-audioAnnotations"], listSpec(Doc));
- if (audioAnnos === undefined) {
- this.dataDoc[this.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
- } else {
- audioAnnos.push(audioDoc);
- }
- };
- runInAction(() => self._audioState = 2);
- recorder.start();
- setTimeout(() => {
- recorder.stop();
- runInAction(() => self._audioState = 0);
- gumStream.getAudioTracks()[0].stop();
- }, 5000);
- });
- }
-
- @undoBatch
- rotate = action(() => {
- const nw = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
- const nh = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
- const w = this.layoutDoc._width;
- const h = this.layoutDoc._height;
- this.dataDoc[this.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.fieldKey + "-rotation"]) + 90) % 360;
- this.dataDoc[this.fieldKey + "-nativeWidth"] = nh;
- this.dataDoc[this.fieldKey + "-nativeHeight"] = nw;
- this.layoutDoc._width = h;
- this.layoutDoc._height = w;
- });
-
- specificContextMenu = (e: React.MouseEvent): void => {
- const field = Cast(this.dataDoc[this.fieldKey], ImageField);
- if (field) {
- const funcs: ContextMenuProps[] = [];
- funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
- funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
- funcs.push({
- description: "Reset Native Dimensions", event: action(async () => {
- const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
- const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
- if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
- } else {
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
- }
- }), icon: "expand-arrows-alt"
- });
-
- const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
- const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
- modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
- modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
- //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
- !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
-
- ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" });
- }
- }
-
- extractFaces = () => {
- const converter = (results: any) => {
- return results.map((face: CognitiveServices.Image.Face) => Docs.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
- };
- this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter);
- }
-
- generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
- const converter = (results: any) => {
- const tagDoc = new Doc;
- const tagsList = new List();
- results.tags.map((tag: Tag) => {
- tagsList.push(tag.name);
- const sanitized = tag.name.replace(" ", "_");
- tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
- });
- this.dataDoc[this.fieldKey + "-generatedTags"] = tagsList;
- tagDoc.title = "Generated Tags Doc";
- tagDoc.confidence = threshold;
- return tagDoc;
- };
- this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-generatedTagsDoc"], this.url, Service.ComputerVision, converter);
- }
-
- @computed private get url() {
- const data = Cast(this.dataDoc[this.fieldKey], ImageField);
- return data ? data.url.href : undefined;
- }
-
- choosePath(url: URL) {
- const lower = url.href.toLowerCase();
- if (url.protocol === "data") {
- return url.href;
- } else if (url.href.indexOf(window.location.origin) === -1) {
- return Utils.CorsProxy(url.href);
- } else if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) {
- return url.href;//Why is this here
- }
- const ext = path.extname(url.href);
- const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix;
- return url.href.replace(ext, suffix + ext);
- }
-
- @observable _smallRetryCount = 1;
- @observable _mediumRetryCount = 1;
- @observable _largeRetryCount = 1;
- @action retryPath = () => {
- if (this._curSuffix === "_s") this._smallRetryCount++;
- if (this._curSuffix === "_m") this._mediumRetryCount++;
- if (this._curSuffix === "_l") this._largeRetryCount++;
- }
- @action onError = (error: any) => {
- const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
- if (timeout < 5) {
- setTimeout(this.retryPath, 500);
- } else {
- const original = StrCast(this.dataDoc[this.fieldKey + "-originalUrl"]);
- if (error.type === "error" && original) {
- this.dataDoc[this.fieldKey] = new ImageField(original);
- }
- }
- }
- _curSuffix = "_m";
-
- resize = (imgPath: string) => {
- const cachedNativeSize = {
- width: NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]),
- height: NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"])
- };
- const docAspect = this.layoutDoc[HeightSym]() / this.layoutDoc[WidthSym]();
- const cachedAspect = cachedNativeSize.height / cachedNativeSize.width;
- if (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(NumCast(this.layoutDoc._width) / NumCast(this.layoutDoc._height) - cachedNativeSize.width / cachedNativeSize.height) > 0.05) {
- if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) {
- requestImageSize(imgPath).then((inquiredSize: any) => {
- const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
- const rotatedNativeSize = rotation === 90 || rotation === 270 ? { height: inquiredSize.width, width: inquiredSize.height } : inquiredSize;
- const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width;
- setTimeout(action(() => {
- if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
- this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect;
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.layoutDoc._nativeWidth = rotatedNativeSize.width;
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.layoutDoc._nativeHeight = rotatedNativeSize.height;
- }
- }), 0);
- }).catch((err: any) => console.log(err));
- } else if (Math.abs(1 - docAspect / cachedAspect) > 0.1) {
- this.layoutDoc._width = this.layoutDoc[WidthSym]() || cachedNativeSize.width;
- this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect;
- }
- } else if (this.layoutDoc._nativeWidth !== cachedNativeSize.width || this.layoutDoc._nativeHeight !== cachedNativeSize.height) {
- !(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc) && setTimeout(() => {
- if (!(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc)) {
- this.layoutDoc._nativeWidth = cachedNativeSize.width;
- this.layoutDoc._nativeHeight = cachedNativeSize.height;
- }
- }, 0);
- }
- }
-
- @action
- onPointerEnter = () => {
- const self = this;
- const audioAnnos = DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]);
- if (audioAnnos && audioAnnos.length && this._audioState === 0) {
- const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
- anno.data instanceof AudioField && new Howl({
- src: [anno.data.url.href],
- format: ["mp3"],
- autoplay: true,
- loop: false,
- volume: 0.5,
- onend: function () {
- runInAction(() => self._audioState = 0);
- }
- });
- this._audioState = 1;
- }
- }
-
- audioDown = () => this.recordAudioAnnotation();
-
- considerGooglePhotosLink = () => {
- const remoteUrl = this.dataDoc.googlePhotosUrl;
- return !remoteUrl ? (null) : (<img
- style={{ transform: `scale(${this.props.ContentScaling()})`, transformOrigin: "bottom right" }}
- id={"google-photos"}
- src={"/assets/google_photos.png"}
- onClick={() => window.open(remoteUrl)}
- />);
- }
-
- considerGooglePhotosTags = () => {
- const tags = this.dataDoc.googlePhotosTags;
- return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />);
- }
-
- @computed
- private get considerDownloadIcon() {
- const data = this.dataDoc[this.fieldKey];
- if (!(data instanceof ImageField)) {
- return (null);
- }
- const primary = data.url.href;
- if (primary.includes(window.location.origin)) {
- return (null);
- }
- return (
- <img
- id={"upload-icon"}
- style={{ transform: `scale(${1 / this.props.ContentScaling()})`, transformOrigin: "bottom right" }}
- src={`/assets/${this.uploadIcon}`}
- onClick={async () => {
- const { dataDoc } = this;
- const { success, failure, idle, loading } = uploadIcons;
- runInAction(() => this.uploadIcon = loading);
- const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
- dataDoc[this.props.fieldKey + "-originalUrl"] = primary;
- let succeeded = true;
- let data: ImageField | undefined;
- try {
- data = new ImageField(Utils.prepend(accessPaths.agnostic.client));
- } catch {
- succeeded = false;
- }
- runInAction(() => this.uploadIcon = succeeded ? success : failure);
- setTimeout(action(() => {
- this.uploadIcon = idle;
- if (data) {
- dataDoc[this.fieldKey] = data;
- }
- }), 2000);
- }}
- />
- );
- }
-
- @computed get nativeSize() {
- const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
- const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], pw);
- const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1);
- return { nativeWidth, nativeHeight };
- }
-
- // this._curSuffix = "";
- // if (w > 20) {
- // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
- // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
- // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
- @computed get paths() {
- const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
- const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url.href).filter(url => url); // access the primary layout data of the alternate documents
- const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
- return paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- }
-
- @computed get content() {
- TraceMobx();
-
- const srcpath = this.paths[0];
- const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
- const { nativeWidth, nativeHeight } = this.nativeSize;
- const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
- const aspect = (rotation % 180) ? nativeHeight / nativeWidth : 1;
- const pwidth = this.props.PanelWidth();
- const pheight = this.props.PanelHeight();
- const shift = (rotation % 180) ? (pheight - pwidth) / aspect / 2 + (pheight - pwidth) / 2 : 0;
-
- this.resize(srcpath);
-
- return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget}>
- <div className="imageBox-fader" >
- <img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
- src={srcpath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
- width={nativeWidth}
- ref={this._imgRef}
- onError={this.onError} />
- {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker">
- <img className="imageBox-fadeaway"
- key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
- src={fadepath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})`, }}
- width={nativeWidth}
- ref={this._imgRef}
- onError={this.onError} /></div>}
- </div>
- {!this.layoutDoc._showAudio ? (null) :
- <div className="imageBox-audioBackground"
- onPointerDown={this.audioDown}
- onPointerEnter={this.onPointerEnter}
- style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
- >
- <FontAwesomeIcon className="imageBox-audioFont"
- style={{ color: [DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }}
- icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : faFileAudio} size="sm" />
- </div>}
- {this.considerDownloadIcon}
- {this.considerGooglePhotosLink()}
- <FaceRectangles document={this.dataDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
- </div>;
- }
-
- contentFunc = () => [this.content];
- render() {
- TraceMobx();
- const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
- return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu}
- style={{
- transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
- width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
- height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
- borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
- }} >
- <CollectionFreeFormView {...this.props}
- forceScaling={true}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- annotationsKey={this.annotationKey}
- isAnnotationOverlay={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- {this.contentFunc}
- </CollectionFreeFormView>
- </div >);
- }
-} \ No newline at end of file