<commit_before>/**
 * Array Utilities
 * @module Ink.Util.Array_1
 * @version 1
 */

Ink.createModule('Ink.Util.Array', '1', [], function() {

    'use strict';

    var arrayProto = Array.prototype;

    /**
     * @namespace Ink.Util.Array_1
     */

    var InkArray = {

        /**
         * Checks if a value is an array
         *
         * @method isArray
         * @param testedObject {Mixed} The object we want to check
         **/
        isArray: Array.isArray || function (testedObject) {
            return {}.toString.call(testedObject) === '[object Array]';
        },

        /**
         * Loops through an array, grouping similar items together.
         * @method groupBy
         * @param arr {Array} The input array.
         * @param [options] {Object} options object, containing:
         * @param [options.key] {Function} A function which computes the group key by which the items are grouped.
         * @param [options.pairs] {Boolean} Set to `true` if you want to output an array of `[key, [group...]]` pairs instead of an array of groups.
         * @return {Array} An array of arrays of chunks.
         *
         * @example
         *
         *        InkArray.groupBy([1, 1, 2, 2, 3, 1])  // -> [ [1, 1], [2, 2], [3], [1] ]
         *        InkArray.groupBy([1.1, 1.2, 2.1], { key: Math.floor })  // -> [ [1.1, 1.2], [2.1] ]
         *        InkArray.groupBy([1.1, 1.2, 2.1], { key: Math.floor, pairs: true })  // -> [ [1, [1.1, 1.2]], [2, [2.1]] ]
         *
         **/
        groupBy: function (arr, options) {
            options = options || {};
            var ret = [];
            var latestGroup;
            function eq(a, b) {
                return outKey(a) === outKey(b);
            }
            function outKey(item) {
                if (typeof options.key === 'function') {
                    return options.key(item);
                } else {
                    return item;
                }
            }

            for (var i = 0, len = arr.length; i < len; i++) {
                latestGroup = [arr[i]];

                // Chunkin'
                while ((i + 1 < len) && eq(arr[i], arr[i + 1])) {
                    latestGroup.push(arr[i + 1]);
                    i++;
                }

                if (options.pairs) {
                    ret.push([outKey(arr[i]), latestGroup]);
                } else {
                    ret.push(latestGroup);
                }
            }
            return ret;
        },

        /**
         * Replacement for Array.prototype.reduce.
         *
         * Produces a single result from a list of values by calling an "aggregator" function.
         *
         * Falls back to Array.prototype.reduce if available.
         *
         * @method reduce
         * @param array {Array} Input array to be reduced.
         * @param callback {Function} `function (previousValue, currentValue, index, all) { return {Mixed} }` to execute for each value.
         * @param initial {Mixed} Object used as the first argument to the first call of `callback`
         *
         * @example
         *          var sum = InkArray.reduce([1, 2, 3], function (a, b) { return a + b; });  // -> 6
         */
        reduce: function (array, callback, initial) {
            if (arrayProto.reduce) {
                return arrayProto.reduce.apply(array, [].slice.call(arguments, 1));
            }

            // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Polyfill
            var t = Object( array ), len = t.length >>> 0, k = 0, value;
            if ( arguments.length >= 3 ) {
                value = initial;
            } else {
                while ( k < len && !(k in t) ) k++;
                if ( k >= len )
                    throw new TypeError('Reduce of empty array with no initial value');
                value = t[ k++ ];
            }
            for ( ; k < len ; k++ ) {
                if ( k in t ) {
                    value = callback( value, t[k], k, t );
                }
            }
            return value;
        },

        /**
         * Checks if a value exists in array
         *
         * @method inArray
         * @public
         * @static
         * @param {Mixed} value     Value to check
         * @param {Array} arr       Array to search in
         * @return {Boolean}        True if value exists in the array
         * @sample Ink_Util_Array_inArray.html
         */
        inArray: function(value, arr) {
            if (typeof arr === 'object') {
                for (var i = 0, f = arr.length; i < f; ++i) {
                    if (arr[i] === value) {
                        return true;
                    }
                }
            }
            return false;
        },

        /**
         * Sorts an array of objects by an object property
         *
         * @method sortMulti
         * @param {Array}           arr         Array of objects to sort
         * @param {String}  key         Property to sort by
         * @return {Array|Boolean}      False if it's not an array, returns a sorted array if it's an array.
         * @public
         * @static
         * @sample Ink_Util_Array_sortMulti.html
         */
        sortMulti: function(arr, key) {
            if (typeof arr === 'undefined' || arr.constructor !== Array) { return false; }
            if (typeof key !== 'string') { return arr.sort(); }
            if (arr.length > 0) {
                if (typeof(arr[0][key]) === 'undefined') { return false; }
                arr.sort(function(a, b){
                    var x = a[key];
                    var y = b[key];
                    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
                });
            }
            return arr;
        },

        /**
         * Gets the indexes of a value in an array
         *
         * @method keyValue
         * @param   {String}      value     Value to search for.
         * @param   {Array}       arr       Array to run the search in.
         * @param   {Boolean}     [first]   Flag to stop the search at the first match. It also returns an index number instead of an array of indexes.
         * @return  {Boolean|Number|Array}  False for no matches. Array of matches or first match index.
         * @public
         * @static
         * @sample Ink_Util_Array_keyValue.html
         */
        keyValue: function(value, arr, first) {
            if (typeof value !== 'undefined' && typeof arr === 'object' && this.inArray(value, arr)) {
                var aKeys = [];
                for (var i = 0, f = arr.length; i < f; ++i) {
                    if (arr[i] === value) {
                        if (typeof first !== 'undefined' && first === true) {
                            return i;
                        } else {
                            aKeys.push(i);
                        }
                    }
                }
                return aKeys;
            }
            return false;
        },

        /**
         * Shuffles an array.
         *
         * @method shuffle
         * @param   {Array}       arr    Array to shuffle
         * @return  {Array|Boolean}      Shuffled Array or false if not an array.
         * @public
         * @static
         * @sample Ink_Util_Array_shuffle.html
         */
        shuffle: function(arr) {
            if (typeof(arr) !== 'undefined' && arr.constructor !== Array) { return false; }
            var total   = arr.length,
                tmp1    = false,
                rnd     = false;

            while (total--) {
                rnd        = Math.floor(Math.random() * (total + 1));
                tmp1       = arr[total];
                arr[total] = arr[rnd];
                arr[rnd]   = tmp1;
            }
            return arr;
        },

        /**
         * Runs a function through each of the elements of an array
         *
         * @method forEach
         * @param   {Array}     arr     The array to be cycled/iterated
         * @param   {Function}  cb      The function receives as arguments the value, index and array.
         * @return  {Array}             Iterated array.
         * @public
         * @static
         * @sample Ink_Util_Array_forEach.html
         */
        forEach: function(array, callback, context) {
            if (arrayProto.forEach) {
                return arrayProto.forEach.call(array, callback, context);
            }
            for (var i = 0, len = array.length >>> 0; i < len; i++) {
                callback.call(context, array[i], i, array);
            }
        },

        /**
         * Alias for backwards compatibility. See forEach
         *
         * @method each
         */
        each: function () {
            InkArray.forEach.apply(InkArray, [].slice.call(arguments));
        },

        /**
         * Runs a function for each item in the array.
         * That function will receive each item as an argument and its return value will change the corresponding array item.
         * @method map
         * @param {Array}       array       The array to map over
         * @param {Function}    map         The map function. Will take `(item, index, array)` as arguments and `this` will be the `context` argument.
         * @param {Object}      [context]   Object to be `this` in the map function.
         *
         * @sample Ink_Util_Array_map.html
         */
        map: function (array, callback, context) {
            if (arrayProto.map) {
                return arrayProto.map.call(array, callback, context);
            }
            var mapped = new Array(len);
            for (var i = 0, len = array.length >>> 0; i < len; i++) {
                mapped[i] = callback.call(context, array[i], i, array);
            }
            return mapped;
        },

        /**
         * Filters an array based on a truth test.
         * This method runs a test function on all the array values and returns a new array with all the values that pass the test.
         * @method filter
         * @param {Array}       array       The array to filter
         * @param {Function}    test        A test function taking `(item, index, array)`
         * @param {Object}      [context]   Object to be `this` in the test function.
         * @return {Array}                  Returns the filtered array
         *
         * @sample Ink_Util_Array_filter.html
         */
        filter: function (array, test, context) {
            if (arrayProto.filter) {
                return arrayProto.filter.call(array, test, context);
            }
            var filtered = [],
                val = null;
            for (var i = 0, len = array.length; i < len; i++) {
                val = array[i]; // it might be mutated
                if (test.call(context, val, i, array)) {
                    filtered.push(val);
                }
            }
            return filtered;
        },

        /**
         * Checks if some element in the array passes a truth test
         *
         * @method some
         * @param   {Array}       arr       The array to iterate through
         * @param   {Function}    cb        The callback to be called on the array's elements. It receives the value, the index and the array as arguments.
         * @param   {Object}      context   Object of the callback function
         * @return  {Boolean}               True if the callback returns true at any point, false otherwise
         * @public
         * @static
         * @sample Ink_Util_Array_some.html
         */
        some: function(arr, cb, context){

            if (arr === null){
                throw new TypeError('First argument is invalid.');
            }

            var t = Object(arr);
            var len = t.length >>> 0;
            if (typeof cb !== "function"){ throw new TypeError('Second argument must be a function.'); }

            for (var i = 0; i < len; i++) {
                if (i in t && cb.call(context, t[i], i, t)){ return true; }
            }

            return false;
        },

        /**
         * Compares the values of two arrays and return the matches
         *
         * @method intersect
         * @param   {Array}   arr1      First array
         * @param   {Array}   arr2      Second array
         * @return  {Array}             Empty array if one of the arrays is false (or do not intersect) | Array with the intersected values
         * @public
         * @static
         * @sample Ink_Util_Array_intersect.html
         */
        intersect: function(arr1, arr2) {
            if (!arr1 || !arr2 || arr1 instanceof Array === false || arr2 instanceof Array === false) {
                return [];
            }

            var shared = [];
            for (var i = 0, I = arr1.length; i<I; ++i) {
                for (var j = 0, J = arr2.length; j < J; ++j) {
                    if (arr1[i] === arr2[j]) {
                        shared.push(arr1[i]);
                    }
                }
            }

            return shared;
        },

        /**
         * Converts an array-like object to an array
         *
         * @method convert
         * @param   {Array}   arr   Array to be converted
         * @return  {Array}         Array resulting of the conversion
         * @public
         * @static
         * @sample Ink_Util_Array_convert.html
         */
        convert: function(arr) {
            return arrayProto.slice.call(arr || [], 0);
        },

        /**
         * Simulates python's range(start, stop, step) function.
         *
         * Creates a list with numbers counting from start until stop, using a for loop.
         *.
         * The optional step argument defines how to step ahead. You can pass a negative number to count backwards (see the examples below).
         *
         * @method range
         * @param {Number} start    The array's first element.
         * @param {Number} stop     Stop counting before this number.
         * @param {Number} [step=1] Interval between numbers. You can use a negative number to count backwards.
         *
         * @sample Ink_Util_Array_1_range.html
         **/
        range: function range(a, b, step) {
            // From: https://github.com/mcandre/node-range
            if (!step) {
                step = 1;
            }

            var r = [];
            var x;

            if (step > 0) {
                for (x = a; x < b; x += step) {
                    r.push(x);
                }
            } else {
                for (x = a; x > b; x += step) {
                    r.push(x);
                }
            }

            return r;
        },

        /**
         * Inserts a value on a specified index
         *
         * @method insert
         * @param {Array}   arr     Array where the value will be inserted
         * @param {Number}  idx     Index of the array where the value should be inserted
         * @param {Mixed}   value   Value to be inserted
         * @public
         * @static
         * @sample Ink_Util_Array_insert.html
         */
        insert: function(arr, idx, value) {
            arr.splice(idx, 0, value);
        },

        /**
         * Removes a range of values from the array
         *
         * @method remove
         * @param   {Array}     arr     Array where the value will be removed
         * @param   {Number}    from    Index of the array where the removal will start removing.
         * @param   {Number}    rLen    Number of items to be removed from the index onwards.
         * @return  {Array}             An array with the remaining values
         * @public
         * @static
         * @sample Ink_Util_Array_remove.html
         */
        remove: function(arr, from, rLen){
            var output = [];

            for(var i = 0, iLen = arr.length; i < iLen; i++){
                if(i >= from && i < from + rLen){
                    continue;
                }

                output.push(arr[i]);
            }

            return output;
        }
    };

    return InkArray;

});
<commit_msg>added Ink.Util.Array.unique<commit_after>/**
 * Array Utilities
 * @module Ink.Util.Array_1
 * @version 1
 */

