/// <summary> /// Adds an event listener. A listener can only be added once to an /// object and if it is added again the key for the listener is /// returned. /// /// Note that a one-off listener will not change an existing listener, /// if any. On the other hand a normal listener will change existing /// one-off listener to become a normal listener. /// </summary> /// <param name="type">The listener event type.</param> /// <param name="listener">This listener callback method.</param> /// <param name="callOnce">Whether the listener is a one-off listener.</param> /// <param name="opt_useCapture">The capture mode of the listener.</param> /// <param name="opt_listenerScope">Object in whose scope to call the /// listener.</param> /// <returns>Unique key for the listener.</returns> public Listener add(string type, Delegate listener, bool callOnce, bool opt_useCapture, object opt_listenerScope) { var typeStr = type.ToString(); if (!this.listeners.TryGetValue(typeStr, out var listenerArray)) { listenerArray = this.listeners[typeStr] = new JsArray <Listener>(); this.typeCount_++; } Listener listenerObj; var index = goog.events.ListenerMap.findListenerIndex_( listenerArray, listener, opt_useCapture, opt_listenerScope); if (index > -1) { listenerObj = listenerArray[index]; if (!callOnce) { // Ensure that, if there is an existing callOnce listener, it is no // longer a callOnce listener. listenerObj.callOnce = false; } } else { listenerObj = new goog.events.Listener( listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope); listenerObj.callOnce = callOnce; listenerArray.Push(listenerObj); } return(listenerObj); }
/// <summary> /// Create a date picker under the date field. /// </summary> public override void showEditor_(bool opt_quietInput) { WidgetDiv.show(this, this.sourceBlock_.RTL, new Action(FieldDate.widgetDispose_)); // Create the date picker using Closure. FieldDate.loadLanguage_(); var picker = new goog.ui.DatePicker(); picker.setAllowNone(false); picker.setShowWeekNum(false); // Position the picker to line up with the field. // Record windowSize and scrollOffset before adding the picker. var windowSize = goog.dom.getViewportSize(); var scrollOffset = goog.style.getViewportPageOffset(Document.Instance); var xy = this.getAbsoluteXY_(); var borderBBox = this.getScaledBBox_(); var div = WidgetDiv.DIV; picker.render(div); picker.setDate(new Date(this.getValue())); // Record pickerSize after adding the date picker. var pickerSize = goog.style.getSize(picker.getElement()); // Flip the picker vertically if off the bottom. if (xy.y + pickerSize.height + borderBBox.height >= windowSize.height + scrollOffset.y) { xy.y -= pickerSize.height - 1; } else { xy.y += borderBBox.height - 1; } if (this.sourceBlock_.RTL) { xy.x += borderBBox.width; xy.x -= pickerSize.width; // Don't go offscreen left. if (xy.x < scrollOffset.x) { xy.x = scrollOffset.x; } } else { // Don't go offscreen right. if (xy.x > windowSize.width + scrollOffset.x - pickerSize.width) { xy.x = windowSize.width + scrollOffset.x - pickerSize.width; } } WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset, this.sourceBlock_.RTL); // Configure event handler. var thisField = this; FieldDate.changeEventKey_ = goog.events.listen(picker, goog.ui.DatePicker.Events.CHANGE, new Action <Bridge.Html5.Event>((e) => { var date = e.Date != null ? e.Date.ToIsoString(true) : ""; WidgetDiv.hide(); if (thisField.sourceBlock_ != null) { // Call any validation function, and allow it to override. date = thisField.callValidator(date); } thisField.setValue(date); })); }
/// <summary> /// Handles an event and dispatches it to the correct listeners. This /// function is a proxy for the real listener the user specified. /// </summary> /// <param name="listener">The listener object.</param> /// <param name="opt_evt">Optional event object that gets passed in via the /// native event handlers.</param> /// <returns>Result of the event handler.</returns> private static bool handleBrowserEvent_(Bridge.Html5.EventTarget _this, goog.events.Listener listener, Bridge.Html5.Event opt_evt = null) { if (listener.removed) { return(true); } // Synthesize event propagation if the browser does not support W3C // event model. if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) { var ieEvent = opt_evt ?? ((Bridge.Html5.Event)goog.le.getObjectByName("window.event")); var evt = new goog.events.BrowserEvent(ieEvent, _this); /** @type {boolean} */ var retval = true; if (goog.events.CAPTURE_SIMULATION_MODE == goog.events.CaptureSimulationMode.ON) { // If we have not marked this event yet, we should perform capture // simulation. if (!goog.events.isMarkedIeEvent_(ieEvent)) { goog.events.markIeEvent_(ieEvent); var ancestors = new JsArray <Node>(); for (var parent = (Node)evt.currentTarget; parent != null; parent = parent.ParentNode) { ancestors.Push(parent); } // Fire capture listeners. var type = listener.type; for (var i = ancestors.Length - 1; !evt.propagationStopped_ && i >= 0; i--) { evt.currentTarget = ancestors[i]; var result = goog.events.fireListeners_(ancestors[i], type, true, evt); retval = retval && result != null; } // Fire bubble listeners. // // We can technically rely on IE to perform bubble event // propagation. However, it turns out that IE fires events in // opposite order of attachEvent registration, which broke // some code and tests that rely on the order. (While W3C DOM // Level 2 Events TR leaves the event ordering unspecified, // modern browsers and W3C DOM Level 3 Events Working Draft // actually specify the order as the registration order.) for (var i = 0; !evt.propagationStopped_ && i < ancestors.Length; i++) { evt.currentTarget = ancestors[i]; var result = goog.events.fireListeners_(ancestors[i], type, false, evt); retval = retval && result != null; } } } else { retval = goog.events.fireListener(listener, evt); } return(retval); } // Otherwise, simply fire the listener. return(goog.events.fireListener( listener, new goog.events.BrowserEvent(opt_evt, _this))); }
/// <summary> /// Create a palette under the colour field. /// </summary> public override void showEditor_(bool opt_quietInput) { WidgetDiv.show(this, this.sourceBlock_.RTL, new Action(FieldColour.widgetDispose_)); // Create the palette using Closure. var picker = new goog.ui.ColorPicker(); picker.setSize(this.columns_ ?? FieldColour.COLUMNS); picker.setColors(new JsArray <string>(this.colours_ ?? FieldColour.COLOURS)); // Position the palette to line up with the field. // Record windowSize and scrollOffset before adding the palette. var windowSize = goog.dom.getViewportSize(); var scrollOffset = goog.style.getViewportPageOffset(Document.Instance); var xy = this.getAbsoluteXY_(); var borderBBox = this.getScaledBBox_(); var div = WidgetDiv.DIV; picker.render(div); picker.setSelectedColor(this.getValue()); // Record paletteSize after adding the palette. var paletteSize = goog.style.getSize(picker.getElement()); // Flip the palette vertically if off the bottom. if (xy.y + paletteSize.height + borderBBox.height >= windowSize.height + scrollOffset.y) { xy.y -= paletteSize.height - 1; } else { xy.y += borderBBox.height - 1; } if (this.sourceBlock_.RTL) { xy.x += borderBBox.width; xy.x -= paletteSize.width; // Don't go offscreen left. if (xy.x < scrollOffset.x) { xy.x = scrollOffset.x; } } else { // Don't go offscreen right. if (xy.x > windowSize.width + scrollOffset.x - paletteSize.width) { xy.x = windowSize.width + scrollOffset.x - paletteSize.width; } } WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset, this.sourceBlock_.RTL); // Configure event handler. var thisField = this; FieldColour.changeEventKey_ = goog.events.listen(picker, goog.ui.ColorPicker.EventType.CHANGE, new Action <goog.events.Event>((e) => { string colour = ((goog.ui.ColorPicker)e.target).getSelectedColor() ?? "#000000"; WidgetDiv.hide(); if (thisField.sourceBlock_ != null) { // Call any validation function, and allow it to override. colour = thisField.callValidator(colour); } if (colour != null) { thisField.setValue(colour); } })); }