<commit_before>/*
    @license
    Copyright (C) 2014 Dave Lesage
    License: MIT
    See license.txt for full license text.
*/
(function(z, undefined) {

    z.iterables = {};

    var _expand = function(iter) {
        if (iter && iter.isGenerator != null && iter.isGenerator()) {
            return iter();
        }
        if (z.getType(iter) === z.types.array) {
            return iter[z.symbols.iterator]();
        }
        return iter;
    };

    z.iterables.aggregate = function(iter, func, seed) {
        z.assert.isIterable(iter);
        z.assert.isFunction(func);
        var result,
            expandedIter = _expand(iter);
        if (seed == null) {
            result = expandedIter.next().value;
        }
        else {
            result = func(seed, expandedIter.next().value);
        }
        for (var v of expandedIter) {
            result = func(result, v)
        }
        return result;
    };

    z.iterables.any = function(iter, predicate) {
        z.assert.isIterable(iter);
        if (z.check.isFunction(predicate)) {
            for (var v of iter) {
                if (predicate(v)) {
                    return true;
                }
            }
        }
        else {
            for (var v of iter) {
                if (v != null) {
                    return true;
                }
            }
        }
        return false;
    };

    z.iterables.concat = function(/* ... iter */) {
        var args = Array.prototype.slice.call(arguments);
        return function*() {
            for (var arg of args) {
                for (var v of arg) {
                    yield v;
                }
            }
        }
    };

    z.iterables.distinct = function(iter, selector) {
        z.assert.isIterable(iter);
        var seen = [];
        if (z.check.isFunction(selector)) {
            return function*() {
                var seen = [];
                for (var v of iter) {
                    var key = selector(v);
                    if (!seen.contains(key)) {
                        seen.push(key);
                        yield v;
                    }
                }
            };
        }
        else {
            return function*() {
                var seen = [];
                for (var v of iter) {
                    if (!seen.contains(v)) {
                        seen.push(v);
                        // yield v; // this works too. may be more efficient?
                    }
                }
                yield* z.arrays.asEnumerable(seen);
            };
        }
    };

    z.iterables.first = function(iter, predicate) {
        z.assert.isIterable(iter);
        if (z.check.isFunction(predicate)) {
            for (var v of iter) {
                if (predicate(v)) {
                    return v;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.exists(v)) {
                    return v;
                }
            }
        }
        return null;
    };

    z.iterables.innerJoin = function(iter1, iter2) {
        z.assert.isIterable(iter1);
        z.assert.isIterable(iter2);
        return {
            on: function(predicate) {
                z.assert.isFunction(predicate);
                return function*() {
                    for (var item1 of iter1) {
                        for (var item2 of iter2) {
                            if (predicate(item1, item2)) {
                                yield z.smash(item1, item2);
                            }
                        }
                    }
                }
            }
        };
    };

    z.iterables.last = function(iter, predicate) {
        z.assert.isIterable(iter);
        // we will have to iterate over the entire iterable in the generic case
        // array has its own specific implementation for last, since we know the end
        var a,
            b,
            result = null,
            expandedIter = _expand(iter);

        if (z.getType(iter) === z.types.array) {
            // if the type is actually an array,
            // then make sure we use the more efficient
            // array specific implementation
            return z.arrays.last(iter, predicate);
        }
        if (z.check.isFunction(predicate)) {
            while (!(a = expandedIter.next()).done) {
                if (predicate(a.value)) {
                    result = a.value;
                }
            }
        }
        else {
            while (!(a = expandedIter.next()).done) {
                b = a;
                // result = a.value; // better way to step? the final "done" object does not contain a value, and no way to step backwards at the end
            }
            result = b.value;
        }
        return result;
    };

    z.iterables.max = function(iter, selector) {
        z.assert.isIterable(iter);
        var maxValue = Number.MIN_VALUE;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var selected = selector(v);
                if (z.check.isNumber(selected) && maxValue < selected) {
                    maxValue = selected;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v) && maxValue < v) {
                    maxValue = v;
                }
            }
        }
        return maxValue;
    };

    z.iterables.min = function(iter, selector) {
        z.assert.isIterable(iter);
        var minValue = Number.MAX_VALUE;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var selected = selector(v);
                if (z.check.isNumber(selected) && selected < minValue) {
                    minValue = selected;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v) && v < minValue) {
                    minValue = v;
                }
            }
        }
        return minValue;
    };

    var buildMapArray = function(count) {
        var mapArray = new Array(count);
        for (var i = 0; i < count; i++) {
            mapArray[i] = i;
        }
        return mapArray;
    };
    var buildKeyArray = function(elements, selector, count) {
        var keyArray = new Array(count);
        for (var i = 0; i < count; i++) {
            keyArray[i] = selector(elements[i]);
        };
        return keyArray;
    };
    var quicksort3 = function(keyArray, mapArray, comparer, left, right) {
        var indexForLessThan = left;
        var indexForGreaterThan = right;
        var pivotIndex = mapArray[left];
        var indexForIterator = left+1;
        while (indexForIterator <= indexForGreaterThan) {
            var cmp = comparer(keyArray[mapArray[indexForIterator]], keyArray[pivotIndex]);
            if (cmp < 0) {
                z.arrays.swap(mapArray, indexForLessThan++, indexForIterator++);
            }
            else if (0 < cmp) {
                z.arrays.swap(mapArray, indexForIterator, indexForGreaterThan--);
            }
            else {
                indexForIterator++;
            }
        }
        if (left < indexForLessThan-1) {
            quicksort3(keyArray, mapArray, comparer, left, indexForLessThan-1);
        }
        if (indexForGreaterThan+1 < right) {
            quicksort3(keyArray, mapArray, comparer, indexForGreaterThan+1, right);
        }
    };
    z.iterables.orderBy = function(iter, selector, comparer) {
        z.assert.isIterable(iter);
        if (!z.check.isFunction(selector))
            selector = z.functions.identity;
        if (!z.check.exists(comparer))
            comparer = ((x,y) => x > y ? 1 : x < y ? -1 : 0);

        var elements = (z.getType(iter) === z.types.generator ? iter.toArray() : iter);

        var yielder = function*() {
            // only execute the sort on iteration
            // this is due to the possibly orderBy().thenBy().thenBy() chained calls
            var unsortedElements = z.iterables.where(elements, x => selector(x) == null).toArray();
            var unsortedCount = unsortedElements.length;
            var sortedElements = z.iterables.where(elements, x => selector(x) != null).toArray();
            var sortedCount = sortedElements.length;
            var sortedKeys = buildKeyArray(sortedElements, selector, sortedCount);
            var sortedMap = buildMapArray(sortedCount);
            quicksort3(sortedKeys, sortedMap, comparer, 0, sortedCount-1);
            for (var i = 0; i < sortedCount; i++) {
                yield sortedElements[sortedMap[i]];
            }
            for (var v of unsortedElements) {
                yield v;
            }
        };
        // we should really create an OrderedIterable class
        // and expand it to store items like comparer/selector
        // by default when extends/classes are available in es6
        z.defineProperty(yielder, "thenBy", { enumerable: false, writable: false, value: thenBy.bind(yielder) });
        z.defineProperty(yielder, "comparer", { enumerable: false, writable: false, value: comparer });
        z.defineProperty(yielder, "elements", { enumerable: false, writable: false, value: elements });
        z.defineProperty(yielder, "selector", { enumerable: false, writable: false, value: selector });
        return yielder;
    };

    var thenBy = function(secondarySelector, secondaryComparer) {
        var self = this;
        z.assert.isIterable(self.elements);
        z.assert.isFunction(self.comparer);
        z.assert.isFunction(self.selector);
        if (!z.check.exists(secondaryComparer))
            secondaryComparer = ((x,y) => x > y ? 1 : x < y ? -1 : 0);
        z.assert.isFunction(secondaryComparer);

        var compoundComparer = function(compoundKeyA, compoundKeyB) {
            var primaryResult = self.comparer(compoundKeyA.primary, compoundKeyB.primary);
            if (primaryResult === 0) {
                return secondaryComparer(compoundKeyA.secondary, compoundKeyB.secondary);
            }
            return primaryResult;
        };
        var compoundSelector = function(item) {
            return {
                primary: self.selector(item),
                secondary: secondarySelector(item)
            };
        };

        var yielder = function*() {
            var unsortedElements = z.iterables.where(self.elements, x => compoundSelector(x) == null).toArray();
            var unsortedCount = unsortedElements.length;
            var sortedElements = z.iterables.where(self.elements, x => compoundSelector(x) != null).toArray();
            var sortedCount = sortedElements.length;
            var sortedKeys = buildKeyArray(sortedElements, compoundSelector, sortedCount);
            var sortedMap = buildMapArray(sortedCount);
            quicksort3(sortedKeys, sortedMap, compoundComparer, 0, sortedCount-1); // move to inside yielder, eventually
            for (var i = 0; i < sortedCount; i++) {
                yield sortedElements[sortedMap[i]];
            }
            for (var v of unsortedElements) {
                yield v;
            }
        };
        z.defineProperty(yielder, "thenBy", { enumerable: false, writable: false, value: thenBy.bind(yielder) });
        z.defineProperty(yielder, "comparer", { enumerable: false, writable: false, value: compoundComparer });
        z.defineProperty(yielder, "elements", { enumerable: false, writable: false, value: self.elements });
        z.defineProperty(yielder, "selector", { enumerable: false, writable: false, value: compoundSelector });
        return yielder;
    };

    var _reverse = function*(iter, a) {
        if (!a.done) {
            yield* _reverse(iter, iter.next());
            yield a.value;
        }
    };
    z.iterables.reverse = function(iter) {
        z.assert.isIterable(iter);
        return function*() {
            var expandedIter = _expand(iter);
            yield* _reverse(expandedIter, expandedIter.next());
        };
    };

    z.iterables.select = function(iter, selector) {
        z.assert.isIterable(iter);
        z.assert.isFunction(selector);
        return function*() {
            for (var v of iter) {
                yield selector(v);
            }
        };
    };

    z.iterables.skip = function(iter, count) {
        z.assert.isIterable(iter);
        return function*() {
            var a,
                i = 0,
                expandedIter = _expand(iter);
            while (!(a = expandedIter.next()).done && i < count) {
                i++;
            }
            if (!a.done) {
                yield a.value; // yield the value at the starting point
                while(!(a = expandedIter.next()).done) {
                    yield a.value; // yield remaining values
                }
            }
        }
    };

    z.iterables.sum = function(iter, selector) {
        z.assert.isIterable(iter);
        var sum = 0;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var num = selector(v);
                if (z.check.isNumber(num)) {
                    sum += num;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v)) {
                    sum += v;
                }
            }
        }
        return sum;
    };

    z.iterables.take = function(iter, count) {
        z.assert.isIterable(iter);
        return function*() {
            var i = 0;
            for (var v of iter) {
                if (count <= i++) {
                    break;
                }
                yield v;
            }
        };
    };

    z.iterables.toArray = function(iter) {
        z.assert.isIterable(iter);
        var result = [];
        for (var v of iter) {
            result.push(v);
        }
        return result;
    };

    z.iterables.where = function(iter, predicate) {
        z.assert.isIterable(iter);
        z.assert.isFunction(predicate);
        return function*() {
            for (var v of iter) {
                if (predicate(v)) {
                    yield v;
                }
            }
        };
    };

    z.iterables.zip = function(iter1, iter2, method) {
        z.assert.isIterable(iter1);
        z.assert.isIterable(iter2);
        return function*() {
            var a, b;
            var expandedIter1 = _expand(iter1);
            var expandedIter2 = _expand(iter2);
            while (!(a = expandedIter1.next()).done && !(b = expandedIter2.next()).done) {
                yield method(a.value, b.value);
            }
        };
    };

}(zUtil.prototype));<commit_msg>expanded iterables orderBy/thenBy chains to override the original comparer and selector, instead of rebuilding the generator function each time thenBy is called<commit_after>/*
    @license
    Copyright (C) 2014 Dave Lesage
    License: MIT
    See license.txt for full license text.
*/
(function(z, undefined) {

    z.iterables = {};

    var _expand = function(iter) {
        if (iter && iter.isGenerator != null && iter.isGenerator()) {
            return iter();
        }
        if (z.getType(iter) === z.types.array) {
            return iter[z.symbols.iterator]();
        }
        return iter;
    };

    z.iterables.aggregate = function(iter, func, seed) {
        z.assert.isIterable(iter);
        z.assert.isFunction(func);
        var result,
            expandedIter = _expand(iter);
        if (seed == null) {
            result = expandedIter.next().value;
        }
        else {
            result = func(seed, expandedIter.next().value);
        }
        for (var v of expandedIter) {
            result = func(result, v)
        }
        return result;
    };

    z.iterables.any = function(iter, predicate) {
        z.assert.isIterable(iter);
        if (z.check.isFunction(predicate)) {
            for (var v of iter) {
                if (predicate(v)) {
                    return true;
                }
            }
        }
        else {
            for (var v of iter) {
                if (v != null) {
                    return true;
                }
            }
        }
        return false;
    };

    z.iterables.concat = function(/* ... iter */) {
        var args = Array.prototype.slice.call(arguments);
        return function*() {
            for (var arg of args) {
                for (var v of arg) {
                    yield v;
                }
            }
        }
    };

    z.iterables.distinct = function(iter, selector) {
        z.assert.isIterable(iter);
        var seen = [];
        if (z.check.isFunction(selector)) {
            return function*() {
                var seen = [];
                for (var v of iter) {
                    var key = selector(v);
                    if (!seen.contains(key)) {
                        seen.push(key);
                        yield v;
                    }
                }
            };
        }
        else {
            return function*() {
                var seen = [];
                for (var v of iter) {
                    if (!seen.contains(v)) {
                        seen.push(v);
                        // yield v; // this works too. may be more efficient?
                    }
                }
                yield* z.arrays.asEnumerable(seen);
            };
        }
    };

    z.iterables.first = function(iter, predicate) {
        z.assert.isIterable(iter);
        if (z.check.isFunction(predicate)) {
            for (var v of iter) {
                if (predicate(v)) {
                    return v;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.exists(v)) {
                    return v;
                }
            }
        }
        return null;
    };

    z.iterables.innerJoin = function(iter1, iter2) {
        z.assert.isIterable(iter1);
        z.assert.isIterable(iter2);
        return {
            on: function(predicate) {
                z.assert.isFunction(predicate);
                return function*() {
                    for (var item1 of iter1) {
                        for (var item2 of iter2) {
                            if (predicate(item1, item2)) {
                                yield z.smash(item1, item2);
                            }
                        }
                    }
                }
            }
        };
    };

    z.iterables.last = function(iter, predicate) {
        z.assert.isIterable(iter);
        // we will have to iterate over the entire iterable in the generic case
        // array has its own specific implementation for last, since we know the end
        var a,
            b,
            result = null,
            expandedIter = _expand(iter);

        if (z.getType(iter) === z.types.array) {
            // if the type is actually an array,
            // then make sure we use the more efficient
            // array specific implementation
            return z.arrays.last(iter, predicate);
        }
        if (z.check.isFunction(predicate)) {
            while (!(a = expandedIter.next()).done) {
                if (predicate(a.value)) {
                    result = a.value;
                }
            }
        }
        else {
            while (!(a = expandedIter.next()).done) {
                b = a;
                // result = a.value; // better way to step? the final "done" object does not contain a value, and no way to step backwards at the end
            }
            result = b.value;
        }
        return result;
    };

    z.iterables.max = function(iter, selector) {
        z.assert.isIterable(iter);
        var maxValue = Number.MIN_VALUE;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var selected = selector(v);
                if (z.check.isNumber(selected) && maxValue < selected) {
                    maxValue = selected;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v) && maxValue < v) {
                    maxValue = v;
                }
            }
        }
        return maxValue;
    };

    z.iterables.min = function(iter, selector) {
        z.assert.isIterable(iter);
        var minValue = Number.MAX_VALUE;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var selected = selector(v);
                if (z.check.isNumber(selected) && selected < minValue) {
                    minValue = selected;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v) && v < minValue) {
                    minValue = v;
                }
            }
        }
        return minValue;
    };

    var buildMapArray = function(count) {
        var mapArray = new Array(count);
        for (var i = 0; i < count; i++) {
            mapArray[i] = i;
        }
        return mapArray;
    };
    var buildKeyArray = function(elements, selector, count) {
        var keyArray = new Array(count);
        for (var i = 0; i < count; i++) {
            keyArray[i] = selector(elements[i]);
        };
        return keyArray;
    };
    var quicksort3 = function(keyArray, mapArray, comparer, left, right) {
        var indexForLessThan = left;
        var indexForGreaterThan = right;
        var pivotIndex = mapArray[left];
        var indexForIterator = left+1;
        while (indexForIterator <= indexForGreaterThan) {
            var cmp = comparer(keyArray[mapArray[indexForIterator]], keyArray[pivotIndex]);
            if (cmp < 0) {
                z.arrays.swap(mapArray, indexForLessThan++, indexForIterator++);
            }
            else if (0 < cmp) {
                z.arrays.swap(mapArray, indexForIterator, indexForGreaterThan--);
            }
            else {
                indexForIterator++;
            }
        }
        if (left < indexForLessThan-1) {
            quicksort3(keyArray, mapArray, comparer, left, indexForLessThan-1);
        }
        if (indexForGreaterThan+1 < right) {
            quicksort3(keyArray, mapArray, comparer, indexForGreaterThan+1, right);
        }
    };
    z.iterables.orderBy = function(iter, selector, comparer) {
        var self = this;
        z.assert.isIterable(iter);
        if (!z.check.isFunction(selector))
            selector = z.functions.identity;
        if (!z.check.exists(comparer))
            comparer = ((x,y) => x > y ? 1 : x < y ? -1 : 0);

        var elements = (z.getType(iter) === z.types.generator ? iter.toArray() : iter);
        var yielder = function*() {
            // only execute the sort on iteration
            // this is due to the possibly orderBy().thenBy().thenBy() chained calls
            var unsortedElements = z.iterables.where(yielder.elements, x => yielder.selector(x) == null).toArray();
            var unsortedCount = unsortedElements.length;
            var sortedElements = z.iterables.where(yielder.elements, x => yielder.selector(x) != null).toArray();
            var sortedCount = sortedElements.length;
            var sortedKeys = buildKeyArray(sortedElements, yielder.selector, sortedCount);
            var sortedMap = buildMapArray(sortedCount);
            quicksort3(sortedKeys, sortedMap, yielder.comparer, 0, sortedCount-1);
            for (var i = 0; i < sortedCount; i++) {
                yield sortedElements[sortedMap[i]];
            }
            for (var v of unsortedElements) {
                yield v;
            }
        };

        z.defineProperty(yielder, "thenBy", { enumerable: false, writable: true, configurable: true, value: thenBy.bind(yielder) });
        z.defineProperty(yielder, "selector", { enumerable: false, writable: true, configurable: true, value: selector });
        z.defineProperty(yielder, "comparer", { enumerable: false, writable: true, configurable: true, value: comparer });
        z.defineProperty(yielder, "elements", { enumerable: false, writeable: false, configurable: false, value: elements });
        return yielder;
    };

    var thenBy = function(newSelector, newComparer) {
        var self = this;
        z.assert.isFunction(self.comparer);
        z.assert.isFunction(self.selector);
        if (!z.check.exists(newComparer))
            newComparer = ((x,y) => x > y ? 1 : x < y ? -1 : 0);
        z.assert.isFunction(newComparer);

        // wrap the old selector in a new selector function
        // which will build all keys into a primary/secondary structure,
        // allowing the primary key selector to grow recursively
        // by appending new selectors on to the original selectors
        var oldSelector = self.selector; // store pointer to avoid accidental recursion
        self.selector = function(item) {
            return {
                primary: oldSelector(item),
                secondary: newSelector(item)
            }
        };

        // wrap the old comparer in a new comparer function
        // which will carry on down the line of comparers
        // in order until a non-zero is found, or until
        // we reach the new comparer
        var oldComparer = self.comparer; // store pointer to avoid accidental recursion
        self.comparer = function(compoundKeyA, compoundKeyB) {
            var primaryResult = oldComparer(compoundKeyA.primary, compoundKeyB.primary);
            if (primaryResult === 0) {
                return newComparer(compoundKeyA.secondary, compoundKeyB.secondary);
            }
            return primaryResult;
        };

        return self;
    };

    var _reverse = function*(iter, a) {
        if (!a.done) {
            yield* _reverse(iter, iter.next());
            yield a.value;
        }
    };
    z.iterables.reverse = function(iter) {
        z.assert.isIterable(iter);
        return function*() {
            var expandedIter = _expand(iter);
            yield* _reverse(expandedIter, expandedIter.next());
        };
    };

    z.iterables.select = function(iter, selector) {
        z.assert.isIterable(iter);
        z.assert.isFunction(selector);
        return function*() {
            for (var v of iter) {
                yield selector(v);
            }
        };
    };

    z.iterables.skip = function(iter, count) {
        z.assert.isIterable(iter);
        return function*() {
            var a,
                i = 0,
                expandedIter = _expand(iter);
            while (!(a = expandedIter.next()).done && i < count) {
                i++;
            }
            if (!a.done) {
                yield a.value; // yield the value at the starting point
                while(!(a = expandedIter.next()).done) {
                    yield a.value; // yield remaining values
                }
            }
        }
    };

    z.iterables.sum = function(iter, selector) {
        z.assert.isIterable(iter);
        var sum = 0;
        if (z.check.isFunction(selector)) {
            for (var v of iter) {
                var num = selector(v);
                if (z.check.isNumber(num)) {
                    sum += num;
                }
            }
        }
        else {
            for (var v of iter) {
                if (z.check.isNumber(v)) {
                    sum += v;
                }
            }
        }
        return sum;
    };

    z.iterables.take = function(iter, count) {
        z.assert.isIterable(iter);
        return function*() {
            var i = 0;
            for (var v of iter) {
                if (count <= i++) {
                    break;
                }
                yield v;
            }
        };
    };

    z.iterables.toArray = function(iter) {
        var result = [];
        for (var v of iter) {
            result.push(v);
        }
        return result;
    };

    z.iterables.where = function(iter, predicate) {
        z.assert.isIterable(iter);
        z.assert.isFunction(predicate);
        return function*() {
            for (var v of iter) {
                if (predicate(v)) {
                    yield v;
                }
            }
        };
    };

    z.iterables.zip = function(iter1, iter2, method) {
        z.assert.isIterable(iter1);
        z.assert.isIterable(iter2);
        return function*() {
            var a, b;
            var expandedIter1 = _expand(iter1);
            var expandedIter2 = _expand(iter2);
            while (!(a = expandedIter1.next()).done && !(b = expandedIter2.next()).done) {
                yield method(a.value, b.value);
            }
        };
    };

}(zUtil.prototype));<|endoftext|>