Ink.createModule('Ink.Util.Array', '1', [], function() {

    'use strict';

    var arrayProto = Array.prototype;

    /**
     * @namespace Ink.Util.Array_1
     */

    var InkArray = {

        /**
         * Checks if a value is an array
         *
         * @method isArray
         * @param testedObject {Mixed} The object we want to check
         **/
        isArray: Array.isArray || function (testedObject) {
            return {}.toString.call(testedObject) === '[object Array]';
        },

        /**
         * Loops through an array, grouping similar items together.
         * @method groupBy
         * @param arr {Array} The input array.
         * @param [options] {Object} options object, containing:
         * @param [options.key] {Function} A function which computes the group key by which the items are grouped.
         * @param [options.pairs] {Boolean} Set to `true` if you want to output an array of `[key, [group...]]` pairs instead of an array of groups.
         * @return {Array} An array of arrays of chunks.
         *
         * @example
         *
         *        InkArray.groupBy([1, 1, 2, 2, 3, 1])  // -> [ [1, 1], [2, 2], [3], [1] ]
         *        InkArray.groupBy([1.1, 1.2, 2.1], { key: Math.floor })  // -> [ [1.1, 1.2], [2.1] ]
         *        InkArray.groupBy([1.1, 1.2, 2.1], { key: Math.floor, pairs: true })  // -> [ [1, [1.1, 1.2]], [2, [2.1]] ]
         *
         **/
        groupBy: function (arr, options) {
            options = options || {};
            var ret = [];
            var latestGroup;
            function eq(a, b) {
                return outKey(a) === outKey(b);
            }
            function outKey(item) {
                if (typeof options.key === 'function') {
                    return options.key(item);
                } else {
                    return item;
                }
            }

            for (var i = 0, len = arr.length; i < len; i++) {
                latestGroup = [arr[i]];

                // Chunkin'
                while ((i + 1 < len) && eq(arr[i], arr[i + 1])) {
                    latestGroup.push(arr[i + 1]);
                    i++;
                }

                if (options.pairs) {
                    ret.push([outKey(arr[i]), latestGroup]);
                } else {
                    ret.push(latestGroup);
                }
            }
            return ret;
        },

        /**
         * Replacement for Array.prototype.reduce.
         *
         * Produces a single result from a list of values by calling an "aggregator" function.
         *
         * Falls back to Array.prototype.reduce if available.
         *
         * @method reduce
         * @param array {Array} Input array to be reduced.
         * @param callback {Function} `function (previousValue, currentValue, index, all) { return {Mixed} }` to execute for each value.
         * @param initial {Mixed} Object used as the first argument to the first call of `callback`
         *
         * @example
         *          var sum = InkArray.reduce([1, 2, 3], function (a, b) { return a + b; });  // -> 6
         */
        reduce: function (array, callback, initial) {
            if (arrayProto.reduce) {
                return arrayProto.reduce.apply(array, [].slice.call(arguments, 1));
            }

            // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Polyfill
            var t = Object( array ), len = t.length >>> 0, k = 0, value;
            if ( arguments.length >= 3 ) {
                value = initial;
            } else {
                while ( k < len && !(k in t) ) k++;
                if ( k >= len )
                    throw new TypeError('Reduce of empty array with no initial value');
                value = t[ k++ ];
            }
            for ( ; k < len ; k++ ) {
                if ( k in t ) {
                    value = callback( value, t[k], k, t );
                }
            }
            return value;
        },

        /**
         * Checks if a value exists in array
         *
         * @method inArray
         * @public
         * @static
         * @param {Mixed} value     Value to check
         * @param {Array} arr       Array to search in
         * @return {Boolean}        True if value exists in the array
         * @sample Ink_Util_Array_inArray.html
         */
        inArray: function(value, arr) {
            if (typeof arr === 'object') {
                for (var i = 0, f = arr.length; i < f; ++i) {
                    if (arr[i] === value) {
                        return true;
                    }
                }
            }
            return false;
        },

        /**
         * Sorts an array of objects by an object property
         *
         * @method sortMulti
         * @param {Array}           arr         Array of objects to sort
         * @param {String}  key         Property to sort by
         * @return {Array|Boolean}      False if it's not an array, returns a sorted array if it's an array.
         * @public
         * @static
         * @sample Ink_Util_Array_sortMulti.html
         */
        sortMulti: function(arr, key) {
            if (typeof arr === 'undefined' || arr.constructor !== Array) { return false; }
            if (typeof key !== 'string') { return arr.sort(); }
            if (arr.length > 0) {
                if (typeof(arr[0][key]) === 'undefined') { return false; }
                arr.sort(function(a, b){
                    var x = a[key];
                    var y = b[key];
                    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
                });
            }
            return arr;
        },

        /**
         * Gets the indexes of a value in an array
         *
         * @method keyValue
         * @param   {String}      value     Value to search for.
         * @param   {Array}       arr       Array to run the search in.
         * @param   {Boolean}     [first]   Flag to stop the search at the first match. It also returns an index number instead of an array of indexes.
         * @return  {Boolean|Number|Array}  False for no matches. Array of matches or first match index.
         * @public
         * @static
         * @sample Ink_Util_Array_keyValue.html
         */
        keyValue: function(value, arr, first) {
            if (typeof value !== 'undefined' && typeof arr === 'object' && this.inArray(value, arr)) {
                var aKeys = [];
                for (var i = 0, f = arr.length; i < f; ++i) {
                    if (arr[i] === value) {
                        if (typeof first !== 'undefined' && first === true) {
                            return i;
                        } else {
                            aKeys.push(i);
                        }
                    }
                }
                return aKeys;
            }
            return false;
        },

        /**
         * Shuffles an array.
         *
         * @method shuffle
         * @param   {Array}       arr    Array to shuffle
         * @return  {Array|Boolean}      Shuffled Array or false if not an array.
         * @public
         * @static
         * @sample Ink_Util_Array_shuffle.html
         */
        shuffle: function(arr) {
            if (typeof(arr) !== 'undefined' && arr.constructor !== Array) { return false; }
            var total   = arr.length,
                tmp1    = false,
                rnd     = false;

            while (total--) {
                rnd        = Math.floor(Math.random() * (total + 1));
                tmp1       = arr[total];
                arr[total] = arr[rnd];
                arr[rnd]   = tmp1;
            }
            return arr;
        },

        /**
         * Runs a function through each of the elements of an array
         *
         * @method forEach
         * @param   {Array}     arr     The array to be cycled/iterated
         * @param   {Function}  cb      The function receives as arguments the value, index and array.
         * @return  {Array}             Iterated array.
         * @public
         * @static
         * @sample Ink_Util_Array_forEach.html
         */
        forEach: function(array, callback, context) {
            if (arrayProto.forEach) {
                return arrayProto.forEach.call(array, callback, context);
            }
            for (var i = 0, len = array.length >>> 0; i < len; i++) {
                callback.call(context, array[i], i, array);
            }
        },

        /**
         * Alias for backwards compatibility. See forEach
         *
         * @method each
         */
        each: function () {
            InkArray.forEach.apply(InkArray, [].slice.call(arguments));
        },

        /**
         * Runs a function for each item in the array.
         * That function will receive each item as an argument and its return value will change the corresponding array item.
         * @method map
         * @param {Array}       array       The array to map over
         * @param {Function}    map         The map function. Will take `(item, index, array)` as arguments and `this` will be the `context` argument.
         * @param {Object}      [context]   Object to be `this` in the map function.
         *
         * @sample Ink_Util_Array_map.html
         */
        map: function (array, callback, context) {
            if (arrayProto.map) {
                return arrayProto.map.call(array, callback, context);
            }
            var mapped = new Array(len);
            for (var i = 0, len = array.length >>> 0; i < len; i++) {
                mapped[i] = callback.call(context, array[i], i, array);
            }
            return mapped;
        },

        /**
         * Filters an array based on a truth test.
         * This method runs a test function on all the array values and returns a new array with all the values that pass the test.
         * @method filter
         * @param {Array}       array       The array to filter
         * @param {Function}    test        A test function taking `(item, index, array)`
         * @param {Object}      [context]   Object to be `this` in the test function.
         * @return {Array}                  Returns the filtered array
         *
         * @sample Ink_Util_Array_filter.html
         */
        filter: function (array, test, context) {
            if (arrayProto.filter) {
                return arrayProto.filter.call(array, test, context);
            }
            var filtered = [],
                val = null;
            for (var i = 0, len = array.length; i < len; i++) {
                val = array[i]; // it might be mutated
                if (test.call(context, val, i, array)) {
                    filtered.push(val);
                }
            }
            return filtered;
        },

        /**
         * Checks if some element in the array passes a truth test
         *
         * @method some
         * @param   {Array}       arr       The array to iterate through
         * @param   {Function}    cb        The callback to be called on the array's elements. It receives the value, the index and the array as arguments.
         * @param   {Object}      context   Object of the callback function
         * @return  {Boolean}               True if the callback returns true at any point, false otherwise
         * @public
         * @static
         * @sample Ink_Util_Array_some.html
         */
        some: function(arr, cb, context){

            if (arr === null){
                throw new TypeError('First argument is invalid.');
            }

            var t = Object(arr);
            var len = t.length >>> 0;
            if (typeof cb !== "function"){ throw new TypeError('Second argument must be a function.'); }

            for (var i = 0; i < len; i++) {
                if (i in t && cb.call(context, t[i], i, t)){ return true; }
            }

            return false;
        },

        /**
         * Compares the values of two arrays and return the matches
         *
         * @method intersect
         * @param   {Array}   arr1      First array
         * @param   {Array}   arr2      Second array
         * @return  {Array}             Empty array if one of the arrays is false (or do not intersect) | Array with the intersected values
         * @public
         * @static
         * @sample Ink_Util_Array_intersect.html
         */
        intersect: function(arr1, arr2) {
            if (!arr1 || !arr2 || arr1 instanceof Array === false || arr2 instanceof Array === false) {
                return [];
            }

            var shared = [];
            for (var i = 0, I = arr1.length; i<I; ++i) {
                for (var j = 0, J = arr2.length; j < J; ++j) {
                    if (arr1[i] === arr2[j]) {
                        shared.push(arr1[i]);
                    }
                }
            }

            return shared;
        },

        /**
         * Converts an array-like object to an array
         *
         * @method convert
         * @param   {Array}   arr   Array to be converted
         * @return  {Array}         Array resulting of the conversion
         * @public
         * @static
         * @sample Ink_Util_Array_convert.html
         */
        convert: function(arr) {
            return arrayProto.slice.call(arr || [], 0);
        },

        /**
 		 * Removes duplicated values in an array.
 		 *
 		 * @method unique
 		 * @param {Array}   arr   Array to filter
 		 * @return {Array} 	      Array with only unique values
 		 * @public
 		 * @static
 		 */
 		unique: function(arr){
 			return this.filter(this.convert(arr), function (e, i, arr) {
                            return arr.lastIndexOf(e) === i;
                        });
 		},

        /**
         * Simulates python's range(start, stop, step) function.
         *
         * Creates a list with numbers counting from start until stop, using a for loop.
         *.
         * The optional step argument defines how to step ahead. You can pass a negative number to count backwards (see the examples below).
         *
         * @method range
         * @param {Number} start    The array's first element.
         * @param {Number} stop     Stop counting before this number.
         * @param {Number} [step=1] Interval between numbers. You can use a negative number to count backwards.
         *
         * @sample Ink_Util_Array_1_range.html
         **/
        range: function range(a, b, step) {
            // From: https://github.com/mcandre/node-range
            if (!step) {
                step = 1;
            }

            var r = [];
            var x;

            if (step > 0) {
                for (x = a; x < b; x += step) {
                    r.push(x);
                }
            } else {
                for (x = a; x > b; x += step) {
                    r.push(x);
                }
            }

            return r;
        },

        /**
         * Inserts a value on a specified index
         *
         * @method insert
         * @param {Array}   arr     Array where the value will be inserted
         * @param {Number}  idx     Index of the array where the value should be inserted
         * @param {Mixed}   value   Value to be inserted
         * @public
         * @static
         * @sample Ink_Util_Array_insert.html
         */
        insert: function(arr, idx, value) {
            arr.splice(idx, 0, value);
        },

        /**
         * Removes a range of values from the array
         *
         * @method remove
         * @param   {Array}     arr     Array where the value will be removed
         * @param   {Number}    from    Index of the array where the removal will start removing.
         * @param   {Number}    rLen    Number of items to be removed from the index onwards.
         * @return  {Array}             An array with the remaining values
         * @public
         * @static
         * @sample Ink_Util_Array_remove.html
         */
        remove: function(arr, from, rLen){
            var output = [];

            for(var i = 0, iLen = arr.length; i < iLen; i++){
                if(i >= from && i < from + rLen){
                    continue;
                }

                output.push(arr[i]);
            }

            return output;
        }
    };

    return InkArray;

});
<|endoftext|>


