mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
definitions fixed
This commit is contained in:
7
ui/builder/builder.d.ts
vendored
Normal file
7
ui/builder/builder.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
//@private
|
||||
declare module "ui/builder" {
|
||||
import view = require("ui/core/view");
|
||||
|
||||
export function load(fileName: string, exports?: any): view.View;
|
||||
export function parse(value: string, exports?: any): view.View;
|
||||
}
|
||||
219
ui/builder/builder.ts
Normal file
219
ui/builder/builder.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import view = require("ui/core/view");
|
||||
import fs = require("file-system");
|
||||
import xml = require("xml");
|
||||
import file_access_module = require("file-system/file-system-access");
|
||||
import types = require("utils/types");
|
||||
import componentBuilder = require("ui/builder/component-builder");
|
||||
import templateBuilderDef = require("ui/builder/template-builder");
|
||||
|
||||
var KNOWNCOLLECTIONS = "knownCollections";
|
||||
|
||||
export function parse(value: string, exports: any): view.View {
|
||||
var viewToReturn: view.View;
|
||||
var componentModule = parseInternal(value, exports);
|
||||
|
||||
if (componentModule) {
|
||||
viewToReturn = componentModule.component;
|
||||
}
|
||||
|
||||
return viewToReturn;
|
||||
}
|
||||
|
||||
function parseInternal(value: string, exports: any): componentBuilder.ComponentModule {
|
||||
var rootComponentModule: componentBuilder.ComponentModule;
|
||||
// Temporary collection used for parent scope.
|
||||
var parents = new Array<componentBuilder.ComponentModule>();
|
||||
var complexProperties = new Array<ComplexProperty>();
|
||||
|
||||
var templateBuilder: templateBuilderDef.TemplateBuilder;
|
||||
|
||||
// Parse the XML.
|
||||
var xmlParser = new xml.XmlParser((args: xml.ParserEvent) => {
|
||||
|
||||
if (templateBuilder) {
|
||||
if (args.eventType === xml.ParserEventType.StartElement) {
|
||||
templateBuilder.addStartElement(args.elementName, args.attributes);
|
||||
} else if (args.eventType === xml.ParserEventType.EndElement) {
|
||||
if (templateBuilder.elementName !== args.elementName) {
|
||||
templateBuilder.addEndElement(args.elementName);
|
||||
} else {
|
||||
templateBuilder.build();
|
||||
templateBuilder = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current parent.
|
||||
var parent = parents[parents.length - 1];
|
||||
var complexProperty = complexProperties[complexProperties.length - 1];
|
||||
|
||||
// Create component instance from every element declaration.
|
||||
if (args.eventType === xml.ParserEventType.StartElement) {
|
||||
if (isComplexProperty(args.elementName)) {
|
||||
|
||||
var name = getComplexProperty(args.elementName);
|
||||
|
||||
complexProperties.push({
|
||||
parent: parent,
|
||||
name: name,
|
||||
items: [],
|
||||
});
|
||||
|
||||
if (templateBuilderDef.isKnownTemplate(name, parent.exports)) {
|
||||
templateBuilder = new templateBuilderDef.TemplateBuilder({
|
||||
parent: parent,
|
||||
name: name,
|
||||
elementName: args.elementName,
|
||||
templateItems: []
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
var componentModule: componentBuilder.ComponentModule;
|
||||
|
||||
if (args.namespace) {
|
||||
// Custom components
|
||||
var xmlPath = fs.path.join(fs.knownFolders.currentApp().path, args.namespace, args.elementName) + ".xml";
|
||||
if (fs.File.exists(xmlPath)) {
|
||||
// Custom components with XML
|
||||
var jsPath = xmlPath.replace(".xml", ".js");
|
||||
var subExports;
|
||||
if (fs.File.exists(jsPath)) {
|
||||
// Custom components with XML and code
|
||||
subExports = require(jsPath.replace(".js", ""))
|
||||
}
|
||||
componentModule = loadInternal(xmlPath, subExports);
|
||||
|
||||
} else {
|
||||
// Custom components without XML
|
||||
componentModule = componentBuilder.getComponentModule(args.elementName, args.namespace, args.attributes, exports);
|
||||
}
|
||||
} else {
|
||||
// Default components
|
||||
componentModule = componentBuilder.getComponentModule(args.elementName, args.namespace, args.attributes, exports);
|
||||
}
|
||||
|
||||
if (componentModule) {
|
||||
if (parent) {
|
||||
if (componentModule.component instanceof view.View) {
|
||||
if (complexProperty) {
|
||||
// Add to complex property to component.
|
||||
addToComplexProperty(parent, complexProperty, componentModule)
|
||||
} else if ((<any>parent.component)._addChildFromBuilder) {
|
||||
// Add component to visual tree
|
||||
(<any>parent.component)._addChildFromBuilder(args.elementName, componentModule.component);
|
||||
}
|
||||
} else if (complexProperty) {
|
||||
// Add component to complex property of parent component.
|
||||
addToComplexProperty(parent, complexProperty, componentModule);
|
||||
}
|
||||
} else if (parents.length === 0) {
|
||||
// Set root component.
|
||||
rootComponentModule = componentModule;
|
||||
}
|
||||
|
||||
// Add the component instance to the parents scope collection.
|
||||
parents.push(componentModule);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (args.eventType === xml.ParserEventType.EndElement) {
|
||||
if (isComplexProperty(args.elementName)) {
|
||||
if (complexProperty) {
|
||||
if (parent && (<any>parent.component)._addArrayFromBuilder) {
|
||||
// If parent is AddArrayFromBuilder call the interface method to populate the array property.
|
||||
(<any>parent.component)._addArrayFromBuilder(complexProperty.name, complexProperty.items);
|
||||
complexProperty.items = [];
|
||||
}
|
||||
}
|
||||
// Remove the last complexProperty from the complexProperties collection (move to the previous complexProperty scope).
|
||||
complexProperties.pop();
|
||||
|
||||
} else {
|
||||
// Remove the last parent from the parents collection (move to the previous parent scope).
|
||||
parents.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}, (e) => {
|
||||
throw new Error("XML parse error: " + e.message);
|
||||
}, true);
|
||||
|
||||
xmlParser.parse(value.replace('xmlns="http://www.nativescript.org/tns.xsd"', "").replace("xmlns='http://www.nativescript.org/tns.xsd'", ""));
|
||||
|
||||
return rootComponentModule;
|
||||
}
|
||||
|
||||
export function load(fileName: string, exports: any): view.View {
|
||||
var viewToReturn: view.View;
|
||||
var componentModule = loadInternal(fileName, exports);
|
||||
|
||||
if (componentModule) {
|
||||
viewToReturn = componentModule.component;
|
||||
}
|
||||
|
||||
return viewToReturn;
|
||||
}
|
||||
|
||||
function loadInternal(fileName: string, exports: any): componentBuilder.ComponentModule {
|
||||
var componentModule: componentBuilder.ComponentModule;
|
||||
|
||||
// Check if the XML file exists.
|
||||
if (fileName && fs.File.exists(fileName)) {
|
||||
|
||||
var fileAccess = new file_access_module.FileSystemAccess();
|
||||
|
||||
// Read the XML file.
|
||||
fileAccess.readText(fileName, result => {
|
||||
componentModule = parseInternal(result, exports);
|
||||
}, (e) => {
|
||||
throw new Error("Error loading file " + fileName + " :" + e.message);
|
||||
});
|
||||
}
|
||||
|
||||
if (componentModule && componentModule.component) {
|
||||
// Save exports to root component (will be used for templates).
|
||||
(<any>componentModule.component).exports = exports;
|
||||
}
|
||||
|
||||
return componentModule;
|
||||
}
|
||||
|
||||
function isComplexProperty(name: string): boolean {
|
||||
return types.isString(name) && name.indexOf(".") !== -1;
|
||||
}
|
||||
|
||||
function getComplexProperty(fullName: string): string {
|
||||
var name: string;
|
||||
|
||||
if (types.isString(fullName)) {
|
||||
var names = fullName.split(".");
|
||||
name = names[names.length - 1];
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function isKnownCollection(name: string, exports: any): boolean {
|
||||
return KNOWNCOLLECTIONS in exports && exports[KNOWNCOLLECTIONS] && name in exports[KNOWNCOLLECTIONS];
|
||||
}
|
||||
|
||||
function addToComplexProperty(parent: componentBuilder.ComponentModule, complexProperty, elementModule: componentBuilder.ComponentModule) {
|
||||
// If property name is known collection we populate array with elements.
|
||||
var parentComponent = <any>parent.component;
|
||||
if (isKnownCollection(complexProperty.name, parent.exports)) {
|
||||
complexProperty.items.push(elementModule.component);
|
||||
} else if (parentComponent._addChildFromBuilder) {
|
||||
parentComponent._addChildFromBuilder("", elementModule.component);
|
||||
} else {
|
||||
// Or simply assign the value;
|
||||
parentComponent[complexProperty.name] = elementModule.component;
|
||||
}
|
||||
}
|
||||
|
||||
interface ComplexProperty {
|
||||
parent: componentBuilder.ComponentModule;
|
||||
name: string;
|
||||
items?: Array<any>;
|
||||
}
|
||||
11
ui/builder/component-builder.d.ts
vendored
Normal file
11
ui/builder/component-builder.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//@private
|
||||
declare module "ui/builder/component-builder" {
|
||||
import view = require("ui/core/view");
|
||||
|
||||
export function getComponentModule(elementName: string, namespace: string, attributes: Object, exports: Object): ComponentModule;
|
||||
|
||||
export interface ComponentModule {
|
||||
component: view.View;
|
||||
exports: any;
|
||||
}
|
||||
}
|
||||
176
ui/builder/component-builder.ts
Normal file
176
ui/builder/component-builder.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import observable = require("data/observable");
|
||||
import view = require("ui/core/view");
|
||||
import bindable = require("ui/core/bindable");
|
||||
import dockLayoutDef = require("ui/layouts/dock-layout");
|
||||
import gridLayoutModule = require("ui/layouts/grid-layout");
|
||||
import absoluteLayoutDef = require("ui/layouts/absolute-layout");
|
||||
import types = require("utils/types");
|
||||
import definition = require("ui/builder/component-builder");
|
||||
import fs = require("file-system");
|
||||
import gestures = require("ui/gestures");
|
||||
|
||||
var KNOWNEVENTS = "knownEvents";
|
||||
var UI_PATH = "ui/";
|
||||
var MODULES = {
|
||||
"ActivityIndicator": "ui/activity-indicator",
|
||||
"ListView": "ui/list-view",
|
||||
"GridLayout": "ui/layouts/grid-layout",
|
||||
"DockLayout": "ui/layouts/dock-layout",
|
||||
"WrapLayout": "ui/layouts/wrap-layout",
|
||||
"AbsoluteLayout": "ui/layouts/absolute-layout",
|
||||
"StackLayout": "ui/layouts/stack-layout",
|
||||
"ScrollView": "ui/scroll-view",
|
||||
"SearchBar": "ui/search-bar",
|
||||
"SlideOut": "ui/slide-out",
|
||||
"TabView": "ui/tab-view",
|
||||
"TabEntry": "ui/tab-view",
|
||||
"TextField": "ui/text-field",
|
||||
"TextView": "ui/text-view",
|
||||
"FormattedString": "text/formatted-string",
|
||||
"Span": "text/span",
|
||||
"WebView": "ui/web-view",
|
||||
"SegmentedBar": "ui/segmented-bar",
|
||||
"SegmentedBarEntry": "ui/segmented-bar",
|
||||
"TimePicker": "ui/time-picker",
|
||||
"DatePicker": "ui/date-picker",
|
||||
"ListPicker": "ui/list-picker",
|
||||
};
|
||||
|
||||
var ROW = "row";
|
||||
var COL = "col";
|
||||
var COL_SPAN = "colSpan";
|
||||
var ROW_SPAN = "rowSpan";
|
||||
var DOCK = "dock";
|
||||
var LEFT = "left";
|
||||
var TOP = "top";
|
||||
|
||||
export function getComponentModule(elementName: string, namespace: string, attributes: Object, exports: Object): definition.ComponentModule {
|
||||
var instance: view.View;
|
||||
var instanceModule: Object;
|
||||
var componentModule: definition.ComponentModule;
|
||||
|
||||
// Get module id.
|
||||
var moduleId = MODULES[elementName] || UI_PATH + elementName.toLowerCase();
|
||||
|
||||
try {
|
||||
// Require module by module id.
|
||||
instanceModule = require(types.isString(namespace) && fs.path.join(fs.knownFolders.currentApp().path, namespace) || moduleId);
|
||||
|
||||
// Get the component type from module.
|
||||
var instanceType = instanceModule[elementName] || Object;
|
||||
|
||||
// Create instance of the component.
|
||||
instance = new instanceType();
|
||||
} catch (ex) {
|
||||
throw new Error("Cannot create module " + moduleId + ". " + ex);
|
||||
}
|
||||
|
||||
if (instance && instanceModule) {
|
||||
var bindings = new Array<bindable.BindingOptions>();
|
||||
|
||||
for (var attr in attributes) {
|
||||
|
||||
var attrValue = attributes[attr];
|
||||
|
||||
if (isBinding(attrValue) && instance.bind) {
|
||||
if (isKnownEvent(attr, instanceModule)) {
|
||||
// Get the event handler from instance.bindingContext.
|
||||
var propertyChangeHandler = (args: observable.PropertyChangeData) => {
|
||||
if (args.propertyName === "bindingContext") {
|
||||
var handler = instance.bindingContext && instance.bindingContext[getPropertyNameFromBinding(attrValue)];
|
||||
// Check if the handler is function and add it to the instance for specified event name.
|
||||
if (types.isFunction(handler)) {
|
||||
instance.on(attr, handler, instance.bindingContext);
|
||||
}
|
||||
instance.off(observable.knownEvents.propertyChange, propertyChangeHandler);
|
||||
}
|
||||
};
|
||||
|
||||
instance.on(observable.knownEvents.propertyChange, propertyChangeHandler);
|
||||
} else {
|
||||
instance.bind(getBinding(instance, attr, attrValue));
|
||||
}
|
||||
} else if (isKnownEvent(attr, instanceModule)) {
|
||||
// Get the event handler from page module exports.
|
||||
var handler = exports && exports[attrValue];
|
||||
|
||||
// Check if the handler is function and add it to the instance for specified event name.
|
||||
if (types.isFunction(handler)) {
|
||||
instance.on(attr, handler);
|
||||
}
|
||||
} else if (isGesture(attr, instance)) {
|
||||
// Get the event handler from page module exports.
|
||||
var gestureHandler = exports && exports[attrValue];
|
||||
|
||||
// Check if the handler is function and add it to the instance for specified gesture.
|
||||
if (types.isFunction(gestureHandler)) {
|
||||
instance.observe(gestures.fromString(attr.toLowerCase()), gestureHandler);
|
||||
}
|
||||
} else if (attr === ROW) {
|
||||
gridLayoutModule.GridLayout.setRow(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} else if (attr === COL) {
|
||||
gridLayoutModule.GridLayout.setColumn(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} else if (attr === COL_SPAN) {
|
||||
gridLayoutModule.GridLayout.setColumnSpan(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} else if (attr === ROW_SPAN) {
|
||||
gridLayoutModule.GridLayout.setRowSpan(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} if (attr === LEFT) {
|
||||
absoluteLayoutDef.AbsoluteLayout.setLeft(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} else if (attr === TOP) {
|
||||
absoluteLayoutDef.AbsoluteLayout.setTop(instance, !isNaN(+attrValue) && +attrValue);
|
||||
} else if (attr === DOCK) {
|
||||
dockLayoutDef.DockLayout.setDock(instance, attrValue);
|
||||
} else {
|
||||
var attrHandled = false;
|
||||
|
||||
if ((<any>instance).applyXmlAttribute) {
|
||||
attrHandled = (<any>instance).applyXmlAttribute(attr, attrValue);
|
||||
}
|
||||
|
||||
if (!attrHandled) {
|
||||
// Try to convert value to number.
|
||||
var valueAsNumber = +attrValue;
|
||||
if (!isNaN(valueAsNumber)) {
|
||||
instance[attr] = valueAsNumber;
|
||||
} else if (attrValue && (attrValue.toLowerCase() === "true" || attrValue.toLowerCase() === "false")) {
|
||||
instance[attr] = attrValue.toLowerCase() === "true" ? true : false;
|
||||
} else {
|
||||
instance[attr] = attrValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentModule = { component: instance, exports: instanceModule, bindings: bindings };
|
||||
}
|
||||
|
||||
return componentModule;
|
||||
}
|
||||
|
||||
function isGesture(name: string, instance: any): boolean {
|
||||
return gestures.fromString(name.toLowerCase()) !== undefined;
|
||||
}
|
||||
|
||||
function isKnownEvent(name: string, exports: any): boolean {
|
||||
return (KNOWNEVENTS in exports && name in exports[KNOWNEVENTS]) ||
|
||||
(KNOWNEVENTS in view && name in view[KNOWNEVENTS]);
|
||||
}
|
||||
|
||||
function getBinding(instance: view.View, name: string, value: string): bindable.BindingOptions {
|
||||
return { targetProperty: name, sourceProperty: getPropertyNameFromBinding(value), twoWay: true };
|
||||
}
|
||||
|
||||
function getPropertyNameFromBinding(value: string): string {
|
||||
return value.replace("{{", "").replace("}}", "").trim();
|
||||
}
|
||||
|
||||
function isBinding(value: string): boolean {
|
||||
var isBinding;
|
||||
|
||||
if (types.isString(value)) {
|
||||
var str = value.trim();
|
||||
isBinding = str.indexOf("{{") === 0 && str.lastIndexOf("}}") === str.length - 2;
|
||||
}
|
||||
|
||||
return isBinding;
|
||||
}
|
||||
2
ui/builder/package.json
Normal file
2
ui/builder/package.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{ "name" : "builder",
|
||||
"main" : "builder.js" }
|
||||
22
ui/builder/template-builder.d.ts
vendored
Normal file
22
ui/builder/template-builder.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
//@private
|
||||
declare module "ui/builder/template-builder" {
|
||||
import componentBuilder = require("ui/builder/component-builder");
|
||||
|
||||
class TemplateBuilder {
|
||||
constructor(templateProperty: TemplateProperty);
|
||||
|
||||
elementName: string;
|
||||
addStartElement(elementName: string, attributes: Object);
|
||||
addEndElement(elementName: string);
|
||||
build();
|
||||
}
|
||||
|
||||
export function isKnownTemplate(name: string, exports: any): boolean;
|
||||
|
||||
interface TemplateProperty {
|
||||
parent: componentBuilder.ComponentModule;
|
||||
name: string;
|
||||
elementName: string;
|
||||
templateItems: Array<string>
|
||||
}
|
||||
}
|
||||
45
ui/builder/template-builder.ts
Normal file
45
ui/builder/template-builder.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import definition = require("ui/builder/template-builder");
|
||||
|
||||
var KNOWNTEMPLATES = "knownTemplates";
|
||||
|
||||
export class TemplateBuilder {
|
||||
private _items: Array<string>;
|
||||
private _templateProperty: definition.TemplateProperty;
|
||||
|
||||
constructor(templateProperty: definition.TemplateProperty) {
|
||||
this._items = new Array<string>();
|
||||
this._templateProperty = templateProperty;
|
||||
}
|
||||
|
||||
public get elementName(): string {
|
||||
return this._templateProperty.elementName;
|
||||
}
|
||||
|
||||
public addStartElement(elementName: string, attributes: Object) {
|
||||
this._items.push("<" + elementName + (attributes ? " " + getAttributesAsString(attributes) + ">" : ">"));
|
||||
}
|
||||
|
||||
public addEndElement(elementName: string) {
|
||||
this._items.push("</" + elementName + ">");
|
||||
}
|
||||
|
||||
public build() {
|
||||
if (this._templateProperty.name in this._templateProperty.parent.component) {
|
||||
this._templateProperty.parent.component[this._templateProperty.name] = this._items.join("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isKnownTemplate(name: string, exports: any): boolean {
|
||||
return KNOWNTEMPLATES in exports && exports[KNOWNTEMPLATES] && name in exports[KNOWNTEMPLATES];
|
||||
}
|
||||
|
||||
function getAttributesAsString(attributes: Object): string {
|
||||
var result = [];
|
||||
|
||||
for (var item in attributes) {
|
||||
result.push(item + '="' + attributes[item] + '"');
|
||||
}
|
||||
|
||||
return result.join(" ");
|
||||
}
|
||||
Reference in New Issue
Block a user