CiviWiki/OpenCiviWiki

View on GitHub
project/core/templates/static/js/thread.js

Summary

Maintainability
F
1 mo
Test Coverage
cw = cw || {};

cw.DEFAULTS = {
    types: ['problem', 'cause', 'solution'],
    types_plural: ['problems', 'causes', 'solutions'],
    civiViewStates: ['recommended', 'other'],
    viewLimit: 5,
};

cw.CiviModel = BB.Model.extend({
    defaults: function(){
        return {
            id: '', //id from server. Client id is under cid
            thread_id: '',
            type: "Civi Type",
            title: "Civi Title",
            body: "Civi Body",
            author: {
                "username": "None",
                "profile_image": "/media/profile/default.png",
                "first_name": "None",
                "last_name": "None",
            },
            votes: {
                "votes_vneg": 0,
                "votes_neg": 0,
                "votes_neutral": 0,
                "votes_pos": 0,
                "votes_vpos": 0
            },
            attachments: [],
            hashtags: [],
            created: "No Date"
        };
    },

    url: function () {
        if ( !this.id ) {
            throw new Error("This is a race condition! and why we can't have nice things :(");
        }
        return '/api/civi_data/' + this.id;
    },

    initialize: function (model, options) {
        options = options || {};
    },

    save: function(attrs, options) { // TODO: Remove this after implementing django REST framework
        options = options || {};

        options.type = 'POST';
        if (!options.url) {
            options.url = '/api/editcivi/' + this.id + '/';
        }
        return Backbone.Model.prototype.save.call(this, attrs, options);
    },

    //TODO: validate: function()

    vote: function (vote_type){

    },
});

cw.CiviCollection = BB.Collection.extend({
    model: cw.CiviModel,

    url: function () {
        if (! this.threadId ) {
            throw new Error("This is a race condition! and why we can't have nice things :(");
        }
        return '/api/threads/' + this.threadId + '/civis';
    },

    initialize: function (model, options) {
        options = options || {};
        this.threadId = options.threadId;
    },

    filterByOptions: function (options) {
        var type = options.type || 'problem';
        var viewState = options.viewState || 'default';
        var limit = options.limit || civi.length;

        var filtered = this.models.filter(function (civi) {
            return civi.get("type") === type && viewState === civi.viewState;
        });

        if (limit < civi.length && limit > 1){
            filtered = filtered.split(limit);
        }
        return filtered;
    },

    filterByType: function (type) {
        var filtered = this.models.filter(function (civi) {
            return civi.get("type") === type;
        });
        return filtered;
    },

    filterByIds: function (arrayIds) {
        var filtered = this.models.filter(function (civi) {
            return (_.indexOf(arrayIds, civi.id) > -1);
        });
        return filtered;
    },

    filterRecByType: function (type, recommended) {
        var filtered = this.models.filter(function (civi) {
            if (!recommended) {
                return (civi.get("type") === type) && civi.otherRecommended;
            }
            return (civi.get("type") === type) && (civi.recommended == recommended);
        });
        return filtered;
    },

    filterByRec: function (recommended) {
        var filtered = this.models.filter(function (civi) {
            if (!recommended) {
                return civi.otherRecommended;
            } else return (civi.recommended == recommended);

        });
        return filtered;
    },
});

cw.ResponseCollection = BB.Collection.extend({
    model: cw.CiviModel,

    url: function () {
        if (! this.threadId ) {
            throw new Error("This is a race condition! and why we can't have nice things :(");
        }
        return '/api/response_data/' + this.threadId + '/' + this.civiId + '/';
    },

    initialize: function (model, options) {
        this.threadId = options.threadId;
        this.civiId = null;
    }
});

cw.ThreadModel = BB.Model.extend({
    url: function () {
        if (! this.threadId ) {
            throw new Error("This is a race condition! and why we can't have nice things :(");
        }
        return '/api/thread_data/' + this.threadId + '/';
    },

    parse: function(data){
        return data;
    },

    initialize: function (model, options) {
        this.threadId = options.threadId;
    }
});