<commit_before>import { combineReducers } from 'redux'

function playlist(state = {
    last: true,
    first: true,
    cursor: 0,
    items: []
}, action) {
    switch (action.type) {
        // can also be used to empty playlist. Good for loading albums.
        case "REPLACE_PLAYLIST":
            return Object.assign({}, state, {
                items:action.items,
                cursor:0,
                last:true,
                first:true,
            })
        case "ENQUEUE_ITEM":
            return Object.assign({}, state, {
                items:[...items,action.item],
                last:false,
            })
        case "PLAYLIST_REMOVE_ITEM":
            return Object.assign({},state,{
                items:items.filter((item) => action.id == item.id),
                TODO set cursor last first
            })
        case "PLAY_ITEM_NEXT":
            return Object.assign({}, state, {
                items:[
                    ...items.slice(0,cursor),
                    action.item,
                    ...items.slice(cursor),
                ],
                last:false,
            })
        default:
            return state
    }
}

const rootReducer = combineReducers({
    playlist,
})

export default rootReducer

<commit_msg>complete 1st attempt at playlist reducer<commit_after>import { combineReducers } from 'redux'

function playlist(state = {
    last: true,
    first: true,
    cursor: 0,
    items: []
}, action) {
    switch (action.type) {
        // can also be used to empty playlist. Good for loading albums.
        case "REPLACE_PLAYLIST":
            return Object.assign({}, state, {
                items:action.items,
                cursor:0,
                last:true,
                first:true,
            })
        case "ENQUEUE_ITEM":
            return Object.assign({}, state, {
                items:[...state.items,action.item],
                last:false,
            })
        case "PLAYLIST_REMOVE_ITEM":
            return Object.assign({},state,{
                items:state.items.filter((item,i) => action.index == i),
                cursor: Math.max(state.cursor,items.length-2)
            })
        case "PLAY_ITEM_NEXT":
            return Object.assign({}, state, {
                items:[
                    ...items.slice(0,cursor),
                    action.item,
                    ...items.slice(cursor),
                ],
                last:false,
            })
        default:
            return state
    }
}

const rootReducer = combineReducers({
    playlist,
})

export default rootReducer

<|endoftext|>


