/*   Copyright (c) 2010-2012, Diaspora Inc.  This file is
 *   licensed under the Affero General Public License version 3 or later.  See
 *   the COPYRIGHT file.
 */

describe("app.views.Publisher", function() {
  context("standalone", function() {
    beforeEach(function() {
      loginAs(factory.userAttrs());

      spec.loadFixture("aspects_index");
      this.view = new app.views.Publisher({
        standalone: true
      });
      this.view.open();
    });

    it("hides the close button in standalone mode", function() {
      expect(this.view.$(".md-cancel").is(":visible")).toBeFalsy();
    });

    it("hides the manage services link in standalone mode", function() {
      expect(this.view.$(".question_mark").is(":visible")).toBeFalsy();
    });

    describe("createStatusMessage", function(){
      it("doesn't add the status message to the stream", function() {
        app.stream = { addNow: $.noop };
        spyOn(app.stream, "addNow");
        this.view.createStatusMessage($.Event());
        jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, responseText: "{\"id\": 1}" });
        expect(app.stream.addNow).not.toHaveBeenCalled();
      });
    });
  });

  context("plain publisher", function() {
    beforeEach(function() {
      loginAs(factory.userAttrs());

      spec.loadFixture("aspects_index");
      this.view = new app.views.Publisher();
    });

    describe("#initSubviews", function() {
      it("calls checkSubmitAvailability if the publisher is prefilled with mentions", function() {
        spyOn(this.view, "checkSubmitAvailability");
        this.view.prefillMention = "user@example.org";
        this.view.initSubviews();
        expect(this.view.checkSubmitAvailability).toHaveBeenCalled();
      });
    });

    describe("#open", function() {
      it("removes the 'closed' class from the publisher element", function() {
        expect($(this.view.el)).toHaveClass("closed");
        this.view.open($.Event());
        expect($(this.view.el)).not.toHaveClass("closed");
      });

      it("won't open when disabled", function() {
        this.view.disabled = true;
        this.view.open($.Event());
        expect($(this.view.el)).toHaveClass("closed");
      });
    });

    describe("#close", function() {
      beforeEach(function() {
        this.view.open($.Event());
      });

      it("removes the 'active' class from the publisher element", function(){
        this.view.close($.Event());
        expect($(this.view.el)).toHaveClass("closed");
      });

      it("resets the element's height", function() {
        $(this.view.el).find("#status_message_text").height(100);
        this.view.close($.Event());
        expect($(this.view.el).find("#status_message_text").attr("style")).not.toContain("height");
      });

      it("calls autosize.update", function() {
        spyOn(autosize, "update");
        this.view.close($.Event());
        expect(autosize.update).toHaveBeenCalledWith(this.view.inputEl);
      });

      it("should hide the poll container correctly", function() {
        this.view.$el.find(".poll-creator").click();
        expect(this.view.$el.find(".publisher-textarea-wrapper")).toHaveClass("with-poll");
        expect(this.view.$el.find(".poll-creator-container")).toBeVisible();
        this.view.close();
        expect(this.view.$el.find(".publisher-textarea-wrapper")).not.toHaveClass("with-poll");
        expect(this.view.$el.find(".poll-creator-container")).not.toBeVisible();
        this.view.open();
        expect(this.view.$el.find(".publisher-textarea-wrapper")).not.toHaveClass("with-poll");
        expect(this.view.$el.find(".poll-creator-container")).not.toBeVisible();
        this.view.$el.find(".poll-creator").click();
        expect(this.view.$el.find(".publisher-textarea-wrapper")).toHaveClass("with-poll");
        expect(this.view.$el.find(".poll-creator-container")).toBeVisible();
      });

      it("should close the publisher when clicking outside", function() {
        expect("#publisher").not.toHaveClass("closed");
        $("body").click();
        expect("#publisher").toHaveClass("closed");
      });

      it("should not close the publisher when clicking inside", function() {
        expect("#publisher").not.toHaveClass("closed");
        $("#publisher").find(".publisher-textarea-wrapper").click();
        expect("#publisher").not.toHaveClass("closed");
        $("#publisher").find(".aspect-dropdown button").click();
        expect("#publisher").not.toHaveClass("closed");
      });

      it("should not close the publisher when clicking inside on a mobile", function() {
        // Bootstrap inserts a .dropdown-backdrop next to the dropdown menu
        // that take the whole page when it detects a mobile.
        // Clicking on this element should not close the publisher.
        // See https://github.com/diaspora/diaspora/issues/6979.
        $("#publisher").find(".aspect-dropdown").append("<div class='dropdown-backdrop'></div>")
          .css({position: "fixed", left: 0, right: 0, bottom: 0, top: 0, "z-index": 990});
        expect("#publisher").not.toHaveClass("closed");
        $("#publisher").find(".aspect-dropdown button").click();
        expect("#publisher").not.toHaveClass("closed");
        $("#publisher").find(".dropdown-backdrop").click();
        expect("#publisher").not.toHaveClass("closed");
      });
    });

    describe("#clear", function() {
      it("calls close", function(){
        spyOn(this.view, "close");

        this.view.clear($.Event());
        expect(this.view.close).toHaveBeenCalled();
      });

      it("calls hidePreview", function() {
        spyOn(this.view.markdownEditor, "hidePreview");

        this.view.clear($.Event());
        expect(this.view.markdownEditor.hidePreview).toHaveBeenCalled();
      });

      it("clears all textareas", function(){
        _.each(this.view.$("textarea"), function(element){
          $(element).val('this is some stuff');
          expect($(element).val()).not.toBe("");
        });

        this.view.clear($.Event());

        _.each(this.view.$("textarea"), function(element){
          expect($(element).val()).toBe("");
        });
      });

      it("removes all photos from the dropzone area", function(){
        var self = this;
        _.times(3, function(){
          self.view.photozoneEl.append($("<li>"));
        });

        expect(this.view.photozoneEl.html()).not.toBe("");
        this.view.clear($.Event());
        expect(this.view.photozoneEl.html()).toBe("");
      });

      it("removes all photo values appended by the photo uploader", function(){
        $(this.view.el).prepend("<input name='photos[]' value='3'/>");
        var photoValuesInput = this.view.$("input[name='photos[]']");

        photoValuesInput.val("3");
        this.view.clear($.Event());
        expect(this.view.$("input[name='photos[]']").length).toBe(0);
      });

      it("destroy location if exists", function(){
        setFixtures('<div id="location"></div>');
        this.view.view_locator = new app.views.Location({el: "#location"});

        expect($("#location").length).toBe(1);
        this.view.clear($.Event());
        expect($("#location").length).toBe(0);
      });

      it("removes the 'submitting' class from the textarea wrapper", function(){
        this.view.wrapperEl.addClass("submitting");
        expect(this.view.wrapperEl).toHaveClass("submitting");
        this.view.clear($.Event());
        expect(this.view.wrapperEl).not.toHaveClass("submitting");
      });
    });

    describe("createStatusMessage", function(){
      it("adds the status message to the stream", function() {
        app.stream = { addNow: $.noop };
        spyOn(app.stream, "addNow");
        this.view.createStatusMessage($.Event());
        jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, responseText: "{\"id\": 1}" });
        expect(app.stream.addNow).toHaveBeenCalled();
      });

      it("adds the 'submitting' class from the textarea wrapper", function(){
        expect(this.view.wrapperEl).not.toHaveClass("submitting");
        this.view.createStatusMessage($.Event());
        expect(this.view.wrapperEl).toHaveClass("submitting");
      });
    });

    describe('#setText', function() {
      it("sets the content text", function() {
        this.view.setText("FOO bar");
        expect(this.view.inputEl.val()).toEqual("FOO bar");
      });
    });

    describe('#setEnabled', function() {
      it("disables the publisher", function() {
        expect(this.view.disabled).toBeFalsy();
        this.view.setEnabled(false);

        expect(this.view.disabled).toBeTruthy();
        expect(this.view.inputEl.prop("disabled")).toBeTruthy();
      });

      it("disables submitting", function() {
        this.view.setText("TESTING");
        expect(this.view.submitEl.prop("disabled")).toBeFalsy();

        this.view.setEnabled(false);
        expect(this.view.submitEl.prop("disabled")).toBeTruthy();
      });
    });

    describe("publishing a post with keyboard", function(){
      it("should submit the form when ctrl+enter is pressed", function(){
        this.view.render();
        var form = this.view.$("form");
        var submitCallback = jasmine.createSpy().and.returnValue(false);
        form.submit(submitCallback);

        var e = $.Event("keydown", { which: Keycodes.ENTER, ctrlKey: true });
        this.view.keyDown(e);

        expect(submitCallback).toHaveBeenCalled();
        expect($(this.view.el)).not.toHaveClass("closed");
      });

      it("should submit the form when cmd+enter is pressed", function() {
        this.view.render();
        var form = this.view.$("form");
        var submitCallback = jasmine.createSpy().and.returnValue(false);
        form.submit(submitCallback);

        var e = $.Event("keydown", {which: Keycodes.ENTER, metaKey: true});
        this.view.keyDown(e);

        expect(submitCallback).toHaveBeenCalled();
        expect($(this.view.el)).not.toHaveClass("closed");
      });
    });

    describe("tryClose", function() {
      it("doesn't close the publisher if it is submittable", function() {
        spyOn(this.view, "_submittable").and.returnValue(true);
        spyOn(this.view, "close");
        this.view.tryClose();
        expect(this.view.close).not.toHaveBeenCalled();
      });

      it("doesn't close the publisher if it is in preview mode", function() {
        spyOn(this.view, "_submittable").and.returnValue(false);
        spyOn(this.view.markdownEditor, "isPreviewMode").and.returnValue(true);
        spyOn(this.view, "close");
        this.view.tryClose();
        expect(this.view.close).not.toHaveBeenCalled();
      });

      it("closes the publisher if it is neither submittable nor in preview mode", function() {
        spyOn(this.view, "_submittable").and.returnValue(false);
        spyOn(this.view.markdownEditor, "isPreviewMode").and.returnValue(false);
        spyOn(this.view, "close");
        this.view.tryClose();
        expect(this.view.close).toHaveBeenCalled();
      });
    });

    describe("_beforeUnload", function(){
      it("calls _submittable", function(){
        spyOn(this.view, "_submittable");
        $(window).trigger('beforeunload');
        expect(this.view._submittable).toHaveBeenCalled();
      });

      it("returns a confirmation if the publisher is submittable", function(){
        spyOn(this.view, "_submittable").and.returnValue(true);
        var e = $.Event();
        expect(this.view._beforeUnload(e)).toBe(Diaspora.I18n.t('confirm_unload'));
        expect(e.returnValue).toBe(Diaspora.I18n.t('confirm_unload'));
      });

      it("doesn't ask for a confirmation if the publisher isn't submittable", function(){
        spyOn(this.view, "_submittable").and.returnValue(false);
        var e = $.Event();
        expect(this.view._beforeUnload(e)).toBe(undefined);
        expect(e.returnValue).toBe(undefined);
      });
    });
  });

  context("services", function(){
    beforeEach( function(){
      spec.loadFixture('aspects_index_services');
      this.view = new app.views.Publisher();
    });

    it("toggles the 'dim' class on a clicked item", function() {
      var first = $(".service_icon").eq(0);
      var second = $(".service_icon").eq(1);

      expect(first.hasClass('dim')).toBeTruthy();
      expect(second.hasClass('dim')).toBeTruthy();

      first.trigger('click');

      expect(first.hasClass('dim')).toBeFalsy();
      expect(second.hasClass('dim')).toBeTruthy();

      first.trigger('click');

      expect(first.hasClass('dim')).toBeTruthy();
      expect(second.hasClass('dim')).toBeTruthy();
    });

    it("creates a counter element", function(){
      expect(this.view.$('.counter').length).toBe(0);
      $(".service_icon").first().trigger('click');
      expect(this.view.$('.counter').length).toBe(1);
    });

    it("removes any old counters", function(){
      spyOn($.fn, "remove");
      $(".service_icon").first().trigger('click');
      expect($.fn.remove).toHaveBeenCalled();
    });

    it("toggles the hidden input field", function(){
      expect(this.view.$('input[name="services[]"]').length).toBe(0);
      $(".service_icon").first().trigger('click');
      expect(this.view.$('input[name="services[]"]').length).toBe(1);
      $(".service_icon").first().trigger('click');
      expect(this.view.$('input[name="services[]"]').length).toBe(0);
    });

    it("toggles the correct input", function() {
      var first = $(".service_icon").eq(0);
      var second = $(".service_icon").eq(1);

      first.trigger('click');
      second.trigger('click');

      expect(this.view.$('input[name="services[]"]').length).toBe(2);

      first.trigger('click');

      var prov1 = first.attr('id');
      var prov2 = second.attr('id');

      expect(this.view.$('input[name="services[]"][value="'+prov1+'"]').length).toBe(0);
      expect(this.view.$('input[name="services[]"][value="'+prov2+'"]').length).toBe(1);
    });

    describe("#clear", function() {
      it("resets the char counter", function() {
        this.view.$(".service_icon").first().trigger("click");
        expect(parseInt(this.view.$(".counter").text(), 10)).toBeGreaterThan(0);
        this.view.$(".counter").text("0");
        expect(parseInt(this.view.$(".counter").text(), 10)).not.toBeGreaterThan(0);
        this.view.clear($.Event());
        expect(parseInt(this.view.$(".counter").text(), 10)).toBeGreaterThan(0);
      });
    });
  });

  context("aspect selection", function(){
    beforeEach( function(){
      loginAs(factory.userAttrs());
      spec.loadFixture("status_message_new");

      this.view = new app.views.Publisher();
      this.view.open();
    });

    it("initializes with 'all_aspects'", function(){
      expect($("#publisher #visibility-icon")).not.toHaveClass("entypo-globe");
      expect($("#publisher #visibility-icon")).toHaveClass("entypo-lock");
    });

    describe("toggles the selected entry visually", function(){
      it("click on the first aspect", function(){
        this.view.$(".aspect-dropdown li.aspect_selector:first").click();
        expect($("#publisher #visibility-icon")).not.toHaveClass("entypo-globe");
        expect($("#publisher #visibility-icon")).toHaveClass("entypo-lock");
      });

      it("click on public", function(){
        this.view.$(".aspect-dropdown li.public").click();
        expect($("#publisher #visibility-icon")).toHaveClass("entypo-globe");
        expect($("#publisher #visibility-icon")).not.toHaveClass("entypo-lock");
      });

      it("click on 'all aspects'", function(){
        expect($("#publisher #visibility-icon")).not.toHaveClass("entypo-globe");
        expect($("#publisher #visibility-icon")).toHaveClass("entypo-lock");
      });
    });

    describe("hidden form elements", function(){
      beforeEach(function(){
        $('.dropdown-menu').append('<li data-aspect_id="42" class="aspect_selector" />');
      });

      it("removes a previous selection and inserts the current one", function() {
        var selected = $('input[name="aspect_ids[]"]');
        expect(selected.length).toBe(1);
        expect(selected.first().val()).toBe('all_aspects');

        var evt = $.Event("click", { target: $('.aspect-dropdown li.aspect_selector:last') });
        this.view.viewAspectSelector.toggleAspect(evt);

        selected = $('input[name="aspect_ids[]"]');
        expect(selected.length).toBe(1);
        expect(selected.first().val()).toBe('42');
      });

      it("toggles the same item", function() {
        expect($('input[name="aspect_ids[]"][value="42"]').length).toBe(0);

        var evt = $.Event("click", { target: $('.aspect-dropdown li.aspect_selector:last') });
        this.view.viewAspectSelector.toggleAspect(evt);
        expect($('input[name="aspect_ids[]"][value="42"]').length).toBe(1);

        evt = $.Event("click", { target: $('.aspect-dropdown li.aspect_selector:last') });
        this.view.viewAspectSelector.toggleAspect(evt);
        expect($('input[name="aspect_ids[]"][value="42"]').length).toBe(0);
      });

      it("keeps other fields with different values", function() {
        $('.dropdown-menu').append('<li data-aspect_id="99" class="aspect_selector" />');
        var evt = $.Event("click", { target: $('.aspect-dropdown li.aspect_selector:eq(-2)') });
        this.view.viewAspectSelector.toggleAspect(evt);
        evt = $.Event("click", { target: $('.aspect-dropdown li.aspect_selector:eq(-1)') });
        this.view.viewAspectSelector.toggleAspect(evt);

        expect($('input[name="aspect_ids[]"][value="42"]').length).toBe(1);
        expect($('input[name="aspect_ids[]"][value="99"]').length).toBe(1);
      });
    });
  });

  context("locator", function() {
    beforeEach(function() {
      loginAs(factory.userAttrs());

      spec.loadFixture("aspects_index");
      this.view = new app.views.Publisher();
    });

    describe('#showLocation', function(){
      it("Show location", function(){

        // inserts location to the DOM; it is the location's view element
        setFixtures('<div class="location-container"></div>');

        // creates a fake Locator
        OSM = {};
        OSM.Locator = function(){return { getAddress:function(){}}};

        // validates there is not location
        expect($("#location").length).toBe(0);

        // this should create a new location
        this.view.showLocation();

        // validates there is one location created
        expect($("#location").length).toBe(1);
      });
    });

    describe('#destroyLocation', function(){
      it("Destroy location if exists", function(){
        setFixtures('<div id="location"></div>');
        this.view.view_locator = new app.views.Location({el: "#location"});
        this.view.destroyLocation();

        expect($("#location").length).toBe(0);
      });
    });

    describe('#avoidEnter', function(){
      it("Avoid submitting the form when pressing enter", function(){
        // simulates the event object
        var evt = $.Event("keydown", { which: Keycodes.ENTER });

        // should return false in order to avoid the form submition
        expect(this.view.avoidEnter(evt)).toBeFalsy();
      });
    });
  });

  context('uploader', function() {
    beforeEach(function() {
      jQuery.fx.off = true;
      setFixtures(
        '<div id="publisher">'+
        '  <div class="content_creation"><form>'+
        '    <div id="publisher-textarea-wrapper"></div>'+
        '    <div id="photodropzone"></div>'+
        '    <input type="submit" />'+
        '  </form></div>'+
        '</div>'
      );
    });

    it("initializes the FineUploader plugin", function() {
      spyOn(qq, "FineUploaderBasic");
      new app.views.Publisher();

      expect(qq.FineUploaderBasic).toHaveBeenCalled();
    });

    context('event handlers', function() {
      beforeEach(function() {
        this.view = new app.views.Publisher();

        // replace the uploader plugin with a dummy object
        var upload_view = this.view.viewUploader;
        this.uploader = {
          onProgress: _.bind(upload_view.progressHandler, upload_view),
          onSubmit:   _.bind(upload_view.submitHandler, upload_view),
          onComplete: _.bind(upload_view.uploadCompleteHandler, upload_view)
        };
        upload_view.uploader = this.uploader;
      });

      context('progress', function() {
        it('shows progress in percent', function() {
          this.uploader.onProgress(null, 'test.jpg', 20, 100);

          var info = this.view.viewUploader.info;
          expect(info.text()).toContain('test.jpg');
          expect(info.text()).toContain('20%');
        });
      });

      context('submitting', function() {
        beforeEach(function() {
          this.uploader.onSubmit(null, 'test.jpg');
        });

        it('adds a placeholder', function() {
          expect(this.view.wrapperEl.attr("class")).toContain("with_attachments");
          expect(this.view.photozoneEl.find("li").length).toBe(1);
        });

        it('disables the publisher buttons', function() {
          expect(this.view.submitEl.prop("disabled")).toBeTruthy();
        });
      });

      context('successful completion', function() {
        beforeEach(function() {
          $('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');

          this.uploader.onComplete(null, 'test.jpg', {
            data: { photo: {
              id: '987',
              unprocessed_image: { url: 'test.jpg' }
            }},
            success: true });
        });

        it('shows it in text form', function() {
          var info = this.view.viewUploader.info;
          expect(info.text()).toBe(Diaspora.I18n.t('photo_uploader.completed', {file: 'test.jpg'}));
        });

        it('adds a hidden input to the publisher', function() {
          var input = this.view.$('input[type="hidden"][value="987"][name="photos[]"]');
          expect(input.length).toBe(1);
        });

        it('replaces the placeholder', function() {
          var li  = this.view.photozoneEl.find("li");
          var img = li.find('img');

          expect(li.attr('class')).not.toContain('loading');
          expect(img.attr('src')).toBe('test.jpg');
          expect(img.attr('data-id')).toBe('987');
        });

        it('re-enables the buttons', function() {
          expect(this.view.submitEl.prop("disabled")).toBeFalsy();
        });
      });

      context('unsuccessful completion', function() {
        beforeEach(function() {
          $('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');

          this.uploader.onComplete(null, 'test.jpg', {
            data: { photo: {
              id: '987',
              unprocessed_image: { url: 'test.jpg' }
            }},
            success: false });
        });

        it('shows error message', function() {
          var info = this.view.viewUploader.info;
          expect(info.text()).toBe(Diaspora.I18n.t('photo_uploader.error', {file: 'test.jpg'}));
        });
      });
    });

    context('photo removal', function() {
      beforeEach(function(done) {
        this.view = new app.views.Publisher();
        this.view.wrapperEl.addClass("with_attachments");
        this.view.photozoneEl.html(
          "<li class=\"publisher_photo\">."+
          "  <img data-id=\"444\" />"+
          "  <div class=\"x\">X</div>"+
          "  <div class=\"circle\"></div>"+
          "</li>"
        );

        spyOn(jQuery, 'ajax').and.callFake(function(opts) { opts.success(); });
        this.view.viewUploader.on("change", done);
        this.view.photozoneEl.find(".x").click();
      });

      it('removes the element', function() {
        var photo = this.view.photozoneEl.find("li.publisher_photo");
        expect(photo.length).toBe(0);
      });

      it('sends an ajax request', function() {
        expect($.ajax).toHaveBeenCalled();
      });

      it('removes class on wrapper element', function() {
        expect(this.view.wrapperEl.attr("class")).not.toContain("with_attachments");
      });
    });
  });
});
