alloy
Version:
TiDev Titanium MVC Framework
227 lines (203 loc) • 6.79 kB
JavaScript
var _ = require('lodash'),
styler = require('../styler'),
U = require('../../../utils'),
CU = require('../compilerUtils'),
CONST = require('../../../common/constants'),
logger = require('../../../logger');
var PROXY_PROPERTIES = [
'_ProxyProperty._Lists.HeaderView',
'_ProxyProperty._Lists.FooterView',
'_ProxyProperty._Lists.HeaderPullView',
'_ProxyProperty._Lists.Search'
];
var SEARCH_PROPERTIES = [
'Ti.UI.SearchBar',
'Ti.UI.Android.SearchView'
];
var REFRESH_PROPERTY = 'Ti.UI.RefreshControl';
var VALID = [
'Ti.UI.TableViewRow',
'Ti.UI.TableViewSection',
'Ti.UI.iOS.PreviewContext'
];
var ALL_VALID = _.union(PROXY_PROPERTIES, SEARCH_PROPERTIES, [REFRESH_PROPERTY], VALID);
exports.parse = function(node, state) {
return require('./base').parse(node, state, parse);
};
function parse(node, state, args) {
var children = U.XML.getElementsFromNodes(node.childNodes),
code = '',
itemCode = '',
isDataBound = args[CONST.BIND_COLLECTION] ? true : false,
extras = [],
proxyProperties = {},
localModel, arrayName, controllerSymbol;
if (state.parentFormFactor || node.hasAttribute('formFactor')) {
// if this node or a parent has set the formFactor attribute
// we need to pass it to the data binding generator
args.parentFormFactor = (state.parentFormFactor || node.getAttribute('formFactor'));
}
// iterate through all children of the TableView
_.each(children, function(child) {
var config = CU.getCompilerConfig(),
platform = config && config.alloyConfig ? config.alloyConfig.platform : undefined;
if (child.nodeName === 'SearchView' && platform !== 'android') {
if (child.getAttribute('platform') !== 'android') {
logger.warn([
'<SearchView> is only available in Android',
'To get rid of this warning, add platform="android" to your <SearchView> element'
]);
}
return;
}
if (child.nodeName === 'SearchView' && !child.hasAttribute('ns')) {
child.setAttribute('ns', 'Ti.UI.Android');
}
var fullname = CU.getNodeFullname(child),
theNode = CU.validateNodeName(child, ALL_VALID),
isSearchBar = false,
isRefreshControl = false,
isProxyProperty = false,
isControllerNode = false,
hasUiNodes = false;
// validate the child element and determine if it's part of
// the table data, a searchbar, or a proxy property assigment
if (!theNode) {
U.dieWithNode(child, 'Ti.UI.TableView child elements must be one of the following: [' + ALL_VALID.join(',') + ']');
} else if (!CU.isNodeForCurrentPlatform(child)) {
return;
} else if (_.includes(CONST.CONTROLLER_NODES, fullname)) {
isControllerNode = true;
} else if (REFRESH_PROPERTY === theNode) {
isRefreshControl = true;
} else if (_.includes(SEARCH_PROPERTIES, theNode)) {
isSearchBar = true;
} else if (_.includes(PROXY_PROPERTIES, theNode)) {
isProxyProperty = true;
}
// manually handle controller node proxy properties
if (isControllerNode) {
// set up any proxy properties at the top-level of the controller
var inspect = CU.inspectRequireNode(child);
_.each(_.uniq(inspect.names), function(name) {
if (_.includes(PROXY_PROPERTIES, name)) {
var propertyName = U.proxyPropertyNameFromFullname(name);
proxyProperties[propertyName] = '<%= controllerSymbol %>.getProxyPropertyEx("' +
propertyName + '", {recurse:true})';
} else {
hasUiNodes = true;
}
});
}
// generate code for proxy property assignments
if (isProxyProperty) {
code += CU.generateNodeExtended(child, state, {
parent: {},
post: function(node, _state, _args) {
if (_args.formFactor) {
state.styles.push({
isId: true,
key: args.id,
queries: { formFactor: _args.formFactor },
style: styler.createVariableStyle(_state.propertyName, _state.parent.symbol)
});
} else {
proxyProperties[U.proxyPropertyNameFromFullname(fullname)] = _state.parent.symbol;
}
}
});
// generate code for search bar
} else if (isSearchBar) {
code += CU.generateNodeExtended(child, state, {
parent: {},
post: function(node, state, args) {
proxyProperties.search = state.parent.symbol;
}
});
// generate code for refreshControl
} else if (isRefreshControl) {
code += CU.generateNodeExtended(child, state, {
parent: {},
post: function(node, state, args) {
proxyProperties.refreshControl = state.parent.symbol;
}
});
// are there UI elements yet to process?
} else if (hasUiNodes || !isControllerNode) {
// generate data binding code
if (isDataBound) {
localModel = localModel || CU.generateUniqueId();
itemCode += CU.generateNodeExtended(child, state, {
parent: {},
local: true,
model: localModel,
post: function(node, state, args) {
controllerSymbol = state.controller;
return 'rows.push(' + state.parent.symbol + ');\n';
}
});
// standard row/section processing
} else {
if (!arrayName) {
arrayName = CU.generateUniqueId();
code += 'var ' + arrayName + '=[];';
}
code += CU.generateNodeExtended(child, state, {
parent: {},
post: function(node, state, args) {
controllerSymbol = state.controller;
return arrayName + '.push(' + state.parent.symbol + ');';
}
});
}
// if there's no UI nodes inside, just generate it
} else if (!hasUiNodes && isControllerNode) {
code += CU.generateNodeExtended(child, state, {
parent: {},
post: function(node, state, args) {
controllerSymbol = state.controller;
}
});
}
// fill in proxy property templates, if present
if (isControllerNode) {
_.each(proxyProperties, function(v, k) {
proxyProperties[k] = _.template(v)({
controllerSymbol: controllerSymbol
});
});
}
});
// add data at creation time
if (arrayName) { extras.push(['data', arrayName]); }
// add all proxy properties at creation time
_.each(proxyProperties, function(v, k) {
extras.push([k, v]);
});
// if we got any extras, add them to the state
if (extras.length) { state.extraStyle = styler.createVariableStyle(extras); }
// generate the code for the table itself
if (isDataBound) {
_.each(CONST.BIND_PROPERTIES, function(p) {
node.removeAttribute(p);
});
}
var tableState = require('./default').parse(node, state);
code += tableState.code;
// finally, fill in any model-view binding code, if present
if (isDataBound) {
localModel = localModel || CU.generateUniqueId();
code += _.template(CU.generateCollectionBindingTemplate(args))({
localModel: localModel,
pre: 'var rows=[];',
items: itemCode,
post: tableState.parent.symbol + '.setData(rows);'
});
}
// Update the parsing state
return {
parent: {},
styles: state.styles,
code: code
};
}