<commit_before>(function(){

	angular.module('ionic-modal-select', [])

		.directive('compile', ['$compile', function ($compile) {
			return function(scope, iElement, iAttrs) {
				var x = scope.$watch(
					function(scope) {
						// watch the 'compile' expression for changes
						return scope.$eval(iAttrs.compile);
					},
					function(value) {
						// when the 'compile' expression changes
						// assign it into the current DOM
						iElement.html(value);

						// compile the new DOM and link it to the current
						// scope.
						// NOTE: we only compile .childNodes so that
						// we don't get into infinite loop compiling ourselves
						$compile(iElement.contents())(scope);

						//deactivate watch if "compile-once" is set to "true"
						if (iAttrs.compileOnce === 'true') {
							x();
						}
					}
				);
			};
		}])

		.directive('modalSelect', ['$ionicModal','$timeout', '$filter', '$parse', '$templateCache',
			function ($ionicModal, $timeout, $filter, $parse, $templateCache ) {

				return {
					restrict: 'A',
					require : 'ngModel',
					scope: {
						initialOptions:"=options",
						optionGetter:"&",
						searchFilters: "=searchFilters",
						searchProperties:'=',
						onSelect: "&",
						onSearch: "&",
						onReset: "&"
					},
					link: function (scope, iElement, iAttrs, ngModelController, transclude) {

						var shortList = true;
						var shortListBreak = iAttrs.shortListBreak ? parseInt(iAttrs.shortListBreak) : 10;
						var setFromProperty= iAttrs.optionProperty;
						var onOptionSelect = iAttrs.optionGetter;
						var clearSearchOnSelect = iAttrs.clearSearchOnSelect !== "false" ? true : false;
						var searchProperties = scope.searchProperties  ? scope.searchProperties : false;

						//multiple values settings.
                        var multiple = iAttrs.multiple  ? true : false;
                        if (multiple) {
                            scope.isChecked = {};
                        }
                        var multipleNullValue = iAttrs.multipleNullValue ? scope.$eval(iAttrs.multipleNullValue) : [];

						scope.ui = {
							modalTitle : iAttrs.modalTitle || 'Select an option',
							okButton : iAttrs.okButton || 'OK',
							hideReset : iAttrs.hideReset  !== "true" ? false : true,
							resetButton : iAttrs.resetButton || 'Reset',
							cancelButton : iAttrs.cancelButton || 'Cancel',
							loadListMessage : iAttrs.loadListMessage || 'Loading',
							modalClass : iAttrs.modalClass || '',
							headerFooterClass : iAttrs.headerFooterClass || 'bar-stable',
							value  : null,
							selectedClass : iAttrs.selectedClass || 'option-selected',
							itemClass: iAttrs.itemClass || 'item item-text-wrap',
							searchTemplate: iAttrs.searchTemplate || (multiple ? 'modal-template-multiple.html' : 'modal-template.html'),

							//search stuff
							hasSearch : iAttrs.hasSearch  !== "true" ? false : true,
							searchValue : '',
							searchPlaceholder : iAttrs.searchPlaceholder || 'Search',
							subHeaderClass : iAttrs.subHeaderClass || 'bar-stable',
							cancelSearchButton : iAttrs.cancelSearchButton || 'Clear'

						};

						var allOptions = [];
						scope.options = [];

                        if (iAttrs.optionsExpression) {
                            var optionsExpression = iAttrs.optionsExpression;
                            var match = optionsExpression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
                            if (!match) {
                                throw new Error("collection-repeat expected expression in form of '_item_ in " +
                                                  "_collection_[ track by _id_]' but got '" + iAttrs.optionsExpression + "'.");
                            }
                            var keyExpr = match[1];
                            var listExpr = match[2];
                            var listGetter = $parse(listExpr);
                            var s = iElement.scope();

                            scope.$watch(
                                function(){
                                    return listGetter(s);
                                },
                                function(nv, ov){
                                    initialOptionsSetup(nv);
                                    updateListMode();
                                },
                                true
                            );

                        } else {
                            scope.$watchCollection('initialOptions', function(nv){
                                initialOptionsSetup(nv);
                                updateListMode();
                            });
                        }

                        //#TODO: this is due to different single vs multiple template
                        //but adds lots of complexity here and in search
                        function initialOptionsSetup(nv){
														nv = nv || [];
                            if ( !multiple ) {
                                allOptions = angular.copy(nv);
                                scope.options = angular.copy(nv);
                            } else {
                                allOptions = nv.map(function(item, idx){
                                    return [idx, angular.copy(item)]
                                });
                                scope.options = angular.copy(allOptions);
                            }
                        }

						// getting options template
						var opt = iElement[0].querySelector('.option');
						if (!opt) {
							throw new Error({
								name:'modalSelectError:noOptionTemplate',
								message:'When using modalSelect directive you must include an element with class "option" to provide a template for your select options.',
								toString:function(){
									return this.name + " " + this.message;
								}
							});
						}
						scope.inner = angular.element(opt).html();

						//add support for .remove for older devices
						if (!('remove' in Element.prototype)) {
							Element.prototype.remove = function() {
								this.parentNode.removeChild(this);
							};
						}

						angular.element(opt).remove();

						var notFound = iElement[0].querySelector('.not-found');
						if(notFound) {
							scope.notFound = angular.element(notFound).html();
							angular.element(notFound).remove();
						}

						function updateListMode(){
							//shortList controls wether using ng-repeat instead of collection-repeat
							if (iAttrs.useCollectionRepeat === "true") {
								shortList = false;
							} else if (iAttrs.useCollectionRepeat === "false") {
								shortList = true;
							} else {
								if (typeof(scope.options) !=="undefined"){
									shortList = !!(scope.options.length < shortListBreak);
								}
							}

							scope.ui.shortList = shortList;
						}

						ngModelController.$render = function(){
							scope.ui.value = ngModelController.$viewValue;
						};

						var getSelectedValue = scope.getSelectedValue = function(option){
							var val = null;
							if (option === null || option === undefined) {
								return option;
							}
							if (onOptionSelect) {
								return scope.optionGetter({option:option});
							}
							if (setFromProperty) {
								val = option[setFromProperty];
							} else {
								val = option;
							}
							return val;
						};

						scope.setOption = function(option){
							var oldValue = ngModelController.$viewValue;
							var val = getSelectedValue(option);
							ngModelController.$setViewValue(val);
							ngModelController.$render();

							if (scope.onSelect) {
								scope.onSelect({ newValue: val, oldValue: oldValue });
							}
							scope.modal.hide().then(function(){
								scope.showList = false;
								if (scope.ui.hasSearch) {
									if(clearSearchOnSelect) {
										scope.ui.searchValue = '';
									}
								}
							});
						};

						// Filter object {id: <filterId>, active: <boolean>}
						// Used as auxiliary query params when querying server for search results
						scope.setFilter = function(filterId) {
							angular.forEach(scope.searchFilters, function(filter) {
								if(filter.id == filterId) {
									filter.active = !filter.active;
								} else {
									filter.active = false;
								}
							});

							// Trigger another search when the search filters change
							if(scope.onSearch) {
								scope.onSearch({query: scope.ui.searchValue});
							}
						};

						scope.unsetValue = function(){
							$timeout(function(){
								ngModelController.$setViewValue("");
								ngModelController.$render();
								scope.modal.hide();
								scope.showList = false;
								if (scope.onReset && angular.isFunction(scope.onReset)) {
									scope.onReset();
								}
							});
						};

                        scope.setValues = function(){
                            var checkedItems = [];
                            angular.forEach(scope.isChecked, function(v, k){
                                if(v){
                                    checkedItems.push(allOptions[k][1])
                                }

                            });
                            var oldValues = ngModelController.$viewValue;
                            var vals = checkedItems.map(function(item){
                                return getSelectedValue(item);
                            });
                            ngModelController.$setViewValue(vals);
                            ngModelController.$render();

                            if (scope.onSelect) {
                                scope.onSelect({ newValue: vals, oldValue: oldValues });
                            }
                            scope.modal.hide().then(function(){
                                scope.showList = false;
                                if (scope.ui.hasSearch) {
                                   if(clearSearchOnSelect){
                                        scope.ui.searchValue = '';
                                    }
                                }
                            });

                        };

                        scope.unsetValues = function(){
                            $timeout(function(){
                                ngModelController.$setViewValue(multipleNullValue);
                                ngModelController.$render();
                                scope.modal.hide();
                                scope.showList = false;
                                if (scope.onReset && angular.isFunction(scope.onReset)) {
                                    scope.onReset();
                                }
                            });
                        };

						scope.closeModal = function(){
							scope.modal.hide().then(function(){
								scope.showList = false;
							});
						};

						scope.compareValues = function(a, b){
							return angular.equals(a, b);
						};

						//loading the modal
						var modalTpl = null;
						if(iAttrs.searchTemplate) {
							scope.modal = $ionicModal.fromTemplate(
								$templateCache.get(iAttrs.searchTemplate),
								{ scope: scope }
							);
						} else {
							modalTpl = multiple ? 'modal-template-multiple.html' : 'modal-template.html';
							scope.modal = $ionicModal.fromTemplate(
								modalSelectTemplates[modalTpl],
								{ scope: scope }
							);
						}

						scope.$on('$destroy', function(){
							scope.modal.remove();
						});

						iElement.on('click', function(){
							if (shortList) {
								scope.showList = true;
								scope.modal.show();
							} else {
								scope.modal.show()
									.then(function(){
										scope.showList = true;
										scope.ui.shortList = shortList;
									});
							}
						});

                        //filter function
						if (scope.ui.hasSearch) {
							scope.$watch('ui.searchValue', function(nv){
                                var whatToSearch;
                                if ( !multiple  ) {
                                    whatToSearch = allOptions;
                                } else {
                                    whatToSearch = allOptions.map(function(item){
                                        return item[1];
                                    });
                                }

								if(iAttrs.onSearch) {
                                    scope.onSearch({query: nv});
								} else {
                                    var filteredOpts = $filter('filter')(whatToSearch, nv, function(actual, expected) {
										if (searchProperties){
											if (typeof actual == 'object'){
												for (var i = 0; i < searchProperties.length; i++){
													if (actual[searchProperties[i]] && actual[searchProperties[i]].toLowerCase().indexOf(expected.toLowerCase()) >= 0){
														return true;
													}
												}
											}
											return false;
										} else {
											if(actual.toString().toLowerCase().indexOf(expected.toLowerCase()) >= 0){
												return true;
											}
										}
										return false;
									});

                                    var oldLen = scope.options.length;
                                    if ( !multiple ){
                                        scope.options = filteredOpts;

                                    } else {
                                        //#TODO: lots of loops here!
                                        var newOpts = [];
                                        angular.forEach(filteredOpts, function(item){
                                            var originalItem = allOptions.find(function(it){
                                                return it[1] == item;
                                            });
                                            if( originalItem ){
                                                newOpts.push(originalItem);
                                            }
                                        });
                                        scope.options = newOpts;
                                    }
                                    if(oldLen != scope.options.length){
                                        //#todo: should resize scroll or scroll up here
                                    }
								}
							});
							scope.clearSearch = function(){
								scope.ui.searchValue = '';
							};
						}

						//#TODO ?: WRAP INTO $timeout?
						ngModelController.$render();

					}
				};
			}
		]
	);
})();
<commit_msg>fix https://github.com/inmagik/ionic-modal-select/issues/63<commit_after>(function(){

	angular.module('ionic-modal-select', [])

		.directive('compile', ['$compile', function ($compile) {
			return function(scope, iElement, iAttrs) {
				var x = scope.$watch(
					function(scope) {
						// watch the 'compile' expression for changes
						return scope.$eval(iAttrs.compile);
					},
					function(value) {
						// when the 'compile' expression changes
						// assign it into the current DOM
						iElement.html(value);

						// compile the new DOM and link it to the current
						// scope.
						// NOTE: we only compile .childNodes so that
						// we don't get into infinite loop compiling ourselves
						$compile(iElement.contents())(scope);

						//deactivate watch if "compile-once" is set to "true"
						if (iAttrs.compileOnce === 'true') {
							x();
						}
					}
				);
			};
		}])

		.directive('modalSelect', ['$ionicModal','$timeout', '$filter', '$parse', '$templateCache',
			function ($ionicModal, $timeout, $filter, $parse, $templateCache ) {

				return {
					restrict: 'A',
					require : 'ngModel',
					scope: {
						initialOptions:"=options",
						optionGetter:"&",
						searchFilters: "=searchFilters",
						searchProperties:'=',
						onSelect: "&",
						onSearch: "&",
						onReset: "&"
					},
					link: function (scope, iElement, iAttrs, ngModelController, transclude) {

						var shortList = true;
						var shortListBreak = iAttrs.shortListBreak ? parseInt(iAttrs.shortListBreak) : 10;
						var setFromProperty= iAttrs.optionProperty;
						var onOptionSelect = iAttrs.optionGetter;
						var clearSearchOnSelect = iAttrs.clearSearchOnSelect !== "false" ? true : false;
						var searchProperties = scope.searchProperties  ? scope.searchProperties : false;

						//multiple values settings.
                        var multiple = iAttrs.multiple  ? true : false;
                        if (multiple) {
                            scope.isChecked = {};
                        }
                        var multipleNullValue = iAttrs.multipleNullValue ? scope.$eval(iAttrs.multipleNullValue) : [];

						scope.ui = {
							modalTitle : iAttrs.modalTitle || 'Select an option',
							okButton : iAttrs.okButton || 'OK',
							hideReset : iAttrs.hideReset  !== "true" ? false : true,
							resetButton : iAttrs.resetButton || 'Reset',
							cancelButton : iAttrs.cancelButton || 'Cancel',
							loadListMessage : iAttrs.loadListMessage || 'Loading',
							modalClass : iAttrs.modalClass || '',
							headerFooterClass : iAttrs.headerFooterClass || 'bar-stable',
							value  : null,
							selectedClass : iAttrs.selectedClass || 'option-selected',
							itemClass: iAttrs.itemClass || 'item item-text-wrap',
							searchTemplate: iAttrs.searchTemplate || (multiple ? 'modal-template-multiple.html' : 'modal-template.html'),

							//search stuff
							hasSearch : iAttrs.hasSearch  !== "true" ? false : true,
							searchValue : '',
							searchPlaceholder : iAttrs.searchPlaceholder || 'Search',
							subHeaderClass : iAttrs.subHeaderClass || 'bar-stable',
							cancelSearchButton : iAttrs.cancelSearchButton || 'Clear'

						};

						var allOptions = [];
						scope.options = [];

                        if (iAttrs.optionsExpression) {
                            var optionsExpression = iAttrs.optionsExpression;
                            var match = optionsExpression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
                            if (!match) {
                                throw new Error("collection-repeat expected expression in form of '_item_ in " +
                                                  "_collection_[ track by _id_]' but got '" + iAttrs.optionsExpression + "'.");
                            }
                            var keyExpr = match[1];
                            var listExpr = match[2];
                            var listGetter = $parse(listExpr);
                            var s = iElement.scope();

                            scope.$watch(
                                function(){
                                    return listGetter(s);
                                },
                                function(nv, ov){
                                    initialOptionsSetup(nv);
                                    updateListMode();
                                },
                                true
                            );

                        } else {
                            scope.$watchCollection('initialOptions', function(nv){
                                initialOptionsSetup(nv);
                                updateListMode();
                            });
                        }

                        //#TODO: this is due to different single vs multiple template
                        //but adds lots of complexity here and in search
                        function initialOptionsSetup(nv){
														nv = nv || [];
                            if ( !multiple ) {
                                allOptions = angular.copy(nv);
                                scope.options = angular.copy(nv);
                            } else {
                                allOptions = nv.map(function(item, idx){
                                    return [idx, angular.copy(item)]
                                });
                                scope.options = angular.copy(allOptions);
                            }
                        }

						// getting options template
						var opt = iElement[0].querySelector('.option');
						if (!opt) {
							throw new Error({
								name:'modalSelectError:noOptionTemplate',
								message:'When using modalSelect directive you must include an element with class "option" to provide a template for your select options.',
								toString:function(){
									return this.name + " " + this.message;
								}
							});
						}
						scope.inner = angular.element(opt).html();

						//add support for .remove for older devices
						if (!('remove' in Element.prototype)) {
							Element.prototype.remove = function() {
								this.parentNode.removeChild(this);
							};
						}

						angular.element(opt).remove();

						var notFound = iElement[0].querySelector('.not-found');
						if(notFound) {
							scope.notFound = angular.element(notFound).html();
							angular.element(notFound).remove();
						}

						function updateListMode(){
							//shortList controls wether using ng-repeat instead of collection-repeat
							if (iAttrs.useCollectionRepeat === "true") {
								shortList = false;
							} else if (iAttrs.useCollectionRepeat === "false") {
								shortList = true;
							} else {
								if (typeof(scope.options) !=="undefined"){
									shortList = !!(scope.options.length < shortListBreak);
								}
							}

							scope.ui.shortList = shortList;
						}

						ngModelController.$render = function(){
							scope.ui.value = ngModelController.$viewValue;
						};

						var getSelectedValue = scope.getSelectedValue = function(option){
							var val = null;
							if (option === null || option === undefined) {
								return option;
							}
							if (onOptionSelect) {
								return scope.optionGetter({option:option});
							}
							if (setFromProperty) {
								val = option[setFromProperty];
							} else {
								val = option;
							}
							return val;
						};

						scope.setOption = function(option){
							var oldValue = ngModelController.$viewValue;
							var val = getSelectedValue(option);
							ngModelController.$setViewValue(val);
							ngModelController.$render();

							if (scope.onSelect) {
								scope.onSelect({ newValue: val, oldValue: oldValue });
							}
							scope.modal.hide().then(function(){
								scope.showList = false;
								if (scope.ui.hasSearch) {
									if(clearSearchOnSelect) {
										scope.ui.searchValue = '';
									}
								}
							});
						};

						// Filter object {id: <filterId>, active: <boolean>}
						// Used as auxiliary query params when querying server for search results
						scope.setFilter = function(filterId) {
							angular.forEach(scope.searchFilters, function(filter) {
								if(filter.id == filterId) {
									filter.active = !filter.active;
								} else {
									filter.active = false;
								}
							});

							// Trigger another search when the search filters change
							if(scope.onSearch) {
								scope.onSearch({query: scope.ui.searchValue});
							}
						};

						scope.unsetValue = function(){
							$timeout(function(){
								ngModelController.$setViewValue("");
								ngModelController.$render();
								scope.modal.hide();
								scope.showList = false;
								if (scope.onReset && angular.isFunction(scope.onReset)) {
									scope.onReset();
								}
							});
						};

                        scope.setValues = function(){
                            var checkedItems = [];
                            angular.forEach(scope.isChecked, function(v, k){
                                if(v){
                                    checkedItems.push(allOptions[k][1])
                                }

                            });
                            var oldValues = ngModelController.$viewValue;
                            var vals = checkedItems.map(function(item){
                                return getSelectedValue(item);
                            });
                            ngModelController.$setViewValue(vals);
                            ngModelController.$render();

                            if (scope.onSelect) {
                                scope.onSelect({ newValue: vals, oldValue: oldValues });
                            }
                            scope.modal.hide().then(function(){
                                scope.showList = false;
                                if (scope.ui.hasSearch) {
                                   if(clearSearchOnSelect){
                                        scope.ui.searchValue = '';
                                    }
                                }
                            });

                        };

                        scope.unsetValues = function(){
                            $timeout(function(){
                                ngModelController.$setViewValue(multipleNullValue);
                                ngModelController.$render();
                                scope.modal.hide();
                                scope.showList = false;
                                if (scope.onReset && angular.isFunction(scope.onReset)) {
                                    scope.onReset();
                                }
                            });
                        };

						scope.closeModal = function(){
							scope.modal.hide().then(function(){
								scope.showList = false;
							});
						};

						scope.compareValues = function(a, b){
							return angular.equals(a, b);
						};

						//loading the modal
						var modalTpl = null;
						if(iAttrs.searchTemplate) {
							scope.modal = $ionicModal.fromTemplate(
								$templateCache.get(iAttrs.searchTemplate),
								{ scope: scope }
							);
						} else {
							modalTpl = multiple ? 'modal-template-multiple.html' : 'modal-template.html';
							scope.modal = $ionicModal.fromTemplate(
								modalSelectTemplates[modalTpl],
								{ scope: scope }
							);
						}

						scope.$on('$destroy', function(){
							scope.modal.remove();
						});

						iElement.on('click', function(){
							if (shortList) {
								scope.showList = true;
								scope.modal.show();
							} else {
								scope.modal.show()
									.then(function(){
										scope.showList = true;
										scope.ui.shortList = shortList;
									});
							}
						});

                        //filter function
						if (scope.ui.hasSearch) {
							scope.$watch('ui.searchValue', function(nv){
                                var whatToSearch;
                                if ( !multiple  ) {
                                    whatToSearch = allOptions;
                                } else {
                                    whatToSearch = allOptions.map(function(item){
                                        return item[1];
                                    });
                                }

								if(iAttrs.onSearch) {
                                    scope.onSearch({query: nv});
								} else {
                                    var filteredOpts = $filter('filter')(whatToSearch, nv, function(actual, expected) {
										if(!actual){
											// if actual is an empty string,empty object,null,or undefined
											return false;
										}
										if (searchProperties){
											if (typeof actual == 'object'){
												for (var i = 0; i < searchProperties.length; i++){
													if (actual[searchProperties[i]] && actual[searchProperties[i]].toLowerCase().indexOf(expected.toLowerCase()) >= 0){
														return true;
													}
												}
											}
											return false;
										} else {
											if(actual.toString().toLowerCase().indexOf(expected.toLowerCase()) >= 0){
												return true;
											}
										}
										return false;
									});

                                    var oldLen = scope.options.length;
                                    if ( !multiple ){
                                        scope.options = filteredOpts;

                                    } else {
                                        //#TODO: lots of loops here!
                                        var newOpts = [];
                                        angular.forEach(filteredOpts, function(item){
                                            var originalItem = allOptions.find(function(it){
                                                return it[1] == item;
                                            });
                                            if( originalItem ){
                                                newOpts.push(originalItem);
                                            }
                                        });
                                        scope.options = newOpts;
                                    }
                                    if(oldLen != scope.options.length){
                                        //#todo: should resize scroll or scroll up here
                                    }
								}
							});
							scope.clearSearch = function(){
								scope.ui.searchValue = '';
							};
						}

						//#TODO ?: WRAP INTO $timeout?
						ngModelController.$render();

					}
				};
			}
		]
	);
})();
<|endoftext|>