<commit_before>import {DOM} from 'aurelia-pal';

//Note: path and deepPath are designed to handle v0 and v1 shadow dom specs respectively
function findOriginalEventTarget(event) {
  return (event.path && event.path[0]) || (event.deepPath && event.deepPath[0]) || event.target;
}

function interceptStopPropagation(event) {
  event.standardStopPropagation = event.stopPropagation;
  event.stopPropagation = function() {
    this.propagationStopped  = true;
    this.standardStopPropagation();
  };
}

function handleCapturedEvent(event) {
  let interceptInstalled = false;
  event.propagationStopped = false;
  let target = findOriginalEventTarget(event);

  let orderedCallbacks = [];
  /**
   * During capturing phase, event 'bubbles' down from parent. Needs to reorder callback from root down to target
   */
  while (target) {
    if (target.capturedCallbacks) {
      let callback = target.capturedCallbacks[event.type];
      if (callback) {
        if (!interceptInstalled) {
          interceptStopPropagation(event);
          interceptInstalled = true;
        }
        orderedCallbacks.push(callback);
      }
    }
    target = target.parentNode;
  }
  for (let i = orderedCallbacks.length - 1; i > 0; i--) {
    let orderedCallback = orderedCallbacks[i];
    orderedCallback(event);
    if (event.propagationStopped) {
      break;
    }
  }
}

