<commit_before>var detailsLat, detailsLon;
var requests = new Array();

// Handlebars helpers
Handlebars.registerHelper('carouselimages', function(spacedata) {
    var space_id = spacedata.id;
    var elements = new Array;
    if (spacedata.images.length > 0) {
        for (i=0; i < spacedata.images.length; i++) {
            image_id = spacedata.images[i].id;
            image_url = "background:url(/space/" + space_id + "/image/" + image_id + "/thumb/constrain/width:500)";
            div_string = "

"
            elements.push(div_string);
        }
    } else {
        image_url = "background:url(/static/img/placeholder_noImage_bw.png)";
        div_string = "

"
        elements.push(div_string);
    }
    return new Handlebars.SafeString(elements.join('\n'));
});

Handlebars.registerHelper('compare', function(lvalue, rvalue, options) {

    if (arguments.length < 3)
        throw new Error("Handlerbars Helper 'compare' needs 2 parameters");

    operator = options.hash.operator || "==";

    var operators = {
        '==':       function(l,r) { return l == r; },
        '===':      function(l,r) { return l === r; },
        '!=':       function(l,r) { return l != r; },
        '<':        function(l,r) { return l < r; },
        '>':        function(l,r) { return l > r; },
        '<=':       function(l,r) { return l <= r; },
        '>=':       function(l,r) { return l >= r; },
        'typeof':   function(l,r) { return typeof l == r; }
    }

    if (!operators[operator])
        throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);

    var result = operators[operator](lvalue,rvalue);

    if( result ) {
        return options.fn(this);
    } else {
        return options.inverse(this);
    }

});

Handlebars.registerHelper('formatHours', function(hours) {
    //tomorrow_starts_at_midnight = true;
    //tomorrow_is_24_hours =
    //if (start_time[0] == 0 && start_time[1] == 0 && end_time[0] == 23 && end_time[1] == 59 && tomorrow_starts_at_midnight && !tomorrow_is_24_hours && tomorrows_hour > 3) {
        //dsf
    //}
    var formatted = {};
    $.each(hours, function(day) {
        if (hours[day].length > 0) {
            $.each(hours[day], function() {
                this[0] = this[0].replace(/^0+/, '');
                this[1] = this[1].replace(/^0+/, '');
            });
            dayMarker = day.charAt(0);
            dayMarker = dayMarker.toUpperCase();
            // Show two characters for Th, Sa, Su
            if (dayMarker == 'T' && day.charAt(1) == 'h' || dayMarker == 'S' && day.charAt(1) == 'a' || dayMarker == 'S' && day.charAt(1) == 'u') {
                dayMarker += day.charAt(1);
            }

            formatted[dayMarker] = to12Hour(hours[day]).join(", ");
        }
    });
    formatted = sortDays(formatted);
    formatted = groupHours(formatted);
    return new Handlebars.SafeString(formatted.join("
"));
});

Handlebars.registerHelper('alphaOptGroupsHTML', function(list) {
    list.sort();
    firstletter = null;
    out = new Array;
    for (var i=0; i < list.length; i++) {
        if (list[i][0] == firstletter) {
            out.push('<option value="'+list[i]+'">'+list[i]+'</option>');
        } else {
            if (firstletter != null) {
                out.push('</optgroup>');
            }
            firstletter = list[i][0];
            out.push('<optgroup label="'+firstletter+'">');
            out.push('<option value="'+list[i]+'">'+list[i]+'</option>');
        }
    }
    out.push('</optgroup>');
    return new Handlebars.SafeString(out.join(''));
});

function groupHours(days) {
    var days1 = [];
    var hours = [];
    var days2 = [];
    var daycount =0;
    var hourscount = 0;
    var final_hours = [];
    var final_hours_count=0;

    for(var i=0;i < days.length; i++) {
        var split = days[i].split(": ");
        var day = split[0];
        var hours1 = split[1].split(", ");
        for(hour in hours1) {
            days1[daycount]=day;
            if(day == "M") {
                days2[daycount]=0;
            }else if(day == "T") {
                days2[daycount]=1;
            }else if(day == "W") {
                days2[daycount]=2;
            }else if(day == "Th") {
                days2[daycount]=3;
            }else if(day == "F") {
                days2[daycount]=4;
            }else if(day == "Sa") {
                days2[daycount]=5;
            }else if(day == "Su") {
                days2[daycount]=6;
            }
            hours[hourscount]= hours1[hour];
            daycount++;
            hourscount++;
        }

    }

    for(var i=0; i<hours.length; i++) {
        var hour = hours[i].split(" - ");

        if(hours[i] != "Open 24 Hours"&& hour[1]=="Midnight") {
            var next_day = "null";
            if(i != hours.length-1) {
                for( var j=i+1; j<hours.length; j++) {
                    var new_hour=hours[j].split(" - ");
                    if((days2[j]-days2[i])> 1) {
                        break;
                    }
                    if((days2[j]-days2[i])== 1 && new_hour[0]=="12AM") {
                        hour[1]=new_hour[1];
                        hours[j]="null";
                        break;
                    }
                }
                hours[i]=hour[0]+" - "+ hour[1];
            }else if( days1[i] == "Su") {
                for( var j=0; j<hours.length; j++) {
                    var new_hour=hours[j].split(" - ");
                    if((days2[j]-days2[i])!= -6) {
                        break;
                    }
                    if((days2[j]-days2[i])==-6 && new_hour[0]=="12AM") {
                        hour[1]=new_hour[1];
                        hours[j]="null";
                        break;
                    }
                }
                hours[i]=hour[0]+" - "+ hour[1];
            }
        }
    }

    for(var i=0; i<hours.length; i++) {
        if (hours[i] != "null" && i != hours.length-1) {
            for(var j =i+1; j<hours.length; j++) {

                if(hours[i]==hours[j]) {
                    days1[i]+= ", "+days1[j];
                    hours[j]="null";
                }
            }
        }
    }
    for(var i=0; i<hours.length; i++) {
        if( hours[i] != "null") {
            final_hours[final_hours_count]=days1[i]+": "+ hours[i];
            final_hours_count++;
        }
    }
    return final_hours;
}

function to12Hour(day) {
    var retData = [];
    for (var j=0; j<day.length; j++) {
        var data = [ day[j][0], day[j][1] ];
        for (var i=0; i<data.length; i++) {
            time = data[i].split(":");
            if(time[0]=="23" & time[1] == "59") {
                data[i] = "Midnight";
            }
            else if (time[0] =="12" & time[1] =="00") {
                data[i] = "Noon";
            }else {
                if (time[0] > 12) {
                    time[0] -= 12;

                    time[1] += "PM";
                }
                else if (time[0] < 1) {
                    time[0] = 12;
                    time[1] += "AM";
                }
                else {
                    time[1] += "AM";
                }
                if (time[1] == "00AM") {
                    data[i] = time[0];
                    data[i] += "AM";
                } else if (time[1] == "00PM") {
                    data[i] = time[0];
                    data[i] += "PM"
                }else {
                    data[i] = time.join(":");
                }
            }
        }
        if(data[0]=="12AM" & data[1]=="Midnight") {
            retData[j]="Open 24 Hours";
        }else {
            retData[j]=data[0] +" - " +data[1];
        }
    }
    return retData;
}

function sortDays(days) {
    var ordered = [];
    order = ["M", "T", "W", "Th", "F", "Sa", "Su"];
    $.each(order, function(day) {
        if (days[order[day]]) {
            ordered.push(order[day] +": " +days[order[day]] );
        }
    });
    return ordered;
}

function default_open_at_filter() {
    // set the default open_at filter to close to now
    var weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    var date = new Date();
    var hour = date.getHours();
    var min = date.getMinutes();

    if (min < 16) {
        min = "00";
    }else if (min < 46) {
        min = "30";
    }else {
        min = "00";
        hour++;
    }

    if (hour > 11) {
        $("#ampm-from").val("PM");
    }else {
        $("#ampm-from").val("AM");
    }
    if (hour > 12) {
        hour = hour-12;
    }
    hour = ""+hour+":"+min;
    $("#day-from").val(weekdays[date.getDay()]);
    $("#hour-from").val(hour);
}

function format_location_filter(data) {
    var source = $('#building_list').html();
    var template = Handlebars.compile(source);
    $('#building_list_container').html(template({data: data}));
}

function get_location_buildings() {
    // populate the location filter list
    url = '/buildings';
    if (window.default_location != null) {
        url = url + '?campus=' + window.default_location;
    }
    $.ajax({
        url: url,
        success: format_location_filter
    });

}

// Found at http://stackoverflow.com/questions/476679/preloading-images-with-jquery
function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('')[0].src = this;
        // Alternatively you could use:
        // (new Image()).src = this;
    });
}

function reset_location_filter() {
    $('#entire_campus').prop('checked', true);
    $('#entire_campus').parent().removeClass("selected");
    $('#e9.building-location').children().children().first()[0].selected = true;
    $('#building_list_container').hide();
    $('#building_list_input').parent().removeClass("selected");
    $('#building_list_container').children().children().children(".select2-search-choice").remove();
    $('#building_list_container').children().children().children().children().val('Select building(s)');
    $('#building_list_container').children().children().children().children().attr('style', "");
    run_custom_search();
}