<commit_before>module('baidu.ui.Slider');

(function() {
	/**
	 * create a div for test start
	 *
create div
	 *
set id div_test
	 *
set type text
	 *
	 * @return div@type text
	 */
	function _testStart() {
		var id = "div_test";
		var div = document.createElement("div");
		div.id = id;
		$(div).css('width', '200px').css('height', '20px').css('border','solid').css('color', 'black');
		document.body.appendChild(div);
		te.dom.push(div);
	}

	var s = QUnit.testStart;
	QUnit.testStart = function() {
		s.apply(this, arguments);
		_testStart();
	}
})();

test('initi', function() {
	expect(5);
	stop();
	ua.loadcss(upath+'Slider/style.css',function(){
		var slider = new baidu.ui.Slider();
		var div = te.dom[0];
		slider.render(div);
		equal(slider.min, 0, 'check range min');
		equal(slider.max, 100, 'check range max');
		equal(slider.uiType, 'slider', 'check ui type');
		equal(slider.value, 0, 'check value');
		ok(!slider.disabled, 'check enable');
		start();
	});
})

test('update', function() {
	var v = 0;
	var first = true;
	expect(11);
	var options = {
		onslidestop : function() {
			ok(false, 'stop will not be called');
		},
		onslidestart : function() {
			ok(false, 'start will not be called');
		},
		onslide : function() {
			ok(false, 'slide will not be called');
		},
		onupdate : function() {
			if (first) {
				/* 第一次update是在render中调用 */
				equal(s.value, 0, 'check init value');
				first = false;
			} else {
				/*
				 * 在slider中点击鼠标，滑块中心位置而不是滑块最左边会滑动到鼠标的坐标处，
				 * 因此需要补足滑块的一半长度
				 */
				var left = parseInt($(s.getThumb()).css('left'));
				if (left > 190) {
					v -= s.offsetWidth;// 滑块不会超出slider外面去，left为200-滑块的宽度
				}

				ok(left,v,
						'check thumb position' + left);
				v += 20;
			}

		}
	}

	var s = new baidu.ui.Slider(options);
	var id = "div_test";
	var div = te.dom[0];
	s.render(div);
	var body = s.getBody();
	stop();
	var x = parseInt(baidu.dom.getPosition(body)['left']);
	v = 20-parseInt($(s.getThumb()).css('width'))/2;
	var handle = setInterval(function() {
		if (s.value == 100) {
			clearInterval(handle);
			start();
		}
		x += 20;
		ua.mousemove(body, {
			/* 一共200px，分10次拖动 */
			clientX : x
		});
		ua.mousedown(body, {
			clientX : x
		});
		ua.mouseup(body, {
			clientX : x
		});
	}, 20);
})

