/**
 * @author Raphaël Benitte
 * @since  2011 oct.
 *
 * NewsItem javascript renderer.
 * Renders news items from an array of json objects
 */
var NewsItemRenderer = function () {

    // ensures required libraries are loaded
    if (undefined === ich || undefined === jQuery) {
        throw new Error('ICanHaz and jQuery libraries must be loaded in order to use NewsItemRenderer');
    }

    this.items = [];
    this.confidentialityLevels = [];

    return this;
};

/**
 * Default settings
 */
NewsItemRenderer.defaults = {
    displayComments:              true,
    displayWatchers:              true,
    enableActions:                true,
    fallbackTemplate:             'common',
    loader:                       '<div class="loader small horizontal"> </div>',
    commentUrl:                   '/',
    watchUrl:                     '/',
    unwatchUrl:                   '/',
    shareUrl:                     '/',
    loginRedirectUrl:             '/',
    commentPhotoFormat:           'small',
    matchingServicePhotoFormat:   'small',
    actorPhotoFormat:             'small',
    watcherPhotoFormat:           'small',
    commentsPreviewCount:         3,
    defaultCommentMessage:        __('label_you_can_type_your_comment_here', null, 'news_item'),
    defaultShowCommentsMessage:   'label_show_%comments_count%_comments',
    defaultHideCommentsMessage:   'label_hide_%comments_count%_comments',
    defaultMeLikeMessage:         'label_you_like',
    defaultMeAndOneLikeMessage:   'label_you_and_%watcher%_like',
    defaultMeAndManyLikeMessage:  'label_you_and_%watchers_count%_like',
    defaultOneLikeMessage:        'label_%watcher%_likes',
    defaultManyLikeMessage:       'label_%watchers_count%_like',
    defaultTheyLikeTitle:         'title_they_like',
    commentMessage:               '',
    showCommentsMessage:          '',
    hideCommentsMessage:          '',
    meLikeMessage:                '',
    meAndOneLikeMessage:          '',
    meAndManyLikeMessage:         '',
    oneLikeMessage:               '',
    manyLikeMessage:              '',
    theyLikeTitle:                '',
    matchingServicesPreviewCount: 6,
    previewLength:                120,
    textSeeMore:                  'link_see_more',
    textSeeLess:                  'link_see_less',
    nonMessageParameters:         {
      comments_count: 1,
      message: 1,
      actor_is_me: 1,
      comments: 1,
      actor: 1,
      id: 1,
      confidentiality_class: 1,
      confidentiality_label: 1,
      matching_services: 1
    }
};


/**
 * Current logged user
 */
NewsItemRenderer.currentUser = undefined;

/**
 * Message template object, key: action_id, value: template config, populated from php.
 */
NewsItemRenderer.messageTempates = {};

/**
 * Returns a template config from an action id, acts like a static method
 *
 * @param  actionId
 */
NewsItemRenderer.messageTemplateByActionId = function(actionId) {

    if (NewsItemRenderer.messageTempates.hasOwnProperty(actionId)) {
        return NewsItemRenderer.messageTempates[actionId];
    }

    return null;
};


