<commit_before>const chartId = `${prefix}-chart`;
const chartLoginRequired = `${prefix}-chart-login-required`;
const chartUpgradeRequired = `${prefix}-chart-upgrade-required`;
const chartClass = `${prefix}-chart-btn`;
const chartClassDisabled = `${prefix}-chart-btn-disabled`;

const charts = [
    { column: 6, label: "Impressions / hour", config: {metric: 'impressions', rate: 'hour', chunk: 'hour', round: true} },
    { column: 7, label: "Clicks / day", config: {metric: 'clicks', rate: 'day', chunk: 'day', round: true} },
    { column: 9, label: "Spend / day", config: {metric: 'spend', rate: 'day', chunk: 'day', round: false} },
    { column: 10, label: "Sales / day", config: {metric: 'salesCount', rate: 'day', chunk: 'day', round: false} },
    { column: 11, label: "ACOS", config: {metric: 'acos', chunk: 'day', round: false} },
];

chrome.runtime.sendMessage({
    action: 'getAllowedCampaigns',
    entityId: getEntityId(),
},
mcatch(response => {
    if (response.error) {
        merror(response.status, response.error);
    }
    const allowedCampaigns = response.data || [];
    window.setInterval(mcatch(() => {
        let tableRows = $('#campaignTable tbody tr');
        addChartButtons(tableRows, allowedCampaigns);
    }), 100);
}));

const templateUrl = chrome.runtime.getURL('dashboard/templates.html');
$.ajax(templateUrl, {
    method: 'GET',
    success: (data) => {
        let dashboard = $('#campaignDashboard');
        dashboard.append(data);
    },
});

function addChartButtons(rows, allowedCampaigns) {
    for (let row of rows) {
        for (let chart of charts) {
            let cells = $(row).children();
            let target = cells[chart.column];
            if (!target || $(target).find(`.${chartClass}`).length > 0)
                continue;

            let select = $(cells[0]).find('select')[0];
            let name = cells[1].innerText;
            if (!select)
                continue;

            let selectName = select.name;
            let campaignId = selectName.split('_').pop();
            campaignId = campaignId.substring(0, campaignId.length - 13); // timestamp is appended for some reason

            let btnClasses = chartClass;
            let allowed = allowedCampaigns.includes(campaignId);
            let eventCategory = 'thumbnail-enabled';
            if (!allowed) {
                btnClasses += ` ${chartClassDisabled}`;
                eventCategory = 'thumbnail-disabled';
            }
            let btn = $(`<a href="#" class="${btnClasses}"><img src="${chartPng}" /></a>`);
            btn.click(mcatch(function() {
                mclick(eventCategory, chart.config.metric);

                let newId = chartId+campaignId+chart.config.metric;
                let popup = $('#'+newId);
                if (!popup.length) {
                    if (allowed) {
                        popup = $('#'+chartId).hide().clone();
                        popup.addClass(chartId);
                    }
                    else if (window.user.isAnon) {
                        popup = $('#'+chartLoginRequired).hide().clone();
                        popup.addClass(chartLoginRequired);
                    }
                    else {
                        popup = $('#'+chartUpgradeRequired).hide().clone();
                        popup.addClass(chartUpgradeRequired);
                    }
                    popup.attr('id', newId);
                    $(document.body).append(popup);
                }
                // hard-coded element width below b/c popup.width() doesn't
                // work as required. must reposition every time we display in
                // order to work correctly with scrolling.
                let pos = btn.offset();
                if (pos.left + 420 > $(document).width()) {
                    popup.css({top: pos.top + btn.height() + 6, left: pos.left + btn.width() - 414});
                }
                else {
                    popup.css({top: pos.top + btn.height() + 6, left: pos.left});
                }
                const bodyTop = $('body').scrollTop();
                const bodyLeft = $('body').scrollLeft();

                popup.slideDown(200, function() {
                    $('body').scrollTop(bodyTop);
                    $('body').scrollLeft(bodyLeft);

                    // Clicking anywhere outside the popup dismisses the chart
                    $(document).on('click.machete.thumbnail-dismiss', mcatch(function() {
                        if (!$.contains(popup[0], this)) {
                            mga('event', eventCategory, 'dismiss', chart.config.metric);
                            popup.hide();
                            $(document).off('click.machete.thumbnail-dismiss');
                        }
                    }));
                });

                if (allowed) {
                    getDataHistory(getEntityId(), campaignId, (data) => {
                        renderChart(data, name, Object.assign({id: newId}, chart));
                    });
                }
            }));
            $(target).append(btn);
        }
    }
}

function getDataHistory(entityId, campaignId, cb) {
    chrome.runtime.sendMessage({
        action: 'getDataHistory',
        entityId: entityId,
        campaignId: campaignId,
    },
    mcatch(response => {
        if (response.error) {
            merror(response.status, response.error);
            return;
        }
        cb(response.data);
    }));
}

function renderChart(data, name, opt) {
    const daysMs = 8 * span.day;
    opt.config.startTimestamp = Date.now() - daysMs;
    data = parallelizeHistoryData(data, opt.config);

    const series = {
      x: data.timestamps,
      y: data[opt.config.metric],
      mode: 'lines+markers',
      name: opt.config.metric,
      connectgaps: true
    };

    let height = 300;
    if (data.timestamps.length < 3) {
        height = 270; // leaving room for the lodata link
    }

    const layout = {
      title: `${opt.label}<br />${name}`,
      width: 400,
      height,
      margin: { l: 40, r: 20, b: 25, t: 60, pad: 4 },
    };

    Plotly.newPlot(opt.id, [series], layout, {displayModeBar: false});

    let container = $('#'+opt.id);
    if (data.timestamps.length < 3) {
        let a = container.find(`a.${prefix}-lodata`);
        a[0].href = chrome.runtime.getURL('common/low-data.html');
        a.show();
    }
}
<commit_msg>move the standard time window to 10 days on the dashboard<commit_after>const chartId = `${prefix}-chart`;
const chartLoginRequired = `${prefix}-chart-login-required`;
const chartUpgradeRequired = `${prefix}-chart-upgrade-required`;
const chartClass = `${prefix}-chart-btn`;
const chartClassDisabled = `${prefix}-chart-btn-disabled`;

const charts = [
    { column: 6, label: "Impressions / hour", config: {metric: 'impressions', rate: 'hour', chunk: 'hour', round: true} },
    { column: 7, label: "Clicks / day", config: {metric: 'clicks', rate: 'day', chunk: 'day', round: true} },
    { column: 9, label: "Spend / day", config: {metric: 'spend', rate: 'day', chunk: 'day', round: false} },
    { column: 10, label: "Sales / day", config: {metric: 'salesCount', rate: 'day', chunk: 'day', round: false} },
    { column: 11, label: "ACOS", config: {metric: 'acos', chunk: 'day', round: false} },
];

chrome.runtime.sendMessage({
    action: 'getAllowedCampaigns',
    entityId: getEntityId(),
},
mcatch(response => {
    if (response.error) {
        merror(response.status, response.error);
    }
    const allowedCampaigns = response.data || [];
    window.setInterval(mcatch(() => {
        let tableRows = $('#campaignTable tbody tr');
        addChartButtons(tableRows, allowedCampaigns);
    }), 100);
}));

const templateUrl = chrome.runtime.getURL('dashboard/templates.html');
$.ajax(templateUrl, {
    method: 'GET',
    success: (data) => {
        let dashboard = $('#campaignDashboard');
        dashboard.append(data);
    },
});

function addChartButtons(rows, allowedCampaigns) {
    for (let row of rows) {
        for (let chart of charts) {
            let cells = $(row).children();
            let target = cells[chart.column];
            if (!target || $(target).find(`.${chartClass}`).length > 0)
                continue;

            let select = $(cells[0]).find('select')[0];
            let name = cells[1].innerText;
            if (!select)
                continue;

            let selectName = select.name;
            let campaignId = selectName.split('_').pop();
            campaignId = campaignId.substring(0, campaignId.length - 13); // timestamp is appended for some reason

            let btnClasses = chartClass;
            let allowed = allowedCampaigns.includes(campaignId);
            let eventCategory = 'thumbnail-enabled';
            if (!allowed) {
                btnClasses += ` ${chartClassDisabled}`;
                eventCategory = 'thumbnail-disabled';
            }
            let btn = $(`<a href="#" class="${btnClasses}"><img src="${chartPng}" /></a>`);
            btn.click(mcatch(function() {
                mclick(eventCategory, chart.config.metric);

                let newId = chartId+campaignId+chart.config.metric;
                let popup = $('#'+newId);
                if (!popup.length) {
                    if (allowed) {
                        popup = $('#'+chartId).hide().clone();
                        popup.addClass(chartId);
                    }
                    else if (window.user.isAnon) {
                        popup = $('#'+chartLoginRequired).hide().clone();
                        popup.addClass(chartLoginRequired);
                    }
                    else {
                        popup = $('#'+chartUpgradeRequired).hide().clone();
                        popup.addClass(chartUpgradeRequired);
                    }
                    popup.attr('id', newId);
                    $(document.body).append(popup);
                }
                // hard-coded element width below b/c popup.width() doesn't
                // work as required. must reposition every time we display in
                // order to work correctly with scrolling.
                let pos = btn.offset();
                if (pos.left + 420 > $(document).width()) {
                    popup.css({top: pos.top + btn.height() + 6, left: pos.left + btn.width() - 414});
                }
                else {
                    popup.css({top: pos.top + btn.height() + 6, left: pos.left});
                }
                const bodyTop = $('body').scrollTop();
                const bodyLeft = $('body').scrollLeft();

                popup.slideDown(200, function() {
                    $('body').scrollTop(bodyTop);
                    $('body').scrollLeft(bodyLeft);

                    // Clicking anywhere outside the popup dismisses the chart
                    $(document).on('click.machete.thumbnail-dismiss', mcatch(function() {
                        if (!$.contains(popup[0], this)) {
                            mga('event', eventCategory, 'dismiss', chart.config.metric);
                            popup.hide();
                            $(document).off('click.machete.thumbnail-dismiss');
                        }
                    }));
                });

                if (allowed) {
                    getDataHistory(getEntityId(), campaignId, (data) => {
                        renderChart(data, name, Object.assign({id: newId}, chart));
                    });
                }
            }));
            $(target).append(btn);
        }
    }
}