(function(g){

	$(document).ready(function() {

        var pinimgs = ['/static/img/pins/pin00.png', '/static/img/pins/pin01.png'];
        preload(pinimgs);

        if ($.cookie('default_location')) {
            $('#location_select').val($.cookie('default_location'));
        }

        // handle changing of the location select
        $('#location_select').change(function() {
            window.default_latitude = $(this).val().split(',')[0];
            window.default_longitude = $(this).val().split(',')[1];
            window.default_location = $(this).val().split(',')[2];

            // in case a new location gets selected before the map loads
            if (window.spacescout_map != null) {
                window.spacescout_map.setCenter(new google.maps.LatLng(window.default_latitude, window.default_longitude));
            }
            window.update_count = true;
            get_location_buildings();
            $.cookie('default_location', $(this).val());
            reset_location_filter();
        });

    	// handle clicking on map centering buttons
        $('#center_all').live('click', function(e){

            e.preventDefault();
            if (window.spacescout_map.getZoom() != window.default_zoom) {
                window.spacescout_map.setZoom(window.default_zoom);
            }
            window.spacescout_map.setCenter(new google.maps.LatLng(window.default_latitude, window.default_longitude));
        });

        get_location_buildings();

        var escape_key_code = 27;

        $(document).keyup(function(e) {
            if (e.keyCode == escape_key_code) {
                if ($('#filter_block').is(':visible')) {
                    $('#filter_block').slideUp(400, function() {
                        //mobile style stuff
                        if ($('#container').attr("style")) {
                            $('#container').height('auto');
                            $('#container').css('overflow','visible');
                        }
                    });
                    $('#filter_button').show();
                    $('#space_count_container').show();
                    $('#view_results_button').hide();
                    $('#cancel_results_button').hide();
                    $('#filter_button').focus();
                }
                if ($('.space-detail').is(':visible')) {
                    $('.space-detail').hide("slide", { direction: "right" }, 400, function() {
                        $('#space_detail_container').remove();
                    });

                        // deselect selected space in list
                    $('#info_items li').removeClass('selected');
                }
            }
        });

        // handle clicking on the "done" button for filters
        $("#view_results_button").click(function() {
            $('.count').hide();
            $('.spaces').hide();
            run_custom_search();
            $('#filter_button').focus();
        });

        default_open_at_filter();

	});

})(this);

function getSpaceMap(lat, lon) {

  if (window.space_latitude) {
    lat = window.space_latitude
  }

  if (window.space_longitude) {
    lon = window.space_longitude
  }

  var mapOptions = {
    zoom: 17,
    center: new google.maps.LatLng(lat , lon),
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    mapTypeControl: false,
    streetViewControl: false
  }

  var map = new google.maps.Map(document.getElementById("spaceMap"), mapOptions);

  var image = '/static/img/pins/pin00.png';

  var spaceLatLng = new google.maps.LatLng(lat , lon);
  var spaceMarker = new google.maps.Marker({
      position: spaceLatLng,
      map: map,
      icon: image
  });

}

function replaceUrls(){
    // Replace urls in reservation notes with actual links.
    var text = $("#ei_reservation_notes").html();
    var patt = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;
    var url = patt.exec(text);
    if (url != null) {
        text = text.replace(url, "" + url + "");
        $("#ei_reservation_notes").html(text);
    }
}
<commit_msg>SPOT-551: now when spot details are closed via escape key, the button for that spot is given focus<commit_after>var detailsLat, detailsLon;
var requests = new Array();

// Handlebars helpers
Handlebars.registerHelper('carouselimages', function(spacedata) {
    var space_id = spacedata.id;
    var elements = new Array;
    if (spacedata.images.length > 0) {
        for (i=0; i < spacedata.images.length; i++) {
            image_id = spacedata.images[i].id;
            image_url = "background:url(/space/" + space_id + "/image/" + image_id + "/thumb/constrain/width:500)";
            div_string = "

"
            elements.push(div_string);
        }
    } else {
        image_url = "background:url(/static/img/placeholder_noImage_bw.png)";
        div_string = "

"
        elements.push(div_string);
    }
    return new Handlebars.SafeString(elements.join('\n'));
});

Handlebars.registerHelper('compare', function(lvalue, rvalue, options) {

    if (arguments.length < 3)
        throw new Error("Handlerbars Helper 'compare' needs 2 parameters");

    operator = options.hash.operator || "==";

    var operators = {
        '==':       function(l,r) { return l == r; },
        '===':      function(l,r) { return l === r; },
        '!=':       function(l,r) { return l != r; },
        '<':        function(l,r) { return l < r; },
        '>':        function(l,r) { return l > r; },
        '<=':       function(l,r) { return l <= r; },
        '>=':       function(l,r) { return l >= r; },
        'typeof':   function(l,r) { return typeof l == r; }
    }

    if (!operators[operator])
        throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);

    var result = operators[operator](lvalue,rvalue);

    if( result ) {
        return options.fn(this);
    } else {
        return options.inverse(this);
    }

});

Handlebars.registerHelper('formatHours', function(hours) {
    //tomorrow_starts_at_midnight = true;
    //tomorrow_is_24_hours =
    //if (start_time[0] == 0 && start_time[1] == 0 && end_time[0] == 23 && end_time[1] == 59 && tomorrow_starts_at_midnight && !tomorrow_is_24_hours && tomorrows_hour > 3) {
        //dsf
    //}
    var formatted = {};
    $.each(hours, function(day) {
        if (hours[day].length > 0) {
            $.each(hours[day], function() {
                this[0] = this[0].replace(/^0+/, '');
                this[1] = this[1].replace(/^0+/, '');
            });
            dayMarker = day.charAt(0);
            dayMarker = dayMarker.toUpperCase();
            // Show two characters for Th, Sa, Su
            if (dayMarker == 'T' && day.charAt(1) == 'h' || dayMarker == 'S' && day.charAt(1) == 'a' || dayMarker == 'S' && day.charAt(1) == 'u') {
                dayMarker += day.charAt(1);
            }

            formatted[dayMarker] = to12Hour(hours[day]).join(", ");
        }
    });
    formatted = sortDays(formatted);
    formatted = groupHours(formatted);
    return new Handlebars.SafeString(formatted.join("
"));
});

Handlebars.registerHelper('alphaOptGroupsHTML', function(list) {
    list.sort();
    firstletter = null;
    out = new Array;
    for (var i=0; i < list.length; i++) {
        if (list[i][0] == firstletter) {
            out.push('<option value="'+list[i]+'">'+list[i]+'</option>');
        } else {
            if (firstletter != null) {
                out.push('</optgroup>');
            }
            firstletter = list[i][0];
            out.push('<optgroup label="'+firstletter+'">');
            out.push('<option value="'+list[i]+'">'+list[i]+'</option>');
        }
    }
    out.push('</optgroup>');
    return new Handlebars.SafeString(out.join(''));
});

function groupHours(days) {
    var days1 = [];
    var hours = [];
    var days2 = [];
    var daycount =0;
    var hourscount = 0;
    var final_hours = [];
    var final_hours_count=0;

    for(var i=0;i < days.length; i++) {
        var split = days[i].split(": ");
        var day = split[0];
        var hours1 = split[1].split(", ");
        for(hour in hours1) {
            days1[daycount]=day;
            if(day == "M") {
                days2[daycount]=0;
            }else if(day == "T") {
                days2[daycount]=1;
            }else if(day == "W") {
                days2[daycount]=2;
            }else if(day == "Th") {
                days2[daycount]=3;
            }else if(day == "F") {
                days2[daycount]=4;
            }else if(day == "Sa") {
                days2[daycount]=5;
            }else if(day == "Su") {
                days2[daycount]=6;
            }
            hours[hourscount]= hours1[hour];
            daycount++;
            hourscount++;
        }

    }

    for(var i=0; i<hours.length; i++) {
        var hour = hours[i].split(" - ");

        if(hours[i] != "Open 24 Hours"&& hour[1]=="Midnight") {
            var next_day = "null";
            if(i != hours.length-1) {
                for( var j=i+1; j<hours.length; j++) {
                    var new_hour=hours[j].split(" - ");
                    if((days2[j]-days2[i])> 1) {
                        break;
                    }
                    if((days2[j]-days2[i])== 1 && new_hour[0]=="12AM") {
                        hour[1]=new_hour[1];
                        hours[j]="null";
                        break;
                    }
                }
                hours[i]=hour[0]+" - "+ hour[1];
            }else if( days1[i] == "Su") {
                for( var j=0; j<hours.length; j++) {
                    var new_hour=hours[j].split(" - ");
                    if((days2[j]-days2[i])!= -6) {
                        break;
                    }
                    if((days2[j]-days2[i])==-6 && new_hour[0]=="12AM") {
                        hour[1]=new_hour[1];
                        hours[j]="null";
                        break;
                    }
                }
                hours[i]=hour[0]+" - "+ hour[1];
            }
        }
    }

    for(var i=0; i<hours.length; i++) {
        if (hours[i] != "null" && i != hours.length-1) {
            for(var j =i+1; j<hours.length; j++) {

                if(hours[i]==hours[j]) {
                    days1[i]+= ", "+days1[j];
                    hours[j]="null";
                }
            }
        }
    }
    for(var i=0; i<hours.length; i++) {
        if( hours[i] != "null") {
            final_hours[final_hours_count]=days1[i]+": "+ hours[i];
            final_hours_count++;
        }
    }
    return final_hours;
}

function to12Hour(day) {
    var retData = [];
    for (var j=0; j<day.length; j++) {
        var data = [ day[j][0], day[j][1] ];
        for (var i=0; i<data.length; i++) {
            time = data[i].split(":");
            if(time[0]=="23" & time[1] == "59") {
                data[i] = "Midnight";
            }
            else if (time[0] =="12" & time[1] =="00") {
                data[i] = "Noon";
            }else {
                if (time[0] > 12) {
                    time[0] -= 12;

                    time[1] += "PM";
                }
                else if (time[0] < 1) {
                    time[0] = 12;
                    time[1] += "AM";
                }
                else {
                    time[1] += "AM";
                }
                if (time[1] == "00AM") {
                    data[i] = time[0];
                    data[i] += "AM";
                } else if (time[1] == "00PM") {
                    data[i] = time[0];
                    data[i] += "PM"
                }else {
                    data[i] = time.join(":");
                }
            }
        }
        if(data[0]=="12AM" & data[1]=="Midnight") {
            retData[j]="Open 24 Hours";
        }else {
            retData[j]=data[0] +" - " +data[1];
        }
    }
    return retData;
}

function sortDays(days) {
    var ordered = [];
    order = ["M", "T", "W", "Th", "F", "Sa", "Su"];
    $.each(order, function(day) {
        if (days[order[day]]) {
            ordered.push(order[day] +": " +days[order[day]] );
        }
    });
    return ordered;
}

function default_open_at_filter() {
    // set the default open_at filter to close to now
    var weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    var date = new Date();
    var hour = date.getHours();
    var min = date.getMinutes();

    if (min < 16) {
        min = "00";
    }else if (min < 46) {
        min = "30";
    }else {
        min = "00";
        hour++;
    }

    if (hour > 11) {
        $("#ampm-from").val("PM");
    }else {
        $("#ampm-from").val("AM");
    }
    if (hour > 12) {
        hour = hour-12;
    }
    hour = ""+hour+":"+min;
    $("#day-from").val(weekdays[date.getDay()]);
    $("#hour-from").val(hour);
}

function format_location_filter(data) {
    var source = $('#building_list').html();
    var template = Handlebars.compile(source);
    $('#building_list_container').html(template({data: data}));
}

function get_location_buildings() {
    // populate the location filter list
    url = '/buildings';
    if (window.default_location != null) {
        url = url + '?campus=' + window.default_location;
    }
    $.ajax({
        url: url,
        success: format_location_filter
    });

}

// Found at http://stackoverflow.com/questions/476679/preloading-images-with-jquery
function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('')[0].src = this;
        // Alternatively you could use:
        // (new Image()).src = this;
    });
}