NewsItemRenderer.prototype = {

    /**
     * Display time from now in a human readable format.
     *
     * @param seconds
     */
    timeAgo: function (seconds) {

        var inputDate = new Date(seconds * 1000),
            now       = new Date(),
            second    = 1000,
            minute    = second * 60,
            hour      = minute * 60,
            day       = hour   * 24,
            week      = day    * 7,
            month     = day    * 30,
            ms        = now.getTime() - inputDate.getTime();

        if (ms < minute) {
            return __('label_less_than_a_minute_ago', null, 'news_item');
        } else if (ms < 10 * minute) {
            return __('label_less_than_few_minutes_ago', null, 'news_item');
        } else if (ms < hour) {
            return __('label_less_than_an_hour_ago', null, 'news_item');
        } else if (ms < day) {
            return __('label_less_than_a_day_ago', null, 'news_item');
        } else if (ms < week) {
            return __('label_less_than_a_week_ago', null, 'news_item');
        } else if (ms < month) {
            return __('label_less_than_a_month_ago', null, 'news_item');
        }

        return __('label_more_than_a_month_ago', null, 'news_item');
    },

    /**
     * Display event period in a human readable format.
     *
     * @param seconds
     */
    eventDate: function (startDate, endDate) {
      var inputStartDate = new Date(startDate.sec * 1000);

      if (endDate !== null) {
          var inputEndDate = new Date(endDate.sec * 1000);
          var now          = new Date();

          if (inputStartDate.print('%d\/%m\/%Y') == inputEndDate.print('%d\/%m\/%Y')) {
              formattedDate = __('label_event_the', null, 'news_item') + ' ' + inputStartDate.print('%d\/%m\/%Y');
              formattedDate += ' ' + __('label_event_hour_from', null, 'news_item') + ' ' + inputStartDate.print('%H:%M');
              formattedDate += ' ' + __('label_event_hour_to', null, 'news_item')   + ' ' + inputEndDate.print('%H:%M');
          } else if (inputStartDate < now) {
              formattedDate = __('label_event_until', null, 'news_item') + ' ' + inputEndDate.print('%d\/%m\/%Y');
          } else {
              formattedDate = __('label_event_from', null, 'news_item') + ' ' + inputStartDate.print('%d\/%m\/%Y');
              formattedDate += ' ' + __('label_event_to', null, 'news_item')   + ' ' + inputEndDate.print('%d\/%m\/%Y');
          }
          return formattedDate;
      } else {
          return __('label_event_the', null, 'news_item') + ' ' + inputStartDate.print('%d\/%m\/%Y');
      }
    },

    /**
     * Prepares a news item object.
     *
     * @param  item  the news item object
     */
    prepareItem: function (item, options) {

        item.settings = jQuery.extend(NewsItemRenderer.defaults, options || {});

        item.commentMessage = item.settings.defaultCommentMessage;
        item.showCommentsMessage = item.settings.defaultShowCommentsMessage;
        item.hideCommentsMessage = item.settings.defaultHideCommentsMessage;

        item.meLikeMessage = item.settings.defaultMeLikeMessage;
        item.meAndOneLikeMessage = item.settings.defaultMeAndOneLikeMessage;
        item.meAndManyLikeMessage = item.settings.defaultMeAndManyLikeMessage;
        item.oneLikeMessage = item.settings.defaultOneLikeMessage;
        item.manyLikeMessage = item.settings.defaultManyLikeMessage;

        item.theyLikeTitle = item.settings.defaultTheyLikeTitle;

        if (item.hasOwnProperty('parameters')) {

            item.parameters.id = item.id;

            // Define confidentiality properties
            item.parameters.confidentiality_class = item.confidentiality_class;
            item.parameters.confidentiality_label = __(item.confidentiality_label, {}, 'news_item');
            
            if (item.parameters.hasOwnProperty('event_updated')) {
                if (item.hasOwnProperty('spawned_at')) {
                    item.parameters.created_at = this.timeAgo(item.spawned_at.sec);
                }
            } else {
                if (item.hasOwnProperty('created_at')) {
                    item.parameters.created_at = this.timeAgo(item.created_at);
                }
            }

            if (item.parameters.hasOwnProperty('event_startdate') && item.parameters.hasOwnProperty('event_enddate')) {
                item.parameters.event_date = this.eventDate(item.parameters.event_startdate, item.parameters.event_enddate);
            }

            // prepares news item message and its parameters.
            var templateMessage = NewsItemRenderer.messageTemplateByActionId(item.action_id);

            if (item.parameters.hasOwnProperty('message')) {
              templateMessage = { message: item.parameters.message };
            }

            var templateMessageParameters = {};

            item.parameters.message = '';
            
            // support both '%param_name%' and 'param_name' notations
            if (templateMessage !== null) {

                for (var parameterName in item.parameters) {
                    if (item.parameters.hasOwnProperty(parameterName) && !item.settings.nonMessageParameters.hasOwnProperty(parameterName)) {
                        if (item.parameters[parameterName] !== null && item.parameters[parameterName] !== undefined) {
                            var replaceableParameterName = parameterName;
                            if (replaceableParameterName.charAt(0) !== '%') {
                                replaceableParameterName = '%' + replaceableParameterName;
                            }
                            if (replaceableParameterName.charAt(replaceableParameterName.length - 1) !== '%') {
                                replaceableParameterName = replaceableParameterName + '%';
                            }

                            templateMessageParameters[replaceableParameterName] = this.filterParameterValue(item.parameters[parameterName]);
                        }
                    }
                }

                item.parameters.message = __(templateMessage.message, templateMessageParameters, 'news_item');
            }

            // Matching services
            if (item.parameters.hasOwnProperty('matching_services')) {

                item.parameters.matchingServicesCount   = item.parameters.matching_services.length;
                item.parameters.matchingServicesPreview = [];
                item.parameters.moreMatchingServices    = false;
                
                if (item.parameters.matchingServicesCount == 1) {
                    keyI18n = (item.parameters.service_type_offer) ? 'label_one_user_search_this_service' : 'label_one_user_propose_this_service';
                    item.parameters.matchingServicesCountSentence = __(keyI18n, null, 'news_item');
                } else {
                    keyI18n = (item.parameters.service_type_offer) ? 'label_%number%_users_search_this_service' : 'label_%number%_users_propose_this_service';
                    item.parameters.matchingServicesCountSentence = __(keyI18n, { '%number%': item.parameters.matchingServicesCount }, 'news_item');              
                }
                
                var matchingServicesDisplayLimit = item.parameters.matchingServicesCount;
                if (item.parameters.matchingServicesCount > item.settings.matchingServicesPreviewCount) {
                    matchingServicesDisplayLimit = item.settings.matchingServicesPreviewCount;
                    item.parameters.moreMatchingServices = true;
                }

                for (var ms = 0; ms < item.parameters.matchingServicesCount; ms ++) {
                    var matchingService = item.parameters.matching_services[ms];
                    if (matchingService.hasOwnProperty('photo_url')
                     && matchingService.photo_url !== null
                     && matchingService.photo_url !== undefined) {
                        matchingService.photo_url = matchingService.photo_url.replace('%format%', item.settings.matchingServicePhotoFormat);
                    }
                }

                for (var ms2 = 0; ms2 < matchingServicesDisplayLimit; ms2 ++) {
                    item.parameters.matchingServicesPreview.push(item.parameters.matching_services[ms2]);
                }
            }

            if (item.settings.displayWatchers === true) {
                if (item.hasOwnProperty('watchers')) {
                    if (item.watchers.length > 0) {
                        var watchers = [];
                        for (var i = 0; i < item.watchers.length; i++) {
                            watchers.push(item.watchers[i].fullname);
                        }

                        item.parameters.watchers = watchers;
                    }
                }
            }

            if (item.settings.displayComments === true) {

                item.parameters.paginate_comments = false;

                // prepare comments
                if (item.parameters.hasOwnProperty('comments')) {

                    var numComments = item.parameters.comments.length;

                    for (var j = 0; j < numComments; j++) {
                        var comment = item.parameters.comments[j];
                        comment.author_fullname = this.filterParameterValue(comment.author_fullname);
                        comment.created_at   = this.timeAgo(comment.created_at.sec);
                        comment.author_photo = comment.author_photo.replace('%format%', item.settings.commentPhotoFormat);
                    }
                }
            }

            if (item.parameters.hasOwnProperty('publication_type')) {
                item.parameters.publication_type = __(item.parameters.publication_type, null, 'news_item');
            }

            if (item.parameters.hasOwnProperty('author') && item.parameters.author !== null && item.parameters.author !== undefined) {
                item.parameters.author = this.filterParameterValue(item.parameters.author);
            }

            item.parameters.no_interest = true;
            if (item.parameters.hasOwnProperty('actor')) {
                if (item.parameters.actor.hasOwnProperty('photo')) {

                    // for homepage
                    if (options.hasOwnProperty('imgFormat')) {
                        item.settings.actorPhotoFormat = options.imgFormat;
                    }

                    item.parameters.actor.photo = item.parameters.actor.photo.replace('%format%', item.settings.actorPhotoFormat);
                }

                if (item.parameters.actor.hasOwnProperty('interests')) {
                    item.parameters.actor.interests_count = item.parameters.actor.interests.length;
                    if (item.parameters.actor.interests_count > 0) {
                        item.parameters.no_interest = false;
                    }
                }

                if (item.hasOwnProperty('actor_card')) {
                    item.parameters.actor_card = 1;
                    item.parameters.actor_card_contact_url = item.actor_card.contact;
                    item.parameters.actor_card_poke_url = item.actor_card.poke;
                    item.parameters.actor_card_add_friend_url = item.actor_card.add_friend;
                }
            }

            if (item.settings.enableActions === true && item.hasOwnProperty('contribute')) {

                var contributeActions = item.contribute;

                if (contributeActions.hasOwnProperty('watch')) {
                    item.parameters.contribute       = true;
                    item.parameters.contribute_watch = true;
                }

                if (contributeActions.hasOwnProperty('comment')) {
                    item.parameters.contribute         = true;
                    item.parameters.contribute_comment = true;
                }

                if (contributeActions.hasOwnProperty('contact')) {
                    item.parameters.contribute              = true;
                    item.parameters.contribute_contact      = true;
                    item.parameters.contribute_contact_url  = contributeActions.contact.url;
                }

                if (contributeActions.hasOwnProperty('make_proposal')) {
                    item.parameters.contribute              = true;
                    item.parameters.contribute_make_proposal      = true;
                    item.parameters.contribute_make_proposal_url  = contributeActions.make_proposal.url;
                }

                if (contributeActions.hasOwnProperty('link_publication')) {
                    item.parameters.contribute              = true;
                    item.parameters.contribute_link_publication   = true;
                    item.parameters.contribute_link_publication_url  = contributeActions.link_publication.url;
                    
                    item.parameters.contribute_link_label  = __('message_contribute_link_label', {}, 'news_item');
                }

                if (contributeActions.hasOwnProperty('share')
                  && contributeActions.share.hasOwnProperty('url')
                  && contributeActions.share.hasOwnProperty('title')
                  && contributeActions.share.hasOwnProperty('description')) {
                  
                    item.parameters.contribute           = true;
                    item.parameters.contribute_share     = true;
                    item.parameters.contribute_share_url = contributeActions.share.url;
                    item.parameters.contribute_share_title = contributeActions.share.title;
                    item.parameters.contribute_share_description = contributeActions.share.description;
                    item.parameters.contribute_share_video_src = '';
                    item.parameters.contribute_share_video_preview = '';

                    if(contributeActions.share.hasOwnProperty('video_src') && contributeActions.share.hasOwnProperty('video_preview'))
                    {
                      item.parameters.contribute_share_video_src = contributeActions.share.video_src;
                      item.parameters.contribute_share_video_preview = contributeActions.share.video_preview;
                    }
                }
            }

            if (item.hasOwnProperty('contexts')) {

                var contexts = item.contexts;
              
                if (contexts.hasOwnProperty('recommend')) {
                    item.parameters.contexts            = true;
                    item.parameters.contexts_recommend  = true;

                    item.commentMessage = __('label_you_can_type_your_recommendation_here', null, 'news_item');
                    item.showCommentsMessage = 'label_show_%comments_count%_recommendations';
                    item.hideCommentsMessage = 'label_hide_%comments_count%_recommendations';


                }

                if (contexts.hasOwnProperty('building_group')) {
                    item.parameters.contexts                = true;
                    item.parameters.contexts_building_group = true;

                    item.meLikeMessage = 'label_you_congrat';
                    item.meAndOneLikeMessage = 'label_you_and_%watcher%_congrat';
                    item.meAndManyLikeMessage = 'label_you_and_%watchers_count%_congrat';
                    item.oneLikeMessage = 'label_%watcher%_congrats';
                    item.manyLikeMessage = 'label_%watchers_count%_congrat';

                    item.theyLikeTitle = 'title_they_congrat';
                }
            }

            item.prepared = true;
        }

        return item;
    },

    filterParameterValue: function (value) {

        if (value === null) {
            return '';
        }

        var valueIsLink = false,
            label = value,
            translationParameters = {},
            translateLabel;

        // link detection
        if (value.hasOwnProperty('label') && value.hasOwnProperty('url')) {
            valueIsLink = true;
            label = (value.label !== null) ? value.label : '';
        }

        // translation detection
        if (label.hasOwnProperty('translate')) {
            translateLabel = label.translate;
            if (typeof translateLabel === 'object') {
                if (translateLabel.hasOwnProperty('parameters')) {
                    translationParameters = translateLabel.parameters;
                }
                if (translateLabel.hasOwnProperty('label')) {
                    translateLabel = translateLabel.label;
                }
            }

            label = __(translateLabel, translationParameters, 'news_item');
        }

        if (valueIsLink === true) {
            label = '<a href="' + value.url + '">' + label + '</a>';
        }

        return label;
    },

    initCommentToggle: function ($comments, settings, item) {

        var $allComments        = $comments.find('ul li'),
            commentsCount       = $allComments.length,
            $more               = $comments.find('> .more'),
            $moreLink           = $more.find('a'),
            moreExpandText      = '',
            moreCollapseText    = '',
            $list               = $comments.find('ul'),
            $extraComments      = $allComments.filter(':lt(' + (commentsCount - settings.commentsPreviewCount)+ ')'),
            extraCommentsHeight = 0,
            $wrapper            = $comments.find('.comments-wrapper');

        $more.unbind('click').hide();

        if ($wrapper.length === 0) {
            $list.wrap('<div class="comments-wrapper" style="overflow: hidden;"/>');
            $wrapper = $list.parent();
        }

        if (commentsCount > settings.commentsPreviewCount) {

            // compute and set top margin in order to hide all but the last comments
            $extraComments.each(function() {
                extraCommentsHeight += jQuery(this).outerHeight(true);
            });

            // defines comment toggle link text
            moreExpandText = __(item.showCommentsMessage, {
                '%comments_count%': commentsCount
            }, 'news_item');
            moreCollapseText = __(item.hideCommentsMessage, {
                '%comments_count%': commentsCount
            }, 'news_item');

            if (!$list.hasClass('expanded')) {
                $list.css('margin-top', -extraCommentsHeight);
                $moreLink.text(moreExpandText);
            } else {
                $moreLink.text(moreCollapseText);
            }

            // defines comment toggle link actions
            $more.click(function(e) {
                e.preventDefault();
                if ($list.hasClass('expanded')) {
                      $list.removeClass('expanded').animate({ marginTop: -extraCommentsHeight }, 400);
                      $moreLink.text(moreExpandText);
                } else {
                      $list.addClass('expanded').animate({ marginTop: 0 }, 400);
                      $moreLink.text(moreCollapseText);
                }
            }).show();
        }
    },

    /**
     * prepares all news items attached to the renderer
     */
    prepareItems: function (options) {

        var i, item;

        for (i = 0; i < this.items.length; i++) {

            item = this.items[i];
            this.prepareItem(item, options);
        }

        return this;
    },
    
    /**
     * show newsitem comment form when focusing in textarea
     */    
    showCommentForm: function ($textarea, $message) {
        var $wrapper = $textarea.parents('.comment-action');
  
        if ($textarea.val() === $message || $textarea.val() === '') {
            $textarea.val('');
        }
        $textarea.removeClass('disabled').animate({
            height:     60,
            width:      288,
            marginLeft: 50
        }, 300, function() {
            $wrapper.find('button, img, p.connect').fadeIn('fast');
        });
    },
    
    /**
     * hide newsitem comment form when textarea looses focus
     */     
    hideCommentForm: function ($textarea, $message) {
        var $wrapper = $textarea.parents('.comment-action');
  
        if ($textarea.val() === $message || $textarea.val() === '') {
            $wrapper.find('button, img, p.connect').stop().hide();
            $textarea.val($message).addClass('disabled').animate({
                height:     14,
                width:      338,
                marginLeft: 0
            }, 300);
        }
    },
    
    /**
     * Ajax call to post comment
     */
    postComment: function ($form, $settings) {

        var self      = this,
            $li       = jQuery($form).parents('li.news-item'),
            $textarea = $li.find('.comment_text_field'),
            $wrapper  = $li.find('.comment-action'),
            item      = $li.data('item');
      
        $wrapper.append($settings.loader);
        $wrapper.click(function(e) {
            e.preventDefault();
            e.stopPropagation();
        });
      
        $wrapper.find(':not(.loader)').css('opacity', 0.5);
      
        var postData = {
            'class':         item.pc,
            'id':            item.pi,
            'comment[body]': $textarea.val(),
            'format':        'json'
        };
      
        jQuery.ajax({
            url:      $settings.commentUrl,
            type:     'post',
            data:     postData,
            dataType: 'json',
            success:  function(response) {
      
                $wrapper.unbind('click').find('*').css('opacity', 1);
                $wrapper.find('>.loader').remove();
      
                if (response.hasOwnProperty('redirection')) {
                    window.location = response.redirection;
                } else {
                    if (response.success === true) {
      
                        // reset and collapse text area
                        $textarea.val('').trigger('blur');
      
                        // build render
                        $comment = ich['comment-template'](response.comment);
                        // makes it hidden
                        $comment.css('display', 'none');
                        // appends it to the comment container
                        $li.find('.comments ul').append($comment);
      
                        // show, set comment data and trigger loaded event
                        $comment.fadeIn('slow')
                                .data('comment', response.comment)
                                .trigger('comment_loaded', self);
      
                        // adjust visible/hidden comments
                        self.initCommentToggle($li.find('.comments'), $settings, item);
                    } else {
      
                    }
      
                    if (response.hasOwnProperty('notice')) {
                        jQuery.notice.display(response.notice);
                    }
                }
            }
        });
    },

    /**
     * Activates comments for the news item
     *
     * @param  $render  a jquery object representing the news item (li element)
     */
    initCommentForm: function ($render, item, settings) {

        var self                   = this,
            $commentWrapper        = $render.find('.comments'),
            $commentForm           = jQuery('<form method="post"/>'),
            $commentActionWrapper  = jQuery('<div class="comment-action"/>'),
            $commentSubmit         = jQuery('<button type="submit" class="button" style="display: none;"/>'),
            $commentMessageConnect = jQuery('<p class="connect" style="display: none;">' + __('message_connect_to_post_comment', null, 'news_item') + '</p>'),
            $commentTextArea       = jQuery('<textarea name="comment[body]" class="comment_text_field disabled"/>');


        $commentForm.attr('action', settings.commentUrl);

        if ($commentWrapper.length < 1) {
            $render.append('<div class="comments"/>');
            $commentWrapper = $render.find('.comments');
        }

        $commentTextArea.val(item.commentMessage);
        $commentTextArea.focus(function(e) {
            self.showCommentForm($(this), item.commentMessage);
        }).blur(function() {
            self.hideCommentForm($(this), item.commentMessage);
        });

        $commentForm.submit(function(e) {
            e.preventDefault();

            if (item && item.hasOwnProperty('pc') && item.hasOwnProperty('pi')) {
                if (NewsItemRenderer.currentUser !== undefined) {
                    self.postComment(this, settings);
                } else {
                    var loginUrl = (settings.loginRedirectUrl == '/') ? '/login' : '/login?redirect=' + settings.loginRedirectUrl;

                    loginUrl += (loginUrl.indexOf('?') === -1) ? '?' : '&';
                    loginUrl += 'signin_post_action=add_comment';
                    loginUrl += '&class=' + item.pc;
                    loginUrl += '&id=' + item.pi;
                    loginUrl += '&body=' + encodeURIComponent($commentTextArea.val());
                    
                    jQuery.colorbox({
                        href: loginUrl
                    }); 
                }
            }
        });

        $img = jQuery('<img/>').hide();
        if (NewsItemRenderer.currentUser !== undefined) {
            $commentSubmit.text(__('button_validate', null, 'news_item'));
            $commentForm.append($commentTextArea).append($commentSubmit);
            $img.attr('src', NewsItemRenderer.currentUser.photoUrl);
        } else {
            $commentSubmit.text(__('button_connect', null, 'news_item'));
            $commentForm.append($commentTextArea).append($commentMessageConnect).append($commentSubmit);
            $img.attr('src', '/images/common/user_small.jpg');
        }
        $commentActionWrapper.append($img).append($commentForm);
        $commentWrapper.append($commentActionWrapper);

        return this;
    },


    /**
     * Renders current news items in given container
     *
     * @param  items       an array containing all the news items
     * @param  $container  jQuery object
     */
    renderIn: function (items, $container, options) {

        this.items          = items;
        this.render_options = options;

        this.prepareItems(options);

        var itemsCount = this.items.length;

        if (itemsCount > 0) {
            if ($container.find(' > ul.feed').length == 0){
              // creates the ul if it does'nt exist
              var ulTag = $container.hasClass('homepage_activity_feed') ? '<ul class="feed homepage_feed"/>'
                                                                        : '<ul class="feed"/>';
              $container.append(ulTag);
            }
            $ul = $container.find(' > ul.feed:last');

            // render each news item with its template and append it to the ul
            for (var i = 0; i < itemsCount; i++) {
                this.renderOneIn(this.items[i], $ul, options);
            }
        }

        return this;
    },
    
    /**
     * Render link see more button + loader in given container
     */
    renderSeeMoreLink: function ($container, catalog) {
        var a_tag = '<a href="#" class="more button large"><span>' +  __('label_see_more', null, catalog) + '</span></a>';
        
        $container.append(a_tag);
        $container.append('<div class="loader horizontal" style="display: none;"></div>');
    },
    
    /**
     * hide link 'see more' and show loader
     */
    hideSeeMoreLink: function () {
        var $more   = jQuery('a.more', '#content').filter('.button');
        $more.hide().next('div.loader').show();
    },
    
    /**
     * remove link 'see more' and loader
     */    
    removeSeeMoreLinkAndLoader: function () {
      var $more   = jQuery('a.more', '#content').filter('.button'),
          $loader = $more.next('div.loader');
      
      $loader.remove();
      $more.remove();
    },
    
    /**
     * Add or refresh the page parameter value in the params used in the call jsonRPC 
     */
    refreshPaginationParam: function (params) {

        params = params || [];

        if (this.next_page == undefined) {
            this.next_page = 2;
        }
        
        if (this.method_pos_param_page == undefined) {
            if (this.render_options.posParamPage != undefined) {
                // position of the page parameter in the method called by jsonRPC has been passed in the option 
                this.method_pos_param_page = (this.render_options.posParamPage-1);
            } else {
                // The page parameter is the last parameter in the methods called by jsonRPC
                this.method_pos_param_page = params.length;
            }
        }
        
        if (params[this.method_pos_param_page] == undefined) {
            for (var i = 0; i < (this.method_pos_param_page); i++) {
                if (params[i] == undefined) {
                    params.push(null);
                }
            }
            params.push(this.next_page);
        } else {
            params[this.method_pos_param_page] = this.next_page;
        }
        this.next_page = this.next_page + 1;
        return params;
    },
    
    /**
     * render and handle see more button in given container
     *
     * @param  items       an array containing all the news items
     * @param  $container  jQuery object
     */    
    renderPagination: function (paginate, $container, options) {
        if (paginate && options.hasOwnProperty('method') && options.hasOwnProperty('params')) {
            var catalog  = (options.hasOwnProperty('catalog')) ? options.catalog : 'news_item',
                renderer = this;
            
            this.renderSeeMoreLink($container, catalog);
            
            // action on click see more
            jQuery('a.more', '#content').filter('.button').click(function(event) {
                event.preventDefault();

                renderer.hideSeeMoreLink();
                
                options.params = renderer.refreshPaginationParam(options.params);
                
                jQuery.jsonRPC.request(options.method, {
                    params: options.params,
                    success: function(result) {
                        renderer.removeSeeMoreLinkAndLoader();

                        if (result.result.hasOwnProperty('items') && result.result.items.length) {
                            renderer.renderIn(result.result.items, $container, renderer.render_options);
                            if (result.result.hasOwnProperty('paginate')) {
                                renderer.renderPagination(result.result.paginate, $container, {
                                    catalog: catalog,
                                    method:  options.method,
                                    params:  options.params
                                });
                            }
                        }
                    }
                }); 
            });
        }
    },

    /**
     * Renders a news item and appends it to the given ul
     *
     * @param  item  an object containing news item data
     * @param  $ul   the ul container jQuery object
     */
    renderOneIn: function (item, $ul, options) {

        item.settings = jQuery.extend(NewsItemRenderer.defaults, options || {});

        var self = this;

        // ensures $ul is an ul element
        if (!$ul.is('ul')) {

            throw 'Container has to be an "ul" element';
        }

        // checks if item has already been prepared
        if (!item.hasOwnProperty('prepared') || item.prepared !== true) {
            this.prepareItem(item, options);
        }

        // checks template name, fallback defined in options
        if (!item.hasOwnProperty('template') || item.template === '' || item.template === undefined) {
            item.template = item.settings.fallbackTemplate;
        }

        if (item.hasOwnProperty('action_id')) {
            item.parameters.actionId = item.action_id;
        }

        // checks template exists
        if (!ich.templates.hasOwnProperty(item.template + '-template')) {

            throw 'News item template "' + item.template + '" not found';

        } else {

            var $render = ich[item.template + '-template'](item.parameters);
            $render.data('item', item)
                   .bind('comment_loaded', self.onCommentLoaded)
                   .bind('watch_response', self.onWatchResponse);

            $ul.append($render);

            if (item.settings.enableActions === true) {

                // focus on comment text area when clicking on comment link
                $render.find('ul.smart_actions .comment a').click(function(e) {
                    e.preventDefault();
                    jQuery(this).parents('.news-item').find('textarea').focus();
                });

                self.initWatchers($render);
            }

            if (item.settings.enableActions === true && item.settings.displayComments === true) {
                if (item.hasOwnProperty('contribute') && item.contribute.hasOwnProperty('comment')) {

                    var $comments = $render.find('.comments');

                    $comments.find('> ul > li').each(function(i) {
                        jQuery(this).data('comment', item.parameters.comments[i])
                                    .trigger('comment_loaded', self);
                    });

                    this.initCommentToggle($comments, item.settings, item);
                    this.initCommentForm($render, item, item.settings);

                    // See more / see less on comments
                    $render.find('.comments-wrapper > ul > li').each(function(e)
                    {
                        var $textPreview = jQuery(this).find('p span.body');
                        var textPreviewText = $textPreview.html(),
                            textPreviewTextlength = textPreviewText.length;

                        // Size +1 to prevent a rare bug when the preview part is nearly the same size of the required preview length
                        if (textPreviewTextlength > item.settings.previewLength +1) {
                            var splitAt = textPreviewText.indexOf(' ', item.settings.previewLength);

                            // We may not have found a space in after the required preview length (middle of the last word)
                            if (splitAt === -1) {
                                splitAt = item.settings.previewLength;
                            }

                            var textPart1     = textPreviewText.substring(0, splitAt),
                                textPart2     = textPreviewText.substring(splitAt, textPreviewTextlength - 1),
                                $moreText     = jQuery('<span/>').text(textPart2).hide(),
                                $showMoreLink = jQuery('<a/>').text(__(item.settings.textSeeMore, null, 'news_item')).attr('href', '#');

                            $textPreview.text(textPart1)
                                        .append($moreText)
                                        .append('&nbsp;')
                                        .append($showMoreLink);

                            $showMoreLink.click(function(e) {
                                e.preventDefault();
                                if ($moreText.css('display') !== 'none') {
                                    $moreText.css('display', 'none');
                                    $showMoreLink.text(__(item.settings.textSeeMore, null, 'news_item'));
                                } else {
                                    $moreText.css('display', 'inline');
                                    $showMoreLink.text(__(item.settings.textSeeLess, null, 'news_item'));
                                }
                            });
                        }
                    });
                }
            }

            // truncate text preview if necessary
            var $textPreview = $render.find('.text.preview p');
            if ($textPreview.length === 1) {
                var textPreviewText = $textPreview.text(),
                    textPreviewTextlength = textPreviewText.length;

                // Size +1 to prevent a rare bug when the preview part is nearly the same size of the required preview length
                if (textPreviewTextlength > item.settings.previewLength +1) {
                    var splitAt       = textPreviewText.indexOf(' ', item.settings.previewLength);
                  
                    // We may not have found a space in after the required preview length (middle of the last word)
                    if(splitAt == -1)
                    {
                      splitAt = item.settings.previewLength;
                    }
                  
                    var textPart1     = textPreviewText.substring(0, splitAt),
                        textPart2     = textPreviewText.substring(splitAt, textPreviewTextlength - 1),
                        $moreText     = jQuery('<span/>').text(textPart2).hide(),
                        $showMoreLink = jQuery('<a/>').addClass('more').text(__(item.settings.textSeeMore, null, 'news_item'));
                    
                    $textPreview.text(textPart1).append($moreText).append('&nbsp;').append($showMoreLink);

                    $showMoreLink.click(function(e) {
                        e.preventDefault();
                        if ($moreText.css('display') !== 'none') {
                            $moreText.css('display', 'none');
                            $showMoreLink.text(__(item.settings.textSeeMore, null, 'news_item'));
                        } else {
                            $moreText.css('display', 'inline');
                            $showMoreLink.text(__(item.settings.textSeeLess, null, 'news_item'));
                        }
                    });
                }
            }

            this.displayMemberships($render);

            this.initMatchingServices($render);

            // tooltip on images
            // deactivated due to conflicts with member cards
//            $render.find('a.profile img.user').each(function() {
//                var $img = jQuery(this);
//                mr.tips.add($img, $img.attr('alt'));
//            });

            // member card
            jQuery('.member_card').each(function(e) {
            
              var memberCard = jQuery(this);
              var timeout = null;
              var hideCard = function() {
                memberCard.fadeOut(200).removeClass('open');
              };
                  
              memberCard.closest('li').find('a.profile').mouseenter(function()
              {
                if(!memberCard.hasClass('open'))
                {
                  memberCard.fadeOut(200).removeClass('open');
                  memberCard.delay(250).addClass('open').show();
                }
              }).mouseleave(function()
              {
                timeout = setTimeout(hideCard, 1000);
              });
              memberCard.mouseenter(function()
              {
                clearTimeout(timeout);
              }).mouseleave(function()
              {
                if(memberCard.hasClass('open'))
                {
                  timeout = setTimeout(hideCard, 500);
                }
              });
            });

        }
        

        $render.find('ul.smart_actions li.link a').hover(function() {
          jQuery(this).siblings('span').stop(true, true).fadeIn(200);
        }, function() {
          jQuery(this).siblings('span').fadeOut(200);
        });
        

        $render.find('span.confidentiality.small').hover(function() {
          jQuery(this).find('span').stop(true, true).fadeIn(200);
        }, function() {
          jQuery(this).find('span').fadeOut(200);
        });

        return this;
    },
    
    /**
     * Used to call the watch/unwatch action when not logged
     */    
    secureWatchAction: function($action, $class, $id, $redirection) {
        var loginUrl = ($redirection == '/') ? '/login' : '/login?redirect=' + $redirection;
            loginUrl = loginUrl + '&signin_post_action=' + $action;
            loginUrl = loginUrl + '&object=' + $class;
            loginUrl = loginUrl + '&id=' + $id;
        
        jQuery.colorbox({
            href: loginUrl
        }); 
    },

    /**
     * Conditional display of who watch the news item subject
     *
     * 1 - no watchers       = no link/text
     * 2 - only me           = "you like"
     * 3 - one not me        = "xxx likes"
     * 4 - more than one     = "n people like"
     * 5 - you and one other = "you and xxx like"
     * 6 - you and others    = "you and n like"
     *
     * @param $element  a jQuery object
     */
    initWatchers: function($element) {

        var $newsItem             = this.getParentNewsItem($element),
            newsItemData          = $newsItem.data('item'),
            settings              = newsItemData.settings,
            self                  = this,
            watchers              = [],
            watchersOthers        = [],
            watchersCount         = 0,
            watchersText          = '',
            currentUserIsWatching = false,
            hasPopin              = false,
            postData              = {},
            commentData,
            $watchLink,
            $unwatchLink,
            $textContainer;

        if ($element.is('.news-item')) {
            $watchLink         = $element.find('.smart_actions .watch');
            $unwatchLink       = $element.find('.smart_actions .unwatch');
            $textContainer     = $element.find('> .liked');
            postData['object'] = newsItemData.pc;
            postData['id']     = newsItemData.pi;

            if (newsItemData.hasOwnProperty('watchers')) {
                watchers = newsItemData.watchers;
            }
        } else if ($element.is('.news-item-comment')) {
            $watchLink         = $element.find('.watch');
            $unwatchLink       = $element.find('.unwatch');
            $textContainer     = $element.find('.liked');
            commentData        = $element.data('comment');
            postData['object'] = commentData['class'];
            postData['id']     = commentData['id'];

            if (commentData.hasOwnProperty('watchers')) {
                watchers = commentData.watchers;
            }
        } else {
            return this;
        }

        watchersCount = watchers.length;

        $watchLink.add($unwatchLink).unbind('click').hide();
        $textContainer.html('').hide();

        if (NewsItemRenderer.currentUser !== undefined) {
            for (var i = 0; i < watchersCount; i++) {
                if (NewsItemRenderer.currentUser.id == watchers[i].id) {
                    currentUserIsWatching = true;
                } else {
                    watchersOthers.push(watchers[i]);
                }
            }
        } else {
            watchersOthers = watchers;
        }

        if (watchersCount > 0) {
            if (watchersCount === 1) {
                if (currentUserIsWatching === true) {
                    watchersText = __(newsItemData.meLikeMessage, null, 'news_item');
                } else {
                    watchersText = __(newsItemData.oneLikeMessage, {
                      '%watcher%': '<a href="' + watchers[0].url + '">' + watchers[0].fullname + '</a>'
                    }, 'news_item');
                }
            } else if (watchersCount === 2 && currentUserIsWatching === true) {
                watchersText = __(newsItemData.meAndOneLikeMessage, {
                  '%watcher%': '<a href="' + watchersOthers[0].url + '">' + watchersOthers[0].fullname + '</a>'
                }, 'news_item');
            } else if (watchersCount > 2 && currentUserIsWatching === true) {
                watchersText = __(newsItemData.meAndManyLikeMessage, {
                  '%watchers_count%': '<a href="#">' + __('%count%_watchers', {'%count%': watchersCount - 1}, 'news_item') + '</a>'
                }, 'news_item');
                hasPopin = true;
            } else {
                watchersText = __(newsItemData.manyLikeMessage, {
                  '%watchers_count%': '<a href="#">' + __('%count%_watchers', {'%count%': watchersCount}, 'news_item') + '</a>'
                }, 'news_item');
                hasPopin = true;
            }
        }

        if (currentUserIsWatching === true) {
            $unwatchLink.show().click(function(e) {
                e.preventDefault();
                
                if (NewsItemRenderer.currentUser !== undefined) {
                    $unwatchLink.css('opacity', 0.35);
                  
                    jQuery.post(settings.unwatchUrl, postData, function(response) {
                        $unwatchLink.css('opacity', 1);
                        $element.trigger('watch_response', [self, response]);
                    }, 'json');
                } else {
                    self.secureWatchAction('unwatch', postData['object'], postData['id'], settings.loginRedirectUrl);
                }
            });
        } else {
            $watchLink.show().click(function(e) {
                e.preventDefault();
                
                if (NewsItemRenderer.currentUser !== undefined) {
                    $watchLink.css('opacity', 0.35);
                    
                    jQuery.post(settings.watchUrl, postData, function(response) {
                        $watchLink.css('opacity', 1);
                        $element.trigger('watch_response', [self, response]);
                    }, 'json');
                } else {
                    self.secureWatchAction('watch', postData['object'], postData['id'], settings.loginRedirectUrl);
              }  
            });
        }

        if (watchersText !== '') {
            $textContainer.html(watchersText).show();
          
            if (hasPopin === true) {

                for (var wo = 0; wo < watchersOthers.length; wo++) {
                    watchersOthers[wo].photo_url = watchersOthers[wo].photo_url.replace('%format%', settings.watcherPhotoFormat);
                }

                $textContainer.find('a').click(function(e) {
                    e.preventDefault();
                    jQuery.colorbox({
                        html: ich['users-template']({
                            title: __(newsItemData.theyLikeTitle, null, 'news_item'),
                            users: watchersOthers
                        })
                    });
                });
            }
        }
    },

    initMatchingServices: function($element) {

        var $newsItem    = this.getParentNewsItem($element),
            newsItemData = $newsItem.data('item');

        if (!newsItemData.parameters.hasOwnProperty('matchingServicesCount')
         || (newsItemData.parameters.matchingServicesCount <= newsItemData.settings.matchingServicesPreviewCount)) {
            return;
        }

        $element.find('ul.matching-services-users').find('a.more').click(function(e) {
            e.preventDefault();

            jQuery.colorbox({
                html: ich['users-template']({
                    title: __('title_matching_user_services', null, 'news_item'),
                    users: newsItemData.parameters.matching_services
                }).html()
            });
        });
    },

    /**
     * Returns the news item element from a child dom element
     *
     * @param $element
     */
    getParentNewsItem: function($element) {

        return $element.is('.news-item') ? $element : $element.parents('.news-item');
    },

    /**
     * Conditional display of those who are members of the news item subject
     *  => users who participate to an event
     *
     * 1 - no memberships       = no link/text
     * 2 - one membership       = one attendee
     * 3 - several memberships  = "xxx attendees"
     *
     * @param $newsItem  a jQuery object containing the news item DOM element
     */
    displayMemberships: function($newsItem) {
        $newsItem.find('.attendees').text('').hide();

        var item           = $newsItem.data('item'),
            membershipText = '';

        if (item) {
            if (item.hasOwnProperty('parameters')) {
                if (item.parameters.hasOwnProperty('memberships')) {
                    var memberships      = item.parameters.memberships,
                        membershipsCount = memberships.length;

                    if (membershipsCount > 0) {
                        if (membershipsCount === 1) {
                            membershipText = __('label_one_attendee', null, 'news_item');
                        } else {
                            membershipText = __('label_%count%_attendees', {
                              '%count%': membershipsCount
                            }, 'news_item');
                        }
                    }
                }
            }
        }

        if (membershipText !== '') {
            membershipText = '<a href="#">' + membershipText + '</a>';
            $newsItem.find('.attendees').html(membershipText).show();

            $newsItem.find('.attendees a').click(function(e) {
                e.preventDefault();

                jQuery.colorbox({
                    html: ich['users-template']({
                        title: __('title_they_participate', null, 'news_item'),
                        users: memberships
                    }).html()
                });
            });
        }
    },

    /**
     * Called when a watch response is received
     * 
     * @param e        a jQuery event
     * @param response the json response
     */
    onWatchResponse: function(e, self, response) {

        var $element = jQuery(e.target);

        if (response.hasOwnProperty('message') && response.message  !== '') {
            jQuery.notice.display(response.message);
        }

        if (response.hasOwnProperty('watchers')) {
            var data;
            if ($element.is('.news-item-comment')) {
                data = $element.data('comment');
                data.watchers = response.watchers;
                $element.data('comment', data);
            } else if ($element.is('.news-item')) {
                data = $element.data('item');
                data.watchers = response.watchers;
                $element.data('item', data);
            }

            self.initWatchers($element);
        }
    },

    /**
     * Called when a comment is loaded/rendered.
     *
     * @param e
     */
    onCommentLoaded: function(e, self) {

        var $comment = jQuery(e.target);

        self.initWatchers($comment);
    }
};

/**
 * Generic function to log json rpc errors.
 *
 * @param error
 */
function logRpcError(error){
  console.log(error);
}