test('onslide', function() {
	stop();
	ua.loadcss(upath+'Slider/style.css',function(){
		var options = {
			onslidestop : function() {
				equal($(s.getThumb()).css('left'), '20px',
						'slide stop check thumb left');
			},
			onslidestart : function() {
				equal($(s.getThumb()).css('left'), '0px',
						'slide start check thumb left');
			},
			onslide : function() {
				ok(true, 'on slide');
			},
			onupdate : function() {
				ok(true, 'update');
			}
		}
		var s = new baidu.ui.Slider(options);
		var div = te.dom[0];
		s.render(div);
		var thumb = s.getThumb();
		alert(s.getBody()['clientWidth']-thumb['offsetWidth']);
		var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
		var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
		ua.mousemove(thumb, {
			clientX : thumbX+3,
			clientY : thumbY
		});
		ua.mousedown(thumb, {
			clientX : thumbX+3,
			clientY : thumbY
		});
		setTimeout(function() {
			ua.mousemove(thumb, {
				clientX : thumbX + 20,
				clientY : thumbY
			});
		}, 30);
		setTimeout(function() {
			ua.mouseup(thumb, {
				clientX : thumbX + 20,
				clientY : thumbY
			});
			start();
		}, 60);
	})
})

/*
test('range', function() {
	stop();
	var options = {
		range : [ 0, 200 ]
	}
	var s = new baidu.ui.Slider(options);
	var div = te.dom[0];
	s.render(div);
	var thumb = s.getThumb();
	var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
	var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
	ua.mousemove(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	ua.mousedown(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	setTimeout(function() {
		ua.mousemove(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
	}, 30);
	setTimeout(function() {
		ua.mouseup(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
		equal(s.value,200,'check value');
		start();
	}, 60);
})
*/