class CapturedHandlerEntry {
  constructor(eventName) {
    this.eventName = eventName;
    this.count = 0;
  }

  increment() {
    this.count++;

    if (this.count === 1) {
      DOM.addEventListener(this.eventName, handleCapturedEvent, true);
    }
  }

  decrement() {
    this.count--;

    if (this.count === 0) {
      DOM.removeEventListener(this.eventName, handleCapturedEvent, true);
    }
  }
}

function handleDelegatedEvent(event) {
  let interceptInstalled = false;
  event.propagationStopped = false;
  let target = findOriginalEventTarget(event);

  while (target && !event.propagationStopped) {
    if (target.delegatedCallbacks) {
      let callback = target.delegatedCallbacks[event.type];
      if (callback) {
        if (!interceptInstalled) {
          interceptStopPropagation(event);
          interceptInstalled = true;
        }
        callback(event);
      }
    }

    target = target.parentNode;
  }
}

class DelegateHandlerEntry {
  constructor(eventName) {
    this.eventName = eventName;
    this.count = 0;
  }

  increment() {
    this.count++;

    if (this.count === 1) {
      DOM.addEventListener(this.eventName, handleDelegatedEvent, false);
    }
  }

  decrement() {
    this.count--;

    if (this.count === 0) {
      DOM.removeEventListener(this.eventName, handleDelegatedEvent);
    }
  }
}

class DefaultEventStrategy {
  delegatedHandlers = {};
  capturedHandlers = {};

  subscribe(target, targetEvent, callback, strategy) {
    let delegatedHandlers, capturedHandlers, handlerEntry;
    if (strategy === delegationStrategy.delegate) {
      delegatedHandlers = this.delegatedHandlers;
      handlerEntry = delegatedHandlers[targetEvent] || (delegatedHandlers[targetEvent] = new DelegateHandlerEntry(targetEvent));
      let delegatedCallbacks = target.delegatedCallbacks || (target.delegatedCallbacks = {});

      handlerEntry.increment();
      delegatedCallbacks[targetEvent] = callback;

      return function() {
        handlerEntry.decrement();
        delegatedCallbacks[targetEvent] = null;
      };
    }
    if (strategy === delegationStrategy.capture) {
      capturedHandlers = this.capturedHandlers;
      handlerEntry = capturedHandlers[targetEvent] || (capturedHandlers[targetEvent] = new CapturedHandlerEntry(targetEvent));
      let capturedCallbacks = target.capturedCallbacks || (target.capturedCallbacks = {});

      handlerEntry.increment();
      capturedCallbacks[targetEvent] = callback;

      return function() {
        handlerEntry.decrement();
        capturedCallbacks[targetEvent] = null;
      };
    }

    target.addEventListener(targetEvent, callback, false);

    return function() {
      target.removeEventListener(targetEvent, callback);
    };
  }
}

export const delegationStrategy = {
  none: 0,
  capturing: 1,
  bubbling: 2
};

export class EventManager {
  constructor() {
    this.elementHandlerLookup = {};
    this.eventStrategyLookup = {};

    this.registerElementConfig({
      tagName: 'input',
      properties: {
        value: ['change', 'input'],
        checked: ['change', 'input'],
        files: ['change', 'input']
      }
    });

    this.registerElementConfig({
      tagName: 'textarea',
      properties: {
        value: ['change', 'input']
      }
    });

    this.registerElementConfig({
      tagName: 'select',
      properties: {
        value: ['change']
      }
    });

    this.registerElementConfig({
      tagName: 'content editable',
      properties: {
        value: ['change', 'input', 'blur', 'keyup', 'paste']
      }
    });

    this.registerElementConfig({
      tagName: 'scrollable element',
      properties: {
        scrollTop: ['scroll'],
        scrollLeft: ['scroll']
      }
    });

    this.defaultEventStrategy = new DefaultEventStrategy();
  }

  registerElementConfig(config) {
    let tagName = config.tagName.toLowerCase();
    let properties = config.properties;
    let propertyName;

    this.elementHandlerLookup[tagName] = {};

    for (propertyName in properties) {
      if (properties.hasOwnProperty(propertyName)) {
        this.registerElementPropertyConfig(tagName, propertyName, properties[propertyName]);
      }
    }
  }

  registerElementPropertyConfig(tagName, propertyName, events) {
    this.elementHandlerLookup[tagName][propertyName] = this.createElementHandler(events);
  }

  createElementHandler(events) {
    return {
      subscribe(target, callback) {
        events.forEach(changeEvent => {
          target.addEventListener(changeEvent, callback, false);
        });

        return function() {
          events.forEach(changeEvent => {
            target.removeEventListener(changeEvent, callback);
          });
        };
      }
    };
  }

  registerElementHandler(tagName, handler) {
    this.elementHandlerLookup[tagName.toLowerCase()] = handler;
  }

  registerEventStrategy(eventName, strategy) {
    this.eventStrategyLookup[eventName] = strategy;
  }

  getElementHandler(target, propertyName) {
    let tagName;
    let lookup = this.elementHandlerLookup;

    if (target.tagName) {
      tagName = target.tagName.toLowerCase();

      if (lookup[tagName] && lookup[tagName][propertyName]) {
        return lookup[tagName][propertyName];
      }

      if (propertyName === 'textContent' || propertyName === 'innerHTML') {
        return lookup['content editable'].value;
      }

      if (propertyName === 'scrollTop' || propertyName === 'scrollLeft') {
        return lookup['scrollable element'][propertyName];
      }
    }

    return null;
  }

  addEventListener(target, targetEvent, callback, delegate) {
    return (this.eventStrategyLookup[targetEvent] || this.defaultEventStrategy)
      .subscribe(target, targetEvent, callback, delegate);
  }
}
<commit_msg>fix(EventManager): capture bugfixes<commit_after>import {DOM} from 'aurelia-pal';

//Note: path and deepPath are designed to handle v0 and v1 shadow dom specs respectively
function findOriginalEventTarget(event) {
  return (event.path && event.path[0]) || (event.deepPath && event.deepPath[0]) || event.target;
}

function interceptStopPropagation(event) {
  event.standardStopPropagation = event.stopPropagation;
  event.stopPropagation = function() {
    this.propagationStopped  = true;
    this.standardStopPropagation();
  };
}

function handleCapturedEvent(event) {
  let interceptInstalled = false;
  event.propagationStopped = false;
  let target = findOriginalEventTarget(event);

  let orderedCallbacks = [];
  /**
   * During capturing phase, event 'bubbles' down from parent. Needs to reorder callback from root down to target
   */
  while (target) {
    if (target.capturedCallbacks) {
      let callback = target.capturedCallbacks[event.type];
      if (callback) {
        if (!interceptInstalled) {
          interceptStopPropagation(event);
          interceptInstalled = true;
        }
        orderedCallbacks.push(callback);
      }
    }
    target = target.parentNode;
  }
  for (let i = orderedCallbacks.length - 1; i >= 0; i--) {
    let orderedCallback = orderedCallbacks[i];
    orderedCallback(event);
    if (event.propagationStopped) {
      break;
    }
  }
}

class CapturedHandlerEntry {
  constructor(eventName) {
    this.eventName = eventName;
    this.count = 0;
  }

  increment() {
    this.count++;

    if (this.count === 1) {
      DOM.addEventListener(this.eventName, handleCapturedEvent, true);
    }
  }

  decrement() {
    this.count--;

    if (this.count === 0) {
      DOM.removeEventListener(this.eventName, handleCapturedEvent, true);
    }
  }
}

function handleDelegatedEvent(event) {
  let interceptInstalled = false;
  event.propagationStopped = false;
  let target = findOriginalEventTarget(event);

  while (target && !event.propagationStopped) {
    if (target.delegatedCallbacks) {
      let callback = target.delegatedCallbacks[event.type];
      if (callback) {
        if (!interceptInstalled) {
          interceptStopPropagation(event);
          interceptInstalled = true;
        }
        callback(event);
      }
    }

    target = target.parentNode;
  }
}

class DelegateHandlerEntry {
  constructor(eventName) {
    this.eventName = eventName;
    this.count = 0;
  }

  increment() {
    this.count++;

    if (this.count === 1) {
      DOM.addEventListener(this.eventName, handleDelegatedEvent, false);
    }
  }

  decrement() {
    this.count--;

    if (this.count === 0) {
      DOM.removeEventListener(this.eventName, handleDelegatedEvent);
    }
  }
}

class DefaultEventStrategy {
  delegatedHandlers = {};
  capturedHandlers = {};