function reset_location_filter() {
    $('#entire_campus').prop('checked', true);
    $('#entire_campus').parent().removeClass("selected");
    $('#e9.building-location').children().children().first()[0].selected = true;
    $('#building_list_container').hide();
    $('#building_list_input').parent().removeClass("selected");
    $('#building_list_container').children().children().children(".select2-search-choice").remove();
    $('#building_list_container').children().children().children().children().val('Select building(s)');
    $('#building_list_container').children().children().children().children().attr('style', "");
    run_custom_search();
}

(function(g){

	$(document).ready(function() {

        var pinimgs = ['/static/img/pins/pin00.png', '/static/img/pins/pin01.png'];
        preload(pinimgs);

        if ($.cookie('default_location')) {
            $('#location_select').val($.cookie('default_location'));
        }

        // handle changing of the location select
        $('#location_select').change(function() {
            window.default_latitude = $(this).val().split(',')[0];
            window.default_longitude = $(this).val().split(',')[1];
            window.default_location = $(this).val().split(',')[2];

            // in case a new location gets selected before the map loads
            if (window.spacescout_map != null) {
                window.spacescout_map.setCenter(new google.maps.LatLng(window.default_latitude, window.default_longitude));
            }
            window.update_count = true;
            get_location_buildings();
            $.cookie('default_location', $(this).val());
            reset_location_filter();
        });

    	// handle clicking on map centering buttons
        $('#center_all').live('click', function(e){

            e.preventDefault();
            if (window.spacescout_map.getZoom() != window.default_zoom) {
                window.spacescout_map.setZoom(window.default_zoom);
            }
            window.spacescout_map.setCenter(new google.maps.LatLng(window.default_latitude, window.default_longitude));
        });

        get_location_buildings();

        var escape_key_code = 27;

        $(document).keyup(function(e) {
            if (e.keyCode == escape_key_code) {
                if ($('#filter_block').is(':visible')) {
                    $('#filter_block').slideUp(400, function() {
                        //mobile style stuff
                        if ($('#container').attr("style")) {
                            $('#container').height('auto');
                            $('#container').css('overflow','visible');
                        }
                    });
                    $('#filter_button').show();
                    $('#space_count_container').show();
                    $('#view_results_button').hide();
                    $('#cancel_results_button').hide();
                    $('#filter_button').focus();
                }
                if ($('.space-detail').is(':visible')) {
                    closeSpotDetail();
                }
            }
        });

        function closeSpotDetail() {
            var the_spot_id = $('.space-detail-inner').attr("id");
            the_spot_id = "#" + the_spot_id.replace(/[^0-9]/g, '');
            $('.space-detail').hide("slide", { direction: "right" }, 400, function() {
                $('#space_detail_container').remove();
            });

                // deselect selected space in list
            $('#info_items li').removeClass('selected');
            $(the_spot_id).focus();
        }

        // handle clicking on the "done" button for filters
        $("#view_results_button").click(function() {
            $('.count').hide();
            $('.spaces').hide();
            run_custom_search();
            $('#filter_button').focus();
        });

        default_open_at_filter();

	});

})(this);

function getSpaceMap(lat, lon) {

  if (window.space_latitude) {
    lat = window.space_latitude
  }

  if (window.space_longitude) {
    lon = window.space_longitude
  }

  var mapOptions = {
    zoom: 17,
    center: new google.maps.LatLng(lat , lon),
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    mapTypeControl: false,
    streetViewControl: false
  }

  var map = new google.maps.Map(document.getElementById("spaceMap"), mapOptions);

  var image = '/static/img/pins/pin00.png';

  var spaceLatLng = new google.maps.LatLng(lat , lon);
  var spaceMarker = new google.maps.Marker({
      position: spaceLatLng,
      map: map,
      icon: image
  });

}

function replaceUrls(){
    // Replace urls in reservation notes with actual links.
    var text = $("#ei_reservation_notes").html();
    var patt = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;
    var url = patt.exec(text);
    if (url != null) {
        text = text.replace(url, "" + url + "");
        $("#ei_reservation_notes").html(text);
    }
}
<|endoftext|>


<commit_before>'use strict';

var streamBuffers = require('stream-buffers'),
  fds = require('fd-slicer'),
  fs = require('fs'),
  xrate = require('../src/xrate');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

var _createFromFd = fds.createFromFd;
var _open = fs.open;

var increment = 0,
  starting = 0;

exports.xrate = {
  develFilesOk: [
  ],
  setUp: function(done) {
    // start xrate
    done();
  },
  tearDown: function(done) {
    xrate.stop(function() {
      fds.createFromFd = _createFromFd;
      fs.open = _open;
      done();
    });
  },
  // doInitCheck: function(test) {
  //   // setup for the test
  //   console.log('\nInit Check');
  //   xrate.once('error', function(err) {
  //     console.log(err);
  //     test.notEqual(err, 'what i thought', 'cou')
  //     test.done();
  //   });

  //   xrate.once('update', function(report) )
  // },
  statusCheck: function(test) {
    console.log('Status Check');
    increment = 60;
    starting = 300;

    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };

    function StreamMock() {
      var self = this;

      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(4);
    xrate.start();

    setTimeout(function() {
      xrate.status(function(report) {
        test.ok(report.o.first === (increment * 2), 'should be recording in that increment');
        test.ok(report.i.first === (increment * 2), 'should be recording in that increment');
        test.ok(report.o.average === (increment * 2), 'average should be 2 times increment');
        test.ok(report.i.average === (increment * 2), 'average should be 2 times increment');
      });
      test.done();
    }, 5000);
  },
  updateCheck: function(test) {
    console.log('Update Check');
    increment = 100;
    starting = 10000;

    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };

    function StreamMock() {
      var self = this;
      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(2);
    xrate.start();
    setTimeout(function() {
      xrate.once('update', function(last) {
        test.ok(last.o === 200, 'right increment');
        test.ok(last.i === 200, 'right increment');
        test.done();
      });
    }, 3000);
  },
  doStopCheck: function(test) {
    console.log('Stop Check');
    increment = 100;
    starting = 10000;
    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };
    function StreamMock() {
      var self = this;

      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(2);
    xrate.start();

    setTimeout(function() {
      xrate.stop(function(last) {
        test.ok(last.o.total === 200, 'how much bandwidth had passed in this amount of time');
        test.ok(last.i.total === 200, 'how much bandwidth had passed in this amount of time');
        test.done();
      });
    }, 6000);
  },
  doErrorCheck: function(test) {
    console.log('Error Check');

    test.expect(1);
    var errorSettings = {
      base: '/should/not/work/',
      device: 'eth0',
      sent: 'statistics/rx_bytes',
      rcvd: 'statistics/tx_bytes'
    };
    xrate.settings(errorSettings);
    xrate.start();

    xrate.once('error', function(message) {
      test.ok(message === 'could not find bandwidth data', 'right error message');
      test.done();
    });
  }
};