function getDataHistory(entityId, campaignId, cb) {
    chrome.runtime.sendMessage({
        action: 'getDataHistory',
        entityId: entityId,
        campaignId: campaignId,
    },
    mcatch(response => {
        if (response.error) {
            merror(response.status, response.error);
            return;
        }
        cb(response.data);
    }));
}

function renderChart(data, name, opt) {
    const daysMs = 10 * span.day;
    opt.config.startTimestamp = Date.now() - daysMs;
    data = parallelizeHistoryData(data, opt.config);

    const series = {
      x: data.timestamps,
      y: data[opt.config.metric],
      mode: 'lines+markers',
      name: opt.config.metric,
      connectgaps: true
    };

    let height = 300;
    if (data.timestamps.length < 3) {
        height = 270; // leaving room for the lodata link
    }

    const layout = {
      title: `${opt.label}<br />${name}`,
      width: 400,
      height,
      margin: { l: 40, r: 20, b: 25, t: 60, pad: 4 },
    };

    Plotly.newPlot(opt.id, [series], layout, {displayModeBar: false});

    let container = $('#'+opt.id);
    if (data.timestamps.length < 3) {
        let a = container.find(`a.${prefix}-lodata`);
        a[0].href = chrome.runtime.getURL('common/low-data.html');
        a.show();
    }
}
<|endoftext|>


<commit_before>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.021'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var Logicat = 0;
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var NinjaAutoClickStatus = 1;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

var start = -1;
var content = "empty";
var length = 0;
var RKLevel = 'abc123';

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function RedundaKitty() {
	//Refresh Timer Variable
	i = Molpy.redactedToggle - Molpy.redactedCountup;

	if (Molpy.redactedVisible > 0) {
		Molpy.Notify('Redacted Visible: ' + Molpy.redactedVisible, 1);
		/*
		RV of 1 is Sand Tools
		RV of 2 is Castle Tools
		RV of 3 is Boosts Main Page
		RV of 4 is Boosts Menu, Hill People Tech
		RV of 5 is Badges Earned?
		RV of 6 is Badges Available?
		*/

		if (Molpy.redactedVisible == 1 || Molpy.redactedVisible == 2 || Molpy.redactedVisible == 3) {
			//Do Nothing
		}
		else if (Molpy.redactedVisible == 5) {
			//showhideToggle('badges');
		}

		//Determines if it is a Logicat. It it is, Logicat = 1, otherwise 0
		var content = $('#redacteditem').html();
		if (content.indexOf("statement") !== -1) {
			Logicat = 1;
			Molpy.Notify("Logicat Found",0);
		}
		else {
			Logicat = 0;
			Molpy.Notify("Redundakitty Found",0);
			start = content.indexOf("ClickRedacted");
			content = content.substring(start,start+17);
			content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
			var length = content.length;
			RKLevel = content.substring(13,length);
			Molpy.Notify('RedundaKitty Level is: ' + RKLevel,0);
		}

		//Highlight Redundakitty Border to make it easier to find
		$('#redacteditem').css("border","2px solid red");

		//Clicks if RedundaKitty AutoClicker Enabled and Not a Logicat
		if (RKAutoClickStatus == 1 && Logicat == 0) {
			Molpy.ClickRedacted(RKLevel);
			//$(":button").click();
		}
		else if (LCAutoClickStatus == 1 && Logicat == 1) {
			Logicat();
		}
		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0 && (AudioAlertsStatus == 1 || AudioAlertsStatus == 3)) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = Molpy.redactedToggle-Molpy.redactedCountup;
		if (i != 0) { i = 0; }
	}
}

function Logicat() {
	i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
	Molpy.redactedPuzzleTarget = undefined;

	/*while (Molpy.redactedPuzzleTarget != undefined) {
		LCSolution = String.fromCharCode(i);
		if (Molpy.redactedPuzzleTarget == Molpy.redactedSGen.StatementValue(LCSolution)) {
			Molpy.ClickRedactedPuzzle(LCSolution);
			Molpy.redactedPuzzleTarget = undefined;
		}
		else {i++;}
	}*/
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 1) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'RKAutoClick' || option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error',1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only';}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG';}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status,1);}
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);

Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1)

function MainLoop() {
	setInterval(function() {
		Molpy.Notify('1 mNP', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, 1800);
}

//Run Main Loop after 5 second startup delay
setTimeout(MainLoop, 20000);

<commit_msg>Main Loop Start Up Delay Implemented<commit_after>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.021'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var Logicat = 0;
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var NinjaAutoClickStatus = 1;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

var start = -1;
var content = "empty";
var length = 0;
var RKLevel = 'abc123';

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function RedundaKitty() {
	//Refresh Timer Variable
	i = Molpy.redactedToggle - Molpy.redactedCountup;

	if (Molpy.redactedVisible > 0) {
		Molpy.Notify('Redacted Visible: ' + Molpy.redactedVisible, 1);
		/*
		RV of 1 is Sand Tools
		RV of 2 is Castle Tools
		RV of 3 is Boosts Main Page
		RV of 4 is Boosts Menu, Hill People Tech
		RV of 5 is Badges Earned?
		RV of 6 is Badges Available?
		*/

		if (Molpy.redactedVisible == 1 || Molpy.redactedVisible == 2 || Molpy.redactedVisible == 3) {
			//Do Nothing
		}
		else if (Molpy.redactedVisible == 5) {
			//showhideToggle('badges');
		}

		//Determines if it is a Logicat. It it is, Logicat = 1, otherwise 0
		var content = $('#redacteditem').html();
		if (content.indexOf("statement") !== -1) {
			Logicat = 1;
			Molpy.Notify("Logicat Found",0);
		}
		else {
			Logicat = 0;
			Molpy.Notify("Redundakitty Found",0);
			start = content.indexOf("ClickRedacted");
			content = content.substring(start,start+17);
			content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
			var length = content.length;
			RKLevel = content.substring(13,length);
			Molpy.Notify('RedundaKitty Level is: ' + RKLevel,0);
		}

		//Highlight Redundakitty Border to make it easier to find
		$('#redacteditem').css("border","2px solid red");

		//Clicks if RedundaKitty AutoClicker Enabled and Not a Logicat
		if (RKAutoClickStatus == 1 && Logicat == 0) {
			Molpy.ClickRedacted(RKLevel);
			//$(":button").click();
		}
		else if (LCAutoClickStatus == 1 && Logicat == 1) {
			Logicat();
		}
		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0 && (AudioAlertsStatus == 1 || AudioAlertsStatus == 3)) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = Molpy.redactedToggle-Molpy.redactedCountup;
		if (i != 0) { i = 0; }
	}
}

function Logicat() {
	i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
	Molpy.redactedPuzzleTarget = undefined;

	/*while (Molpy.redactedPuzzleTarget != undefined) {
		LCSolution = String.fromCharCode(i);
		if (Molpy.redactedPuzzleTarget == Molpy.redactedSGen.StatementValue(LCSolution)) {
			Molpy.ClickRedactedPuzzle(LCSolution);
			Molpy.redactedPuzzleTarget = undefined;
		}
		else {i++;}
	}*/
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 1) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'RKAutoClick' || option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error',1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only';}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG';}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status,1);}
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);

function MainLoop() {
	Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1)

	setInterval(function() {
		Molpy.Notify('1 mNP', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, 1800);
}

//Run Main Loop after 3 second startup delay
setTimeout(MainLoop, 3000);

<|endoftext|>


<commit_before>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.021'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var Logicat = 0;
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var NinjaAutoClickStatus = 1;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

var start = -1;
var content = "empty";
var length = 0;
var RKLevel = '-1';
var RKLocation = 'null';
var lootBoxes = new Array();
	lootBoxes[0] = 'boosts';
	lootBoxes[1] = 'ninj';
	lootBoxes[2] = 'cyb';
	lootBoxes[3] = 'hpt';
	lootBoxes[4] = 'bean';
	lootBoxes[5] = 'chron';

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location, do nothing for locations 1, 2 or 3
	location = 'null';
	if (Molpy.redactedVisible == 4) {
		i = 0;
		while (location = 'null' || i < 6) {
			Molpy.Notify(lootBoxes[i], 1)
			if ($('#' + lootboxes[i]).length) {
				showhideToggle(lootBoxes[i]);
				if ($('#redacteditem').length) {
					location = lootBoxes[i];
				}
				else {
					showhideToggle(lootBoxes[i]);
				}
			}
			i++;
			Molpy.Notify(i, 1);
		}
	}
	else if (Molpy.redactedVisible == 5) {
		location = 'badges';
	}
	else if (Molpy.redactedVisible == 6) {
		location = 'badgesav';
	}
	return location;
}

function RedundaKitty() {
	//Refresh Timer Variable
	i = Molpy.redactedToggle - Molpy.redactedCountup;

	//If RedundaKitty is available
	if (Molpy.redactedVisible > 0) {
		RKLocation = FindRK();

		if (RKLocation != 'null' && !Molpy.options.showhide[RKLocation]) {
			showhideToggle(RKLocation);
		}

		//Determines if it is a Logicat or RK
		content = $('#redacteditem').html();
		if (content.indexOf("statement") !== -1) {
			Logicat = 1;
			//Molpy.Notify("Logicat Found",0);
		}
		else {
			Logicat = 0;
			//Molpy.Notify("Redundakitty Found",0);
			start = content.indexOf("ClickRedacted");
			content = content.substring(start,start+17);
			content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
			var length = content.length;
			RKLevel = content.substring(13,length);
			Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
		}

		//Highlights RK/LC Border to make it easier to find
		$('#redacteditem').css("border","2px solid red");

		//Clicks if RedundaKitty AutoClicker Enabled and Not a Logicat
		if (RKAutoClickStatus == 1 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			if (RKLocation != 'null') {
				showhideToggle(RKLocation);
				RKLocation = 'null';
			}
		}
		else if (LCAutoClickStatus == 1 && Logicat == 1) {
			Logicat();
			if (RKLocation != 'null') {
				showhideToggle(RKLocation);
				RKLocation = 'null';
			}
		}
		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0 && (AudioAlertsStatus == 1 || AudioAlertsStatus == 3)) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = i;
		//if (i != 0) { i = 0; }
	}
}