cw.CiviView =  BB.View.extend({
    template: _.template($('#civi-template').html()),

    initialize: function (options) {
        this.options = options || {};
        this.can_edit = options.can_edit;
        this.is_draft = options.is_draft;
        this.can_respond = options.can_respond;
        this.parentView = options.parentView;
        this.civis = this.parentView.civis;
        this.model.set('view', this);
        this.render();
    },

    render: function () {
        this.$el.empty().append(this.template());
    },

    events: {
        'click .rating-button': 'clickRating',
        'click .edit': 'clickEdit',
        'click .delete': 'deleteEdit',
        'click .edit-confirm': 'saveEdit',
        'click .edit-cancel': 'closeEdit',
        'click .civi-image-thumb': 'viewImageModal',
        'change #civi-type-form': 'clickNewType',
        'click .delete-civi-image': 'addImageToDeleteList',
        'click #add-more-images': 'showImageForm',
        'change .attachment-image-pick': 'previewImageNames',
        'input .civi-link-images': 'previewImageNames',
        'click #add-image-link-input': 'addImageLinkInput',
        'click #respond-button': 'addRebuttal',
    },

    addImageLinkInput: function(){
        var link_images = this.$('.civi-link-images').length;
        if (link_images > 20 ) {
            Materialize.toast("Don't think you need any more...", 5000);
        } else {
            this.$('.image-link-list').append('<input type="text" class="civi-link-images" placeholder="Paste your image link here..."/>');
        }

    },

    previewImageNames: function(e) {
        var attachment_input = this.$('#id_attachment_image');
        var uploaded_images = attachment_input[0].files;
        var $previewlist = this.$('.file-preview');
        $previewlist.empty();
        // File Upload Images
        _.each(uploaded_images, function(img_file){
            $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+img_file.name+"</div>");
        }, this);

        // Link Images
        this.attachment_links = [];
        var link_images = $('.civi-link-images');
        _.each(link_images, function(img_link){
            var link_value = img_link.value.trim();
            if (link_value){
                $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+link_value+"</div>");
                this.attachment_links.push(link_value);
            }
        }, this);

        // Total images count
        var image_total = uploaded_images.length + this.attachment_links.length;
        if (image_total === 0) {
            $previewlist.prepend("<div>No Images</div>");
        } else if (image_total === 1) {
            $previewlist.prepend("<div>1 Image</div>");
        } else {
            $previewlist.prepend("<div>" + image_total + " Images</div>");
        }

        this.attachmentCount = image_total;

    },

    showImageForm: function(e){
        // HEREHERE
        this.$('.edit-images').removeClass('hide');
        this.$('#add-more-images').addClass('hide');
    },

    viewImageModal: function(e){
        var img_src = $(e.currentTarget).attr('src');
        var $modal = $('#civi-image-modal');
        $('#civi-image-big').attr('src', img_src);
        $modal.openModal();
        e.stopPropagation();
    },
    clickFavorite: function (e) {
        var _this = this;

        if ($this.text() === 'star_border') {
            $.ajax({
                url: '/api/favorite_civi/',
                type: 'POST',
                data: {
                    civi_id: this.model.id,
                },
                success: function (response) {
                    Materialize.toast('Favorited Civi', 5000);
                    $this.text('star');
                },
                error: function(r){
                    Materialize.toast('Could not favor the civi', 5000);
                }
            });

        } else {
            $.ajax({
                url: '/api/favorite_civi/',
                type: 'POST',
                data: {
                    civi_id: this.model.id,
                },
                success: function (response) {
                    Materialize.toast('Favorited Civi', 5000);
                },
                error: function(r){
                    Materialize.toast('Could not favor the civi', 5000);
                }
            });
            $this.text('star_border');
        }
    },

    grabLink: function () {
        e.stopPropagation();
        Materialize.toast('Civi link copied to clipboard.', 1500);
    },

    clickRating: function (e) {
        e.stopPropagation();
        var _this = this;
        var $this = $(e.currentTarget);

        var rating = $this.data('rating');
        var civi_id = $(e.currentTarget).closest('.civi-card').data('civi-id');

        if (rating && civi_id){
            $.ajax({
                url: '/api/rate_civi/',
                type: 'POST',
                data: {
                    civi_id: civi_id,
                    rating: rating
                },
                success: function (response) {
                    Materialize.toast('Voted!', 5000);

                    var prev_votes = _this.parentView.model.get('user_votes');
                    var prev_vote = _.findWhere(prev_votes, {civi_id: civi_id});
                    if (!prev_vote) {
                        prev_votes.push(response.data);
                        _this.parentView.model.set('user_votes', prev_votes);
                    } else {
                        prev_votes = _.reject(prev_votes, function(v) {return v.civi_id === civi_id;});
                        prev_votes.push(response.data);
                        _this.parentView.model.set('user_votes', prev_votes);
                    }

                    if (_this.model.get('type') != "response" && _this.model.get('type') != "rebuttal") {
                        _this.parentView.initRecommended(); //THISTHIS
                        _this.parentView.renderBodyContents();
                        _this.parentView.processCiviScroll();
                    }

                    _this.$('.rating-button').removeClass('current');
                    $this.addClass('current');


                },
                error: function(r){
                    Materialize.toast('Could not vote :(', 5000);
                }
            });
        }
    },

    clickEdit: function (e) {
        e.stopPropagation();
        // Populate with data TODO: move to a template
        this.$('.edit-civi-body').text(this.model.get('body'));
        this.$('.edit-civi-title').val(this.model.get('title'));
        this.$('#'+ this.model.get('type') + "-" + this.model.id).prop("checked", true);
        if (this.model.get('type') != 'response' && this.model.get('type') != 'rebuttal') {
            this.magicSuggestView = new cw.LinkSelectView({$el: this.$('#magicsuggest-'+this.model.id), civis: this.civis});
            this.magicSuggestView.setLinkableData(this.model.get('type'));
            this.magicSuggestView.ms.setValue(this.model.get('links'));
        }
        this.attachment_links = [];
        this.attachmentCount = 0;

        this.imageRemoveList = [];

        this.$('.edit-wrapper').removeClass('hide');
        this.$('.edit-action').removeClass('hide');
        this.$('.text-wrapper').addClass('hide');
        this.$('.edit').addClass('hide');
        this.$('.delete').addClass('hide');

        if (this.model.get('type') === 'response' || this.model.get('type') === 'rebuttal') {
            this.$('.edit-links').addClass('hide');
            this.$('#civi-type-form').addClass('hide');
        } else if (this.model.get('type') === 'problem') {
            this.$('#magicsuggest-'+this.model.id).addClass('hide');
        }
    },

    clickNewType: function(e){
        var new_type = $(e.target).closest("input[type='radio']:checked").val();
        // var new_type = $("#civi-type-form input[type='radio']:checked").val();
        if (new_type === "problem") {
            this.$('.edit-links').addClass('hide');
            this.$('#magicsuggest-'+this.model.id).addClass('hide');
        } else {
            this.$('.edit-links').removeClass('hide');
            this.$('#magicsuggest-'+this.model.id).removeClass('hide');
        }
        this.magicSuggestView.setLinkableData(new_type);
        this.magicSuggestView.ms.clear();
    },

    addRebuttal: function(){
        this.parentView.newResponseView.rebuttal_ref = this.model.id;
        this.parentView.newResponseView.render();
        this.parentView.newResponseView.show();
    },


    addImageToDeleteList: function(e) {
        var target = $(e.currentTarget);
        var target_image = target.data('image-id');

        this.imageRemoveList.push(target_image);
        target.remove();
    },

    closeEdit: function (e) {
        e.stopPropagation();
        this.$('.edit-wrapper').addClass('hide');
        this.$('.edit-action').addClass('hide');
        this.$('.text-wrapper').removeClass('hide');
        this.$('.edit').removeClass('hide');
        this.$('.delete').removeClass('hide');
    },

    saveEdit: function(e) {
        e.stopPropagation();
        var _this = this;
        var c_type = this.model.get('type');
        var new_body = this.$('.edit-civi-body').val().trim();
            new_title = this.$('.edit-civi-title').val().trim();
        var links;
        if (c_type != 'response' && c_type != 'rebuttal') {
            links= this.magicSuggestView.ms.getValue();
        } else {
            links = [];
        }

        var new_type = this.$("#civi-type-form input[type='radio']:checked").val();

        if (!new_body || !new_title){
            Materialize.toast('Please do not leave fields blank', 5000);
            return;
        } else if (this.imageRemoveList.length===0 && this.attachmentCount===0 && (new_body == this.model.get('body') && new_title == this.model.get('title') && _.isEqual(links, this.model.get('links')) && new_type == this.model.get('type') )){
            this.closeEdit(e);
            return;
        } else {
            var data;


            if (c_type === 'response' || c_type === 'rebuttal') {
                new_type = c_type;
                data = {
                    civi_id: this.model.id,
                    title: new_title,
                    body: new_body
                };
            } else {
                data = {
                    civi_id: this.model.id,
                    title: new_title,
                    body: new_body,
                    links: links,
                    type: new_type
                };
            }

            if (this.imageRemoveList.length){
                data.image_remove_list = this.imageRemoveList;
            }

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    }
                }
            });

            $.ajax({
                url: '/api/edit_civi/',
                type: 'POST',
                data: data,
                success: function (response) {
                    _this.closeEdit(e);
                    // var score = $this.find('.rate-value');
                    // var new_vote = parseInt(score.text())+ 1;
                    // score.text(new_vote);
                    //
                    var attachment_input = _this.$('#id_attachment_image');
                    var uploaded_images = attachment_input[0].files;
                    if (_this.attachmentCount > 0) {
                        var formData = new FormData(_this.$('#attachment_image_form')[0]);
                        formData.set('civi_id', _this.model.id);
                        if (_this.attachment_links.length){
                            _.each(_this.attachment_links, function(img_link){
                                formData.append('attachment_links[]', img_link);
                            });
                        }

                        $.ajaxSetup({
                            beforeSend: function(xhr, settings) {
                                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                                }
                            }
                        });

                        $.ajax({
                            url: '/api/upload_images/',
                            type: 'POST',
                            success: function (response2) {

                                Materialize.toast('Saved.', 5000);

                                // Set the models with new data and rerender
                                _this.model.set('title', new_title);
                                _this.model.set('body', new_body);
                                _this.model.set('links', links);
                                _this.model.set('type', new_type);
                                _this.model.set('attachments', response2.attachments);
                                _this.model.set('score', response.score);
                                if (_this.magicSuggestView){
                                    _this.magicSuggestView.remove();
                                }

                                _this.render();

                                if (_this.model.get('type') != "response" && _this.model.get('type') != "rebuttal") {
                                    var parent_links = _this.model.get('links');
                                    _.each(parent_links, function(parent_id){
                                        var parent_civi = _this.options.parentView.civis.get(parent_id);
                                        if (parent_civi) {
                                            var prev_links = parent_civi.get('links');
                                            prev_links.push(_this.model.id);
                                            parent_civi.set('links', prev_links);
                                        }

                                    }, this);

                                    _this.parentView.initRecommended(); //THISTHIS
                                    _this.parentView.renderBodyContents();
                                    _this.parentView.processCiviScroll();
                                }
                            },
                            error: function(e){
                                Materialize.toast('Civi was edited but one or more images could not be uploaded', 5000);

                                // Set the models with new data and rerender
                                _this.model.set('title', new_title);
                                _this.model.set('body', new_body);
                                _this.model.set('links', links);
                                _this.model.set('type', new_type);
                                _this.model.set('attachments', response2.attachments);
                                _this.model.set('score', response.score);
                                if (_this.magicSuggestView){
                                    _this.magicSuggestView.remove();
                                }

                                _this.render();

                                if (_this.model.get('type') != "response" && _this.model.get('type') != "rebuttal") {
                                    var parent_links = _this.model.get('links');
                                    _.each(parent_links, function(parent_id){
                                        var parent_civi = _this.options.parentView.civis.get(parent_id);
                                        if (parent_civi) {
                                            var prev_links = parent_civi.get('links');
                                            prev_links.push(_this.model.id);
                                            parent_civi.set('links', prev_links);
                                        }

                                    }, this);

                                    _this.parentView.initRecommended(); //THISTHIS
                                    _this.parentView.renderBodyContents();
                                    _this.parentView.processCiviScroll();
                                }
                            },
                            data: formData,
                            cache: false,
                            contentType: false,
                            processData: false
                        });

                    } else {
                        Materialize.toast('Saved', 5000);

                        // Clean up previous links
                        if (_this.model.get('type') != "response" && _this.model.get('type') != "rebuttal") {
                            var orig_links = _this.model.get('links');
                            _.each(orig_links, function(parent_id){
                                var parent_civi = _this.options.parentView.civis.get(parent_id);
                                if (parent_civi) {
                                    var prev_links = parent_civi.get('links');
                                    var cleaned = _.without(prev_links, _this.model.id);
                                    parent_civi.set('links', cleaned);
                                }
                            }, this);
                        }
                        // Set the models with new data and rerender
                        _this.model.set('title', new_title);
                        _this.model.set('body', new_body);
                        _this.model.set('links', links);
                        _this.model.set('type', new_type);
                        _this.model.set('attachments', response.attachments);
                        _this.model.set('score', response.score);
                        if (_this.magicSuggestView){
                            _this.magicSuggestView.remove();
                        }

                        _this.render();

                        if (_this.model.get('type') != "response" && _this.model.get('type') != "rebuttal") {
                            var parent_links = _this.model.get('links');
                            _.each(parent_links, function(parent_id){
                                var parent_civi = _this.options.parentView.civis.get(parent_id);
                                if (parent_civi) {
                                    var prev_links = parent_civi.get('links');
                                    prev_links.push(_this.model.id);
                                    parent_civi.set('links', prev_links);
                                }
                            }, this);

                            _this.parentView.initRecommended(); //THISTHIS
                            _this.parentView.renderBodyContents();
                            _this.parentView.processCiviScroll();
                        }
                    }


                },
                error: function(r){
                    Materialize.toast('Could not edit the civi', 5000);
                    _this.closeEdit(e);
                    _this.render();

                }
            });
        }
    },

    deleteEdit: function(e) {
        var _this = this;
        e.stopPropagation();

        $.ajax({
            url: '/api/delete_civi/',
            type: 'POST',
            data: {
                csrfmiddlewaretoken: csrftoken,
                civi_id: this.model.id,
            },
            success: function (response) {
                Materialize.toast('Deleted Civi succssfully', 5000);
                
                _.each(_this.model.get('links'), function(link){
                    var linked_civi = _this.civis.findWhere({id: link});

                    if(linked_civi.attributes.type == 'solution') {
                        _this.civis.remove(linked_civi)
                    } else {
                        var prev_links =linked_civi.get('links');
                        new_links = _.without(prev_links, _this.model.id);
                        linked_civi.set('links', new_links);
                    }
                });

                _this.civis.remove(_this.model);
                _this.remove();
                _this.parentView.initRecommended();
                _this.parentView.renderBodyContents();
                _this.parentView.processCiviScroll();

            },
            error: function(r){
                Materialize.toast('Could not delete the civi', 5000);
            }
        });
    }
});

