<commit_before>next_case_num = 1;

function showPersonHistoryInSidebar(person) {
    $("#sidebar").html("
Loading...
");
    $.get("/personHistory/" + person.id,
          null,
           function(data, status, jqXHR) {
            $("#sidebar").html(data);
    });
}

function Person(id, name) {
    this.name = name;
    this.id = id;
    var self = this;

    this.render = function() {
        return app.person_template({"name": name});
    }

    // called by PeopleChooser after self.el has been
    // initialized.
    this.activateClick = function() {
        self.el.click(
            function() {
                showPersonHistoryInSidebar(self);
            }
        );
    }
}

function PeopleChooser(el, on_add, on_remove) {
    this.el = el;
    this.people = [];
    var self = this;

    this.search_box = el.find(".person_search");
    this.search_box.autocomplete({
        source: "/jsonPeople",
    });

    self.one_person_mode = false;

//  el.mouseleave(function() {
//      if (self.people.length > 0) {
//          self.search_box.fadeOut();
//      } } );
//  el.mouseenter(function() { self.search_box.fadeIn(); } );
//
    this.search_box.bind( "autocompleteselect", function(event, ui) {
        new_person = self.addPerson(ui.item.id, ui.item.label);

        if (on_add && new_person) {
            on_add(new_person);
        }

        self.search_box.val('');
        event.preventDefault(); // keep jquery from inserting name into textbox
    });

    this.addPerson = function(id, name) {

        // Don't add people who have already been added.
        for (i in self.people) {
            if (id == self.people[i].id) {
                return;
            }
        }

        if (self.one_person_mode) {
            self.search_box.hide();
            self.el.find(".glyphicon").hide();
        }

        var p = new Person(id, name);
        self.people.push(p);
        p.el = self.el.find(".people").append(p.render()).children(":last-child");
        p.activateClick();

        p.el.find("img").click(function() { self.removePerson(p) });

        return p;
    }

    this.removePerson = function(person) {
        if (on_remove) {
            on_remove(person);
        }

        $(person.el).remove();

        for (i in self.people) {
            if (self.people[i] == person) {
                self.people.splice(i, 1);
            }
        }

        if (self.one_person_mode) {
            self.search_box.show();
            self.el.find(".glyphicon").show();
        }
    }

    this.loadPeople = function(people) {
        for (i in people) {
            self.addPerson(people[i].id, people[i].name);
        }
    }

    this.setOnePersonMode = function(one_person_mode) {
        self.one_person_mode = one_person_mode;
        return self;
    }
}

function RuleChooser(el, on_change) {
    this.el = el;
    var self = this;

    this.search_box = el.find(".rule_search");
    this.search_box.autocomplete({
        source: "/jsonRules",
    });

    this.search_box.bind( "autocompleteselect", function(event, ui) {
        self.setRule(ui.item.id, ui.item.label);

        if (on_change) { on_change(); }

        self.search_box.val('');
        event.preventDefault(); // keep jquery from inserting name into textbox
    });

    this.setRule = function(id, title) {
        self.search_box.hide();

        self.rule = id;
        self.rule_el =
            self.el.prepend(app.rule_template({name: title})).
                children(":first-child");

        self.rule_el.find("img").click(function() { self.unsetRule() });
    }

    this.unsetRule = function() {
        if (on_change) { on_change(); }

        $(self.rule_el).remove();

        self.rule = null;
        self.rule_el = null;

        self.search_box.show();
    }

    this.loadData = function(json) {
        self.setRule(json.id, json.title);
    }
}

function displayName(p) {
	if (p.display_name) {
		return p.display_name;
	} else {
		return p.first_name;
	}
}

function Charge(charge_id, el) {
    var self = this;

    this.loadData = function(json) {
        el.find(".resolution_plan").val(json.resolution_plan);
        if (json.plea == "Guilty") {
            el.find(".plea-guilty").prop("checked", true);
        } else if (json.plea == "Not Guilty") {
            el.find(".plea-not-guilty").prop("checked", true);
        }

        // rule, person
        if (json.person) {
            self.people_chooser.addPerson(
                json.person.person_id,
                displayName(json.person));
        }

        if (json.rule) {
            self.rule_chooser.loadData(json.rule);
        }
    }

    this.saveIfNeeded = function() {
        window.setTimeout(self.saveIfNeeded, 2000);
        if (!self.is_modified) {
            return;
        }

        url = "/saveCharge?id=" + charge_id;

        if (self.people_chooser.people.length > 0) {
            url += "&person_id=" + self.people_chooser.people[0].id;
        }

        url += "&resolution_plan=" + encodeURIComponent(el.find(".resolution_plan").val());
        plea = el.find(":checked");
        if (plea) {
            url += "&plea=" + plea.val();
        }

        if (self.rule_chooser.rule) {
            url += "&rule_id=" + self.rule_chooser.rule;
        }

        self.is_modified = false;
        $.post(url);
    }

    this.removeCharge = function() {
        self.el.remove();
        $.post("/removeCharge?id=" + charge_id);
    }

    this.markAsModified = function() {
        self.is_modified = true;
    }

    self.el = el;
    self.is_modified = false;
    window.setTimeout(self.saveIfNeeded, 2000);

    self.remove_button = el.find("button")
    self.remove_button.click(self.removeCharge);

    el.find(".resolution_plan").change(self.markAsModified);
    el.find(".plea-guilty").change(self.markAsModified);
    el.find(".plea-not-guilty").change(self.markAsModified);

    self.people_chooser = new PeopleChooser(el.find(".people_chooser"),
                                            self.markAsModified,
                                            self.markAsModified);
    self.people_chooser.setOnePersonMode(true);

    self.rule_chooser = new RuleChooser(el.find(".rule_chooser"),
                                        self.markAsModified);

    el.find("input[type=radio]").prop("name", "plea-" + charge_id);

    el.mouseleave(function() { self.remove_button.hide(); } );
    el.mouseenter(function() { self.remove_button.show(); } );
}

function Case (id, el) {
    var self = this;

    this.saveIfNeeded = function() {
        window.setTimeout(self.saveIfNeeded, 2000);
        if (!self.is_modified) {
            return;
        }

        url = "/saveCase?id=" + id;
        if (self.writer_chooser.people.length > 0) {
            url += "&writer_id=" + self.writer_chooser.people[0].id;
        }
        url += "&location=" + encodeURIComponent(self.el.find(".location").val());
        url += "&findings=" + encodeURIComponent(self.el.find(".findings").val());
        url += "&date=" + encodeURIComponent(self.el.find(".date").val());

        self.is_modified = false;
        $.post(url);
    }

    this.markAsModified = function() {
        self.is_modified = true;
    }

    this.loadData = function(data) {
        el.find(".location").val(data.location);
        el.find(".date").val(data.date);
        el.find(".findings").val(data.findings);

        if (data.writer) {
            self.writer_chooser.addPerson(data.writer.person_id,
                  displayName(data.writer));
        }

        for (i in data.testify_records) {
            t_r = data.testify_records[i];
            self.testifier_chooser.addPerson(
                t_r.person.person_id,
                displayName(t_r.person));
        }

        for (i in data.charges) {
            ch = data.charges[i];
            new_charge = self.addChargeNoServer(ch.id);
            new_charge.loadData(ch);
        }
    }

    this.addCharge = function() {
        $('body').animate({'scrollTop': $('body').scrollTop() + 90}, 'slow');
        $.post("/addCharge?case_number=" + id,
               function(data, textStatus, jqXHR) {
            self.addChargeNoServer(parseInt(data))
        } );
    }

    this.addChargeNoServer = function(charge_id) {
        var new_charge_el = self.el.find(".charges").append(
            app.charge_template()).children(":last-child");
        var new_charge = new Charge(charge_id, new_charge_el);
        self.charges.push(new_charge);
        return new_charge;
    }

    this.id = id
    this.el = el;
    this.writer_chooser = new PeopleChooser(el.find(".writer"),
                                            self.markAsModified,
                                            self.markAsModified);
    this.writer_chooser.setOnePersonMode(true);

    this.testifier_chooser = new PeopleChooser(
        el.find(".testifier"),
        function(person) {
            $.post("/addTestifier?case_number=" + id +
                   "&person_id=" + person.id);
        },
        function(person) {
            $.post("/removeTestifier?case_number=" + id +
                   "&person_id=" + person.id);
        });
    this.is_modified = false;
    this.charges = []

    el.find(".location").change(self.markAsModified);
    el.find(".findings").change(self.markAsModified);
    el.find(".date").change(self.markAsModified);

    el.find(".add-charges").click(self.addCharge);

    window.setTimeout(self.saveIfNeeded, 2000);
}

function addPersonAtMeeting(person, role) {
    $.post("/addPersonAtMeeting?meeting_id=" + app.meeting_id
           + "&person_id=" + person.id +
           "&role=" + role );
}

function removePersonAtMeeting(person, role) {
    $.post("/removePersonAtMeeting?meeting_id=" + app.meeting_id
           + "&person_id=" + person.id +
           "&role=" + role );
}

function makePeopleChooser(selector, role) {
    return new PeopleChooser(
        $(selector),
        function(person) { addPersonAtMeeting(person, role) },
        function(person) { removePersonAtMeeting(person, role) });
}

function loadInitialData() {
    app.committee_chooser.loadPeople(app.initial_data.committee);
    app.chair_chooser.loadPeople(app.initial_data.chair);
    app.notetaker_chooser.loadPeople(app.initial_data.notetaker);
    app.sub_chooser.loadPeople(app.initial_data.sub);

    for (i in app.initial_data.cases) {
        data = app.initial_data.cases[i];
        new_case = addCaseNoServer(data.case_number);
        new_case.loadData(data);
    }
}

$(function () {
    Handlebars.registerHelper('render', function(partialId, options) {
      var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
          source = $(selector).html(),
          html = Handlebars.compile(source)(options.hash);

      return new Handlebars.SafeString(html);
    });

    $("#meeting").append(Handlebars.compile($("#meeting-template").html())());

    Handlebars.registerPartial("people-chooser", $("#people-chooser").html());
    Handlebars.registerPartial("rule-chooser", $("#rule-chooser").html());

    app.case_template = Handlebars.compile($("#case-template").html());
    app.person_template = Handlebars.compile($("#person-template").html());
    app.charge_template = Handlebars.compile($("#charge-template").html());
    app.rule_template = Handlebars.compile($("#rule-template").html());

    app.committee_chooser =
        makePeopleChooser(".committee", app.ROLE_JC_MEMBER);

    app.chair_chooser =
        makePeopleChooser(".chair", app.ROLE_JC_CHAIR);
    app.chair_chooser.setOnePersonMode(true);

    app.notetaker_chooser =
        makePeopleChooser(".notetaker", app.ROLE_NOTE_TAKER);
    app.sub_chooser =
        makePeopleChooser(".sub", app.ROLE_JC_SUB);

    loadInitialData();
});

function addCaseNoServer(id)
{
    new_case = $("#meeting-cases").append(
        app.case_template({"num": id})).
        children(":last-child");

    var case_obj = new Case(id, new_case);
    app.cases.push(case_obj);

    $("#meeting-cases").append(case_obj.el);

    next_case_num += 1;

    return case_obj;
}

function addCase()
{
    d = new Date();

    case_id = app.case_number_prefix;
    if (next_case_num < 10) {
        case_id += "0";
    }
    case_id += next_case_num;
    $.post("/newCase?id=" + case_id +
           "&meeting_id=" + app.meeting_id, "",
           function(data, textStatus, jqXHR) {
        new_case = addCaseNoServer(case_id);
        $('body').animate({'scrollTop': new_case.el.offset().top + 500}, 'slow');
    });
}

<commit_msg>Remove Case.charges array because it is unused and is not kept up-to-date when charges are deleted.<commit_after>next_case_num = 1;

function showPersonHistoryInSidebar(person) {
    $("#sidebar").html("
Loading...
");
    $.get("/personHistory/" + person.id,
          null,
           function(data, status, jqXHR) {
            $("#sidebar").html(data);
    });
}

function Person(id, name) {
    this.name = name;
    this.id = id;
    var self = this;

    this.render = function() {
        return app.person_template({"name": name});
    }

    // called by PeopleChooser after self.el has been
    // initialized.
    this.activateClick = function() {
        self.el.click(
            function() {
                showPersonHistoryInSidebar(self);
            }
        );
    }
}

function PeopleChooser(el, on_add, on_remove) {
    this.el = el;
    this.people = [];
    var self = this;

    this.search_box = el.find(".person_search");
    this.search_box.autocomplete({
        source: "/jsonPeople",
    });

    self.one_person_mode = false;

//  el.mouseleave(function() {
//      if (self.people.length > 0) {
//          self.search_box.fadeOut();
//      } } );
//  el.mouseenter(function() { self.search_box.fadeIn(); } );
//
    this.search_box.bind( "autocompleteselect", function(event, ui) {
        new_person = self.addPerson(ui.item.id, ui.item.label);

        if (on_add && new_person) {
            on_add(new_person);
        }

        self.search_box.val('');
        event.preventDefault(); // keep jquery from inserting name into textbox
    });

    this.addPerson = function(id, name) {

        // Don't add people who have already been added.
        for (i in self.people) {
            if (id == self.people[i].id) {
                return;
            }
        }

        if (self.one_person_mode) {
            self.search_box.hide();
            self.el.find(".glyphicon").hide();
        }

        var p = new Person(id, name);
        self.people.push(p);
        p.el = self.el.find(".people").append(p.render()).children(":last-child");
        p.activateClick();

        p.el.find("img").click(function() { self.removePerson(p) });

        return p;
    }

    this.removePerson = function(person) {
        if (on_remove) {
            on_remove(person);
        }

        $(person.el).remove();

        for (i in self.people) {
            if (self.people[i] == person) {
                self.people.splice(i, 1);
            }
        }

        if (self.one_person_mode) {
            self.search_box.show();
            self.el.find(".glyphicon").show();
        }
    }

    this.loadPeople = function(people) {
        for (i in people) {
            self.addPerson(people[i].id, people[i].name);
        }
    }

    this.setOnePersonMode = function(one_person_mode) {
        self.one_person_mode = one_person_mode;
        return self;
    }
}

function RuleChooser(el, on_change) {
    this.el = el;
    var self = this;

    this.search_box = el.find(".rule_search");
    this.search_box.autocomplete({
        source: "/jsonRules",
    });

    this.search_box.bind( "autocompleteselect", function(event, ui) {
        self.setRule(ui.item.id, ui.item.label);

        if (on_change) { on_change(); }

        self.search_box.val('');
        event.preventDefault(); // keep jquery from inserting name into textbox
    });

    this.setRule = function(id, title) {
        self.search_box.hide();

        self.rule = id;
        self.rule_el =
            self.el.prepend(app.rule_template({name: title})).
                children(":first-child");

        self.rule_el.find("img").click(function() { self.unsetRule() });
    }

    this.unsetRule = function() {
        if (on_change) { on_change(); }

        $(self.rule_el).remove();

        self.rule = null;
        self.rule_el = null;

        self.search_box.show();
    }

    this.loadData = function(json) {
        self.setRule(json.id, json.title);
    }
}

function displayName(p) {
	if (p.display_name) {
		return p.display_name;
	} else {
		return p.first_name;
	}
}

function Charge(charge_id, el) {
    var self = this;

    this.loadData = function(json) {
        el.find(".resolution_plan").val(json.resolution_plan);
        if (json.plea == "Guilty") {
            el.find(".plea-guilty").prop("checked", true);
        } else if (json.plea == "Not Guilty") {
            el.find(".plea-not-guilty").prop("checked", true);
        }

        // rule, person
        if (json.person) {
            self.people_chooser.addPerson(
                json.person.person_id,
                displayName(json.person));
        }

        if (json.rule) {
            self.rule_chooser.loadData(json.rule);
        }
    }

    this.saveIfNeeded = function() {
        window.setTimeout(self.saveIfNeeded, 2000);
        if (!self.is_modified) {
            return;
        }

        url = "/saveCharge?id=" + charge_id;

        if (self.people_chooser.people.length > 0) {
            url += "&person_id=" + self.people_chooser.people[0].id;
        }

        url += "&resolution_plan=" + encodeURIComponent(el.find(".resolution_plan").val());
        plea = el.find(":checked");
        if (plea) {
            url += "&plea=" + plea.val();
        }

        if (self.rule_chooser.rule) {
            url += "&rule_id=" + self.rule_chooser.rule;
        }

        self.is_modified = false;
        $.post(url);
    }

    this.removeCharge = function() {
        self.el.remove();
        $.post("/removeCharge?id=" + charge_id);
    }

    this.markAsModified = function() {
        self.is_modified = true;
    }

    self.el = el;
    self.is_modified = false;
    window.setTimeout(self.saveIfNeeded, 2000);

    self.remove_button = el.find("button")
    self.remove_button.click(self.removeCharge);

    el.find(".resolution_plan").change(self.markAsModified);
    el.find(".plea-guilty").change(self.markAsModified);
    el.find(".plea-not-guilty").change(self.markAsModified);

    self.people_chooser = new PeopleChooser(el.find(".people_chooser"),
                                            self.markAsModified,
                                            self.markAsModified);
    self.people_chooser.setOnePersonMode(true);

    self.rule_chooser = new RuleChooser(el.find(".rule_chooser"),
                                        self.markAsModified);

    el.find("input[type=radio]").prop("name", "plea-" + charge_id);

    el.mouseleave(function() { self.remove_button.hide(); } );
    el.mouseenter(function() { self.remove_button.show(); } );
}

function Case (id, el) {
    var self = this;

    this.saveIfNeeded = function() {
        window.setTimeout(self.saveIfNeeded, 2000);
        if (!self.is_modified) {
            return;
        }

        url = "/saveCase?id=" + id;
        if (self.writer_chooser.people.length > 0) {
            url += "&writer_id=" + self.writer_chooser.people[0].id;
        }
        url += "&location=" + encodeURIComponent(self.el.find(".location").val());
        url += "&findings=" + encodeURIComponent(self.el.find(".findings").val());
        url += "&date=" + encodeURIComponent(self.el.find(".date").val());

        self.is_modified = false;
        $.post(url);
    }

    this.markAsModified = function() {
        self.is_modified = true;
    }

    this.loadData = function(data) {
        el.find(".location").val(data.location);
        el.find(".date").val(data.date);
        el.find(".findings").val(data.findings);

        if (data.writer) {
            self.writer_chooser.addPerson(data.writer.person_id,
                  displayName(data.writer));
        }

        for (i in data.testify_records) {
            t_r = data.testify_records[i];
            self.testifier_chooser.addPerson(
                t_r.person.person_id,
                displayName(t_r.person));
        }

        for (i in data.charges) {
            ch = data.charges[i];
            new_charge = self.addChargeNoServer(ch.id);
            new_charge.loadData(ch);
        }
    }

    this.addCharge = function() {
        $('body').animate({'scrollTop': $('body').scrollTop() + 90}, 'slow');
        $.post("/addCharge?case_number=" + id,
               function(data, textStatus, jqXHR) {
            self.addChargeNoServer(parseInt(data))
        } );
    }

    this.addChargeNoServer = function(charge_id) {
        var new_charge_el = self.el.find(".charges").append(
            app.charge_template()).children(":last-child");
        var new_charge = new Charge(charge_id, new_charge_el);
        return new_charge;
    }

    this.id = id
    this.el = el;
    this.writer_chooser = new PeopleChooser(el.find(".writer"),
                                            self.markAsModified,
                                            self.markAsModified);
    this.writer_chooser.setOnePersonMode(true);

    this.testifier_chooser = new PeopleChooser(
        el.find(".testifier"),
        function(person) {
            $.post("/addTestifier?case_number=" + id +
                   "&person_id=" + person.id);
        },
        function(person) {
            $.post("/removeTestifier?case_number=" + id +
                   "&person_id=" + person.id);
        });
    this.is_modified = false;

    el.find(".location").change(self.markAsModified);
    el.find(".findings").change(self.markAsModified);
    el.find(".date").change(self.markAsModified);

    el.find(".add-charges").click(self.addCharge);

    window.setTimeout(self.saveIfNeeded, 2000);
}

function addPersonAtMeeting(person, role) {
    $.post("/addPersonAtMeeting?meeting_id=" + app.meeting_id
           + "&person_id=" + person.id +
           "&role=" + role );
}

function removePersonAtMeeting(person, role) {
    $.post("/removePersonAtMeeting?meeting_id=" + app.meeting_id
           + "&person_id=" + person.id +
           "&role=" + role );
}

function makePeopleChooser(selector, role) {
    return new PeopleChooser(
        $(selector),
        function(person) { addPersonAtMeeting(person, role) },
        function(person) { removePersonAtMeeting(person, role) });
}

function loadInitialData() {
    app.committee_chooser.loadPeople(app.initial_data.committee);
    app.chair_chooser.loadPeople(app.initial_data.chair);
    app.notetaker_chooser.loadPeople(app.initial_data.notetaker);
    app.sub_chooser.loadPeople(app.initial_data.sub);

    for (i in app.initial_data.cases) {
        data = app.initial_data.cases[i];
        new_case = addCaseNoServer(data.case_number);
        new_case.loadData(data);
    }
}

$(function () {
    Handlebars.registerHelper('render', function(partialId, options) {
      var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
          source = $(selector).html(),
          html = Handlebars.compile(source)(options.hash);

      return new Handlebars.SafeString(html);
    });

    $("#meeting").append(Handlebars.compile($("#meeting-template").html())());

    Handlebars.registerPartial("people-chooser", $("#people-chooser").html());
    Handlebars.registerPartial("rule-chooser", $("#rule-chooser").html());

    app.case_template = Handlebars.compile($("#case-template").html());
    app.person_template = Handlebars.compile($("#person-template").html());
    app.charge_template = Handlebars.compile($("#charge-template").html());
    app.rule_template = Handlebars.compile($("#rule-template").html());

    app.committee_chooser =
        makePeopleChooser(".committee", app.ROLE_JC_MEMBER);

    app.chair_chooser =
        makePeopleChooser(".chair", app.ROLE_JC_CHAIR);
    app.chair_chooser.setOnePersonMode(true);

    app.notetaker_chooser =
        makePeopleChooser(".notetaker", app.ROLE_NOTE_TAKER);
    app.sub_chooser =
        makePeopleChooser(".sub", app.ROLE_JC_SUB);

    loadInitialData();
});

function addCaseNoServer(id)
{
    new_case = $("#meeting-cases").append(
        app.case_template({"num": id})).
        children(":last-child");

    var case_obj = new Case(id, new_case);
    app.cases.push(case_obj);

    $("#meeting-cases").append(case_obj.el);

    next_case_num += 1;

    return case_obj;
}

function addCase()
{
    d = new Date();

    case_id = app.case_number_prefix;
    if (next_case_num < 10) {
        case_id += "0";
    }
    case_id += next_case_num;
    $.post("/newCase?id=" + case_id +
           "&meeting_id=" + app.meeting_id, "",
           function(data, textStatus, jqXHR) {
        new_case = addCaseNoServer(case_id);
        $('body').animate({'scrollTop': new_case.el.offset().top + 500}, 'slow');
    });
}

<|endoftext|>


<commit_before>(function (xhr) {
  "use strict";

  var EnvironmentsProvider = function () {
  };

  EnvironmentsProvider.prototype.get = function (callback) {
    jQuery.getJSON("/environments/", function (data) {
      callback(data);
    });
  };

  xhr.EnvironmentsProvider = EnvironmentsProvider;

}(xhr));

(function (exports, prov) {
  "use strict";

  var Environments = function (envSelector) {
    this.envSelector = envSelector;
    this.provider = new prov.EnvironmentsProvider();
  };

  Environments.prototype.load = function (env) {
    this.provider.get(jQuery.proxy(this.renderEnvironments(env),
                                   this));
  };

  Environments.prototype.bindEvents = function () {
    var envSelector = this.envSelector;
    var parentObj = this;

    this.envSelector.bind('change', function () {
      jQuery.publish( "new-environment", jQuery(this).val() );
    });

    jQuery.subscribe( "new-environment", function (env) {
      jQuery.proxy(parentObj.renderEnvironments(env), parentObj);
    });
  };

  Environments.prototype.renderEnvironments = function (env) {
    return function (data) {
      var environments = jQuery(".status");
      var selectPlan = null;

      environments.empty();

      for(var i = 0, l = data.length; i < l; ++i) {
        var status = jQuery("#environment-status").tmpl(data[i]);
        status.appendTo(environments);

        if (env === data[i].plan) {
          selectPlan = data[i].plan;
        }
      }

      if (selectPlan) {
        jQuery.publish( "select-plan", selectPlan);
      }
    };
  };

  exports.Environments = Environments;

}(window, provider));<commit_msg>Don't select a plan based on environment.<commit_after>(function (xhr) {
  "use strict";

  var EnvironmentsProvider = function () {
  };

  EnvironmentsProvider.prototype.get = function (callback) {
    jQuery.getJSON("/environments/", function (data) {
      callback(data);
    });
  };

  xhr.EnvironmentsProvider = EnvironmentsProvider;

}(xhr));

