This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch feature/coffeescript-to-typescript
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit fac3530ea608d510ef5c26a97b019f9694bfafcb
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Sun Apr 13 16:54:18 2025 -0300

    TAP5-2804: adding dom-prototype.ts plus little tweaks
---
 .../main/typescript/src/t5/core/confirm-click.ts   |  17 +-
 .../src/main/typescript/src/t5/core/dom-jquery.ts  |   2 +-
 .../main/typescript/src/t5/core/dom-prototype.ts   | 658 +++++++++++++++++++++
 .../src/main/typescript/src/t5/core/palette.ts     |   3 +
 4 files changed, 672 insertions(+), 8 deletions(-)

diff --git a/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts 
b/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
index fd6cb0fdf..6c309edf7 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
@@ -8,13 +8,16 @@
 import $ from "jquery";
 import  "bootstrap/modal";
 
-type DialogOptions = {
-  title: any;
-  message: any;
-  okClass: any;
-  okLabel: any;
-  cancelLabel: any;
-  ok: any;
+/**
+ * Dialog options.
+ */
+interface DialogOptions {
+  title?: string;
+  message: string;
+  okClass?: string;
+  okLabel?: string;
+  cancelLabel?: string;
+  ok: () => void;
 };
 
 // Runs a modal dialog, invoking a callback if the user selects the OK option. 
On any form of cancel,
diff --git a/tapestry-core/src/main/typescript/src/t5/core/dom-jquery.ts 
b/tapestry-core/src/main/typescript/src/t5/core/dom-jquery.ts
index 29d52ec6c..9c2921a8c 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/dom-jquery.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/dom-jquery.ts
@@ -13,7 +13,7 @@
 import type { ElementWrapper, EventWrapper, RequestWrapper, ResponseWrapper, 
ElementOffset, DOM, AddableContent, OnEventHandler, AjaxRequestOptions } from 
"t5/core/types.js";
 import _ from "underscore";
 import "t5/core/utils.js";
-import events from "./events.js";
+import events from "t5/core/events.js";
 import $ from "jquery";
 
 const convertContent = function(content: AddableContent) {
diff --git a/tapestry-core/src/main/typescript/src/t5/core/dom-prototype.ts 
b/tapestry-core/src/main/typescript/src/t5/core/dom-prototype.ts
new file mode 100644
index 000000000..254301765
--- /dev/null
+++ b/tapestry-core/src/main/typescript/src/t5/core/dom-prototype.ts
@@ -0,0 +1,658 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http:#www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import type { ElementWrapper, EventWrapper, RequestWrapper, ResponseWrapper, 
ElementOffset, DOM, AddableContent, OnEventHandler, AjaxRequestOptions } from 
"t5/core/types.js";
+import _ from "underscore";
+import utils from "t5/core/utils.js";
+import events from "t5/core/events.js";
+
+// @ts-ignore
+const $ = window.$ as (element: string | HTMLElement) => HTMLElement | null;
+
+// @ts-ignore
+const $$ = window.$$ as (selector: string | string[]) => HTMLElement[];
+
+const on = function(selector: string | HTMLElement | Document | HTMLElement[], 
events: string, match: string | null, handler: OnEventHandler) {
+  let elements;
+  if (handler == null) {
+    // @ts-ignore
+    handler = match;
+    match = null;
+  }
+  // @ts-ignore
+  elements = parseSelectorToElements(selector);
+  // @ts-ignore
+  events = utils.split(events);
+  // @ts-ignore
+  return onevent(elements, events, match, handler);
+}
+
+const fireNativeEvent = function(element: HTMLElement, eventName: string) {
+  let event;
+  // @ts-ignore
+  if (document.createEventObject) {
+    // @ts-ignore
+    event = document.createEventObject();
+    // @ts-ignore
+    return element.fireEvent("on" + eventName, event);
+  }
+  event = document.createEvent("HTMLEvents");
+  event.initEvent(eventName, true, true);
+  element.dispatchEvent(event);
+  return !event.defaultPrevented;
+};
+
+const parseSelectorToElements = function(selector: string | string[] | 
HTMLElement | ElementWrapper) {
+  if (_.isString(selector)) {
+    return $$(selector);
+  }
+  if (_.isArray(selector)) {
+    return selector;
+  }
+  return [selector];
+};
+
+const convertContent = function(content: string | HTMLElement | 
PrototypeElementWrapper): Element | string {
+  if (_.isString(content)) {
+    return content;
+  }
+  // @ts-ignore
+  if (_.isElement(content)) {
+    return content;
+  }
+  if (content instanceof PrototypeElementWrapper) {
+    return content.element;
+  }
+  throw new Error("Provided value <" + content + "> is not valid as DOM 
element content.");
+};
+
+class PrototypeEventWrapper implements EventWrapper {
+  readonly nativeEvent: Event;
+  readonly memo: any;
+  readonly type: string;
+  readonly char?: string;
+  readonly key?: string;
+
+  constructor(event: Event) {
+    let i, len, name, ref;
+    this.nativeEvent = event;
+    // @ts-ignore
+    this.memo = event.memo;
+    this.type = event.type;
+    const o = event as any;
+    this.char = o['char'];
+    this.key = o['key'];
+  }
+
+  stop() {
+    // @ts-ignore
+    return this.nativeEvent.stop();
+  };
+
+}
+
+type PrototypeEvent = {
+  findElement: () => HTMLElement;
+  stop: () => void;
+}
+
+const onevent = function(elements: HTMLElement[], eventNames: string[], match: 
string | null, handler: OnEventHandler) {
+  let element, eventHandlers, eventName, i, j, len, len1, wrapped;
+  if (handler == null) {
+    throw new Error("No event handler was provided.");
+  }
+  wrapped = function(prototypeEvent: PrototypeEvent) {
+    let elementWrapper, eventWrapper, result;
+    elementWrapper = new PrototypeElementWrapper(prototypeEvent.findElement());
+    // @ts-ignore
+    eventWrapper = new PrototypeEventWrapper(prototypeEvent);
+    // @ts-ignore
+    result = prototypeEvent.stopped ? false : handler.call(elementWrapper, 
eventWrapper, eventWrapper.memo);
+    if (result === false) {
+      prototypeEvent.stop();
+    }
+  };
+  eventHandlers = [];
+  for (i = 0, len = elements.length; i < len; i++) {
+    element = elements[i];
+    for (j = 0, len1 = eventNames.length; j < len1; j++) {
+      eventName = eventNames[j];
+      // @ts-ignore
+      eventHandlers.push(Event.on(element, eventName, match, wrapped));
+    }
+  }
+  return function() {
+    let eventHandler, k, len2, results;
+    results = [];
+    for (k = 0, len2 = eventHandlers.length; k < len2; k++) {
+      eventHandler = eventHandlers[k];
+      results.push(eventHandler.stop());
+    }
+    return results;
+  };
+};
+
+class PrototypeElementWrapper implements ElementWrapper {
+  readonly element: HTMLElement;
+
+  constructor(element1: HTMLElement) {
+    this.element = element1;
+  }
+
+  toStringn() {
+    let markup;
+    markup = this.element.outerHTML;
+    return "ElementWrapper[" + (markup.substring(0, (markup.indexOf(">")) + 
1)) + "]";
+  };
+
+  hide() {
+    // @ts-ignore
+    this.element.hide();
+    return this;
+  };
+
+  show() {
+    // @ts-ignore
+    this.element.show();
+    return this;
+  };
+
+  css = function(name: string, value?: string) {
+    if (arguments.length === 1) {
+      // @ts-ignore
+      return this.element.getStyle(name);
+    }
+    // @ts-ignore
+    this.element.setStyle({
+      name: value
+    });
+    // @ts-ignore
+    return this;
+  };
+
+  offset() {
+    // @ts-ignore
+    return this.element.viewportOffset();
+  };
+
+  remove() {
+    // @ts-ignore
+    this.element.remove();
+    return this;
+  };
+
+  attr(name: string, value?: string) {
+    let attributeName, current;
+    if (_.isObject(name)) {
+      // @ts-ignore
+      for (attributeName in name) {
+        value = name[attributeName];
+        this.attr(attributeName, value);
+      }
+      return this;
+    }
+    // @ts-ignore
+    current = this.element.readAttribute(name);
+    if (arguments.length > 1) {
+      // @ts-ignore
+      this.element.writeAttribute(name, value === void 0 ? null : value);
+    }
+    return current;
+  };
+
+  // @ts-ignore
+  focus() {
+    this.element.focus();
+    return this;
+  };
+
+  hasClass(name: string) {
+    // @ts-ignore
+    return this.element.hasClassName(name);
+  };
+
+  removeClass(name: string) {
+    // @ts-ignore
+    this.element.removeClassName(name);
+    return this;
+  };
+
+  addClass(name: string) {
+    // @ts-ignore
+    this.element.addClassName(name);
+    return this;
+  };
+
+  update(content: AddableContent) {
+    // @ts-ignore
+    this.element.update(content && convertContent(content));
+    return this;
+  };
+
+  append(content: AddableContent) {
+    // @ts-ignore
+    this.element.insert({
+      // @ts-ignore
+      bottom: convertContent(content)
+    });
+    return this;
+  };
+
+  prepend(content: AddableContent) {
+    // @ts-ignore
+    this.element.insert({
+      // @ts-ignore
+      top: convertContent(content)
+    });
+    return this;
+  };
+
+  insertBefore(content: AddableContent) {
+    // @ts-ignore
+    this.element.insert({
+      // @ts-ignore
+      before: convertContent(content)
+    });
+    return this;
+  };
+
+  insertAfter(content: AddableContent) {
+    // @ts-ignore
+    this.element.insert({
+      // @ts-ignore
+      after: convertContent(content)
+    });
+    return this;
+  };
+
+  findFirst(selector: string) {
+    let match;
+    // @ts-ignore
+    match = this.element.down(selector);
+    if (match) {
+      return new PrototypeElementWrapper(match);
+    } else {
+      return null;
+    }
+  };
+
+  find(selector: string) {
+    let e, i, len, matches, results;
+    // @ts-ignore
+    matches = this.element.select(selector);
+    results = [];
+    for (i = 0, len = matches.length; i < len; i++) {
+      e = matches[i];
+      results.push(new PrototypeElementWrapper(e));
+    }
+    return results;
+  };
+
+  findParent(selector: string) {
+    let parent;
+    // @ts-ignore
+    parent = this.element.up(selector);
+    if (!parent) {
+      return null;
+    }
+    return new PrototypeElementWrapper(parent);
+  };
+  
+  closest(selector: string) {
+    // @ts-ignore
+    if (this.element.match(selector)) {
+      return this;
+    }
+    return this.findParent(selector);
+  };
+
+  parent() {
+    let parent;
+    parent = this.element.parentNode;
+    if (!parent) {
+      return null;
+    }
+    // @ts-ignore
+    return new PrototypeElementWrapper(parent);
+  };
+
+  children() {
+    let e, i, len, ref, results;
+    // @ts-ignore
+    ref = this.element.childElements();
+    results = [];
+    for (i = 0, len = ref.length; i < len; i++) {
+      e = ref[i];      
+      results.push(new PrototypeElementWrapper(e));
+    }
+    return results;
+  };
+
+  visible() {
+    // @ts-ignore
+    return this.element.visible();
+  };
+
+  deepVisible() {
+    let element;
+    ({
+      element
+    } = this);
+    return element.offsetWidth > 0 && element.offsetHeight > 0;
+  };
+
+  trigger(eventName: string, memo: any) {
+    let event;
+    if (eventName == null) {
+      throw new Error("Attempt to trigger event with null event name");
+    }
+    if (!((_.isNull(memo)) || (_.isObject(memo)) || (_.isUndefined(memo)))) {
+      throw new Error("Event memo may be null or an object, but not a simple 
type.");
+    }
+    if ((eventName.indexOf(':')) > 0) {
+      // @ts-ignore
+      event = this.element.fire(eventName, memo);
+      return !event.defaultPrevented;
+    }
+    if (memo) {
+      throw new Error("Memo must be null when triggering a native event");
+    }
+    // @ts-ignore
+    if (!(Prototype.Browser.WebKit && eventName === 'submit' && this.element 
instanceof HTMLFormElement)) {
+      return fireNativeEvent(this.element, eventName);
+    } else {
+      return this.element.requestSubmit();
+    }
+  };
+
+  value(newValue?: string) {
+    let current;
+    // @ts-ignore
+    current = this.element.getValue();
+    if (arguments.length > 0) {
+      // @ts-ignore
+      this.element.setValue(newValue);
+    }
+    return current;
+  };
+
+  checked() {
+    // @ts-ignore
+    return this.element.checked;
+  };
+
+  meta(name: string, value?: string) {
+    let current;
+    // @ts-ignore
+    current = this.element.retrieve(name);
+    if (arguments.length > 1) {
+      // @ts-ignore
+      this.element.store(name, value);
+    }
+    return current;
+  };
+
+  on(events: string, match: string | null, handler: OnEventHandler): 
ElementWrapper {
+    // @ts-ignore
+    on(this.element, events, match, handler);
+    return this;
+  };
+
+  text() {
+    return this.element.textContent || this.element.innerText;
+  };
+
+};
+
+class PrototypeRequestWrapper {
+  readonly req: Request;
+  constructor(req: Request) {
+    this.req = req;
+  }
+
+  abort() {
+    throw "Cannot abort Ajax request when using Prototype.";
+  };
+
+};
+
+class PrototypeResponseWrapper {
+  readonly res: Response;
+  readonly status: number;
+  readonly statusText: string;
+  readonly json: any;
+  readonly text: any;
+  constructor(res: Response) {
+    this.res = res;
+    this.status = this.res.status;
+    this.statusText = this.res.statusText;
+    // @ts-ignore
+    this.json = this.res.responseJSON;
+    // @ts-ignore
+    this.text = this.res.responseText;
+  }
+
+  header(name: string) {
+    // @ts-ignore
+    return this.res.getHeader(name);
+  };
+
+};
+
+const body = new PrototypeElementWrapper(document.body);
+
+let activeAjaxCount = 0;
+const adjustAjaxCount = function(delta: number) {
+  activeAjaxCount += delta;
+  return body.attr("data-ajax-active", String(activeAjaxCount));
+};
+
+const ajaxRequest = function(url: string, options: AjaxRequestOptions | 
undefined) {
+  let finalOptions: AjaxRequestOptions;
+  if (options == null) {
+    options = {};
+  }
+  finalOptions = {
+    method: options.method || "post",
+    contentType: options.contentType || "application/x-www-form-urlencoded",
+    // @ts-ignore
+    parameters: options.data,
+    requestHeaders: options.headers,
+
+    onException(ajaxRequest: any, exception: any) {
+      adjustAjaxCount(-1);
+      if (options.exception) {
+        options.exception(exception);
+      } else {
+        throw exception;
+      }
+    },
+
+    onFailure(response: Response) {
+      let message, text;
+      adjustAjaxCount(-1);
+      // @ts-ignore
+      message = "Request to " + url + " failed with status " + 
(response.getStatus());
+      // @ts-ignore
+      text = response.getStatusText();
+      if (!_.isEmpty(text)) {
+        message += " -- " + text;
+      }
+      message += ".";
+      if (options.failure) {
+        options.failure(new PrototypeResponseWrapper(response), message);
+      } else {
+        throw new Error(message);
+      }
+    },
+
+    onSuccess(response: Response) {
+      adjustAjaxCount(-1);
+      // @ts-ignore
+      if ((!response.getStatus()) || (!response.request.success())) {
+        // @ts-ignore
+        finalOptions.onFailure(new PrototypeResponseWrapper(response));
+        return;
+      }
+      options.success && options.success(new 
PrototypeResponseWrapper(response));
+    }
+  };
+  adjustAjaxCount(+1);
+  // @ts-ignore
+  return new PrototypeRequestWrapper(new Ajax.Request(url, finalOptions));
+};
+
+type Scanner = (element: ElementWrapper) => void;
+
+let scanners: Scanner[] = [];
+
+let scanner = function(selector: string, callback: (e: ElementWrapper) => 
void) {
+  var scan;
+  scan = function(root: ElementWrapper) {
+    var el, j, len, ref;
+    ref = root.find(selector);
+    for (j = 0, len = ref.length; j < len; j++) {
+      el = ref[j];
+      callback(el);
+    }
+  };
+  scan(body!);
+  if (scanners === null) {
+    scanners = [];
+    body!.on(events.initializeComponents, null, function() {
+      var f, j, len;
+      for (j = 0, len = scanners.length; j < len; j++) {
+        f = scanners[j];
+        f(body!);
+      }
+    });
+  }
+  scanners.push(scan);
+};
+
+const wrapElement = function(element: string | HTMLElement) {
+  if (_.isString(element)) {
+    // @ts-ignore
+    element = $(element);
+    if (!element) {
+      return null;
+    }
+  } else {
+    if (!element) {
+      throw new Error("Attempt to wrap a null DOM element");
+    }
+  }
+  // @ts-ignore
+  return new PrototypeElementWrapper(element);
+};
+
+const createElement = function(elementName: string, attributes?: object, 
body?: AddableContent): ElementWrapper {
+  let element;
+  if (_.isObject(elementName)) {
+    // @ts-ignore
+    body = attributes;
+    attributes = elementName;
+    // @ts-ignore
+    elementName = null;
+  }
+  if (_.isString(attributes)) {
+    body = attributes;
+    // @ts-ignore
+    attributes = null;
+  }
+  element = wrapElement(document.createElement(elementName || "div"));
+  if (attributes) {
+    // @ts-ignore
+    element.attr(attributes);
+  }
+  if (body) {
+    // @ts-ignore
+    element.update(body);
+  }
+
+  return element!;
+};
+
+
+const getDataAttributeAsObject = function(element: HTMLElement, attribute: 
string) {
+  let value;
+  // @ts-ignore
+  value = $(element).readAttribute('data-' + attribute);
+  if (value !== null) {
+    return value = JSON.parse(value);
+  } else {
+    return value = {};
+  }
+};
+
+const getEventUrl = function(eventName: string, element?: HTMLElement): string 
{
+  let data, ref, ref1, url;
+  if (!(eventName != null)) {
+    throw 'dom.getEventUrl: the eventName parameter cannot be null';
+  }
+  if (!_.isString(eventName)) {
+    throw 'dom.getEventUrl: the eventName parameter should be a string';
+  }
+  eventName = eventName.toLowerCase();
+  if (element === null) {
+    element = document.body;
+  } else if (element instanceof PrototypeElementWrapper) {
+    ({
+      element
+    } = element);
+    // @ts-ignore
+  } else if (element.jquery != null) {
+    // @ts-ignore
+    element = element[0];
+  }
+  url = null;
+  // @ts-ignore
+  while ((url == null) && (element.previousElementSibling != null)) {
+    // @ts-ignore
+    data = getDataAttributeAsObject(element, 'component-events');
+    url = data != null ? (ref = data[eventName]) != null ? ref.url : void 0 : 
void 0;
+    // @ts-ignore
+    element = element.previousElementSibling;
+  }
+  if (url == null) {
+    // @ts-ignore
+    while ((url == null) && (element.parentElement != null)) {
+      // @ts-ignore
+      data = getDataAttributeAsObject(element, 'component-events');
+      url = data != null ? (ref1 = data[eventName]) != null ? ref1.url : void 
0 : void 0;
+      // @ts-ignore
+      element = element.parentElement;
+    }
+  }
+  return url;
+};
+
+const onDocument = function(events: string, match: string | null, handler: 
OnEventHandler) {
+  // @ts-ignore
+  return on(document, events, match, handler);
+};
+
+// A bit of a hack to export a function that also provides properties defined 
in an interface.
+// Inspired by https://stackoverflow.com/a/48675307 (yay for Thiagos! hehehe)
+const dom = wrapElement as DOM;
+dom.getEventUrl = getEventUrl;
+dom.wrap = wrapElement;
+dom.create = createElement;
+dom.ajaxRequest = ajaxRequest;
+dom.on = on;
+dom.onDocument = onDocument;
+dom.body = body!;
+dom.scanner = scanner;
+
+const exports: DOM = dom;
+
+export default exports;
\ No newline at end of file
diff --git a/tapestry-core/src/main/typescript/src/t5/core/palette.ts 
b/tapestry-core/src/main/typescript/src/t5/core/palette.ts
index 3093a56b2..ad3a24675 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/palette.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/palette.ts
@@ -25,6 +25,9 @@ import { ElementWrapper } from "./types.js";
 
 const isSelected = (option: any) => option.selected;
 
+/**
+ * Palette controller class.
+ */
 class PaletteController {
   selected: ElementWrapper;
   container: ElementWrapper;

Reply via email to