<commit_msg>fixes format<commit_after>'use strict';

var streamBuffers = require('stream-buffers'),
  fds = require('fd-slicer'),
  fs = require('fs'),
  xrate = require('../src/xrate');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

var _createFromFd = fds.createFromFd;
var _open = fs.open;

var increment = 0,
  starting = 0;

exports.xrate = {
  develFilesOk: [
  ],
  setUp: function(done) {
    // start xrate
    done();
  },
  tearDown: function(done) {
    xrate.stop(function() {
      fds.createFromFd = _createFromFd;
      fs.open = _open;
      done();
    });
  },
  // doInitCheck: function(test) {
  //   // setup for the test
  //   console.log('\nInit Check');
  //   xrate.once('error', function(err) {
  //     console.log(err);
  //     test.notEqual(err, 'what i thought', 'cou')
  //     test.done();
  //   });

  //   xrate.once('update', function(report) )
  // },
  statusCheck: function(test) {
    console.log('\nStatus Check');
    increment = 60;
    starting = 300;

    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };

    function StreamMock() {
      var self = this;

      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(4);
    xrate.start();

    setTimeout(function() {
      xrate.status(function(report) {
        test.ok(report.o.first === (increment * 2), 'should be recording in that increment');
        test.ok(report.i.first === (increment * 2), 'should be recording in that increment');
        test.ok(report.o.average === (increment * 2), 'average should be 2 times increment');
        test.ok(report.i.average === (increment * 2), 'average should be 2 times increment');
      });
      test.done();
    }, 5000);
  },
  updateCheck: function(test) {
    console.log('Update Check');
    increment = 100;
    starting = 10000;

    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };

    function StreamMock() {
      var self = this;
      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(2);
    xrate.start();
    setTimeout(function() {
      xrate.once('update', function(last) {
        test.ok(last.o === 200, 'right increment');
        test.ok(last.i === 200, 'right increment');
        test.done();
      });
    }, 3000);
  },
  doStopCheck: function(test) {
    console.log('Stop Check');
    increment = 100;
    starting = 10000;
    fs.open = function(x, y, callback) {
      // dont need to give anything useful back;
      callback(null, null);
    };
    function StreamMock() {
      var self = this;

      var sb = new streamBuffers.ReadableStreamBuffer({
        frequency: 1000,
        chunkSize: 32
      });

      self.createReadStream = function() {
        starting += increment;
        sb.put(starting, 'utf8');
        return sb;
      };
    }
    fds.createFromFd = function() {
      return new StreamMock();
    };

    test.expect(2);
    xrate.start();

    setTimeout(function() {
      xrate.stop(function(last) {
        test.ok(last.o.total === 200, 'how much bandwidth had passed in this amount of time');
        test.ok(last.i.total === 200, 'how much bandwidth had passed in this amount of time');
        test.done();
      });
    }, 6000);
  },
  doErrorCheck: function(test) {
    console.log('Error Check');

    test.expect(1);
    var errorSettings = {
      base: '/should/not/work/',
      device: 'eth0',
      sent: 'statistics/rx_bytes',
      rcvd: 'statistics/tx_bytes'
    };
    xrate.settings(errorSettings);
    xrate.start();

    xrate.once('error', function(message) {
      test.ok(message === 'could not find bandwidth data', 'right error message');
      test.done();
    });
  }
};

<|endoftext|>


<commit_before>define(['backbone'], function(_) {

  var NewsItem = Backbone.Model.extend({
    urlRoot: '/newsItems/',
    initialize: function() {
    },
    defaults: {
      title: '',
      body: '',
      tags: [],
      posted_date: new Date(),
      last_modified_date: new Date(),
      draft: true
    },
    tagsStr: function() {
      return this.get('tags').join(', ');
    },
  });

  var NewsItems = Backbone.Collection.extend({
    url: '/newsItems/',
    model: NewsItem
  });

  return {
    NewsItem: NewsItem,
    NewsItems: NewsItems
  };
});

<commit_msg>Backbone Comment model and collection<commit_after>define(['backbone'], function(_) {

  var NewsItem = Backbone.Model.extend({
    urlRoot: '/newsItems/',
    initialize: function() {
    },
    defaults: {
      title: '',
      body: '',
      tags: [],
      posted_date: new Date(),
      last_modified_date: new Date(),
      draft: true
    },
    tagsStr: function() {
      return this.get('tags').join(', ');
    },
  });

  var NewsItems = Backbone.Collection.extend({
    url: '/newsItems/',
    model: NewsItem
  });

  var Comment = Backbone.Model.extend({
    urlRoot: '/comments/',
    initialize: function() {
    },
    defaults: {
      name: '',
      homepage: '',
      email: '',
      body: '',
      posted_date: new Date(),
    },
  });

  var Comments = Backbone.Collection.extend({
    url: '/comments/',
    model: Comment
  });

  return {
    NewsItem: NewsItem,
    NewsItems: NewsItems,
    Comment: Comment,
    Comments: Comments
  };
});

<|endoftext|>


<commit_before>function ajax_select_seminar(id_speaker)
{
   $("#id_title").html('<option value="">Loading...</option>');
   $.ajax({
       type: "GET",
       url: "/activity/seminar_show_titles",
       dataType: "json",
       data: {'speaker':id_speaker},
       success: function(retorno) {
           $("#id_title").empty();
           $("#id_title").append('<option value="">--------</option>');

           $.each(retorno, function(i, item){
               $("#id_title").append('<option value="'+item.pk+'">'+item.valor+'</option>');
           });
       },
       error: function(erro) {
           alert('Error: No request return.');
       }
   });
}
<commit_msg>Fixed a typo<commit_after>function ajax_select_seminar(id_speaker)
{
   $("#id_title").html('<option value="">Loading...</option>');
   $.ajax({
       type: "GET",
       url: "/activity/seminars_show_titles",
       dataType: "json",
       data: {'speaker':id_speaker},
       success: function(retorno) {
           $("#id_title").empty();
           $("#id_title").append('<option value="">--------</option>');

           $.each(retorno, function(i, item){
               $("#id_title").append('<option value="'+item.pk+'">'+item.valor+'</option>');
           });
       },
       error: function(erro) {
           alert('Error: No request return.');
       }
   });
}
<|endoftext|>


<commit_before>const util = require("./util");
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
function preserve_state(send_after_reload, save_pointer, save_narrow, save_compose) {
    if (!localstorage.supported()) {
        // If local storage is not supported by the browser, we can't
        // save the browser's position across reloads (since there's
        // no secure way to pass that state in a signed fashion to the
        // next instance of the browser client).
        //
        // So we jure return here and let the reload proceed without
        // having preserved state.  We keep the hash the same so we'll
        // at least save their narrow state.
        blueslip.log("Can't preserve state; no local storage.");
        return;
    }

    if (send_after_reload === undefined) {
        send_after_reload = 0;
    }
    let url = "#reload:send_after_reload=" + Number(send_after_reload);
    url += "+csrf_token=" + encodeURIComponent(csrf_token);

    if (save_compose) {
        const msg_type = compose_state.get_message_type();
        if (msg_type === 'stream') {
            url += "+msg_type=stream";
            url += "+stream=" + encodeURIComponent(compose_state.stream_name());
            url += "+topic=" + encodeURIComponent(compose_state.topic());
        } else if (msg_type === 'private') {
            url += "+msg_type=private";
            url += "+recipient=" + encodeURIComponent(compose_state.private_message_recipient());
        }

        if (msg_type) {
            url += "+msg=" + encodeURIComponent(compose_state.message_content());
        }
    }

    if (save_pointer) {
        const pointer = home_msg_list.selected_id();
        if (pointer !== -1) {
            url += "+pointer=" + pointer;
        }
    }

    if (save_narrow) {
        const row = home_msg_list.selected_row();
        if (!narrow_state.active()) {
            if (row.length > 0) {
                url += "+offset=" + row.offset().top;
            }
        } else {
            url += "+offset=" + home_msg_list.pre_narrow_offset;

            const narrow_pointer = message_list.narrowed.selected_id();
            if (narrow_pointer !== -1) {
                url += "+narrow_pointer=" + narrow_pointer;
            }
            const narrow_row = message_list.narrowed.selected_row();
            if (narrow_row.length > 0) {
                url += "+narrow_offset=" + narrow_row.offset().top;
            }
        }
    }

    let oldhash = window.location.hash;
    if (oldhash.length !== 0 && oldhash[0] === '#') {
        oldhash = oldhash.slice(1);
    }
    url += "+oldhash=" + encodeURIComponent(oldhash);

    const ls = localstorage();
    // Delete all the previous preserved states.
    ls.removeRegex('reload:\\d+');

    // To protect the browser against CSRF type attacks, the reload
    // logic uses a random token (to distinct this browser from
    // others) which is passed via the URL to the browser (post
    // reloading).  The token is a key into local storage, where we
    // marshall and store the URL.
    //
    // TODO: Remove the now-unnecessary URL-encoding logic above and
    // just pass the actual data structures through local storage.
    const token = util.random_int(0, 1024 * 1024 * 1024 * 1024);

    ls.set("reload:" + token, url);
    window.location.replace("#reload:" + token);
}