(function (exports, prov) {
  "use strict";

  var Environments = function (envSelector) {
    this.envSelector = envSelector;
    this.provider = new prov.EnvironmentsProvider();
  };

  Environments.prototype.load = function (env) {
    this.provider.get(jQuery.proxy(this.renderEnvironments(env),
                                   this));
  };

  Environments.prototype.bindEvents = function () {
    var envSelector = this.envSelector;
    var parentObj = this;

    this.envSelector.bind('change', function () {
      jQuery.publish( "new-environment", jQuery(this).val() );
    });

    jQuery.subscribe( "new-environment", function (env) {
      jQuery.proxy(parentObj.renderEnvironments(env), parentObj);
    });
  };

  Environments.prototype.renderEnvironments = function (env) {
    return function (data) {
      var environments = jQuery(".status");
      var selectPlan = null;

      environments.empty();

      for(var i = 0, l = data.length; i < l; ++i) {
        var status = jQuery("#environment-status").tmpl(data[i]);
        status.appendTo(environments);
      }
    };
  };

  exports.Environments = Environments;

}(window, provider));<|endoftext|>


<commit_before>if (!Midas) var Midas = {};
Midas.Region = Class.create({
  version: 0.2,
  name: null,
  options: {
    configuration: null,
    contentWindow: window,
    inline: false
  },
  previewing: false,

  initialize: function(element, options, name) {
    if (!Midas.version) throw('Midas.Region requires Midas');
    if (!Midas.agentIsCapable()) throw('Midas.Region requires a browser that has contentEditable features');

    this.element = $(element);
    if (!this.element) throw('Unable to locate the element "' + element + '"');
    this.sibling = this.element.previousSibling;

    this.options = Object.extend(Object.clone(this.options), options);
    this.options['configuration'] = this.options['configuration'] || Midas.Config;
    this.config = this.options['configuration'];
    this.name = this.element.getAttribute('id') || name;
    this.doc = this.options['contentWindow'].document;
    this.selections = [];

    this.makeEditable();
    this.setupObservers();
  },

  makeEditable: function() {
    this.element.addClassName('midas-region');

    if (this.element.innerHTML.replace(/^\s+|\s+$/g, "") == '' && Prototype.Browser.Gecko) {
      this.setContents(' ')
    }

    if (this.options['inline']) {
      this.element.setStyle({height: 'auto', minHeight: '20px', minWidth: '20px'});
    } else {
      this.element.setStyle({overflow: 'auto'});
      var width = this.element.getWidth();
      if (width) this.element.setStyle({maxWidth: width + 'px'});
    }
    this.element.contentEditable = true;

    this.doc.execCommand('styleWithCSS', false, false);
    //this.doc.execCommand('enableInlineTableEditing', false, false);
  },

  setupObservers: function() {
    Event.observe(document, 'mouseup', function() {
      if (!this.selecting) return;
      this.selecting = false;
      this.updateSelections();
    }.bind(this));

    Event.observe(this.element, 'focus', function(e) {
      if (this.previewing) return;
      Midas.fire('region', {region: this, name: this.name, event: e});
      if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
    }.bind(this));

    Event.observe(this.element, 'paste', function(e) {
      if (Midas.modal.showing) e.stop();
      var html = this.element.innerHTML;
      setTimeout(function() { this.afterPaste(html); }.bind(this), 1);
    }.bind(this));
    Event.observe(this.element, 'drop', function(e) {
      var html = this.element.innerHTML;
      setTimeout(function() { this.afterPaste(html); }.bind(this), 1);
    }.bind(this));

    Event.observe(this.element, 'mousedown', function() {
      this.selecting = true;
    }.bind(this));
    Event.observe(this.element, 'mouseup', function(e) {
      if (this.previewing) return;
      Midas.fire('region:update', {region: this, name: this.name, event: e});
    }.bind(this));
    Event.observe(this.element, 'click', function(e) {
      if (this.previewing) {
        var element = e.target;
        if (element.tagName == 'A') {
          var uri = element.getAttribute('href');
          var host = uri.match(/^[http:|https:]/) ? uri.split('://')[1].split('/')[0] : false;
          if (host && host != top.location.host && host != top.location.hostname) {
            this.options['contentWindow'].onbeforeunload = Midas.onBeforeUnload;
            top.location.href = uri;
            e.stop();
          }
        }
      } else {
        Midas.fire('region', {region: this, name: this.name, event: e});
        if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
      }
    }.bind(this));

    Event.observe(this.element, 'keydown', function(e) {
      if (Midas.modal.showing) e.stop();
    }.bind(this));
    Event.observe(this.element, 'keyup', function(e) {
      if (this.previewing) return;
      this.updateSelections();
      Midas.fire('region:update', {region: this, name: this.name, event: e, changed: true});
    }.bind(this));
    Event.observe(this.element, 'keypress', function(e) {
      if (this.previewing) return;
      Midas.fire('region:update', {region: this, name: this.name, event: e});

      switch (e.keyCode) {
        case 9: // tab
          this.selections.each(function(selection) {
            var container = selection.commonAncestorContainer;
            if (container.nodeType == 3) container = container.parentNode;
            if (container.tagName == 'LI' || container.up('li')) {
              e.stop();
              this.handleAction('indent');
            }
          }.bind(this));
          break;
        case 13: // enter
          break;
      }
    }.bind(this));
  },

  setContents: function(content) {
    this.element.innerHTML = content;
  },

  getContents: function() {
    return this.element.innerHTML.replace(/^\s+|\s+$/g, "");
  },

  updateSelections: function() {
    var selection = this.options['contentWindow'].getSelection();
    this.selections = [];

    for (var i = 0; i <= selection.rangeCount - 1; ++i) {
      var range = selection.getRangeAt(i);

      if (range.commonAncestorContainer == this.element ||
          Element.descendantOf(range.commonAncestorContainer, this.element)) {
        this.selections.push(range);
      }
    }
  },

  afterPaste: function(beforeHtml) {
    var pastedRegion = this.element.down('.midas-region');
    if (pastedRegion) {
      var selection = this.options['contentWindow'].getSelection();
      selection.removeAllRanges();

      var range = this.doc.createRange();
      range.selectNode(pastedRegion);
      selection.addRange(range);

      this.execCommand('undo', null);
      this.execCommand('insertHTML', pastedRegion.innerHTML);
    }

    var pastedFromWord = this.element.innerHTML.indexOf('') > -1 || this.element.innerHTML.indexOf('="mso-') > -1 || this.element.innerHTML.indexOf('<o:') > -1;
    if (pastedFromWord) {
      var pasted = beforeHtml.singleDiff(this.element.innerHTML);
      var cleaned = this.sanitizeHtml(pasted);
      try {
        this.doc.execCommand('undo', false, null);
        this.execCommand('insertHTML', cleaned);
      } catch(e) {
        this.setContents(beforeHtml);
        Midas.modal('/midas/sanitizer.html', {
          title: 'HTML Sanitizer',
          afterLoad: function() {
            $('midas_sanitized_content').value = cleaned.replace(/
/g, '\n');
          }
        });
      }
    }
  },

  sanitizeHtml: function(html) {
    var temp = new Element('div').update(html);
    temp.select('style').each(function(style) {
      style.remove();
    });

    return temp.textContent.replace(/\n\n/g, '
').
                            replace(/.*/g, '').
                            replace(/^(
)+|(
\s*)+$/g, '');
  },

  execCommand: function(action, argument) {
    argument = typeof(argument) == 'undefined' ? null : argument;

    var supported = this.doc.execCommand('styleWithCSS', false, false);
    var handled;
    try {
      handled = this.doc.execCommand(action, false, argument);
    } catch(e) {
      Midas.trace(e);
      // Gecko does some interesting things when it fails on indent
      if (action == 'indent') {
        var sibling = this.element.previousSibling;
        if (sibling != this.sibling) sibling.remove();
      }
      handled = true;
    }
    if (!handled && supported) throw('Unknown action "' + action + '"');
  },

  serialize: function() {
    return {name: this.name, content: this.getContents()}
  },

  togglePreview: function() {
    if (this.previewing) {
      this.element.removeClassName('midas-region-preview');
      this.makeEditable();
      this.previewing = false;
    } else {
      this.element.contentEditable = false;
      this.element.addClassName('midas-region-preview');
      this.element.setStyle({height: null, minHeight: null, minWidth: null, maxWidth: null, overflow: null});
      this.element.removeClassName('midas-region');
      this.previewing = true;
    }
  },

  destroy: function() {
    this.element.contentEditable = 'false';
    this.element.blur();
    this.element.removeClassName('midas-region');
  },

  handleAction: function(action, event, toolbar, options) {
    options = options || {};

    if (this.config['behaviors'][action]) {
      var behaviors = this.config['behaviors'][action];

      for (var behavior in behaviors) {
        if (Object.isFunction(this.handle[behavior])) {
          this.handle[behavior].call(this, action, toolbar, options, behaviors[behavior]);

          var sel = window.getSelection();
          this.selections.each(function(selection) {
            //sel.removeRange(selection);
            sel.addRange(selection);
          })
        } else {
          throw('Unknown behavior method "' + behavior + '"');
        }
      }
    } else {
      switch (action) {
        case 'removeformatting':
          this.execCommand('insertHTML', this.selections[0].cloneContents().textContent);
          break;
        case 'style':
          this.wrap('span', function() {
            return new Element('span', {'class': options['value']});
          }, function(element) {
            element.addClassName(options['value']);
          });
          break;
        case 'backcolor':
          this.wrap('font', function() {
            return new Element('font', {style: 'background-color:' + options['value']});
          }, function(element) {
            element.setStyle('background-color:' + options['value']);
          });
          break;
        case 'replaceHTML':
          var selection = this.options['contentWindow'].getSelection();
          var range = this.doc.createRange();
          range.selectNodeContents(this.element);
          selection.addRange(range);
          this.execCommand('insertHTML', options['value']);
          break;
        default: this.execCommand(action, options['value']);
      }
    }
  },

  wrap: function(tagName, newElementCallback, updateElementCallback) {
    var range = this.selections[0];
    var fragment = range.cloneContents();

    if (fragment.containsTags('div table tr td')) {
      this.wrapTextnodes(fragment, tagName, newElementCallback, updateElementCallback);
    } else {
      this.wrapFragment(fragment, newElementCallback, updateElementCallback);
    }
  },

  wrapTextnodes: function(fragment, tagName, newElementCallback, updateElementCallback) {
    var textnodes = fragment.getTextNodes();
    for (var i = 0; i < textnodes.length; ++i) {
      if (textnodes[i].parentNode.tagName != tagName.toUpperCase()) {
        Element.wrap(textnodes[i], newElementCallback.call(this));
      } else {
        updateElementCallback.call(this, textnodes[i].parentNode);
      }
    }

    var wrapper = new Element('div');
    wrapper.appendChild(fragment);

    var html = wrapper.innerHTML;
    this.execCommand('insertHTML', html);
  },

  wrapFragment: function(fragment, newElementCallback) {
    var container = newElementCallback.call(this);
    container.appendChild(fragment);

    var wrapper = new Element('div');
    wrapper.appendChild(container);

    var html = wrapper.innerHTML;
    this.execCommand('insertHTML', html);
  },

  handle: {

    insertHTML: function(action, toolbar, options, callbackOrValue) {
      var value = (Object.isFunction(callbackOrValue)) ?
                  callbackOrValue.call(this, action, toolbar, options) :
                  callbackOrValue;
      this.execCommand('insertHTML', value);
    },

    call: function(action, toolbar, options, callback) {
      callback.call(this, action, toolbar, options);
    }

  }

});
<commit_msg>fix for midas not hiding when clicking escape in the iframe example<commit_after>if (!Midas) var Midas = {};
Midas.Region = Class.create({
  version: 0.2,
  name: null,
  options: {
    configuration: null,
    contentWindow: window,
    inline: false
  },
  previewing: false,

  initialize: function(element, options, name) {
    if (!Midas.version) throw('Midas.Region requires Midas');
    if (!Midas.agentIsCapable()) throw('Midas.Region requires a browser that has contentEditable features');

    this.element = $(element);
    if (!this.element) throw('Unable to locate the element "' + element + '"');
    this.sibling = this.element.previousSibling;

    this.options = Object.extend(Object.clone(this.options), options);
    this.options['configuration'] = this.options['configuration'] || Midas.Config;
    this.config = this.options['configuration'];
    this.name = this.element.getAttribute('id') || name;
    this.doc = this.options['contentWindow'].document;
    this.selections = [];

    this.makeEditable();
    this.setupObservers();
  },

  makeEditable: function() {
    this.element.addClassName('midas-region');

    if (this.element.innerHTML.replace(/^\s+|\s+$/g, "") == '' && Prototype.Browser.Gecko) {
      this.setContents(' ')
    }

    if (this.options['inline']) {
      this.element.setStyle({height: 'auto', minHeight: '20px', minWidth: '20px'});
    } else {
      this.element.setStyle({overflow: 'auto'});
      var width = this.element.getWidth();
      if (width) this.element.setStyle({maxWidth: width + 'px'});
    }
    this.element.contentEditable = true;

    this.doc.execCommand('styleWithCSS', false, false);
    //this.doc.execCommand('enableInlineTableEditing', false, false);
  },

  setupObservers: function() {
    Event.observe(document, 'mouseup', function() {
      if (!this.selecting) return;
      this.selecting = false;
      this.updateSelections();
    }.bind(this));

    Event.observe(this.element, 'focus', function(e) {
      if (this.previewing) return;
      Midas.fire('region', {region: this, name: this.name, event: e});
      if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
    }.bind(this));

    Event.observe(this.element, 'paste', function(e) {
      if (Midas.modal.showing) e.stop();
      var html = this.element.innerHTML;
      setTimeout(function() { this.afterPaste(html); }.bind(this), 1);
    }.bind(this));
    Event.observe(this.element, 'drop', function(e) {
      var html = this.element.innerHTML;
      setTimeout(function() { this.afterPaste(html); }.bind(this), 1);
    }.bind(this));

    Event.observe(this.element, 'mousedown', function() {
      this.selecting = true;
    }.bind(this));
    Event.observe(this.element, 'mouseup', function(e) {
      if (this.previewing) return;
      Midas.fire('region:update', {region: this, name: this.name, event: e});
    }.bind(this));
    Event.observe(this.element, 'click', function(e) {
      if (this.previewing) {
        var element = e.target;
        if (element.tagName == 'A') {
          var uri = element.getAttribute('href');
          var host = uri.match(/^[http:|https:]/) ? uri.split('://')[1].split('/')[0] : false;
          if (host && host != top.location.host && host != top.location.hostname) {
            this.options['contentWindow'].onbeforeunload = Midas.onBeforeUnload;
            top.location.href = uri;
            e.stop();
          }
        }
      } else {
        Midas.fire('region', {region: this, name: this.name, event: e});
        if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
      }
    }.bind(this));

    Event.observe(this.element, 'keydown', function(e) {
      if (Midas.modal.showing && e.keyCode != 27) e.stop();
    }.bind(this));
    Event.observe(this.element, 'keyup', function(e) {
      if (this.previewing) return;
      this.updateSelections();
      Midas.fire('region:update', {region: this, name: this.name, event: e, changed: true});
    }.bind(this));
    Event.observe(this.element, 'keypress', function(e) {
      if (this.previewing) return;
      Midas.fire('region:update', {region: this, name: this.name, event: e});

      switch (e.keyCode) {
        case 9: // tab
          this.selections.each(function(selection) {
            var container = selection.commonAncestorContainer;
            if (container.nodeType == 3) container = container.parentNode;
            if (container.tagName == 'LI' || container.up('li')) {
              e.stop();
              this.handleAction('indent');
            }
          }.bind(this));
          break;
        case 13: // enter
          break;
      }
    }.bind(this));
  },

  setContents: function(content) {
    this.element.innerHTML = content;
  },

  getContents: function() {
    return this.element.innerHTML.replace(/^\s+|\s+$/g, "");
  },

  updateSelections: function() {
    var selection = this.options['contentWindow'].getSelection();
    this.selections = [];

    for (var i = 0; i <= selection.rangeCount - 1; ++i) {
      var range = selection.getRangeAt(i);

      if (range.commonAncestorContainer == this.element ||
          Element.descendantOf(range.commonAncestorContainer, this.element)) {
        this.selections.push(range);
      }
    }
  },

  afterPaste: function(beforeHtml) {
    var pastedRegion = this.element.down('.midas-region');
    if (pastedRegion) {
      var selection = this.options['contentWindow'].getSelection();
      selection.removeAllRanges();

      var range = this.doc.createRange();
      range.selectNode(pastedRegion);
      selection.addRange(range);

      this.execCommand('undo', null);
      this.execCommand('insertHTML', pastedRegion.innerHTML);
    }

    var pastedFromWord = this.element.innerHTML.indexOf('') > -1 || this.element.innerHTML.indexOf('="mso-') > -1 || this.element.innerHTML.indexOf('<o:') > -1;
    if (pastedFromWord) {
      var pasted = beforeHtml.singleDiff(this.element.innerHTML);
      var cleaned = this.sanitizeHtml(pasted);
      try {
        this.doc.execCommand('undo', false, null);
        this.execCommand('insertHTML', cleaned);
      } catch(e) {
        this.setContents(beforeHtml);
        Midas.modal('/midas/sanitizer.html', {
          title: 'HTML Sanitizer',
          afterLoad: function() {
            $('midas_sanitized_content').value = cleaned.replace(/
/g, '\n');
          }
        });
      }
    }
  },

  sanitizeHtml: function(html) {
    var temp = new Element('div').update(html);
    temp.select('style').each(function(style) {
      style.remove();
    });

    return temp.textContent.replace(/\n\n/g, '
').
                            replace(/.*/g, '').
                            replace(/^(
)+|(
\s*)+$/g, '');
  },

  execCommand: function(action, argument) {
    argument = typeof(argument) == 'undefined' ? null : argument;

    var supported = this.doc.execCommand('styleWithCSS', false, false);
    var handled;
    try {
      handled = this.doc.execCommand(action, false, argument);
    } catch(e) {
      Midas.trace(e);
      // Gecko does some interesting things when it fails on indent
      if (action == 'indent') {
        var sibling = this.element.previousSibling;
        if (sibling != this.sibling) sibling.remove();
      }
      handled = true;
    }
    if (!handled && supported) throw('Unknown action "' + action + '"');
  },

  serialize: function() {
    return {name: this.name, content: this.getContents()}
  },

  togglePreview: function() {
    if (this.previewing) {
      this.element.removeClassName('midas-region-preview');
      this.makeEditable();
      this.previewing = false;
    } else {
      this.element.contentEditable = false;
      this.element.addClassName('midas-region-preview');
      this.element.setStyle({height: null, minHeight: null, minWidth: null, maxWidth: null, overflow: null});
      this.element.removeClassName('midas-region');
      this.previewing = true;
    }
  },

  destroy: function() {
    this.element.contentEditable = 'false';
    this.element.blur();
    this.element.removeClassName('midas-region');
  },

  handleAction: function(action, event, toolbar, options) {
    options = options || {};

    if (this.config['behaviors'][action]) {
      var behaviors = this.config['behaviors'][action];

      for (var behavior in behaviors) {
        if (Object.isFunction(this.handle[behavior])) {
          this.handle[behavior].call(this, action, toolbar, options, behaviors[behavior]);

          var sel = window.getSelection();
          this.selections.each(function(selection) {
            //sel.removeRange(selection);
            sel.addRange(selection);
          })
        } else {
          throw('Unknown behavior method "' + behavior + '"');
        }
      }
    } else {
      switch (action) {
        case 'removeformatting':
          this.execCommand('insertHTML', this.selections[0].cloneContents().textContent);
          break;
        case 'style':
          this.wrap('span', function() {
            return new Element('span', {'class': options['value']});
          }, function(element) {
            element.addClassName(options['value']);
          });
          break;
        case 'backcolor':
          this.wrap('font', function() {
            return new Element('font', {style: 'background-color:' + options['value']});
          }, function(element) {
            element.setStyle('background-color:' + options['value']);
          });
          break;
        case 'replaceHTML':
          var selection = this.options['contentWindow'].getSelection();
          var range = this.doc.createRange();
          range.selectNodeContents(this.element);
          selection.addRange(range);
          this.execCommand('insertHTML', options['value']);
          break;
        default: this.execCommand(action, options['value']);
      }
    }
  },

  wrap: function(tagName, newElementCallback, updateElementCallback) {
    var range = this.selections[0];
    var fragment = range.cloneContents();

    if (fragment.containsTags('div table tr td')) {
      this.wrapTextnodes(fragment, tagName, newElementCallback, updateElementCallback);
    } else {
      this.wrapFragment(fragment, newElementCallback, updateElementCallback);
    }
  },

  wrapTextnodes: function(fragment, tagName, newElementCallback, updateElementCallback) {
    var textnodes = fragment.getTextNodes();
    for (var i = 0; i < textnodes.length; ++i) {
      if (textnodes[i].parentNode.tagName != tagName.toUpperCase()) {
        Element.wrap(textnodes[i], newElementCallback.call(this));
      } else {
        updateElementCallback.call(this, textnodes[i].parentNode);
      }
    }

    var wrapper = new Element('div');
    wrapper.appendChild(fragment);

    var html = wrapper.innerHTML;
    this.execCommand('insertHTML', html);
  },

  wrapFragment: function(fragment, newElementCallback) {
    var container = newElementCallback.call(this);
    container.appendChild(fragment);

    var wrapper = new Element('div');
    wrapper.appendChild(container);

    var html = wrapper.innerHTML;
    this.execCommand('insertHTML', html);
  },

  handle: {

    insertHTML: function(action, toolbar, options, callbackOrValue) {
      var value = (Object.isFunction(callbackOrValue)) ?
                  callbackOrValue.call(this, action, toolbar, options) :
                  callbackOrValue;
      this.execCommand('insertHTML', value);
    },

    call: function(action, toolbar, options, callback) {
      callback.call(this, action, toolbar, options);
    }

  }

});
<|endoftext|>


<commit_before>//frontend javascript for chat
console.log('frontend javascript for chat');
var Chat = Class.extend({
    init: function() {
        $('.all-users-container').mCustomScrollbar({
            axis:"y",
            theme:"dark",
            scrollbarPosition: "inside",
            alwaysShowScrollbar: 1,
            mouseWheel:{
                enable: true,
                preventDefault: true
            }
        });

        $(".chat-bar-wrapper").mousewheel(function(event, delta) {
            event.preventDefault();
            event.stopPropagation();
            $('.chat-bar').mCustomScrollbar("scrollTo",(delta < 0) ? '-=80' : '+=80' );
        });

        $('.chat-bar')
            .mCustomScrollbar({
                axis:"x",
                advanced:{
                    autoExpandHorizontalScroll: true,
                    updateOnContentResize: true
                }
            })
    },
    getChatHTML: function(id,username, image){
        var emptyChat = $('.empty-chat').clone(true);
        emptyChat.attr('data-partner-id',id);
        emptyChat.removeClass('empty-chat');
        emptyChat.find('.chat-title').text(username);
        emptyChat.find('img').attr('src',image);

        return emptyChat;
    },
    addChat: function (id, username, avatar) {
        if ($('div[data-partner-id="' + id + '"]').length < 1 && currentUser.id!==id) {
            var chatHtml = this.getChatHTML(id,username, avatar);
            chatHtml.css('display','none');
            $('.chat-bar').find('.chat').parent().append(chatHtml);

            var that = this;
            chatHtml.fadeIn({
                duration:200,
                complete:function(){
                    that.toggleChat(chatHtml);
                }
            });
        } else {
            if(currentUser.id===id){
                utils.displayMessage('You dont need this chat to talk to yourself :)');
            }

            if($('div[data-partner-id="' + id + '"]').length >0){
                utils.displayMessage('You have already opened a chat with this user');
            }
        }
    },
    toggleChat: function(chat){
        var chatBody = chat.find('.chat-body');

        if(chat.hasClass('opened')){
            chatBody.animate({height:0},{
                queue:false,
                complete:function(){
                    chatBody.hide();
                }
            });
        } else {
            chatBody.show();
            chatBody.css('height','30px');
            chatBody.animate({height:250},{queue:false});
        }
        chat.toggleClass('opened');
    },
    sendMessage: function (message,chat) {
        var newMessage = $('
')
            .addClass('chat-message chat-message-sent')
            .html(message);

        chat.find('.chat-messages')
            .append(newMessage);
    },
    receivedMessage: function (data) {
        var newMessage = $('
')
            .addClass('chat-message chat-message-received')
            .html(data.message);

        if($('.chat[data-partner-id="'+data.sender+'"]').length<1){
            var sender = $('.single-user[data-user-id="'+data.sender+'"]'),
                id = sender.attr('data-user-id'),
                username = sender.find('.single-user-username').text(),
                avatar = sender.find('img.single-user-image').attr('src');
            console.log('will add chat',id,username,avatar);

            console.log('.single-user [data-user-id="'+data.sender+'"]');
            console.log($('.single-user [data-user-id="'+data.sender+'"]'));
            this.addChat(id,username, avatar)
        }
        $('.chat[data-partner-id="'+data.sender+'"]')
            .find('.chat-body .chat-messages')
            .append(newMessage);
        //$('.received-messages .panel-body').append(newMessage);
    },
    sentMessage: function (msg,chat) {

    }
});

var chatHandler = new Chat();

$('input').on('keypress', function (e) {
    if (e.which !== 13) {
        return;
    }
    e.preventDefault();
    chatHandler.sendMessage();
});
//
//$('.btn').on('click',function(e){
//    e.preventDefault();
//    chatHandler.sendMessage();
//});

$('.single-user').on('click', function (e) {
    var clicked = $(this),
        id = clicked.attr('data-user-id'),
        username = clicked.find('.single-user-username').text(),
        avatar = clicked.find('img.single-user-image').attr('src');

    chatHandler.addChat(id, username, avatar);

});

$('.chat-bar').on('mouseup','.chat-title',function(){
    var chat = $(this).parent();

    chatHandler.toggleChat(chat);
});

$('.chat-top-bar').on('click','.glyphicon',function(){
    console.log($(this));
    console.log($(this).parent().parent());
    chatHandler.toggleChat($(this).closest('.chat'));
});

$('.chat textarea').on('keydown',function(e){
    if(e.which===13 || e.keyCode===13){
        e.preventDefault();
        var message = $(this).val(),
            receiver = $(this).closest('.chat').attr('data-partner-id');

        $(this).val('');
        var message = message.trim(' ');
        if(message.length>0) {
            chatHandler.sendMessage(message,$(this).closest('.chat'));
            socketHandler.sendMessage(message, receiver);
        }
    }
});

$('.chat .chat-send').on('click',function(){
    var message = $(this).siblings('textarea').val(),
        receiver = $(this).closest('.chat').attr('data-partner-id');

    $(this).siblings('textarea').val('');
    var message = message.trim(' ');
    if(message.length>0) {
        chatHandler.sendMessage(message,$(this).closest('.chat'));
        socketHandler.sendMessage(message, receiver);
    }
});

var $divForNoMatches = $('

').addClass('no-matches'),
    $spanForNoMatches = $('No users found');

$spanForNoMatches.appendTo($divForNoMatches);

$('.users-search-form .form-control').on('keyup',function(e){

    var $searchText = $(this).val(),
        $listOfUsers = $('.all-users').children('div.single-user'),
        counterOfHiddenUsers = 0,
        lenghtOfAllUsers = $listOfUsers.size();
    $.each($listOfUsers, function(){
        var $userName = $(this).children('.single-user-username').html(),
            $this = $(this);
        if ($userName.toLowerCase().indexOf($searchText.toLowerCase()) < 0) {
            $this.hide();
            counterOfHiddenUsers++;
        }
        else{
            $this.show();
        }
    });
    if(counterOfHiddenUsers === lenghtOfAllUsers){
       $('.all-users').append($divForNoMatches);
    }
    else{
        $( "div" ).remove( ".no-matches" );
    }
    counterOfHiddenUsers=0;
});<commit_msg>search-bar adjusted<commit_after>//frontend javascript for chat
console.log('frontend javascript for chat');
var Chat = Class.extend({
    init: function() {
        $('.all-users-container').mCustomScrollbar({
            axis:"y",
            theme:"dark",
            scrollbarPosition: "inside",
            alwaysShowScrollbar: 1,
            mouseWheel:{
                enable: true,
                preventDefault: true
            }
        });

        $(".chat-bar-wrapper").mousewheel(function(event, delta) {
            event.preventDefault();
            event.stopPropagation();
            $('.chat-bar').mCustomScrollbar("scrollTo",(delta < 0) ? '-=80' : '+=80' );
        });

        $('.chat-bar')
            .mCustomScrollbar({
                axis:"x",
                advanced:{
                    autoExpandHorizontalScroll: true,
                    updateOnContentResize: true
                }
            })
    },
    getChatHTML: function(id,username, image){
        var emptyChat = $('.empty-chat').clone(true);
        emptyChat.attr('data-partner-id',id);
        emptyChat.removeClass('empty-chat');
        emptyChat.find('.chat-title').text(username);
        emptyChat.find('img').attr('src',image);

        return emptyChat;
    },
    addChat: function (id, username, avatar) {
        if ($('div[data-partner-id="' + id + '"]').length < 1 && currentUser.id!==id) {
            var chatHtml = this.getChatHTML(id,username, avatar);
            chatHtml.css('display','none');
            $('.chat-bar').find('.chat').parent().append(chatHtml);

            var that = this;
            chatHtml.fadeIn({
                duration:200,
                complete:function(){
                    that.toggleChat(chatHtml);
                }
            });
        } else {
            if(currentUser.id===id){
                utils.displayMessage('You dont need this chat to talk to yourself :)');
            }

            if($('div[data-partner-id="' + id + '"]').length >0){
                utils.displayMessage('You have already opened a chat with this user');
            }
        }
    },
    toggleChat: function(chat){
        var chatBody = chat.find('.chat-body');

        if(chat.hasClass('opened')){
            chatBody.animate({height:0},{
                queue:false,
                complete:function(){
                    chatBody.hide();
                }
            });
        } else {
            chatBody.show();
            chatBody.css('height','30px');
            chatBody.animate({height:250},{queue:false});
        }
        chat.toggleClass('opened');
    },
    sendMessage: function (message,chat) {
        var newMessage = $('
')
            .addClass('chat-message chat-message-sent')
            .html(message);

        chat.find('.chat-messages')
            .append(newMessage);
    },
    receivedMessage: function (data) {
        var newMessage = $('
')
            .addClass('chat-message chat-message-received')
            .html(data.message);

        if($('.chat[data-partner-id="'+data.sender+'"]').length<1){
            var sender = $('.single-user[data-user-id="'+data.sender+'"]'),
                id = sender.attr('data-user-id'),
                username = sender.find('.single-user-username').text(),
                avatar = sender.find('img.single-user-image').attr('src');
            console.log('will add chat',id,username,avatar);

            console.log('.single-user [data-user-id="'+data.sender+'"]');
            console.log($('.single-user [data-user-id="'+data.sender+'"]'));
            this.addChat(id,username, avatar)
        }
        $('.chat[data-partner-id="'+data.sender+'"]')
            .find('.chat-body .chat-messages')
            .append(newMessage);
        //$('.received-messages .panel-body').append(newMessage);
    },
    sentMessage: function (msg,chat) {

    }
});

var chatHandler = new Chat();

$('input').on('keypress', function (e) {
    if (e.which !== 13) {
        return;
    }
    e.preventDefault();
    chatHandler.sendMessage();
});
//
//$('.btn').on('click',function(e){
//    e.preventDefault();
//    chatHandler.sendMessage();
//});

$('.single-user').on('click', function (e) {
    var clicked = $(this),
        id = clicked.attr('data-user-id'),
        username = clicked.find('.single-user-username').text(),
        avatar = clicked.find('img.single-user-image').attr('src');

    chatHandler.addChat(id, username, avatar);

});

$('.chat-bar').on('mouseup','.chat-title',function(){
    var chat = $(this).parent();

    chatHandler.toggleChat(chat);
});

$('.chat-top-bar').on('click','.glyphicon',function(){
    console.log($(this));
    console.log($(this).parent().parent());
    chatHandler.toggleChat($(this).closest('.chat'));
});

$('.chat textarea').on('keydown',function(e){
    if(e.which===13 || e.keyCode===13){
        e.preventDefault();
        var message = $(this).val(),
            receiver = $(this).closest('.chat').attr('data-partner-id');

        $(this).val('');
        var message = message.trim(' ');
        if(message.length>0) {
            chatHandler.sendMessage(message,$(this).closest('.chat'));
            socketHandler.sendMessage(message, receiver);
        }
    }
});

$('.chat .chat-send').on('click',function(){
    var message = $(this).siblings('textarea').val(),
        receiver = $(this).closest('.chat').attr('data-partner-id');

    $(this).siblings('textarea').val('');
    var message = message.trim(' ');
    if(message.length>0) {
        chatHandler.sendMessage(message,$(this).closest('.chat'));
        socketHandler.sendMessage(message, receiver);
    }
});

var $divForNoMatches = $('

').addClass('no-matches'),
    $spanForNoMatches = $('No users found');

$spanForNoMatches.appendTo($divForNoMatches);

$('.users-search-form .form-control').on('keyup',function(e){

    var $searchText = $(this).val(),
        $listOfUsers = $('.all-users').children('div.single-user'),
        counterOfHiddenUsers = 0,
        lenghtOfAllUsers = $listOfUsers.size();
    $.each($listOfUsers, function(){
        var $userName = $(this).children('.single-user-username').html(),
            $this = $(this);
        if ($userName.toLowerCase().indexOf($searchText.toLowerCase()) < 0) {
            $this.hide();
            counterOfHiddenUsers++;
        }
        else{
            $this.show();
        }
    });
    if(counterOfHiddenUsers === lenghtOfAllUsers){
       $('.all-users').append($divForNoMatches);
    }
    else{
        $('div').remove('.no-matches');
    }
    counterOfHiddenUsers=0;
});<|endoftext|>


<commit_before>dojo.provide("voeis.Server");
dojo.require("dojo.store.JsonRest");
dojo.require("dojo.data.ObjectStore");
dojo.require("voeis.store.Projects");

dojo.declare("voeis.Server", null, {
    constructor: function(args){
        this.baseUrl = "";
        dojo.mixin(this, args);
    },
    projectsPath: function() {
        return [this.baseUrl, "projects"].join("/");
    },
    projectPath: function(id) {
        return [this.projectsPath(), id].join("/");
    },
    projectSitesPath: function(projectId) {
        return [this.projectPath(projectId), "sites"].join("/");
    },
    projectSitePath: function(projectId, siteId) {
        return [this.projectSitesPath(projectId), siteId].join("/");
    },

    /** Stores **/
    projects: function() {
        this._projects = this._projects || voeis.store.Projects(new dojo.store.JsonRest({target:this.projectsPath() + "/"}), this);
        return this._projects;
    },
    projectsDataStore: function() {
        return new dojo.data.ObjectStore({objectStore:this.projects()});
    },

    projectSites: function(projectId) {
        this._projectSites = this.projectSites || {};
        this._projectSites[projectId] = this._projectSites[projectId] || new dojo.store.JsonRest({target:this.projectSitesPath(projectId) + "/"});
        return this._projectSites[projectId];
    },
    projectSitesDataStore: function(projectId) {
        return new dojo.data.ObjectStore({objectStore:this.projectSites(projectId)});
    }

});

voeis.Server.DEFAULT = new voeis.Server();

voeis.Server.BackboneSync = function(method, model, success, error) {
    var methodFor = {
        'create': function(model) { return 'add'; },
        'update': function(model) { return 'put'; },
        'read': function(model) {
            return (model instanceof Backbone.Collection) ? 'query' : 'get'
        },
        'delete': function(model) { return 'delete' }
    };

    var argsFor = {
        'add': function(model) {
            return [model.toJSON()];
        },
        'put': function(model) {
            return [model.id, model.toJSON()];
        },
        'read': function(model) {
            return [model.id];
        },
        'delete': function(model) {
            return [model.id];
        },
        'query': function(model) {
            return [model.query || ""];
        }
    };

    var objectStore = model.objectStore;

    var storeMethod = methodFor[method](model);
    var storeArgs = argsFor[storeMethod](model);

    var result = objectStore[storeMethod].apply(objectStore, storeArgs);
    result.addCallbacks(success, error);
}
<commit_msg>wrap store returned from voeis.Server#projectSites() with voeis.store.Sites()<commit_after>dojo.provide("voeis.Server");
dojo.require("dojo.store.JsonRest");
dojo.require("dojo.data.ObjectStore");
dojo.require("voeis.store.Projects");
dojo.require("voeis.store.Sites");

dojo.declare("voeis.Server", null, {
    constructor: function(args){
        this.baseUrl = "";
        dojo.mixin(this, args);
    },
    projectsPath: function() {
        return [this.baseUrl, "projects"].join("/");
    },
    projectPath: function(id) {
        return [this.projectsPath(), id].join("/");
    },
    projectSitesPath: function(projectId) {
        return [this.projectPath(projectId), "sites"].join("/");
    },
    projectSitePath: function(projectId, siteId) {
        return [this.projectSitesPath(projectId), siteId].join("/");
    },

    /** Stores **/
    projects: function() {
        this._projects = this._projects || voeis.store.Projects(new dojo.store.JsonRest({target:this.projectsPath() + "/"}), this);
        return this._projects;
    },
    projectsDataStore: function() {
        return new dojo.data.ObjectStore({objectStore:this.projects()});
    },

    projectSites: function(projectId) {
        this._projectSites = this.projectSites || {};
        this._projectSites[projectId] = this._projectSites[projectId] || voeis.store.Sites(new dojo.store.JsonRest({target:this.projectSitesPath(projectId) + "/"}), projectId, this);
        return this._projectSites[projectId];
    },
    projectSitesDataStore: function(projectId) {
        return new dojo.data.ObjectStore({objectStore:this.projectSites(projectId)});
    }

});

voeis.Server.DEFAULT = new voeis.Server();

voeis.Server.BackboneSync = function(method, model, success, error) {
    var methodFor = {
        'create': function(model) { return 'add'; },
        'update': function(model) { return 'put'; },
        'read': function(model) {
            return (model instanceof Backbone.Collection) ? 'query' : 'get'
        },
        'delete': function(model) { return 'delete' }
    };

    var argsFor = {
        'add': function(model) {
            return [model.toJSON()];
        },
        'put': function(model) {
            return [model.id, model.toJSON()];
        },
        'read': function(model) {
            return [model.id];
        },
        'delete': function(model) {
            return [model.id];
        },
        'query': function(model) {
            return [model.query || ""];
        }
    };

    var objectStore = model.objectStore;

    var storeMethod = methodFor[method](model);
    var storeArgs = argsFor[storeMethod](model);

    var result = objectStore[storeMethod].apply(objectStore, storeArgs);
    result.addCallbacks(success, error);
}
<|endoftext|>


<commit_before>define(function(require, exports, module) {
  var dialog = require('MDialog');
  module.exports = Backbone.View.extend({
    el: '#main',
    template: '',
    Model: Backbone.Model.extend(),
    Collection: function() {
      return {collection: new Backbone.Collection.extend()};
    },
    ItemView: function(opt) {
      return (new Backbone.View.extend()(opt));
    },
    FormView: function(opt) {
      return (new Backbone.View.extend()(opt));
    },
    pageIndex: 0,
    pageSize: 10,
    isEnd: false,
    currentColumn: 0,
    columnCnt: 1,
    isLoad: true,
    initialize: function(options) {
      this.rel = options.rel;
      this.$el.html( this.template );
      if(this.columnCnt > 1) {
        var col = '

',
          list = this.$el.find('#list');
        for(var i=0; i<this.columnCnt; i++) {
          list.append(col);
        }
      }

      this.list = this.Collection().collection;
      this.listenTo(this.list, 'add', this.addOne);
      this.listenTo(this.list, 'reset', this.addAll);
      this.listenTo(this.list, 'all', this.render);

      this.fetch();
    },
    events: {
      'click .btn-create': 'onCreate',
      'click .btn-more': 'onLoad',
      'click .btn-back': 'onBack'
    },
    fetch: function(option) {
      option = option || {};
      var self = this;
      if(!this.isEnd) {
        var list = this.Collection().collection;
        var data = {
          index: this.pageIndex,
          size: this.pageSize
        };
        if(this.rel) data.rel = this.rel;
        list.fetch({
          data: data,
          success: function(results, resp, opt) {
            if(results.length < self.pageSize) {
              self.isEnd = true;
              self.$el.find('.btn-more').addClass('disabled').hide();
            }
            self.pageIndex++;
            if(results.length > 0) {
              self.list.add(results.models);
            }
            if(option.success && _.isFunction(option.success)) option.success(results, resp, opt);
          },
          error: function(obj, resp, opt) {
            dialog.toast(resp.responseText);
            if(option.error && _.isFunction(option.error)) option.error(obj, resp, opt);
          }
        });
      }
    },
    addOne: function(model) {
      var view = this.ItemView({
        model: model
      });
      if(this.columnCnt > 1) {
        if(this.isLoad) {
          this.$el.find('#list > .col').eq(this.currentColumn%this.columnCnt).append(view.render().el);
        } else {
          this.$el.find('#list > .col').eq(this.currentColumn%this.columnCnt).prepend(view.render().el);
        }
        this.currentColumn++;
      } else {
        this.$el.find('#list').append(view.render().el);
      }
      this.isLoad = true;
    },
    addAll: function() {
      this.list.each(this.addOne, this);
    },
    onCreate: function(e) {
      var self = this;
      var model = new this.Model;
      model.collection = this.list;
      var formView = this.FormView({
        model: model
      });
      require('util/BBPopup')({
        content: formView.render().el,
        buttons: [{
          label: '保存',
          action: function(e, popup) {
            formView.submit({
              success: function(obj, resp, opt) {
                self.isLoad = false;
                self.list.add(formView.model);
                popup.close();
              }
            });
          }
        }, {
          label: '取消',
          cssClass: 'btn-flat'
        }]
      }).open();
    },
    onLoad: function(e) {
      this.fetch();
    },
    onBack: function(e) {
      window.history.back();
    }
  });
});<commit_msg>增强rel，添加relAttrName属性支持<commit_after>define(function(require, exports, module) {
  var dialog = require('MDialog');
  module.exports = Backbone.View.extend({
    el: '#main',
    template: '',
    Model: Backbone.Model.extend(),
    Collection: function() {
      return {collection: new Backbone.Collection.extend()};
    },
    ItemView: function(opt) {
      return (new Backbone.View.extend()(opt));
    },
    FormView: function(opt) {
      return (new Backbone.View.extend()(opt));
    },
    pageIndex: 0,
    pageSize: 10,
    isEnd: false,
    currentColumn: 0,
    columnCnt: 1,
    isLoad: true,
    rel: null,
    relAttrName: '',
    initialize: function(options) {
      this.rel = options.rel;
      this.$el.html( this.template );
      if(this.columnCnt > 1) {
        var col = '

',
          list = this.$el.find('#list');
        for(var i=0; i<this.columnCnt; i++) {
          list.append(col);
        }
      }

      this.list = this.Collection().collection;
      this.listenTo(this.list, 'add', this.addOne);
      this.listenTo(this.list, 'reset', this.addAll);
      this.listenTo(this.list, 'all', this.render);

      this.fetch();
    },
    events: {
      'click .btn-create': 'onCreate',
      'click .btn-more': 'onLoad',
      'click .btn-back': 'onBack'
    },
    fetch: function(option) {
      option = option || {};
      var self = this;
      if(!this.isEnd) {
        var list = this.Collection().collection;
        list.fetch({
          data: {
            index: this.pageIndex,
            size: this.pageSize,
            rel: this.rel
          },
          success: function(results, resp, opt) {
            if(results.length < self.pageSize) {
              self.isEnd = true;
              self.$el.find('.btn-more').addClass('disabled').hide();
            }
            self.pageIndex++;
            if(results.length > 0) {
              self.list.add(results.models);
            }
            if(option.success && _.isFunction(option.success)) option.success(results, resp, opt);
          },
          error: function(obj, resp, opt) {
            dialog.toast(resp.responseText);
            if(option.error && _.isFunction(option.error)) option.error(obj, resp, opt);
          }
        });
      }
    },
    addOne: function(model) {
      var view = this.ItemView({
        model: model
      });
      if(this.columnCnt > 1) {
        if(this.isLoad) {
          this.$el.find('#list > .col').eq(this.currentColumn%this.columnCnt).append(view.render().el);
        } else {
          this.$el.find('#list > .col').eq(this.currentColumn%this.columnCnt).prepend(view.render().el);
        }
        this.currentColumn++;
      } else {
        this.$el.find('#list').append(view.render().el);
      }
      this.isLoad = true;
    },
    addAll: function() {
      this.list.each(this.addOne, this);
    },
    onCreate: function(e) {
      var self = this;
      var attrs = {};
      if(this.rel && this.relAttrName) attrs[this.relAttrName] = this.rel;
      var model = new this.Model(attrs);
      model.collection = this.list;
      var formView = this.FormView({
        model: model
      });
      require('util/BBPopup')({
        content: formView.render().el,
        buttons: [{
          label: '保存',
          action: function(e, popup) {
            formView.submit({
              success: function(obj, resp, opt) {
                self.isLoad = false;
                self.list.add(formView.model);
                popup.close();
              }
            });
          }
        }, {
          label: '取消',
          cssClass: 'btn-flat'
        }]
      }).open();
    },
    onLoad: function(e) {
      this.fetch();
    },
    onBack: function(e) {
      window.history.back();
    }
  });
});<|endoftext|>


<commit_before>const React = require("react");
const { DOM: dom, PropTypes } = React;
const ImPropTypes = require("react-immutable-proptypes");
const { connect } = require("react-redux");
const { bindActionCreators } = require("redux");
const Isvg = React.createFactory(require("react-inlinesvg"));
const { getSelectedSource, getSourceTabs } = require("../selectors");
const { endTruncateStr } = require("../utils/utils");
const classnames = require("classnames");
const actions = require("../actions");
const { isEnabled } = require("../feature");

require("./SourceTabs.css");
require("./Dropdown.css");

/**
 * TODO: this is a placeholder function
 */
function getFilename(url) {
  if (!url) {
    return "";
  }

  let name = url;
  const m = url.toString().match(/.*\/(.+?\..*$)/);
  if (m && m.length > 1) {
    name = m[1];
  }

  return endTruncateStr(name, 50);
}

/*
 * Finds the hidden tabs by comparing the tabs' top offset.
 * hidden tabs will have a great top offset.
 *
 * @param sourceTabs Immutable.list
 * @param sourceTabEls HTMLCollection
 *
 * @returns Immutable.list
 */
function getHiddenTabs(sourceTabs, sourceTabEls) {
  sourceTabEls = [].slice.call(sourceTabEls);
  function getTopOffset() {
    const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
    return Math.min(...topOffsets);
  }

  const tabTopOffset = getTopOffset();
  return sourceTabs.filter((tab, index) => {
    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset;
  });
}

/*
 * Get the last visible tab index so that we can replace the last
 * tab with the newly selected source.
 */
function getLastVisibleTabIndex(sourceTabs, sourceTabEls) {
  const hiddenTabs = getHiddenTabs(sourceTabs, sourceTabEls);
  const firstHiddenTab = hiddenTabs.first();
  const firstHiddenTabIndex = sourceTabs.indexOf(firstHiddenTab);
  return firstHiddenTabIndex - 1;
}

const SourceTabs = React.createClass({
  propTypes: {
    sourceTabs: ImPropTypes.list,
    selectedSource: ImPropTypes.map,
    selectSource: PropTypes.func.isRequired,
    closeTab: PropTypes.func.isRequired
  },

  displayName: "SourceTabs",

  getInitialState() {
    return {
      dropdownShown: false,
      hiddenSourceTabs: null
    };
  },

  componentDidUpdate() {
    this.updateHiddenSourceTabs(this.props.sourceTabs);
  },

  /*
   * Updates the hiddenSourceTabs state, by
   * finding the source tabs who have wrapped and are not on the top row.
   */
  updateHiddenSourceTabs(sourceTabs) {
    if (!this.refs.sourceTabs) {
      return;
    }

    const sourceTabEls = this.refs.sourceTabs.children;
    const hiddenSourceTabs = getHiddenTabs(sourceTabs, sourceTabEls);

    if (!hiddenSourceTabs.equals(this.state.hiddenSourceTabs)) {
      this.setState({ hiddenSourceTabs });
    }
  },

  toggleSourcesDropdown(e) {
    this.setState({
      dropdownShown: !this.state.dropdownShown,
    });
  },

  renderSourcesDropdown() {
    if (!this.state.hiddenSourceTabs) {
      return dom.div({});
    }

    return dom.div({
      className: "sources-dropdown dropdown",
      ref: "sourcesDropdown",
      style: { display: (this.state.dropdownShown ? "block" : "none") }
    },
      dom.ul({}, this.state.hiddenSourceTabs.map(this.renderDropdownSource))
    );
  },

  renderDropdownSource(source) {
    const { selectSource, sourceTabs } = this.props;
    const url = source && source.get("url");
    const filename = getFilename(url);
    const sourceTabEls = this.refs.sourceTabs.children;

    return dom.li({
      key: source.get("id"),
      onClick: () => {
        const tabIndex = getLastVisibleTabIndex(sourceTabs, sourceTabEls);
        selectSource(source.get("id"), { position: tabIndex });
        this.toggleSourcesDropdown();
      }
    }, filename);
  },

  renderSourcesDropdownButon() {
    const hiddenSourceTabs = this.state.hiddenSourceTabs;
    if (!hiddenSourceTabs || hiddenSourceTabs.size == 0) {
      return dom.div({});
    }

    return dom.span(
      {
        className: "subsettings",
        onClick: this.toggleSourcesDropdown
      },
      dom.img({ src: "images/subSettings.svg" })
    );
  },

  renderTabs() {
    const sourceTabs = this.props.sourceTabs;
    return dom.div(
      { className: "source-tabs", ref: "sourceTabs" },
      sourceTabs.map(this.renderTab)
    );
  },

  renderTab(source) {
    const { selectedSource, selectSource, closeTab } = this.props;
    const url = source && source.get("url");
    const filename = getFilename(url);
    const active = source.get("id") == selectedSource.get("id");

    function onClickClose(ev) {
      ev.stopPropagation();
      closeTab(source.get("id"));
    }

    return dom.div(
      {
        className: classnames("source-tab", { active }),
        key: source.get("id"),
        onClick: () => selectSource(source.get("id"))
      },
      dom.div({ className: "filename" }, filename),
      dom.div(
        { onClick: onClickClose },
        Isvg({
          className: "close-btn",
          src: "images/close.svg",
        })
      )
    );
  },

  render() {
    if (!isEnabled("features.tabs")) {
      return dom.div({ className: "source-header" });
    }

    return dom.div({ className: "source-header" },
      this.renderSourcesDropdown(),
      this.renderTabs(),
      this.renderSourcesDropdownButon()
    );
  }
});

module.exports = connect(
  state => ({
    selectedSource: getSelectedSource(state),
    sourceTabs: getSourceTabs(state)
  }),
  dispatch => bindActionCreators(actions, dispatch)
)(SourceTabs);
<commit_msg>Add full URL as tab title (#385)<commit_after>const React = require("react");
const { DOM: dom, PropTypes } = React;
const ImPropTypes = require("react-immutable-proptypes");
const { connect } = require("react-redux");
const { bindActionCreators } = require("redux");
const Isvg = React.createFactory(require("react-inlinesvg"));
const { getSelectedSource, getSourceTabs } = require("../selectors");
const { endTruncateStr } = require("../utils/utils");
const classnames = require("classnames");
const actions = require("../actions");
const { isEnabled } = require("../feature");

require("./SourceTabs.css");
require("./Dropdown.css");

/**
 * TODO: this is a placeholder function
 */
function getFilename(url) {
  if (!url) {
    return "";
  }

  let name = url;
  const m = url.toString().match(/.*\/(.+?\..*$)/);
  if (m && m.length > 1) {
    name = m[1];
  }

  return endTruncateStr(name, 50);
}

/*
 * Finds the hidden tabs by comparing the tabs' top offset.
 * hidden tabs will have a great top offset.
 *
 * @param sourceTabs Immutable.list
 * @param sourceTabEls HTMLCollection
 *
 * @returns Immutable.list
 */
function getHiddenTabs(sourceTabs, sourceTabEls) {
  sourceTabEls = [].slice.call(sourceTabEls);
  function getTopOffset() {
    const topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
    return Math.min(...topOffsets);
  }

  const tabTopOffset = getTopOffset();
  return sourceTabs.filter((tab, index) => {
    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset;
  });
}

/*
 * Get the last visible tab index so that we can replace the last
 * tab with the newly selected source.
 */
function getLastVisibleTabIndex(sourceTabs, sourceTabEls) {
  const hiddenTabs = getHiddenTabs(sourceTabs, sourceTabEls);
  const firstHiddenTab = hiddenTabs.first();
  const firstHiddenTabIndex = sourceTabs.indexOf(firstHiddenTab);
  return firstHiddenTabIndex - 1;
}

const SourceTabs = React.createClass({
  propTypes: {
    sourceTabs: ImPropTypes.list,
    selectedSource: ImPropTypes.map,
    selectSource: PropTypes.func.isRequired,
    closeTab: PropTypes.func.isRequired
  },

  displayName: "SourceTabs",

  getInitialState() {
    return {
      dropdownShown: false,
      hiddenSourceTabs: null
    };
  },

  componentDidUpdate() {
    this.updateHiddenSourceTabs(this.props.sourceTabs);
  },

  /*
   * Updates the hiddenSourceTabs state, by
   * finding the source tabs who have wrapped and are not on the top row.
   */
  updateHiddenSourceTabs(sourceTabs) {
    if (!this.refs.sourceTabs) {
      return;
    }

    const sourceTabEls = this.refs.sourceTabs.children;
    const hiddenSourceTabs = getHiddenTabs(sourceTabs, sourceTabEls);

    if (!hiddenSourceTabs.equals(this.state.hiddenSourceTabs)) {
      this.setState({ hiddenSourceTabs });
    }
  },

  toggleSourcesDropdown(e) {
    this.setState({
      dropdownShown: !this.state.dropdownShown,
    });
  },

  renderSourcesDropdown() {
    if (!this.state.hiddenSourceTabs) {
      return dom.div({});
    }

    return dom.div({
      className: "sources-dropdown dropdown",
      ref: "sourcesDropdown",
      style: { display: (this.state.dropdownShown ? "block" : "none") }
    },
      dom.ul({}, this.state.hiddenSourceTabs.map(this.renderDropdownSource))
    );
  },

  renderDropdownSource(source) {
    const { selectSource, sourceTabs } = this.props;
    const url = source && source.get("url");
    const filename = getFilename(url);
    const sourceTabEls = this.refs.sourceTabs.children;

    return dom.li({
      key: source.get("id"),
      onClick: () => {
        const tabIndex = getLastVisibleTabIndex(sourceTabs, sourceTabEls);
        selectSource(source.get("id"), { position: tabIndex });
        this.toggleSourcesDropdown();
      }
    }, filename);
  },

  renderSourcesDropdownButon() {
    const hiddenSourceTabs = this.state.hiddenSourceTabs;
    if (!hiddenSourceTabs || hiddenSourceTabs.size == 0) {
      return dom.div({});
    }

    return dom.span(
      {
        className: "subsettings",
        onClick: this.toggleSourcesDropdown
      },
      dom.img({ src: "images/subSettings.svg" })
    );
  },

  renderTabs() {
    const sourceTabs = this.props.sourceTabs;
    return dom.div(
      { className: "source-tabs", ref: "sourceTabs" },
      sourceTabs.map(this.renderTab)
    );
  },

  renderTab(source) {
    const { selectedSource, selectSource, closeTab } = this.props;
    const url = source && source.get("url");
    const filename = getFilename(url);
    const active = source.get("id") == selectedSource.get("id");

    function onClickClose(ev) {
      ev.stopPropagation();
      closeTab(source.get("id"));
    }

    return dom.div(
      {
        className: classnames("source-tab", { active }),
        key: source.get("id"),
        onClick: () => selectSource(source.get("id")),
        title: url
      },
      dom.div({ className: "filename" }, filename),
      dom.div(
        { onClick: onClickClose },
        Isvg({
          className: "close-btn",
          src: "images/close.svg",
        })
      )
    );
  },

  render() {
    if (!isEnabled("features.tabs")) {
      return dom.div({ className: "source-header" });
    }

    return dom.div({ className: "source-header" },
      this.renderSourcesDropdown(),
      this.renderTabs(),
      this.renderSourcesDropdownButon()
    );
  }
});

module.exports = connect(
  state => ({
    selectedSource: getSelectedSource(state),
    sourceTabs: getSourceTabs(state)
  }),
  dispatch => bindActionCreators(actions, dispatch)
)(SourceTabs);
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 20.08.2015.
 */
import angular from "angular";
import { attach, get, getIn } from "../utils.js";
import { connect2Redux } from "../won-utils.js";
import { getPostUriFromRoute } from "../selectors/general-selectors.js";
import {
  generateFullNeedTypesLabel,
  generateShortNeedTypesLabel,
  generateNeedMatchingContext,
} from "../need-utils.js";
import { actionCreators } from "../actions/actions.js";
import postContextDropDownModule from "../components/post-context-dropdown.js";
import shareDropdownModule from "../components/share-dropdown.js";
import * as needUtils from "../need-utils.js";

import "style/_visitor-title-bar.scss";

const serviceDependencies = ["$ngRedux", "$scope"];
function genComponentConf() {
  let template = `

                    <won-square-image
                        uri="self.post.get('uri')">
                    </won-square-image>
                    <hgroup>

{{ self.post.get('humanReadable') }}

                        {{ self.personaName }}

                          Group Chat

                          Group Chat enabled

{{ self.shortTypesLabel }}{{ self.matchingContext }}

{{ self.fullTypesLabel }}

                    </hgroup>

            <won-share-dropdown need-uri="self.post.get('uri')"></won-share-dropdown>
            <won-post-context-dropdown need-uri="self.post.get('uri')"></won-post-context-dropdown>

    `;

  class Controller {
    constructor() {
      attach(this, serviceDependencies, arguments);
      window.vtb4dbg = this;

      const selectFromState = state => {
        const postUri = getPostUriFromRoute(state);
        const post = state.getIn(["needs", postUri]);

        const personaUri = get(post, "heldBy");
        const persona = personaUri && getIn(state, ["needs", personaUri]);
        const personaName = get(persona, "humanReadable");

        return {
          postUri,
          post,
          personaName,
          isGroupChatEnabled: needUtils.hasChatFacet(post),
          isChatEnabled: needUtils.hasGroupFacet(post),
          fullTypesLabel: post && generateFullNeedTypesLabel(post),
          shortTypesLabel: post && generateShortNeedTypesLabel(post),
          matchingContext: post && generateNeedMatchingContext(post),
          shouldShowRdf: state.getIn(["view", "showRdf"]),
        };
      };
      connect2Redux(selectFromState, actionCreators, [], this);
    }
    back() {
      window.history.back();
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: { item: "=" },
    template: template,
  };
}

export default angular
  .module("won.owner.components.visitorTitleBar", [
    postContextDropDownModule,
    shareDropdownModule,
  ])
  .directive("wonVisitorTitleBar", genComponentConf).name;
<commit_msg>Fixes #2698<commit_after>/**
 * Created by ksinger on 20.08.2015.
 */
import angular from "angular";
import { attach, get, getIn } from "../utils.js";
import { connect2Redux } from "../won-utils.js";
import { getPostUriFromRoute } from "../selectors/general-selectors.js";
import {
  generateFullNeedTypesLabel,
  generateShortNeedTypesLabel,
  generateNeedMatchingContext,
} from "../need-utils.js";
import { actionCreators } from "../actions/actions.js";
import postContextDropDownModule from "../components/post-context-dropdown.js";
import shareDropdownModule from "../components/share-dropdown.js";
import * as needUtils from "../need-utils.js";

import "style/_visitor-title-bar.scss";

const serviceDependencies = ["$ngRedux", "$scope"];
function genComponentConf() {
  let template = `

                    <won-square-image
                        uri="self.post.get('uri')">
                    </won-square-image>
                    <hgroup>

{{ self.post.get('humanReadable') }}

                        {{ self.personaName }}

                          Group Chat

                          Group Chat enabled

{{ self.shortTypesLabel }}{{ self.matchingContext }}

{{ self.fullTypesLabel }}

                    </hgroup>

            <won-share-dropdown need-uri="self.post.get('uri')"></won-share-dropdown>
            <won-post-context-dropdown need-uri="self.post.get('uri')"></won-post-context-dropdown>

    `;

  class Controller {
    constructor() {
      attach(this, serviceDependencies, arguments);
      window.vtb4dbg = this;

      const selectFromState = state => {
        const postUri = getPostUriFromRoute(state);
        const post = state.getIn(["needs", postUri]);

        const personaUri = get(post, "heldBy");
        const persona = personaUri && getIn(state, ["needs", personaUri]);
        const personaName = get(persona, "humanReadable");

        return {
          postUri,
          post,
          personaName,
          isGroupChatEnabled: needUtils.hasGroupFacet(post),
          isChatEnabled: needUtils.hasChatFacet(post),
          fullTypesLabel: post && generateFullNeedTypesLabel(post),
          shortTypesLabel: post && generateShortNeedTypesLabel(post),
          matchingContext: post && generateNeedMatchingContext(post),
          shouldShowRdf: state.getIn(["view", "showRdf"]),
        };
      };
      connect2Redux(selectFromState, actionCreators, [], this);
    }
    back() {
      window.history.back();
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: { item: "=" },
    template: template,
  };
}

export default angular
  .module("won.owner.components.visitorTitleBar", [
    postContextDropDownModule,
    shareDropdownModule,
  ])
  .directive("wonVisitorTitleBar", genComponentConf).name;
<|endoftext|>