  subscribe(target, targetEvent, callback, strategy) {
    let delegatedHandlers, capturedHandlers, handlerEntry;
    if (strategy === delegationStrategy.bubbling) {
      delegatedHandlers = this.delegatedHandlers;
      handlerEntry = delegatedHandlers[targetEvent] || (delegatedHandlers[targetEvent] = new DelegateHandlerEntry(targetEvent));
      let delegatedCallbacks = target.delegatedCallbacks || (target.delegatedCallbacks = {});

      handlerEntry.increment();
      delegatedCallbacks[targetEvent] = callback;

      return function() {
        handlerEntry.decrement();
        delegatedCallbacks[targetEvent] = null;
      };
    }
    if (strategy === delegationStrategy.capturing) {
      capturedHandlers = this.capturedHandlers;
      handlerEntry = capturedHandlers[targetEvent] || (capturedHandlers[targetEvent] = new CapturedHandlerEntry(targetEvent));
      let capturedCallbacks = target.capturedCallbacks || (target.capturedCallbacks = {});

      handlerEntry.increment();
      capturedCallbacks[targetEvent] = callback;

      return function() {
        handlerEntry.decrement();
        capturedCallbacks[targetEvent] = null;
      };
    }

    target.addEventListener(targetEvent, callback, false);

    return function() {
      target.removeEventListener(targetEvent, callback);
    };
  }
}

export const delegationStrategy = {
  none: 0,
  capturing: 1,
  bubbling: 2
};

export class EventManager {
  constructor() {
    this.elementHandlerLookup = {};
    this.eventStrategyLookup = {};

    this.registerElementConfig({
      tagName: 'input',
      properties: {
        value: ['change', 'input'],
        checked: ['change', 'input'],
        files: ['change', 'input']
      }
    });

    this.registerElementConfig({
      tagName: 'textarea',
      properties: {
        value: ['change', 'input']
      }
    });

    this.registerElementConfig({
      tagName: 'select',
      properties: {
        value: ['change']
      }
    });

    this.registerElementConfig({
      tagName: 'content editable',
      properties: {
        value: ['change', 'input', 'blur', 'keyup', 'paste']
      }
    });

    this.registerElementConfig({
      tagName: 'scrollable element',
      properties: {
        scrollTop: ['scroll'],
        scrollLeft: ['scroll']
      }
    });

    this.defaultEventStrategy = new DefaultEventStrategy();
  }

  registerElementConfig(config) {
    let tagName = config.tagName.toLowerCase();
    let properties = config.properties;
    let propertyName;

    this.elementHandlerLookup[tagName] = {};

    for (propertyName in properties) {
      if (properties.hasOwnProperty(propertyName)) {
        this.registerElementPropertyConfig(tagName, propertyName, properties[propertyName]);
      }
    }
  }

  registerElementPropertyConfig(tagName, propertyName, events) {
    this.elementHandlerLookup[tagName][propertyName] = this.createElementHandler(events);
  }

  createElementHandler(events) {
    return {
      subscribe(target, callback) {
        events.forEach(changeEvent => {
          target.addEventListener(changeEvent, callback, false);
        });

        return function() {
          events.forEach(changeEvent => {
            target.removeEventListener(changeEvent, callback);
          });
        };
      }
    };
  }

  registerElementHandler(tagName, handler) {
    this.elementHandlerLookup[tagName.toLowerCase()] = handler;
  }

  registerEventStrategy(eventName, strategy) {
    this.eventStrategyLookup[eventName] = strategy;
  }

  getElementHandler(target, propertyName) {
    let tagName;
    let lookup = this.elementHandlerLookup;

    if (target.tagName) {
      tagName = target.tagName.toLowerCase();

      if (lookup[tagName] && lookup[tagName][propertyName]) {
        return lookup[tagName][propertyName];
      }

      if (propertyName === 'textContent' || propertyName === 'innerHTML') {
        return lookup['content editable'].value;
      }

      if (propertyName === 'scrollTop' || propertyName === 'scrollLeft') {
        return lookup['scrollable element'][propertyName];
      }
    }

    return null;
  }

  addEventListener(target, targetEvent, callback, delegate) {
    return (this.eventStrategyLookup[targetEvent] || this.defaultEventStrategy)
      .subscribe(target, targetEvent, callback, delegate);
  }
}
<|endoftext|>


<commit_before>import { forElements } from '../core';
import { on } from './on';

export function undrag(undragProperties) {
  forEach(undragProperties, (undragProperty) => {
    off(undragProperty.element, 'touchstart, mousedown', undragProperty.begin);
    off(document.body, 'touchmove, mousemove', undragProperty.move);
    off(document.body, 'touchend, mouseup', undragProperty.end);
  });
}
<commit_msg>fix undrag<commit_after>import { forEach } from '../core';
import { off } from './off';

export function undrag(undragProperties) {
  forEach(undragProperties, (undragProperty) => {
    off(undragProperty.element, 'touchstart, mousedown', undragProperty.begin);
    off(document.body, 'touchmove, mousemove', undragProperty.move);
    off(document.body, 'touchend, mouseup', undragProperty.end);
  });
}
<|endoftext|>