cw.NewCiviView = BB.View.extend({
    el: '#new-civi-box',
    template: _.template($('#new-civi-template').html()),

    initialize: function (options) {
        this.options = options || {};
    },

    render: function () {
        this.$el.empty().append(this.template());
        // $('.responses').height($('#new-civi-box').height() + $('.responses-box').height());

        this.magicSuggestView = new cw.LinkSelectView(
            {$el: this.$('#magicsuggest'), civis: this.options.parentView.civis}
        );

        this.$('.edit-links').addClass('hide');
        this.$('#magicsuggest').addClass('hide');

        this.attachment_links = [];
        this.attachmentCount = 0;
        // this.renderMagicSuggest();
    },

    events: {
        'click .cancel-new-civi': 'cancelCivi',
        'click .create-new-civi': 'createCivi',
        'click .civi-type-button': 'clickType',
        'change .attachment-image-pick': 'previewImageNames',
        'input .civi-link-images': 'previewImageNames',
        'click #image-from-computer': 'showImageUploadForm',
        'click #image-from-link': 'showImageLinkForm',
        'click #add-image-link-input': 'addImageLinkInput',
        'click .ms-sel-ctn': ''
    },

    addImageLinkInput: function(){
        var link_images = this.$('.civi-link-images').length;
        if (link_images > 20 ) {
            Materialize.toast("Don't think you need any more...", 5000);
        } else {
            this.$('.image-link-list').append('<input type="text" class="civi-link-images" placeholder="Paste your image link here..."/>');
        }

    },

    previewImageNames: function(e) {
        var attachment_input = this.$('#id_attachment_image');
        var uploaded_images = attachment_input[0].files;
        var $previewlist = this.$('.file-preview');
        $previewlist.empty();
        // File Upload Images
        _.each(uploaded_images, function(img_file){
            $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+img_file.name+"</div>");
        }, this);

        // Link Images
        this.attachment_links = [];
        var link_images = $('.civi-link-images');
        _.each(link_images, function(img_link){
            var link_value = img_link.value.trim();
            if (link_value){
                $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+link_value+"</div>");
                this.attachment_links.push(link_value);
            }
        }, this);

        // Total images count
        var image_total = uploaded_images.length + this.attachment_links.length;
        if (image_total === 0) {
            $previewlist.prepend("<div>No Images</div>");
        } else if (image_total === 1) {
            $previewlist.prepend("<div>1 Image</div>");
        } else {
            $previewlist.prepend("<div>" + image_total + " Images</div>");
        }

        this.attachmentCount = image_total;

    },

    cancelCivi: function () {
        this.$el.empty();
    },

    createCivi: function (e) {
        var _this = this;

        var title = this.$el.find('#civi-title').val(),
            body = this.$el.find('#civi-body').val(),
            c_type = this.$el.find('.civi-types > .current').val();
        var links = this.magicSuggestView.ms.getValue();

        this.$(e.currentTarget).addClass('disabled').attr('disabled', true);

        if (title && body && c_type) {
            if (links.length === 0) {
                if (c_type === 'cause') {
                    Materialize.toast('A CAUSE Civi must be linked to a PROBLEM Civi. If it is only linked to a solution it will not appear', 5000);
                    this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                    return;
                } else if (c_type === 'solution') {
                    Materialize.toast('A SOLUTION Civi must be linked to a CAUSE Civi', 5000);
                    this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                    return;
                }
            }
            $.ajax({
                url: '/api/new_civi/',
                type: 'POST',
                data: {
                    csrfmiddlewaretoken: csrftoken,
                    title: title,
                    body: body,
                    c_type: c_type,
                    thread_id: _this.model.threadId,
                    links: links,
                },
                success: function (response) {
                    var new_civi_data = response.data;
                    var new_civi = new cw.CiviModel(new_civi_data);
                    var can_edit = new_civi.get('author').username == _this.options.parentView.username ? true : false;

                    var attachment_input = _this.$('#id_attachment_image');
                    var uploaded_images = attachment_input[0].files;
                    if (_this.attachmentCount > 0) {
                        var formData = new FormData(_this.$('#attachment_image_form')[0]);
                        formData.set('civi_id', response.data.id);
                        if (_this.attachment_links.length){
                            _.each(_this.attachment_links, function(img_link){
                                formData.append('attachment_links[]', img_link);
                            });
                        }

                        $.ajaxSetup({
                            beforeSend: function(xhr, settings) {
                                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                                }
                            }
                        });

                        $.ajax({
                            url: '/api/upload_images/',
                            type: 'POST',
                            success: function (response2) {

                                Materialize.toast('New civi created.', 5000);
                                new_civi.set('attachments', response2.attachments);

                                // _this.hide();
                                $('#thread-' + c_type + 's').append(new cw.CiviView({model: new_civi, can_edit: can_edit, parentView: _this.options.parentView}).el);
                                _this.options.parentView.civis.add(new_civi);

                                _this.options.parentView.initRecommended();
                                _this.options.parentView.renderBodyContents(); //TODO: move renders into listeners

                                _this.$el.empty();

                                $('body').css({overflow: 'hidden'});
                            },
                            error: function(e){
                                Materialize.toast('Civi was created but one or more images could not be uploaded', 5000);

                                // _this.hide();
                                $('#thread-' + c_type + 's').append(new cw.CiviView({model: new_civi, can_edit: can_edit, parentView: _this.options.parentView}).el);
                                _this.options.parentView.civis.add(new_civi);

                                _this.options.parentView.initRecommended();
                                _this.options.parentView.renderBodyContents(); //TODO: move renders into listeners

                                _this.$el.empty();

                                $('body').css({overflow: 'hidden'});
                            },
                            data: formData,
                            cache: false,
                            contentType: false,
                            processData: false
                        });

                    } else {
                        // _this.hide();
                        Materialize.toast('New civi created.', 5000);
                        $('#thread-' + c_type + 's').append(new cw.CiviView({model: new_civi, can_edit: can_edit, parentView: _this.options.parentView}).el);
                        _this.options.parentView.civis.add(new_civi);

                        var parent_links = new_civi.get('links');
                        _.each(parent_links, function(parent_id){
                            var parent_civi = _this.options.parentView.civis.get(parent_id);
                            if (parent_civi) {
                                var prev_links = parent_civi.get('links');
                                prev_links.push(new_civi.id);
                                parent_civi.set('links', prev_links);
                            }

                        }, this);

                        _this.options.parentView.initRecommended();
                        _this.options.parentView.renderBodyContents(); //TODO: move renders into listeners

                        _this.$el.empty();
                    }



                },
                error: function (response) {
                    Materialize.toast('Could not create Civi', 5000);
                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                }
            });
        } else {
            Materialize.toast('Please input all fields.', 5000);
            this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
        }
    },

    clickType: function (e) {
        var $this = $(e.target).closest('.civi-type-button');

        $this.addClass('current');
        $this.siblings().removeClass('current');

        var c_type = this.$el.find('.civi-types > .current').val();

        if (c_type === "problem") {
            this.$('.edit-links').addClass('hide');
            this.$('#magicsuggest').addClass('hide');
        } else {
            this.$('.edit-links').removeClass('hide');
            this.$('#magicsuggest').removeClass('hide');
            this.magicSuggestView.setLinkableData(c_type);
            this.magicSuggestView.ms.clear();
        }

    },
});

