提交 19dea473 编写于 作者: B Benjamin Pasero

🎉 debt - remove builder 🎉

上级 55d89a74
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-builder-hidden {
display: none !important;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./builder';
import * as types from 'vs/base/common/types';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import * as assert from 'vs/base/common/assert';
import * as DOM from 'vs/base/browser/dom';
/**
* !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*
* @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*/
export interface QuickBuilder {
(): Builder;
(builders: Builder[]): Builder;
(element: HTMLElement): Builder;
(element: HTMLElement[]): Builder;
(window: Window): Builder;
(htmlOrQuerySyntax: string): Builder; // Or, MultiBuilder
(name: string, args?: any, fn?: (builder: Builder) => any): Builder;
(one: string, two: string, three: string): Builder;
(builder: Builder): Builder;
}
// --- Implementation starts here
let MS_DATA_KEY = '_msDataKey';
let DATA_BINDING_ID = '__$binding';
let LISTENER_BINDING_ID = '__$listeners';
let VISIBILITY_BINDING_ID = '__$visibility';
function data(element: any): any {
if (!element[MS_DATA_KEY]) {
element[MS_DATA_KEY] = {};
}
return element[MS_DATA_KEY];
}
function hasData(element: any): boolean {
return !!element[MS_DATA_KEY];
}
/**
* !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*
* @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*/
export class Builder implements IDisposable {
private currentElement: HTMLElement;
private offdom: boolean;
private container: HTMLElement;
private createdElements: HTMLElement[];
private toDispose: { [type: string]: IDisposable[]; };
private captureToDispose: { [type: string]: IDisposable[]; };
constructor(element?: HTMLElement, offdom?: boolean) {
this.offdom = offdom;
this.container = element;
this.currentElement = element;
this.createdElements = [];
this.toDispose = {};
this.captureToDispose = {};
}
/**
* Returns a new builder that lets the current HTML Element of this builder be the container
* for future additions on the builder.
*/
asContainer(): Builder {
return withBuilder(this, this.offdom);
}
/**
* Clones the builder providing the same properties as this one.
*/
clone(): Builder {
let builder = new Builder(this.container, this.offdom);
builder.currentElement = this.currentElement;
builder.createdElements = this.createdElements;
builder.captureToDispose = this.captureToDispose;
builder.toDispose = this.toDispose;
return builder;
}
/**
* Inserts all created elements of this builder as children to the given container. If the
* container is not provided, the element that was passed into the Builder at construction
* time is being used. The caller can provide the index of insertion, or omit it to append
* at the end.
* This method is a no-op unless the builder was created with the offdom option to be true.
*/
build(container?: Builder, index?: number): Builder;
build(container?: HTMLElement, index?: number): Builder;
build(container?: any, index?: number): Builder {
assert.ok(this.offdom, 'This builder was not created off-dom, so build() can not be called.');
// Use builders own container if present
if (!container) {
container = this.container;
}
// Handle case of passed in Builder
else if (container instanceof Builder) {
container = (<Builder>container).getHTMLElement();
}
assert.ok(container, 'Builder can only be build() with a container provided.');
assert.ok(DOM.isHTMLElement(container), 'The container must either be a HTMLElement or a Builder.');
let htmlContainer = <HTMLElement>container;
// Append
let i: number, len: number;
let childNodes = htmlContainer.childNodes;
if (types.isNumber(index) && index < childNodes.length) {
for (i = 0, len = this.createdElements.length; i < len; i++) {
htmlContainer.insertBefore(this.createdElements[i], childNodes[index++]);
}
} else {
for (i = 0, len = this.createdElements.length; i < len; i++) {
htmlContainer.appendChild(this.createdElements[i]);
}
}
return this;
}
/**
* Similar to #build, but does not require that the builder is off DOM, and instead
* attached the current element. If the current element has a parent, it will be
* detached from that parent.
*/
appendTo(container?: Builder, index?: number): Builder;
appendTo(container?: HTMLElement, index?: number): Builder;
appendTo(container?: any, index?: number): Builder {
// Use builders own container if present
if (!container) {
container = this.container;
}
// Handle case of passed in Builder
else if (container instanceof Builder) {
container = (<Builder>container).getHTMLElement();
}
assert.ok(container, 'Builder can only be build() with a container provided.');
assert.ok(DOM.isHTMLElement(container), 'The container must either be a HTMLElement or a Builder.');
let htmlContainer = <HTMLElement>container;
// Remove node from parent, if needed
if (this.currentElement.parentNode) {
this.currentElement.parentNode.removeChild(this.currentElement);
}
let childNodes = htmlContainer.childNodes;
if (types.isNumber(index) && index < childNodes.length) {
htmlContainer.insertBefore(this.currentElement, childNodes[index]);
} else {
htmlContainer.appendChild(this.currentElement);
}
return this;
}
/**
* Performs the exact reverse operation of #append.
* Doing `a.append(b)` is the same as doing `b.appendTo(a)`, with the difference
* of the return value being the builder which called the operation (`a` in the
* first case; `b` in the second case).
*/
append(child: HTMLElement, index?: number): Builder;
append(child: Builder, index?: number): Builder;
append(child: any, index?: number): Builder {
assert.ok(child, 'Need a child to append');
if (DOM.isHTMLElement(child)) {
child = _withElement(child);
}
assert.ok(child instanceof Builder || child instanceof MultiBuilder, 'Need a child to append');
(<Builder>child).appendTo(this, index);
return this;
}
/**
* Removes the current element of this builder from its parent node.
*/
offDOM(): Builder {
if (this.currentElement.parentNode) {
this.currentElement.parentNode.removeChild(this.currentElement);
}
return this;
}
/**
* Returns the HTML Element the builder is currently active on.
*/
getHTMLElement(): HTMLElement {
return this.currentElement;
}
/**
* Returns the HTML Element the builder is building in.
*/
getContainer(): HTMLElement {
return this.container;
}
// HTML Elements
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
div(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('div', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
p(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('p', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
ul(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('ul', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
li(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('li', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
span(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('span', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
img(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('img', attributes, fn);
}
/**
* Creates a new element of this kind as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
a(attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement('a', attributes, fn);
}
/**
* Creates a new element of given tag name as child of the current element or parent.
* Accepts an object literal as first parameter that can be used to describe the
* attributes of the element.
* Accepts a function as second parameter that can be used to create child elements
* of the element. The function will be called with a new builder created with the
* provided element.
*/
element(name: string, attributes?: any, fn?: (builder: Builder) => void): Builder {
return this.doElement(name, attributes, fn);
}
private doElement(name: string, attributesOrFn?: any, fn?: (builder: Builder) => void): Builder {
// Create Element
let element = document.createElement(name);
this.currentElement = element;
// Off-DOM: Remember in array of created elements
if (this.offdom) {
this.createdElements.push(element);
}
// Object (apply properties as attributes to HTML element)
if (types.isObject(attributesOrFn)) {
this.attr(attributesOrFn);
}
// Support second argument being function
if (types.isFunction(attributesOrFn)) {
fn = attributesOrFn;
}
// Apply Functions (Elements created in Functions will be added as child to current element)
if (types.isFunction(fn)) {
let builder = new Builder(element);
fn.call(builder, builder); // Set both 'this' and the first parameter to the new builder
}
// Add to parent
if (!this.offdom) {
this.container.appendChild(element);
}
return this;
}
/**
* Calls focus() on the current HTML element;
*/
domFocus(): Builder {
this.currentElement.focus();
return this;
}
/**
* Calls blur() on the current HTML element;
*/
domBlur(): Builder {
this.currentElement.blur();
return this;
}
/**
* Registers listener on event types on the current element.
*/
on<E extends Event = Event>(type: string, fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder;
on<E extends Event = Event>(typeArray: string[], fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder;
on<E extends Event = Event>(arg1: any, fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder {
// Event Type Array
if (types.isArray(arg1)) {
arg1.forEach((type: string) => {
this.on(type, fn, listenerToDisposeContainer, useCapture);
});
}
// Single Event Type
else {
let type = arg1;
// Add Listener
let unbind: IDisposable = DOM.addDisposableListener(this.currentElement, type, (e) => {
fn(e, this, unbind); // Pass in Builder as Second Argument
}, useCapture || false);
// Remember for off() use
if (useCapture) {
if (!this.captureToDispose[type]) {
this.captureToDispose[type] = [];
}
this.captureToDispose[type].push(unbind);
} else {
if (!this.toDispose[type]) {
this.toDispose[type] = [];
}
this.toDispose[type].push(unbind);
}
// Bind to Element
let listenerBinding: IDisposable[] = this.getProperty(LISTENER_BINDING_ID, []);
listenerBinding.push(unbind);
this.setProperty(LISTENER_BINDING_ID, listenerBinding);
// Add to Array if passed in
if (listenerToDisposeContainer && types.isArray(listenerToDisposeContainer)) {
listenerToDisposeContainer.push(unbind);
}
}
return this;
}
/**
* Removes all listeners from all elements created by the builder for the given event type.
*/
off(type: string, useCapture?: boolean): Builder;
off(typeArray: string[], useCapture?: boolean): Builder;
off(arg1: any, useCapture?: boolean): Builder {
// Event Type Array
if (types.isArray(arg1)) {
arg1.forEach((type: string) => {
this.off(type);
});
}
// Single Event Type
else {
let type = arg1;
if (useCapture) {
if (this.captureToDispose[type]) {
this.captureToDispose[type] = dispose(this.captureToDispose[type]);
}
} else {
if (this.toDispose[type]) {
this.toDispose[type] = dispose(this.toDispose[type]);
}
}
}
return this;
}
/**
* Registers listener on event types on the current element and removes
* them after first invocation.
*/
once<E extends Event = Event>(type: string, fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder;
once<E extends Event = Event>(typesArray: string[], fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder;
once<E extends Event = Event>(arg1: any, fn: (e: E, builder: Builder, unbind: IDisposable) => void, listenerToDisposeContainer?: IDisposable[], useCapture?: boolean): Builder {
// Event Type Array
if (types.isArray(arg1)) {
arg1.forEach((type: string) => {
this.once(type, fn);
});
}
// Single Event Type
else {
let type = arg1;
// Add Listener
let unbind: IDisposable = DOM.addDisposableListener(this.currentElement, type, (e) => {
fn(e, this, unbind); // Pass in Builder as Second Argument
unbind.dispose();
}, useCapture || false);
// Add to Array if passed in
if (listenerToDisposeContainer && types.isArray(listenerToDisposeContainer)) {
listenerToDisposeContainer.push(unbind);
}
}
return this;
}
/**
* This method has different characteristics based on the parameter provided:
* a) a single string passed in as argument will return the attribute value using the
* string as key from the current element of the builder.
* b) two strings passed in will set the value of an attribute identified by the first
* parameter to match the second parameter
* c) an object literal passed in will apply the properties of the literal as attributes
* to the current element of the builder.
*/
attr(name: string): string;
attr(name: string, value: string): Builder;
attr(name: string, value: boolean): Builder;
attr(name: string, value: number): Builder;
attr(attributes: any): Builder;
attr(firstP: any, secondP?: any): any {
// Apply Object Literal to Attributes of Element
if (types.isObject(firstP)) {
for (let prop in firstP) {
if (firstP.hasOwnProperty(prop)) {
let value = firstP[prop];
this.doSetAttr(prop, value);
}
}
return this;
}
// Get Attribute Value
if (types.isString(firstP) && !types.isString(secondP)) {
return this.currentElement.getAttribute(firstP);
}
// Set Attribute Value
if (types.isString(firstP)) {
if (!types.isString(secondP)) {
secondP = String(secondP);
}
this.doSetAttr(firstP, secondP);
}
return this;
}
private doSetAttr(prop: string, value: any): void {
if (prop === 'class') {
prop = 'addClass'; // Workaround for the issue that a function name can not be 'class' in ES
}
if ((<any>this)[prop]) {
if (types.isArray(value)) {
(<any>this)[prop].apply(this, value);
} else {
(<any>this)[prop].call(this, value);
}
} else {
this.currentElement.setAttribute(prop, value);
}
}
/**
* Removes an attribute by the given name.
*/
removeAttribute(prop: string): void {
this.currentElement.removeAttribute(prop);
}
/**
* Sets the id attribute to the value provided for the current HTML element of the builder.
*/
id(id: string): Builder {
this.currentElement.setAttribute('id', id);
return this;
}
/**
* Sets the title attribute to the value provided for the current HTML element of the builder.
*/
title(title: string): Builder {
this.currentElement.setAttribute('title', title);
return this;
}
/**
* Sets the type attribute to the value provided for the current HTML element of the builder.
*/
type(type: string): Builder {
this.currentElement.setAttribute('type', type);
return this;
}
/**
* Sets the value attribute to the value provided for the current HTML element of the builder.
*/
value(value: string): Builder {
this.currentElement.setAttribute('value', value);
return this;
}
/**
* Sets the tabindex attribute to the value provided for the current HTML element of the builder.
*/
tabindex(index: number): Builder {
this.currentElement.setAttribute('tabindex', index.toString());
return this;
}
/**
* This method has different characteristics based on the parameter provided:
* a) a single string passed in as argument will return the style value using the
* string as key from the current element of the builder.
* b) two strings passed in will set the style value identified by the first
* parameter to match the second parameter. The second parameter can be null
* to unset a style
* c) an object literal passed in will apply the properties of the literal as styles
* to the current element of the builder.
*/
style(name: string): string;
style(name: string, value: string): Builder;
style(attributes: any): Builder;
style(firstP: any, secondP?: any): any {
// Apply Object Literal to Styles of Element
if (types.isObject(firstP)) {
for (let prop in firstP) {
if (firstP.hasOwnProperty(prop)) {
let value = firstP[prop];
this.doSetStyle(prop, value);
}
}
return this;
}
const hasFirstP = types.isString(firstP);
// Get Style Value
if (hasFirstP && types.isUndefined(secondP)) {
return this.currentElement.style[this.cssKeyToJavaScriptProperty(firstP)];
}
// Set Style Value
else if (hasFirstP) {
this.doSetStyle(firstP, secondP);
}
return this;
}
private doSetStyle(key: string, value: string): void {
if (key.indexOf('-') >= 0) {
let segments = key.split('-');
key = segments[0];
for (let i = 1; i < segments.length; i++) {
let segment = segments[i];
key = key + segment.charAt(0).toUpperCase() + segment.substr(1);
}
}
this.currentElement.style[this.cssKeyToJavaScriptProperty(key)] = value;
}
private cssKeyToJavaScriptProperty(key: string): string {
// Automagically convert dashes as they are not allowed when programmatically
// setting a CSS style property
if (key.indexOf('-') >= 0) {
let segments = key.split('-');
key = segments[0];
for (let i = 1; i < segments.length; i++) {
let segment = segments[i];
key = key + segment.charAt(0).toUpperCase() + segment.substr(1);
}
}
// Float is special too
else if (key === 'float') {
key = 'cssFloat';
}
return key;
}
/**
* Returns the computed CSS style for the current HTML element of the builder.
*/
getComputedStyle(): CSSStyleDeclaration {
return DOM.getComputedStyle(this.currentElement);
}
/**
* Adds the variable list of arguments as class names to the current HTML element of the builder.
*/
addClass(...classes: string[]): Builder {
classes.forEach((nameValue: string) => {
let names = nameValue.split(' ');
names.forEach((name: string) => {
DOM.addClass(this.currentElement, name);
});
});
return this;
}
/**
* Sets the class name of the current HTML element of the builder to the provided className.
* If shouldAddClass is provided - for true class is added, for false class is removed.
*/
setClass(className: string, shouldAddClass: boolean = null): Builder {
if (shouldAddClass === null) {
this.currentElement.className = className;
} else if (shouldAddClass) {
this.addClass(className);
} else {
this.removeClass(className);
}
return this;
}
/**
* Returns whether the current HTML element of the builder has the provided class assigned.
*/
hasClass(className: string): boolean {
return DOM.hasClass(this.currentElement, className);
}
/**
* Removes the variable list of arguments as class names from the current HTML element of the builder.
*/
removeClass(...classes: string[]): Builder {
classes.forEach((nameValue: string) => {
let names = nameValue.split(' ');
names.forEach((name: string) => {
DOM.removeClass(this.currentElement, name);
});
});
return this;
}
/**
* Adds or removes the provided className for the current HTML element of the builder.
*/
toggleClass(className: string): Builder {
if (this.hasClass(className)) {
this.removeClass(className);
} else {
this.addClass(className);
}
return this;
}
/**
* Sets the CSS property color.
*/
color(color: string): Builder {
this.currentElement.style.color = color;
return this;
}
/**
* Sets the CSS property padding.
*/
padding(padding: string): Builder;
padding(top: number, right?: number, bottom?: number, left?: number): Builder;
padding(top: string, right?: string, bottom?: string, left?: string): Builder;
padding(top: any, right?: any, bottom?: any, left?: any): Builder {
if (types.isString(top) && top.indexOf(' ') >= 0) {
return this.padding.apply(this, top.split(' '));
}
if (!types.isUndefinedOrNull(top)) {
this.currentElement.style.paddingTop = this.toPixel(top);
}
if (!types.isUndefinedOrNull(right)) {
this.currentElement.style.paddingRight = this.toPixel(right);
}
if (!types.isUndefinedOrNull(bottom)) {
this.currentElement.style.paddingBottom = this.toPixel(bottom);
}
if (!types.isUndefinedOrNull(left)) {
this.currentElement.style.paddingLeft = this.toPixel(left);
}
return this;
}
/**
* Sets the CSS property margin.
*/
margin(margin: string): Builder;
margin(top: number, right?: number, bottom?: number, left?: number): Builder;
margin(top: string, right?: string, bottom?: string, left?: string): Builder;
margin(top: any, right?: any, bottom?: any, left?: any): Builder {
if (types.isString(top) && top.indexOf(' ') >= 0) {
return this.margin.apply(this, top.split(' '));
}
if (!types.isUndefinedOrNull(top)) {
this.currentElement.style.marginTop = this.toPixel(top);
}
if (!types.isUndefinedOrNull(right)) {
this.currentElement.style.marginRight = this.toPixel(right);
}
if (!types.isUndefinedOrNull(bottom)) {
this.currentElement.style.marginBottom = this.toPixel(bottom);
}
if (!types.isUndefinedOrNull(left)) {
this.currentElement.style.marginLeft = this.toPixel(left);
}
return this;
}
/**
* Sets the CSS property position.
*/
position(position: string): Builder;
position(top: number, right?: number, bottom?: number, left?: number, position?: string): Builder;
position(top: string, right?: string, bottom?: string, left?: string, position?: string): Builder;
position(top: any, right?: any, bottom?: any, left?: any, position?: string): Builder {
if (types.isString(top) && top.indexOf(' ') >= 0) {
return this.position.apply(this, top.split(' '));
}
if (!types.isUndefinedOrNull(top)) {
this.currentElement.style.top = this.toPixel(top);
}
if (!types.isUndefinedOrNull(right)) {
this.currentElement.style.right = this.toPixel(right);
}
if (!types.isUndefinedOrNull(bottom)) {
this.currentElement.style.bottom = this.toPixel(bottom);
}
if (!types.isUndefinedOrNull(left)) {
this.currentElement.style.left = this.toPixel(left);
}
if (!position) {
position = 'absolute';
}
this.currentElement.style.position = position;
return this;
}
/**
* Sets the CSS property size.
*/
size(size: string): Builder;
size(width: number, height?: number): Builder;
size(width: string, height?: string): Builder;
size(width: any, height?: any): Builder {
if (types.isString(width) && width.indexOf(' ') >= 0) {
return this.size.apply(this, width.split(' '));
}
if (!types.isUndefinedOrNull(width)) {
this.currentElement.style.width = this.toPixel(width);
}
if (!types.isUndefinedOrNull(height)) {
this.currentElement.style.height = this.toPixel(height);
}
return this;
}
/**
* Sets the CSS property display.
*/
display(display: string): Builder {
this.currentElement.style.display = display;
return this;
}
/**
* Shows the current element of the builder.
*/
show(): Builder {
if (this.hasClass('monaco-builder-hidden')) {
this.removeClass('monaco-builder-hidden');
}
this.attr('aria-hidden', 'false');
// Cancel any pending showDelayed() invocation
this.cancelVisibilityTimeout();
return this;
}
/**
* Shows the current builder element after the provided delay. If the builder
* was set to hidden using the hide() method before this method executed, the
* function will return without showing the current element. This is useful to
* only show the element when a specific delay is reached (e.g. for a long running
* operation.
*/
showDelayed(delay: number): Builder {
// Cancel any pending showDelayed() invocation
this.cancelVisibilityTimeout();
// Install new delay for showing
const handle = setTimeout(() => {
this.removeProperty(VISIBILITY_BINDING_ID);
this.show();
}, delay);
this.setProperty(VISIBILITY_BINDING_ID, toDisposable(() => clearTimeout(handle)));
return this;
}
/**
* Hides the current element of the builder.
*/
hide(): Builder {
if (!this.hasClass('monaco-builder-hidden')) {
this.addClass('monaco-builder-hidden');
}
this.attr('aria-hidden', 'true');
// Cancel any pending showDelayed() invocation
this.cancelVisibilityTimeout();
return this;
}
/**
* Returns true if the current element of the builder is hidden.
*/
isHidden(): boolean {
return this.hasClass('monaco-builder-hidden') || this.currentElement.style.display === 'none';
}
private cancelVisibilityTimeout(): void {
const visibilityDisposable = this.getProperty(VISIBILITY_BINDING_ID) as IDisposable;
if (visibilityDisposable) {
visibilityDisposable.dispose();
this.removeProperty(VISIBILITY_BINDING_ID);
}
}
private toPixel(obj: any): string {
if (obj.toString().indexOf('px') === -1) {
return obj.toString() + 'px';
}
return obj;
}
/**
* Sets the innerHTML attribute.
*/
innerHtml(html: string, append?: boolean): Builder {
if (append) {
this.currentElement.innerHTML += html;
} else {
this.currentElement.innerHTML = html;
}
return this;
}
/**
* Sets the textContent property of the element.
* All HTML special characters will be escaped.
*/
text(text: string, append?: boolean): Builder {
if (append) {
// children is child Elements versus childNodes includes textNodes
if (this.currentElement.children.length === 0) {
this.currentElement.textContent += text;
}
else {
// if there are elements inside this node, append the string as a new text node
// to avoid wiping out the innerHTML and replacing it with only text content
this.currentElement.appendChild(document.createTextNode(text));
}
} else {
this.currentElement.textContent = text;
}
return this;
}
/**
* Sets the innerHTML attribute in escaped form.
*/
safeInnerHtml(html: string, append?: boolean): Builder {
return this.innerHtml(strings.escape(html), append);
}
/**
* Allows to store arbritary data into the current element.
*/
setProperty(key: string, value: any): Builder {
_setPropertyOnElement(this.currentElement, key, value);
return this;
}
/**
* Allows to get arbritary data from the current element.
*/
getProperty(key: string, fallback?: any): any {
return _getPropertyFromElement(this.currentElement, key, fallback);
}
/**
* Removes a property from the current element that is stored under the given key.
*/
removeProperty(key: string): Builder {
if (hasData(this.currentElement)) {
delete data(this.currentElement)[key];
}
return this;
}
/**
* Returns a new builder with the child at the given index.
*/
child(index = 0): Builder {
let children = this.currentElement.children;
return _withElement(<HTMLElement>children.item(index));
}
/**
* Recurse through all descendant nodes and remove their data binding.
*/
private unbindDescendants(current: HTMLElement): void {
if (current && current.children) {
for (let i = 0, length = current.children.length; i < length; i++) {
let element = current.children.item(i);
// Unbind
if (hasData(<HTMLElement>element)) {
// Listeners
let listeners: IDisposable[] = data(<HTMLElement>element)[LISTENER_BINDING_ID];
if (types.isArray(listeners)) {
while (listeners.length) {
listeners.pop().dispose();
}
}
// Delete Data Slot
delete element[MS_DATA_KEY];
}
// Recurse
this.unbindDescendants(<HTMLElement>element);
}
}
}
/**
* Removes all HTML elements from the current element of the builder. Will also clean up any
* event listners registered and also clear any data binding and properties stored
* to any child element.
*/
empty(): Builder {
this.unbindDescendants(this.currentElement);
this.clearChildren();
if (this.offdom) {
this.createdElements = [];
}
return this;
}
/**
* Removes all HTML elements from the current element of the builder.
*/
clearChildren(): Builder {
// Remove Elements
if (this.currentElement) {
DOM.clearNode(this.currentElement);
}
return this;
}
/**
* Removes the current HTML element and all its children from its parent and unbinds
* all listeners and properties set to the data slots.
*/
destroy(): void {
if (this.currentElement) {
// Remove from parent
if (this.currentElement.parentNode) {
this.currentElement.parentNode.removeChild(this.currentElement);
}
// Empty to clear listeners and bindings from children
this.empty();
// Unbind
if (hasData(this.currentElement)) {
// Listeners
let listeners: IDisposable[] = data(this.currentElement)[LISTENER_BINDING_ID];
if (types.isArray(listeners)) {
while (listeners.length) {
listeners.pop().dispose();
}
}
// Delete Data Slot
delete this.currentElement[MS_DATA_KEY];
}
}
let type: string;
for (type in this.toDispose) {
if (this.toDispose.hasOwnProperty(type) && types.isArray(this.toDispose[type])) {
this.toDispose[type] = dispose(this.toDispose[type]);
}
}
for (type in this.captureToDispose) {
if (this.captureToDispose.hasOwnProperty(type) && types.isArray(this.captureToDispose[type])) {
this.captureToDispose[type] = dispose(this.captureToDispose[type]);
}
}
// Nullify fields
this.currentElement = null;
this.container = null;
this.offdom = null;
this.createdElements = null;
this.captureToDispose = null;
this.toDispose = null;
}
/**
* Removes the current HTML element and all its children from its parent and unbinds
* all listeners and properties set to the data slots.
*/
dispose(): void {
this.destroy();
}
/**
* Gets the size (in pixels) of an element, including the margin.
*/
getTotalSize(): DOM.Dimension {
let totalWidth = DOM.getTotalWidth(this.currentElement);
let totalHeight = DOM.getTotalHeight(this.currentElement);
return new DOM.Dimension(totalWidth, totalHeight);
}
/**
* Another variant of getting the inner dimensions of an element.
*/
getClientArea(): DOM.Dimension {
return DOM.getClientArea(this.currentElement);
}
}
/**
* !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*
* @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*/
export class MultiBuilder extends Builder {
length: number;
private builders: Builder[];
constructor(multiBuilder: MultiBuilder);
constructor(builder: Builder);
constructor(builders: Builder[]);
constructor(elements: HTMLElement[]);
constructor(builders: any) {
assert.ok(types.isArray(builders) || builders instanceof MultiBuilder, 'Expected Array or MultiBuilder as parameter');
super();
this.length = 0;
this.builders = [];
// Add Builders to Array
if (types.isArray(builders)) {
for (let i = 0; i < builders.length; i++) {
if (builders[i] instanceof HTMLElement) {
this.push(_withElement(builders[i]));
} else {
this.push(builders[i]);
}
}
} else {
for (let i = 0; i < (<MultiBuilder>builders).length; i++) {
this.push((<MultiBuilder>builders).item(i));
}
}
// Mixin Builder functions to operate on all builders
let $outer = this;
let propertyFn = (prop: string) => {
(<any>$outer)[prop] = function (): any {
let args = Array.prototype.slice.call(arguments);
let returnValues: any[];
let mergeBuilders = false;
for (let i = 0; i < $outer.length; i++) {
let res = (<any>$outer.item(i))[prop].apply($outer.item(i), args);
// Merge MultiBuilders into one
if (res instanceof MultiBuilder) {
if (!returnValues) {
returnValues = [];
}
mergeBuilders = true;
for (let j = 0; j < (<MultiBuilder>res).length; j++) {
returnValues.push((<MultiBuilder>res).item(j));
}
}
// Any other Return Type (e.g. boolean, integer)
else if (!types.isUndefined(res) && !(res instanceof Builder)) {
if (!returnValues) {
returnValues = [];
}
returnValues.push(res);
}
}
if (returnValues && mergeBuilders) {
return new MultiBuilder(returnValues);
}
return returnValues || $outer;
};
};
for (let prop in Builder.prototype) {
if (prop !== 'clone' && prop !== 'and') { // Skip methods that are explicitly defined in MultiBuilder
if (Builder.prototype.hasOwnProperty(prop) && types.isFunction((<any>Builder).prototype[prop])) {
propertyFn(prop);
}
}
}
}
item(i: number): Builder {
return this.builders[i];
}
push(...items: Builder[]): void {
for (let i = 0; i < items.length; i++) {
this.builders.push(items[i]);
}
this.length = this.builders.length;
}
clone(): MultiBuilder {
return new MultiBuilder(this);
}
}
function withBuilder(builder: Builder, offdom?: boolean): Builder {
if (builder instanceof MultiBuilder) {
return new MultiBuilder((<MultiBuilder>builder));
}
return new Builder(builder.getHTMLElement(), offdom);
}
export function _withElement(element: HTMLElement, offdom?: boolean): Builder {
return new Builder(element, offdom);
}
function offDOM(): Builder {
return new Builder(null, true);
}
// Binding functions
/**
* Allows to store arbritary data into element.
*/
export function _setPropertyOnElement(element: HTMLElement, key: string, value: any): void {
data(element)[key] = value;
}
/**
* Allows to get arbritary data from element.
*/
export function _getPropertyFromElement(element: HTMLElement, key: string, fallback?: any): any {
if (hasData(element)) {
let value = data(element)[key];
if (!types.isUndefined(value)) {
return value;
}
}
return fallback;
}
/**
* Adds the provided object as property to the given element. Call getBinding()
* to retrieve it again.
*/
export function _bindElement(element: HTMLElement, object: any): void {
_setPropertyOnElement(element, DATA_BINDING_ID, object);
}
let SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/;
/**
* !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*
* @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!!
*/
export const $: QuickBuilder = function (arg?: any): Builder {
// Off-DOM use
if (types.isUndefined(arg)) {
return offDOM();
}
// Falsified values cause error otherwise
if (!arg) {
throw new Error('Bad use of $');
}
// Wrap the given element
if (DOM.isHTMLElement(arg) || arg === window) {
return _withElement(arg);
}
// Wrap the given builders
if (types.isArray(arg)) {
return new MultiBuilder(arg);
}
// Wrap the given builder
if (arg instanceof Builder) {
return withBuilder((<Builder>arg));
}
if (types.isString(arg)) {
// Use the argument as HTML code
if (arg[0] === '<') {
let element: Node;
let container = document.createElement('div');
container.innerHTML = strings.format.apply(strings, arguments);
if (container.children.length === 0) {
throw new Error('Bad use of $');
}
if (container.children.length === 1) {
element = container.firstChild;
container.removeChild(element);
return _withElement(<HTMLElement>element);
}
let builders: Builder[] = [];
while (container.firstChild) {
element = container.firstChild;
container.removeChild(element);
builders.push(_withElement(<HTMLElement>element));
}
return new MultiBuilder(builders);
}
// Use the argument as a selector constructor
else if (arguments.length === 1) {
let match = SELECTOR_REGEX.exec(arg);
if (!match) {
throw new Error('Bad use of $');
}
let tag = match[1] || 'div';
let id = match[3] || undefined;
let classes = (match[4] || '').replace(/\./g, ' ');
let props: any = {};
if (id) {
props['id'] = id;
}
if (classes) {
props['class'] = classes;
}
return offDOM().element(tag, props);
}
// Use the arguments as the arguments to Builder#element(...)
else {
let result = offDOM();
result.element.apply(result, arguments);
return result;
}
} else {
throw new Error('Bad use of $');
}
};
......@@ -970,7 +970,6 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T {
const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/;
// Similar to builder, but much more lightweight
export function $<T extends HTMLElement>(description: string, attrs?: { [key: string]: any; }, ...children: (Node | string)[]): T {
let match = SELECTOR_REGEX.exec(description);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Builder, MultiBuilder, $, _bindElement, _withElement, _setPropertyOnElement, _getPropertyFromElement } from 'vs/base/browser/builder';
import * as Types from 'vs/base/common/types';
import * as DomUtils from 'vs/base/browser/dom';
import { IDisposable } from 'vs/base/common/lifecycle';
import { timeout } from 'vs/base/common/async';
function withElementById(id: string, offdom?: boolean): Builder {
let element = document.getElementById(id);
if (element) {
return new Builder(element, offdom);
}
return null;
}
const Build = {
withElementById: withElementById
};
let withElementsBySelector = function (selector: string, offdom: boolean = false) {
let elements = window.document.querySelectorAll(selector);
let builders = [];
for (let i = 0; i < elements.length; i++) {
builders.push(new Builder(<HTMLElement>elements.item(i), offdom));
}
return new MultiBuilder(builders);
};
let withBuilder = function (builder: Builder, offdom: boolean) {
if (builder instanceof MultiBuilder) {
return new MultiBuilder(builder);
}
return new Builder(builder.getHTMLElement(), offdom);
};
function select(builder: Builder, selector: string, offdom?: boolean): MultiBuilder {
let elements = builder.getHTMLElement().querySelectorAll(selector);
let builders: Builder[] = [];
for (let i = 0; i < elements.length; i++) {
builders.push(_withElement(<HTMLElement>elements.item(i), offdom));
}
return new MultiBuilder(builders);
}
suite('Builder', () => {
let fixture: HTMLElement;
let fixtureId = 'builder-fixture';
setup(() => {
fixture = document.createElement('div');
fixture.id = fixtureId;
document.body.appendChild(fixture);
});
teardown(() => {
document.body.removeChild(fixture);
});
test('Binding', function () {
let b = Build.withElementById(fixtureId, false);
let element = b.getHTMLElement();
assert(element);
// Properties
_setPropertyOnElement(element, 'foo', 'bar');
assert.strictEqual(_getPropertyFromElement(element, 'foo'), 'bar');
_setPropertyOnElement(element, 'foo', { foo: 'bar' });
assert.deepEqual(_getPropertyFromElement(element, 'foo'), { foo: 'bar' });
_setPropertyOnElement(element, 'bar', 'bar');
assert.strictEqual(_getPropertyFromElement(element, 'bar'), 'bar');
_setPropertyOnElement(element, 'bar', { foo: 'bar' });
assert.deepEqual(_getPropertyFromElement(element, 'bar'), { foo: 'bar' });
});
test('Select', function () {
let b = Build.withElementById(fixtureId, false);
assert(b);
let allDivs = withElementsBySelector('div');
assert(allDivs);
assert(allDivs.length >= 1);
assert(Types.isFunction(allDivs.push));
assert(allDivs instanceof MultiBuilder);
for (let key in b) {
if (b.hasOwnProperty(key) && Types.isFunction((b as any)[key])) {
assert(allDivs.hasOwnProperty(key));
}
}
let noElement = withElementsBySelector('#thiselementdoesnotexist');
assert(noElement);
assert(noElement.length === 0);
assert(Types.isFunction(noElement.push));
assert(noElement instanceof MultiBuilder);
for (let key in b) {
if (b.hasOwnProperty(key) && Types.isFunction((b as any)[key])) {
assert(noElement.hasOwnProperty(key));
}
}
});
test('Build.withElement()', function () {
let f = Build.withElementById(fixtureId, false);
let b = $(f.getHTMLElement());
b.addClass('foo');
assert(b.hasClass('foo'));
b.removeClass('foo');
assert(!b.hasClass('foo'));
assert.strictEqual(f.getHTMLElement(), document.getElementById(fixtureId));
assert.strictEqual(b.getHTMLElement(), document.getElementById(fixtureId));
});
test('Build.withBuilder()', function () {
let f = Build.withElementById(fixtureId, false);
let b = withBuilder(f, false);
b.addClass('foo');
assert(b.hasClass('foo'));
b.removeClass('foo');
assert(!b.hasClass('foo'));
assert.strictEqual(f.getHTMLElement(), document.getElementById(fixtureId));
assert.strictEqual(b.getHTMLElement(), document.getElementById(fixtureId));
});
test('Build.withBuilder() - Multibuilder', function () {
let f = withElementsBySelector('#' + fixtureId);
let b = withBuilder(f, false);
b.addClass('foo');
assert(b.hasClass('foo')[0]);
b.removeClass('foo');
assert(!b.hasClass('foo')[0]);
});
test('Build.offDOM()', function () {
let b = $();
assert(b);
b.div({
id: 'foobar'
}, function (div) {
div.span({
id: 'foobarspan',
innerHtml: 'foo bar'
});
});
assert(Build.withElementById('foobar') === null);
b.build(Build.withElementById(fixtureId, false));
assert(Build.withElementById('foobar'));
assert(Build.withElementById('foobarspan'));
assert.strictEqual(Build.withElementById('foobarspan').getHTMLElement().innerHTML, 'foo bar');
});
test('Build.withElementById()', function () {
let b = Build.withElementById(fixtureId, false);
b.addClass('foo');
assert(b.hasClass('foo'));
b.removeClass('foo');
assert(!b.hasClass('foo'));
assert.strictEqual(b.getHTMLElement(), document.getElementById(fixtureId));
});
test('withElementsBySelector()', function () {
let b = withElementsBySelector('#' + fixtureId, false);
b.addClass('foo');
assert(b.hasClass('foo')[0]);
b.removeClass('foo');
assert(!b.hasClass('foo')[0]);
});
test('Off DOM withElementById and container passed in', function () {
let b = Build.withElementById(fixtureId, true);
assert(b);
assert.strictEqual(b.getHTMLElement(), document.getElementById(fixtureId));
b.div({
id: 'foobar'
}, function (div) {
div.span({
id: 'foobarspan',
innerHtml: 'foo bar'
});
});
assert(Build.withElementById('foobar') === null);
b.build();
assert(Build.withElementById('foobar'));
assert(Build.withElementById('foobarspan'));
assert.strictEqual(Build.withElementById('foobarspan').getHTMLElement().innerHTML, 'foo bar');
});
test('Off DOM withSelector and container passed in', function () {
let b = withElementsBySelector('#' + fixtureId, true);
assert(b);
b.div({
id: 'foobar'
}, function (div) {
div.span({
id: 'foobarspan',
innerHtml: 'foo bar'
});
});
assert(Build.withElementById('foobar') === null);
b.build();
assert(Build.withElementById('foobar'));
assert(Build.withElementById('foobarspan'));
assert.strictEqual(Build.withElementById('foobarspan').getHTMLElement().innerHTML, 'foo bar');
});
test('Builder.build() with index specified', function () {
let b = Build.withElementById(fixtureId);
b.empty();
b.div({ id: '1' });
b.div({ id: '2' });
b.div({ id: '3' });
b = $();
b.div({ id: '4' });
b.build(Build.withElementById(fixtureId), 0);
b = Build.withElementById(fixtureId);
let divs = select(b, 'div');
assert.strictEqual(divs.length, 4);
let ids = divs.attr('id');
assert.strictEqual(ids.length, 4);
assert.strictEqual(ids[0], '4');
assert.strictEqual(ids[1], '1');
assert.strictEqual(ids[2], '2');
assert.strictEqual(ids[3], '3');
b = $();
b.div({ id: '5' });
b.build(Build.withElementById(fixtureId), 2);
b = Build.withElementById(fixtureId);
divs = select(b, 'div');
assert.strictEqual(divs.length, 5);
ids = divs.attr('id');
assert.strictEqual(ids.length, 5);
assert.strictEqual(ids[0], '4');
assert.strictEqual(ids[1], '1');
assert.strictEqual(ids[2], '5');
assert.strictEqual(ids[3], '2');
assert.strictEqual(ids[4], '3');
});
test('Builder.asContainer()', function () {
let f = Build.withElementById(fixtureId, false);
f.div({
id: 'foobar'
});
let divBuilder = f.asContainer();
divBuilder.span({
innerHtml: 'see man'
});
});
test('Builder.clone()', function () {
let b = Build.withElementById(fixtureId);
let clone = b.clone();
assert(clone);
assert(clone instanceof Builder);
assert.strictEqual(b.getHTMLElement(), clone.getHTMLElement());
assert.deepEqual(b, clone);
let multiB = withElementsBySelector('div');
let multiClone = multiB.clone();
assert(multiClone);
});
test('Builder Multibuilder fn call that returns Multibuilder', function () {
let b = Build.withElementById(fixtureId);
b.div(function (div: Builder) {
div.span();
});
b.div(function (div: Builder) {
div.span();
});
b.div(function (div: Builder) {
div.span();
});
let multiBuilder = select(Build.withElementById(fixtureId), 'div');
assert(multiBuilder.length === 3);
});
test('Builder.p() and other elements', function () {
let b = Build.withElementById(fixtureId);
b.empty();
b.div(function (div: Builder) {
assert(div !== b);
assert.strictEqual('div', div.getHTMLElement().nodeName.toLowerCase());
div.p(function (p: Builder) {
p.ul(function (ul: Builder) {
ul.li(function (li: Builder) {
li.span({
id: 'builderspan',
innerHtml: 'Foo Bar'
});
assert.strictEqual('span', li.getHTMLElement().nodeName.toLowerCase());
li.img({
id: 'builderimg',
src: '#'
});
assert.strictEqual('img', li.getHTMLElement().nodeName.toLowerCase());
li.a({
id: 'builderlink',
href: '#',
innerHtml: 'Link'
});
assert.strictEqual('a', li.getHTMLElement().nodeName.toLowerCase());
});
});
});
assert.strictEqual('p', div.getHTMLElement().nodeName.toLowerCase());
});
assert.strictEqual(select(Build.withElementById(fixtureId), 'div').length, 1);
assert.strictEqual(select(Build.withElementById(fixtureId), '*').length, 7);
assert.strictEqual(Build.withElementById('builderspan').getHTMLElement().innerHTML, 'Foo Bar');
assert.strictEqual(Build.withElementById('builderimg').attr('src'), '#');
assert.strictEqual(Build.withElementById('builderlink').attr('href'), '#');
// Assert HTML through DOM
let root = document.getElementById(fixtureId);
assert.strictEqual(root.childNodes.length, 1);
let div = root.childNodes[0];
assert.strictEqual('div', div.nodeName.toLowerCase());
assert.strictEqual(b.getHTMLElement(), div);
assert.strictEqual(div.childNodes.length, 1);
let p = div.childNodes[0];
assert.strictEqual('p', p.nodeName.toLowerCase());
assert.strictEqual(p.childNodes.length, 1);
let ul = p.childNodes[0];
assert.strictEqual('ul', ul.nodeName.toLowerCase());
assert.strictEqual(ul.childNodes.length, 1);
let li = ul.childNodes[0];
assert.strictEqual('li', li.nodeName.toLowerCase());
assert.strictEqual(li.childNodes.length, 3);
let span = <HTMLElement>li.childNodes[0];
assert.strictEqual('span', span.nodeName.toLowerCase());
assert.strictEqual(span.childNodes.length, 1);
assert.strictEqual(span.innerHTML, 'Foo Bar');
let img = <HTMLElement>li.childNodes[1];
assert.strictEqual('img', img.nodeName.toLowerCase());
assert.strictEqual(img.childNodes.length, 0);
assert.strictEqual(img.getAttribute('src'), '#');
let a = <HTMLElement>li.childNodes[2];
assert.strictEqual('a', a.nodeName.toLowerCase());
assert.strictEqual(a.childNodes.length, 1);
assert.strictEqual(a.getAttribute('href'), '#');
assert.strictEqual(a.innerHTML, 'Link');
});
test('Builder.p() and other elements', function () {
let b = Build.withElementById(fixtureId);
b.element('div', function (div: Builder) {
div.element('p', function (p: Builder) {
p.element('ul', function (ul: Builder) {
ul.element('li', function (li: Builder) {
li.element('span', {
id: 'builderspan',
innerHtml: 'Foo Bar'
});
li.element('img', {
id: 'builderimg',
src: '#'
});
li.element('a', {
id: 'builderlink',
href: '#',
innerHtml: 'Link'
});
});
});
});
});
assert.strictEqual(select(Build.withElementById(fixtureId), 'div').length, 1);
assert.strictEqual(select(Build.withElementById(fixtureId), '*').length, 7);
assert.strictEqual(Build.withElementById('builderspan').getHTMLElement().innerHTML, 'Foo Bar');
assert.strictEqual(Build.withElementById('builderimg').attr('src'), '#');
assert.strictEqual(Build.withElementById('builderlink').attr('href'), '#');
});
test('Builder.attr()', function () {
let b = Build.withElementById(fixtureId);
b.div();
assert(!b.attr('id'));
b.attr('id', 'foobar');
assert.strictEqual(b.attr('id'), 'foobar');
b.attr({
id: 'barfoo',
padding: [4, 3, 2, 1],
margin: '4px 3px 2px 1px'
});
assert.strictEqual(b.attr('id'), 'barfoo');
assert.strictEqual(b.getHTMLElement().getAttribute('id'), 'barfoo');
assert.strictEqual(b.style('margin-top'), '4px');
assert.strictEqual(b.getHTMLElement().style.marginTop, '4px');
assert.strictEqual(b.style('margin-right'), '3px');
assert.strictEqual(b.style('margin-bottom'), '2px');
assert.strictEqual(b.style('margin-left'), '1px');
assert.strictEqual(b.style('padding-top'), '4px');
assert.strictEqual(b.style('padding-right'), '3px');
assert.strictEqual(b.style('padding-bottom'), '2px');
assert.strictEqual(b.style('padding-left'), '1px');
b.attr({
padding: '1 2 3 4',
position: '100 200 300 400',
size: '200 300'
});
assert.strictEqual(b.style('padding-top'), '1px');
assert.strictEqual(b.style('padding-right'), '2px');
assert.strictEqual(b.style('padding-bottom'), '3px');
assert.strictEqual(b.style('padding-left'), '4px');
assert.strictEqual(b.style('top'), '100px');
assert.strictEqual(b.style('right'), '200px');
assert.strictEqual(b.style('bottom'), '300px');
assert.strictEqual(b.style('left'), '400px');
assert.strictEqual(b.style('width'), '200px');
assert.strictEqual(b.style('height'), '300px');
});
test('Builder.style()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.style('padding-bottom', '5px');
b.style('paddingTop', '4px');
assert.strictEqual(b.style('paddingBottom'), '5px');
assert.strictEqual(b.style('padding-bottom'), '5px');
assert.strictEqual(b.style('paddingTop'), '4px');
assert.strictEqual(b.style('padding-top'), '4px');
});
test('Builder.style() as object literal', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.style({
'padding-bottom': '5px',
paddingTop: '4px',
border: '1px solid red'
});
assert.strictEqual(b.getHTMLElement().style.paddingBottom, '5px');
assert.strictEqual(b.style('paddingBottom'), '5px');
assert.strictEqual(b.style('padding-bottom'), '5px');
assert.strictEqual(b.style('paddingTop'), '4px');
assert.strictEqual(b.style('padding-top'), '4px');
assert.strictEqual(b.style('border-width'), '1px');
assert.strictEqual(b.style('border-style'), 'solid');
assert.strictEqual(b.style('border-color'), 'red');
});
test('Builder.attributes', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.id('foobar');
b.title('foobar');
b.type('foobar');
b.value('foobar');
b.tabindex(0);
assert.strictEqual(b.attr('id'), 'foobar');
assert.strictEqual(b.attr('title'), 'foobar');
assert.strictEqual(b.attr('type'), 'foobar');
assert.strictEqual(b.attr('value'), 'foobar');
assert.strictEqual(b.attr('tabindex'), '0');
assert.strictEqual(b.getHTMLElement().getAttribute('id'), 'foobar');
assert.strictEqual(b.getHTMLElement().getAttribute('title'), 'foobar');
assert.strictEqual(b.getHTMLElement().getAttribute('type'), 'foobar');
assert.strictEqual(b.getHTMLElement().getAttribute('value'), 'foobar');
assert.strictEqual(b.getHTMLElement().getAttribute('tabindex'), '0');
});
test('Builder.addClass() and Co', function () {
let b = Build.withElementById(fixtureId);
b.div();
assert(!b.hasClass('foobar'));
assert(!b.getHTMLElement().className);
b.addClass('foobar');
assert(b.getComputedStyle());
assert(b.hasClass('foobar'));
assert.strictEqual(b.getHTMLElement().className, 'foobar');
b.removeClass('foobar');
assert(!b.hasClass('foobar'));
assert(!b.getHTMLElement().className);
assert(!b.hasClass('foobar'));
b.attr({ 'class': 'foobar' });
assert(b.hasClass('foobar'));
assert.strictEqual(b.getHTMLElement().className, 'foobar');
b.removeClass('foobar');
assert(!b.hasClass('foobar'));
assert(!b.getHTMLElement().className);
b.addClass('foobar').addClass('barfoo').addClass('foobar');
assert(b.hasClass('barfoo'));
assert(b.hasClass('foobar'));
b.removeClass('foobar').removeClass('barfoo');
assert(!b.hasClass('barfoo'));
assert(!b.hasClass('foobar'));
assert(!b.getHTMLElement().className);
});
test('Builder.padding() and .margin()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.padding(4, 3, 2, 1).margin(1, 2, 3, 4);
assert.strictEqual(b.style('padding-top'), '4px');
assert.strictEqual(b.style('padding-right'), '3px');
assert.strictEqual(b.style('padding-bottom'), '2px');
assert.strictEqual(b.style('padding-left'), '1px');
assert.strictEqual(b.style('margin-top'), '1px');
assert.strictEqual(b.style('margin-right'), '2px');
assert.strictEqual(b.style('margin-bottom'), '3px');
assert.strictEqual(b.style('margin-left'), '4px');
assert(b.getComputedStyle());
});
test('Builder.position()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.position(100, 200, 300, 400, 'relative');
assert.strictEqual(b.style('top'), '100px');
assert.strictEqual(b.style('right'), '200px');
assert.strictEqual(b.style('bottom'), '300px');
assert.strictEqual(b.style('left'), '400px');
assert.strictEqual(b.style('position'), 'relative');
});
test('Builder.size(), .minSize() and .maxSize()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.size(100, 200);
assert.strictEqual(b.style('width'), '100px');
assert.strictEqual(b.style('height'), '200px');
});
test('Builder.show() and .hide()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.show();
assert(!b.hasClass('monaco-builder-hidden'));
assert(!b.isHidden());
b.hide();
assert(b.isHidden());
b.show();
b.hide();
assert(b.hasClass('monaco-builder-hidden'));
assert(b.isHidden());
});
test('Builder.showDelayed()', function () {
let b = Build.withElementById(fixtureId);
b.div().hide();
b.showDelayed(20);
assert(b.hasClass('monaco-builder-hidden'));
return timeout(30).then(() => {
assert(!b.hasClass('monaco-builder-hidden'));
});
});
test('Builder.showDelayed() but interrupted', function () {
let b = Build.withElementById(fixtureId);
b.div().hide();
b.showDelayed(20);
assert(b.hasClass('monaco-builder-hidden'));
b.hide(); // Should cancel the visibility promise
return timeout(30).then(() => {
assert(b.hasClass('monaco-builder-hidden'));
});
});
test('Builder.innerHtml()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.innerHtml('<b>Foo Bar</b>');
assert.strictEqual(b.getHTMLElement().innerHTML, '<b>Foo Bar</b>');
});
test('Builder.safeInnerHtml()', function () {
let b = Build.withElementById(fixtureId);
b.div();
b.safeInnerHtml('<b>Foo Bar</b>');
assert.strictEqual(b.getHTMLElement().innerHTML, '&lt;b&gt;Foo Bar&lt;/b&gt;');
b.safeInnerHtml('Foo Bar');
assert.strictEqual(b.getHTMLElement().innerHTML, 'Foo Bar');
});
test('Build Client Area', function () {
// Global
let dimensions = $(document.body).getClientArea();
assert(dimensions.width > 0);
assert(dimensions.height > 0);
// Local
let b = Build.withElementById(fixtureId);
dimensions = b.getClientArea();
// assert(dimensions.width >= 0);
// assert(dimensions.height >= 0);
});
test('Builder.once()', function () {
let b = Build.withElementById(fixtureId);
b.element('input', {
type: 'button'
});
let counter = 0;
b.once(DomUtils.EventType.CLICK, function (e) {
counter++;
assert(counter <= 1);
});
b.getHTMLElement().click();
b.getHTMLElement().click();
});
test('Builder.once() with capture', function () {
let b = Build.withElementById(fixtureId);
b.element('input', {
type: 'button'
});
let counter = 0;
b.once(DomUtils.EventType.CLICK, function (e) {
counter++;
assert(counter <= 1);
}, null, true);
b.getHTMLElement().click();
b.getHTMLElement().click();
});
test('Builder.on() and .off()', function () {
let b = Build.withElementById(fixtureId);
b.element('input', {
type: 'button'
});
let listeners: Builder[] = [];
let counter = 0;
b.on(DomUtils.EventType.CLICK, function (e) {
counter++;
}, listeners);
assert(listeners.length === 1);
b.getHTMLElement().click();
b.off(DomUtils.EventType.BLUR);
b.getHTMLElement().click();
b.off(DomUtils.EventType.CLICK);
b.getHTMLElement().click();
b.getHTMLElement().click();
assert.equal(counter, 2);
});
test('Builder.on() and .off() with capture', function () {
let b = Build.withElementById(fixtureId);
b.element('input', {
type: 'button'
});
let listeners: Builder[] = [];
let counter = 0;
b.on(DomUtils.EventType.CLICK, function (e) {
counter++;
}, listeners, true);
assert(listeners.length === 1);
b.getHTMLElement().click();
b.off(DomUtils.EventType.BLUR);
b.getHTMLElement().click();
b.off(DomUtils.EventType.BLUR, true);
b.getHTMLElement().click();
b.off(DomUtils.EventType.CLICK);
b.getHTMLElement().click();
b.off(DomUtils.EventType.CLICK, true);
b.getHTMLElement().click();
b.getHTMLElement().click();
assert(counter === 4);
});
test('Builder.empty()', function () {
let inputs: Builder[] = [];
let bindings: Builder[] = [];
let b = Build.withElementById(fixtureId);
let counter1 = 0;
let counter2 = 0;
let counter3 = 0;
let counter4 = 0;
let counter5 = 0;
let counter6 = 0;
let counter7 = 0;
b.div(function (div: Builder) {
_bindElement(div.getHTMLElement(), 'Foo Bar');
div.setProperty('Foo', 'Bar');
bindings.push(div.clone());
div.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function () {
counter1++;
assert(counter1 <= 1);
});
inputs.push(div.clone());
div.p(function (p: Builder) {
_bindElement(p.getHTMLElement(), 'Foo Bar');
p.setProperty('Foo', 'Bar');
bindings.push(p.clone());
p.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function () {
counter2++;
assert(counter2 <= 1);
});
inputs.push(p.clone());
p.ul(function (ul: Builder) {
_bindElement(ul.getHTMLElement(), 'Foo Bar');
ul.setProperty('Foo', 'Bar');
bindings.push(ul.clone());
ul.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter3++;
assert(counter3 <= 1);
});
inputs.push(ul.clone());
ul.li(function (li: Builder) {
_bindElement(li.getHTMLElement(), 'Foo Bar');
li.setProperty('Foo', 'Bar');
bindings.push(li.clone());
li.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter4++;
assert(counter4 <= 1);
});
inputs.push(li.clone());
li.span({
id: 'builderspan',
innerHtml: 'Foo Bar'
}, function (span) {
_bindElement(span.getHTMLElement(), 'Foo Bar');
span.setProperty('Foo', 'Bar');
bindings.push(span.clone());
span.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter5++;
assert(counter5 <= 1);
});
inputs.push(span.clone());
});
li.img({
id: 'builderimg',
src: '#'
}, function (img) {
_bindElement(img.getHTMLElement(), 'Foo Bar');
img.setProperty('Foo', 'Bar');
bindings.push(img.clone());
img.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter6++;
assert(counter6 <= 1);
});
inputs.push(img.clone());
});
li.a({
id: 'builderlink',
href: '#',
innerHtml: 'Link'
}, function (a) {
_bindElement(a.getHTMLElement(), 'Foo Bar');
a.setProperty('Foo', 'Bar');
bindings.push(a.clone());
a.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter7++;
assert(counter7 <= 1);
});
inputs.push(a.clone());
});
});
});
});
});
inputs.forEach(function (input) {
input.getHTMLElement().click();
});
for (let i = 0; i < bindings.length; i++) {
assert(bindings[i].getProperty('Foo'));
}
Build.withElementById(fixtureId).empty();
assert(select(Build.withElementById(fixtureId), '*').length === 0);
inputs.forEach(function (input) {
input.getHTMLElement().click();
});
for (let i = 0; i < bindings.length; i++) {
assert(!bindings[i].getProperty('Foo'));
}
assert.equal(counter1, 1);
assert.equal(counter2, 1);
assert.equal(counter3, 1);
assert.equal(counter4, 1);
assert.equal(counter5, 1);
assert.equal(counter6, 1);
assert.equal(counter7, 1);
});
test('Builder.empty() cleans all listeners', function () {
let b = Build.withElementById(fixtureId);
let unbindCounter = 0;
let old = DomUtils.addDisposableListener;
try {
(DomUtils as any).addDisposableListener = function (node: any, type: any, handler: any) {
let unbind: IDisposable = old.call(null, node, type, handler);
return {
dispose: function () {
unbindCounter++;
unbind.dispose();
}
};
};
b.div(function (div: Builder) {
div.p(function (p: Builder) {
p.span().on([DomUtils.EventType.CLICK, DomUtils.EventType.KEY_DOWN], function (e) { });
p.img().on([DomUtils.EventType.KEY_PRESS, DomUtils.EventType.MOUSE_OUT], function (e) { }, null, true); // useCapture
p.a(function (a: Builder) {
a.span().on([DomUtils.EventType.CLICK, DomUtils.EventType.KEY_DOWN], function (e) { });
}).on([DomUtils.EventType.SELECT, DomUtils.EventType.BLUR], function (e) { });
});
});
b.empty();
assert.strictEqual(unbindCounter, 8);
} finally {
(DomUtils as any).addDisposableListener = old;
}
});
test('Builder.destroy()', function () {
let inputs: Builder[] = [];
let bindings: Builder[] = [];
let b = Build.withElementById(fixtureId);
let counter1 = 0;
let counter2 = 0;
let counter3 = 0;
let counter4 = 0;
let counter5 = 0;
let counter6 = 0;
let counter7 = 0;
b.div(function (div: Builder) {
_bindElement(div.getHTMLElement(), 'Foo Bar');
div.setProperty('Foo', 'Bar');
bindings.push(div.clone());
div.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter1++;
assert(counter1 <= 1);
}, null, true); // useCapture
inputs.push(div.clone());
div.p(function (p: Builder) {
_bindElement(p.getHTMLElement(), 'Foo Bar');
p.setProperty('Foo', 'Bar');
bindings.push(p.clone());
p.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter2++;
assert(counter2 <= 1);
});
inputs.push(p.clone());
p.ul(function (ul: Builder) {
_bindElement(ul.getHTMLElement(), 'Foo Bar');
ul.setProperty('Foo', 'Bar');
bindings.push(ul.clone());
ul.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter3++;
assert(counter3 <= 1);
});
inputs.push(ul.clone());
ul.li(function (li: Builder) {
_bindElement(li.getHTMLElement(), 'Foo Bar');
li.setProperty('Foo', 'Bar');
bindings.push(li.clone());
li.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function () {
counter4++;
assert(counter4 <= 1);
});
inputs.push(li.clone());
li.span({
id: 'builderspan',
innerHtml: 'Foo Bar'
}, function (span) {
_bindElement(span.getHTMLElement(), 'Foo Bar');
span.setProperty('Foo', 'Bar');
bindings.push(span.clone());
span.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter5++;
assert(counter5 <= 1);
});
inputs.push(span.clone());
});
li.img({
id: 'builderimg',
src: '#'
}, function (img) {
_bindElement(img.getHTMLElement(), 'Foo Bar');
img.setProperty('Foo', 'Bar');
bindings.push(img.clone());
img.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter6++;
assert(counter6 <= 1);
});
inputs.push(img.clone());
});
li.a({
id: 'builderlink',
href: '#',
innerHtml: 'Link'
}, function (a) {
_bindElement(a.getHTMLElement(), 'Foo Bar');
a.setProperty('Foo', 'Bar');
bindings.push(a.clone());
a.element('input', {
type: 'button'
}).on(DomUtils.EventType.CLICK, function (e) {
counter7++;
assert(counter7 <= 1);
});
inputs.push(a.clone());
});
});
});
});
});
inputs.forEach(function (input) {
input.getHTMLElement().click();
});
for (let i = 0; i < bindings.length; i++) {
assert(bindings[i].getProperty('Foo'));
}
select(Build.withElementById(fixtureId), 'div').destroy();
assert(select(Build.withElementById(fixtureId), '*').length === 0);
inputs.forEach(function (input) {
input.getHTMLElement().click();
});
for (let i = 0; i < bindings.length; i++) {
assert(!bindings[i].getProperty('Foo'));
}
assert.equal(counter1, 1);
assert.equal(counter2, 1);
assert.equal(counter3, 1);
assert.equal(counter4, 1);
assert.equal(counter5, 1);
assert.equal(counter6, 1);
assert.equal(counter7, 1);
});
test('Builder.destroy() cleans all listeners', function () {
let b = Build.withElementById(fixtureId);
let unbindCounter = 0;
let old = DomUtils.addDisposableListener;
try {
(DomUtils as any).addDisposableListener = function (node: any, type: any, handler: any) {
let unbind: IDisposable = old.call(null, node, type, handler);
return {
dispose: function () {
unbindCounter++;
unbind.dispose();
}
};
};
b.div(function (div: Builder) {
div.p(function (p: Builder) {
p.span().on([DomUtils.EventType.CLICK, DomUtils.EventType.KEY_DOWN], function (e) { });
p.img().on([DomUtils.EventType.KEY_PRESS, DomUtils.EventType.MOUSE_OUT], function (e) { });
p.a(function (a: Builder) {
a.span().on([DomUtils.EventType.CLICK, DomUtils.EventType.KEY_DOWN], function (e) { });
}).on([DomUtils.EventType.SELECT, DomUtils.EventType.BLUR], function (e) { });
});
})
.on([DomUtils.EventType.CLICK, DomUtils.EventType.KEY_DOWN], function (e) { })
.on([DomUtils.EventType.BLUR, DomUtils.EventType.FOCUS], function (e) { }, null, true); //useCapture
b.destroy();
assert.strictEqual(unbindCounter, 16);
} finally {
(DomUtils as any).addDisposableListener = old;
}
});
test('Builder.offDOM()', function () {
let b = Build.withElementById(fixtureId);
b.div({ id: '1' });
assert(Build.withElementById('1'));
b.offDOM();
assert(!Build.withElementById('1'));
});
test('$ - selector construction', function () {
let obj = $('div');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'div');
assert.equal(obj.getHTMLElement().id, '');
assert.equal(obj.getHTMLElement().className, '');
obj = $('#myid');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'div');
assert.equal(obj.getHTMLElement().id, 'myid');
assert.equal(obj.getHTMLElement().className, '');
obj = $('.myclass');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'div');
assert.equal(obj.getHTMLElement().id, '');
assert.equal(obj.getHTMLElement().className, 'myclass');
obj = $('.myclass.element');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'div');
assert.equal(obj.getHTMLElement().id, '');
assert.equal(obj.getHTMLElement().className, 'myclass element');
obj = $('#myid.element');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'div');
assert.equal(obj.getHTMLElement().id, 'myid');
assert.equal(obj.getHTMLElement().className, 'element');
obj = $('ul#myid');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'ul');
assert.equal(obj.getHTMLElement().id, 'myid');
assert.equal(obj.getHTMLElement().className, '');
obj = $('header#monaco.container');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'header');
assert.equal(obj.getHTMLElement().id, 'monaco');
assert.equal(obj.getHTMLElement().className, 'container');
obj = $('header#monaco.container.box');
assert(obj instanceof Builder);
assert(DomUtils.isHTMLElement(obj.getHTMLElement()));
assert.equal(obj.getHTMLElement().tagName.toLowerCase(), 'header');
assert.equal(obj.getHTMLElement().id, 'monaco');
assert.equal(obj.getHTMLElement().className, 'container box');
});
test('$ - wrap elements and builders', function () {
let obj = $('#' + fixtureId);
assert(obj instanceof Builder);
obj = $(obj.getHTMLElement());
assert(obj instanceof Builder);
obj = $(obj);
assert(obj instanceof Builder);
});
test('$ - delegate to #element', function () {
let obj = $('a', { 'class': 'a1', innerHtml: 'Hello' });
assert(obj instanceof Builder);
let el = obj.getHTMLElement();
assert.equal(el.tagName.toLowerCase(), 'a');
assert.equal(el.className, 'a1');
assert.equal(el.innerHTML, 'Hello');
});
test('$ - html', function () {
let obj = $('<a class="a1">Hello</a>');
assert(obj instanceof Builder);
let el = obj.getHTMLElement();
assert.equal(el.tagName.toLowerCase(), 'a');
assert.equal(el.className, 'a1');
assert.equal(el.innerHTML, 'Hello');
});
test('$ - multiple html tags', function () {
let objs = <MultiBuilder>$('<a class="a1">Hello</a><a class="a2">There</a>');
assert(objs instanceof MultiBuilder);
assert.equal(objs.length, 2);
let obj = objs.item(0).getHTMLElement();
assert.equal(obj.tagName.toLowerCase(), 'a');
assert.equal(obj.className, 'a1');
assert.equal(obj.innerHTML, 'Hello');
obj = objs.item(1).getHTMLElement();
assert.equal(obj.tagName.toLowerCase(), 'a');
assert.equal(obj.className, 'a2');
assert.equal(obj.innerHTML, 'There');
});
test('$ - html format', function () {
let objs = <MultiBuilder>(<any>$)('<a class="{0}">{1}</a><a class="{2}">{3}</a>', 'a1', 'Hello', 'a2', 'There');
assert(objs instanceof MultiBuilder);
assert.equal(objs.length, 2);
let obj = objs.item(0).getHTMLElement();
assert.equal(obj.tagName.toLowerCase(), 'a');
assert.equal(obj.className, 'a1');
assert.equal(obj.innerHTML, 'Hello');
obj = objs.item(1).getHTMLElement();
assert.equal(obj.tagName.toLowerCase(), 'a');
assert.equal(obj.className, 'a2');
assert.equal(obj.innerHTML, 'There');
});
test('$ - exceptions', function () {
assert.throws(function () { $(''); });
assert.throws(function () { $(<any>123); });
});
test('$ - appendTo, append', function () {
let peel = $('<div class="peel"></div>');
let core = $('<span class="core"></span>').appendTo(peel);
let obj = peel.getHTMLElement();
assert(obj);
assert.equal(obj.tagName.toLowerCase(), 'div');
assert.equal(obj.className, 'peel');
assert.equal(obj.children.length, 1);
assert(obj.firstChild);
assert.equal((<HTMLElement>obj.firstChild).children.length, 0);
assert.equal((<HTMLElement>obj.firstChild).tagName.toLowerCase(), 'span');
assert.equal((<HTMLElement>obj.firstChild).className, 'core');
obj = core.getHTMLElement();
assert.equal(obj.children.length, 0);
assert.equal(obj.tagName.toLowerCase(), 'span');
assert.equal(obj.className, 'core');
peel = $('<div class="peel"></div>').append($('<span class="core"></span>'));
obj = peel.getHTMLElement();
assert(obj);
assert.equal(obj.tagName.toLowerCase(), 'div');
assert.equal(obj.className, 'peel');
assert.equal(obj.children.length, 1);
assert(obj.firstChild);
assert.equal((<HTMLElement>obj.firstChild).children.length, 0);
assert.equal((<HTMLElement>obj.firstChild).tagName.toLowerCase(), 'span');
assert.equal((<HTMLElement>obj.firstChild).className, 'core');
});
});
......@@ -71,7 +71,7 @@ export abstract class Composite extends Component implements IComposite {
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Called to create this composite on the provided builder. This method is only
* Called to create this composite on the provided parent. This method is only
* called once during the lifetime of the workbench.
* Note that DOM-dependent calculations should be performed from the setVisible()
* call. Only then the composite will be part of the DOM.
......
......@@ -244,12 +244,14 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
this.isVisible = true;
dom.show(this.$el);
dom.removeClass(this.$el, 'debug-toolbar-hidden');
this.setCoordinates();
}
private hide(): void {
this.isVisible = false;
dom.hide(this.$el);
dom.addClass(this.$el, 'debug-toolbar-hidden');
}
public static getActions(allActions: AbstractDebugAction[], toDispose: IDisposable[], debugService: IDebugService, keybindingService: IKeybindingService, instantiationService: IInstantiationService): AbstractDebugAction[] {
......
......@@ -23,7 +23,7 @@ const BREAKPOINT_GLYPH = '.debug-breakpoint';
const PAUSE = `.debug-actions-widget .debug-action.pause`;
const DEBUG_STATUS_BAR = `.statusbar.debugging`;
const NOT_DEBUG_STATUS_BAR = `.statusbar:not(debugging)`;
const TOOLBAR_HIDDEN = `.debug-actions-widget.monaco-builder-hidden`;
const TOOLBAR_HIDDEN = `.debug-actions-widget.debug-toolbar-hidden`;
const STACK_FRAME = `${VIEWLET} .monaco-tree-row .stack-frame`;
const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title*="${filename}"]`;
const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册