<commit_before>var mask_merge;
(function(){

	mask_merge = function(a, b, owner){
		if (typeof a === 'string')
			a = parser_parse(a);
		if (typeof b === 'string')
			b = parser_parse(b);

		var contents = _getContents(b, b, new Contents);
		return _merge(a, contents, owner);
	};

	var tag_ELSE = '@else',
		tag_IF = '@if',
		tag_EACH = '@each',
		tag_PLACEHOLDER = '@placeholder',

		dom_NODE = Dom.NODE,
		dom_TEXTNODE = Dom.TEXTNODE,
		dom_FRAGMENT = Dom.FRAGMENT,
		dom_STATEMENT = Dom.STATEMENT
		;

	function _merge(node, contents, tmplNode, clonedParent){
		if (node == null)
			return null;

		if (is_Array(node))
			return _mergeArray(node, contents, tmplNode, clonedParent);

		switch(node.type){
			case dom_TEXTNODE:
				return _cloneTextNode(node, contents, tmplNode);
			case dom_NODE:
			case dom_STATEMENT:
				return _mergeNode(node, contents, tmplNode, clonedParent);
			case dom_FRAGMENT:
				return _mergeFragment(node, contents, tmplNode, clonedParent);
		}
		log_warn('Uknown type', node.type);
		return null;
	}
	function _mergeArray(nodes, contents, tmplNode, clonedParent){
		var fragment = [],
			imax = nodes.length,
			i = -1,
			x, node;
		while( ++i < imax ) {
			node = nodes[i];

			if (node.tagName === tag_ELSE) {
				// check previous
				if (x != null)
					continue;

				if (node.expression && !eval_(node.expression, contents, tmplNode))
					continue;

				x = _merge(nodes[i].nodes, contents, tmplNode, clonedParent)
			}
			else {
				x = _merge(node, contents, tmplNode, clonedParent);
			}

			appendAny(fragment, x);
		}
		return fragment;
	}
	function _mergeFragment(frag, contents, tmplNode, clonedParent) {
		var fragment = new Dom.Fragment;
		fragment.parent = clonedParent;
		fragment.nodes = _mergeArray(frag.nodes, contents, tmplNode, fragment);
		return fragment;
	}
	function _mergeNode(node, contents, tmplNode, clonedParent){
		var tagName = node.tagName;
		if (tagName.charCodeAt(0) !== 64) {
			// @
			return _cloneNode(node, contents, tmplNode, clonedParent);
		}

		var id = node.attr.id;
		if (tagName === tag_PLACEHOLDER && id == null)
			return tmplNode.nodes;

		if (tag_EACH === tagName) {
			var arr = contents[node.expression],
				x;
			if (arr == null) {
				log_error('No template node: @' + node.expression);
				return null;
			}
			if (is_Array(arr) === false) {
				x = arr;
				return _merge(
					node.nodes
					, _getContents(x.nodes, x.nodes, new Contents(contents))
					, x
					, clonedParent
				);
			}
			var fragment = new Dom.Fragment,
				imax = arr.length,
				i = -1;
			while ( ++i < imax ){
				x = arr[i];
				appendAny(fragment, _merge(
					node.nodes
					, _getContents(x.nodes, x.nodes, new Contents(contents))
					, x
					, clonedParent
				));
			}
			return fragment;
		}
		if (tag_IF === tagName) {
			var val = eval_(node.expression, contents, tmplNode);
			return val
				? _merge(node.nodes, contents, tmplNode, clonedParent)
				: null
				;
		}

		if (id == null)
			id = tagName.substring(1);

		var content = contents.$getNode(id);
		if (content == null)
			return null;

		if (content.parent)
			_modifyParents(clonedParent, content.parent);

		var contentNodes = content.nodes,
			wrapperNode;
		if (node.attr.as !== void 0) {
			var tagName_ = node.attr.as;
			wrapperNode = {
				type: dom_NODE,
				tagName: tagName_,
				attr: _mergeAttr(node.attr, content.attr, contents, tmplNode),
				parent: clonedParent,
				nodes: contentNodes
			};
			wrapperNode.attr.as = null;
		}

		if (node.nodes == null)
			return wrapperNode || contentNodes;

		var nodes =  _merge(
			node.nodes
			, _getContents(contentNodes, contentNodes, new Contents(contents))
			, content
			, wrapperNode || clonedParent
		);
		if (wrapperNode != null) {
			wrapperNode.nodes = nodes;
			return wrapperNode;
		}
		return nodes;
	}
	function _mergeAttr(a, b, contents, tmplNode){
		if (a == null || b == null)
			return a || b;

		var out = interpolate_obj_(a, contents, tmplNode);
		for (var key in b){
			out[key] = interpolate_str_(b[key], contents, tmplNode);
		}
		return out;
	}

	function _cloneNode(node, contents, tmplNode, clonedParent){
		var tagName = node.tagName || node.compoName;
		if (':template' === tagName) {
			var id = interpolate_str_(node.attr.id, contents, tmplNode);
			Mask.templates.register(id, node.nodes);
			return null;
		}
		if (':import' === tagName) {
			var id = interpolate_str_(node.attr.id, contents, tmplNode),
				nodes = Mask.templates.resolve(node, id);
			return _merge(nodes, contents, tmplNode, clonedParent);
		}
		var outnode = {
			type: node.type,
			tagName: tagName,
			attr: interpolate_obj_(node.attr, contents, tmplNode),
			expression: interpolate_str_(node.expression, contents, tmplNode),
			controller: node.controller,
			parent: clonedParent
		};
		if (node.nodes)
			outnode.nodes = _merge(node.nodes, contents, tmplNode, outnode);

		return outnode;
	}
	function _cloneTextNode(node, contents, tmplNode, clonedParent){
		return {
			type: node.type,
			content: interpolate_str_(node.content, contents, tmplNode),
			parent: clonedParent
		};
	}
	function interpolate_obj_(obj, contents, node){
		var clone = _Object_create(obj),
			x;
		for(var key in clone){
			x = clone[key];
			if (x == null)
				continue;

			clone[key] = interpolate_str_(x, contents, node);
		}
		return clone;
	}
	function interpolate_str_(mix, contents, node){
		var index = -1,
			isFn = false,
			str = mix;

		if (typeof mix === 'function') {
			isFn = true;
			str = mix();
		}
		if (typeof str !== 'string' || (index = str.indexOf('@')) === -1)
			return mix;

		var result = str.substring(0, index),
			length = str.length,
			isBlockEntry = str.charCodeAt(index + 1) === 91, // [
			last = -1,
			c;

		while (index < length) {
			// interpolation
			last = index;
			if (isBlockEntry === true) {
				index = str.indexOf(']', last);
				if (index === -1)
					index = length;
				last += 2;
			}
			else {
				while (index < length) {
					c = str.charCodeAt(++index);
					if (c === 36 || c === 95 || c === 46) {
						// $ _ .
						continue;
					}
					if ((48 <= c && c <= 57) ||		// 0-9
						(65 <= c && c <= 90) ||		// A-Z
						(97 <= c && c <= 122)) {	// a-z
						continue;
					}
					break;
				}
			}

			var expr = str.substring(last, index),
				fn = isBlockEntry ? eval_ : interpolate_,
				x = fn(expr, contents, node);

			if (x != null)
				result += x;

			// tail
			last = isBlockEntry ? (index + 1): index;
			index = str.indexOf('@', index);
			if (index === -1)
				index = length;

			result += str.substring(last, index);
		}

		return isFn
			? parser_ensureTemplateFunction(result)
			: result
			;
	}
	function interpolate_(path, contents, node) {
		var index = path.indexOf('.');
		if (index === -1) {
			log_warn('Merge templates. Accessing node', path);
			return '';
		}
		var tagName = path.substring(0, index),
			id = tagName.substring(1),
			property = path.substring(index + 1),
			obj = null;

		if (node != null) {
			if (tagName === '@attr')
				obj = node.attr;
			else if (tagName === node.tagName)
				obj = node;
		}

		if (obj == null)
			obj = contents.$getNode(id);

		if (obj == null) {
			log_error('Merge templates. Node not found', tagName);
			return '';
		}
		return obj_getProperty(obj, property);
	}

	function appendAny(node, mix){
		if (mix == null)
			return;
		if (typeof mix.concat === 'function') {
			var imax = mix.length;
			for (var i = 0; i < imax; i++) {
				appendAny(node, mix[i]);
			}
			return;
		}
		if (mix.type === dom_FRAGMENT) {
			appendAny(node, mix.nodes);
			return;
		}

		if (typeof node.appendChild === 'function') {
			node.appendChild(mix);
			return;
		}

		var l = node.length;
		if (l > 0) {
			var prev = node[l - 1];
			prev.nextSibling = mix;
		}
		node.push(mix);
	}

	var RESERVED = ' else placeholder each attr if parent scope'
	function _getContents(b, node, contents) {
		if (node == null)
			return contents;

		if (is_Array(node)) {
			var imax = node.length,
				i = -1;
			while( ++i < imax ){
				_getContents(node === b ? node[i] : b, node[i], contents);
			}
			return contents;
		}

		var type = node.type;
		if (type === dom_TEXTNODE)
			return contents;

		if (type === dom_NODE) {
			var tagName = node.tagName;
			if (tagName != null && tagName.charCodeAt(0) === 64) {
				// @
				var id = tagName.substring(1);
				// if DEBUG
				if (RESERVED.indexOf(' ' + id + ' ') !== -1)
					log_error('MaskMerge. Reserved Name', id);
				// endif
				var x = {
					tagName: node.tagName,
					parent: _getParentModifiers(b, node),
					nodes: node.nodes,
					attr: node.attr,
					expression: node.expression
				};
				if (contents[id] == null) {
					contents[id] = x;
				} else {
					var current = contents[id];
					if (is_Array(current)) {
						current.push(x);
					}
					else {
						contents[id] = [current, x];
					}
				}
				return contents;
			}
		}
		return _getContents(b, node.nodes, contents);
	}
	function _getParentModifiers(root, node) {
		if (node === root)
			return null;

		var current, parents, parent = node.parent;
		while (true) {
			if (parent == null)
				break;
			if (parent === root && root.type !== dom_NODE)
				break;

			var p = {
					type: parent.type,
					tagName: parent.tagName,
					attr: parent.attr,
					controller: parent.controller,
					expression: parent.expression,
					nodes: null,
					parent: null
				};

			if (parents == null) {
				current = parents = p;
			} else {
				current.parent = p;
				current = p;
			}
			parent = parent.parent;
		}
		return parents;
	}
	function _modifyParents(clonedParent, parents){
		var nodeParent = clonedParent, modParent = parents;
		while(nodeParent != null && modParent != null){

			if (modParent.tagName)
				nodeParent.tagName = modParent.tagName;

			if (modParent.expression)
				nodeParent.expression = modParent.expression;

			for(var key in modParent.attr){
				nodeParent.attr[key] = modParent.attr[key];
			}

			nodeParent = nodeParent.parent;
			modParent = modParent.parent;
		}
	}

	function eval_(expr, contents, tmplNode) {
		if (tmplNode)
			contents.attr = tmplNode.attr;

		return ExpressionUtil.eval(expr, contents, null, contents);
	}
	function Contents(parent){
		this.scope = this;
		this.parent = parent;
	}
	Contents.prototype = {
		parent: null,
		attr: null,
		scope: null,
		$getNode: function(id){
			var ctx = this, node;
			while(ctx != null){
				node = ctx[id];
				if (node != null)
					return node;
				ctx = ctx.parent;
			}
		}
	};

}());<commit_msg>* add Component type merger<commit_after>var mask_merge;
(function(){

	mask_merge = function(a, b, owner){
		if (typeof a === 'string')
			a = parser_parse(a);
		if (typeof b === 'string')
			b = parser_parse(b);

		var contents = _getContents(b, b, new Contents);
		return _merge(a, contents, owner);
	};

	var tag_ELSE = '@else',
		tag_IF = '@if',
		tag_EACH = '@each',
		tag_PLACEHOLDER = '@placeholder',

		dom_NODE = Dom.NODE,
		dom_TEXTNODE = Dom.TEXTNODE,
		dom_FRAGMENT = Dom.FRAGMENT,
		dom_STATEMENT = Dom.STATEMENT,
		dom_COMPONENT = Dom.COMPONENT
		;

	function _merge(node, contents, tmplNode, clonedParent){
		if (node == null)
			return null;

		var fn;
		if (is_Array(node)) {
			fn = _mergeArray;
		} else {
			switch(node.type){
				case dom_TEXTNODE:
					fn = _cloneTextNode;
					break;
				case dom_NODE:
				case dom_STATEMENT:
					fn = _mergeNode;
					break;
				case dom_FRAGMENT:
					fn = _mergeFragment;
					break;
				case dom_COMPONENT:
					fn = _mergeComponent;
					break;
			}
		}
		if (fn !== void 0) {
			return fn(node, contents, tmplNode, clonedParent);
		}
		log_warn('Uknown type', node.type);
		return null;
	}
	function _mergeArray(nodes, contents, tmplNode, clonedParent){
		var fragment = [],
			imax = nodes.length,
			i = -1,
			x, node;
		while( ++i < imax ) {
			node = nodes[i];

			if (node.tagName === tag_ELSE) {
				// check previous
				if (x != null)
					continue;

				if (node.expression && !eval_(node.expression, contents, tmplNode))
					continue;

				x = _merge(nodes[i].nodes, contents, tmplNode, clonedParent)
			}
			else {
				x = _merge(node, contents, tmplNode, clonedParent);
			}

			appendAny(fragment, x);
		}
		return fragment;
	}
	function _mergeFragment(frag, contents, tmplNode, clonedParent) {
		var fragment = new Dom.Fragment;
		fragment.parent = clonedParent;
		fragment.nodes = _mergeArray(frag.nodes, contents, tmplNode, fragment);
		return fragment;
	}
	function _mergeComponent(node, contents, tmplNode, clonedParent) {
		if (node.nodes == null)
			return node;

		var cloned = new Dom.Component;
		obj_extend(cloned, node);
		cloned.nodes = _merge(cloned.nodes, contents, tmplNode, clonedParent);
		return cloned;
	}
	function _mergeNode(node, contents, tmplNode, clonedParent){
		var tagName = node.tagName;
		if (tagName.charCodeAt(0) !== 64) {
			// @
			return _cloneNode(node, contents, tmplNode, clonedParent);
		}

		var id = node.attr.id;
		if (tagName === tag_PLACEHOLDER && id == null)
			return tmplNode.nodes;

		if (tag_EACH === tagName) {
			var arr = contents[node.expression],
				x;
			if (arr == null) {
				log_error('No template node: @' + node.expression);
				return null;
			}
			if (is_Array(arr) === false) {
				x = arr;
				return _merge(
					node.nodes
					, _getContents(x.nodes, x.nodes, new Contents(contents))
					, x
					, clonedParent
				);
			}
			var fragment = new Dom.Fragment,
				imax = arr.length,
				i = -1;
			while ( ++i < imax ){
				x = arr[i];
				appendAny(fragment, _merge(
					node.nodes
					, _getContents(x.nodes, x.nodes, new Contents(contents))
					, x
					, clonedParent
				));
			}
			return fragment;
		}
		if (tag_IF === tagName) {
			var val = eval_(node.expression, contents, tmplNode);
			return val
				? _merge(node.nodes, contents, tmplNode, clonedParent)
				: null
				;
		}

		if (id == null)
			id = tagName.substring(1);

		var content = contents.$getNode(id);
		if (content == null)
			return null;

		if (content.parent)
			_modifyParents(clonedParent, content.parent);

		var contentNodes = content.nodes,
			wrapperNode;
		if (node.attr.as !== void 0) {
			var tagName_ = node.attr.as;
			wrapperNode = {
				type: dom_NODE,
				tagName: tagName_,
				attr: _mergeAttr(node.attr, content.attr, contents, tmplNode),
				parent: clonedParent,
				nodes: contentNodes
			};
			wrapperNode.attr.as = null;
		}

		if (node.nodes == null)
			return wrapperNode || contentNodes;

		var nodes =  _merge(
			node.nodes
			, _getContents(contentNodes, contentNodes, new Contents(contents))
			, content
			, wrapperNode || clonedParent
		);
		if (wrapperNode != null) {
			wrapperNode.nodes = nodes;
			return wrapperNode;
		}
		return nodes;
	}
	function _mergeAttr(a, b, contents, tmplNode){
		if (a == null || b == null)
			return a || b;

		var out = interpolate_obj_(a, contents, tmplNode);
		for (var key in b){
			out[key] = interpolate_str_(b[key], contents, tmplNode);
		}
		return out;
	}

	function _cloneNode(node, contents, tmplNode, clonedParent){
		var tagName = node.tagName || node.compoName;
		if (':template' === tagName) {
			var id = interpolate_str_(node.attr.id, contents, tmplNode);
			Mask.templates.register(id, node.nodes);
			return null;
		}
		if (':import' === tagName) {
			var id = interpolate_str_(node.attr.id, contents, tmplNode),
				nodes = Mask.templates.resolve(node, id);
			return _merge(nodes, contents, tmplNode, clonedParent);
		}
		var outnode = {
			type: node.type,
			tagName: tagName,
			attr: interpolate_obj_(node.attr, contents, tmplNode),
			expression: interpolate_str_(node.expression, contents, tmplNode),
			controller: node.controller,
			parent: clonedParent
		};
		if (node.nodes)
			outnode.nodes = _merge(node.nodes, contents, tmplNode, outnode);

		return outnode;
	}
	function _cloneTextNode(node, contents, tmplNode, clonedParent){
		return {
			type: node.type,
			content: interpolate_str_(node.content, contents, tmplNode),
			parent: clonedParent
		};
	}
	function interpolate_obj_(obj, contents, node){
		var clone = _Object_create(obj),
			x;
		for(var key in clone){
			x = clone[key];
			if (x == null)
				continue;

			clone[key] = interpolate_str_(x, contents, node);
		}
		return clone;
	}
	function interpolate_str_(mix, contents, node){
		var index = -1,
			isFn = false,
			str = mix;

		if (typeof mix === 'function') {
			isFn = true;
			str = mix();
		}
		if (typeof str !== 'string' || (index = str.indexOf('@')) === -1)
			return mix;

		var result = str.substring(0, index),
			length = str.length,
			isBlockEntry = str.charCodeAt(index + 1) === 91, // [
			last = -1,
			c;

		while (index < length) {
			// interpolation
			last = index;
			if (isBlockEntry === true) {
				index = str.indexOf(']', last);
				if (index === -1)
					index = length;
				last += 2;
			}
			else {
				while (index < length) {
					c = str.charCodeAt(++index);
					if (c === 36 || c === 95 || c === 46) {
						// $ _ .
						continue;
					}
					if ((48 <= c && c <= 57) ||		// 0-9
						(65 <= c && c <= 90) ||		// A-Z
						(97 <= c && c <= 122)) {	// a-z
						continue;
					}
					break;
				}
			}

			var expr = str.substring(last, index),
				fn = isBlockEntry ? eval_ : interpolate_,
				x = fn(expr, contents, node);

			if (x != null)
				result += x;

			// tail
			last = isBlockEntry ? (index + 1): index;
			index = str.indexOf('@', index);
			if (index === -1)
				index = length;

			result += str.substring(last, index);
		}

		return isFn
			? parser_ensureTemplateFunction(result)
			: result
			;
	}
	function interpolate_(path, contents, node) {
		var index = path.indexOf('.');
		if (index === -1) {
			log_warn('Merge templates. Accessing node', path);
			return '';
		}
		var tagName = path.substring(0, index),
			id = tagName.substring(1),
			property = path.substring(index + 1),
			obj = null;

		if (node != null) {
			if (tagName === '@attr')
				obj = node.attr;
			else if (tagName === node.tagName)
				obj = node;
		}

		if (obj == null)
			obj = contents.$getNode(id);

		if (obj == null) {
			log_error('Merge templates. Node not found', tagName);
			return '';
		}
		return obj_getProperty(obj, property);
	}

	function appendAny(node, mix){
		if (mix == null)
			return;
		if (typeof mix.concat === 'function') {
			var imax = mix.length;
			for (var i = 0; i < imax; i++) {
				appendAny(node, mix[i]);
			}
			return;
		}
		if (mix.type === dom_FRAGMENT) {
			appendAny(node, mix.nodes);
			return;
		}

		if (typeof node.appendChild === 'function') {
			node.appendChild(mix);
			return;
		}

		var l = node.length;
		if (l > 0) {
			var prev = node[l - 1];
			prev.nextSibling = mix;
		}
		node.push(mix);
	}

	var RESERVED = ' else placeholder each attr if parent scope'
	function _getContents(b, node, contents) {
		if (node == null)
			return contents;

		if (is_Array(node)) {
			var imax = node.length,
				i = -1;
			while( ++i < imax ){
				_getContents(node === b ? node[i] : b, node[i], contents);
			}
			return contents;
		}

		var type = node.type;
		if (type === dom_TEXTNODE)
			return contents;

		if (type === dom_NODE) {
			var tagName = node.tagName;
			if (tagName != null && tagName.charCodeAt(0) === 64) {
				// @
				var id = tagName.substring(1);
				// if DEBUG
				if (RESERVED.indexOf(' ' + id + ' ') !== -1)
					log_error('MaskMerge. Reserved Name', id);
				// endif
				var x = {
					tagName: node.tagName,
					parent: _getParentModifiers(b, node),
					nodes: node.nodes,
					attr: node.attr,
					expression: node.expression
				};
				if (contents[id] == null) {
					contents[id] = x;
				} else {
					var current = contents[id];
					if (is_Array(current)) {
						current.push(x);
					}
					else {
						contents[id] = [current, x];
					}
				}
				return contents;
			}
		}
		return _getContents(b, node.nodes, contents);
	}
	function _getParentModifiers(root, node) {
		if (node === root)
			return null;

		var current, parents, parent = node.parent;
		while (true) {
			if (parent == null)
				break;
			if (parent === root && root.type !== dom_NODE)
				break;

			var p = {
					type: parent.type,
					tagName: parent.tagName,
					attr: parent.attr,
					controller: parent.controller,
					expression: parent.expression,
					nodes: null,
					parent: null
				};

			if (parents == null) {
				current = parents = p;
			} else {
				current.parent = p;
				current = p;
			}
			parent = parent.parent;
		}
		return parents;
	}
	function _modifyParents(clonedParent, parents){
		var nodeParent = clonedParent, modParent = parents;
		while(nodeParent != null && modParent != null){

			if (modParent.tagName)
				nodeParent.tagName = modParent.tagName;

			if (modParent.expression)
				nodeParent.expression = modParent.expression;

			for(var key in modParent.attr){
				nodeParent.attr[key] = modParent.attr[key];
			}

			nodeParent = nodeParent.parent;
			modParent = modParent.parent;
		}
	}

	function eval_(expr, contents, tmplNode) {
		if (tmplNode)
			contents.attr = tmplNode.attr;

		return ExpressionUtil.eval(expr, contents, null, contents);
	}
	function Contents(parent){
		this.scope = this;
		this.parent = parent;
	}
	Contents.prototype = {
		parent: null,
		attr: null,
		scope: null,
		$getNode: function(id){
			var ctx = this, node;
			while(ctx != null){
				node = ctx[id];
				if (node != null)
					return node;
				ctx = ctx.parent;
			}
		}
	};

}());<|endoftext|>


