Skip to content

Commit

Permalink
push changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ammarahm-ed committed Mar 29, 2023
1 parent 98d6951 commit 6cb5d8a
Show file tree
Hide file tree
Showing 45 changed files with 1,365 additions and 556 deletions.
39 changes: 30 additions & 9 deletions packages/core/data/observable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,13 @@ interface EventDescriptior extends AddEventListenerOptions {
removed?: boolean;
abortHandler?: () => void;
thisArg?: unknown;
listener: EventListenerOrEventListenerObject;
listener?: EventListener;
listenerObject?: EventListenerObject;
}

function isThisArg(value: any) {
if (!value) return false;
if (typeof value === 'function') return false;
if (typeof value !== 'object') return false;
if (value?.constructor?.toString().indexOf('class') === 0) return true;
// If object has none of the values in event options, it's thisArg.
Expand All @@ -111,12 +113,22 @@ function isThisArg(value: any) {
}

const getEventDescriptor = (target: EventTarget, type: string, listener: EventListenerOrEventListenerObject, options?: (AddEventListenerOptions & { thisArg: any }) | boolean): EventDescriptior => {
const handleEvent = typeof listener === 'function' ? listener : listener.handleEvent;
const listenerPart: { listener?: EventListener; listenerObject?: EventListenerObject } = {};

if (typeof listener === 'function') {
listenerPart.listener = listener;
} else if (listener.handleEvent) {
listenerPart.listenerObject = listener;
if (typeof options === 'function') {
listenerPart.listener = options;
}
}

/**
* the most common case is handled first. No options. No capturing. No thisArg.
*/
if (!options || typeof options === 'boolean') {
return { target, capture: !!options, type, listener: handleEvent };
return { target, capture: !!options, type, ...listenerPart };
}

// The second most common case, last argument is thisArg.
Expand All @@ -125,7 +137,7 @@ const getEventDescriptor = (target: EventTarget, type: string, listener: EventLi
type,
target,
thisArg: options,
listener: handleEvent,
...listenerPart,
};
}
// Finally the last and "new" case of event options.
Expand All @@ -137,8 +149,8 @@ const getEventDescriptor = (target: EventTarget, type: string, listener: EventLi
once,
passive,
signal,
listener: handleEvent,
thisArg: thisArg,
...listenerPart,
};
};

Expand Down Expand Up @@ -593,11 +605,20 @@ export class Observable extends EventTarget implements globalThis.EventTarget {
}

