lib/utils/utils.js
function sortAttrsValues(attrs, compareAttributesAsJSON) {
    /**
     * Recursively sorts the given object by key
     * @param {Object} obj
     * @returns {Object}
     */
    function _sortObj(obj) {
        if (!_.isObject(obj)) {
            return JSON.stringify(obj);
        }

        if (_.isArray(obj)) {
            return '[' + _(obj).map(_sortObj).join(',') + ']';
        }

        return '{' +
            _(obj)
                .keys()
                .sort()
                .map(function (key) {
                    return JSON.stringify(key) + ':' + _sortObj(obj[key]);
                })
                .join(',') +
            '}';
    }

    /**
     * Parses the given in HTML attribute JSON
     * @param {String} val
     * @param {Boolean} [isClick]
     * @returns {Object}
     */
    function _parseAttr(val, isClick) {
        try {
            if (isClick) {
                /*jshint evil: true */
                var fn = Function(val);

                return fn ? fn() : {};
            }

            return JSON.parse(val);
        } catch (err) {
            return undefined;
        }
    }

    _.forEach(compareAttributesAsJSON, function (attr) {
        if (typeof attr === 'string') {
            var name = attr;
            attr = {};
            attr.name = name;
            attr.isFunction = false;
        }

        var attrValue,
            isFunction = (attr.isFunction),
            attrIndexes = _getIndexesInArray(attrs, attr.name);

        _.forEach(attrIndexes, function (index) {
            attrValue = _parseAttr(attrs[index].value, isFunction);

            attrValue = _sortObj(attrValue);

            attrs[index].value = (isFunction && attrValue ? 'return ' : '') + attrValue;
        });
    });

    return attrs;
}
function sortAttrsValues(attrs, compareAttributesAsJSON) {
    /**
     * Recursively sorts the given object by key
     * @param {Object} obj
     * @returns {Object}
     */
    function _sortObj(obj) {
        if (!_.isObject(obj)) {
            return JSON.stringify(obj);
        }

        if (_.isArray(obj)) {
            return '[' + _(obj).map(_sortObj).join(',') + ']';
        }

        return '{' +
            _(obj)
                .keys()
                .sort()
                .map(function (key) {
                    return JSON.stringify(key) + ':' + _sortObj(obj[key]);
                })
                .join(',') +
            '}';
    }

    /**
     * Parses the given in HTML attribute JSON
     * @param {String} val
     * @param {Boolean} [isClick]
     * @returns {Object}
     */
    function _parseAttr(val, isClick) {
        try {
            if (isClick) {
                /*jshint evil: true */
                var fn = Function(val);

                return fn ? fn() : {};
            }

            return JSON.parse(val);
        } catch (err) {
            return undefined;
        }
    }

    _.forEach(compareAttributesAsJSON, function (attr) {
        if (typeof attr === 'string') {
            var name = attr;
            attr = {};
            attr.name = name;
            attr.isFunction = false;
        }

        var attrValue,
            isFunction = (attr.isFunction),
            attrIndexes = _getIndexesInArray(attrs, attr.name);

        _.forEach(attrIndexes, function (index) {
            attrValue = _parseAttr(attrs[index].value, isFunction);

            attrValue = _sortObj(attrValue);

            attrs[index].value = (isFunction && attrValue ? 'return ' : '') + attrValue;
        });
    });

    return attrs;
}
function sortAttrsValues ( attrs , compareAttributesAsJSON ) { /**
     * Recursively sorts the given object by key
     * @param {Object} obj
     * @returns {Object}
     */ function _sortObj ( obj ) { if ( ! _ . isObject ( obj ) ) { return JSON . stringify ( obj ) ; } if ( _ . isArray ( obj ) ) { return '[' + _ ( obj ) . map ( _sortObj ) . join ( ',' ) + ']' ; } return '{' + _ ( obj ) . keys ( ) . sort ( ) . map ( function ( key ) { return JSON . stringify ( key ) + ':' + _sortObj ( obj [ key ] ) ; } ) . join ( ',' ) + '}' ; } /**
     * Parses the given in HTML attribute JSON
     * @param {String} val
     * @param {Boolean} [isClick]
     * @returns {Object}
     */ function _parseAttr ( val , isClick ) { try { if ( isClick ) { /*jshint evil: true */ var fn = Function ( val ) ; return fn ? fn ( ) : { } ; } return JSON . parse ( val ) ; } catch ( err ) { return undefined ; } } _ . forEach ( compareAttributesAsJSON , function ( attr ) { if ( typeof attr === 'string' ) { var name = attr ; attr = { } ; attr . name = name ; attr . isFunction = false ; } var attrValue , isFunction = ( attr . isFunction ) , attrIndexes = _getIndexesInArray ( attrs , attr . name ) ; _ . forEach ( attrIndexes , function ( index ) { attrValue = _parseAttr ( attrs [ index ] . value , isFunction ) ; attrValue = _sortObj ( attrValue ) ; attrs [ index ] . value = ( isFunction && attrValue ? 'return ' : '' ) + attrValue ; } ) ; } ) ; return attrs ; }
Sorts values of attributes which have JSON format
@param {Array} attrs
@param {Array} compareAttributesAsJSON
return {Array}
Sorts values of attributes which have JSON format