<commit_before>import React from 'react';
import FilterOptions from './FilterOptions';
import _isUndefined from 'lodash/isUndefined';

class Filter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {clearFieldIsVisible: false};
    this.addSearchValueToFilterValues = this.addSearchValueToFilterValues.bind(this);
  }

  /**
   * shouldComponentUpdate - description
   * Componente não deve atualizar caso o estado clearFieldIsVisible
   * seja o mesmo do próximo estado
   * @param  {type} nextProps description
   * @param  {type} nextState description
   * @return {boolean}
   */
  shouldComponentUpdate(nextProps, nextState) {
    return this.state.clearFieldIsVisible !== nextState.clearFieldIsVisible;
  }

  toggleClearField(e) {
    this.setState({clearFieldIsVisible: e.target.value !== ''});
  }

  /**
   * addSearchValueToFilterValues - description
   * Função executada pelo filho FilterOptions. Recebe o objeto com os valores do
   * filtro e adiciona o valor do campo de busca a este objeto.
   * @param  {type} filterValues description
   * @return {void}
   */
  addSearchValueToFilterValues(filterValues) {
    let searchFieldValue = document.getElementsByName(this.props.name)[0].value;
    filterValues[this.props.name] = searchFieldValue;
    this.props.onFilter(filterValues);
  }

  /**
   * doFilterBySearchField - description
   * Função executada quando o usuario usa o enter para realizar o filtro.
   * Se o filtro é um filtro com opções, uma função do filho FilterOptions é
   * chamada para recuperar os valores das opções, em seguida é chamada
   * a função doFilterByApplyFilter do pai para acrescentar a este objeto o valor
   * do campo de busca
   * @param  {type} e description
   * @return {void}
   */
  doFilterBySearchField(e) {
    let isFilterWithOptions = this.refs.filterOptions.props.children;
    if(isFilterWithOptions) {
      this.refs.filterOptions.mountFilterOptionsObject();
    } else {
      this.props.onFilter(e.target.value);
    }
  }

  clearSearchField() {
    document.getElementsByName(this.props.name)[0].value = '';
    let isFilterWithOptions = this.refs.filterOptions.props.children;

    this.setState({clearFieldIsVisible: false}, () => {
      //A função que aplica o filtro é chamada para não perder os possiveis filtros ja feitos
      if(isFilterWithOptions) {
        this.refs.filterOptions.mountFilterOptionsObject();
      } else {
        this.props.onFilter('');
      }
    });
  }

  render() {

    const {
      applyFilterButtonLabel,
      cancelButtonLabel,
      clearFilterOptionsButtonLabel,
      filterButtonLabel,
      placeholder,
      name,
      children,
      onClearAll,
      onFilter,
    } = this.props;

    let clearFieldIcon = '';

    if(this.state.clearFieldIsVisible) {
      clearFieldIcon = (

           this.clearSearchField()}
            style={{'lineHeight': '34px',
              'marginLeft': '-25px',
              'cursor': 'pointer'}}/>

      );
    }

    return (
      <form className='sv-form'>

          <input
            className='on-center'
            name={name}
            onKeyUp={(e) => {this.toggleClearField(e); this.doFilterBySearchField(e);}}
            placeholder={placeholder}
            ref='searchField'
            type='search'/>
            {clearFieldIcon}
          <FilterOptions
            addSearchValueToFilterValues={this.addSearchValueToFilterValues}
            applyFilterButtonLabel={applyFilterButtonLabel}
            cancelButtonLabel={cancelButtonLabel}
            clearFilterOptionsButtonLabel={clearFilterOptionsButtonLabel}
            filterButtonLabel={filterButtonLabel}
            hasFilterOptions={!_isUndefined(children)}
            onClearAll={onClearAll}
            onFilter={onFilter}
            ref='filterOptions'>
            {this.props.children}
          </FilterOptions>

      </form>
    );
  }
}

Filter.defaultProps = {
  applyFilterButtonLabel: 'Aplicar Filtro',
  cancelButtonLabel: 'Cancelar',
  clearFilterOptionsButtonLabel: 'Limpar',
  filterButtonLabel: 'Filtro',
  placeholder: 'Buscar',
};

Filter.propTypes = {
  applyFilterButtonLabel: React.PropTypes.string,
  cancelButtonLabel: React.PropTypes.string,
  clearFilterOptionsButtonLabel: React.PropTypes.string,
  filterButtonLabel: React.PropTypes.string,
  name: React.PropTypes.string.isRequired,
  onClearAll: React.PropTypes.func,
  onFilter: React.PropTypes.func,
  placeholder: React.PropTypes.string,
};

Filter.displayName = 'Filter';

export default Filter;
<commit_msg>fix de um bug no filtro que fechava as opções quando o enter era pressionado<commit_after>import React from 'react';
import FilterOptions from './FilterOptions';
import _isUndefined from 'lodash/isUndefined';