test('set min max', function() {
	stop();
	var options = {
		min:0,
		max:300
	}
	var s = new baidu.ui.Slider(options);
	var div = te.dom[0];
	s.render(div);
	var thumb = s.getThumb();
	var body = s.getBody();
	var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
	var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
	var divwidth =  parseInt($(body).css('width'));
	ua.mousemove(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	ua.mousedown(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	setTimeout(function() {
		ua.mousemove(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
	}, 30);
	setTimeout(function() {
		ua.mouseup(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
		equal($(thumb).css('left'),(divwidth-parseInt($(thumb).css('width')))+'px','check thumb left');
		equal(s.getValue(),options.max,'check value');
		start();
	}, 60);
})
/**
 * 事件没有删除 ，开问题单 PUBLICGE-344
 */
test('dispose',function(){
	expect(2);
	var l1 = baidu.event._listeners.length;
	var s = new baidu.ui.Slider();
	s.render(te.dom[0]);
	ok(baidu.dom.g(s.getId()),'created');
	s.dispose();
	equal(baidu.dom.g(s.getId()),null,'disposed');
	equals(baidu.event._listeners.length, l1, 'event removed all');
});
/**
 * disable 的效果是，滑块不能滑动；
 * enable的效果是取消disable的作用；
 * 两者要组合测试
 */
test('disable enable',function(){
		var options = {};
		stop();
		var s = new baidu.ui.Slider(options);
		var div = te.dom[0];
		s.render(div);
		var body = s.getBody(),
		    thumb = s.getThumb();
		    thumbleft = thumb.style.left;
		    thumbwidth = parseInt(baidu.dom.getStyle(thumb,'width'));
		var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
		var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
		s.disable();
		ok(s.disabled,'set disabled');
		ua.mousemove(body, {
			clientX : thumbX+50,
			clientY : thumbY
		});
		ua.mousedown(body, {
			clientX : thumbX+50,
			clientY : thumbY
		});
		setTimeout(function(){
			ua.mouseup(body, {
				clientX : thumbX + 50,
				clientY : thumbY
			});
			equal($(s.getThumb()).css('left'), thumbleft,'after disable thumb left ');
			s.enable();
		},30);
		setTimeout(function(){
			ua.mousedown(body, {
				clientX : thumbX+50,
				clientY : thumbY
			});
			setTimeout(function(){
				ua.mouseup(body, {
					clientX : thumbX+50,
					clientY : thumbY
				});
				equal($(s.getThumb()).css('left'), (50-thumbwidth/2)+'px','slide stop check thumb left');
				start();
			},100);
		},60)

});

<commit_msg>删除调试信息<commit_after>module('baidu.ui.Slider');

(function() {
	/**
	 * create a div for test start
	 *
create div
	 *
set id div_test
	 *
set type text
	 *
	 * @return div@type text
	 */
	function _testStart() {
		var id = "div_test";
		var div = document.createElement("div");
		div.id = id;
		$(div).css('width', '200px').css('height', '20px').css('border','solid').css('color', 'black');
		document.body.appendChild(div);
		te.dom.push(div);
	}

	var s = QUnit.testStart;
	QUnit.testStart = function() {
		s.apply(this, arguments);
		_testStart();
	}
})();

test('initi', function() {
	expect(5);
	stop();
	ua.loadcss(upath+'Slider/style.css',function(){
		var slider = new baidu.ui.Slider();
		var div = te.dom[0];
		slider.render(div);
		equal(slider.min, 0, 'check range min');
		equal(slider.max, 100, 'check range max');
		equal(slider.uiType, 'slider', 'check ui type');
		equal(slider.value, 0, 'check value');
		ok(!slider.disabled, 'check enable');
		start();
	});
})

test('update', function() {
	var v = 0;
	var first = true;
	expect(11);
	var options = {
		onslidestop : function() {
			ok(false, 'stop will not be called');
		},
		onslidestart : function() {
			ok(false, 'start will not be called');
		},
		onslide : function() {
			ok(false, 'slide will not be called');
		},
		onupdate : function() {
			if (first) {
				/* 第一次update是在render中调用 */
				equal(s.value, 0, 'check init value');
				first = false;
			} else {
				/*
				 * 在slider中点击鼠标，滑块中心位置而不是滑块最左边会滑动到鼠标的坐标处，
				 * 因此需要补足滑块的一半长度
				 */
				var left = parseInt($(s.getThumb()).css('left'));
				if (left > 190) {
					v -= s.offsetWidth;// 滑块不会超出slider外面去，left为200-滑块的宽度
				}

				ok(left,v,
						'check thumb position' + left);
				v += 20;
			}

		}
	}

	var s = new baidu.ui.Slider(options);
	var id = "div_test";
	var div = te.dom[0];
	s.render(div);
	var body = s.getBody();
	stop();
	var x = parseInt(baidu.dom.getPosition(body)['left']);
	v = 20-parseInt($(s.getThumb()).css('width'))/2;
	var handle = setInterval(function() {
		if (s.value == 100) {
			clearInterval(handle);
			start();
		}
		x += 20;
		ua.mousemove(body, {
			/* 一共200px，分10次拖动 */
			clientX : x
		});
		ua.mousedown(body, {
			clientX : x
		});
		ua.mouseup(body, {
			clientX : x
		});
	}, 20);
})

test('onslide', function() {
	stop();
	ua.loadcss(upath+'Slider/style.css',function(){
		var options = {
			onslidestop : function() {
				equal($(s.getThumb()).css('left'), '20px',
						'slide stop check thumb left');
			},
			onslidestart : function() {
				equal($(s.getThumb()).css('left'), '0px',
						'slide start check thumb left');
			},
			onslide : function() {
				ok(true, 'on slide');
			},
			onupdate : function() {
				ok(true, 'update');
			}
		}
		var s = new baidu.ui.Slider(options);
		var div = te.dom[0];
		s.render(div);
		var thumb = s.getThumb();
		var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
		var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
		ua.mousemove(thumb, {
			clientX : thumbX+3,
			clientY : thumbY
		});
		ua.mousedown(thumb, {
			clientX : thumbX+3,
			clientY : thumbY
		});
		setTimeout(function() {
			ua.mousemove(thumb, {
				clientX : thumbX + 20,
				clientY : thumbY
			});
		}, 30);
		setTimeout(function() {
			ua.mouseup(thumb, {
				clientX : thumbX + 20,
				clientY : thumbY
			});
			start();
		}, 60);
	})
})

/*
test('range', function() {
	stop();
	var options = {
		range : [ 0, 200 ]
	}
	var s = new baidu.ui.Slider(options);
	var div = te.dom[0];
	s.render(div);
	var thumb = s.getThumb();
	var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
	var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
	ua.mousemove(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	ua.mousedown(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	setTimeout(function() {
		ua.mousemove(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
	}, 30);
	setTimeout(function() {
		ua.mouseup(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
		equal(s.value,200,'check value');
		start();
	}, 60);
})
*/

test('set min max', function() {
	stop();
	var options = {
		min:0,
		max:300
	}
	var s = new baidu.ui.Slider(options);
	var div = te.dom[0];
	s.render(div);
	var thumb = s.getThumb();
	var body = s.getBody();
	var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
	var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
	var divwidth =  parseInt($(body).css('width'));
	ua.mousemove(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	ua.mousedown(thumb, {
		clientX : thumbX,
		clientY : thumbY
	});
	setTimeout(function() {
		ua.mousemove(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
	}, 30);
	setTimeout(function() {
		ua.mouseup(thumb, {
			clientX : thumbX + 405,
			clientY : thumbY
		});
		equal($(thumb).css('left'),(divwidth-parseInt($(thumb).css('width')))+'px','check thumb left');
		equal(s.getValue(),options.max,'check value');
		start();
	}, 60);
})
/**
 * 事件没有删除 ，开问题单 PUBLICGE-344
 */
test('dispose',function(){
	expect(2);
	var l1 = baidu.event._listeners.length;
	var s = new baidu.ui.Slider();
	s.render(te.dom[0]);
	ok(baidu.dom.g(s.getId()),'created');
	s.dispose();
	equal(baidu.dom.g(s.getId()),null,'disposed');
	equals(baidu.event._listeners.length, l1, 'event removed all');
});
/**
 * disable 的效果是，滑块不能滑动；
 * enable的效果是取消disable的作用；
 * 两者要组合测试
 */
test('disable enable',function(){
		var options = {};
		stop();
		var s = new baidu.ui.Slider(options);
		var div = te.dom[0];
		s.render(div);
		var body = s.getBody(),
		    thumb = s.getThumb();
		    thumbleft = thumb.style.left;
		    thumbwidth = parseInt(baidu.dom.getStyle(thumb,'width'));
		var thumbX = parseInt(baidu.dom.getPosition(thumb)['left']);
		var thumbY = parseInt(baidu.dom.getPosition(thumb)['top']);
		s.disable();
		ok(s.disabled,'set disabled');
		ua.mousemove(body, {
			clientX : thumbX+50,
			clientY : thumbY
		});
		ua.mousedown(body, {
			clientX : thumbX+50,
			clientY : thumbY
		});
		setTimeout(function(){
			ua.mouseup(body, {
				clientX : thumbX + 50,
				clientY : thumbY
			});
			equal($(s.getThumb()).css('left'), thumbleft,'after disable thumb left ');
			s.enable();
		},30);
		setTimeout(function(){
			ua.mousedown(body, {
				clientX : thumbX+50,
				clientY : thumbY
			});
			setTimeout(function(){
				ua.mouseup(body, {
					clientX : thumbX+50,
					clientY : thumbY
				});
				equal($(s.getThumb()).css('left'), (50-thumbwidth/2)+'px','slide stop check thumb left');
				start();
			},100);
		},60)

});

<|endoftext|>


<commit_before>import React, { useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import mapbox from 'mapbox-gl';

import config from '../../config';

export default function Map({ center, bbox, scrollZoomDisabled, children }) {
  const containerRef = useRef();

  const [map, setMap] = useState(null);

  useEffect(() => {
    mapbox.accessToken = config.mapbox.token;
    const m = new mapbox.Map({
      container: containerRef.current,
      style: config.mapbox.baseStyle,
      zoom: 1,
      center: [0, 0],
    });

    m.addControl(new mapbox.NavigationControl());

    if (scrollZoomDisabled) m.scrollZoom.disable();

    m.on('load', () => {
      setMap(m);

      if (process.env.DS_ENV === 'development') {
        // makes map accessible in console for debugging
        window.map = m;
      }

      if (center) {
        m.flyTo({ center, zoom: 15 });
      } else if (bbox) {
        m.fitBounds(bbox, { padding: 20 });
      }
    });

    return () => {
      if (map) {
        map.remove();
      }
    };
  }, []);

  return (
    <div className="map" style={{ minHeight: `20rem` }}>

        {map &&
          children &&
          React.Children.map(children, child =>
            React.cloneElement(child, {
              map,
            })
          )}

  );
}

Map.propTypes = {
  center: PropTypes.arrayOf(PropTypes.number),
  bbox: PropTypes.arrayOf(PropTypes.number),
  scrollZoomDisabled: PropTypes.bool,
  children: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.arrayOf(PropTypes.element),
  ]),
};
<commit_msg>Set max zoom on fit bounds<commit_after>import React, { useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import mapbox from 'mapbox-gl';

import config from '../../config';

export default function Map({ center, bbox, scrollZoomDisabled, children }) {
  const containerRef = useRef();

  const [map, setMap] = useState(null);

  useEffect(() => {
    mapbox.accessToken = config.mapbox.token;
    const m = new mapbox.Map({
      container: containerRef.current,
      style: config.mapbox.baseStyle,
      zoom: 1,
      center: [0, 0],
    });

    m.addControl(new mapbox.NavigationControl());

    if (scrollZoomDisabled) m.scrollZoom.disable();

    m.on('load', () => {
      setMap(m);

      if (process.env.DS_ENV === 'development') {
        // makes map accessible in console for debugging
        window.map = m;
      }

      if (center) {
        m.flyTo({ center, zoom: 15 });
      } else if (bbox) {
        m.fitBounds(bbox, { padding: 20, maxZoom: 18 });
      }
    });

    return () => {
      if (map) {
        map.remove();
      }
    };
  }, []);

  return (
    <div className="map" style={{ minHeight: `20rem` }}>

        {map &&
          children &&
          React.Children.map(children, child =>
            React.cloneElement(child, {
              map,
            })
          )}

  );
}

Map.propTypes = {
  center: PropTypes.arrayOf(PropTypes.number),
  bbox: PropTypes.arrayOf(PropTypes.number),
  scrollZoomDisabled: PropTypes.bool,
  children: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.arrayOf(PropTypes.element),
  ]),
};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Panel from 'react-bootstrap/lib/Panel';
import Button from 'react-bootstrap/lib/Button';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Form from 'react-bootstrap/lib/Form';
import FormGroup from 'react-bootstrap/lib/FormGroup';
import FormControl from 'react-bootstrap/lib/FormControl';
import ControlLabel from 'react-bootstrap/lib/ControlLabel';

export default class LoginForm extends Component {
	static propTypes = {
		login: PropTypes.func.isRequired,
		resume: PropTypes.func.isRequired,
		fields: PropTypes.object.isRequired
	};

	handleSubmit(e) {
		e.preventDefault();

		this.props.login(
			this.props.fields.email.value,
			this.props.fields.password.value,
			this.props.fields.username.value
		);
	}

	componentWillMount() {
		this.props.resume();
	}

	render() {
		const { fields : { email, password, username } } = this.props;

		return (
			<Grid>
				<Row>
					<Col md={6} mdPush={3}>
						<Panel className="login">
							<Form>
								<FormGroup controlId="email" bsClass="form-group label-floating">
									<ControlLabel>email</ControlLabel>
									<FormControl
										type="text"
										{...email}
									/>
								</FormGroup>
								<FormGroup controlId="password" bsClass="form-group label-floating">
									<ControlLabel>password</ControlLabel>
									<FormControl
										type="password"
										{...password}
									/>
								</FormGroup>
								<FormGroup controlId="username" bsClass="form-group label-floating">
									<ControlLabel>username</ControlLabel>
									<FormControl
										type="text"
										{...username}
									/>
								</FormGroup>
								<Button
									bsStyle="primary"
									type="submit"
									onClick={this.handleSubmit.bind(this)}>login</Button>
							</Form>
						</Panel>
					</Col>
				</Row>
			</Grid>
		);
	}
}
<commit_msg>fix: Fix unknown props warning following React update<commit_after>import React, { Component, PropTypes } from 'react';
import Panel from 'react-bootstrap/lib/Panel';
import Button from 'react-bootstrap/lib/Button';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Form from 'react-bootstrap/lib/Form';
import FormGroup from 'react-bootstrap/lib/FormGroup';
import FormControl from 'react-bootstrap/lib/FormControl';
import ControlLabel from 'react-bootstrap/lib/ControlLabel';
import omit from 'lodash/omit';

export default class LoginForm extends Component {
	static propTypes = {
		login: PropTypes.func.isRequired,
		resume: PropTypes.func.isRequired,
		fields: PropTypes.object.isRequired
	};

	handleSubmit(e) {
		e.preventDefault();

		this.props.login(
			this.props.fields.email.value,
			this.props.fields.password.value,
			this.props.fields.username.value
		);
	}

	componentWillMount() {
		this.props.resume();
	}

	render() {
		const { fields : { email, password, username } } = this.props;
		// ReduxFrom field props unknown to FormControl (causes warning since React 0.15.2)
		const unknownProps = [
			'initialValue', 'autofill', 'onUpdate', 'valid', 'invalid',
			'dirty', 'pristine', 'active', 'touched', 'visited', 'autofilled'
		];

		return (
			<Grid>
				<Row>
					<Col md={6} mdPush={3}>
						<Panel className="login">
							<Form>
								<FormGroup controlId="email" bsClass="form-group label-floating">
									<ControlLabel>email</ControlLabel>
									<FormControl
										type="text"
										{...omit(email, unknownProps)}
									/>
								</FormGroup>
								<FormGroup controlId="password" bsClass="form-group label-floating">
									<ControlLabel>password</ControlLabel>
									<FormControl
										type="password"
										{...omit(password, unknownProps)}
									/>
								</FormGroup>
								<FormGroup controlId="username" bsClass="form-group label-floating">
									<ControlLabel>username</ControlLabel>
									<FormControl
										type="text"
										{...omit(username, unknownProps)}
									/>
								</FormGroup>
								<Button
									bsStyle="primary"
									type="submit"
									onClick={this.handleSubmit.bind(this)}>login</Button>
							</Form>
						</Panel>
					</Col>
				</Row>
			</Grid>
		);
	}
}
<|endoftext|>


<commit_before>function BaseRenderer(){}
BaseRenderer.prototype.checkLayers = function(num){
    var i, len = this.layers.length, data;
    this.completeLayers = true;
    for (i = len - 1; i >= 0; i--) {
        if (!this.elements[i]) {
            data = this.layers[i];
            if(data.ip - data.st <= (num - this.layers[i].st) && data.op - data.st > (num - this.layers[i].st))
            {
                this.buildItem(i);
            }
        }
        this.completeLayers = this.elements[i] ? this.completeLayers:false;
    }
    this.checkPendingElements();
};

BaseRenderer.prototype.createItem = function(layer){
    switch(layer.ty){
        case 2:
            return this.createImage(layer);
        case 0:
            return this.createComp(layer);
        case 1:
            return this.createSolid(layer);
        case 4:
            return this.createShape(layer);
        case 5:
            return this.createText(layer);
        case 99:
            return null;
    }
    return this.createBase(layer);
};
BaseRenderer.prototype.buildAllItems = function(){
    var i, len = this.layers.length;
    for(i=0;i<len;i+=1){
        this.buildItem(i);
    }
    this.checkPendingElements();
};

BaseRenderer.prototype.includeLayers = function(newLayers){
    this.completeLayers = false;
    var i, len = newLayers.length;
    var j, jLen = this.layers.length;
    for(i=0;i<len;i+=1){
        j = 0;
        while(j<jLen){
            if(this.layers[j].id == newLayers[i].id){
                this.layers[j] = newLayers[i];
                break;
            }
            j += 1;
        }
    }
};

BaseRenderer.prototype.setProjectInterface = function(pInterface){
    this.globalData.projectInterface = pInterface;
};

BaseRenderer.prototype.initItems = function(){
    if(!this.globalData.progressiveLoad){
        this.buildAllItems();
    }
};
BaseRenderer.prototype.buildElementParenting = function(element, parentName, hierarchy){
    hierarchy = hierarchy || [];
    var elements = this.elements;
    var layers = this.layers;
    var i=0, len = layers.length;
    while(i<len){
        if(layers[i].ind == parentName){
            if(!elements[i] || elements[i] === true){
                this.buildItem(i);
                this.addPendingElement(element);
            } else if(layers[i].parent !== undefined){
                hierarchy.push(elements[i]);
                elements[i]._isParent = true;
                this.buildElementParenting(element,layers[i].parent, hierarchy);
            } else {
                hierarchy.push(elements[i]);
                elements[i]._isParent = true;
                element.setHierarchy(hierarchy);
            }

        }
        i += 1;
    }
};

BaseRenderer.prototype.addPendingElement = function(element){
    this.pendingElements.push(element);
};<commit_msg>restored camera object for html renderer<commit_after>function BaseRenderer(){}
BaseRenderer.prototype.checkLayers = function(num){
    var i, len = this.layers.length, data;
    this.completeLayers = true;
    for (i = len - 1; i >= 0; i--) {
        if (!this.elements[i]) {
            data = this.layers[i];
            if(data.ip - data.st <= (num - this.layers[i].st) && data.op - data.st > (num - this.layers[i].st))
            {
                this.buildItem(i);
            }
        }
        this.completeLayers = this.elements[i] ? this.completeLayers:false;
    }
    this.checkPendingElements();
};

BaseRenderer.prototype.createItem = function(layer){
    switch(layer.ty){
        case 2:
            return this.createImage(layer);
        case 0:
            return this.createComp(layer);
        case 1:
            return this.createSolid(layer);
        case 4:
            return this.createShape(layer);
        case 5:
            return this.createText(layer);
        case 13:
            return this.createCamera(layer);
        case 99:
            return null;
    }
    return this.createBase(layer);
};
BaseRenderer.prototype.buildAllItems = function(){
    var i, len = this.layers.length;
    for(i=0;i<len;i+=1){
        this.buildItem(i);
    }
    this.checkPendingElements();
};

BaseRenderer.prototype.includeLayers = function(newLayers){
    this.completeLayers = false;
    var i, len = newLayers.length;
    var j, jLen = this.layers.length;
    for(i=0;i<len;i+=1){
        j = 0;
        while(j<jLen){
            if(this.layers[j].id == newLayers[i].id){
                this.layers[j] = newLayers[i];
                break;
            }
            j += 1;
        }
    }
};

BaseRenderer.prototype.setProjectInterface = function(pInterface){
    this.globalData.projectInterface = pInterface;
};

BaseRenderer.prototype.initItems = function(){
    if(!this.globalData.progressiveLoad){
        this.buildAllItems();
    }
};
BaseRenderer.prototype.buildElementParenting = function(element, parentName, hierarchy){
    hierarchy = hierarchy || [];
    var elements = this.elements;
    var layers = this.layers;
    var i=0, len = layers.length;
    while(i<len){
        if(layers[i].ind == parentName){
            if(!elements[i] || elements[i] === true){
                this.buildItem(i);
                this.addPendingElement(element);
            } else if(layers[i].parent !== undefined){
                hierarchy.push(elements[i]);
                elements[i]._isParent = true;
                this.buildElementParenting(element,layers[i].parent, hierarchy);
            } else {
                hierarchy.push(elements[i]);
                elements[i]._isParent = true;
                element.setHierarchy(hierarchy);
            }

        }
        i += 1;
    }
};

BaseRenderer.prototype.addPendingElement = function(element){
    this.pendingElements.push(element);
};<|endoftext|>


<commit_before>SingleresourcepageController = AppController.extend({
  data: {

  }
});
<commit_msg>Added data.<commit_after>SingleResourcePageController = AppController.extend({
    data: {
        'title': 'Example content',
        'author': 'Illya Nziyev',
        'date': " 29 Tammi 2015",
        'content': "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est."
    }
});
<|endoftext|>