cw.NewResponseView = BB.View.extend({
    el: '#new-response-box',
    template: _.template($('#new-response-template').html()),
    initialize: function (options) {
        this.options = options || {};
        this.rebuttal_ref = '';
    },

    render: function () {
        this.$el.empty().append(this.template());
        $('#add-new-response').hide();

        this.attachment_links = [];
        this.attachmentCount = 0;
    },

    events: {
        'click .create-new-response': 'createResponse',
        'change .attachment-image-pick': 'previewImageNames',
        'click .cancel-new-response': 'hide',
        'input .civi-link-images': 'previewImageNames',
        'click #add-image-link-input': 'addImageLinkInput',
    },
    hide: function () {
        $('#new-response-box').empty();
        $('#add-new-response').show();
    },

    addImageLinkInput: function(){
        var link_images = this.$('.civi-link-images').length;
        if (link_images > 20 ) {
            Materialize.toast("Don't think you need any more...", 5000);
        } else {
            this.$('.image-link-list').append('<input type="text" class="civi-link-images" placeholder="Paste your image link here..."/>');
        }

    },

    previewImageNames: function(e) {
        var attachment_input = this.$('#response_attachment_image');
        var uploaded_images = attachment_input[0].files;
        var $previewlist = this.$('.file-preview');
        $previewlist.empty();
        // File Upload Images
        _.each(uploaded_images, function(img_file){
            $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+img_file.name+"</div>");
        }, this);

        // Link Images
        this.attachment_links = [];
        var link_images = $('.civi-link-images');
        _.each(link_images, function(img_link){
            var link_value = img_link.value.trim();
            if (link_value){
                $previewlist.append("<div class=\"link-lato gray-text preview-item \">"+link_value+"</div>");
                this.attachment_links.push(link_value);
            }
        }, this);

        // Total images count
        var image_total = uploaded_images.length + this.attachment_links.length;
        if (image_total === 0) {
            $previewlist.prepend("<div>No Images</div>");
        } else if (image_total === 1) {
            $previewlist.prepend("<div>1 Image</div>");
        } else {
            $previewlist.prepend("<div>" + image_total + " Images</div>");
        }

        this.attachmentCount = image_total;

    },

    createResponse: function (e) {
        var _this = this;
        this.$(e.currentTarget).addClass('disabled').attr('disabled', true);
        var title = this.$('#response-title').val(),
            body = this.$('#response-body').val(),
            related_civi, c_type;
        if (this.rebuttal_ref) {
            c_type = 'rebuttal';
            related_civi = this.rebuttal_ref;
        } else {
            c_type = 'response';
            related_civi = this.options.parentView.currentCivi;
        }
        if (title && body) {
            $.ajax({
                url: '/api/new_civi/',
                type: 'POST',
                data: {
                    title: title,
                    body: body,
                    c_type: c_type,
                    thread_id: this.model.threadId,
                    related_civi: related_civi
                },
                success: function (response) {
                    var attachment_input = _this.$('#response_attachment_image');
                    var uploaded_images = attachment_input[0].files;
                    if (_this.attachmentCount > 0) {
                        var formData = new FormData(_this.$('#response_attachment_image_form')[0]);
                        formData.set('civi_id', response.data.id);
                        if (_this.attachment_links.length){
                            _.each(_this.attachment_links, function(img_link){
                                formData.append('attachment_links[]', img_link);
                            });
                        }

                        $.ajaxSetup({
                            beforeSend: function(xhr, settings) {
                                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                                }
                            }
                        });

                        $.ajax({
                            url: '/api/upload_images/',
                            type: 'POST',
                            success: function (response2) {
                                Materialize.toast('New response created.', 5000);
                                _this.options.parentView.responseCollection.fetch();
                                _this.options.parentView.renderResponses();
                                _this.$el.empty();
                            },
                            error: function(e){
                                Materialize.toast('Response was created but images could not be uploaded', 5000);
                                // _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                                _this.options.parentView.responseCollection.fetch();
                                _this.options.parentView.renderResponses();
                                _this.$el.empty();
                            },
                            data: formData,
                            cache: false,
                            contentType: false,
                            processData: false
                        });
                    } else {
                        Materialize.toast('New response created.', 5000);
                        _this.options.parentView.responseCollection.fetch();
                        _this.options.parentView.renderResponses();
                        _this.$el.empty();
                    }
                },
                error: function(){
                    Materialize.toast('Could not create response', 5000);
                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                }
            });
        } else {
            Materialize.toast('Please input all fields.', 5000);
            _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
        }
    }
});