class Filter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {clearFieldIsVisible: false};
    this.addSearchValueToFilterValues = this.addSearchValueToFilterValues.bind(this);
  }

  /**
   * shouldComponentUpdate - description
   * Componente não deve atualizar caso o estado clearFieldIsVisible
   * seja o mesmo do próximo estado
   * @param  {type} nextProps description
   * @param  {type} nextState description
   * @return {boolean}
   */
  shouldComponentUpdate(nextProps, nextState) {
    return this.state.clearFieldIsVisible !== nextState.clearFieldIsVisible;
  }

  toggleClearField(e) {
    this.setState({clearFieldIsVisible: e.target.value !== ''});
  }

  /**
   * addSearchValueToFilterValues - description
   * Função executada pelo filho FilterOptions. Recebe o objeto com os valores do
   * filtro e adiciona o valor do campo de busca a este objeto.
   * @param  {type} filterValues description
   * @return {void}
   */
  addSearchValueToFilterValues(filterValues) {
    let searchFieldValue = document.getElementsByName(this.props.name)[0].value;
    filterValues[this.props.name] = searchFieldValue;
    this.props.onFilter(filterValues);
  }

  /**
   * doFilterBySearchField - description
   * Função executada quando o usuario usa o enter para realizar o filtro.
   * Se o filtro é um filtro com opções, uma função do filho FilterOptions é
   * chamada para recuperar os valores das opções, em seguida é chamada
   * a função doFilterByApplyFilter do pai para acrescentar a este objeto o valor
   * do campo de busca
   * @param  {type} e description
   * @return {void}
   */
  doFilterBySearchField(e) {
    let isFilterWithOptions = this.refs.filterOptions.props.children;
    if(isFilterWithOptions) {
      this.refs.filterOptions.mountFilterOptionsObject();
    } else {
      this.props.onFilter(e.target.value);
    }
  }

  clearSearchField() {
    document.getElementsByName(this.props.name)[0].value = '';
    let isFilterWithOptions = this.refs.filterOptions.props.children;

    this.setState({clearFieldIsVisible: false}, () => {
      //A função que aplica o filtro é chamada para não perder os possiveis filtros ja feitos
      if(isFilterWithOptions) {
        this.refs.filterOptions.mountFilterOptionsObject();
      } else {
        this.props.onFilter('');
      }
    });
  }

  render() {

    const {
      applyFilterButtonLabel,
      cancelButtonLabel,
      clearFilterOptionsButtonLabel,
      filterButtonLabel,
      placeholder,
      name,
      children,
      onClearAll,
      onFilter,
    } = this.props;

    let clearFieldIcon = '';

    if(this.state.clearFieldIsVisible) {
      clearFieldIcon = (

           this.clearSearchField()}
            style={{'lineHeight': '34px',
              'marginLeft': '-25px',
              'cursor': 'pointer'}}/>

      );
    }

    return (
      <form className='sv-form' onSubmit={(e) => e.preventDefault()}>

          <input
            className='on-center'
            name={name}
            onKeyUp={(e) => {this.toggleClearField(e); this.doFilterBySearchField(e);}}
            placeholder={placeholder}
            ref='searchField'
            type='search'/>
            {clearFieldIcon}
          <FilterOptions
            addSearchValueToFilterValues={this.addSearchValueToFilterValues}
            applyFilterButtonLabel={applyFilterButtonLabel}
            cancelButtonLabel={cancelButtonLabel}
            clearFilterOptionsButtonLabel={clearFilterOptionsButtonLabel}
            filterButtonLabel={filterButtonLabel}
            hasFilterOptions={!_isUndefined(children)}
            onClearAll={onClearAll}
            onFilter={onFilter}
            ref='filterOptions'>
            {this.props.children}
          </FilterOptions>

      </form>
    );
  }
}

Filter.defaultProps = {
  applyFilterButtonLabel: 'Aplicar Filtro',
  cancelButtonLabel: 'Cancelar',
  clearFilterOptionsButtonLabel: 'Limpar',
  filterButtonLabel: 'Filtro',
  placeholder: 'Buscar',
};

Filter.propTypes = {
  applyFilterButtonLabel: React.PropTypes.string,
  cancelButtonLabel: React.PropTypes.string,
  clearFilterOptionsButtonLabel: React.PropTypes.string,
  filterButtonLabel: React.PropTypes.string,
  name: React.PropTypes.string.isRequired,
  onClearAll: React.PropTypes.func,
  onFilter: React.PropTypes.func,
  placeholder: React.PropTypes.string,
};

Filter.displayName = 'Filter';

export default Filter;
<|endoftext|>


<commit_before>define('fixed-element', ['require', 'util', 'layout'], function(require) {
    'use strict';

    var util = require('util');
    var layout = require('layout');
    var platform = util.platform;
    var css = util.css;

    /**
     * The fixed element processor.
     * @class
     */
    function FixedElement() {
        /**
         * @private
         * @type {HTMLElement}
         */
        this._fixedLayer = null;

        /**
         * @private
         * @type {number}
         */
        this._count = 0;

        /**
         * @private
         * @type {Object}
         */
        this._fixedTypes = {
            'top': 1,
            'bottom': 1,
            'gototop': 1,
            'other': 1
        };

        /**
         * Whether the platform is android and uc browser.
         * @private
         * @type {boolean}
         */
        this._isAndroidUc = platform.isUc() && !platform.isIos();

        /**
         * @private
         * @type {Array.<FixedElement>}
         */
        this._fixedElements = [];
    }

    /**
     * Initializition of current fixed element processor.
     */
    FixedElement.prototype.init = function () {
        var mipFixedElements = document.querySelectorAll('mip-fixed');
        this.setFixedElement(mipFixedElements);
        var fixedLen = this._fixedElements.length;
        var hasParentPage = window.parent !== window;
        if ((platform.isIos()) && fixedLen > 0 && hasParentPage) {
            var fixedLayer = this.getFixedLayer();

            for (var i = 0; i < fixedLen; i++) {
                this.moveToFixedLayer(this._fixedElements[i], i);
            }
        }
        if (hasParentPage) {
            this.doCustomElements();
        }
    };

    /**
     * Process some fixed elements.
     * @param {Array.<MIPElement>} fixedElements
     */
    FixedElement.prototype.setFixedElement = function (fixedElements) {
        var fixedEle = {};
        var fixedTypeCount = {};
        for (var i = 0; i < fixedElements.length; i++) {
            var ele = fixedElements[i];
            // Calculate z-index based on the declared z-index and DOM position.
            css(ele, {
                'z-index': 10000 - i
            });
            // While platform is android-uc, change the position to 'absolute'.
            if (this._isAndroidUc) {
                css(ele, {
                    position: 'absolute'
                });
            }
            var fType = ele.getAttribute('type');
            var transfType = (fType == 'right' || fType == 'left') ? 'other': fType;
            if (transfType === 'gototop' && ele.firstElementChild.tagName.toLowerCase() !== 'mip-gototop') {
                continue;
            }
            if (this._fixedTypes[transfType] && this._fixedTypes[transfType] < 2) {
                this._fixedTypes[transfType] += 1;
                this.setFixedElementRule(ele, fType);
                if (this._fixedTypes[transfType] < 2) {
                    this._count--;
                    continue;
                }
                var eleId = 'Fixed' + (this._count++);
                fixedEle = {
                    id: eleId,
                    element: fixedElements[i]
                };
                fixedEle.element.setAttribute('mipdata-fixedIdx', eleId);
                this._fixedElements.push(fixedEle);
            } else {
                ele.parentElement.removeChild(ele);
            }
        }
    };

    /**
     * Create the fixed layer of current object if it does not exsit and return it.
     * @return {Element}
     */
    FixedElement.prototype.getFixedLayer = function () {
        if (this._fixedLayer) {
            return this._fixedLayer;
        }
        this._fixedLayer = document.createElement('body');
        this._fixedLayer.className = 'mip-fixedlayer';
        var height = (this._isAndroidUc) ? '100%': 0;
        var width = (this._isAndroidUc) ? '100%': 0;
        css(this._fixedLayer, {
            'position': 'absolute',
            'top': 0,
            'left': 0,
            'height': height,
            'width': width,
            'pointer-events': 'none',
            'overflow': 'hidden',
            'animation': 'none',
            '-webkit-animation': 'none',
            'border': 'none',
            'box-sizing': 'border-box',
            'box-shadow': 'none',
            'display': 'block',
            'float': 'none',
            'margin': 0,
            'opacity': 1,
            'outline': 'none',
            'transform': 'none',
            'transition': 'none',
            'visibility': 'visible',
            'background': 'none'
        });
        var html = document.getElementsByTagName('html')[0];
        html.appendChild(this._fixedLayer);
        return this._fixedLayer;
    };

    /**
     * Move a fixed element to the fixed layer.
     * @param {MIPElement} fixedEle
     * @param {string} idx
     */
    FixedElement.prototype.moveToFixedLayer = function (fixedEle, idx) {
        var element = fixedEle.element;
        if (element.parentElement == this._fixedLayer) {
            return;
        }
        if (!fixedEle.placeholder) {
            css(element, {
                'pointer-events': 'initial'
            });
            fixedEle.placeholder = document.createElement('mip-i-ph');
            fixedEle.placeholder.setAttribute('mipdata-fixedIdx', fixedEle.id);
            fixedEle.placeholder.style.display = 'none';
        }

        element.parentElement.replaceChild(fixedEle.placeholder, element);
        this.getFixedLayer().appendChild(element);

    };

    /**
     * Process custom elements created by user.
     */
    FixedElement.prototype.doCustomElements = function () {
        var stylesheets = document.styleSheets;
        if (!stylesheets) {
            return;
        }
        // Find the 'position: fixed' elements.
        var fixedSelectors = [];
        for (var i = 0; i < stylesheets.length; i++) {
            var stylesheet = stylesheets[i];
            if (stylesheet.disabled || !stylesheet.ownerNode || stylesheet.ownerNode.tagName != 'STYLE' || stylesheet.ownerNode.hasAttribute('mip-extension')) {
                continue;
            }
            this._findFixedSelectors(stylesheet.cssRules);
        }
    };

    /**
     * Find the selectors of 'position: fixed' elements.
     * CSSRule: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants
     */
    FixedElement.prototype._findFixedSelectors = function (cssRules) {
        for (var i = 0; i < cssRules.length; i++) {
            var cssRule = cssRules[i];
            var rType = cssRule.type;
            if (rType == 1) {
                /* CSSStyleRule */
                if (cssRule.selectorText != '*' && cssRule.style.position == 'fixed') {
                    try {
                        var fixedSelector = cssRule.selectorText;
                        var elements = document.querySelectorAll(fixedSelector);
                        for (var j = 0; j < elements.length; j++) {
                            // remove ?
                            elements[j].parentElement.removeChild(elements[j]);
                        }
                    } catch(e) {
                        console.warn('Cannot find the selector of custom fixed elements');
                    }
                }
            } else if (rType == 4) {
                /* CSSMediaRule */
                this._findFixedSelectors(cssRule.cssRules);
            } else if (rType == 12) {
                /* CSSSupportsRule */
                this._findFixedSelectors(cssRule.cssRules);
            }
        }
    };

    /**
     * Set styles of a fixed element with type.
     * @param {MIPElement} fixedEle
     * @param {string} type Layout type of the fixedEle.
     */
    FixedElement.prototype.setFixedElementRule = function (fixedEle, type) {
        switch (type) {
            case "top":
                fixedEle.style.maxHeight = '88px';
                break;
            case "bottom":
                fixedEle.style.maxHeight = '88px';
                break;
            case "right":
                this.setStyle(fixedEle);
                fixedEle.style.maxHeight = '25%';
                fixedEle.style.maxWidth = '10%';
                break;
            case "left":
                this.setStyle(fixedEle);
                fixedEle.style.maxHeight = '25%';
                fixedEle.style.maxWidth = '10%';
                break;
            case 'gototop':

                var bottom = fixedEle.getAttribute('bottom') || '88';
                var right = fixedEle.getAttribute('right') || '10';
                var width = window.innerWidth ? window.innerWidth
                                : document.body ? document.body.clientWidth : 0;

                bottom = Math.max(parseInt(bottom.match(/\d+/g)[0]), 85);
                right = Math.max(parseInt(right.match(/\d+/g)[0], 10) / width * 100, 10);

                fixedEle.style.bottom = bottom + 'px';
                fixedEle.style.right = right + '%';

                break;
            default:
                fixedEle.style.display = 'none';
        }
    };

    /**
     * Set styles of a fixed element.
     * @param {MIPElement} fixedEle
     */
    FixedElement.prototype.setStyle = function (fixedEle) {
        var bottom = layout.parseLength(fixedEle.getAttribute('bottom'));
        if (bottom) {
            fixedEle.style.bottom = bottom;
            return;
        }
        var top = layout.parseLength(fixedEle.getAttribute('top'));
        if (top) {
            fixedEle.style.top = top;
            return;
        }
        if (!top && !bottom) {
            fixedEle.parentElement.removeChild(fixedEle);
            // It will not be counted if the elements's type is non-standard.
            this._fixedTypes.other -= 1;
        }
    };

    /**
     * Show fixed layer
     * @param {HTMLElement} layer
     */
    FixedElement.prototype.showFixedLayer = function(layer) {
        if(layer) {
            css(layer, {
                display: 'block'
            });
        }
    };

    /**
     * Hide fixed layer
     * @param {HTMLElement} layer
     */
    FixedElement.prototype.hideFixedLayer = function(layer) {
        if(layer) {
            css(layer, {
                display: 'none'
            });
        }
    };

    return new FixedElement();
});

