diff options
author | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-04-21 09:30:59 +0530 |
---|---|---|
committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-04-21 09:30:59 +0530 |
commit | 62936fbe7186956e038d4e7bffbeafd7873bcd8c (patch) | |
tree | 6221036f874254e595b8293d299a8a4d57a95e9c | |
parent | cea978f8c3856c897efd92be4367ba31137e29d9 (diff) | |
parent | a9b6fba3a742f95815e2664770079bebd3b87d5f (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-rw-r--r-- | src/client/documents/Documents.ts | 1 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 2 | ||||
-rw-r--r-- | src/client/util/SearchUtil.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.scss | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 28 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss | 1 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/QueryBox.tsx | 13 | ||||
-rw-r--r-- | src/client/views/search/IconBar.tsx | 4 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 59 | ||||
-rw-r--r-- | src/server/ApiManagers/SearchManager.ts | 24 | ||||
-rw-r--r-- | src/server/authentication/models/current_user_utils.ts | 5 |
12 files changed, 76 insertions, 66 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e3cc8ccfe..5ff8f29ec 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -140,6 +140,7 @@ export interface DocumentOptions { icon?: string; sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script + searchFileTypes?: List<string>; // file types allowed in a search query strokeWidth?: number; treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view treeViewHideTitle?: boolean; // whether to hide the title of a tree view diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 3e9a5b63a..3ea030171 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -19,7 +19,7 @@ import { DateField } from "../../new_fields/DateField"; import { DocumentView } from "../views/nodes/DocumentView"; import { UndoManager } from "./UndoManager"; -export type dropActionType = "place" | "alias" | "copy" | undefined; +export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move export function SetupDrag( _reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc> | undefined, diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 2026bf940..6501da34a 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -44,7 +44,7 @@ export namespace SearchUtil { const { ids, highlighting } = result; const txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), { - qs: { ...options, q: query }, + qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, "") }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures })); const fileids = txtresult ? txtresult.ids : []; diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 1e59c493f..a00bb6bfb 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -82,6 +82,7 @@ text-overflow: ellipsis; white-space: pre-wrap; overflow: hidden; + min-width: 10px; // width:100%;//width: max-content; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 011e07287..f589c2c76 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -189,7 +189,7 @@ class TreeView extends React.Component<TreeViewProps> { Doc.SetInPlace(this.props.document, key, value, false); const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) }); Doc.SetInPlace(this.props.document, "editTitle", undefined, false); - Doc.SetInPlace(this.props.document, "editTitle", true, false); + Doc.SetInPlace(doc, "editTitle", true, false); return this.props.addDocument(doc); })} onClick={() => { @@ -228,7 +228,8 @@ class TreeView extends React.Component<TreeViewProps> { addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc); } const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId[Id] ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments); - return ((de.complete.docDragData.dropAction && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ? + const move = de.complete.docDragData.dropAction === "move" || de.complete.docDragData.dropAction; + return ((!move && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ? de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d) || added, false) : de.complete.docDragData.moveDocument ? movedDocs.reduce((added, d) => de.complete.docDragData?.moveDocument?.(d, undefined, addDoc) || added, false) @@ -335,7 +336,7 @@ class TreeView extends React.Component<TreeViewProps> { {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document), this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, + StrCast(this.props.document.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)} </ul >; @@ -456,7 +457,7 @@ class TreeView extends React.Component<TreeViewProps> { pinToPres={emptyFunction} onClick={this.props.onChildClick || editTitle} dropAction={this.props.dropAction} - moveDocument={this.props.moveDocument} + moveDocument={this.move} removeDocument={this.removeDoc} ScreenToLocalTransform={this.getTransform} ContentScaling={returnOne} @@ -472,7 +473,7 @@ class TreeView extends React.Component<TreeViewProps> { bringToFront={emptyFunction} dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)} ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} + ContainingCollectionDoc={this.props.containingCollection} />} </div > {this.props.treeViewHideHeaderFields() ? (null) : headerElements} @@ -486,11 +487,15 @@ class TreeView extends React.Component<TreeViewProps> { return <div className="treeViewItem-container" ref={this.createTreeDropTarget}> <li className="collection-child"> <div className="treeViewItem-header" ref={this._header} onClick={e => { - e.stopPropagation(); - e.preventDefault(); + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } }} onPointerDown={e => { - e.stopPropagation(); - e.preventDefault(); + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> {this.renderBullet} {this.renderTitle} @@ -657,7 +662,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll @computed get dataDoc() { return this.props.DataDoc || this.props.Document; } protected createTreeDropTarget = (ele: HTMLDivElement) => { - this.treedropDisposer && this.treedropDisposer(); + this.treedropDisposer?.(); if (this._mainEle = ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); } @@ -665,7 +670,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll componentWillUnmount() { super.componentWillUnmount(); - this.treedropDisposer && this.treedropDisposer(); + this.treedropDisposer?.(); } @action @@ -809,6 +814,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll Doc.SetInPlace(this.dataDoc, "title", value, false); const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) }); EditableView.loadId = doc[Id]; + Doc.SetInPlace(doc, "editTitle", true, false); this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); })} />)} {this.props.Document.allowClear ? this.renderClearButton : (null)} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index e1516b468..a00311a9c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -36,6 +36,7 @@ height: 100%; display: flex; align-items: center; + overflow: hidden; .collectionfreeformview-placeholderSpan { font-size: 32; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 196104fe0..8a05cfb0d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -867,7 +867,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu SelectionManager.SelectDoc(this, false); } }); - const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), ""); + const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), ""); cm.addItem({ description: `path: ${path}`, event: () => { this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true); diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index 947167200..76885eada 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -3,13 +3,14 @@ import { IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from '../../../new_fields/FieldSymbols'; -import { makeInterface } from "../../../new_fields/Schema"; -import { StrCast } from "../../../new_fields/Types"; +import { makeInterface, listSpec } from "../../../new_fields/Schema"; +import { StrCast, Cast } from "../../../new_fields/Types"; import { SelectionManager } from "../../util/SelectionManager"; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./QueryBox.scss"; +import { List } from "../../../new_fields/List"; type QueryDocument = makeInterface<[typeof documentSchema]>; const QueryDocument = makeInterface(documentSchema); @@ -28,7 +29,13 @@ export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryD render() { const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging"; return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} > - <SearchBox id={this.props.Document[Id]} searchQuery={StrCast(this.dataDoc.searchQuery)} filterQquery={StrCast(this.dataDoc.filterQuery)} /> + <SearchBox + id={this.props.Document[Id]} + setSearchQuery={q => this.dataDoc.searchQuery = q} + searchQuery={StrCast(this.dataDoc.searchQuery)} + setSearchFileTypes={q => this.dataDoc.searchFileTypes = new List<string>(q)} + searchFileTypes={Cast(this.dataDoc.searchFileTypes, listSpec("string"), [])} + filterQquery={StrCast(this.dataDoc.filterQuery)} /> </div >; } }
\ No newline at end of file diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index 9cf5a9c87..9b7cf2fc6 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -25,7 +25,7 @@ library.add(faGlobeAsia); library.add(faBan); export interface IconBarProps { - setIcons: (icons: string[]) => {}; + setIcons: (icons: string[]) => void; } @@ -44,7 +44,7 @@ export class IconBar extends React.Component<IconBarProps> { @action.bound updateIcon(newArray: string[]) { - this._icons = newArray; + this._icons = newArray; this.props.setIcons?.(this._icons); } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 0947bff8d..b0a49f750 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -19,13 +19,17 @@ import { FieldView } from '../nodes/FieldView'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentView } from '../nodes/DocumentView'; import { SelectionManager } from '../../util/SelectionManager'; +import { listSpec } from '../../../new_fields/Schema'; library.add(faTimes); export interface SearchProps { id: string; - searchQuery?: string; + searchQuery: string; filterQquery?: string; + setSearchQuery: (q: string) => {}; + searchFileTypes: string[]; + setSearchFileTypes: (types: string[]) => {} } export enum Keys { @@ -37,7 +41,8 @@ export enum Keys { @observer export class SearchBox extends React.Component<SearchProps> { - @observable private _searchString: string = ""; + private get _searchString() { return this.props.searchQuery; } + private set _searchString(value) { this.props.setSearchQuery(value); } @observable private _resultsOpen: boolean = false; @observable private _searchbarOpen: boolean = false; @observable private _results: [Doc, string[], string[]][] = []; @@ -72,20 +77,16 @@ export class SearchBox extends React.Component<SearchProps> { this.resultsScrolled = this.resultsScrolled.bind(this); } - componentDidMount = () => { + componentDidMount = action(() => { if (this.inputRef.current) { this.inputRef.current.focus(); - runInAction(() => this._searchbarOpen = true); + this._searchbarOpen = true; } - if (this.props.searchQuery && this.props.filterQquery) { - console.log(this.props.searchQuery); - const sq = this.props.searchQuery; - runInAction(() => { - this._searchString = sq; - this.submitSearch(); - }); + if (this.props.searchQuery) { // bcz: why was this here? } && this.props.filterQquery) { + this._searchString = this.props.searchQuery; + this.submitSearch(); } - } + }) @action @@ -133,7 +134,10 @@ export class SearchBox extends React.Component<SearchProps> { //this also serves as an indicator if the word status filter is applied @observable private _filterOpen: boolean = false; //if icons = all icons, then no icon filter is applied - @observable private _icons: string[] = this._allIcons; + get _icons() { return this.props.searchFileTypes; } + set _icons(value) { + this.props.setSearchFileTypes(value); + } //if all of these are true, no key filter is applied @observable private _titleFieldStatus: boolean = true; @observable private _authorFieldStatus: boolean = true; @@ -167,15 +171,7 @@ export class SearchBox extends React.Component<SearchProps> { } basicRequireWords(query: string): string { - const oldWords = query.split(" "); - const newWords: string[] = []; - oldWords.forEach(word => { - const newWrd = "+" + word; - newWords.push(newWrd); - }); - query = newWords.join(" "); - - return query; + return query.split(" ").join(" + ").replace(/ + /, ""); } @action @@ -214,12 +210,6 @@ export class SearchBox extends React.Component<SearchProps> { return this._icons.length === this._allIcons.length ? undefined : this._icons; } - @action.bound - updateIcon(newArray: string[]) { this._icons = newArray; } - - @action.bound - getIcons(): string[] { return this._icons; } - //TODO: basically all of this //gets all of the collections of all the docviews that are selected //if a collection is the only thing selected, search only in that collection (not its container) @@ -316,10 +306,13 @@ export class SearchBox extends React.Component<SearchProps> { private get filterQuery() { const types = this.filterTypes; - const includeDeleted = this.getDataStatus() ? "" : " AND NOT deleted_b:true"; - const includeIcons = this.getDataStatus() ? "" : " AND NOT type_t:fonticonbox"; + const baseExpr = "NOT baseProto_b:true"; + const includeDeleted = this.getDataStatus() ? "" : " NOT deleted_b:true"; + const includeIcons = this.getDataStatus() ? "" : " NOT type_t:fonticonbox"; + const typeExpr = !types ? "" : ` (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})`; // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello - return "NOT baseProto_b:true" + includeDeleted + includeIcons + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : ""); + const query = [baseExpr, includeDeleted, includeIcons, typeExpr].join(" AND ").replace(/AND $/, ""); + return query; } getDataStatus() { return this._deletedDocsStatus; } @@ -653,7 +646,9 @@ export class SearchBox extends React.Component<SearchProps> { <button className="filter-item" style={this._nodeStatus ? { background: "#aaaaa3" } : {}} onClick={this.handleNodeChange}>Nodes</button> </div> <div id={`node${this.props.id}`} className="filter-body" style={this._nodeStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}> - <IconBar setIcons={(icons: string[]) => this._icons = icons} /> + <IconBar setIcons={(icons: string[]) => { + this._icons = icons; + }} /> </div> <div className="filter-key" id={`key${this.props.id}`} style={this._keyStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}> <div className="filter-keybar"> diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts index 5f7d1cf6d..753c31fcf 100644 --- a/src/server/ApiManagers/SearchManager.ts +++ b/src/server/ApiManagers/SearchManager.ts @@ -1,18 +1,14 @@ -import ApiManager, { Registration } from "./ApiManager"; -import { Method } from "../RouteManager"; -import { Search } from "../Search"; -const findInFiles = require('find-in-files'); +import { exec } from "child_process"; +import { cyan, green, red, yellow } from "colors"; import * as path from 'path'; -import { pathToDirectory, Directory } from "./UploadManager"; -import { red, cyan, yellow, green } from "colors"; -import RouteSubscriber from "../RouteSubscriber"; -import { exec, execSync } from "child_process"; -import { onWindows } from ".."; -import { get } from "request-promise"; import { log_execution } from "../ActionUtilities"; import { Database } from "../database"; -import rimraf = require("rimraf"); -import { mkdirSync, chmod, chmodSync } from "fs"; +import { Method } from "../RouteManager"; +import RouteSubscriber from "../RouteSubscriber"; +import { Search } from "../Search"; +import ApiManager, { Registration } from "./ApiManager"; +import { Directory, pathToDirectory } from "./UploadManager"; +const findInFiles = require('find-in-files'); export class SearchManager extends ApiManager { @@ -48,8 +44,10 @@ export class SearchManager extends ApiManager { res.send([]); return; } - const results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, pathToDirectory(Directory.text), ".txt$"); const resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] }; + let results: any; + const dir = pathToDirectory(Directory.text); + results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, dir, ".txt$"); for (const result in results) { resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, "")); resObj.lines.push(results[result].line); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 85938e026..3955f64fc 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -141,7 +141,7 @@ export class CurrentUserUtils { if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, - onClick: ScriptField.MakeScript("deiconifyView(this)") + onClick: ScriptField.MakeScript("deiconifyView(self)") }); Doc.GetProto(iconView).icon = 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":[]}', ""); iconView.isTemplateDoc = makeTemplate(iconView); @@ -397,7 +397,7 @@ export class CurrentUserUtils { _width: 50, _height: 25, title: "Library", fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { - title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true + title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true })) as any as Doc, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") @@ -413,6 +413,7 @@ export class CurrentUserUtils { _width: 50, _height: 25, title: "Search", fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc, + searchFileTypes: new List<string>([DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.VID, DocumentType.WEB, DocumentType.SCRIPTING]), targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, lockedPosition: true, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel") |