cw.EditThreadView = BB.View.extend({
    el: '.thread-wiki-holder',
    template: _.template($('#edit-wiki-template').html()),
    initialize: function (options) {
        this.options = options || {};
        this.threadId = options.threadId;
        this.parentView = options.parentView;
        this.removeImage = false;
        this.imageMode = "";
        this.render();
    },

    render: function () {
        this.$el.empty().append(this.template());
        this.$('#thread-image-forms').addClass('hide');
        cw.materializeShit();
    },

    events: {
        'click .create-new-response': 'createResponse',
        'click .cancel-thread': 'cancelEdit',
        'click .delete-previous-image': 'showImageForm',
        'click .use-previous-image': 'hideImageForm',
        'click .edit-thread': 'editThread',
        'click #image-from-computer': 'showImageUploadForm',
        'click #image-from-link': 'showImageLinkForm',
        'change #thread-location': 'showStates',
    },

    show: function () {
        this.$('.edit-thread-modal').openModal();
    },

    hide: function () {
        this.$('.edit-thread-modal').closeModal();
    },

    cancelEdit: function () {
        this.parentView.threadWikiRender();
    },

    showImageForm: function () {
        this.$('#thread-image-forms').removeClass('hide');
        this.$('.previous-image').addClass('hide');
        this.removeImage = true;
    },

    hideImageForm: function () {
        this.$('#thread-image-forms').addClass('hide');
        this.$('.previous-image').removeClass('hide');
        this.removeImage = false;
    },
    showImageUploadForm: function () {
        this.imageMode="upload";
        this.$('#attachment_image_form').removeClass('hide');
        this.$('#link-image-form').addClass('hide');
    },
    showImageLinkForm: function () {
        this.imageMode="link";
        this.$('#attachment_image_form').addClass('hide');
        this.$('#link-image-form').removeClass('hide');
    },

    editThread: function (e) {
        var _this = this;
        _this.$(e.currentTarget).addClass('disabled').attr('disabled', true);

        var title = this.$el.find('#thread-title').val().trim();
        var summary = this.$el.find('#thread-body').val().trim();
        var category_id = this.$el.find('#thread-category').val();

        var thread_id = this.threadId;
        
        if (title && summary && category_id) {
            $.ajax({
                url: '/api/edit_thread/',
                type: 'POST',
                data: {
                    title: title,
                    summary: summary,
                    category_id: category_id,
                    thread_id: thread_id
                },
                success: function (response) {
                    var file = $('#thread_attachment_image').val();
                    var img_url = _this.$('#link-image-form').val().trim();
                    if (_this.removeImage) {
                        // if file
                        if (_this.imageMode==="upload" && file){
                            var formData = new FormData(_this.$('#attachment_image_form')[0]);
                            formData.set('thread_id', response.data.thread_id);

                            $.ajaxSetup({
                                beforeSend: function(xhr, settings) {
                                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                                    }
                                }
                            });

                            $.ajax({
                                url: '/api/upload_image/',
                                type: 'POST',
                                success: function (response2) {
                                    Materialize.toast('Saved changes', 5000);
                                    // _this.hide();


                                    new_data = response.data;
                                    _this.parentView.model.set('title', new_data.title);
                                    _this.parentView.model.set('summary', new_data.summary);
                                    _this.parentView.model.set('category', new_data.category);
                                    _this.parentView.model.set('image', response2.image);
                                    _this.parentView.threadWikiRender();
                                    _this.parentView.threadNavRender();

                                },
                                error: function(e){
                                    Materialize.toast('ERROR: Image could not be uploaded', 5000);
                                    Materialize.toast(e.statusText, 5000);
                                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                                },
                                data: formData,
                                cache: false,
                                contentType: false,
                                processData: false
                            });
                        } else if (_this.imageMode==="link" && img_url){

                            $.ajax({
                                url: '/api/upload_image/',
                                type: 'POST',
                                data: {
                                    csrfmiddlewaretoken: csrftoken,
                                    link: img_url,
                                    thread_id: response.data.thread_id
                                },
                                success: function (response2) {
                                    Materialize.toast('Saved changes', 5000);
                                    _this.hide();


                                    new_data = response.data;
                                    _this.parentView.model.set('title', new_data.title);
                                    _this.parentView.model.set('summary', new_data.summary);
                                    _this.parentView.model.set('category', new_data.category);
                                    _this.parentView.model.set('image', response2.image);
                                    _this.parentView.threadWikiRender();
                                    _this.parentView.threadNavRender();
                                },
                                error: function(e){
                                    Materialize.toast('ERROR: Image could not be uploaded', 5000);
                                    Materialize.toast(e.statusText, 5000);
                                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                                }
                            });
                        }
                        else { // Remove image
                            $.ajax({
                                url: '/api/upload_image/',
                                type: 'POST',
                                data: {
                                    csrfmiddlewaretoken: csrftoken,
                                    remove: true,
                                    thread_id: response.data.thread_id
                                },
                                success: function (response2) {
                                    Materialize.toast('Saved changes', 5000);
                                    // _this.hide();

                                    new_data = response.data;
                                    _this.parentView.model.set('title', new_data.title);
                                    _this.parentView.model.set('summary', new_data.summary);
                                    _this.parentView.model.set('category', new_data.category);
                                    _this.parentView.model.set('image', response2.image);
                                    _this.parentView.threadWikiRender();
                                    _this.parentView.threadNavRender();
                                },
                                error: function(e){
                                    Materialize.toast('ERROR: Image could not be uploaded', 5000);
                                    Materialize.toast(e.statusText, 5000);
                                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                                }
                            });
                        }

                    } else {
                        Materialize.toast('Saved changes', 5000);
                        // _this.hide();

                        // New Data
                        new_data = response.data;
                        _this.parentView.model.set('title', new_data.title);
                        _this.parentView.model.set('summary', new_data.summary);
                        _this.parentView.model.set('category',new_data.category);
                        _this.parentView.threadWikiRender();
                        _this.parentView.threadNavRender();
                    }

                },
                error: function() {
                    Materialize.toast('Servor Error: Thread could not be edited', 5000);
                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                }
            });
        } else {
            Materialize.toast('Please input all fields.', 5000);
            _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
        }
    },
});

cw.OutlineView = BB.View.extend({});

cw.LinkSelectView = BB.View.extend({
    el: '#magicsuggest',

    initialize: function (options) {
        this.options = options || {};
        this.civis = options.civis;

        this.$el = options.$el || this.$el;
        this.setElement(this.$el);
        this.render();

        return this;
    },

    render: function(){
        var _this = this;

        this.ms = this.$el.magicSuggest({
            allowFreeEntries: false,
            groupBy: 'type',
            valueField: 'id',
            id : 'civi_links',
            displayField: 'title',
            expandOnFocus: true,
            data: [],
            renderer: function(data){
                return '<div class="link-lato" data-civi-id="' + data.id +
                '"><span class="gray-text">'+data.type+'</span> ' + data.title + '</div>';
            },
            selectionRenderer: function(data){
                return '<span class="gray-text bold-text">'+data.type.toUpperCase() +'</span> '  + data.title;
            },
        });
    },

    setLinkableData: function(c_type) {
        var _this = this;
        var linkableCivis = [];
        if (c_type == 'cause') {
            linkableCivis = _this.civis.where({type:'problem'});
        } else if  (c_type == 'solution') {
            linkableCivis = _this.civis.where({type:'cause'});
        }
        var msdata = [];
        _.each(linkableCivis, function(c_model){
            var civi = {
                'id': c_model.get('id'),
                'type': c_model.get('type'),
                'title': c_model.get('title')
            };
            msdata.push(civi);
        });

        this.ms.setData(msdata);

        return this;
    },
});