let returnValue;
if (thisArg) {
returnValue = (listener as EventListener).apply(thisArg, [event]);
} else {
returnValue = (listener as EventListener)(event as NativeDOMEvent);
if (descriptor.listenerObject) {
if (thisArg) {
returnValue = descriptor.listenerObject.handleEvent.apply(thisArg, [event]);
} else {
returnValue = descriptor.listenerObject.handleEvent(event as NativeDOMEvent);
}
} else if (listener) {
if (thisArg) {
returnValue = listener.apply(thisArg, [event]);
} else {
returnValue = listener(event as NativeDOMEvent);
}
}

// This ensures errors thrown inside asynchronous functions do not get swallowed
if (returnValue && returnValue instanceof Promise) {
returnValue.catch((err) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nativescript/core",
"version": "8.4.8",
"version": "8.4.8-dom-alpha.0",
"description": "A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.",
"main": "index",
"types": "index.d.ts",
Expand Down
79 changes: 79 additions & 0 deletions packages/core/ui/core/dom/src/cssstylesheet/CSSStyleSheet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { addTaggedAdditionalCSS, removeTaggedAdditionalCSS } from '../../../../styling/style-scope';

class CSSRuleList extends Array {}

class CSSRule {
constructor(public cssText: string) {}
}

export class CSSStyleSheet {
timestampTag: number;
cssRules: CSSRuleList;
ownerRule: CSSRule;
get rules(): CSSRuleList {
return this.cssRules;
}
addRule(selector?: string, style?: string, index?: number): number {
return this.insertRule(`${selector} {${style}}`, index);
}
deleteRule(index: number): void {
this.removeRule(index);
}
insertRule(rule: string, index?: number): number {
if (typeof index !== 'number') index = -1;
this.cssRules.splice(index, 0, new CSSRule(rule));
return 0;
}
removeRule(index?: number): void {
this.cssRules.splice(index, 1);
}
replace(text: string): Promise<CSSStyleSheet> {
return new Promise((resolve) => {
this.replaceSync(text);
resolve(this);
});
}
replaceSync(text: string): void {
this.cssRules = [new CSSRule(text)];
this.adoptStyles();
}
disabled: boolean;
href: string;
media: MediaList;
ownerNode: globalThis.Element | ProcessingInstruction;
parentStyleSheet: CSSStyleSheet;
title: string;
type: string;

adoptStyles() {
this.timestampTag = this.timestampTag || Date.now();
const cssText = (this.cssRules as unknown as Array<CSSRule>).map((rule) => rule.cssText).join('');
removeTaggedAdditionalCSS(this.timestampTag);
addTaggedAdditionalCSS(cssText, this.timestampTag);
}

// Todo
// Parse the ast
// prefix with element tag so it becomes scoped.
// private parseCSSAst() {
// const cssText = (this.cssRules as unknown as Array<CSSRule>).map((rule) => rule.cssText).join('');
// let ast;
// if (cssText) {
// if (__CSS_PARSER__ === 'css-tree') {
// const cssTreeParse = require('../../../../../css/css-tree-parser').cssTreeParse;
// ast = cssTreeParse(cssText);
// } else if (__CSS_PARSER__ === 'nativescript') {
// const CSS3Parser = require('../../../../../css/CSS3Parser').CSS3Parser;
// const CSSNativeScript = require('../../../../../css/CSSNativeScript').CSSNativeScript;
// const cssparser = new CSS3Parser(cssText);
// const stylesheet = cssparser.parseAStylesheet();
// const cssNS = new CSSNativeScript();
// ast = cssNS.parseStylesheet(stylesheet);
// } else if (__CSS_PARSER__ === 'rework') {
// const parseCss = require('../../../../../css').parse;
// ast = parseCss(cssText, { source: undefined });
// }
// }
// return ast;
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export default class CustomElementRegistry {
elementClass._observedAttributes = elementClass.observedAttributes;
}

//@ts-ignore Add a css type so this tag name can be referred to in css files.
elementClass.prototype.cssType = tagName;

if (this._callbacks[tagName]) {
const callbacks = this._callbacks[tagName];
delete this._callbacks[tagName];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export default class DOMImplementation {
// @ts-ignore
documentClass._defaultView = this._ownerDocument.defaultView;
// @ts-ignore
return new documentClass();
const doc = new documentClass();
doc.initDocument();
return doc;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type Element from '../element/Element';
import Node from '../node/Node';
import NodeTypeEnum from '../node/NodeTypeEnum';

/**
* Character data base class.
Expand All @@ -8,31 +10,50 @@ import Node from '../node/Node';
*/
export default abstract class CharacterData extends Node {
get data(): string {
return this.data;
return this._nodeValue;
}

set data(data) {
this.data = data;
this._nodeValue = data;
propogateTextChangeToParentElement(this);
}
get length() {
return this.data.length;
return this._nodeValue.length;
}

get nodeValue() {
return this.data;
return this._nodeValue;
}
set nodeValue(data) {
this.data = data;
this._nodeValue = data;
propogateTextChangeToParentElement(this);
}

get textContent() {
return this.data;
return this._nodeValue;
}
set textContent(text) {
this.data = `${text}`;
this._nodeValue = `${text}`;
propogateTextChangeToParentElement(this);
}

appendData(data: string) {
this.data += data;
this._nodeValue += data;
}
}

function propogateTextChangeToParentElement(node: Node) {
let parentElement: Element = null;
let currentNode = node.parentNode;
while (currentNode && !parentElement) {
if (currentNode.nodeType === NodeTypeEnum.elementNode) {
parentElement = currentNode as Element;
}
currentNode = currentNode.parentNode;
}
if (!parentElement) return;

if ('text' in parentElement) {
parentElement['text'] = parentElement.textContent;
}
}
1 change: 1 addition & 0 deletions packages/core/ui/core/dom/src/nodes/comment/Comment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Node from '../node/Node';
import CharacterData from '../character-data/CharacterData';
import type Element from '../element/Element';

/**
* Comment node.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ export default class DocumentFragment extends ParentNode {
public nodeType = NodeTypeEnum.documentFragmentNode;
public nodeName: string = '#document-fragment';
public localName: string = '#document-fragment';
isParentNode = true;
constructor() {
super();
this._rootNode = this;
}
}
DocumentFragment.prototype['adoptedStyleSheets'] = [];
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ export default class DocumentType extends Node {
public publicId = '';
public systemId = '';

set parentNode(node: any) {
this._parentNode = node;
}
//@ts-ignore
get nodeName() {
return this.name;
Expand Down
40 changes: 37 additions & 3 deletions packages/core/ui/core/dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import ElementTag from '../../config/ElementTag';
import DOMImplementation from '../../dom-implementation/DOMImplementation';
import { Event } from '../../event/Event';
import INodeFilter from '../../tree-walker/INodeFilter';
import TreeWalker from '../../tree-walker/TreeWalker';
import Comment from '../comment/Comment';
import DocumentFragment from '../document-fragment/DocumentFragment';
import HTMLElement from '../html-element/HTMLElement';
import type Node from '../node/Node';
import NodeTypeEnum from '../node/NodeTypeEnum';
import ParentNode from '../parent-node/ParentNode';
import Text from '../text/Text';
Expand All @@ -12,16 +15,18 @@ const createElement = (type: string, owner: Document) => {
let element;

if (htmlElementRegistry.has(type)) {
element = new (htmlElementRegistry.get(type) as any)();
element.tagName = htmlElementRegistry[type].NODE_TAG_NAME;
const Class = htmlElementRegistry.get(type) as any;
element = new Class();
element.tagName = Class.NODE_TAG_NAME;
} else if (ElementTag[type]) {
element = new ElementTag[type]();
element.tagName = type;
} else if (customElements.get(type)) {
element = new (customElements.get(type))();
element.tagName = type;
} else {
element = new HTMLElement(NodeTypeEnum.elementNode, type);
element = new HTMLElement();
element.tagName = type;
}
element.ownerDocument = owner;
element._isRegisteredDOMElement = true;
Expand All @@ -38,6 +43,7 @@ export default class Document extends ParentNode {
body: HTMLElement;
implementation: DOMImplementation;
nodeType: NodeTypeEnum = NodeTypeEnum.documentNode;
isParentNode = true;
/* eslint-disable class-methods-use-this */
constructor() {
super();
Expand Down Expand Up @@ -100,4 +106,32 @@ export default class Document extends ParentNode {
set defaultView(scope: any) {
this._defaultView = scope;
}

/**
* Creates a Tree Walker.
*
* @param root Root.
* @param [whatToShow] What to show.
* @param [filter] Filter.
*/
public createTreeWalker(root: Node, whatToShow = -1, filter: INodeFilter = null): TreeWalker {
return new TreeWalker(root, whatToShow, filter);
}

/**
* Imports a node.
*
* @param node Node to import.
* @param [deep=false] Set to "true" if the clone should be deep.
*/
public importNode(node: Node, deep = false): Node {
if (!node.isNode) {
throw new Error('Parameter 1 was not of type Node.');
}
const clone = node.cloneNode(deep);
(<Document>clone.ownerDocument) = this;
return clone;
}
}

Document.prototype['adoptedStyleSheets'] = [];

0 comments on commit 6cb5d8a

Please sign in to comment.