diff --git a/tests/app/ui/styling/style-tests.ts b/tests/app/ui/styling/style-tests.ts index b634637b8..23bcfd1f5 100644 --- a/tests/app/ui/styling/style-tests.ts +++ b/tests/app/ui/styling/style-tests.ts @@ -12,6 +12,7 @@ import types = require("utils/types"); import viewModule = require("ui/core/view"); import styleModule = require("ui/styling/style"); import dependencyObservableModule = require("ui/core/dependency-observable"); +import {StyleScope} from 'ui/styling/style-scope'; export function test_css_dataURI_is_applied_to_backgroundImageSource() { var stack = new stackModule.StackLayout(); @@ -1443,6 +1444,17 @@ export function test_CascadingClassNamesAppliesAfterPageLoad() { helper.assertViewBackgroundColor(stack, "#FF0000"); }); } + +export function test_SortingOfCssSelectorsWithSameSpecificity() { + let scope = new StyleScope(); + scope.css = ".button { border-color: #b2b2b2; background-color: hotpink; color: #444; margin: 5; padding: 7 2; border-width: 1; border-style: solid; border-radius: 2; text-align: center; font-size: 18; line-height: 42; } .button-small { background-color: salmon; } .button-large { font-size: 26; } .button-light { border-color: #ddd; background-color: #fff; color: #444; } .button-stable { border-color: #b2b2b2; background-color: #f8f8f8; color: #444; } .button-positive { border-color: #0c60ee; background-color: #387ef5; color: #fff; } .button-calm { border-color: #0a9dc7;background-color: #11c1f3; color: #fff; } .button-balanced { border-color: #28a54c; background-color: #33cd5f; color: #fff; } .button-energized { border-color: #e6b500; background-color: #ffc900; color: #fff; } .button-assertive { border-color: #e42112; background-color: #ef473a; color: #fff; } .button-royal { border-color: #6b46e5; background-color: #886aea; color: #fff; } .button-dark { border-color: #111; background-color: #444; color: #fff; }"; + scope.ensureSelectors(); + let expressions = []; + (scope)._mergedCssSelectors.forEach((v) => { + expressions.push(v.expression); + }); + TKUnit.assertTrue(expressions.indexOf('button') < expressions.indexOf('button-calm'), "button class selector should be before button-calm selector."); +} // // For information and example how to use style properties please refer to special [**Styling**](../../../styling.md) topic. // diff --git a/tns-core-modules/ui/styling/style-scope.ts b/tns-core-modules/ui/styling/style-scope.ts index 07be3695e..365ad0043 100644 --- a/tns-core-modules/ui/styling/style-scope.ts +++ b/tns-core-modules/ui/styling/style-scope.ts @@ -191,7 +191,8 @@ export class StyleScope { mergedResult.push.apply(mergedResult, arrays[i]); } } - mergedResult.sort((a, b) => a.specificity - b.specificity); + ensureUtils(); + mergedResult = utils.mergeSort(mergedResult, (a, b) => { return a.specificity - b.specificity; }); return mergedResult; } @@ -200,9 +201,9 @@ export class StyleScope { this.ensureSelectors(); view.style._beginUpdate(); - let i, - selector: cssSelector.CssSelector, - matchedStateSelectors = new Array() + let i; + let selector: cssSelector.CssSelector; + let matchedStateSelectors = new Array(); // Go trough all selectors - and directly apply all non-state selectors for (i = 0; i < this._mergedCssSelectors.length; i++) { diff --git a/tns-core-modules/utils/utils-common.ts b/tns-core-modules/utils/utils-common.ts index e3e9f30df..b730bccd8 100644 --- a/tns-core-modules/utils/utils-common.ts +++ b/tns-core-modules/utils/utils-common.ts @@ -134,3 +134,36 @@ export function isDataURI(uri: string): boolean { return firstSegment && firstSegment.indexOf("data:") === 0 && firstSegment.indexOf('base64') >= 0; } + +export function mergeSort(arr, compareFunc) { + if (arr.length < 2) { + return arr; + } + + let middle = arr.length / 2; + let left = arr.slice(0, middle); + let right = arr.slice(middle, arr.length); + + return merge(mergeSort(left, compareFunc), mergeSort(right, compareFunc), compareFunc); +} + +export function merge(left, right, compareFunc) { + let result = []; + while(left.length && right.length) { + if (compareFunc(left[0], right[0]) <= 0) { + result.push(left.shift()); + } else { + result.push(right.shift()); + } + } + + while (left.length) { + result.push(left.shift()); + } + + while (right.length) { + result.push(right.shift()); + } + + return result; +} \ No newline at end of file diff --git a/tns-core-modules/utils/utils.d.ts b/tns-core-modules/utils/utils.d.ts index a2c83fbd0..76f9518bf 100644 --- a/tns-core-modules/utils/utils.d.ts +++ b/tns-core-modules/utils/utils.d.ts @@ -249,5 +249,12 @@ * Converts string value to number or boolean. * @param value The original value. */ - export function convertString(value: any): any + export function convertString(value: any): any + + /** + * Sorts an array by using merge sort algoritm (which ensures stable sort since the built-in Array.sort() does not promise a stable sort). + * @param arr - array to be sorted + * @param compareFunc - function that will be used to compare two elements of the array + */ + export function mergeSort(arr: Array, compareFunc: (a: any, b: any) => number): Array }