diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 786adf343..8dcc19ea0 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -80,6 +80,7 @@
data-binding.xml
+
@@ -183,8 +184,8 @@
- nordic.xml
-
+ nordic.xml
+
@@ -1984,7 +1985,7 @@
False
-
+
\ No newline at end of file
diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts
index 83cb56918..1e84be3b6 100644
--- a/apps/tests/testRunner.ts
+++ b/apps/tests/testRunner.ts
@@ -7,7 +7,7 @@ import uiTestModule = require("./ui-test");
frameModule.Frame.defaultAnimatedNavigation = false;
-function isRunningOnEmulator(): boolean {
+export function isRunningOnEmulator(): boolean {
// This checks are not good enough to be added to modules but keeps unittests green.
if (platform.device.os === platform.platformNames.android) {
@@ -61,6 +61,7 @@ allTests["IMAGE"] = require("./ui/image/image-tests");
allTests["SLIDER"] = require("./ui/slider/slider-tests");
allTests["SWITCH"] = require("./ui/switch/switch-tests");
allTests["PROGRESS"] = require("./ui/progress/progress-tests");
+allTests["PLACEHOLDER"] = require("./ui/placeholder/placeholder-tests");
allTests["PAGE"] = require("./ui/page/page-tests");
allTests["LISTVIEW"] = require("./ui/list-view/list-view-tests");
allTests["ACTIVITY-INDICATOR"] = require("./ui/activity-indicator/activity-indicator-tests");
diff --git a/apps/tests/ui/list-view/list-view-tests.ts b/apps/tests/ui/list-view/list-view-tests.ts
index de6a53567..3b6db9736 100644
--- a/apps/tests/ui/list-view/list-view-tests.ts
+++ b/apps/tests/ui/list-view/list-view-tests.ts
@@ -6,6 +6,7 @@ import observable = require("data/observable");
import types = require("utils/types");
import platform = require("platform");
import utils = require("utils/utils");
+import testRunner = require("../../testRunner");
//
// # ListView
@@ -633,6 +634,9 @@ export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
}
export function test_no_memory_leak_when_items_is_regular_array() {
+ if (testRunner.isRunningOnEmulator()) {
+ return;
+ }
var createFunc = function (): listViewModule.ListView {
var listView = new listViewModule.ListView();
listView.items = FEW_ITEMS;
@@ -645,6 +649,9 @@ export function test_no_memory_leak_when_items_is_regular_array() {
}
export function test_no_memory_leak_when_items_is_observable_array() {
+ if (testRunner.isRunningOnEmulator()) {
+ return;
+ }
// Keep the reference to the observable array to test the weakEventListener
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
diff --git a/apps/tests/ui/observable-tests.ts b/apps/tests/ui/observable-tests.ts
index 23f76f057..b8bb5598e 100644
--- a/apps/tests/ui/observable-tests.ts
+++ b/apps/tests/ui/observable-tests.ts
@@ -378,3 +378,30 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati
TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly.");
}
+
+export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() {
+ var observableInstance = new observable.Observable();
+ var firstHandlerCalled = false;
+ var secondHandlerCalled= false;
+
+ var firstHandler = function (args) {
+ observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver);
+ firstHandlerCalled = true;
+ }
+
+ var secondHandler = function (args) {
+ observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver);
+ secondHandlerCalled = true;
+ }
+
+ var firstObserver = new observable.Observable();
+ var secondObserver = new observable.Observable();
+
+ observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver);
+ observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver);
+
+ observableInstance.set("someProperty", "some value");
+
+ TKUnit.assertEqual(firstHandlerCalled, true);
+ TKUnit.assertEqual(secondHandlerCalled, true);
+}
diff --git a/apps/tests/ui/placeholder/placeholder-tests.ts b/apps/tests/ui/placeholder/placeholder-tests.ts
new file mode 100644
index 000000000..97395cf15
--- /dev/null
+++ b/apps/tests/ui/placeholder/placeholder-tests.ts
@@ -0,0 +1,78 @@
+import TKUnit = require("../../TKUnit");
+import platform = require("platform");
+import utils = require("utils/utils");
+import helper = require("../helper");
+import viewModule = require("ui/core/view");
+
+//
+// # Placeholder
+// Using the placeholder requires the Placeholder module.
+// ``` JavaScript
+import placeholderModule = require("ui/placeholder");
+// ```
+
+// Creating native view for the Placeholder using creatingView event.
+//```XML
+//
+// {%raw%}{%endraw%}
+//
+//```
+//```JS
+//var platform = require("platform");
+//var utils = require("utils/utils");
+//
+//function creatingView(args) {
+// var nativeView;
+// if (platform.device.os === platform.platformNames.ios) {
+// nativeView = new UITextView();
+// nativeView.text = "Native";
+// } else if (platform.device.os === platform.platformNames.android) {
+// nativeView = new android.widget.TextView(utils.ad.getApplicationContext());
+// nativeView.setText("Native");
+// }
+//
+// args.view = nativeView;
+//}
+//
+//exports.creatingView = creatingView;
+//```
+//
+
+export function test_placeholder_creatingView() {
+ var nativeView;
+
+ var p = new placeholderModule.Placeholder();
+ p.id = "test";
+ p.on(placeholderModule.Placeholder.creatingViewEvent, (args: placeholderModule.CreateViewEventData) => {
+ if (platform.device.os === platform.platformNames.ios) {
+ nativeView = new UITextView();
+ nativeView.text = "Native";
+ } else if (platform.device.os === platform.platformNames.android) {
+ nativeView = new android.widget.TextView(utils.ad.getApplicationContext());
+ nativeView.setText("Native");
+ }
+
+ args.view = nativeView;
+ });
+
+ if (platform.device.os === platform.platformNames.ios) {
+ TKUnit.assert(p.ios instanceof UITextView, "ios property should be UITextView. Current value: " + p.ios);
+ } else if (platform.device.os === platform.platformNames.android) {
+ p._emit("creatingView");
+ TKUnit.assert(nativeView instanceof android.widget.TextView, "Native view should be android.widget.TextView. Current value: " + nativeView);
+ }
+}
+
+export function test_placeholder_will_not_crash_wihout_creatingView() {
+ var p = new placeholderModule.Placeholder();
+
+ function testAction(views: Array) {
+ if (platform.device.os === platform.platformNames.ios) {
+ TKUnit.assert(p.ios === undefined, "ios property should be undefined. Current value: " + p.ios);
+ } else if (platform.device.os === platform.platformNames.android) {
+ TKUnit.assert(p.android === undefined, "android view should be undefined. Current value: " + p.android);
+ }
+ };
+
+ helper.buildUIAndRunTest(p, testAction);
+}
\ No newline at end of file
diff --git a/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml b/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml
index addde5ae1..667c672a1 100644
--- a/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml
+++ b/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/apps/tests/ui/text-field/text-field-tests.ts b/apps/tests/ui/text-field/text-field-tests.ts
index a6d78c6db..637535aef 100644
--- a/apps/tests/ui/text-field/text-field-tests.ts
+++ b/apps/tests/ui/text-field/text-field-tests.ts
@@ -1,4 +1,5 @@
import TKUnit = require("../../TKUnit");
+import testRunner = require("../../testRunner");
import helper = require("../helper");
import viewModule = require("ui/core/view");
import pagesModule = require("ui/page");
@@ -427,6 +428,9 @@ export var testNativeTextAlignmentFromLocal = function () {
}
export var testMemoryLeak = function () {
+ if (testRunner.isRunningOnEmulator()) {
+ return;
+ }
helper.buildUIWithWeakRefAndInteract(_createTextFieldFunc, function (textField) {
textFieldTestsNative.typeTextNatively(textField, "Hello, world!");
});
diff --git a/apps/tests/ui/text-view/text-view-tests.ts b/apps/tests/ui/text-view/text-view-tests.ts
index 1666e99c7..631088a61 100644
--- a/apps/tests/ui/text-view/text-view-tests.ts
+++ b/apps/tests/ui/text-view/text-view-tests.ts
@@ -1,4 +1,5 @@
import TKUnit = require("../../TKUnit");
+import testRunner = require("../../testRunner");
import helper = require("../helper");
import viewModule = require("ui/core/view");
import pagesModule = require("ui/page");
@@ -468,6 +469,9 @@ export var testNativeTextAlignmentFromLocal = function () {
}
export var testMemoryLeak = function () {
+ if (testRunner.isRunningOnEmulator()) {
+ return;
+ }
helper.buildUIWithWeakRefAndInteract(_createTextViewFunc, function (textView) {
textViewTestsNative.typeTextNatively(textView, "Hello, world!");
});
diff --git a/apps/tests/xml-declaration/xml-declaration-tests.ts b/apps/tests/xml-declaration/xml-declaration-tests.ts
index 4083ea09d..5ab1be618 100644
--- a/apps/tests/xml-declaration/xml-declaration-tests.ts
+++ b/apps/tests/xml-declaration/xml-declaration-tests.ts
@@ -603,7 +603,7 @@ export function test_parse_ShouldParseNestedListViewInListViewTemplate() {
}
export function test_parse_ShouldEvaluateEventBindingExpressionInListViewTemplate() {
- var p = builder.parse('');
+ var p = builder.parse('');
function testAction(views: Array) {
var ctrl: segmentedBar.SegmentedBar;
diff --git a/data/observable/observable.ts b/data/observable/observable.ts
index 3fd51a3c3..565fc58e7 100644
--- a/data/observable/observable.ts
+++ b/data/observable/observable.ts
@@ -136,7 +136,7 @@ export class Observable implements definition.Observable {
var i;
var entry: ListenerEntry;
var observersLength = observers.length;
- for (i = 0; i < observersLength; i++) {
+ for (i = observersLength - 1; i >= 0 ; i--) {
entry = observers[i];
if (entry.thisArg) {
entry.callback.apply(entry.thisArg, [data]);
diff --git a/ui/core/bindable.ts b/ui/core/bindable.ts
index 9cc0f646a..0c4bc3c30 100644
--- a/ui/core/bindable.ts
+++ b/ui/core/bindable.ts
@@ -140,6 +140,15 @@ export class Binding {
source: WeakRef