/// <summary> /// Parse the provided toolbox tree into a consistent DOM format. /// </summary> /// <param name="tree_">DOM tree of blocks, or text representation of same.</param> /// <returns>tree of blocks, or null.</returns> public static Element parseToolboxTree(Union <string, Element> tree_) { Element tree = tree_.As <Element>(); if (tree_ != null) { if (!tree_.Is <string>()) { if (false /*typeof XSLTProcessor == 'undefined' && tree.outerHTML*/) { // In this case the tree will not have been properly built by the // browser. The HTML will be contained in the element, but it will // not have the proper DOM structure since the browser doesn't support // XSLTProcessor (XML -> HTML). This is the case in IE 9+. tree_ = tree_.As <Element>().OuterHTML; } else if (!(tree_.Is <Element>())) { tree = null; } } if (tree_.Is <string>()) { tree = Xml.textToDom(tree_.As <string>()); } } else { tree = null; } return(tree); }
/// <summary> /// Dispatches the given event on the ancestorsTree. /// </summary> /// <param name="target">he target to dispatch on.</param> /// <param name="e_">The event object.</param> /// <param name="opt_ancestorsTree">The ancestors /// tree of the target, in reverse order from the closest ancestor /// to the root event target. May be null if the target has no ancestor.</param> /// <returns>If anyone called preventDefault on the event object (or /// if any of the listeners returns false) this will also return false.</returns> private static bool dispatchEventInternal_(object target, Union <string, Event> e_, JsArray <EventTarget> opt_ancestorsTree = null) { var type = e_.Is <Event>() ? e_.As <Event>().type : e_.As <string>(); Event e; // If accepting a string or object, create a custom event object so that // preventDefault and stopPropagation work with the event. if (e_.Is <string>()) { e = new Event(e_.As <string>(), target); } else { e = e_.As <Event>(); e.target = e.target ?? target; } var rv = true; EventTarget currentTarget; // Executes all capture listeners on the ancestors, if any. if (opt_ancestorsTree != null) { for (var i = opt_ancestorsTree.Length - 1; !e.propagationStopped_ && i >= 0; i--) { e.currentTarget = currentTarget = opt_ancestorsTree[i]; rv = currentTarget.fireListeners(type, true, e) && rv; } } // Executes capture and bubble listeners on the target. if (!e.propagationStopped_) { e.currentTarget = currentTarget = (EventTarget)target; rv = currentTarget.fireListeners(type, true, e) && rv; if (!e.propagationStopped_) { rv = currentTarget.fireListeners(type, false, e) && rv; } } // Executes all bubble listeners on the ancestors, if any. if (opt_ancestorsTree != null) { for (var i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.Length; i++) { e.currentTarget = currentTarget = opt_ancestorsTree[i]; rv = currentTarget.fireListeners(type, false, e) && rv; } } return(rv); }
public static string ToHtmlValue(this Union <string, int, float> value) { if (value.Is <string>()) { return(value.As <string>()); } else if (value.Is <int>()) { return(value.As <int>().ToPx()); } else { return(value.As <float>().ToPx()); } }
/// <summary> /// Change a connection's compatibility. /// </summary> /// <param name="check">Compatible value type or list of value types. /// Null if all types are compatible.</param> /// <returns>The connection being modified /// (to allow chaining).</returns> public Connection setCheck(Union <string, string[]> check) { if (check != null) { // Ensure that check is in an array. if (check.Is <string>()) { this.check_ = new string[] { check.As <string>() }; } else { this.check_ = check.As <string[]>(); } // The new value type may not be compatible with the existing connection. if (this.isConnected() && !this.checkType_(this.targetConnection)) { var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_; child.unplug(); // Bump away. this.sourceBlock_.bumpNeighbours_(); } } else { this.check_ = null; } return(this); }
public EventHandler unlisten(EventTarget src, Union <string, JsArray <string> > type, Delegate opt_fn = null, bool opt_capture = false, object opt_scope = null) { var self = this; if (type.Is <JsArray <string> >()) { foreach (var i in type.As <JsArray <string> >()) { self.unlisten(src, i, opt_fn, opt_capture, opt_scope); } } else { var listener = goog.events.getListener( src, type.As <string>(), opt_fn ?? new Action <Event>(self.handleEvent), opt_capture, opt_scope ?? self.handler_ ?? (object)self); if (listener != null) { goog.events.unlistenByKey(listener); self.keys_.Remove(listener.GetHashCode()); } } return(self); }
/// <summary> /// Set the maximum, minimum and precision constraints on this field. /// Any of these properties may be undefiend or NaN to be disabled. /// Setting precision (usually a power of 10) enforces a minimum step between /// values. That is, the user's value will rounded to the closest multiple of /// precision. The least significant digit place is inferred from the precision. /// Integers values can be enforces by choosing an integer precision. /// </summary> /// <param name="min">Minimum value.</param> /// <param name="max">Maximum value.</param> /// <param name="precision">Precision for value.</param> public void setConstraints(Union <string, double> _min, Union <string, double> _max, Union <string, double> _precision) { var precision = _precision.Is <string>() ? Script.ParseFloat(_precision.As <string>()) : (double)_precision; this.precision_ = Double.IsNaN(precision) ? 0 : precision; double min, max; if (_min.Is <double>()) { min = _min.As <double>(); } else if (!Double.TryParse(_min.As <string>(), out min)) { min = Double.NaN; } this.min_ = Double.IsNaN(min) ? Double.NegativeInfinity : min; if (_max.Is <double>()) { max = _max.As <double>(); } else if (!Double.TryParse(_max.As <string>(), out max)) { max = Double.NaN; } this.max_ = Double.IsNaN(max) ? Double.PositiveInfinity : max; this.setValue(this.callValidator(this.getValue())); }
/// <summary> /// Inject a Blockly editor into the specified container element (usually a div). /// </summary> /// <param name="container">Containing element, or its ID, or a CSS selector.</param> /// <param name="opt_options">opt_options Optional dictionary of options.</param> /// <returns>Newly created main workspace.</returns> public static WorkspaceSvg inject(Union <string, Element> container_, Dictionary <string, object> opt_options = null) { Element container; if (container_.Is <string>()) { container = Document.GetElementById(container_.As <string>()) ?? Document.QuerySelector(container_.As <string>()); } else { container = container_.As <Element>(); } // Verify that the container is in document. if (!goog.dom.contains(Document.Instance, container)) { throw new Exception("Error: container is not in current document."); } var options = new Options(opt_options ?? new Dictionary <string, object>()); var subContainer = goog.dom.createDom("div", "injectionDiv"); container.AppendChild(subContainer); var svg = Core.createDom_(subContainer, options); var workspace = Core.createMainWorkspace_(svg, options); Core.init_(workspace); workspace.markFocused(null); Core.bindEventWithChecks_(svg, "focus", workspace, new Action <Event>(workspace.markFocused)); Core.svgResize(workspace); return(workspace); }
public static Listener listenOnce(EventTarget src, Union <string, JsArray <string> > type_, Delegate listener, bool opt_capt = false, object opt_handler = null) { if (type_.Is <JsArray <string> >()) { var type = type_.As <JsArray <string> >(); for (var i = 0; i < type.Length; i++) { goog.events.listenOnce(src, type[i], listener, opt_capt, opt_handler); } return(null); } else { var type = type_.As <string>(); listener = goog.events.wrapListener(listener); if (src.isImplementedBy()) { return(src.listenOnce(type, listener, opt_capt, opt_handler)); } else { //return goog.events.listen_(src, type, listener, // /* callOnce */ true, opt_capt, opt_handler); throw new NotImplementedException(); } } }
/// <summary> /// Find all user-created variables that are in use in the workspace. /// For use by generators. /// </summary> /// <param name="root">Root block or workspace.</param> /// <returns>Array of variable names.</returns> public string[] allUsedVariables(Union <Block, Workspace> root) { Block[] blocks; if (root.Is <Block>()) { // Root is Block. blocks = root.As <Block>().getDescendants(); } else if (root.Is <Workspace>()) { // Root is Workspace. blocks = root.As <Workspace>().getAllBlocks(); } else { throw new Exception("Not Block or Workspace: " + root); } var variableHash = new Dictionary <string, string>(); // Iterate through every block and add each variable to the hash. for (var x = 0; x < blocks.Length; x++) { var blockVariables = blocks[x].getVars(); if (blockVariables != null) { for (var y = 0; y < blockVariables.Length; y++) { var varName = blockVariables[y]; // Variable name may be null if the block is only half-built. if (varName != null) { variableHash[varName.ToLower()] = varName; } } } } // Flatten the hash into a list. var variableList = new JsArray <string>(); foreach (var name in variableHash.Keys) { variableList.Push(variableHash[name]); } return(variableList); }
/// <summary> /// Used to bind a click to a certain DOM element (used for category tabs). /// Taken directly from code.js /// </summary> /// <param name="e1">Tab element or corresponding ID string.</param> /// <param name="func"> Function to be executed on click.</param> public void bindClick(Union <string, Element> el, Delegate func) { if (el.Is <string>()) { el = Document.GetElementById(el.As <string>()); } el.As <Element>().AddEventListener("click", func, true); el.As <Element>().AddEventListener("touchend", func, true); }
/// <summary> /// Start or stop a group. /// </summary> /// <param name="state">True to start new group, false to end group. /// String to set group explicitly.</param> public static void setGroup(Union <bool, string> state) { if (state.Is <bool>()) { Events.group_ = state.As <bool>() ? Core.genUid() : ""; } else { Events.group_ = state.As <string>(); } }
/// <summary> /// Returns HTML-escaped text as a SafeHtml object, with newlines changed to /// <br>. /// </summary> /// <param name="textOrHtml">The text to escape. If /// the parameter is of type SafeHtml it is returned directly (no escaping /// is done).</param> /// <returns>The escaped text, wrapped as a SafeHtml.</returns> public static SafeHtml htmlEscapePreservingNewlines(Union <string, SafeHtml> textOrHtml) { if (textOrHtml.Is <SafeHtml>()) { return(textOrHtml.As <SafeHtml>()); } var html = goog.html.SafeHtml.htmlEscape(textOrHtml); return(goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse( [email protected](goog.html.SafeHtml.unwrap(html)), html.getDirection())); }
/// <summary> /// Sets the size of the palette grid to the given size. Callers can either /// pass a single {@link goog.math.Size} or a pair of numbers (first the number /// of columns, then the number of rows) to this method. In both cases, the /// number of rows is optional and will be calculated automatically if needed. /// It is an error to attempt to change the size of the palette after it has /// been rendered. /// </summary> /// <param name="size">Either a size object or the number of /// columns.</param> /// <param name="opt_rows">The number of rows (optional).</param> public void setSize(Union <goog.math.Size, int> size, int opt_rows = 0) { if (this.getElement() != null) { throw new Exception(goog.ui.Component.Error.ALREADY_RENDERED); } this.size_ = size.Is <int>() ? new goog.math.Size(size.As <int>(), opt_rows) : size.As <math.Size>(); // Adjust size, if needed. this.adjustSize_(); }
/// <summary> /// Context menus on touch devices are activated using a long-press. /// Unfortunately the contextmenu touch event is currently (2015) only suported /// by Chrome. This function is fired on any touchstart event, queues a task, /// which after about a second opens the context menu. The tasks is killed /// if the touch event terminates early. /// </summary> /// <param name="e">Touch start event.</param> /// <param name="uiObject">The block or workspace /// under the touchstart event.</param> internal static void longStart_(TouchEvent e, Union <BlockSvg, WorkspaceSvg> uiObject) { Core.longStop_(e); Core.longPid_ = Window.SetTimeout(() => { e.Button = 2; // Simulate a right button click. if (uiObject.Is <BlockSvg>()) { uiObject.As <BlockSvg>().onMouseDown_(e); } else { uiObject.As <WorkspaceSvg>().onMouseDown_(e); } }, Core.LONGPRESS); }
public virtual JsArray <Node> FlyoutCategory(Union <string, JsArray <Node>, NodeList> name, Workspace workspace) { if (!name.Is <string>()) { return(name.As <JsArray <Node> >()); } FlyoutCategoryHandler handler; if (FlyoutCategoryHandlers.TryGetValue(name.As <string>(), out handler)) { return(handler(workspace)); } return(new JsArray <Node>()); }
/// <summary> /// Add an item to the end of the input's field row. /// </summary> /// <param name="field">Something to add as a field.</param> /// <param name="opt_name">Language-neutral identifier which may used to find /// this field again.Should be unique to the host block.</param> /// <returns>The input being append to (to allow chaining).</returns> public Input appendField(Union <string, Field> field_, string opt_name = null) { // Empty string, Null or undefined generates no field, unless field is named. if (field_ == null && opt_name == null) { return(this); } Field field; // Generate a FieldLabel when given a plain text field. if (field_.Is <string>()) { field = new FieldLabel(field_.As <string>()); } else { field = field_.As <Field>(); } field.setSourceBlock(this.sourceBlock_); if (this.sourceBlock_.rendered) { field.init(); } field.name = opt_name; if (field.prefixField != null) { // Add any prefix. this.appendField(field.prefixField); } // Add the field to the field row. this.fieldRow.Push(field); if (field.suffixField != null) { // Add any suffix. this.appendField(field.suffixField); } if (this.sourceBlock_.rendered) { this.sourceBlock_.render(); // Adding a field will cause the block to change shape. this.sourceBlock_.bumpNeighbours_(); } return(this); }
/// <summary> /// Sets the margin to place around the popup. /// </summary> /// <param name="arg1"> Top value or Box.</param> /// <param name="opt_arg2"> Right value.</param> /// <param name="opt_arg3"> Bottom value.</param> /// <param name="opt_arg4"> Left value.</param> public void setMargin(Union <goog.math.Box, double> arg1, double opt_arg2 = Double.NaN, double opt_arg3 = Double.NaN, double opt_arg4 = Double.NaN) { if (arg1 == null || arg1.Is <goog.math.Box>()) { this.margin_ = arg1.As <goog.math.Box>(); } else { this.margin_ = new goog.math.Box( arg1.As <double>(), opt_arg2, opt_arg3, opt_arg4); } if (this.isVisible()) { this.reposition(); } }
/// <summary> /// Removes an event listener which was added with listen(). /// </summary> /// <param name="src">The target to stop listening to events on.</param> /// <param name="type">Event type or array of event types to unlisten to.</param> /// <param name="listener">The listener function to remove.</param> /// <param name="opt_capt">In DOM-compliant browsers, this determines /// whether the listener is fired during the capture or bubble phase of the /// event.</param> /// <param name="opt_handler">Element in whose scope to call the listener.</param> /// <returns>indicating whether the listener was there to remove.</returns> public static bool unlisten(Union <Bridge.Html5.EventTarget, Listenable> src, Union <string, JsArray <string> > type, Union <Delegate, Listener> listener_, bool opt_capt = false, object opt_handler = null) { if (type.Is <JsArray <string> >()) { foreach (var i in type.As <JsArray <string> >()) { goog.events.unlisten(src, i, listener_, opt_capt, opt_handler); } return(false); } var listener = goog.events.wrapListener(listener_); if (src.As <Listenable>().isImplementedBy()) { return(src.As <Listenable>().unlisten(type.As <string>(), listener, opt_capt, opt_handler)); } if (src == null) { // TODO(chrishenry): We should tighten the API to only accept // non-null objects, or add an assertion here. return(false); } var capture = !!opt_capt; var listenerMap = goog.events.getListenerMap_(src.As <Bridge.Html5.EventTarget>()); if (listenerMap != null) { var listenerObj = listenerMap.getListener(type.As <string>(), listener, capture, opt_handler); if (listenerObj != null) { return(goog.events.unlistenByKey(listenerObj)); } } return(false); }
public static Delegate wrapListener(Union <Delegate, Listener> listener_) { goog.asserts.assert(listener_.Value != null, "Listener can not be null."); if (listener_.Is <Delegate>()) { return(listener_.As <Delegate>()); } var listener = listener_.As <Listener>(); goog.asserts.assert( listener.handleEvent != null, "An object listener must have handleEvent method."); if (listener[goog.events.LISTENER_WRAPPER_PROP_] == null) { listener[goog.events.LISTENER_WRAPPER_PROP_] = new Func <Bridge.Html5.Event, bool>((e) => { return(listener.handleEvent(e)); }); } return((Delegate)listener[goog.events.LISTENER_WRAPPER_PROP_]); }
/// <summary> /// Removes the given child from this component, and returns it. Throws an error /// if the argument is invalid or if the specified child isn't found in the /// parent component. The argument can either be a string (interpreted as the /// ID of the child component to remove) or the child component itself. /// /// If {@code opt_unrender} is true, calls {@link goog.ui.component#exitDocument} /// on the removed child, and subsequently detaches the child's DOM from the /// document. Otherwise it is the caller's responsibility to clean up the child /// component's DOM. /// /// @see goog.ui.Component#removeChildAt /// </summary> /// <param name="child">The ID of the child to remove,</param> /// or the child component itself. /// <param name="opt_unrender">If true, calls {@code exitDocument} on the</param> /// removed child component, and detaches its DOM from the document. /// <returns>The removed component, if any.</returns> public virtual Component removeChild(Union <string, Component> child_, bool opt_unrender = false) { Component child = null; if (child_ != null) { // Normalize child to be the object and id to be the ID string. This also // ensures that the child is really ours. var id = child_.Is <string>() ? child_.As <string>() : child_.As <Component>().getId(); child = this.getChild(id); if (id != null && child != null) { this.childIndex_.Remove(id); this.children_.Remove(child); if (opt_unrender) { // Remove the child component's DOM from the document. We have to call // exitDocument first (see documentation). child.exitDocument(); if (child.element_ != null) { goog.dom.removeNode(child.element_); } } // Child's parent must be set to null after exitDocument is called // so that the child can unlisten to its parent if required. child.setParent(null); } } if (child == null) { throw new Exception(goog.ui.Component.Error.NOT_OUR_CHILD); } return /** @type {!goog.ui.Component} */ (child); }
/// <summary> /// Listen to an event on a Listenable. If the function is omitted then the /// EventHandler's handleEvent method will be used. /// </summary> /// <param name="src">Event source.</param> /// <param name="type_">Event type to listen for or array of event types.</param> /// <param name="opt_fn">Optional callback function to be used as the listener or an object with /// handleEvent function.</param> /// <param name="opt_capture">Optional whether to use capture phase.</param> /// <param name="opt_scope">Object in whose scope to call the listener.</param> /// <returns>This object, allowing for chaining of calls.</returns> private EventHandler listen_(Union <Bridge.Html5.EventTarget, Listenable> src, Union <string, JsArray <string> > type_, Delegate opt_fn = null, bool opt_capture = false, object opt_scope = null) { var self = this; JsArray <string> type; if (!type_.Is <JsArray <string> >()) { if (type_ != null) { goog.events.EventHandler.typeArray_ = new JsArray <string> { type_.As <string>() }; } type = goog.events.EventHandler.typeArray_; } else { type = type_.As <JsArray <string> >(); } for (var i = 0; i < type.Length; i++) { var listenerObj = goog.events.listen( src, type[i], opt_fn ?? new Action <Event>(self.handleEvent), opt_capture || false, opt_scope ?? self.handler_ ?? (object)self); if (listenerObj == null) { // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT // (goog.events.CaptureSimulationMode) in IE8-, it will return null // value. return(self); } var key = listenerObj.GetHashCode(); self.keys_[key] = listenerObj; } return(self); }
public static Listener listen(Union <Bridge.Html5.EventTarget, Listenable> src, Union <string, JsArray <string> > type, Delegate listener_, bool opt_capt = false, object opt_handler = null) { if (type.Is <JsArray <string> >()) { foreach (var i in type.As <JsArray <string> >()) { goog.events.listen(src, i, listener_, opt_capt, opt_handler); } return(null); } var listener = goog.events.wrapListener(listener_); if (src.As <Listenable>().isImplementedBy()) { return(src.As <Listenable>().listen(type.As <string>(), listener, opt_capt, opt_handler)); } else { return(goog.events.listen_(src.As <Bridge.Html5.EventTarget>(), type.As <string>(), listener, false, opt_capt, opt_handler)); } }
/// <summary> /// Updates the control's DOM by adding or removing the specified class name /// to/from its root element. May add additional combined classes as needed in /// IE6 and lower. Because of this, subclasses should use this method when /// modifying class names on the control's root element. /// </summary> /// <param name="control">Control instance (or root element) /// to be updated.</param> /// <param name="className">CSS class name to add or remove.</param> /// <param name="enable">Whether to add or remove the class name.</param> public void enableClassName(Union <Control, HTMLElement> control, string className, bool enable) { var element = control.Is <Control>() ? control.As <Control>().getElement() : control.As <HTMLElement>(); if (element != null) { var classNames = new JsArray <string>() { className }; // For IE6, we need to enable any combined classes involving this class // as well. // TODO(user): Remove this as IE6 is no longer in use. if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher("7")) { classNames = this.getAppliedCombinedClassNames_( goog.dom.classlist.get(element), className); classNames.Push(className); } goog.dom.classlist.enableAll(element, classNames, enable); } }
public Workspace Init(string id, Union <string, Element> toolbox) { this.toolbox = toolbox.Is <string>() ? toolbox.As <string>() : toolbox.As <Element>().InnerHTML; var tab = Document.GetElementById(id); // <div dir="LTR" id="blockly-div"></div> var div = (HTMLDivElement)goog.dom.createDom("div"); div.SetAttribute("dir", "LTR"); div.SetAttribute("class", "blockly-div"); _IdNo = No++; _WorkspaceElementId = "blockly-div" + _IdNo; div.SetAttribute("id", _WorkspaceElementId); div.SetAttribute("style", "z-index: " + _IdNo); div.Style.Left = "0"; div.Style.Top = "0"; div.Style.Width = "100%"; div.Style.Height = "100%"; div.Style.Position = Position.Absolute; tab.AppendChild(div); Core.HSV_SATURATION = 1.0; Core.HSV_VALUE = 0.8; _Workspace = Core.inject(_WorkspaceElementId, new Dictionary <string, object>() { { "toolbox", toolbox.Value ?? Document.GetElementById("toolbox") }, { "collapse", true }, { "comments", true }, { "disable", true }, { "maxBlocks", Int32.MaxValue }, { "trashcan", true }, { "horizontalLayout", false }, { "toolboxPosition", "start" }, { "css", true }, { "rtl", false }, { "scrollbars", true }, { "sounds", false }, { "oneBasedIndex", false }, { "zoom", new Dictionary <string, object>() { { "controls", true }, { "wheel", true }, { "startScale", 0.8 }, { "maxcale", 3 }, { "minScale", 0.3 } } } }); if (No != 2) { Hide(); } else { Show(); } _Workspace.toolbox_.flyout_.flyoutCategory = new Func <Union <string, JsArray <Node>, NodeList>, Workspace, JsArray <Node> >(FlyoutCategory); _Workspace.addChangeListener(Workspace_Changed); return(_Workspace); }