// Check if we're doing a compose-preserving reload.  This must be
// done before the first call to get_events
exports.initialize = function () {
    const location = window.location.toString();
    const hash_fragment = location.substring(location.indexOf('#') + 1);

    // hash_fragment should be e.g. `reload:12345123412312`
    if (hash_fragment.search("reload:") !== 0) {
        return;
    }

    // Using the token, recover the saved pre-reload data from local
    // storage.  Afterwards, we clear the reload entry from local
    // storage to avoid a local storage space leak.
    const ls = localstorage();
    let fragment = ls.get(hash_fragment);
    if (fragment === undefined) {
        // Since this can happen sometimes with hand-reloading, it's
        // not really worth throwing an exception if these don't
        // exist, but be log it so that it's available for future
        // debugging if an exception happens later.
        blueslip.info("Invalid hash change reload token");
        hashchange.changehash("");
        return;
    }
    ls.remove(hash_fragment);

    fragment = fragment.replace(/^reload:/, "");
    const keyvals = fragment.split("+");
    const vars = {};

    for (const str of keyvals) {
        const pair = str.split("=");
        vars[pair[0]] = decodeURIComponent(pair[1]);
    }

    if (vars.msg !== undefined) {
        const send_now = parseInt(vars.send_after_reload, 10);

        try {
            // TODO: preserve focus
            const topic = util.get_reload_topic(vars);

            compose_actions.start(vars.msg_type, {stream: vars.stream || '',
                                                  topic: topic || '',
                                                  private_message_recipient: vars.recipient || '',
                                                  content: vars.msg || ''});
            if (send_now) {
                compose.finish();
            }
        } catch (err) {
            // We log an error if we can't open the compose box, but otherwise
            // we continue, since this is not critical.
            blueslip.warn(err.toString());
        }
    }

    const pointer = parseInt(vars.pointer, 10);

    if (pointer) {
        page_params.initial_pointer = pointer;
    }
    const offset = parseInt(vars.offset, 10);
    if (offset) {
        page_params.initial_offset = offset;
    }

    const narrow_pointer = parseInt(vars.narrow_pointer, 10);
    if (narrow_pointer) {
        page_params.initial_narrow_pointer = narrow_pointer;
    }
    const narrow_offset = parseInt(vars.narrow_offset, 10);
    if (narrow_offset) {
        page_params.initial_narrow_offset = narrow_offset;
    }

    activity.new_user_input = false;
    hashchange.changehash(vars.oldhash);
};

function do_reload_app(send_after_reload, save_pointer, save_narrow, save_compose, message) {
    if (reload_state.is_in_progress()) {
        blueslip.log("do_reload_app: Doing nothing since reload_in_progress");
        return;
    }

    // TODO: we should completely disable the UI here
    if (save_pointer || save_narrow || save_compose) {
        try {
            preserve_state(send_after_reload, save_pointer, save_narrow, save_compose);
        } catch (ex) {
            blueslip.error('Failed to preserve state',
                           undefined, ex.stack);
        }
    }

    if (message === undefined) {
        message = "Reloading ...";
    }

    // TODO: We need a better API for showing messages.
    ui_report.message(message, $("#reloading-application"));
    blueslip.log('Starting server requested page reload');
    reload_state.set_state_to_in_progress();

    // Sometimes the window.location.reload that we attempt has no
    // immediate effect (likely by browsers trying to save power by
    // skipping requested reloads), which can leave the Zulip app in a
    // broken state and cause lots of confusing tracebacks.  So, we
    // set ourselves to try reloading a bit later, both periodically
    // and when the user focuses the window.
    $(window).on('focus', function () {
        blueslip.log("Retrying on-focus page reload");
        window.location.reload(true);
    });
    setInterval(function () {
        blueslip.log("Retrying page reload due to 30s timer");
        window.location.reload(true);
    }, 30000);

    try {
        server_events.cleanup_event_queue();
    } catch (ex) {
        blueslip.error('Failed to cleanup before reloading',
                       undefined, ex.stack);
    }

    window.location.reload(true);
}

exports.initiate = function (options) {
    options = {
        immediate: false,
        save_pointer: true,
        save_narrow: true,
        save_compose: true,
        send_after_reload: false,
        ...options,
    };

    if (options.save_pointer === undefined ||
        options.save_narrow === undefined ||
        options.save_compose === undefined) {
        blueslip.error("reload.initiate() called without explicit save options.");
    }

    if (options.immediate) {
        do_reload_app(options.send_after_reload,
                      options.save_pointer,
                      options.save_narrow,
                      options.save_compose,
                      options.message);
    }

    if (reload_state.is_pending()) {
        return;
    }
    reload_state.set_state_to_pending();

    // We're now planning to execute a reload of the browser, usually
    // to get an updated version of the Zulip webapp code.  Because in
    // most cases all browsers will be receiving this notice at the
    // same or similar times, we need to randomize the time that we do
    // this in order to avoid a thundering herd overloading the server.
    //
    // Additionally, we try to do this reload at a time the user will
    // not notice.  So completely idle clients will reload first;
    // those will an open compose box will wait until the message has
    // been sent (or until it's clear the user isn't likely to send it).
    //
    // And then we unconditionally reload sometime after 30 minutes
    // even if there is continued activity, because we don't support
    // old JavaScript versions against newer servers and eventually
    // letting that situation continue will lead to users seeing bugs.
    //
    // It's a little odd that how this timeout logic works with
    // compose box resets including the random variance, but that
    // makes it simple to reason about: We know that reloads will be
    // spread over at least 5 minutes in all cases.

    let idle_control;
    const random_variance = util.random_int(0, 1000 * 60 * 5);
    const unconditional_timeout = 1000 * 60 * 30 + random_variance;
    const composing_timeout     = 1000 * 60 * 7  + random_variance;
    const home_timeout          = 1000 * 60 * 1  + random_variance;
    let compose_started_handler;

    function reload_from_idle() {
        do_reload_app(false,
                      options.save_pointer,
                      options.save_narrow,
                      options.save_compose,
                      options.message);
    }

    // Make sure we always do a reload eventually after
    // unconditional_timeout.  Because we save cursor location and
    // compose state when reloading, we expect this to not be
    // particularly disruptive.
    setTimeout(reload_from_idle, unconditional_timeout);

    const compose_done_handler = function () {
        // If the user sends their message or otherwise closes
        // compose, we return them to the not-composing timeouts.
        idle_control.cancel();
        idle_control = $(document).idle({idle: home_timeout,
                                         onIdle: reload_from_idle});
        $(document).off('compose_canceled.zulip compose_finished.zulip',
                        compose_done_handler);
        $(document).on('compose_started.zulip', compose_started_handler);
    };
    compose_started_handler = function () {
        // If the user stops being idle and starts composing a
        // message, switch to the compose-open timeouts.
        idle_control.cancel();
        idle_control = $(document).idle({idle: composing_timeout,
                                         onIdle: reload_from_idle});
        $(document).off('compose_started.zulip', compose_started_handler);
        $(document).on('compose_canceled.zulip compose_finished.zulip',
                       compose_done_handler);
    };

    if (compose_state.composing()) {
        idle_control = $(document).idle({idle: composing_timeout,
                                         onIdle: reload_from_idle});
        $(document).on('compose_canceled.zulip compose_finished.zulip',
                       compose_done_handler);
    } else {
        idle_control = $(document).idle({idle: home_timeout,
                                         onIdle: reload_from_idle});
        $(document).on('compose_started.zulip', compose_started_handler);
    }
};

window.addEventListener('beforeunload', function () {
    // When navigating away from the page do not try to reload.
    // The polling get_events call will fail after we delete the event queue.
    // When that happens we reload the page to correct the problem. If this
    // happens before the navigation is complete the user is kept captive at
    // zulip.
    blueslip.log("Setting reload_in_progress in beforeunload handler");
    reload_state.set_state_to_in_progress();
});

