/// <summary> /// Size the SVG image to completely fill its container. Call this when the view /// actually changes sizes (e.g. on a window resize/device orientation change). /// See Blockly.resizeSvgContents to resize the workspace when the contents /// change (e.g. when a block is added or removed). /// Record the height/width of the SVG image. /// </summary> /// <param name="workspace">Any workspace in the SVG.</param> public static void svgResize(WorkspaceSvg workspace) { var mainWorkspace = workspace; while (mainWorkspace.options.parentWorkspace != null) { mainWorkspace = (WorkspaceSvg)mainWorkspace.options.parentWorkspace; } var svg = mainWorkspace.getParentSvg(); var div = (HTMLDivElement)svg.ParentNode; if (div == null) { // Workspace deleted, or something. return; } var width = div.OffsetWidth; var height = div.OffsetHeight; if ((double?)svg["cachedWidth_"] != width) { svg.SetAttribute("width", width + "px"); svg["cachedWidth_"] = width; } if ((double?)svg["cachedHeight_"] != height) { svg.SetAttribute("height", height + "px"); svg["cachedHeight_"] = height; } mainWorkspace.resize(); }
/// <summary> /// Bind the localStorage backup function to the unload event. /// </summary> /// <param name="opt_workspace">Workspace.</param> public static void backupOnUnload(WorkspaceSvg opt_workspace = null) { var workspace = opt_workspace ?? Blockly.Core.getMainWorkspace(); Window.AddEventListener("unload", new Action(() => { BlocklyStorage.backupBlocks_(workspace); }), false); }
/// <summary> /// Return the absolute coordinates of the top-left corner of this element, /// scales that after canvas SVG element, if it's a descendant. /// The origin (0,0) is the top-left corner of the Blockly SVG. /// </summary> /// <param name="element">Element to find the coordinates of.</param> /// <param name="workspace">Element must be in this workspace.</param> /// <returns>Object with .x and .y properties.</returns> internal static goog.math.Coordinate getSvgXY_(SVGElement element, WorkspaceSvg workspace) { var x = 0.0; var y = 0.0; var scale = 1.0; if (goog.dom.contains(workspace.getCanvas(), element) || goog.dom.contains(workspace.getBubbleCanvas(), element)) { // Before the SVG canvas, scale the coordinates. scale = workspace.scale; } do { // Loop through this block and every parent. var xy = Core.getRelativeXY_(element); if (element == workspace.getCanvas() || element == workspace.getBubbleCanvas()) { // After the SVG canvas, don't scale the coordinates. scale = 1; } x += xy.x * scale; y += xy.y * scale; element = element.ParentNode as SVGElement; } while (element != null && element != workspace.getParentSvg()); return(new goog.math.Coordinate(x, y)); }
/// <summary> /// Initialize Blockly with various handlers. /// </summary> /// <param name="mainWorkspace">Newly created main workspace.</param> private static void init_(WorkspaceSvg mainWorkspace) { var options = mainWorkspace.options; var svg = mainWorkspace.getParentSvg(); // Supress the browser's context menu. Core.bindEventWithChecks_(svg, "contextmenu", null, new Action <Event>((e) => { if (!Core.isTargetInput_(e)) { e.PreventDefault(); } })); var workspaceResizeHandler = Core.bindEventWithChecks_(new Node(Window.Instance.instance), "resize", null, new Action <Event>((e) => { Core.hideChaff(true); Core.svgResize(mainWorkspace); })); mainWorkspace.setResizeHandlerWrapper(workspaceResizeHandler); Core.inject_bindDocumentEvents_(); if (options.languageTree != null) { if (mainWorkspace.toolbox_ != null) { mainWorkspace.toolbox_.init(/*mainWorkspace*/); } else if (mainWorkspace.flyout_ != null) { // Build a fixed flyout with the root blocks. mainWorkspace.flyout_.init(mainWorkspace); mainWorkspace.flyout_.show(options.languageTree.ChildNodes); mainWorkspace.flyout_.scrollToStart(); // Translate the workspace sideways to avoid the fixed flyout. mainWorkspace.scrollX = mainWorkspace.flyout_.width_; if (options.toolboxPosition == Core.TOOLBOX_AT_RIGHT) { mainWorkspace.scrollX *= -1; } mainWorkspace.translate(mainWorkspace.scrollX, 0); } } if (options.hasScrollbars) { mainWorkspace.scrollbar = new ScrollbarPair(mainWorkspace); mainWorkspace.scrollbar.resize(); } // Load the sounds. if (options.hasSounds) { Core.inject_loadSounds_(options.pathToMedia, mainWorkspace); } }
/// <summary> /// Show the inline free-text editor on top of the text. /// </summary> /// <param name="opt_quietInput">True if editor should be created without /// focus.Defaults to false.</param> public override void showEditor_(bool opt_quietInput) { this.workspace_ = (WorkspaceSvg)this.sourceBlock_.workspace; var quietInput = opt_quietInput || false; if (!quietInput && (goog.userAgent.MOBILE || goog.userAgent.ANDROID || goog.userAgent.IPAD)) { // Mobile browsers have issues with in-line textareas (focus & keyboards). var newValue = Window.Prompt(Msg.CHANGE_VALUE_TITLE, this.text_); if (this.sourceBlock_ != null) { newValue = this.callValidator(newValue); } this.setValue(newValue); return; } WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_()); var div = WidgetDiv.DIV; // Create the input. var htmlInput = (HTMLInputElement) goog.dom.createDom(goog.dom.TagName.INPUT, "blocklyHtmlInput"); htmlInput.SetAttribute("spellcheck", this.spellcheck_.ToString()); var fontSize = (FieldTextInput.FONTSIZE * this.workspace_.scale) + "pt"; div.Style.FontSize = fontSize; htmlInput.Style.FontSize = fontSize; /** @type {!HTMLInputElement} */ FieldTextInput.htmlInput_ = htmlInput; div.AppendChild(htmlInput); htmlInput.Value = htmlInput.DefaultValue = this.text_; htmlInput["oldValue_"] = null; this.validate_(); this.resizeEditor_(); if (!quietInput) { htmlInput.Focus(); htmlInput.Select(); } // Bind to keydown -- trap Enter without IME and Esc to hide. htmlInput["onKeyDownWrapper_"] = Core.bindEventWithChecks_(htmlInput, "keydown", this, new Action <KeyboardEvent>(this.onHtmlInputKeyDown_)); // Bind to keyup -- trap Enter; resize after every keystroke. htmlInput["onKeyUpWrapper_"] = Core.bindEventWithChecks_(htmlInput, "keyup", this, new Action <Event>(this.onHtmlInputChange_)); // Bind to keyPress -- repeatedly resize when holding down a key. htmlInput["onKeyPressWrapper_"] = Core.bindEventWithChecks_(htmlInput, "keypress", this, new Action <Event>(this.onHtmlInputChange_)); htmlInput["onWorkspaceChangeWrapper_"] = new Action <Events.Abstract>(this.resizeEditor_); this.workspace_.addChangeListener((Action <Events.Abstract>)htmlInput["onWorkspaceChangeWrapper_"]); }
/// <summary> /// Dispose of this toolbox. /// </summary> public void dispose() { this.flyout_.dispose(); this.tree_.dispose(); goog.dom.removeNode(this.HtmlDiv); this.workspace_ = null; this.lastCategory_ = null; }
/// <summary> /// Save blocks to database and return a link containing key to XML. /// </summary> /// <param name="opt_workspace">Workspace.</param> public static void link(WorkspaceSvg opt_workspace = null) { var workspace = opt_workspace ?? Blockly.Core.getMainWorkspace(); var xml = Blockly.Xml.workspaceToDom(workspace); var data = Blockly.Xml.domToText(xml); BlocklyStorage.makeRequest_("/storage", "xml", data, workspace); }
public void dispose() { if (this.svgGroup_ != null) { goog.dom.removeNode(this.svgGroup_); this.svgGroup_ = null; } this.workspace_ = null; }
/// <summary> /// Class for a button in the flyout. /// </summary> /// <param name="workspace">The workspace in which to place this /// button.</param> /// <param name="targetWorkspace">The flyout's target workspace.</param> /// <param name="text">The text to display on the button.</param> public FlyoutButton(WorkspaceSvg workspace, WorkspaceSvg targetWorkspace, string text, string d = null, bool label = false) { this.workspace_ = workspace; this.targetWorkspace_ = targetWorkspace; this.text_ = text; position_ = new goog.math.Coordinate(0.0, 0.0); this.callback_ = Core.flyoutButtonCallbacks_[d]; this.isLabel_ = label; }
/// <summary> /// Backup code blocks to localStorage. /// </summary> /// <param name="workspace">Workspace.</param> private static void backupBlocks_(WorkspaceSvg workspace) { if (Window.LocalStorage != null) { var xml = Blockly.Xml.workspaceToDom(workspace); // Gets the current URL, not including the hash. var url = Window.Location.Href.Split('#')[0]; Window.LocalStorage.SetItem(url, Blockly.Xml.domToText(xml)); } }
/// <summary> /// Dispose of this pair of scrollbars. /// Unlink from all DOM elements to prevent memory leaks. /// </summary> public void dispose() { goog.dom.removeNode(this.corner_); this.corner_ = null; this.workspace_ = null; this.oldHostMetrics_ = null; this.hScroll.dispose(); this.hScroll = null; this.vScroll.dispose(); this.vScroll = null; }
/// <summary> /// Initialize a set of connection DBs for a specified workspace. /// </summary> /// <param name="workspace">The workspace this DB is for.</param> public static void init(WorkspaceSvg workspace) { // Create four databases, one for each connection type. var dbList = new ConnectionDB[5]; dbList[Core.INPUT_VALUE] = new ConnectionDB(); dbList[Core.OUTPUT_VALUE] = new ConnectionDB(); dbList[Core.NEXT_STATEMENT] = new ConnectionDB(); dbList[Core.PREVIOUS_STATEMENT] = new ConnectionDB(); workspace.connectionDBList = dbList; }
/// <summary> /// Restore code blocks from localStorage. /// </summary> /// <param name="opt_workspace">Workspace.</param> public static void restoreBlocks(WorkspaceSvg opt_workspace = null) { var url = Window.Location.Href.Split('#')[0]; if (Window.LocalStorage != null && Window.LocalStorage[url] != null) { var workspace = opt_workspace ?? Blockly.Core.getMainWorkspace(); var xml = Blockly.Xml.textToDom((string)Window.LocalStorage[url]); Blockly.Xml.domToWorkspace(xml, workspace); } }
/// <summary> /// Dispose of this trash can. /// Unlink from all DOM elements to prevent memory leaks. /// </summary> public void dispose() { if (this.svgGroup_ != null) { goog.dom.removeNode(this.svgGroup_); this.svgGroup_ = null; } this.svgLid_ = null; this.workspace_ = null; goog.Timer.clear(this.lidTask_); }
/// <summary> /// Dispose of this bubble. /// </summary> public void dispose() { Bubble.unbindDragEvents_(); // Dispose of and unlink the bubble. goog.dom.removeNode(this.bubbleGroup_); this.bubbleGroup_ = null; this.bubbleArrow_ = null; this.bubbleBack_ = null; this.resizeGroup_ = null; this.workspace_ = null; this.content_ = null; this.shape_ = null; }
public ScrollbarPair(WorkspaceSvg workspace) { this.workspace_ = workspace; this.hScroll = new Scrollbar(workspace, true, true); this.vScroll = new Scrollbar(workspace, false, true); this.corner_ = Core.createSvgElement("rect", new Dictionary <string, object>() { { "height", Scrollbar.scrollbarThickness }, { "width", Scrollbar.scrollbarThickness }, { "class", "blocklyScrollbarBackground" } }, null); Scrollbar.insertAfter_(this.corner_, workspace.getBubbleCanvas()); }
/// <summary> /// Dispose of this scrollbar. /// Unlink from all DOM elements to prevent memory leaks. /// </summary> public void dispose() { this.cleanUp_(); Core.unbindEvent_(this.onMouseDownBarWrapper_); this.onMouseDownBarWrapper_ = null; Core.unbindEvent_(this.onMouseDownHandleWrapper_); this.onMouseDownHandleWrapper_ = null; goog.dom.removeNode(this.svgGroup_); this.svgGroup_ = null; this.svgBackground_ = null; this.svgHandle_ = null; this.workspace_ = null; }
/// <summary> /// Duplicate this block and its children. /// </summary> /// <param name="block">Block to be copied.</param> internal static void duplicate_(Block block) { // Save the clipboard. var clipboardXml = Core.clipboardXml_; var clipboardSource = Core.clipboardSource_; // Create a duplicate via a copy/paste operation. Core.copy_(block); ((WorkspaceSvg)block.workspace).paste(Core.clipboardXml_); // Restore the clipboard. Core.clipboardXml_ = clipboardXml; Core.clipboardSource_ = clipboardSource; }
/// <summary> /// Load blocks from XML. /// </summary> /// <param name="xml">Text representation of XML.</param> /// <param name="workspace">Workspace.</param> private static void loadXml_(string xmlStr, WorkspaceSvg workspace) { Element xml; try { xml = Blockly.Xml.textToDom(xmlStr); } catch (Exception) { BlocklyStorage.alert(BlocklyStorage.XML_ERROR + "\nXML: " + xmlStr); return; } // Clear the workspace to avoid merge. workspace.clear(); Blockly.Xml.domToWorkspace(xml, workspace); }
/// <summary> /// Class for a WorkspaceFactoryGenerator /// </summary> public WorkspaceFactoryGenerator(WorkspaceFactoryModel model) { // Model to share information about categories and shadow blocks. this.model = model; // Create hidden workspace to load saved XML to generate toolbox XML. var hiddenBlocks = Document.CreateElement <HTMLDivElement>("div"); // Generate a globally unique ID for the hidden div element to avoid // collisions. var hiddenBlocksId = Blockly.Core.genUid(); hiddenBlocks.Id = hiddenBlocksId; hiddenBlocks.Style.Display = Display.None; Document.Body.AppendChild(hiddenBlocks); this.hiddenWorkspace = Blockly.Core.inject(hiddenBlocksId); }
/// <summary> /// Copy a block onto the local clipboard. /// </summary> /// <param name="block">Block to be copied.</param> private static void copy_(Block block) { var xmlBlock = Xml.blockToDom(block); if (Core.dragMode_ != Core.DRAG_FREE) { Xml.deleteNext(xmlBlock); } // Encode start position in XML. var xy = block.getRelativeToSurfaceXY(); xmlBlock.SetAttribute("x", (block.RTL ? -xy.x : xy.x).ToString()); xmlBlock.SetAttribute("y", xy.y.ToString()); Core.clipboardXml_ = xmlBlock; Core.clipboardSource_ = (WorkspaceSvg)block.workspace; }
/// <summary> /// Start monitoring the workspace. If a change is made that changes the XML, /// clear the key from the URL. Stop monitoring the workspace once such a /// change is detected. /// </summary> /// <param name="workspace">Workspace.</param> private static void monitorChanges_(WorkspaceSvg workspace) { var startXmlDom = Blockly.Xml.workspaceToDom(workspace); var startXmlText = Blockly.Xml.domToText(startXmlDom); Action <Events.Abstract> bindData = null; var change = new Action <Events.Abstract>((e) => { var xmlDom = Blockly.Xml.workspaceToDom(workspace); var xmlText = Blockly.Xml.domToText(xmlDom); if (startXmlText != xmlText) { Window.Location.Hash = ""; workspace.removeChangeListener(bindData); } }); bindData = workspace.addChangeListener(change); }
/// <summary> /// Fire a new AJAX request. /// </summary> /// <param name="url">URL to fetch.</param> /// <param name="name">Name of parameter.</param> /// <param name="content">Content of parameter.</param> /// <param name="workspace">Workspace.</param> private static void makeRequest_(string url, string name, string content, WorkspaceSvg workspace) { if (BlocklyStorage.httpRequest_ != null) { // AJAX call is in-flight. BlocklyStorage.httpRequest_.Abort(); } BlocklyStorage.httpRequest_ = new XMLHttpRequest(); BlocklyStorage.httpRequest_.Name = name; BlocklyStorage.httpRequest_.OnReadyStateChange = new Action(BlocklyStorage.handleRequest_); BlocklyStorage.httpRequest_.Open("POST", url); BlocklyStorage.httpRequest_.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded"); BlocklyStorage.httpRequest_.Send(name + "=" + Script.EncodeURIComponent(content)); BlocklyStorage.httpRequest_["workspace"] = workspace; }
/// <summary> /// Class for UI bubble. /// </summary> /// <param name="workspace">The workspace on which to draw the /// bubble.</param> /// <param name="content">SVG content for the bubble.</param> /// <param name="shape">SVG element to avoid eclipsing.</param> /// <param name="anchorXY">Absolute position of bubble's anchor /// point.</param> /// <param name="bubbleWidth">Width of bubble, or null if not resizable.</param> /// <param name="bubbleHeight">Height of bubble, or null if not resizable.</param> public Bubble(WorkspaceSvg workspace, SVGElement content, SVGElement shape, goog.math.Coordinate anchorXY, double bubbleWidth, double bubbleHeight) { this.workspace_ = workspace; this.content_ = content; this.shape_ = shape; var angle = Bubble.ARROW_ANGLE; if (this.workspace_.RTL) { angle = -angle; } this.arrow_radians_ = goog.math.toRadians(angle); var canvas = workspace.getBubbleCanvas(); canvas.AppendChild(this.createDom_(content, !(bubbleWidth != 0.0 && bubbleHeight != 0.0))); this.setAnchorLocation(anchorXY); if (bubbleWidth == 0.0 || bubbleHeight == 0.0) { var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox(); bubbleWidth = bBox.width + 2 * Bubble.BORDER_WIDTH; bubbleHeight = bBox.height + 2 * Bubble.BORDER_WIDTH; } this.setBubbleSize(bubbleWidth, bubbleHeight); // Render the bubble. this.positionBubble_(); this.renderArrow_(); this.rendered_ = true; if (!workspace.options.readOnly) { Core.bindEventWithChecks_(this.bubbleBack_, "mousedown", this, new Action <MouseEvent>(this.bubbleMouseDown_)); if (this.resizeGroup_ != null) { Core.bindEventWithChecks_(this.resizeGroup_, "mousedown", this, new Action <MouseEvent>(this.resizeMouseDown_)); } } }
public Scrollbar(WorkspaceSvg workspace, bool horizontal, bool opt_pair = false) { this.workspace_ = workspace; this.pair_ = opt_pair; this.horizontal_ = horizontal; this.oldHostMetrics_ = null; this.createDom_(); this.position_ = new goog.math.Coordinate(0, 0); if (horizontal) { this.svgBackground_.SetAttribute("height", Scrollbar.scrollbarThickness.ToString()); this.svgHandle_.SetAttribute("height", (Scrollbar.scrollbarThickness - 5).ToString()); this.svgHandle_.SetAttribute("y", 2.5.ToString()); this.lengthAttribute_ = "width"; this.positionAttribute_ = "x"; } else { this.svgBackground_.SetAttribute("width", Scrollbar.scrollbarThickness.ToString()); this.svgHandle_.SetAttribute("width", (Scrollbar.scrollbarThickness - 5).ToString()); this.svgHandle_.SetAttribute("x", 2.5.ToString()); this.lengthAttribute_ = "height"; this.positionAttribute_ = "y"; } var scrollbar = this; this.onMouseDownBarWrapper_ = Core.bindEventWithChecks_( this.svgBackground_, "mousedown", scrollbar, new Action <MouseEvent>(scrollbar.onMouseDownBar_)); this.onMouseDownHandleWrapper_ = Core.bindEventWithChecks_(this.svgHandle_, "mousedown", scrollbar, new Action <MouseEvent>(scrollbar.onMouseDownHandle_)); }
/// <summary> /// Load sounds for the given workspace. /// </summary> /// <param name="pathToMedia">The path to the media directory.</param> /// <param name="workspace">The workspace to load sounds for.</param> private static void inject_loadSounds_(string pathToMedia, WorkspaceSvg workspace) { workspace.loadAudio_(new string[] { pathToMedia + "click.mp3", pathToMedia + "click.wav", pathToMedia + "click.ogg" }, "click"); workspace.loadAudio_(new string[] { pathToMedia + "disconnect.wav", pathToMedia + "disconnect.mp3", pathToMedia + "disconnect.ogg" }, "disconnect"); workspace.loadAudio_(new string[] { pathToMedia + "delete.mp3", pathToMedia + "delete.ogg", pathToMedia + "delete.wav" }, "delete"); // Bind temporary hooks that preload the sounds. var soundBinds = new JsArray <JsArray <EventWrapInfo> >(); var unbindSounds = new Action <Event>((e) => { while (soundBinds.Length != 0) { Core.unbindEvent_((JsArray <EventWrapInfo>)soundBinds.Pop()); } workspace.preloadAudio_(); }); // These are bound on mouse/touch events with Blockly.bindEventWithChecks_, so // they restrict the touch identifier that will be recognized. But this is // really something that happens on a click, not a drag, so that's not // necessary. // Android ignores any sound not loaded as a result of a user action. soundBinds.Push( Core.bindEventWithChecks_(Document.Instance, "mousemove", null, unbindSounds, true)); soundBinds.Push( Core.bindEventWithChecks_(Document.Instance, "touchstart", null, unbindSounds, true)); }
/// <summary> /// Class for a Toolbox. /// Creates the toolbox's DOM. /// </summary> /// <param name="workspaceSvg">The workspace in which to create new</param> public Toolbox(WorkspaceSvg workspace) { this.workspace_ = workspace; this.RTL = workspace.options.RTL; this.horizontalLayout_ = workspace.options.horizontalLayout; this.toolboxPosition = workspace.options.toolboxPosition; this.config_ = new Config { indentWidth = 19, cssRoot = "blocklyTreeRoot", cssHideRoot = "blocklyHidden", cssItem = "", cssTreeRow = "blocklyTreeRow", cssItemLabel = "blocklyTreeLabel", cssTreeIcon = "blocklyTreeIcon", cssExpandedFolderIcon = "blocklyTreeIconOpen", cssFileIcon = "blocklyTreeIconNone", cssSelectedRow = "blocklyTreeSelected" }; this.treeSeparatorConfig_ = new Config { cssTreeRow = "blocklyTreeSeparator" }; if (this.horizontalLayout_) { this.config_.cssTreeRow = this.config_.cssTreeRow + (workspace.RTL ? " blocklyHorizontalTreeRtl" : " blocklyHorizontalTree"); this.treeSeparatorConfig_.cssTreeRow = "blocklyTreeSeparatorHorizontal " + (workspace.RTL ? "blocklyHorizontalTreeRtl" : "blocklyHorizontalTree"); this.config_.cssTreeIcon = ""; } }
/// <summary> /// Class for a zoom controls. /// </summary> /// <param name="workspace">The workspace to sit in.</param> public ZoomControls(WorkspaceSvg workspace) { this.workspace_ = workspace; }
/// <summary> /// Size the workspace when the contents change. This also updates /// scrollbars accordingly. /// </summary> /// <param name="workspace"></param> public static void resizeSvgContents(WorkspaceSvg workspace) { workspace.resizeContents(); }
/// <summary> /// Create a main workspace and add it to the SVG. /// </summary> /// <param name="svg">SVG element with pattern defined.</param> /// <param name="options">Dictionary of options.</param> /// <returns>Newly created main workspace.</returns> private static WorkspaceSvg createMainWorkspace_(SVGElement svg, Options options) { options.parentWorkspace = null; var mainWorkspace = new WorkspaceSvg(options); mainWorkspace.scale = options.zoomOptions.startScale; svg.AppendChild(mainWorkspace.createDom("blocklyMainBackground")); // A null translation will also apply the correct initial scale. mainWorkspace.translate(0, 0); mainWorkspace.markFocused(null); if (!options.readOnly && !options.hasScrollbars) { var workspaceChanged = new Action <Events.Abstract>((e) => { if (Core.dragMode_ == Core.DRAG_NONE) { var metrics = mainWorkspace.getMetrics(); var edgeLeft = metrics.viewLeft + metrics.absoluteLeft; var edgeTop = metrics.viewTop + metrics.absoluteTop; if (metrics.contentTop < edgeTop || metrics.contentTop + metrics.contentHeight > metrics.viewHeight + edgeTop || metrics.contentLeft < (options.RTL ? metrics.viewLeft : edgeLeft) || metrics.contentLeft + metrics.contentWidth > (options.RTL ? metrics.viewWidth : metrics.viewWidth + edgeLeft)) { // One or more blocks may be out of bounds. Bump them back in. var MARGIN = 25; var blocks = mainWorkspace.getTopBlocks(false); foreach (var block in blocks) { var blockXY = block.getRelativeToSurfaceXY(); var blockHW = ((BlockSvg)block).getHeightWidth(); // Bump any block that's above the top back inside. var overflowTop = edgeTop + MARGIN - blockHW.height - blockXY.y; if (overflowTop > 0) { block.moveBy(0, overflowTop); } // Bump any block that's below the bottom back inside. var overflowBottom = edgeTop + metrics.viewHeight - MARGIN - blockXY.y; if (overflowBottom < 0) { block.moveBy(0, overflowBottom); } // Bump any block that's off the left back inside. var overflowLeft = MARGIN + edgeLeft - blockXY.x - (options.RTL ? 0 : blockHW.width); if (overflowLeft > 0) { block.moveBy(overflowLeft, 0); } // Bump any block that's off the right back inside. var overflowRight = edgeLeft + metrics.viewWidth - MARGIN - blockXY.x + (options.RTL ? blockHW.width : 0); if (overflowRight < 0) { block.moveBy(overflowRight, 0); } } } } }); mainWorkspace.addChangeListener(workspaceChanged); } // The SVG is now fully assembled. Core.svgResize(mainWorkspace); WidgetDiv.createDom(); Tooltip.createDom(); return(mainWorkspace); }