function Logicat() {
	i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);

	/*while (Molpy.redactedPuzzleTarget != undefined) {
		LCSolution = String.fromCharCode(i);
		if (Molpy.redactedPuzzleTarget == Molpy.redactedSGen.StatementValue(LCSolution)) {
			Molpy.ClickRedactedPuzzle(LCSolution);
			Molpy.redactedPuzzleTarget = undefined;
		}
		else {i++;}
	}*/
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 1) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'RKAutoClick' || option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error',1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only';}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG';}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status,1);}
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Spawn RK

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);

function MainLoop() {
	Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
	setInterval(function() {
		//Molpy.Notify('1 mNP', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, 1800);
}

//Run Main Loop after 3 second startup delay
setTimeout(MainLoop, 3000);

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 5;
}

<commit_msg>RK Location 4 work Bug Fixing<commit_after>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.021'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var Logicat = 0;
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var NinjaAutoClickStatus = 1;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

var start = -1;
var content = "empty";
var length = 0;
var RKLevel = '-1';
var RKLocation = 'null';
var lootBoxes = new Array();
	lootBoxes[0] = 'boosts';
	lootBoxes[1] = 'ninj';
	lootBoxes[2] = 'cyb';
	lootBoxes[3] = 'hpt';
	lootBoxes[4] = 'bean';
	lootBoxes[5] = 'chron';

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location, do nothing for locations 1, 2 or 3
	location = 'null';
	if (Molpy.redactedVisible == 4) {
		i = 0;
		while (location = 'null' || i < 6) {
			Molpy.Notify(lootBoxes[i], 1)
			if ($('#' + lootboxes[i]).length) {
				showhideToggle(lootBoxes[i]);
				if ($('#redacteditem').length) {
					location = lootBoxes[i];
				}
				else {
					showhideToggle(lootBoxes[i]);
				}
			}
			i++;
			Molpy.Notify(i, 1);
		}
	}
	else if (Molpy.redactedVisible == 5) {
		location = 'badges';
	}
	else if (Molpy.redactedVisible == 6) {
		location = 'badgesav';
	}
	return location;
}

function RedundaKitty() {
	//Refresh Timer Variable
	i = Molpy.redactedToggle - Molpy.redactedCountup;
	RKLocation = 'null';

	//If RedundaKitty is available
	if (Molpy.redactedVisible > 0) {
		RKLocation = FindRK();

		if (RKLocation != 'null' && !Molpy.options.showhide[RKLocation]) {
			showhideToggle(RKLocation);
		}

		//Determines if it is a Logicat or RK
		content = $('#redacteditem').html();
		if (content.indexOf("statement") !== -1) {
			Logicat = 1;
			//Molpy.Notify("Logicat Found",0);
		}
		else {
			Logicat = 0;
			//Molpy.Notify("Redundakitty Found",0);
			start = content.indexOf("ClickRedacted");
			content = content.substring(start,start+17);
			content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
			var length = content.length;
			RKLevel = content.substring(13,length);
			Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
		}

		//Highlights RK/LC Border to make it easier to find
		$('#redacteditem').css("border","2px solid red");

		//Clicks if RedundaKitty AutoClicker Enabled and Not a Logicat
		if (RKAutoClickStatus == 1 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			if (RKLocation != 'null') {
				showhideToggle(RKLocation);
				RKLocation = 'null';
			}
		}
		else if (LCAutoClickStatus == 1 && Logicat == 1) {
			Logicat();
			if (RKLocation != 'null') {
				showhideToggle(RKLocation);
				RKLocation = 'null';
			}
		}
		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0 && (AudioAlertsStatus == 1 || AudioAlertsStatus == 3)) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = i;
		//if (i != 0) { i = 0; }
	}
}

function Logicat() {
	i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);

	/*while (Molpy.redactedPuzzleTarget != undefined) {
		LCSolution = String.fromCharCode(i);
		if (Molpy.redactedPuzzleTarget == Molpy.redactedSGen.StatementValue(LCSolution)) {
			Molpy.ClickRedactedPuzzle(LCSolution);
			Molpy.redactedPuzzleTarget = undefined;
		}
		else {i++;}
	}*/
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 1) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'RKAutoClick' || option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error',1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only';}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG';}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status,1);}
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Spawn RK

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);