window.reload = exports;
<commit_msg>reload: Use better variable names for timeouts.<commit_after>const util = require("./util");
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
function preserve_state(send_after_reload, save_pointer, save_narrow, save_compose) {
    if (!localstorage.supported()) {
        // If local storage is not supported by the browser, we can't
        // save the browser's position across reloads (since there's
        // no secure way to pass that state in a signed fashion to the
        // next instance of the browser client).
        //
        // So we jure return here and let the reload proceed without
        // having preserved state.  We keep the hash the same so we'll
        // at least save their narrow state.
        blueslip.log("Can't preserve state; no local storage.");
        return;
    }

    if (send_after_reload === undefined) {
        send_after_reload = 0;
    }
    let url = "#reload:send_after_reload=" + Number(send_after_reload);
    url += "+csrf_token=" + encodeURIComponent(csrf_token);

    if (save_compose) {
        const msg_type = compose_state.get_message_type();
        if (msg_type === 'stream') {
            url += "+msg_type=stream";
            url += "+stream=" + encodeURIComponent(compose_state.stream_name());
            url += "+topic=" + encodeURIComponent(compose_state.topic());
        } else if (msg_type === 'private') {
            url += "+msg_type=private";
            url += "+recipient=" + encodeURIComponent(compose_state.private_message_recipient());
        }

        if (msg_type) {
            url += "+msg=" + encodeURIComponent(compose_state.message_content());
        }
    }

    if (save_pointer) {
        const pointer = home_msg_list.selected_id();
        if (pointer !== -1) {
            url += "+pointer=" + pointer;
        }
    }

    if (save_narrow) {
        const row = home_msg_list.selected_row();
        if (!narrow_state.active()) {
            if (row.length > 0) {
                url += "+offset=" + row.offset().top;
            }
        } else {
            url += "+offset=" + home_msg_list.pre_narrow_offset;

            const narrow_pointer = message_list.narrowed.selected_id();
            if (narrow_pointer !== -1) {
                url += "+narrow_pointer=" + narrow_pointer;
            }
            const narrow_row = message_list.narrowed.selected_row();
            if (narrow_row.length > 0) {
                url += "+narrow_offset=" + narrow_row.offset().top;
            }
        }
    }

    let oldhash = window.location.hash;
    if (oldhash.length !== 0 && oldhash[0] === '#') {
        oldhash = oldhash.slice(1);
    }
    url += "+oldhash=" + encodeURIComponent(oldhash);

    const ls = localstorage();
    // Delete all the previous preserved states.
    ls.removeRegex('reload:\\d+');

    // To protect the browser against CSRF type attacks, the reload
    // logic uses a random token (to distinct this browser from
    // others) which is passed via the URL to the browser (post
    // reloading).  The token is a key into local storage, where we
    // marshall and store the URL.
    //
    // TODO: Remove the now-unnecessary URL-encoding logic above and
    // just pass the actual data structures through local storage.
    const token = util.random_int(0, 1024 * 1024 * 1024 * 1024);

    ls.set("reload:" + token, url);
    window.location.replace("#reload:" + token);
}

// Check if we're doing a compose-preserving reload.  This must be
// done before the first call to get_events
exports.initialize = function () {
    const location = window.location.toString();
    const hash_fragment = location.substring(location.indexOf('#') + 1);

    // hash_fragment should be e.g. `reload:12345123412312`
    if (hash_fragment.search("reload:") !== 0) {
        return;
    }

    // Using the token, recover the saved pre-reload data from local
    // storage.  Afterwards, we clear the reload entry from local
    // storage to avoid a local storage space leak.
    const ls = localstorage();
    let fragment = ls.get(hash_fragment);
    if (fragment === undefined) {
        // Since this can happen sometimes with hand-reloading, it's
        // not really worth throwing an exception if these don't
        // exist, but be log it so that it's available for future
        // debugging if an exception happens later.
        blueslip.info("Invalid hash change reload token");
        hashchange.changehash("");
        return;
    }
    ls.remove(hash_fragment);

    fragment = fragment.replace(/^reload:/, "");
    const keyvals = fragment.split("+");
    const vars = {};

    for (const str of keyvals) {
        const pair = str.split("=");
        vars[pair[0]] = decodeURIComponent(pair[1]);
    }

    if (vars.msg !== undefined) {
        const send_now = parseInt(vars.send_after_reload, 10);

        try {
            // TODO: preserve focus
            const topic = util.get_reload_topic(vars);

            compose_actions.start(vars.msg_type, {stream: vars.stream || '',
                                                  topic: topic || '',
                                                  private_message_recipient: vars.recipient || '',
                                                  content: vars.msg || ''});
            if (send_now) {
                compose.finish();
            }
        } catch (err) {
            // We log an error if we can't open the compose box, but otherwise
            // we continue, since this is not critical.
            blueslip.warn(err.toString());
        }
    }

    const pointer = parseInt(vars.pointer, 10);

    if (pointer) {
        page_params.initial_pointer = pointer;
    }
    const offset = parseInt(vars.offset, 10);
    if (offset) {
        page_params.initial_offset = offset;
    }

    const narrow_pointer = parseInt(vars.narrow_pointer, 10);
    if (narrow_pointer) {
        page_params.initial_narrow_pointer = narrow_pointer;
    }
    const narrow_offset = parseInt(vars.narrow_offset, 10);
    if (narrow_offset) {
        page_params.initial_narrow_offset = narrow_offset;
    }

    activity.new_user_input = false;
    hashchange.changehash(vars.oldhash);
};

function do_reload_app(send_after_reload, save_pointer, save_narrow, save_compose, message) {
    if (reload_state.is_in_progress()) {
        blueslip.log("do_reload_app: Doing nothing since reload_in_progress");
        return;
    }

    // TODO: we should completely disable the UI here
    if (save_pointer || save_narrow || save_compose) {
        try {
            preserve_state(send_after_reload, save_pointer, save_narrow, save_compose);
        } catch (ex) {
            blueslip.error('Failed to preserve state',
                           undefined, ex.stack);
        }
    }

    if (message === undefined) {
        message = "Reloading ...";
    }

    // TODO: We need a better API for showing messages.
    ui_report.message(message, $("#reloading-application"));
    blueslip.log('Starting server requested page reload');
    reload_state.set_state_to_in_progress();

    // Sometimes the window.location.reload that we attempt has no
    // immediate effect (likely by browsers trying to save power by
    // skipping requested reloads), which can leave the Zulip app in a
    // broken state and cause lots of confusing tracebacks.  So, we
    // set ourselves to try reloading a bit later, both periodically
    // and when the user focuses the window.
    $(window).on('focus', function () {
        blueslip.log("Retrying on-focus page reload");
        window.location.reload(true);
    });
    setInterval(function () {
        blueslip.log("Retrying page reload due to 30s timer");
        window.location.reload(true);
    }, 30000);

    try {
        server_events.cleanup_event_queue();
    } catch (ex) {
        blueslip.error('Failed to cleanup before reloading',
                       undefined, ex.stack);
    }

    window.location.reload(true);
}

exports.initiate = function (options) {
    options = {
        immediate: false,
        save_pointer: true,
        save_narrow: true,
        save_compose: true,
        send_after_reload: false,
        ...options,
    };

    if (options.save_pointer === undefined ||
        options.save_narrow === undefined ||
        options.save_compose === undefined) {
        blueslip.error("reload.initiate() called without explicit save options.");
    }

    if (options.immediate) {
        do_reload_app(options.send_after_reload,
                      options.save_pointer,
                      options.save_narrow,
                      options.save_compose,
                      options.message);
    }

    if (reload_state.is_pending()) {
        return;
    }
    reload_state.set_state_to_pending();

    // We're now planning to execute a reload of the browser, usually
    // to get an updated version of the Zulip webapp code.  Because in
    // most cases all browsers will be receiving this notice at the
    // same or similar times, we need to randomize the time that we do
    // this in order to avoid a thundering herd overloading the server.
    //
    // Additionally, we try to do this reload at a time the user will
    // not notice.  So completely idle clients will reload first;
    // those will an open compose box will wait until the message has
    // been sent (or until it's clear the user isn't likely to send it).
    //
    // And then we unconditionally reload sometime after 30 minutes
    // even if there is continued activity, because we don't support
    // old JavaScript versions against newer servers and eventually
    // letting that situation continue will lead to users seeing bugs.
    //
    // It's a little odd that how this timeout logic works with
    // compose box resets including the random variance, but that
    // makes it simple to reason about: We know that reloads will be
    // spread over at least 5 minutes in all cases.

    let idle_control;
    const random_variance = util.random_int(0, 1000 * 60 * 5);
    const unconditional_timeout = 1000 * 60 * 30 + random_variance;
    const composing_idle_timeout = 1000 * 60 * 7  + random_variance;
    const basic_idle_timeout = 1000 * 60 * 1  + random_variance;
    let compose_started_handler;

    function reload_from_idle() {
        do_reload_app(false,
                      options.save_pointer,
                      options.save_narrow,
                      options.save_compose,
                      options.message);
    }

    // Make sure we always do a reload eventually after
    // unconditional_timeout.  Because we save cursor location and
    // compose state when reloading, we expect this to not be
    // particularly disruptive.
    setTimeout(reload_from_idle, unconditional_timeout);

    const compose_done_handler = function () {
        // If the user sends their message or otherwise closes
        // compose, we return them to the not-composing timeouts.
        idle_control.cancel();
        idle_control = $(document).idle({idle: basic_idle_timeout,
                                         onIdle: reload_from_idle});
        $(document).off('compose_canceled.zulip compose_finished.zulip',
                        compose_done_handler);
        $(document).on('compose_started.zulip', compose_started_handler);
    };
    compose_started_handler = function () {
        // If the user stops being idle and starts composing a
        // message, switch to the compose-open timeouts.
        idle_control.cancel();
        idle_control = $(document).idle({idle: composing_idle_timeout,
                                         onIdle: reload_from_idle});
        $(document).off('compose_started.zulip', compose_started_handler);
        $(document).on('compose_canceled.zulip compose_finished.zulip',
                       compose_done_handler);
    };

    if (compose_state.composing()) {
        idle_control = $(document).idle({idle: composing_idle_timeout,
                                         onIdle: reload_from_idle});
        $(document).on('compose_canceled.zulip compose_finished.zulip',
                       compose_done_handler);
    } else {
        idle_control = $(document).idle({idle: basic_idle_timeout,
                                         onIdle: reload_from_idle});
        $(document).on('compose_started.zulip', compose_started_handler);
    }
};

