diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 690a20ed5..43aa68733 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -302,6 +302,7 @@
trace.d.ts
+
list-picker.d.ts
diff --git a/apps/tests/ui/bindable-tests.ts b/apps/tests/ui/bindable-tests.ts
index 8cc886578..e02cfe016 100644
--- a/apps/tests/ui/bindable-tests.ts
+++ b/apps/tests/ui/bindable-tests.ts
@@ -9,6 +9,7 @@ import buttonModule = require("ui/button");
import utils = require("utils/utils");
import pageModule = require("ui/page");
import stackLayoutModule = require("ui/layouts/stack-layout");
+import bindingBuilder = require("ui/builder/binding-builder");
//
// For information and examples how to use bindings please refer to special [**Data binding**](../../../../bindings.md) topic.
@@ -383,7 +384,7 @@ export var test_Bindable_BindingContext_String_DoesNotThrow = function () {
export var test_getBindableOptionsFromStringFullFormat = function () {
var bindingExpression = "bindProperty, bindProperty * 2, false";
- var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
@@ -393,7 +394,7 @@ export var test_getBindableOptionsFromStringFullFormat = function () {
export var test_getBindableOptionsFromStringShortFormatExpression = function () {
var bindingExpression = "bindProperty * 2";
- var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
@@ -403,17 +404,56 @@ export var test_getBindableOptionsFromStringShortFormatExpression = function ()
export var test_getBindableOptionsFromStringShortFormatProperty = function () {
var bindingExpression = "bindProperty";
- var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
- TKUnit.assert(bindOptions.expression === null, "Expected: null, Actual: " + bindOptions.expression);
+ TKUnit.assert(bindOptions.expression === undefined, "Expected: null, Actual: " + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringTwoParamsFormat = function () {
var bindingExpression = "bindProperty, bindProperty * 2";
- var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
+
+ TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
+ TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
+ TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
+ TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
+}
+
+export var test_getBindableOptionsFromStringFullNamedFormat = function () {
+ var bindingExpression = "bindProperty, expression = bindProperty * 2, twoWay = false";
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
+
+ TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
+ TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
+ TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
+ TKUnit.assert(bindOptions.twoWay === false, "Expected: false, Actual: " + bindOptions.twoWay);
+}
+
+export var test_getBindableOptionsFromStringShortNamedFormatExpression = function () {
+ var bindingExpression = "sourceProperty = bindProperty, expression = bindProperty * 2";
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
+
+ TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
+ TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
+ TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual: " + bindOptions.expression);
+ TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
+}
+
+export var test_getBindableOptionsFromStringShortNamedFormatProperty = function () {
+ var bindingExpression = "sourceProperty = bindProperty";
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
+ TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
+ TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
+ TKUnit.assert(bindOptions.expression === undefined, "Expected: null, Actual: " + bindOptions.expression);
+ TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
+}
+
+export var test_getBindableOptionsFromStringTwoParamsNamedFormat = function () {
+ var bindingExpression = "bindProperty, expression = bindProperty * 2";
+ var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
diff --git a/ui/builder/binding-builder.ts b/ui/builder/binding-builder.ts
new file mode 100644
index 000000000..90512c4cc
--- /dev/null
+++ b/ui/builder/binding-builder.ts
@@ -0,0 +1,154 @@
+var expressionSymbolsRegex = /[ \+\-\*%\?:<>=!\|&\(\)\[\]]/;
+
+export module bindingConstants {
+ export var sourceProperty = "sourceProperty";
+ export var targetProperty = "targetProperty";
+ export var expression = "expression";
+ export var twoWay = "twoWay";
+ export var source = "source";
+};
+
+var hasEqualSignRegex = /=+/;
+var equalSignComparisionOperatorsRegex = /(==|===|>=|<=|!=|!==)/;
+function isNamedParam(value) {
+ var equalSignIndex = value.search(hasEqualSignRegex);
+ if (equalSignIndex > -1) {
+ var equalSignSurround = value.substr(equalSignIndex > 0 ? equalSignIndex - 1 : 0, 3);
+ if (equalSignSurround.search(equalSignComparisionOperatorsRegex) === -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function areNamedParams(params: Array) {
+ var i;
+ for (i = 0; i < params.length; i++) {
+ if (isNamedParam(params[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+var namedParamConstants = {
+ propName: "propName",
+ propValue: "propValue"
+}
+
+function getPropertyNameValuePair(param, knownOptions, callback) {
+ var nameValuePair = {};
+ var propertyName = param.substr(0, param.indexOf("=")).trim();
+ var propertyValue = param.substr(param.indexOf("=") + 1).trim();
+ if (knownOptions) {
+ if (!propertyName) {
+ propertyName = knownOptions.defaultProperty;
+ }
+ else {
+ propertyName = propertyName in knownOptions ? propertyName : null;
+ }
+ }
+
+ if (propertyName) {
+ if (callback) {
+ nameValuePair = callback(propertyName, propertyValue);
+ }
+ else {
+ nameValuePair[namedParamConstants.propName] = propertyName;
+ nameValuePair[namedParamConstants.propValue] = propertyValue;
+ }
+ return nameValuePair;
+ }
+
+ return null;
+}
+
+function parseNamedProperties(parameterList, knownOptions, callback) {
+ var result = {};
+ var i;
+ for (i = 0; i < parameterList.length; i++) {
+ var nameValuePair = getPropertyNameValuePair(parameterList[i], knownOptions, callback);
+ if (nameValuePair) {
+ result[nameValuePair[namedParamConstants.propName]] = nameValuePair[namedParamConstants.propValue];
+ }
+ }
+ return result;
+}
+
+function extractPropertyNameFromExpression(expression: string): string {
+ var firstExpressionSymbolIndex = expression.search(expressionSymbolsRegex);
+ if (firstExpressionSymbolIndex > -1) {
+ return expression.substr(0, firstExpressionSymbolIndex).trim();
+ }
+ else {
+ return expression;
+ }
+}
+
+export function getBindingOptions(name: string, value: string): any {
+ var namedParams = [];
+ var params = value.split(",");
+ if (!areNamedParams(params)) {
+ if (params.length === 1) {
+ namedParams.push(bindingConstants.sourceProperty + " = " + extractPropertyNameFromExpression(params[0].trim()));
+ var expression = params[0].search(expressionSymbolsRegex) > -1 ? params[0].trim() : null;
+ if (expression) {
+ namedParams.push(bindingConstants.expression + " = " + expression);
+ }
+ namedParams.push(bindingConstants.twoWay + " = true");
+ }
+ else {
+ namedParams.push(bindingConstants.sourceProperty + " = " + extractPropertyNameFromExpression(params[0].trim()));
+ namedParams.push(bindingConstants.expression + " = " + params[1].trim());
+ var twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
+ namedParams.push(bindingConstants.twoWay + " = " + twoWay);
+ }
+ }
+ else {
+ namedParams = params;
+ }
+
+ var bindingPropertyHandler = function (prop, value) {
+ var result = {};
+ result[namedParamConstants.propName] = prop;
+ if (prop === bindingConstants.twoWay) {
+ // create a real boolean value
+ if (value === "true") {
+ result[namedParamConstants.propValue] = true;
+ }
+ else {
+ result[namedParamConstants.propValue] = false;
+ }
+ }
+ else {
+ result[namedParamConstants.propValue] = value;
+ }
+
+ return result;
+ };
+
+ var bindingOptionsParameters = parseNamedProperties(namedParams, xmlBindingProperties, bindingPropertyHandler);
+ var bindOptions = {
+ targetProperty: name
+ };
+
+ for (var prop in bindingOptionsParameters) {
+ if (bindingOptionsParameters.hasOwnProperty(prop)) {
+ bindOptions[prop] = bindingOptionsParameters[prop];
+ }
+ }
+
+ if (bindOptions[bindingConstants.twoWay] === undefined) {
+ bindOptions[bindingConstants.twoWay] = true;
+ }
+
+ return bindOptions;
+}
+
+var xmlBindingProperties = {
+ sourceProperty: true,
+ expression: true,
+ twoWay: true,
+ source: true,
+ defaultProperty: bindingConstants.sourceProperty
+};
\ No newline at end of file
diff --git a/ui/builder/component-builder.ts b/ui/builder/component-builder.ts
index fa136035a..5a9b646a0 100644
--- a/ui/builder/component-builder.ts
+++ b/ui/builder/component-builder.ts
@@ -8,6 +8,7 @@ import types = require("utils/types");
import definition = require("ui/builder/component-builder");
import fs = require("file-system");
import gestures = require("ui/gestures");
+import bindingBuilder = require("ui/builder/binding-builder");
var KNOWNEVENTS = "knownEvents";
var UI_PATH = "ui/";
@@ -76,7 +77,13 @@ export function getComponentModule(elementName: string, namespace: string, attri
if (isKnownEvent(attr, instanceModule)) {
attachEventBinding(instance, attr, attrValue);
} else {
- instance.bind(getBinding(instance, attr, attrValue));
+ var bindOptions = bindingBuilder.getBindingOptions(attr, getBindingExpressionFromAttribute(attrValue));
+ instance.bind({
+ sourceProperty : bindOptions[bindingBuilder.bindingConstants.sourceProperty],
+ targetProperty: bindOptions[bindingBuilder.bindingConstants.targetProperty],
+ expression: bindOptions[bindingBuilder.bindingConstants.expression],
+ twoWay: bindOptions[bindingBuilder.bindingConstants.twoWay]
+ }, bindOptions[bindingBuilder.bindingConstants.source]);
}
} else if (isKnownEvent(attr, instanceModule)) {
// Get the event handler from page module exports.
@@ -160,10 +167,6 @@ function isKnownEvent(name: string, exports: any): boolean {
(KNOWNEVENTS in view && name in view[KNOWNEVENTS]);
}
-function getBinding(instance: view.View, name: string, value: string): bindable.BindingOptions {
- return bindable.Bindable._getBindingOptions(name, getBindingExpressionFromAttribute(value));
-}
-
function getBindingExpressionFromAttribute(value: string): string {
return value.replace("{{", "").replace("}}", "").trim();
}
@@ -177,4 +180,4 @@ function isBinding(value: string): boolean {
}
return isBinding;
-}
+}
\ No newline at end of file
diff --git a/ui/core/bindable.d.ts b/ui/core/bindable.d.ts
index 87828ff89..3f4666f41 100644
--- a/ui/core/bindable.d.ts
+++ b/ui/core/bindable.d.ts
@@ -66,7 +66,6 @@
unbind(property: string);
//@private
- static _getBindingOptions(name: string, bindingExpression: string): BindingOptions;
_updateTwoWayBinding(propertyName: string, value: any);
_onBindingContextChanged(oldValue: any, newValue: any);
//@endprivate
diff --git a/ui/core/bindable.ts b/ui/core/bindable.ts
index ea6e1a091..0dd0d4eb5 100644
--- a/ui/core/bindable.ts
+++ b/ui/core/bindable.ts
@@ -6,8 +6,6 @@ import types = require("utils/types");
import trace = require("trace");
import polymerExpressions = require("js-libs/polymer-expressions");
-var expressionSymbolsRegex = /[ \+\-\*%\?:<>=!\|&\(\)\[\]]/;
-
var bindingContextProperty = new dependencyObservable.Property(
"bindingContext",
"Bindable",
@@ -116,38 +114,6 @@ export class Bindable extends dependencyObservable.DependencyObservable implemen
}
}
}
-
- private static extractPropertyNameFromExpression(expression: string): string {
- var firstExpressionSymbolIndex = expression.search(expressionSymbolsRegex);
- if (firstExpressionSymbolIndex > -1) {
- return expression.substr(0, firstExpressionSymbolIndex).trim();
- }
- else {
- return expression;
- }
- }
-
- public static _getBindingOptions(name: string, bindingExpression: string): definition.BindingOptions {
- var result: definition.BindingOptions;
- result = {
- targetProperty: name,
- sourceProperty: ""
- };
- if (types.isString(bindingExpression)) {
- var params = bindingExpression.split(",");
- if (params.length === 1) {
- result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
- result.expression = params[0].search(expressionSymbolsRegex) > -1 ? params[0].trim() : null;
- result.twoWay = true;
- }
- else {
- result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
- result.expression = params[1].trim();
- result.twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
- }
- }
- return result;
- }
}
export class Binding {