<commit_msg>Fix kuawei p8 "position:fixed" bug<commit_after>define('fixed-element', ['require', 'util', 'layout'], function(require) {
    'use strict';

    var util = require('util');
    var layout = require('layout');
    var platform = util.platform;
    var css = util.css;

    /**
     * The fixed element processor.
     * @class
     */
    function FixedElement() {
        /**
         * @private
         * @type {HTMLElement}
         */
        this._fixedLayer = null;

        /**
         * @private
         * @type {number}
         */
        this._count = 0;

        /**
         * @private
         * @type {Object}
         */
        this._fixedTypes = {
            'top': 1,
            'bottom': 1,
            'gototop': 1,
            'other': 1
        };

        /**
         * Whether the platform is android and uc browser.
         * @private
         * @type {boolean}
         */
        this._isAndroidUc = platform.isUc() && !platform.isIos();

        /**
         * @private
         * @type {Array.<FixedElement>}
         */
        this._fixedElements = [];
    }

    /**
     * Initializition of current fixed element processor.
     */
    FixedElement.prototype.init = function () {
        var mipFixedElements = document.querySelectorAll('mip-fixed');
        this.setFixedElement(mipFixedElements);
        var fixedLen = this._fixedElements.length;
        var hasParentPage = window.parent !== window;
        if ((platform.isIos()) && fixedLen > 0 && hasParentPage) {
            var fixedLayer = this.getFixedLayer();

            for (var i = 0; i < fixedLen; i++) {
                this.moveToFixedLayer(this._fixedElements[i], i);
            }
        }
        if (hasParentPage) {
            this.doCustomElements();
        }
    };

    /**
     * Process some fixed elements.
     * @param {Array.<MIPElement>} fixedElements
     */
    FixedElement.prototype.setFixedElement = function (fixedElements) {
        var fixedEle = {};
        var fixedTypeCount = {};
        for (var i = 0; i < fixedElements.length; i++) {
            var ele = fixedElements[i];
            // Calculate z-index based on the declared z-index and DOM position.
            css(ele, {
                'z-index': 10000 - i
            });
            // While platform is android-uc, change the position to 'absolute'.
            if (this._isAndroidUc) {
                css(ele, {
                    position: 'absolute'
                });
            }
            var fType = ele.getAttribute('type');
            var transfType = (fType == 'right' || fType == 'left') ? 'other': fType;
            if (transfType === 'gototop' && ele.firstElementChild.tagName.toLowerCase() !== 'mip-gototop') {
                continue;
            }
            if (this._fixedTypes[transfType] && this._fixedTypes[transfType] < 2) {
                this._fixedTypes[transfType] += 1;
                this.setFixedElementRule(ele, fType);
                if (this._fixedTypes[transfType] < 2) {
                    this._count--;
                    continue;
                }
                var eleId = 'Fixed' + (this._count++);
                fixedEle = {
                    id: eleId,
                    element: fixedElements[i]
                };
                fixedEle.element.setAttribute('mipdata-fixedIdx', eleId);
                this._fixedElements.push(fixedEle);
            } else {
                ele.parentElement.removeChild(ele);
            }
        }
    };

    /**
     * Create the fixed layer of current object if it does not exsit and return it.
     * @return {Element}
     */
    FixedElement.prototype.getFixedLayer = function () {
        if (this._fixedLayer) {
            return this._fixedLayer;
        }
        this._fixedLayer = document.createElement('body');
        this._fixedLayer.className = 'mip-fixedlayer';
        var height = (this._isAndroidUc) ? '100%': 0;
        var width = (this._isAndroidUc) ? '100%': 0;
        css(this._fixedLayer, {
            'position': 'absolute',
            'top': 0,
            'left': 0,
            'height': height,
            'width': width,
            'pointer-events': 'none',
            'overflow': 'hidden',
            'animation': 'none',
            '-webkit-animation': 'none',
            'border': 'none',
            'box-sizing': 'border-box',
            'box-shadow': 'none',
            'display': 'block',
            'float': 'none',
            'margin': 0,
            'opacity': 1,
            'outline': 'none',
            'transform': 'none',
            'transition': 'none',
            'visibility': 'visible',
            'background': 'none'
        });
        var html = document.getElementsByTagName('html')[0];
        html.appendChild(this._fixedLayer);
        return this._fixedLayer;
    };

    /**
     * Move a fixed element to the fixed layer.
     * @param {MIPElement} fixedEle
     * @param {string} idx
     */
    FixedElement.prototype.moveToFixedLayer = function (fixedEle, idx) {
        var element = fixedEle.element;
        if (element.parentElement == this._fixedLayer) {
            return;
        }
        if (!fixedEle.placeholder) {
            css(element, {
                'pointer-events': 'initial'
            });
            fixedEle.placeholder = document.createElement('mip-i-ph');
            fixedEle.placeholder.setAttribute('mipdata-fixedIdx', fixedEle.id);
            fixedEle.placeholder.style.display = 'none';
        }

        element.parentElement.replaceChild(fixedEle.placeholder, element);
        this.getFixedLayer().appendChild(element);

    };

    /**
     * Process custom elements created by user.
     */
    FixedElement.prototype.doCustomElements = function () {
        var stylesheets = document.styleSheets;
        if (!stylesheets) {
            return;
        }
        // Find the 'position: fixed' elements.
        var fixedSelectors = [];
        for (var i = 0; i < stylesheets.length; i++) {
            var stylesheet = stylesheets[i];
            if (stylesheet.disabled || !stylesheet.ownerNode || stylesheet.ownerNode.tagName != 'STYLE' || stylesheet.ownerNode.hasAttribute('mip-extension')) {
                continue;
            }
            this._findFixedSelectors(stylesheet.cssRules);
        }
    };

    /**
     * Find the selectors of 'position: fixed' elements.
     * CSSRule: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants
     */
    FixedElement.prototype._findFixedSelectors = function (cssRules) {
        for (var i = 0; i < cssRules.length; i++) {
            var cssRule = cssRules[i];
            var rType = cssRule.type;
            if (rType == 1) {
                /* CSSStyleRule */
                if (cssRule.selectorText != '*' && cssRule.style.position == 'fixed') {
                    try {
                        var fixedSelector = cssRule.selectorText;
                        var elements = document.querySelectorAll(fixedSelector);
                        for (var j = 0; j < elements.length; j++) {
                            // remove ?
                            elements[j].parentElement.removeChild(elements[j]);
                        }
                    } catch(e) {
                        console.warn('Cannot find the selector of custom fixed elements');
                    }
                }
            } else if (rType == 4) {
                /* CSSMediaRule */
                this._findFixedSelectors(cssRule.cssRules);
            } else if (rType == 12) {
                /* CSSSupportsRule */
                this._findFixedSelectors(cssRule.cssRules);
            }
        }
    };

    /**
     * Set styles of a fixed element with type.
     * @param {MIPElement} fixedEle
     * @param {string} type Layout type of the fixedEle.
     */
    FixedElement.prototype.setFixedElementRule = function (fixedEle, type) {
        switch (type) {
            case "top":
                fixedEle.style.maxHeight = '44px';
                fixedEle.style.top = '44px';
                break;
            case "bottom":
                fixedEle.style.maxHeight = '88px';
                break;
            case "right":
                this.setStyle(fixedEle);
                fixedEle.style.maxHeight = '25%';
                fixedEle.style.maxWidth = '10%';
                break;
            case "left":
                this.setStyle(fixedEle);
                fixedEle.style.maxHeight = '25%';
                fixedEle.style.maxWidth = '10%';
                break;
            case 'gototop':

                var bottom = fixedEle.getAttribute('bottom') || '88';
                var right = fixedEle.getAttribute('right') || '10';
                var width = window.innerWidth ? window.innerWidth
                                : document.body ? document.body.clientWidth : 0;

                bottom = Math.max(parseInt(bottom.match(/\d+/g)[0]), 85);
                right = Math.max(parseInt(right.match(/\d+/g)[0], 10) / width * 100, 10);

                fixedEle.style.bottom = bottom + 'px';
                fixedEle.style.right = right + '%';

                break;
            default:
                fixedEle.style.display = 'none';
        }
    };

    /**
     * Set styles of a fixed element.
     * @param {MIPElement} fixedEle
     */
    FixedElement.prototype.setStyle = function (fixedEle) {
        var bottom = layout.parseLength(fixedEle.getAttribute('bottom'));
        if (bottom) {
            fixedEle.style.bottom = bottom;
            return;
        }
        var top = layout.parseLength(fixedEle.getAttribute('top'));
        if (top) {
            fixedEle.style.top = top;
            return;
        }
        if (!top && !bottom) {
            fixedEle.parentElement.removeChild(fixedEle);
            // It will not be counted if the elements's type is non-standard.
            this._fixedTypes.other -= 1;
        }
    };

    /**
     * Show fixed layer
     * @param {HTMLElement} layer
     */
    FixedElement.prototype.showFixedLayer = function(layer) {
        if(layer) {
            css(layer, {
                display: 'block'
            });
        }
    };

    /**
     * Hide fixed layer
     * @param {HTMLElement} layer
     */
    FixedElement.prototype.hideFixedLayer = function(layer) {
        if(layer) {
            css(layer, {
                display: 'none'
            });
        }
    };

    return new FixedElement();
});

<|endoftext|>