window.addEventListener('beforeunload', function () {
    // When navigating away from the page do not try to reload.
    // The polling get_events call will fail after we delete the event queue.
    // When that happens we reload the page to correct the problem. If this
    // happens before the navigation is complete the user is kept captive at
    // zulip.
    blueslip.log("Setting reload_in_progress in beforeunload handler");
    reload_state.set_state_to_in_progress();
});

window.reload = exports;
<|endoftext|>


<commit_before>import autosize from "autosize";
import $ from "jquery";

import * as blueslip from "./blueslip";
import * as condense from "./condense";
import * as message_lists from "./message_lists";
import * as message_viewport from "./message_viewport";
import * as navbar_alerts from "./navbar_alerts";
import * as navigate from "./navigate";
import * as popovers from "./popovers";
import * as ui from "./ui";
import {user_settings} from "./user_settings";
import * as util from "./util";

let narrow_window = false;

function confine_to_range(lo, val, hi) {
    if (val < lo) {
        return lo;
    }
    if (val > hi) {
        return hi;
    }
    return val;
}

function size_blocks(blocks, usable_height) {
    let sum_height = 0;

    for (const block of blocks) {
        sum_height += block.real_height;
    }

    for (const block of blocks) {
        let ratio = block.real_height / sum_height;
        ratio = confine_to_range(0.05, ratio, 0.85);
        block.max_height = confine_to_range(80, usable_height * ratio, 1.2 * block.real_height);
    }
}

function get_new_heights() {
    const res = {};
    const viewport_height = message_viewport.height();
    const top_navbar_height = $("#top_navbar").safeOuterHeight(true);
    const right_sidebar_shorcuts_height = $(".right-sidebar-shortcuts").safeOuterHeight(true) || 0;
    const add_streams_link_height = $("#add-stream-link").safeOuterHeight(true) || 0;

    res.bottom_whitespace_height = viewport_height * 0.4;

    res.main_div_min_height = viewport_height - top_navbar_height;

    res.stream_filters_max_height =
        viewport_height -
        Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
        $("#global_filters").safeOuterHeight(true) -
        $("#streams_header").safeOuterHeight(true) -
        add_streams_link_height;

    // Don't let us crush the stream sidebar completely out of view
    res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height);

    // RIGHT SIDEBAR

    const usable_height =
        viewport_height -
        Number.parseInt($("#right-sidebar").css("marginTop"), 10) -
        $("#userlist-header").safeOuterHeight(true) -
        $("#user_search_section").safeOuterHeight(true) -
        right_sidebar_shorcuts_height;

    res.buddy_list_wrapper_max_height = Math.max(80, usable_height);

    return res;
}

function left_userlist_get_new_heights() {
    const res = {};
    const viewport_height = message_viewport.height();
    const viewport_width = message_viewport.width();
    res.viewport_height = viewport_height;
    res.viewport_width = viewport_width;

    // main div
    const top_navbar_height = $(".header").safeOuterHeight(true);
    res.bottom_whitespace_height = viewport_height * 0.4;
    res.main_div_min_height = viewport_height - top_navbar_height;

    // left sidebar
    const stream_filters = $("#stream_filters").expectOne();
    const buddy_list_wrapper = $("#buddy_list_wrapper").expectOne();

    const stream_filters_real_height = stream_filters.prop("scrollHeight");
    const user_list_real_height = ui.get_scroll_element(buddy_list_wrapper).prop("scrollHeight");

    res.total_leftlist_height =
        viewport_height -
        Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
        $("#global_filters").safeOuterHeight(true) -
        $("#streams_header").safeOuterHeight(true) -
        $("#userlist-header").safeOuterHeight(true) -
        $("#user_search_section").safeOuterHeight(true) -
        Number.parseInt(stream_filters.css("marginBottom"), 10);

    const blocks = [
        {
            real_height: stream_filters_real_height,
        },
        {
            real_height: user_list_real_height,
        },
    ];

    size_blocks(blocks, res.total_leftlist_height);

    res.stream_filters_max_height = blocks[0].max_height;
    res.buddy_list_wrapper_max_height = blocks[1].max_height;

    return res;
}

export function watch_manual_resize(element) {
    const box = document.querySelector(element);

    if (!box) {
        blueslip.error("Bad selector in watch_manual_resize: " + element);
        return undefined;
    }

    const meta = {
        box,
        height: null,
        mousedown: false,
    };

    const box_handler = function () {
        meta.mousedown = true;
        meta.height = meta.box.clientHeight;
    };
    meta.box.addEventListener("mousedown", box_handler);

    // If the user resizes the textarea manually, we use the
    // callback to stop autosize from adjusting the height.
    // It will be re-enabled when this component is next opened.
    const body_handler = function () {
        if (meta.mousedown === true) {
            meta.mousedown = false;
            if (meta.height !== meta.box.clientHeight) {
                meta.height = meta.box.clientHeight;
                autosize.destroy($(element)).height(meta.height + "px");
            }
        }
    };
    document.body.addEventListener("mouseup", body_handler);

    return [box_handler, body_handler];
}

export function reset_compose_textarea_max_height(bottom_whitespace_height) {
    // If the compose-box is open, we set the `max-height` property of
    // `compose-textarea` so that the compose-box's maximum extent
    // does not overlap the last message in the current stream.is the
    // right size to leave a tiny bit of space after the last message
    // of the current stream.

    // Compute bottom_whitespace_height if not provided by caller.
    if (bottom_whitespace_height === undefined) {
        const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
        bottom_whitespace_height = h.bottom_whitespace_height;
    }

    const compose_height = Number.parseInt($("#compose").outerHeight(), 10);
    const compose_textarea_height = Number.parseInt($("#compose-textarea").outerHeight(), 10);
    const compose_non_textarea_height = compose_height - compose_textarea_height;

    $("#compose-textarea").css(
        "max-height",
        // The 10 here leaves space for the selected message border.
        bottom_whitespace_height - compose_non_textarea_height - 10,
    );
}

export function resize_bottom_whitespace(h) {
    $("#bottom_whitespace").height(h.bottom_whitespace_height);

    // The height of the compose box is tied to that of
    // bottom_whitespace, so update it if necessary.
    //
    // reset_compose_textarea_max_height cannot compute the right
    // height correctly while compose is hidden. This is OK, because
    // we also resize compose every time it is opened.
    if ($(".message_comp").is(":visible")) {
        reset_compose_textarea_max_height(h.bottom_whitespace_height);
    }
}

export function resize_stream_filters_container(h) {
    h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
    resize_bottom_whitespace(h);
    $("#stream-filters-container").css("max-height", h.stream_filters_max_height);
}

export function resize_sidebars() {
    let sidebar;

    if (user_settings.left_side_userlist) {
        const css_narrow_mode = message_viewport.is_narrow();

        $("#top_navbar").removeClass("rightside-userlist");

        const right_items = $(".right-sidebar-items").expectOne();

        if (css_narrow_mode && !narrow_window) {
            // move stuff to the left sidebar (skinny mode)
            narrow_window = true;
            popovers.set_userlist_placement("left");
            sidebar = $("#left-sidebar").expectOne();
            sidebar.append(right_items);
            $("#buddy_list_wrapper").css("margin", "0px");
            $("#userlist-toggle").css("display", "none");
            $("#invite-user-link").hide();
        } else if (!css_narrow_mode && narrow_window) {
            // move stuff to the right sidebar (wide mode)
            narrow_window = false;
            popovers.set_userlist_placement("right");
            sidebar = $("#right-sidebar").expectOne();
            sidebar.append(right_items);
            $("#buddy_list_wrapper").css("margin", "");
            $("#userlist-toggle").css("display", "");
            $("#invite-user-link").show();
        }
    }

    const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();

    $("#buddy_list_wrapper").css("max-height", h.buddy_list_wrapper_max_height);
    $("#stream-filters-container").css("max-height", h.stream_filters_max_height);

    return h;
}

export function resize_page_components() {
    navbar_alerts.resize_app();
    const h = resize_sidebars();
    resize_bottom_whitespace(h);
}

let _old_width = $(window).width();

