<|start_filename|>bower_components/ngtweet/src/twitterTimeline.directive.js<|end_filename|>
(function() {
'use strict';

angular
    .module('ngtweet')
    .directive('twitterTimeline', TwitterTimeline);

function TwitterTimeline(ngTweetLogger, TwitterWidgetFactory) {
    /**
     * An error
     */
    function TimelineArgumentException(timelineType, message) {
        this.timelineType = timelineType;
        this.message = message;
    }

    var scopeParamsMap = {
        id: '=?twitterTimelineId',
        screenName: '=?twitterTimelineScreenName',
        sourceType: '@?twitterTimelineType',
        userId: '=?twitterTimelineUserId',
        ownerScreenName: '=?twitterTimelineOwnerScreenName',
        slug: '=?twitterTimelineSlug',
        url: '=?twitterTimelineUrl',
    };

    // Convert the scope params to a collection of bound args
    var scopeParams = Object.keys(scopeParamsMap).reduce(function(acc, keyName) {
        if (scopeParamsMap[keyName][0] === '=') { // if two-way bound
            acc.push(keyName);
        }
        return acc;
    }, []);

    // Rules for the given parameter
    var rules = {
        profile: [['screenName'], ['userId']],
        likes: [['screenName'], ['userId']],
        collection: [['id']],
        widget: [['id']],
        list: [['id'], ['ownerScreenName', 'slug']],
        url: [['url']],
    };

    /**
     * Diff two arrays.
     * @param {Array} arr1 - The first array
     * @param {Array} arr2 - The second array
     * @return {boolean} true if the arrays are different, false otherwise
     */
    function diffArrays(arr1, arr2) {
        if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
            return true;
        }
        if (arr1.length !== arr2.length) {
            return true;
        }
        for (var i = 0, len = arr1.length; i < len; i++) {
            if (arr1[i] !== arr2[i]) {
                return true;
            }
        }
        return false;
    }

    /**
     * Turn the rules for a sourceType into a human-readable string
     * @param {string[][]} sourceRule - The sourceType rules
     * @return {string} the readable rules
     */
    function getSourceRuleString(sourceRule) {
        function getRuleString(rule) {
            if (rule.length === 1) {
                return '"' + rule + '"';
            }
            return '("' + rule.join('" AND "') + '")';
        }

        return sourceRule.map(getRuleString).join(' OR ');
    }

    /**
     * Get the arguments for the timeline based on parameter rules
     * @param {Object} scope - The directive scope
     * @return {Object} Key-Value arg object
     */
    function getTimelineArgs(scope) {
        var timelineArgs = {sourceType: scope.sourceType};
        // if this is a valid sourceType...
        if (rules.hasOwnProperty(scope.sourceType)) {
            var sourceRules = rules[scope.sourceType];
            var valid = false;
            // Loop over the required args for the source
            for (var i = 0, len = sourceRules.length; i < len; i++) {
                var rule = sourceRules[i];
                var params = {};
                for (var j = 0, ruleLen = rule.length; j < ruleLen; j++) {
                    if (angular.isDefined(scope[rule[j]])) { // if the rule is present, add it to the params collection
                        params[rule[j]] = scope[rule[j]];
                    }
                }
                if (Object.keys(params).length === ruleLen) {
                    angular.merge(timelineArgs, params);
                    valid = true;
                    break;
                }
            }
            if (!valid) {
                throw new TimelineArgumentException(scope.sourceType, 'args: ' + getSourceRuleString(sourceRules));
            }
        } else {
            throw new TimelineArgumentException(scope.sourceType, 'unknown type');
        }

        return timelineArgs;
    }

    function link(scope, element, attrs) {
        ngTweetLogger.debug('Linking', scope, element, attrs);
        if (scope.id && !angular.isString(scope.id)) {
            ngTweetLogger.warn('twitterTimelineId should probably be a string due to loss of precision.');
        }
        try {
            scope.twitterTimelineOptions = JSON.parse(attrs.twitterTimelineOptions);
        } catch (e) {
            scope.$watch(function() {
                return scope.$parent.$eval(attrs.twitterTimelineOptions);
            }, function(newValue, oldValue) {
                scope.twitterTimelineOptions = newValue;
            });
        }
        if (angular.isUndefined(scope.twitterTimelineOptions)) {
            scope.twitterTimelineOptions = {};
        }
        if (scope.sourceType) { // new style embed
            scope.$watchGroup(scopeParams, function(newValue, oldValue) {
                if (diffArrays(oldValue, newValue)) { //replacing, clear node
                    angular.element(element[0]).empty();
                }
                if (newValue.every(function isEmpty(arg) { return !arg; })) {
                    ngTweetLogger.debug('Falsey args received. Not rendering the timeline.');
                    return;
                }
                var timelineArgs;
                try {
                    timelineArgs = getTimelineArgs(scope);
                } catch (e) {
                    ngTweetLogger.error('Could not create new timeline: bad args for type "' +
                                        e.timelineType + '". Reason: ' + e.message);
                    return;
                }
                TwitterWidgetFactory.createTimelineNew(timelineArgs, element[0], scope.twitterTimelineOptions)
                    .then(function success(embed) {
                    ngTweetLogger.debug('New Timeline Success!');
                }).catch(function creationError(message) {
                    ngTweetLogger.error('Could not create new timeline: ', message, element);
                });
            });
        } else if (angular.isDefined(scope.id) || angular.isString(scope.screenName)) { // old style
            scope.$watch('id', function(newValue, oldValue) {
                if (scope.id === null) {
                    return;
                }
                if (oldValue !== undefined && newValue !== oldValue) {
                    angular.element(element[0]).empty();
                }
                TwitterWidgetFactory.createTimeline(scope.id, scope.screenName, element[0],
                        scope.twitterTimelineOptions).then(function success(embed) {
                    ngTweetLogger.debug('Timeline Success!!!');
                }).catch(function creationError(message) {
                            ngTweetLogger.error('Could not create timeline: ', message, element);
                        });
            });
        } else {
            TwitterWidgetFactory.load(element[0]);
        }
    }

    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: scopeParamsMap,
        template: '<div class="ngtweet-wrapper" ng-transclude></div>',
        link: link
    };
}
})();