function MainLoop() {
	Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
	setInterval(function() {
		//Molpy.Notify('1 mNP', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, 1800);
}

//Run Main Loop after 3 second startup delay
setTimeout(MainLoop, 3000);

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 5;
}

<|endoftext|>


<commit_before>var fs = require('fs'),
    http = require('http'),
    url = require('url');

/**
 * A library of net-interaction functions - this could be simplified
 */

module.exports = {
    /**
     * Download a file and return data
     *
     * @param {String} file_url the URI of the file.
     * @param {String} filename the filename on the system.
     * @param {String} encoding the encoding to use for the file.
     * @param {Function} callback to call after finishing the download and
     *   run with arguments [err, filename, data].
     */
    get: function(file_url, filename, encoding, callback) {
        var encoding = encoding || 'binary';
        var file_url = url.parse(file_url);
        var c = http.createClient(file_url.port || 80, file_url.hostname);
        var request = c.request('GET', file_url.pathname + '?' + file_url.query, {
            host: file_url.host,
            query: file_url.query
        });
        request.end();

        var body = [];
        request.on('response', function(response) {
            response.setEncoding(encoding);
            response.on('data', function(chunk) {
                body.push(chunk);
            });
            response.on('end', function() {
                body = body.join("");
                callback(null, filename, body);
            });
            response.on('error', function(err) {
                callback(err, null);
            });
        });
    },

    /**
     * Download a file
     *
     * @param {String} file_url the URI of the file.
     * @param {String} filename the filename on the system.
     * @param {String} encoding the encoding to use for the file.
     * @param {Function} callback to call after finishing the download and
     *   run with arguments [err, filename, data].
     */
    download: function(file_url_raw, filename, encoding, callback) {
        var encoding = encoding || 'binary';
        var file_url = url.parse(file_url_raw);
        var c = http.createClient(file_url.port || 80, file_url.hostname);
        var request = c.request('GET', file_url.pathname + '?' + file_url.query, {
            host: file_url.hostname
        });
        request.end();

        var body = [];
        request.on('response', function(response) {
            response.setEncoding(encoding);
            response.on('data', function(chunk) {
                body.push(chunk);
            });
            response.on('end', function() {
                body = body.join("");
                fs.writeFile(filename, body, encoding, function() {
                    callback(null, file_url_raw, filename);
                });
            });
            response.on('error', function(err) {
                callback(err, null);
            });
        });
    },

    /**
     * Encode a string as base64
     *
     * TODO: actually make safe64 by chunking
     *
     * @param {String} s the string to be encoded.
     * @return {String} base64 encoded string.
     */
    safe64: function(s) {
        var b = new Buffer(s, 'utf-8');
        return b.toString('base64').replace('/', '_').replace('+', '-');
    }
};
<commit_msg>Check that file_url is valid before making an HTTP request.<commit_after>var fs = require('fs'),
    http = require('http'),
    url = require('url');

/**
 * A library of net-interaction functions - this could be simplified
 */

module.exports = {
    /**
     * Download a file and return data
     *
     * @param {String} file_url the URI of the file.
     * @param {String} filename the filename on the system.
     * @param {String} encoding the encoding to use for the file.
     * @param {Function} callback to call after finishing the download and
     *   run with arguments [err, filename, data].
     */
    get: function(file_url, filename, encoding, callback) {
        var encoding = encoding || 'binary';
        var file_url = url.parse(file_url);

        // Check that the URL is valid.
        if (!file_url.hostname) return callback(new Error('Invalid URL'));

        var c = http.createClient(file_url.port || 80, file_url.hostname);
        var request = c.request('GET', file_url.pathname + '?' + file_url.query, {
            host: file_url.host,
            query: file_url.query
        });
        request.end();

        var body = [];
        request.on('response', function(response) {
            response.setEncoding(encoding);
            response.on('data', function(chunk) {
                body.push(chunk);
            });
            response.on('end', function() {
                body = body.join("");
                callback(null, filename, body);
            });
            response.on('error', function(err) {
                callback(err, null);
            });
        });
    },

    /**
     * Download a file
     *
     * @param {String} file_url the URI of the file.
     * @param {String} filename the filename on the system.
     * @param {String} encoding the encoding to use for the file.
     * @param {Function} callback to call after finishing the download and
     *   run with arguments [err, filename, data].
     */
    download: function(file_url_raw, filename, encoding, callback) {
        var encoding = encoding || 'binary';
        var file_url = url.parse(file_url_raw);

        // Check that the URL is valid.
        if (!file_url.hostname) return callback(new Error('Invalid URL'));

        var c = http.createClient(file_url.port || 80, file_url.hostname);
        var request = c.request('GET', file_url.pathname + '?' + file_url.query, {
            host: file_url.hostname
        });
        request.end();

        var body = [];
        request.on('response', function(response) {
            response.setEncoding(encoding);
            response.on('data', function(chunk) {
                body.push(chunk);
            });
            response.on('end', function() {
                body = body.join("");
                fs.writeFile(filename, body, encoding, function() {
                    callback(null, file_url_raw, filename);
                });
            });
            response.on('error', function(err) {
                callback(err, null);
            });
        });
    },

    /**
     * Encode a string as base64
     *
     * TODO: actually make safe64 by chunking
     *
     * @param {String} s the string to be encoded.
     * @return {String} base64 encoded string.
     */
    safe64: function(s) {
        var b = new Buffer(s, 'utf-8');
        return b.toString('base64').replace('/', '_').replace('+', '-');
    }
};
<|endoftext|>


<commit_before>var http   = require("http"),
    url    = require("url"),
    wompt  = require("./includes"),
    util   = require('util'),
    logger = wompt.logger,
    SocketIO= wompt.socketIO,
    assetManager = require('./asset_manager'),
    Hoptoad = require('./hoptoad'),
    express = wompt.dependencies.express;

function App(options){
	this.config = options.config;
	this.pretty_print_config();
	this.meta_users = new wompt.MetaUserManager();
	this.clients = new wompt.ClientPool();
	this.express = this.create_express_server();

	// default namespace
	this.channels =	this.chatNamespace('chat', {logged: true, allowIframe: true});

	// other namespaces
	this.chatNamespace('unlisted', {logged: true});

	this.client_connectors = new wompt.ClientConnectors();
	this.popular_channels = new wompt.monitors.PopularChannels(this.channels);

	this.appStateMonitor = new wompt.monitors.AppState(this, wompt.env.logs.monitor);
	this.appStateLogger = new wompt.loggers.AppStateLogger(wompt.util.mergeCopy(wompt.env.logs.monitor, {
		path: wompt.env.logs.root + '/app_state.log',
		monitor: this.appStateMonitor
	}));
}

App.prototype = {
	pretty_print_config: function(){
		logger.log("Wompt.com started with config: ");
		logger.dir(this.config);
	},

	create_express_server:function(){
		var config = this.config;
		var exp = express.createServer();
		var me = this;

		exp.configure(function(){
			exp.use(me.statusMiddleware());
			exp.use(wompt.Auth.forward_to_auth_app_middleware());
			exp.use(express.logger({format: ':method :url :status :response-time' }));
			exp.use(assetManager.middleware);
			exp.use(express.favicon(config.public_dir + '/favicon.ico'));
			exp.use(express.static(config.public_dir, {maxAge:1000*60*60*24*14})); // 2 weeks
			exp.use(express.cookieParser());
			exp.use(express.bodyParser());
			exp.use(wompt.Auth.one_time_token_middleware());
			exp.use(wompt.Auth.lookup_user_middleware());
			exp.use(wompt.Auth.meta_user_middleware(me.meta_users));
			exp.error(Hoptoad.expressErrorNotifier);
		});

		exp.helpers({
			assets: new wompt.Helpers.Assets(this.config.root + '/public'),
			assetHelpers: assetManager.helpers,
			cacheTimeStamps: assetManager.middleware.cacheTimestamps
		});

		exp.set('views', wompt.env.root + '/views');
		exp.set('view options',{layout: 'layouts/standard.jade'});
		exp.set('view engine', 'jade');
		exp.register('jade', wompt.dependencies.jade);
		exp.register('html', require('./templating/raw_html')({stripNewlines: true}));

		this.plain_routes(exp, [
			,'/support'
			,'/terms'
			,'/privacy'
		]);

		exp.get("/re-authenticate", function(req, res, next){
			if(req.meta_user && req.meta_user.authenticated()){
				var token = wompt.Auth.get_or_set_token(req, res);
				var connector = me.client_connectors.add({
					meta_user:req.meta_user,
					token: token
				});
				res.send({connector_id:connector.id, version_hash:wompt.env.constants.version_hash});
			}else next();
		});

		exp.get("/", landingPage);

		exp.get("/profile", function(req, res){
			if(req.meta_user && req.meta_user.authenticated())
				res.redirect('/users/' + req.meta_user.id())
			else
				res.redirect('/');
		});

		exp.get("/users/:id", function(req, res){
			if(req.params.id && req.user && req.meta_user.id() == req.params.id){
				//User record is already loaded but auth middleware, no need to load again
				profilePage(req, res, req.user);
			}
			else{
				wompt.User.findById(req.params.id, function(err, user){
					profilePage(req, res, user);
				});
			}
		});

		exp.get("/channels/search", function(req, res){
			var results = [], term, terms;
			(term = req.query) && (term = term.term) && (term = term.toLowerCase());
			// Limit length, split on spaces, remove blank terms, enforce maximum term count
			terms = term.substr(0,50).split(' ').filter(function(t){return t.length > 0 ;}).slice(0,5);

			if(terms.length > 1){
				me.channels.each(function(channel){
					var name = channel.name;
					if(terms.every(function(term){return name.indexOf(term) >= 0;})) results.push(channel);
					if(results.length >= max_results) return true;
				});
			} else if(term = terms[0]){
				me.channels.each(function(channel){
					if(channel.name.indexOf(term) >= 0) results.push(channel);
					if(results.length >= max_results) return true;
				});
			}
			results = results.map(function(channel){ return channel.name});

			res.writeHead(200, {"Content-Type":"application/json"});
			res.end(JSON.stringify(results));
		});

		exp.post("/", function(req, res){
			wompt.Auth.get_or_set_token(req, res);
			res.redirect('/chat/' + req.body.channel);
		});

		exp.get("/unlisted/new", function(req, res){
			var name = wompt.Auth.random_string(10);
			res.redirect('/unlisted/' + name);
		});

		if(wompt.env.force_sign_in){
			exp.get("/users/force_sign_in/:num", function(req, res){
				var num = parseInt(req.params.num);
				wompt.User.findOne({}, function(err, user){
					if(user){
						wompt.Auth.start_session(res, user);
					}
					res.redirect(req.headers.referer || '/');
				});
			});
		}

		exp.get("/user/sign_out", function(req, res){
			wompt.Auth.sign_out_user(req, res, function(){
				res.redirect(req.headers.referer || '/');
			});
		});

		exp.get("/admin/stats", wompt.Auth.adminMiddleware, function(req, res){
			res.render('admin/stats', {
				locals: {w: me},
				layout: 'admin/layout'
			});
		});

		function landingPage(req, res){
			wompt.Auth.get_or_set_token(req, res);
			res.render('index', {
				locals: me.standard_page_vars(req, {
					app:me,
					jquery: true,
					page_js: 'landing',
					page_name:'landing',
					subtitle: me.choose_subtitle()
				})
			});
		}

		function profilePage(req, res, user){
			if(!user) return res.send("", 404);

			res.render('profile', {
				locals: me.standard_page_vars(req, {
					app:me,
					profileUser: user,
					jquery: true,
					page_js: 'profile'
				})
			});
		}

		return exp;
	},

	standard_page_vars: function(req, custom_vars){
		var vars = {
			jquery: false,
			url: req.url,
			user: req.meta_user,
			footer: true,
			host: req.headers.host,
			config: this.config
		};

		if(custom_vars)
			wompt.util.merge(vars, custom_vars);

		return {
			w:vars
		};
	},

	choose_subtitle: function(){
		var subtitles = [
			 "Try it free, no signup required"
			,"Always free, sign in using Facebook"
			,"Speak <tt>c0d3?</tt> sign in using Github"
		];
		return subtitles[Math.floor(Math.random() * subtitles.length)];
	},

	start_server: function(){
		this.express.listen(this.config.port, this.config.host);
		this.start_socket_io();

		logger.log("Starting server on port: " + this.config.port);
	},

	start_socket_io: function(){
		if(this.socket) return;
		var app = this;

		this.socket = SocketIO.listen(this.express, this.config.socketIO.serverOptions);
		this.socket.on('connection', function(client){
			app.new_connection(client);
		});
	},

	new_connection: function(client){
		logger.log("Connection from: " + client.sessionId);
		var app = this;

		this.clients.add(client);

		client.once('message', function(data){
			if(data && data.action == 'join'){
				var connector = app.client_connectors.get(data.connector_id);

				if(!connector) {
					client.send("You must pass your connector_id with the join message");
					return client._onDisconnect();
				}

				var namespace = connector.namespace || app.namespaces[data.namespace],
				    user      = connector.meta_user || new wompt.MetaUser();

				client.user = user;

				if(!client.uid){
					//When the user signs out, we can still know the user id while cleaning up the client
					client.uid = user.id();
					// Because this client didn't have a uid when it was first added
					app.clients.inc_user_count(client);
				}

				logger.log('Handing off client:' + client.sessionId + ' to Channel: ' + data.channel)
				var channel = namespace.get(data.channel);
				if(channel){
					channel.add_client(client, connector && connector.token, data);
				}
				user.clients.add(client);
			}
		});
	},

	statusMiddleware: function(){
		return function(req, res, next){
			if(req.url == '/ok'){
				res.writeHead(200, {'Content-Type': 'text/plain'});
				res.end("OK");
			}else{
				next();
			}
		};
	},

	chatNamespace: function(namespace, options){
		this.namespaces = this.namespaces || {};
		otions = options || {};

		var me = this,
		exp = this.express,
		channelManager = new wompt.ChannelManager();

		this.namespaces[namespace] = channelManager;

		if(options.logged){
			new wompt.loggers.LoggerCreator(channelManager, namespace);
		}

		var argumentsForGet = [new RegExp("\\/" + namespace + "\\/(.+)")];
		if(options.fakeUsers) argumentsForGet.push(wompt.Auth.fake_user_middleware());

		function handleChatRoomGet(req, res){
			if(req.url.substr(-1,1) == '/'){
				return res.redirect(wompt.util.chop(req.url));
			}

			var meta_user = req.meta_user,
					channel = req.params[0];

			var token = wompt.Auth.get_or_set_token(req, res);

			var connector = me.client_connectors.add({
				meta_user:meta_user,
				namespace:channelManager,
				token: token
			});

			var locals = me.standard_page_vars(req, {
				channel: channel,
				namespace: namespace,
				connector_id: connector.id,
				url: req.url,
				jquery: true,
				page_name: 'chat',
				page_js: 'channel'
			});

			var opt = {
				locals:locals
			};

			if(options.allowIframe && req.query.iframe == '1'){
				opt.layout = 'layouts/iframe';
				locals.w.ga_source = 'embedd';
				if(options.allowCSS) locals.w.css_file = req.query.css_file;
			}

			res.render('chat', opt);
		}

		argumentsForGet.push(handleChatRoomGet);
		exp.get.apply(exp, argumentsForGet);

		return channelManager;
	},

	plain_routes: function(exp, urls){
		var me = this;
		urls.forEach(function(url){
			var view = url.substr(1);
			exp.get(url, function(req, res){
				res.render(view, {
						locals: me.standard_page_vars(req)
					});
			});
		});
	},
}

module.exports = App;
<commit_msg>Fix landing page auto-suggestion input box bug<commit_after>var http   = require("http"),
    url    = require("url"),
    wompt  = require("./includes"),
    util   = require('util'),
    logger = wompt.logger,
    SocketIO= wompt.socketIO,
    assetManager = require('./asset_manager'),
    Hoptoad = require('./hoptoad'),
    express = wompt.dependencies.express;

function App(options){
	this.config = options.config;
	this.pretty_print_config();
	this.meta_users = new wompt.MetaUserManager();
	this.clients = new wompt.ClientPool();
	this.express = this.create_express_server();

	// default namespace
	this.channels =	this.chatNamespace('chat', {logged: true, allowIframe: true});

	// other namespaces
	this.chatNamespace('unlisted', {logged: true});

	this.client_connectors = new wompt.ClientConnectors();
	this.popular_channels = new wompt.monitors.PopularChannels(this.channels);

	this.appStateMonitor = new wompt.monitors.AppState(this, wompt.env.logs.monitor);
	this.appStateLogger = new wompt.loggers.AppStateLogger(wompt.util.mergeCopy(wompt.env.logs.monitor, {
		path: wompt.env.logs.root + '/app_state.log',
		monitor: this.appStateMonitor
	}));
}

App.prototype = {
	pretty_print_config: function(){
		logger.log("Wompt.com started with config: ");
		logger.dir(this.config);
	},

	create_express_server:function(){
		var config = this.config;
		var exp = express.createServer();
		var me = this;

		exp.configure(function(){
			exp.use(me.statusMiddleware());
			exp.use(wompt.Auth.forward_to_auth_app_middleware());
			exp.use(express.logger({format: ':method :url :status :response-time' }));
			exp.use(assetManager.middleware);
			exp.use(express.favicon(config.public_dir + '/favicon.ico'));
			exp.use(express.static(config.public_dir, {maxAge:1000*60*60*24*14})); // 2 weeks
			exp.use(express.cookieParser());
			exp.use(express.bodyParser());
			exp.use(wompt.Auth.one_time_token_middleware());
			exp.use(wompt.Auth.lookup_user_middleware());
			exp.use(wompt.Auth.meta_user_middleware(me.meta_users));
			exp.error(Hoptoad.expressErrorNotifier);
		});

		exp.helpers({
			assets: new wompt.Helpers.Assets(this.config.root + '/public'),
			assetHelpers: assetManager.helpers,
			cacheTimeStamps: assetManager.middleware.cacheTimestamps
		});

		exp.set('views', wompt.env.root + '/views');
		exp.set('view options',{layout: 'layouts/standard.jade'});
		exp.set('view engine', 'jade');
		exp.register('jade', wompt.dependencies.jade);
		exp.register('html', require('./templating/raw_html')({stripNewlines: true}));

		this.plain_routes(exp, [
			,'/support'
			,'/terms'
			,'/privacy'
		]);

		exp.get("/re-authenticate", function(req, res, next){
			if(req.meta_user && req.meta_user.authenticated()){
				var token = wompt.Auth.get_or_set_token(req, res);
				var connector = me.client_connectors.add({
					meta_user:req.meta_user,
					token: token
				});
				res.send({connector_id:connector.id, version_hash:wompt.env.constants.version_hash});
			}else next();
		});

		exp.get("/", landingPage);

		exp.get("/profile", function(req, res){
			if(req.meta_user && req.meta_user.authenticated())
				res.redirect('/users/' + req.meta_user.id())
			else
				res.redirect('/');
		});

		exp.get("/users/:id", function(req, res){
			if(req.params.id && req.user && req.meta_user.id() == req.params.id){
				//User record is already loaded but auth middleware, no need to load again
				profilePage(req, res, req.user);
			}
			else{
				wompt.User.findById(req.params.id, function(err, user){
					profilePage(req, res, user);
				});
			}
		});

		exp.get("/channels/search", function(req, res){
			var results = [], term, terms, max_results = 10;
			(term = req.query) && (term = term.term) && (term = term.toLowerCase());
			// Limit length, split on spaces, remove blank terms, enforce maximum term count
			terms = term.substr(0,50).split(' ').filter(function(t){return t.length > 0 ;}).slice(0,5);

			if(terms.length > 1){
				me.channels.each(function(channel){
					var name = channel.name;
					if(terms.every(function(term){return name.indexOf(term) >= 0;})) results.push(channel);
					if(results.length >= max_results) return true;
				});
			} else if(term = terms[0]){
				me.channels.each(function(channel){
					if(channel.name.indexOf(term) >= 0) results.push(channel);
					if(results.length >= max_results) return true;
				});
			}
			results = results.map(function(channel){ return channel.name});

			res.writeHead(200, {"Content-Type":"application/json"});
			res.end(JSON.stringify(results));
		});

		exp.post("/", function(req, res){
			wompt.Auth.get_or_set_token(req, res);
			res.redirect('/chat/' + req.body.channel);
		});

		exp.get("/unlisted/new", function(req, res){
			var name = wompt.Auth.random_string(10);
			res.redirect('/unlisted/' + name);
		});

		if(wompt.env.force_sign_in){
			exp.get("/users/force_sign_in/:num", function(req, res){
				var num = parseInt(req.params.num);
				wompt.User.findOne({}, function(err, user){
					if(user){
						wompt.Auth.start_session(res, user);
					}
					res.redirect(req.headers.referer || '/');
				});
			});
		}

		exp.get("/user/sign_out", function(req, res){
			wompt.Auth.sign_out_user(req, res, function(){
				res.redirect(req.headers.referer || '/');
			});
		});

		exp.get("/admin/stats", wompt.Auth.adminMiddleware, function(req, res){
			res.render('admin/stats', {
				locals: {w: me},
				layout: 'admin/layout'
			});
		});

		function landingPage(req, res){
			wompt.Auth.get_or_set_token(req, res);
			res.render('index', {
				locals: me.standard_page_vars(req, {
					app:me,
					jquery: true,
					page_js: 'landing',
					page_name:'landing',
					subtitle: me.choose_subtitle()
				})
			});
		}

		function profilePage(req, res, user){
			if(!user) return res.send("", 404);

			res.render('profile', {
				locals: me.standard_page_vars(req, {
					app:me,
					profileUser: user,
					jquery: true,
					page_js: 'profile'
				})
			});
		}

		return exp;
	},

	standard_page_vars: function(req, custom_vars){
		var vars = {
			jquery: false,
			url: req.url,
			user: req.meta_user,
			footer: true,
			host: req.headers.host,
			config: this.config
		};

		if(custom_vars)
			wompt.util.merge(vars, custom_vars);

		return {
			w:vars
		};
	},

	choose_subtitle: function(){
		var subtitles = [
			 "Try it free, no signup required"
			,"Always free, sign in using Facebook"
			,"Speak <tt>c0d3?</tt> sign in using Github"
		];
		return subtitles[Math.floor(Math.random() * subtitles.length)];
	},

	start_server: function(){
		this.express.listen(this.config.port, this.config.host);
		this.start_socket_io();

		logger.log("Starting server on port: " + this.config.port);
	},

	start_socket_io: function(){
		if(this.socket) return;
		var app = this;

		this.socket = SocketIO.listen(this.express, this.config.socketIO.serverOptions);
		this.socket.on('connection', function(client){
			app.new_connection(client);
		});
	},

	new_connection: function(client){
		logger.log("Connection from: " + client.sessionId);
		var app = this;

		this.clients.add(client);

		client.once('message', function(data){
			if(data && data.action == 'join'){
				var connector = app.client_connectors.get(data.connector_id);

				if(!connector) {
					client.send("You must pass your connector_id with the join message");
					return client._onDisconnect();
				}

				var namespace = connector.namespace || app.namespaces[data.namespace],
				    user      = connector.meta_user || new wompt.MetaUser();

				client.user = user;

				if(!client.uid){
					//When the user signs out, we can still know the user id while cleaning up the client
					client.uid = user.id();
					// Because this client didn't have a uid when it was first added
					app.clients.inc_user_count(client);
				}

				logger.log('Handing off client:' + client.sessionId + ' to Channel: ' + data.channel)
				var channel = namespace.get(data.channel);
				if(channel){
					channel.add_client(client, connector && connector.token, data);
				}
				user.clients.add(client);
			}
		});
	},

	statusMiddleware: function(){
		return function(req, res, next){
			if(req.url == '/ok'){
				res.writeHead(200, {'Content-Type': 'text/plain'});
				res.end("OK");
			}else{
				next();
			}
		};
	},

	chatNamespace: function(namespace, options){
		this.namespaces = this.namespaces || {};
		otions = options || {};

		var me = this,
		exp = this.express,
		channelManager = new wompt.ChannelManager();

		this.namespaces[namespace] = channelManager;

		if(options.logged){
			new wompt.loggers.LoggerCreator(channelManager, namespace);
		}

		var argumentsForGet = [new RegExp("\\/" + namespace + "\\/(.+)")];
		if(options.fakeUsers) argumentsForGet.push(wompt.Auth.fake_user_middleware());

		function handleChatRoomGet(req, res){
			if(req.url.substr(-1,1) == '/'){
				return res.redirect(wompt.util.chop(req.url));
			}

			var meta_user = req.meta_user,
					channel = req.params[0];

			var token = wompt.Auth.get_or_set_token(req, res);

			var connector = me.client_connectors.add({
				meta_user:meta_user,
				namespace:channelManager,
				token: token
			});

			var locals = me.standard_page_vars(req, {
				channel: channel,
				namespace: namespace,
				connector_id: connector.id,
				url: req.url,
				jquery: true,
				page_name: 'chat',
				page_js: 'channel'
			});

			var opt = {
				locals:locals
			};

			if(options.allowIframe && req.query.iframe == '1'){
				opt.layout = 'layouts/iframe';
				locals.w.ga_source = 'embedd';
				if(options.allowCSS) locals.w.css_file = req.query.css_file;
			}

			res.render('chat', opt);
		}

		argumentsForGet.push(handleChatRoomGet);
		exp.get.apply(exp, argumentsForGet);

		return channelManager;
	},

	plain_routes: function(exp, urls){
		var me = this;
		urls.forEach(function(url){
			var view = url.substr(1);
			exp.get(url, function(req, res){
				res.render(view, {
						locals: me.standard_page_vars(req)
					});
			});
		});
	},
}

module.exports = App;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var Path = require('path');
var Async = require('async');
var Inherits = require('util').inherits;
var URI = require('urijs');
var ConvertToSwagger2 = require('swagger-converter');

var BaseType = require('./base-type.js');
var Types = require('./types.js');
var Util = require('./util.js');

var Swagger1 = module.exports = function() {
  Swagger1.super_.apply(this, arguments);
  this.type = 'swagger_1';

  this.converters.swagger_2 = function(swagger1, callback) {
    try {
      var swagger2 = ConvertToSwagger2(swagger1.spec, swagger1.subResources);
    } catch(e) {
      return callback(e);
    }
    if (swagger2.info.title === 'Title was not specified') {
      swagger2.info.title = swagger2.host;
    }
    callback(null, new Types.swagger_2(swagger2));
  }
}

Inherits(Swagger1, BaseType);

Swagger1.prototype.formatName = 'swagger';
Swagger1.prototype.supportedVersions = ['1.0', '1.1', '1.2'];
Swagger1.prototype.getFormatVersion = function () {
  return this.spec.swaggerVersion;
}

Swagger1.prototype.parsers = [Util.parseJSON];

Swagger1.prototype.checkFormat = function (spec) {
  return !_.isUndefined(spec.swaggerVersion);
}

Swagger1.prototype.fixSpec = function () {
  if (this.sourceType == 'url') {
    var url = URI(this.source);

    if (!this.spec.basePath)
      this.spec.basePath = url.filename('').href();
    else {
      var basePath = URI(this.spec.basePath);
      basePath.scheme(basePath.scheme() || url.scheme());
      basePath.host(basePath.host() || url.host());
      this.spec.basePath = basePath.href();
    }
  }
};

Swagger1.prototype.listSubResources = function () {
  var resources = [];
  _.each(this.spec.apis, function (api) {
     if (_.isUndefined(api.path))
       return;

     // skip embedded documents;
     if (!_.isEmpty(api.operations))
       return;

     resources.push(api.path);
  });
  return resources;
};

Swagger1.prototype.resolveSubResource = function (resource) {
  resource = resource.replace('{format}', 'json');
  if (this.sourceType === 'file') {
    var dir = Path.dirname(this.source);
    var filename = Path.join(dir, resource);
    if (filename.indexOf('.json') === -1)
      filename += '.json';
    return filename;
  } else if (this.sourceType === 'url') {
    var baseUrl = URI(this.source).query('');

    if (this.spec.basePath)
      baseUrl = URI(this.spec.basePath).absoluteTo(baseUrl);

    if (this.getFormatVersion() === '1.0' && baseUrl.suffix() === 'json')
      baseUrl.filename('');

    var resourceUrl = resource;
    if (URI(resource).is('relative'))
      resourceUrl = baseUrl + resource;
    return URI(resourceUrl).normalize().href();
  }
  //TODO: report error
}
<commit_msg>fix swagger-converter import<commit_after>'use strict';

var _ = require('lodash');
var Path = require('path');
var Async = require('async');
var Inherits = require('util').inherits;
var URI = require('urijs');
var ConvertToSwagger2 = require('swagger-converter').convert;

var BaseType = require('./base-type.js');
var Types = require('./types.js');
var Util = require('./util.js');

var Swagger1 = module.exports = function() {
  Swagger1.super_.apply(this, arguments);
  this.type = 'swagger_1';

  this.converters.swagger_2 = function(swagger1, callback) {
    try {
      var swagger2 = ConvertToSwagger2(swagger1.spec, swagger1.subResources);
    } catch(e) {
      return callback(e);
    }
    if (swagger2.info.title === 'Title was not specified') {
      swagger2.info.title = swagger2.host;
    }
    callback(null, new Types.swagger_2(swagger2));
  }
}

Inherits(Swagger1, BaseType);

Swagger1.prototype.formatName = 'swagger';
Swagger1.prototype.supportedVersions = ['1.0', '1.1', '1.2'];
Swagger1.prototype.getFormatVersion = function () {
  return this.spec.swaggerVersion;
}

Swagger1.prototype.parsers = [Util.parseJSON];

Swagger1.prototype.checkFormat = function (spec) {
  return !_.isUndefined(spec.swaggerVersion);
}

Swagger1.prototype.fixSpec = function () {
  if (this.sourceType == 'url') {
    var url = URI(this.source);

    if (!this.spec.basePath)
      this.spec.basePath = url.filename('').href();
    else {
      var basePath = URI(this.spec.basePath);
      basePath.scheme(basePath.scheme() || url.scheme());
      basePath.host(basePath.host() || url.host());
      this.spec.basePath = basePath.href();
    }
  }
};

Swagger1.prototype.listSubResources = function () {
  var resources = [];
  _.each(this.spec.apis, function (api) {
     if (_.isUndefined(api.path))
       return;

     // skip embedded documents;
     if (!_.isEmpty(api.operations))
       return;

     resources.push(api.path);
  });
  return resources;
};

Swagger1.prototype.resolveSubResource = function (resource) {
  resource = resource.replace('{format}', 'json');
  if (this.sourceType === 'file') {
    var dir = Path.dirname(this.source);
    var filename = Path.join(dir, resource);
    if (filename.indexOf('.json') === -1)
      filename += '.json';
    return filename;
  } else if (this.sourceType === 'url') {
    var baseUrl = URI(this.source).query('');

    if (this.spec.basePath)
      baseUrl = URI(this.spec.basePath).absoluteTo(baseUrl);

    if (this.getFormatVersion() === '1.0' && baseUrl.suffix() === 'json')
      baseUrl.filename('');

    var resourceUrl = resource;
    if (URI(resource).is('relative'))
      resourceUrl = baseUrl + resource;
    return URI(resourceUrl).normalize().href();
  }
  //TODO: report error
}
<|endoftext|>


<commit_before>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/

/*
 * Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

var fs = require("fs");
var glob = require("glob");
var path = require('path');
var Properties = require("../util/properties");
var log4js = require("log4js");
var LibManager = require('../util/libmanager');
var FileUtil = require("../util/fileutil");

function ArrowSetup(config, argv) {
    this.config = config;
    this.argv = argv;
}

ArrowSetup.prototype.setup = function () {
    this.setuplog4js();
    this.setupReportDir();
    this.setupDefaultDriverName();
    this.setupTestEngine();
    this.setupDefaultLib();
    this.setupHeadlessParam();
    this.setupCapabilities();
    this.setupMisc();
    this.errorCheck();
    this.setupReplaceParam();
    this.setupDefaultParam();

};

ArrowSetup.prototype.setupReplaceParam = function () {
    if (this.argv.replaceParamJSON) {
        this.config.replaceParamJSON = this.argv.replaceParamJSON;
    }
};

ArrowSetup.prototype.setupDefaultParam = function () {
    if (this.argv.defaultParamJSON) {
        this.config.defaultParamJSON = this.argv.defaultParamJSON;
    }
};

ArrowSetup.prototype.errorCheck = function () {
};

ArrowSetup.prototype.setupReportDir = function () {

    global.reportFolder = "";
    // To generate the reports, if either report is true or reportFolder is passed
    if (this.argv.reportFolder || true === this.argv.report) {

        var fileUtil = new FileUtil(),
            targetFolderPath,
            reportFolderPath;

        // If reportFolder is passed in the argument,
        if (this.argv.reportFolder) {
            targetFolderPath =  path.resolve(global.workingDirectory, this.argv.reportFolder);
        } else {
            // Report folder not passed by the user
            // By default, reports shall go under arrow-target/reports
            targetFolderPath =  path.resolve(global.workingDirectory, 'arrow-target');
        }

        // Cleanup report folder if keepTestReport set to false or undefined
        if (this.argv.keepTestReport === undefined || false === this.argv.keepTestReport) {
            fileUtil.removeDirectory(targetFolderPath);
        }

        reportFolderPath = path.resolve(targetFolderPath, 'arrow-report');

        fileUtil.createDirectory(reportFolderPath);

        this.argv.reportFolder = targetFolderPath;
        global.reportFolder = this.argv.reportFolder ||"";

    }

};

ArrowSetup.prototype.setupMisc = function () {

    if (this.argv.coverage !== undefined) {
        this.config.coverage = this.argv.coverage;
    }

    if (this.argv.report === undefined && this.config.report) {
        this.argv.report = this.config.report;
    }

    if (this.argv.dimensions) {
        this.argv.dimensions = path.resolve(global.workingDirectory, this.argv.dimensions);
        this.config.dimensions = this.argv.dimensions;
    }
};

ArrowSetup.prototype.setuplog4js = function () {
    var logLevel;
    logLevel = this.config["logLevel"];
    log4js.setGlobalLogLevel(logLevel);
    log4js.restoreConsole();
    this.logger = log4js.getLogger("ArrowSetup");
};

ArrowSetup.prototype.setupDefaultDriverName = function () {
    if (!this.argv.driver) {
        // turn on reuseSession, if browser is being reused
        // adding support for "reuse-" too, just in case if a null version is passed with browser.
        if ("reuse" === this.argv.browser || "reuse-" === this.argv.browser) {
            this.argv.reuseSession = true;
            delete this.argv.browser;
        }

        if (this.argv.reuseSession) {
            this.argv.driver = "selenium";
        }
    }

    // setup the selenium host using the auto hookup if possible
    this.setupSeleniumHost();
};

ArrowSetup.prototype.setupSeleniumHost = function () {
    var wdHubHost,
        wdStatusFile = "/tmp/arrow_sel_server.status";

    // setup the selenium host using the auto hookup if possible
    wdHubHost = this.config["seleniumHost"];
    if (0 === wdHubHost.length) {
        // check if we have a hooked up server
        try {
            fs.statSync(wdStatusFile).isFile();
            wdHubHost = fs.readFileSync(wdStatusFile, "utf-8");
        } catch (ex) {
        }

        // final default
        if (wdHubHost.length === 0) {
            wdHubHost = "http://localhost:4444/wd/hub";
        }
        this.config["seleniumHost"] = wdHubHost;
    }

};

ArrowSetup.prototype.setupDefaultLib = function () {
    this.argv.lib = new LibManager().getAllCommonLib(this.config, this.argv.lib);
    this.logger.debug("Commandline + Common Libs for Test :" + this.argv.lib);
};

ArrowSetup.prototype.setupHeadlessParam = function () {
    var hd,
        results,
        ext;

    if (!this.argv.argv.remain[0]) {
        return;
    }

    hd = this.argv.argv.remain[0];

    // checking is user passed comma seprated string ?
    if (hd.indexOf(",") !== -1) {
        results = hd.split(",");
        this.logger.info("Found Descriptors: " + results);
    } else {
        results = glob.sync(hd);
        this.logger.info("Glob result: " + results);
    }

    if (0 === results.length) {
        this.logger.error("Nothing to test with: " + hd);
        process.exit();
    }

    ext = path.extname(results[0]);
    // check the first file to determine the type
    if (".json" === ext) {
        this.config.arrDescriptor = results;
    } else if ((".js" === ext) || (".html" === ext)) {
        if (results.length > 1) {
            this.argv.tests = results;
        } else {
            this.argv.test = results[0];
        }
    } else {
        this.logger.fatal("Unknown test file type " + results[0]);
        process.exit(0);
    }
};

ArrowSetup.prototype.setupCapabilities = function () {
    if (this.argv.capabilities) {
        this.config.capabilities = this.argv.capabilities;
    }
};

ArrowSetup.prototype.setupTestEngine = function () {
    this.config.engine = "yui";
    if (this.argv.engine) {
        this.config.engine = this.argv.engine;
    }
    if (this.argv.engineConfig) {
        try {
            this.config.engineConfig = this.argv.engineConfig;
            if (fs.statSync(this.argv.engineConfig).isFile()) {
                //get absolute path before chdir
                this.config.engineConfig = path.resolve("", this.argv.engineConfig);
            }
        } catch (e) {
        }
    }
};

module.exports = ArrowSetup;

<commit_msg>removing comma handling for headless param<commit_after>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/

/*
 * Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

var fs = require("fs");
var glob = require("glob");
var path = require('path');
var Properties = require("../util/properties");
var log4js = require("log4js");
var LibManager = require('../util/libmanager');
var FileUtil = require("../util/fileutil");

function ArrowSetup(config, argv) {
    this.config = config;
    this.argv = argv;
}

ArrowSetup.prototype.setup = function () {
    this.setuplog4js();
    this.setupReportDir();
    this.setupDefaultDriverName();
    this.setupTestEngine();
    this.setupDefaultLib();
    this.setupHeadlessParam();
    this.setupCapabilities();
    this.setupMisc();
    this.errorCheck();
    this.setupReplaceParam();
    this.setupDefaultParam();

};

ArrowSetup.prototype.setupReplaceParam = function () {
    if (this.argv.replaceParamJSON) {
        this.config.replaceParamJSON = this.argv.replaceParamJSON;
    }
};

ArrowSetup.prototype.setupDefaultParam = function () {
    if (this.argv.defaultParamJSON) {
        this.config.defaultParamJSON = this.argv.defaultParamJSON;
    }
};

ArrowSetup.prototype.errorCheck = function () {
};

ArrowSetup.prototype.setupReportDir = function () {

    global.reportFolder = "";
    // To generate the reports, if either report is true or reportFolder is passed
    if (this.argv.reportFolder || true === this.argv.report) {

        var fileUtil = new FileUtil(),
            targetFolderPath,
            reportFolderPath;

        // If reportFolder is passed in the argument,
        if (this.argv.reportFolder) {
            targetFolderPath =  path.resolve(global.workingDirectory, this.argv.reportFolder);
        } else {
            // Report folder not passed by the user
            // By default, reports shall go under arrow-target/reports
            targetFolderPath =  path.resolve(global.workingDirectory, 'arrow-target');
        }

        // Cleanup report folder if keepTestReport set to false or undefined
        if (this.argv.keepTestReport === undefined || false === this.argv.keepTestReport) {
            fileUtil.removeDirectory(targetFolderPath);
        }

        reportFolderPath = path.resolve(targetFolderPath, 'arrow-report');

        fileUtil.createDirectory(reportFolderPath);

        this.argv.reportFolder = targetFolderPath;
        global.reportFolder = this.argv.reportFolder || "";

    }

};

ArrowSetup.prototype.setupMisc = function () {

    if (this.argv.coverage !== undefined) {
        this.config.coverage = this.argv.coverage;
    }

    if (this.argv.report === undefined && this.config.report) {
        this.argv.report = this.config.report;
    }

    if (this.argv.dimensions) {
        this.argv.dimensions = path.resolve(global.workingDirectory, this.argv.dimensions);
        this.config.dimensions = this.argv.dimensions;
    }
};

ArrowSetup.prototype.setuplog4js = function () {
    var logLevel;
    logLevel = this.config["logLevel"];
    log4js.setGlobalLogLevel(logLevel);
    log4js.restoreConsole();
    this.logger = log4js.getLogger("ArrowSetup");
};

ArrowSetup.prototype.setupDefaultDriverName = function () {
    if (!this.argv.driver) {
        // turn on reuseSession, if browser is being reused
        // adding support for "reuse-" too, just in case if a null version is passed with browser.
        if ("reuse" === this.argv.browser || "reuse-" === this.argv.browser) {
            this.argv.reuseSession = true;
            delete this.argv.browser;
        }

        if (this.argv.reuseSession) {
            this.argv.driver = "selenium";
        }
    }

    // setup the selenium host using the auto hookup if possible
    this.setupSeleniumHost();
};

ArrowSetup.prototype.setupSeleniumHost = function () {
    var wdHubHost,
        wdStatusFile = "/tmp/arrow_sel_server.status";

    // setup the selenium host using the auto hookup if possible
    wdHubHost = this.config["seleniumHost"];
    if (0 === wdHubHost.length) {
        // check if we have a hooked up server
        try {
            fs.statSync(wdStatusFile).isFile();
            wdHubHost = fs.readFileSync(wdStatusFile, "utf-8");
        } catch (ex) {
        }

        // final default
        if (wdHubHost.length === 0) {
            wdHubHost = "http://localhost:4444/wd/hub";
        }
        this.config["seleniumHost"] = wdHubHost;
    }

};

ArrowSetup.prototype.setupDefaultLib = function () {
    this.argv.lib = new LibManager().getAllCommonLib(this.config, this.argv.lib);
    this.logger.debug("Commandline + Common Libs for Test :" + this.argv.lib);
};

ArrowSetup.prototype.setupHeadlessParam = function () {
    var hd,
        results,
        ext;

    if (!this.argv.argv.remain[0]) {
        return;
    }

    hd = this.argv.argv.remain[0];

    results = glob.sync(hd);
    this.logger.info("Glob result: " + results);

    if (0 === results.length) {
        this.logger.error("ERROR : No Test or Descriptor Found, while looking for : " + hd);
        process.exit(1);
    }

    ext = path.extname(results[0]);
    // check the first file to determine the type
    if (".json" === ext) {
        this.config.arrDescriptor = results;
    } else if ((".js" === ext) || (".html" === ext)) {
        if (results.length > 1) {
            this.argv.tests = results;
        } else {
            this.argv.test = results[0];
        }
    } else {
        this.logger.fatal("Unknown test file type " + results[0]);
        process.exit(0);
    }
};

ArrowSetup.prototype.setupCapabilities = function () {
    if (this.argv.capabilities) {
        this.config.capabilities = this.argv.capabilities;
    }
};

ArrowSetup.prototype.setupTestEngine = function () {
    this.config.engine = "yui";
    if (this.argv.engine) {
        this.config.engine = this.argv.engine;
    }
    if (this.argv.engineConfig) {
        try {
            this.config.engineConfig = this.argv.engineConfig;
            if (fs.statSync(this.argv.engineConfig).isFile()) {
                //get absolute path before chdir
                this.config.engineConfig = path.resolve("", this.argv.engineConfig);
            }
        } catch (e) {
        }
    }
};

module.exports = ArrowSetup;

<|endoftext|>


<commit_before>/**
 * Imports
 */

import {MenuItem, Button, Grid} from 'vdux-containers'
import {Block, Menu, Icon} from 'vdux-ui'
import Loading from 'components/loading'
import PicturePasswordModal from 'components/PicturePasswordModal'
import {component, element} from 'vdux'
import mapValues from '@f/map-values'
import enroute from 'enroute'
import fire from 'vdux-fire'

const router = enroute({
	classList: (params, props) => <ClassList {...props}/>,
	studentList: (params, props) => <StudentList {...props}/>
})

/**
 * <Student Sign In/>
 */

const colors = ['green', '#8242b3', '#f9cb3e', '#fd686f', 'blue', '#555']

export default component({
  render ({props, context}) {
  	const {classId} = props
  	const route = classId ? 'studentList' : 'classList'

    return (
      <Block>
        <Block fontFamily='"Press Start 2P"' fs='m' textAlign='center' pb='l'>
          PixelBots
        </Block>
      	<Block column minWidth={300} maxHeight={600} overflow='hidden' bgColor='white' boxShadow='0 1px 2px rgba(0,0,0,.3)' color='primary'>
      		{router(route, props)}
      	</Block>
      </Block>
    )
  }
})

const StudentList = fire(({classId}) => ({
	classInfo: {
		ref: `/classes/${classId}`,
		join: {
			ref: '/users',
			child: 'students',
			childRef: (val, ref) => mapValues((val, key) => ref.child(key), val.students)
		}
	},
}))(component({
  render ({props, context, actions, state}) {
  	const {classInfo, schoolId} = props
    const {loading} = state
  	if (classInfo.loading) return
    const disabledProps = loading
      ? {opacity: .1, pointerEvents: 'none', userSelect: 'none'}
      : {}
    const students = mapValues((val, key) => val, classInfo.value.students)
    const sorted = students.slice().sort((a, b) => {
      const nameA = a.displayName.toLowerCase()
      const nameB = b.displayName.toLowerCase()
      if (nameA < nameB) return -1
      if (nameA > nameB) return 1
      return 0
    })

    const cols = getCols(sorted.length)

    return (
      <Block relative h='100%'>
        <Block {...disabledProps}>
          <Block px='l' pt='l'>
            <Block fs='m' pb='s' bold align='start center'>
              <Block>Select Your Name</Block>
            </Block>
            <Block fs='xs' pb textTransform='capitalize' align='start center'>
              <Icon mr='s' pointer name='arrow_back' onClick={context.setUrl(`/schools/${schoolId}`)}/>
              {classInfo.value.displayName}
            </Block>
          </Block>
          <Grid itemsPerRow={cols} pb px align='center start'>
            {
              mapValues((val, key) => (
                <Button px='s' py m='s' textAlign='center' bold w={175} bgColor={colors[key % cols]} color='white' onClick={actions.openPasswordModal(val)}>
                  <Block ellipsis fs='s'>{val.displayName}</Block>
                </Button>
              ), sorted)
            }
          </Grid>
        </Block>
        <Loading hide={!loading} absolute bottom='130px' />
      </Block>
    )
  },
  controller: {

    * openPasswordModal({context, actions}, val) {
      yield context.openModal(() => <PicturePasswordModal redirect={actions} value={val} />)
    },

    * getSignInToken ({context, actions}, uid) {
      yield actions.setLoading(true)
      const {value} = yield context.fetch(`${process.env.CLOUD_FUNCTIONS}/weoAuth`, {
        method: 'POST',
        headers: {'CONTENT-TYPE': 'application/json'},
        body: JSON.stringify({uid})
      })
      if (value.status === 'success') {
        yield context.signInWithToken(value.payload)
      } else {
        yield actions.setLoading(false)
        yield context.toast(value.payload)
      }
    }
  },

  reducer: {
    setLoading: (state, loading) => ({loading})
  }

}))

const ClassList = fire(({schoolId}) => ({
	school: {
		ref: `/schools/${schoolId}/classes#orderByValue`,
		join: {
			ref: '/classes',
			child: 'classInfo',
			childRef: (val, ref) => val.map(({key}) => ref.child(key))
		}
	},
}))(component({
  render ({props, context}) {
  	const {school} = props
  	if (school.loading) return

    const schools = school.value

  const cols = getCols(schools.length)

    return (
    	<Block>
    		<Block fs='m' p='l' pb textAlign='center' bold>Find Your Class</Block>
        <Grid itemsPerRow={cols} pb px align='center start'>
          {
            mapValues(({classInfo, key}, i) => (
              <Button
                onClick={context.setUrl(`/schools/${props.schoolId}/${key}`)}
                border={`2px solid ${colors[i % cols]}`}
                hoverProps={{highlight: 0.02}}
                color={colors[i % cols]}
                textAlign='center'
                maxWidth={175}
                minWidth={175}
                bgColor='white'
                px='s'
                m='s'
                py
                bold
                // bgColor={colors[i % cols]}
                // color='white'
                >
                <Block ellipsis fs='s'>{classInfo.displayName}</Block>
              </Button>
            ), school.value)
          }
        </Grid>
    	</Block>
    )
  }
}))

function getCols(items) {
  return Math.ceil(items / Math.ceil(Math.sqrt(items)))
}
<commit_msg>able to disable passwords<commit_after>/**
 * Imports
 */

import {MenuItem, Button, Grid} from 'vdux-containers'
import {Block, Menu, Icon} from 'vdux-ui'
import Loading from 'components/loading'
import PicturePasswordModal from 'components/PicturePasswordModal'
import {component, element} from 'vdux'
import mapValues from '@f/map-values'
import enroute from 'enroute'
import fire from 'vdux-fire'

const router = enroute({
	classList: (params, props) => <ClassList {...props}/>,
	studentList: (params, props) => <StudentList {...props}/>
})

/**
 * <Student Sign In/>
 */

const colors = ['green', '#8242b3', '#f9cb3e', '#fd686f', 'blue', '#555']

export default component({
  render ({props, context}) {
  	const {classId} = props
  	const route = classId ? 'studentList' : 'classList'

    return (
      <Block>
        <Block fontFamily='"Press Start 2P"' fs='m' textAlign='center' pb='l'>
          PixelBots
        </Block>
      	<Block column minWidth={300} maxHeight={600} overflow='hidden' bgColor='white' boxShadow='0 1px 2px rgba(0,0,0,.3)' color='primary'>
      		{router(route, props)}
      	</Block>
      </Block>
    )
  }
})

const StudentList = fire(({classId}) => ({
	classInfo: {
		ref: `/classes/${classId}`,
		join: {
			ref: '/users',
			child: 'students',
			childRef: (val, ref) => mapValues((val, key) => ref.child(key), val.students)
		}
	},
  passwordSetting: `/classes/${classId}/hasPicturePassword`
}))(component({
  render ({props, context, actions, state}) {
  	const {classInfo, schoolId, passwordSetting} = props
    const {loading} = state
  	if (classInfo.loading) return
    const disabledProps = loading
      ? {opacity: .1, pointerEvents: 'none', userSelect: 'none'}
      : {}
    const students = mapValues((val, key) => val, classInfo.value.students)
    const sorted = students.slice().sort((a, b) => {
      const nameA = a.displayName.toLowerCase()
      const nameB = b.displayName.toLowerCase()
      if (nameA < nameB) return -1
      if (nameA > nameB) return 1
      return 0
    })

    const cols = getCols(sorted.length)

    return (
      <Block relative h='100%'>
        <Block {...disabledProps}>
          <Block px='l' pt='l'>
            <Block fs='m' pb='s' bold align='start center'>
              <Block>Select Your Name</Block>
            </Block>
            <Block fs='xs' pb textTransform='capitalize' align='start center'>
              <Icon mr='s' pointer name='arrow_back' onClick={context.setUrl(`/schools/${schoolId}`)}/>
              {classInfo.value.displayName}
            </Block>
          </Block>
          <Grid itemsPerRow={cols} pb px align='center start'>
            {
              mapValues((val, key) => (
                <Button px='s' py m='s' textAlign='center' bold w={175} bgColor={colors[key % cols]} color='white' onClick={passwordSetting.value ? actions.openPasswordModal(val) : actions.getSignInToken(val.key)}>
                  <Block ellipsis fs='s'>{val.displayName}</Block>
                </Button>
              ), sorted)
            }
          </Grid>
        </Block>
        <Loading hide={!loading} absolute bottom='130px' />
      </Block>
    )
  },
  controller: {

    * openPasswordModal({context, actions}, val) {
      yield context.openModal(() => <PicturePasswordModal redirect={actions} value={val} />)
    },

    * getSignInToken ({context, actions}, uid) {
      yield actions.setLoading(true)
      const {value} = yield context.fetch(`${process.env.CLOUD_FUNCTIONS}/weoAuth`, {
        method: 'POST',
        headers: {'CONTENT-TYPE': 'application/json'},
        body: JSON.stringify({uid})
      })
      if (value.status === 'success') {
        yield context.signInWithToken(value.payload)
      } else {
        yield actions.setLoading(false)
        yield context.toast(value.payload)
      }
    }
  },

  reducer: {
    setLoading: (state, loading) => ({loading})
  }

}))

const ClassList = fire(({schoolId}) => ({
	school: {
		ref: `/schools/${schoolId}/classes#orderByValue`,
		join: {
			ref: '/classes',
			child: 'classInfo',
			childRef: (val, ref) => val.map(({key}) => ref.child(key))
		}
	},
}))(component({
  render ({props, context}) {
  	const {school} = props
  	if (school.loading) return

    const schools = school.value

  const cols = getCols(schools.length)

    return (
    	<Block>
    		<Block fs='m' p='l' pb textAlign='center' bold>Find Your Class</Block>
        <Grid itemsPerRow={cols} pb px align='center start'>
          {
            mapValues(({classInfo, key}, i) => (
              <Button
                onClick={context.setUrl(`/schools/${props.schoolId}/${key}`)}
                border={`2px solid ${colors[i % cols]}`}
                hoverProps={{highlight: 0.02}}
                color={colors[i % cols]}
                textAlign='center'
                maxWidth={175}
                minWidth={175}
                bgColor='white'
                px='s'
                m='s'
                py
                bold
                // bgColor={colors[i % cols]}
                // color='white'
                >
                <Block ellipsis fs='s'>{classInfo.displayName}</Block>
              </Button>
            ), school.value)
          }
        </Grid>
    	</Block>
    )
  }
}))

function getCols(items) {
  return Math.ceil(items / Math.ceil(Math.sqrt(items)))
}
<|endoftext|>