export function handler() {
    const new_width = $(window).width();

    if (new_width !== _old_width) {
        _old_width = new_width;
        condense.clear_message_content_height_cache();
    }

    // On mobile web, we want to avoid hiding a popover here,
    // especially if this resize was triggered by a virtual keyboard
    // popping up when the user opened that very popover.
    const mobile = util.is_mobile();
    if (!mobile) {
        popovers.hide_all();
    }
    resize_page_components();

    // Re-compute and display/remove [More] links to messages
    condense.condense_and_collapse($(".message_table .message_row"));

    // This function might run onReady (if we're in a narrow window),
    // but before we've loaded in the messages; in that case, don't
    // try to scroll to one.
    if (message_lists.current.selected_id() !== -1) {
        if (mobile) {
            popovers.set_suppress_scroll_hide();
        }

        navigate.scroll_to_selected();
    }
}
<commit_msg>resize: Hide popover on width change on mobile devices.<commit_after>import autosize from "autosize";
import $ from "jquery";

import * as blueslip from "./blueslip";
import * as condense from "./condense";
import * as message_lists from "./message_lists";
import * as message_viewport from "./message_viewport";
import * as navbar_alerts from "./navbar_alerts";
import * as navigate from "./navigate";
import * as popovers from "./popovers";
import * as ui from "./ui";
import {user_settings} from "./user_settings";
import * as util from "./util";

let narrow_window = false;

function confine_to_range(lo, val, hi) {
    if (val < lo) {
        return lo;
    }
    if (val > hi) {
        return hi;
    }
    return val;
}

function size_blocks(blocks, usable_height) {
    let sum_height = 0;

    for (const block of blocks) {
        sum_height += block.real_height;
    }

    for (const block of blocks) {
        let ratio = block.real_height / sum_height;
        ratio = confine_to_range(0.05, ratio, 0.85);
        block.max_height = confine_to_range(80, usable_height * ratio, 1.2 * block.real_height);
    }
}

function get_new_heights() {
    const res = {};
    const viewport_height = message_viewport.height();
    const top_navbar_height = $("#top_navbar").safeOuterHeight(true);
    const right_sidebar_shorcuts_height = $(".right-sidebar-shortcuts").safeOuterHeight(true) || 0;
    const add_streams_link_height = $("#add-stream-link").safeOuterHeight(true) || 0;

    res.bottom_whitespace_height = viewport_height * 0.4;

    res.main_div_min_height = viewport_height - top_navbar_height;

    res.stream_filters_max_height =
        viewport_height -
        Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
        $("#global_filters").safeOuterHeight(true) -
        $("#streams_header").safeOuterHeight(true) -
        add_streams_link_height;

    // Don't let us crush the stream sidebar completely out of view
    res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height);

    // RIGHT SIDEBAR

    const usable_height =
        viewport_height -
        Number.parseInt($("#right-sidebar").css("marginTop"), 10) -
        $("#userlist-header").safeOuterHeight(true) -
        $("#user_search_section").safeOuterHeight(true) -
        right_sidebar_shorcuts_height;

    res.buddy_list_wrapper_max_height = Math.max(80, usable_height);

    return res;
}

function left_userlist_get_new_heights() {
    const res = {};
    const viewport_height = message_viewport.height();
    const viewport_width = message_viewport.width();
    res.viewport_height = viewport_height;
    res.viewport_width = viewport_width;

    // main div
    const top_navbar_height = $(".header").safeOuterHeight(true);
    res.bottom_whitespace_height = viewport_height * 0.4;
    res.main_div_min_height = viewport_height - top_navbar_height;

    // left sidebar
    const stream_filters = $("#stream_filters").expectOne();
    const buddy_list_wrapper = $("#buddy_list_wrapper").expectOne();

    const stream_filters_real_height = stream_filters.prop("scrollHeight");
    const user_list_real_height = ui.get_scroll_element(buddy_list_wrapper).prop("scrollHeight");

    res.total_leftlist_height =
        viewport_height -
        Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
        Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
        $("#global_filters").safeOuterHeight(true) -
        $("#streams_header").safeOuterHeight(true) -
        $("#userlist-header").safeOuterHeight(true) -
        $("#user_search_section").safeOuterHeight(true) -
        Number.parseInt(stream_filters.css("marginBottom"), 10);

    const blocks = [
        {
            real_height: stream_filters_real_height,
        },
        {
            real_height: user_list_real_height,
        },
    ];

    size_blocks(blocks, res.total_leftlist_height);

    res.stream_filters_max_height = blocks[0].max_height;
    res.buddy_list_wrapper_max_height = blocks[1].max_height;

    return res;
}

export function watch_manual_resize(element) {
    const box = document.querySelector(element);

    if (!box) {
        blueslip.error("Bad selector in watch_manual_resize: " + element);
        return undefined;
    }

    const meta = {
        box,
        height: null,
        mousedown: false,
    };

    const box_handler = function () {
        meta.mousedown = true;
        meta.height = meta.box.clientHeight;
    };
    meta.box.addEventListener("mousedown", box_handler);

    // If the user resizes the textarea manually, we use the
    // callback to stop autosize from adjusting the height.
    // It will be re-enabled when this component is next opened.
    const body_handler = function () {
        if (meta.mousedown === true) {
            meta.mousedown = false;
            if (meta.height !== meta.box.clientHeight) {
                meta.height = meta.box.clientHeight;
                autosize.destroy($(element)).height(meta.height + "px");
            }
        }
    };
    document.body.addEventListener("mouseup", body_handler);

    return [box_handler, body_handler];
}

export function reset_compose_textarea_max_height(bottom_whitespace_height) {
    // If the compose-box is open, we set the `max-height` property of
    // `compose-textarea` so that the compose-box's maximum extent
    // does not overlap the last message in the current stream.is the
    // right size to leave a tiny bit of space after the last message
    // of the current stream.

    // Compute bottom_whitespace_height if not provided by caller.
    if (bottom_whitespace_height === undefined) {
        const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
        bottom_whitespace_height = h.bottom_whitespace_height;
    }

    const compose_height = Number.parseInt($("#compose").outerHeight(), 10);
    const compose_textarea_height = Number.parseInt($("#compose-textarea").outerHeight(), 10);
    const compose_non_textarea_height = compose_height - compose_textarea_height;

    $("#compose-textarea").css(
        "max-height",
        // The 10 here leaves space for the selected message border.
        bottom_whitespace_height - compose_non_textarea_height - 10,
    );
}

export function resize_bottom_whitespace(h) {
    $("#bottom_whitespace").height(h.bottom_whitespace_height);

    // The height of the compose box is tied to that of
    // bottom_whitespace, so update it if necessary.
    //
    // reset_compose_textarea_max_height cannot compute the right
    // height correctly while compose is hidden. This is OK, because
    // we also resize compose every time it is opened.
    if ($(".message_comp").is(":visible")) {
        reset_compose_textarea_max_height(h.bottom_whitespace_height);
    }
}

export function resize_stream_filters_container(h) {
    h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
    resize_bottom_whitespace(h);
    $("#stream-filters-container").css("max-height", h.stream_filters_max_height);
}

export function resize_sidebars() {
    let sidebar;

    if (user_settings.left_side_userlist) {
        const css_narrow_mode = message_viewport.is_narrow();

        $("#top_navbar").removeClass("rightside-userlist");

        const right_items = $(".right-sidebar-items").expectOne();

        if (css_narrow_mode && !narrow_window) {
            // move stuff to the left sidebar (skinny mode)
            narrow_window = true;
            popovers.set_userlist_placement("left");
            sidebar = $("#left-sidebar").expectOne();
            sidebar.append(right_items);
            $("#buddy_list_wrapper").css("margin", "0px");
            $("#userlist-toggle").css("display", "none");
            $("#invite-user-link").hide();
        } else if (!css_narrow_mode && narrow_window) {
            // move stuff to the right sidebar (wide mode)
            narrow_window = false;
            popovers.set_userlist_placement("right");
            sidebar = $("#right-sidebar").expectOne();
            sidebar.append(right_items);
            $("#buddy_list_wrapper").css("margin", "");
            $("#userlist-toggle").css("display", "");
            $("#invite-user-link").show();
        }
    }

    const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();

    $("#buddy_list_wrapper").css("max-height", h.buddy_list_wrapper_max_height);
    $("#stream-filters-container").css("max-height", h.stream_filters_max_height);

    return h;
}

export function resize_page_components() {
    navbar_alerts.resize_app();
    const h = resize_sidebars();
    resize_bottom_whitespace(h);
}

let _old_width = $(window).width();

export function handler() {
    const new_width = $(window).width();

    // On mobile web, we want to avoid hiding a popover here on height change,
    // especially if this resize was triggered by a virtual keyboard
    // popping up when the user opened that very popover.
    const mobile = util.is_mobile();
    if (!mobile || new_width !== _old_width) {
        popovers.hide_all();
    }

    if (new_width !== _old_width) {
        _old_width = new_width;
        condense.clear_message_content_height_cache();
    }
    resize_page_components();

    // Re-compute and display/remove [More] links to messages
    condense.condense_and_collapse($(".message_table .message_row"));

    // This function might run onReady (if we're in a narrow window),
    // but before we've loaded in the messages; in that case, don't
    // try to scroll to one.
    if (message_lists.current.selected_id() !== -1) {
        if (mobile) {
            popovers.set_suppress_scroll_hide();
        }

        navigate.scroll_to_selected();
    }
}
<|endoftext|>