cw.ThreadView = BB.View.extend({
    el: '#thread',
    template: _.template($('#thread-template').html()),
    wikiTemplate: _.template($('#thread-wiki-template').html()),
    bodyTemplate: _.template($('#thread-body-template').html()),
    navTemplate: _.template($('#thread-nav-template').html()),
    responseWrapper: _.template($('#thread-response-template').html()),
    outlineTemplate: _.template($('#outline-template').html()),
  

    initialize: function (options) {
        options = options || {};
        this.username = options.username;
        this.civis = options.civis;
        this.navExpanded = true;
        this.is_draft = options.is_draft;

        this.civiRecViewLimits = {problem : 0,cause: 0,solution: 0};
        this.civiOtherViewLimits = {problem : 0,cause: 0,solution: 0};
        this.civiRecViewTotals = {problem : 0,cause: 0,solution: 0};
        this.civiOtherViewTotals = {problem : 0,cause: 0,solution: 0};

        this.viewRecommended = true;
        this.recommendedCivis = [];
        this.otherCivis = [];
        this.outlineCivis = {};
        this.initRecommended();

        this.responseCollection = new cw.ResponseCollection({}, {
            threadId: this.model.threadId
        });

        this.listenTo(this.responseCollection, 'sync', this.renderResponses);
        this.render();
    },

    initRecommended: function() {
        var _this = this;
        this.recommendedCivis = [];
        this.otherCivis = [];
        this.civiRecViewTotals = {problem : 0,cause: 0,solution: 0};
        this.civiOtherViewTotals = {problem : 0,cause: 0,solution: 0};

        var civiRecViewLimits= {problem : 0,cause: 0,solution: 0},
        civiOtherViewLimits= {problem : 0,cause: 0,solution: 0};
        // 1. Get id list of voted civis\
        var votes = this.model.get('user_votes');

        if (this.is_draft) {
            _.each(this.civis.models, function(civi){
                this.recommendedCivis.push(civi.id);
            }, this);
        } else {
            _.each(this.civis.models, function(civi){
                var voteData = _.findWhere(votes, {civi_id: civi.id});
                if (!_.isUndefined(voteData)) {

                    civi.voted = true;
                    var type = civi.get('type');
                    if (type === 'problem') {
                        civiRecViewLimits[type]++;
                        civiOtherViewLimits[type]++;
                    } else if (voteData.activity_type == 'vote_pos' || voteData.activity_type == 'vote_vpos') {
                        civiRecViewLimits[type]++;
                    } else {
                        civiOtherViewLimits[type]++;
                    }

                    if ((voteData.activity_type == 'vote_pos' || voteData.activity_type == 'vote_vpos')){
                        _.each(civi.get('links'), function(link){
                            var linked_civi = this.civis.get(link);
                            if (linked_civi) {
                                this.recommendedCivis.push(link);
                            }
                        }, this);
                    } else {
                        _.each(civi.get('links'), function(link){
                            var linked_civi = this.civis.get(link);
                            if (linked_civi) {
                                this.otherCivis.push(link);
                            }
                        }, this);
                    }
                } else {
                    civi.voted = false;
                }
            }, this);
        }


        // Recommended civis pool takes precedence
        this.otherCivis = _.difference(this.otherCivis, this.recommendedCivis);

        _.each(this.civis.filterByType('problem'), function(civi){
            this.recommendedCivis.push(civi.id);
            this.otherCivis.push(civi.id);
        },this);

        _.each(cw.DEFAULTS.types, function(type){
            if (this.civiRecViewLimits[type] === 0) {
                if (civiRecViewLimits[type] < 5) {
                    civiRecViewLimits[type] = 5;
                }
                this.civiRecViewLimits[type] = civiRecViewLimits[type];
            }
            if (this.civiOtherViewLimits[type] === 0) {
                if (civiOtherViewLimits[type] < 5) {
                    civiOtherViewLimits[type] = 5;
                }
                this.civiOtherViewLimits[type] = civiOtherViewLimits[type];
            }



        }, this);




    },

    render: function () {
        this.$el.empty().append(this.template());

        this.editThreadView = new cw.EditThreadView({
            model: this.model,
            parentView: this,
            threadId: this.model.threadId
        });

        this.$('.thread-wiki-holder').addClass('hide');

        this.threadWikiRender();
        this.threadBodyRender();
        this.threadNavRender();

        this.newCiviView = new cw.NewCiviView({
            model: this.model,
            parentView: this
        });


        this.renderBodyContents();
        this.scrollToBody();

    },

    threadWikiRender: function () {
        if (this.$('.thread-wiki-holder').length) {
            this.$('.thread-wiki-holder').empty().append(this.wikiTemplate());
        }
    },

    threadBodyRender: function () {
        var _this = this;

        if (this.$('.thread-body-holder').length) {
            var bodyRenderData = {
                is_draft: this.is_draft,
            };
            this.$('.thread-body-holder').empty().append(this.bodyTemplate(bodyRenderData));
            this.$('.main-thread').on('scroll', function (e) {
                _this.processCiviScroll();
            });
        }
    },

    // Renders thread navbar element independently of main body.
    // Useful for updating after thread edits are saved.
    threadNavRender: function () {
      var _this = this;
      var navRenderData = {
          is_draft: this.is_draft,
      };
      this.$('.thread-nav').empty().append(this.navTemplate(navRenderData));
      this.$('.main-thread').on('scroll', function (e) {
            _this.processCiviScroll();
      });
    },

    renderBodyContents: function () {
        this.renderCivis();
        this.renderOutline();

        if (!this.is_draft) {
            this.renderVotes();
        }
    },

    renderOutline: function(){
        var _this = this;
        if (this.civis.length === 0){
            // render with mock data to prevent errors
            var mockData = {
                is_draft: undefined,
                count: 0,
                problems: [],
                causes: [],
                solutions: []
            }
            
            this.$('#civi-outline').empty().append(this.outlineTemplate(mockData));
        }

        // Render Outline Template based on models
        var problems = this.outlineCivis.problem;
            causes = this.outlineCivis.cause;
            solutions = this.outlineCivis.solution;

        var renderData = {
            problems: problems,
            causes: causes,
            solutions: solutions
        };

        var recCount = { problem:0, cause:0, solution:0};
        var otherCount = { problem:0, cause:0, solution:0};
        var votes = this.model.get('user_votes'),
            voteIds = _.pluck(votes, 'civi_id');

        var counterCount = 0;
        _.each(_this.civis.models, function(c){
            var type = c.get('type');
            if (_.indexOf(voteIds, c.id) > -1) {
                // If part of current view setting
                if (_.indexOf(_this.recommendedCivis, c.id) > -1) {
                    recCount[type]++;
                } else if (_.indexOf(_this.otherCivis, c.id) > -1){
                    otherCount[type]++;
                }
            }
        });

        var highlightCount = {problem: 0,cause: 0,solution: 0};
        _.each(cw.DEFAULTS.types, function(type) {
            if (_this.viewRecommended){
                highlightCount[type] = _this.civiRecViewTotals[type];
            } else {
                highlightCount[type] = _this.civiOtherViewTotals[type];
            }
        });
        var voteCount, totalRec, totalOther;
        if (this.viewRecommended){
            voteCount = recCount;
        } else {
            voteCount = otherCount;
        }

        var count;

        if (this.is_draft) {
            count = {
                problem: highlightCount.problem,
                cause: highlightCount.cause,
                solution: highlightCount.solution,
            };
        } else {
            count = {
                problem: highlightCount.problem - recCount.problem,
                cause: highlightCount.cause - voteCount.cause,
                solution: highlightCount.solution - voteCount.solution,
            };
        }


        count.totalRec = this.civiRecViewTotals.problem + this.civiRecViewTotals.cause + this.civiRecViewTotals.solution - recCount.problem - recCount.cause - recCount.solution;
        count.totalOther = this.civiOtherViewTotals.problem + this.civiOtherViewTotals.cause + this.civiOtherViewTotals.solution - recCount.problem - otherCount.cause - otherCount.solution;

        renderData.count = count;

        renderData.is_draft = this.is_draft;

        this.$('#civi-outline').empty().append(this.outlineTemplate(renderData));
        this.$('#recommended-switch').attr('checked', this.viewRecommended);

        if (this.viewRecommended){
            this.$(".label-recommended").addClass('current');
            this.$(".label-other").removeClass('current');
            this.$(".badge-recommended").addClass('current');
            this.$(".badge-other").removeClass('current');
            this.$(".civi-nav-count").removeClass('other');
        } else {
            this.$(".label-recommended").removeClass('current');
            this.$(".label-other").addClass('current');
            this.$(".badge-recommended").removeClass('current');
            this.$(".badge-other").addClass('current');
            this.$(".civi-nav-count").addClass('other');
        }
        // view more

        _.each(cw.DEFAULTS.types, function(type) {
            var loadMore = this.$('#thread-'+type+'s>.' + type + '-loader');
            if (loadMore) {
                loadMore.clone().appendTo('#'+type+'-nav');
            }
        }, this);

        // Calculate tracking
        this.calcCiviLocations();

        var scrollPadding = 100;
        
        if (this.civiLocations.length) {
            // Padding so you can scroll and track the last civi element;
            scrollPadding = this.$('.main-thread').height() - this.civiLocations[this.civiLocations.length-1].height;
        }
        
        this.$('.civi-padding').height(scrollPadding - 8);

        // Vote indication
        var outline = this.$('#civi-outline');
        _.each(this.model.get('user_votes'), function(v){
            // var navItem = outline.find('#civi-nav-'+ v.civi_id);
            // navItem.before('<i class="material-icons tiny voted">beenhere</i>').addClass('nav-inactive');
            outline.find('#civi-nav-'+ v.civi_id).addClass('nav-inactive');
            var navItemState = outline.find('#civi-nav-state-' + v.civi_id);
            navItemState.addClass('voted').text('beenhere');
        }, this);



        this.expandNav();
    },

    renderCivis: function () {
        this.$('#thread-problems').empty();
        this.$('#thread-causes').empty();
        this.$('#thread-solutions').empty();

        this.threadCivis = {};
        _.each(['problem', 'cause', 'solution'], function(type){
            var civis = this.civis.filterByType(type);
            // Filter by Recommended state if not 'problem' type
            var recCivis = _.filter(civis, function(c) {
                return (_.indexOf(this.recommendedCivis, c.id) != -1);
            }, this);
            this.civiRecViewTotals[type] = recCivis.length;
            var otherCivis = _.filter(civis, function(c) {
                return (_.indexOf(this.otherCivis, c.id) != -1);
            }, this);
            this.civiOtherViewTotals[type] = otherCivis.length;
            if (type != 'problem') {
                if (this.viewRecommended) {
                    civis =recCivis;
                } else {
                    civis =otherCivis;
                }
            }
            // Sort civi list by score
            civis = _.sortBy(civis, function(civi){
                if (civi.voted) {
                    return -civi.get('score') - 100;
                }
                return -civi.get('score');
            });

            // Cut by type view limit TODO: move to rendering

            var limit;
            if (this.viewRecommended) {
                limit = this.civiRecViewLimits[type];
            } else {
                limit = this.civiOtherViewLimits[type];
            }
            var totalCount = civis.length;

            if(totalCount > limit && !this.is_draft) {
                civis = civis.slice(0,limit);
                _.each(civis, this.civiRenderHelper, this);
                this.$('#thread-'+type+'s').append('<div class="'+type+'-loader civi-load-more"><span class="civi-show-count">'+limit+'/'+totalCount+ ' '+ type+'s</span> <span class="btn-loadmore" data-civi-type="'+type+'">View More +</span></div>');
            } else {
                _.each(civis, this.civiRenderHelper, this);
            }

            this.outlineCivis[type] = civis;

        }, this);
    },

    civiRenderHelper: function(civi){
        var is_draft = civi.is_draft;
        var can_edit = civi.get('author').username == this.username ? true : false;
        this.$('#thread-'+civi.get('type')+'s').append(new cw.CiviView({model: civi, can_edit: can_edit, is_draft: is_draft, parentView: this}).el);

    },

    renderVotes: function() {
        var _this = this;
        var savedVotes = this.model.get('user_votes');
        _.each(savedVotes, function(v){
            this.$('#civi-'+ v.civi_id).find("." +v.activity_type).addClass('current');
        });
    },

    renderResponses: function () {
        this.$('.responses-box').empty().append(this.responseWrapper());
        this.newResponseView = new cw.NewResponseView({
            model: this.model,
            parentView: this
        });


        _.each(this.responseCollection.models, function(civi){
            var can_edit = civi.get('author').username == this.username ? true : false;
            var can_respond = this.civis.get(this.responseCollection.civiId).get('author').username == this.username ? true : false;

            var new_civi_view = new cw.CiviView({model: civi, can_edit: can_edit, can_respond: can_respond, parentView: this, response: true});
            this.$('#response-list').append(new_civi_view.el);

            var vote = _.find(this.model.get('user_votes'), function(v){
                return v.civi_id === civi.id;
            });
            if (vote) {
                this.$('#civi-'+ vote.civi_id).find("." +vote.activity_type).addClass('current');
            }
            if (civi.get('rebuttal')) {
                var rebuttal_model = new cw.CiviModel(civi.get('rebuttal'));
                var rebuttal_can_edit = rebuttal_model.get('author').username == this.username ? true : false;
                var rebuttal_view = new cw.CiviView({model: rebuttal_model, can_edit: rebuttal_can_edit, can_respond: false, parentView: this, response: true});
                rebuttal_view.$('.civi-card').addClass('push-right');
                new_civi_view.el.after(rebuttal_view.el);

                vote = _.find(this.model.get('user_votes'), function(v){
                    return v.civi_id === rebuttal_model.id;
                });
                if (vote) {
                    this.$('#civi-'+ vote.civi_id).find("." +vote.activity_type).addClass('current');
                }
            }
        }, this);

        // add padding
        var $lastResponseCivi = this.$('#response-list div:last-child');
        var scrollPadding = this.$('.responses').height() - $lastResponseCivi.height();
        this.$('.responses-padding').height(scrollPadding - 8);
    },

    events: {
        'click .enter-body': 'scrollToBody',
        'click .enter-wiki': 'scrollToWiki',
        'click .expand-nav': 'toggleExpandNav',
        'click .civi-nav-link': 'goToCivi',
        'click .civi-card': 'drilldownCivi',
        'click .add-civi': 'openNewCiviModal',
        'click .add-response': 'openNewResponseModal',
        'click #recommended-switch': 'toggleRecommended',
        'click .btn-loadmore': 'loadMoreCivis',
        'click .edit-thread-button': 'openEditThreadModal',
        'click #js-publish-btn': 'publishThread'
    },

    scrollToBody: function () {
        var _this = this;
        this.$('.thread-wiki-holder').addClass('hide');
        this.$('.thread-body-holder').removeClass('hide');
        this.$('.thread-body-holder').css({display: 'block'});

        this.resizeBodyToWindow();

        this.renderOutline();

        this.processCiviScroll();
    },

    resizeBodyToWindow: function() {
        $('body').css({overflow: 'hidden'});

        var $windowHeight = $('body').height();
        var bodyHeight = $windowHeight - $("#js-global-nav").height();

        var $civiNavScroll = this.$('.civi-outline');
        $civiNavScroll.css({height: $windowHeight - $civiNavScroll.offset().top});
        var $civiThreadScroll = this.$('.main-thread');
        $civiThreadScroll.css({height: $windowHeight - $civiThreadScroll.offset().top});
        var $civiResponseScroll = this.$('.responses');
        $civiResponseScroll.css({height: $windowHeight - $civiResponseScroll.offset().top});
    },

    scrollToWiki: function () {
        var _this = this;

        this.$('.thread-body-holder').addClass('hide');
        this.$('.thread-wiki-holder').removeClass('hide');
        $('body').css({overflow: 'scroll'});
    },

    toggleExpandNav: function(e) {
        this.navExpanded = !this.navExpanded;
        this.expandNav(e);
    },

    expandNav: function (e) {
        var _this = this,
            $this = _.isUndefined(e) ? this.$('.expand-nav') : $(e.target);

        if (!this.navExpanded) {
            $('.civi-nav-wrapper').hide();
            $this.removeClass('expanded');
        } else {
            $('.civi-nav-wrapper').show();
            $this.addClass('expanded');
        }
        this.activateNav();
    },

    goToCivi: function (e) {
        var $link = $(e.target).closest('.civi-nav-link');
        this.$('.main-thread').animate({scrollTop: _.findWhere(this.civiLocations, {id: $link.attr('data-civi-nav-id')}).top - 15}, 250);
    },

    calcCiviLocations: function(){
        var _this = this;
        var threadPos = this.$('.main-thread').position().top;
        var scrollPos = this.$('.main-thread').scrollTop();
        this.civiLocations = [];

        this.$('.civi-card').each(function (idx, civi) {
            var $civi = $(civi),
                $civiTop = $civi.position().top + scrollPos - threadPos;
            _this.civiLocations.push({top: $civiTop, bottom: $civiTop + $civi.height(), height: $civi.height(), target: $civi, id: $civi.attr('data-civi-id')});
        });
    },

    processCiviScroll: function () {
        var _this = this;
        var scrollPosition = this.$('.main-thread').scrollTop();
        // 1. Check if there are any civis. No tracking if none
        if (this.civis.length === 0){
            return;
        }

        // 2. Go through list of heights to get current active civi
        var OFFSET = 100;
        var element = _.find(this.civiLocations, function (l) {
            return this.currentNavCivi !== l.id &&
                scrollPosition >= l.top - OFFSET &&
                scrollPosition < l.bottom - OFFSET;
        }, this);

        // 3. Activate Corresponding Civi Card and Nav
        if (!element) return;
        else {
            this.activateNav(element.id);
            this.autoscrollCivi(this.$('#civi-'+ element.id));
        }

    },

    activateNav: function(id) {
        var _this = this;
        this.currentNavCivi = id || this.currentNavCivi;
        var $currentNavCivi = _this.$('[data-civi-nav-id="' + _this.currentNavCivi + '"]');

        if (!_this.navExpanded) {
            this.$('.civi-nav-header').removeClass('current');
            $($currentNavCivi.closest('.civi-nav-wrapper').siblings()[0]).addClass('current');
        } else {
            this.$('.civi-nav-link').removeClass('current');
            this.$('.civi-nav-header').removeClass('current');
            $currentNavCivi.addClass('current');
        }

    },

    autoscrollCivi: function (target) {
        // TODO: Throttle may need to be somewhere else
        var $this = target;

        var $currentCivi = this.$('[data-civi-id="' + this.currentCivi + '"]'),
            $newCivi = $this.closest('.civi-card');

        if (this.currentCivi !== $newCivi.attr('data-civi-id')) {
            // $currentCivi.removeClass('current');
            this.$('.civi-card').removeClass('current');
            $newCivi.addClass('current');
            var civi_id = $newCivi.data('civi-id');

            this.currentCivi = $newCivi.attr('data-civi-id');
            if (!_.isUndefined(this.currentCivi)){
                this.responseCollection.civiId = this.currentCivi;
                this.responseCollection.fetch();
            }

        } else {
            $currentCivi.removeClass('current');
            this.$('.civi-card').removeClass('linked');

            this.currentCivi = null;
            this.$('.responses-box').empty();
        }

    },

    drilldownCivi: function (e) {
        var target = $(e.target);
        var ms_check = target.hasClass('ms-ctn') || target.hasClass('ms-sel-ctn') || target.hasClass('ms-close-btn') || target.hasClass('ms-trigger') || target.hasClass('ms-trigger-ico') || target.hasClass('ms-res-ctn') || target.hasClass('ms-sel-item') || target.hasClass('ms-res-group');

        if (target.hasClass('btn') || target.hasClass('rating-button') || target.is('input') || target.is('textarea') || target.is('label') || target.hasClass('input') || ms_check) {
            return;
        }
        var $this = $(e.currentTarget);
        if ($this.find('.civi-type').text() != "response" && $this.find('.civi-type').text() != "rebuttal") {
            var $currentCivi = this.$('[data-civi-id="' + this.currentCivi + '"]'),
                $newCivi = $this.closest('.civi-card');

            if (this.currentCivi !== $newCivi.attr('data-civi-id')) {
                // $currentCivi.removeClass('current');
                this.$('.civi-card').removeClass('current');
                $newCivi.addClass('current');
                var civi_id = $newCivi.data('civi-id');

                this.currentCivi = $newCivi.attr('data-civi-id');

                this.responseCollection.civiId = this.currentCivi;
                this.responseCollection.fetch();

            } else {
                $currentCivi.removeClass('current');
                this.$('.civi-card').removeClass('linked');

                this.currentCivi = null;
                this.$('.responses-box').empty();
            }
        }
    },

    selectInitialCiviAfterToggle: function(){
        var $newCivi = this.$('.civi-card').first();

        $newCivi.addClass('current');
        this.currentCivi = $newCivi.attr('data-civi-id');

        this.responseCollection.civiId = this.currentCivi;
        this.responseCollection.fetch();

    },

    loadMoreCivis: function(e) {
        var $target = $(e.currentTarget);
        var type = $target.data('civi-type');
        var limit, remaining;
        if (this.viewRecommended) {
            limit = this.civiRecViewLimits[type];
            remaining =  this.civiRecViewTotals[type] - limit;
        } else {
            limit = this.civiOtherViewLimits[type];
            remaining =  this.civiOtherViewTotals[type] - limit;
        }
        if (remaining <= 0) {
            return;
        }
        var addCount = (cw.DEFAULTS.viewLimit < remaining ? cw.DEFAULTS.viewLimit : remaining);

        if (this.viewRecommended) {
            this.civiRecViewLimits[type] += addCount;
        } else {
            this.civiOtherViewLimits[type] += addCount;
        }
        this.renderBodyContents();
    },

    toggleRecommended: function(e) {
        var target = $(e.currentTarget);
        var recommend_state = target.is(":checked");

        this.viewRecommended = recommend_state;

        this.$('.main-thread').scrollTop(0);
        this.renderBodyContents();
        this.processCiviScroll();
        this.selectInitialCiviAfterToggle();
    },

    publishThread: function(e){
        var _this = this;
        _this.$(e.currentTarget).addClass('disabled').attr('disabled', true);

        $.ajax({
            url: '/api/edit_thread/',
            type: 'POST',
            data: {
                csrfmiddlewaretoken: csrftoken,
                thread_id: _this.model.threadId,
                title: _this.model.attributes.title,
                summary: _this.model.attributes.summary,
                // Why is categories an array, when only one category is ever chosen?
                category_id: _this.model.attributes.categories[0].id,
                is_draft: false,
            },
            success: function (response) {
                _this.is_draft = false
                Materialize.toast('Thread is now public. Refreshing the page...', 5000);
                _this.$("#js-publish-btn").hide()
                var reload_page = _.bind(location.reload, location);
                _.delay(reload_page, 1000);
            },
            error: function (response) {
                if (response.status === 403) {
                    Materialize.toast('You do not have permission to publish the thread', 5000);
                }
                else if (response.status === 500) {
                    Materialize.toast('Server Error: Thread could not be published', 5000);
                    _this.$(e.currentTarget).removeClass('disabled').attr('disabled', false);
                }
            }
        });
    },

    openNewCiviModal: function () {
        this.newCiviView.render();
    },

    openNewResponseModal: function () {
        this.newResponseView.render();
    },

    openEditThreadModal: function() {
        this.editThreadView.render();
    },

    assign: function(view, selector) {
        view.setElement(this.$(selector)).render();
    }
});