<commit_before>var XML_EXPENSE_ADD_URL = null;
var ADD_EXPENSES_TABLE = null;
var XML_TREES_ELTS = new Array();
var LAST_EXPENSE_ID = 0;
var unitializedCategories = new Array();

function readExpenseDate(expense) {
	var date_input = expense.find("input.date-input");
	if (date_input.length != 1) {
		return undefined;
	}
	if (date_input[0].type == "date") {
		if (date_input.val().match('^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$')) {
			return date_input.val();
		}
	} else { //text
		var m = date_input.val().match('^([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})$');
		if (m) {
			return m[3] + '-' + m[2] + '-' + m[1];
		}
	}
	return undefined;
}

function readExpenseTitle(expense) {
	var title_input = expense.find("input.title-input");
	if (title_input.length != 1 || title_input.val().length == 0) {
		return undefined;
	}
	return title_input.val();
}

function readExpensePrice(expense) {
	var price_input = expense.find("input.price-input");
	if (price_input.length != 1
				|| ! price_input.val().match('^[-+]?(([0-9]{1,3}([, ][0-9]{3})*|[0-9]+)(.[0-9]?[0-9]?)?)$')
				|| parseFloat(price_input.val().replace(/ /g, '').replace(/,/g, '')) == 0) {
		return undefined;
	}
	return parseFloat(price_input.val().replace(/ /g, '').replace(/,/g, ''));
}

function readExpenseCategories(expense) {
	var categories_elt = expense.find(".categories-data ulautocomplete-selection li");
	var categories = new Array();
	for (var i = 0 ; i != categories_elt.length ; i++) {
		categories.push($(categories_elt[i]).attr("data-id"));
	}
	return categories;
}

function isFilledExpense(expense) {
	// Given an expense, return true is it is fully and correctly filled

	var expense_date = readExpenseDate(expense);
	if (! expense_date) {
		return undefined;
	}

	var expense_title = readExpenseTitle(expense);
	if (! expense_title) {
		return undefined;
	}

	var expense_price = readExpensePrice(expense);
	if (! expense_price) {
		return undefined;
	}

	var expense_categories = readExpenseCategories(expense);

	return {
		date: expense_date,
		title: expense_title,
		price: expense_price,
		categories: expense_categories
	};
}

function reactOnExpenseChange() {
	// Automaticcaly append expenses when the last line is fully filled
	// Only the last line is checked

	var expense = $(this).parent().parent();
	// expense is the last expense of the table is not equivalent to
	// data-expense-identifier == LAST_EXPENSE_ID as the expense could have been deleted..
	if (expense[0] == ADD_EXPENSES_TABLE.children().last()[0] && isFilledExpense(expense)) {
		appendExpense();
	}
}

function reactOnPriceFocusOut() {
	// Automatically format the price on focus out action

	var price_value = readExpensePrice($(this).parent().parent());
	if (! price_value) {
		return false;
	}

	var price_value_str = $(this).val();
	var price_value_new_str = price_value.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});

	var language = window.navigator.userLanguage || window.navigator.language;
	if (language.toLowerCase() == 'fr' || language.toLowerCase() == 'fr-fr') {
		price_value_new_str = price_value_new_str.replace(/,/g, ' ');
	}
	if (price_value_str != price_value_new_str) {
		$(this).val(price_value_new_str);
	}
}

function reactOnPriceFocus() {
	// Automatically format the price on focus in order to ease the modification

	var price_value = readExpensePrice($(this).parent().parent());
	if (! price_value) {
		return false;
	}

	var price_value_str = $(this).val();
	var price_value_new_str = price_value.toFixed(2);
	if (price_value_str != price_value_new_str) {
		$(this).val(price_value_new_str);
	}
}

function appendExpense() {
	// Append an empty expense in the form

	LAST_EXPENSE_ID++;
	var expense = $("<tr/>");
	expense.addClass('expense-edit');
	expense.attr("data-expense-identifier", LAST_EXPENSE_ID);

	var date_td = $("<td/>");
	var date_input_desc = $("<span/>");
	date_input_desc.addClass("glyphicon glyphicon-calendar");
	var date_input = $("<input/>");
	date_input.addClass("date-input");
	date_input.attr("required", "");
	date_input.attr("type", "date");
	if (date_input[0].type == "text") //date type not supported (HTML5)
	{
		date_input.attr("placeholder", "dd/mm/yyyy");
		date_input.datepicker({dateFormat: "dd/mm/yy"});
	}
	date_input.change(reactOnExpenseChange);
	date_input.keyup(reactOnExpenseChange);
	date_td.append(date_input_desc);
	date_td.append(date_input);
	expense.append(date_td);

	var title_td = $("<td/>");
	var title_input = $("<input/>");
	title_input.addClass("title-input");
	title_input.attr("required", "");
	title_input.attr("size", "50");
	title_input.attr("type", "text");
	title_input.attr("placeholder", "Short description of the expense");
	title_input.keyup(reactOnExpenseChange);
	title_td.append(title_input);
	expense.append(title_td);

	var price_td = $("<td/>");
	var price_input = $("<input/>");
	price_input.addClass("price-input");
	price_input.attr("required", "");
	price_input.attr("size", "10");
	price_input.attr("type", "text");
	price_input.attr("pattern", "[-+]?(\\d{1,3}([\\s,]\\d{3})*|\\d+)(\\.\\d{0,2})?"); // 1000.00, 1 000.00, 1000 are valids
	price_input.attr("placeholder", "0.00");
	price_input.focus(reactOnPriceFocus);
	price_input.focusout(reactOnPriceFocusOut);
	price_input.keyup(reactOnExpenseChange);
	price_td.append(price_input);
	expense.append(price_td);

	var categories_td = $("<td/>");
	categories_td.addClass("categories-data");
	var categories_input = $("<input/>");
	categories_input.addClass("categories-input");
	categories_input.attr("type", "text");
	categories_input.attr("placeholder", "Classify expense");
	var autocomp_categories = XML_TREES_ELTS;
	var autocomp = new AutocompleteItem(categories_input, autocomp_categories);
	if (autocomp_categories.length == 0) {
		unitializedCategories.push(autocomp);
	}
	categories_td.append(categories_input);
	expense.append(categories_td);

	var actions_td = $("<td/>");
	var delete_img = $("<span/>");
	delete_img.addClass("glyphicon glyphicon-trash");
	delete_img.attr("style", "cursor:pointer;");
	delete_img.attr("title", "Delete this expense");
	delete_img.click(function() {
		$(this).parent().parent().remove();
		if (ADD_EXPENSES_TABLE.children().length == 0) {
			appendExpense();
		}
	});
	actions_td.append(delete_img);
	var send_img = $("<span/>");
	send_img.addClass("glyphicon glyphicon-ok");
	send_img.attr("style", "cursor:pointer;");
	send_img.attr("title", "Save this expense");
	send_img.click(ajaxSaveNewExpense);
	actions_td.append(send_img);
	expense.append(actions_td);

	ADD_EXPENSES_TABLE.append(expense);
}

function getTreeNodePath(node) {
	if (node && node.attr("title")) {
		var parent_path = getTreeNodePath(node.parent());
		if (parent_path.length > 0) {
			return parent_path + " > " + node.attr("title");
		} else {
			return node.attr("title");
		}
	}
	return "";
}

function loadTrees(nodes) {
	for (var i = 0 ; i != nodes.length ; i++) {
		var node = $(nodes[i]);
		var children = node.children();
		if (children.length > 0) {
			loadTrees(children);
		} else {
			var elt = {
					autocomplete_rawdata_on: getTreeNodePath(node),
					autocomplete_rawdata_after: node.attr("title"),
					autocomplete_id: node.attr("id")
			};
			XML_TREES_ELTS.push(elt);
		}
	}
}

function initAddExpensesTable(addExpenseUrl, xmlTreeUrl) {
	// Initialize expenses table

	XML_EXPENSE_ADD_URL = addExpenseUrl;
	ADD_EXPENSES_TABLE = $('table#add_expenses_table tbody');
	ADD_EXPENSES_TABLE.html("");
	appendExpense();
	$("button#submit-expenses").click(ajaxSaveAllExpenses);

	$.ajax({
		type: "get",
		url: xmlTreeUrl,
		dataType: "xml",
		success: function(xml)
		{
			XML_TREES_ELTS = new Array();
			var root_nodes = $(xml).find("trees").first().find("> node");
			loadTrees(root_nodes);
			for (var i = 0 ; i != unitializedCategories.length ; i++) {
				unitializedCategories[i].updateList(XML_TREES_ELTS);
			}
			unitializedCategories = new Array();
		}
	});
}

/**
 * AJAX query to save all expenses
 */
function ajaxSaveAllExpenses()
{
	if (! XML_EXPENSE_ADD_URL)
	{
		alert("Unable to find the url");
		return;
	}

	var expenses = ADD_EXPENSES_TABLE.find("tr");
	for (var i = 0 ; i != expenses.length ; i++) {
		var expense = $(expenses[i]);
		var expense_details = isFilledExpense(expense);
		if (expense_details && (expense.hasClass('expense-edit') || expense.hasClass('expense-fail'))) {
			expense.find("span.glyphicon-ok").click();
		}
	}
}

/**
 * AJAX query to save new expense
 */
function ajaxSaveNewExpense()
{
	if (! XML_EXPENSE_ADD_URL)
	{
		alert("Unable to find the url");
		return;
	}

	var expense = $(this).parent().parent();
	if (! expense.hasClass('expense-edit') && ! expense.hasClass('expense-fail'))
	{
		alert("The operation cannot be run on this expense");
		return;
	}
	var expense_details = isFilledExpense(expense);
	if (! expense_details) {
		alert("Please fill the expense before saving it");
		return;
	}

	var categories = "";
	for (var i = 0 ; i != expense_details["categories"].length ; i++) {
		categories += "[" + expense_details["categories"][i] + "]";
	}

	expense.removeClass('expense-edit');
	expense.addClass('expense-wait');
	$.ajax({
		type: "post",
		url: XML_EXPENSE_ADD_URL,
		data:
		{
			_xsrf: getCookie("_xsrf"),
			client_id: expense.attr('data-expense-identifier'),
			title: expense_details['title'],
			date: expense_details['date'],
			price: expense_details['price'],
			categories: categories,
		},
		dataType: "xml",
		success: function(xml)
		{
			expense.removeClass('expense-wait');
			expense.addClass('expense-success');
			setTimeout(function() {
				expense.remove();
			}, 3000);
		},
		error: function(xhr)
		{
			expense.removeClass("expense-wait");
			expense.addClass('expense-fail');
			setTimeout(function() {
				expense.removeClass("expense-fail");
				expense.addClass('expense-edit');
			}, 3000);
			alert($(xhr.responseText).text());
		}
	});
}

<commit_msg>[bug] Categories were not sent to server (due to move to js-tools/autocomplete)<commit_after>var XML_EXPENSE_ADD_URL = null;
var ADD_EXPENSES_TABLE = null;
var XML_TREES_ELTS = new Array();
var LAST_EXPENSE_ID = 0;
var unitializedCategories = new Array();

function readExpenseDate(expense) {
	var date_input = expense.find("input.date-input");
	if (date_input.length != 1) {
		return undefined;
	}
	if (date_input[0].type == "date") {
		if (date_input.val().match('^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$')) {
			return date_input.val();
		}
	} else { //text
		var m = date_input.val().match('^([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})$');
		if (m) {
			return m[3] + '-' + m[2] + '-' + m[1];
		}
	}
	return undefined;
}

function readExpenseTitle(expense) {
	var title_input = expense.find("input.title-input");
	if (title_input.length != 1 || title_input.val().length == 0) {
		return undefined;
	}
	return title_input.val();
}

function readExpensePrice(expense) {
	var price_input = expense.find("input.price-input");
	if (price_input.length != 1
				|| ! price_input.val().match('^[-+]?(([0-9]{1,3}([, ][0-9]{3})*|[0-9]+)(.[0-9]?[0-9]?)?)$')
				|| parseFloat(price_input.val().replace(/ /g, '').replace(/,/g, '')) == 0) {
		return undefined;
	}
	return parseFloat(price_input.val().replace(/ /g, '').replace(/,/g, ''));
}

function readExpenseCategories(expense) {
	var categories_elt = expense.find(".categories-data ul.autocomplete-selection li");
	var categories = new Array();
	for (var i = 0 ; i != categories_elt.length ; i++) {
		categories.push($(categories_elt[i]).attr("data-id"));
	}
	return categories;
}

function isFilledExpense(expense) {
	// Given an expense, return true is it is fully and correctly filled

	var expense_date = readExpenseDate(expense);
	if (! expense_date) {
		return undefined;
	}

	var expense_title = readExpenseTitle(expense);
	if (! expense_title) {
		return undefined;
	}

	var expense_price = readExpensePrice(expense);
	if (! expense_price) {
		return undefined;
	}

	var expense_categories = readExpenseCategories(expense);

	return {
		date: expense_date,
		title: expense_title,
		price: expense_price,
		categories: expense_categories
	};
}

function reactOnExpenseChange() {
	// Automaticcaly append expenses when the last line is fully filled
	// Only the last line is checked

	var expense = $(this).parent().parent();
	// expense is the last expense of the table is not equivalent to
	// data-expense-identifier == LAST_EXPENSE_ID as the expense could have been deleted..
	if (expense[0] == ADD_EXPENSES_TABLE.children().last()[0] && isFilledExpense(expense)) {
		appendExpense();
	}
}

function reactOnPriceFocusOut() {
	// Automatically format the price on focus out action

	var price_value = readExpensePrice($(this).parent().parent());
	if (! price_value) {
		return false;
	}

	var price_value_str = $(this).val();
	var price_value_new_str = price_value.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});

	var language = window.navigator.userLanguage || window.navigator.language;
	if (language.toLowerCase() == 'fr' || language.toLowerCase() == 'fr-fr') {
		price_value_new_str = price_value_new_str.replace(/,/g, ' ');
	}
	if (price_value_str != price_value_new_str) {
		$(this).val(price_value_new_str);
	}
}

function reactOnPriceFocus() {
	// Automatically format the price on focus in order to ease the modification

	var price_value = readExpensePrice($(this).parent().parent());
	if (! price_value) {
		return false;
	}

	var price_value_str = $(this).val();
	var price_value_new_str = price_value.toFixed(2);
	if (price_value_str != price_value_new_str) {
		$(this).val(price_value_new_str);
	}
}

function appendExpense() {
	// Append an empty expense in the form

	LAST_EXPENSE_ID++;
	var expense = $("<tr/>");
	expense.addClass('expense-edit');
	expense.attr("data-expense-identifier", LAST_EXPENSE_ID);

	var date_td = $("<td/>");
	var date_input_desc = $("<span/>");
	date_input_desc.addClass("glyphicon glyphicon-calendar");
	var date_input = $("<input/>");
	date_input.addClass("date-input");
	date_input.attr("required", "");
	date_input.attr("type", "date");
	if (date_input[0].type == "text") //date type not supported (HTML5)
	{
		date_input.attr("placeholder", "dd/mm/yyyy");
		date_input.datepicker({dateFormat: "dd/mm/yy"});
	}
	date_input.change(reactOnExpenseChange);
	date_input.keyup(reactOnExpenseChange);
	date_td.append(date_input_desc);
	date_td.append(date_input);
	expense.append(date_td);

	var title_td = $("<td/>");
	var title_input = $("<input/>");
	title_input.addClass("title-input");
	title_input.attr("required", "");
	title_input.attr("size", "50");
	title_input.attr("type", "text");
	title_input.attr("placeholder", "Short description of the expense");
	title_input.keyup(reactOnExpenseChange);
	title_td.append(title_input);
	expense.append(title_td);

	var price_td = $("<td/>");
	var price_input = $("<input/>");
	price_input.addClass("price-input");
	price_input.attr("required", "");
	price_input.attr("size", "10");
	price_input.attr("type", "text");
	price_input.attr("pattern", "[-+]?(\\d{1,3}([\\s,]\\d{3})*|\\d+)(\\.\\d{0,2})?"); // 1000.00, 1 000.00, 1000 are valids
	price_input.attr("placeholder", "0.00");
	price_input.focus(reactOnPriceFocus);
	price_input.focusout(reactOnPriceFocusOut);
	price_input.keyup(reactOnExpenseChange);
	price_td.append(price_input);
	expense.append(price_td);

	var categories_td = $("<td/>");
	categories_td.addClass("categories-data");
	var categories_input = $("<input/>");
	categories_input.addClass("categories-input");
	categories_input.attr("type", "text");
	categories_input.attr("placeholder", "Classify expense");
	var autocomp_categories = XML_TREES_ELTS;
	var autocomp = new AutocompleteItem(categories_input, autocomp_categories);
	if (autocomp_categories.length == 0) {
		unitializedCategories.push(autocomp);
	}
	categories_td.append(categories_input);
	expense.append(categories_td);

	var actions_td = $("<td/>");
	var delete_img = $("<span/>");
	delete_img.addClass("glyphicon glyphicon-trash");
	delete_img.attr("style", "cursor:pointer;");
	delete_img.attr("title", "Delete this expense");
	delete_img.click(function() {
		$(this).parent().parent().remove();
		if (ADD_EXPENSES_TABLE.children().length == 0) {
			appendExpense();
		}
	});
	actions_td.append(delete_img);
	var send_img = $("<span/>");
	send_img.addClass("glyphicon glyphicon-ok");
	send_img.attr("style", "cursor:pointer;");
	send_img.attr("title", "Save this expense");
	send_img.click(ajaxSaveNewExpense);
	actions_td.append(send_img);
	expense.append(actions_td);

	ADD_EXPENSES_TABLE.append(expense);
}

function getTreeNodePath(node) {
	if (node && node.attr("title")) {
		var parent_path = getTreeNodePath(node.parent());
		if (parent_path.length > 0) {
			return parent_path + " > " + node.attr("title");
		} else {
			return node.attr("title");
		}
	}
	return "";
}

function loadTrees(nodes) {
	for (var i = 0 ; i != nodes.length ; i++) {
		var node = $(nodes[i]);
		var children = node.children();
		if (children.length > 0) {
			loadTrees(children);
		} else {
			var elt = {
					autocomplete_rawdata_on: getTreeNodePath(node),
					autocomplete_rawdata_after: node.attr("title"),
					autocomplete_id: node.attr("id")
			};
			XML_TREES_ELTS.push(elt);
		}
	}
}

function initAddExpensesTable(addExpenseUrl, xmlTreeUrl) {
	// Initialize expenses table

	XML_EXPENSE_ADD_URL = addExpenseUrl;
	ADD_EXPENSES_TABLE = $('table#add_expenses_table tbody');
	ADD_EXPENSES_TABLE.html("");
	appendExpense();
	$("button#submit-expenses").click(ajaxSaveAllExpenses);

	$.ajax({
		type: "get",
		url: xmlTreeUrl,
		dataType: "xml",
		success: function(xml)
		{
			XML_TREES_ELTS = new Array();
			var root_nodes = $(xml).find("trees").first().find("> node");
			loadTrees(root_nodes);
			for (var i = 0 ; i != unitializedCategories.length ; i++) {
				unitializedCategories[i].updateList(XML_TREES_ELTS);
			}
			unitializedCategories = new Array();
		}
	});
}

/**
 * AJAX query to save all expenses
 */
function ajaxSaveAllExpenses()
{
	if (! XML_EXPENSE_ADD_URL)
	{
		alert("Unable to find the url");
		return;
	}

	var expenses = ADD_EXPENSES_TABLE.find("tr");
	for (var i = 0 ; i != expenses.length ; i++) {
		var expense = $(expenses[i]);
		var expense_details = isFilledExpense(expense);
		if (expense_details && (expense.hasClass('expense-edit') || expense.hasClass('expense-fail'))) {
			expense.find("span.glyphicon-ok").click();
		}
	}
}

/**
 * AJAX query to save new expense
 */
function ajaxSaveNewExpense()
{
	if (! XML_EXPENSE_ADD_URL)
	{
		alert("Unable to find the url");
		return;
	}

	var expense = $(this).parent().parent();
	if (! expense.hasClass('expense-edit') && ! expense.hasClass('expense-fail'))
	{
		alert("The operation cannot be run on this expense");
		return;
	}
	var expense_details = isFilledExpense(expense);
	if (! expense_details) {
		alert("Please fill the expense before saving it");
		return;
	}

	var categories = "";
	for (var i = 0 ; i != expense_details["categories"].length ; i++) {
		categories += "[" + expense_details["categories"][i] + "]";
	}

	expense.removeClass('expense-edit');
	expense.addClass('expense-wait');
	$.ajax({
		type: "post",
		url: XML_EXPENSE_ADD_URL,
		data:
		{
			_xsrf: getCookie("_xsrf"),
			client_id: expense.attr('data-expense-identifier'),
			title: expense_details['title'],
			date: expense_details['date'],
			price: expense_details['price'],
			categories: categories,
		},
		dataType: "xml",
		success: function(xml)
		{
			expense.removeClass('expense-wait');
			expense.addClass('expense-success');
			setTimeout(function() {
				expense.remove();
			}, 3000);
		},
		error: function(xhr)
		{
			expense.removeClass("expense-wait");
			expense.addClass('expense-fail');
			setTimeout(function() {
				expense.removeClass("expense-fail");
				expense.addClass('expense-edit');
			}, 3000);
			alert($(xhr.responseText).text());
		}
	});
}

<|endoftext|>


<commit_before>if (typeof cordova === 'undefined') {
    cordova = {};
    cordova.plugins = {};
    cordova.plugins.notification = {};
    cordova.plugins.notification.local = {};
}
cordova.plugins.notification.local.schedule = function(options, callback, scope) {
    var notifications = Array.isArray(options) ? options : [options];
    for (var i = 0; i < notifications.length; i++) {
	var properties = notifications[i];

	CDVNotification_mergeWithDefaults(properties);
	CDVNotification_convertProperties(properties);
    }
    CDVNotification_schedule(notifications, callback);
};

cordova.plugins.notification.local.update = function(options, callback, scope) {
    var notifications = Array.isArray(options) ? options : [options];
    for (var i = 0; i < notifications.length; i++) {
	var properties = notifications[i];
	CDVNotification_convertProperties(properties);
    }
    CDVNotification_update(notifications, callback);
};

cordova.plugins.notification.local.clear = function(ids, callback, scope) {
    ids = Array.isArray(ids) ? ids : [ids];
    ids = CDVNotification_convertIds(ids);
    CDVNotification_clear(ids, callback);
};

cordova.plugins.notification.local.cancel = function(ids, callback, scope) {
    ids = Array.isArray(ids) ? ids : [ids];
    ids = CDVNotification_convertIds(ids);
    CDVNotification_cancel(ids, callback);
};

cordova.plugins.notification.local.on = function(event, callback, scope) {
    if (!CDVNotification_listener[event]) {
	CDVNotification_listener[event] = [];
    }
    var item = [callback, scope || window];
    CDVNotification_listener[event].push(item);
};

CDVNotification_defaults = {
    text:  '',
    title: '',
    sound: 'res://platform_default',
    badge: 0,
    id:    "0",
    data: undefined,
    every: undefined,
    at: undefined
};

CDVNotification_getValueFor = function(options) {
    var keys = Array.apply(null, arguments).slice(1);

    for (var i = 0; i < keys.length; i++) {
	var key = keys[i];

	if (options.hasOwnProperty(key)) {
	    return options[key];
	}
    }
};

CDVNotification_mergeWithDefaults = function(options) {
    var defaults = CDVNotification_defaults;

    options.at   = CDVNotification_getValueFor(options, 'at', 'firstAt', 'date');
    options.text = CDVNotification_getValueFor(options, 'text', 'message');
    options.data = CDVNotification_getValueFor(options, 'data', 'json');

    if (options.at === undefined || options.at === null) {
        options.at = new Date();
    }

    for (var key in defaults) {
        if (options[key] === null || options[key] === undefined) {
            if (options.hasOwnProperty(key) && ['data','sound'].indexOf(key) > -1) {
                options[key] = undefined;
            } else {
                options[key] = defaults[key];
            }
        }
    }

    for (key in options) {
        if (!defaults.hasOwnProperty(key)) {
            delete options[key];
        }
    }

    return options;
};

CDVNotification_convertProperties = function(options) {
    if (options.id) {
        if (isNaN(options.id)) {
            options.id = CDVNotification_defaults;
        } else {
            options.id = options.id.toString();
        }
    }

    if (options.title) {
        options.title = options.title.toString();
    }

    if (options.text) {
        options.text  = options.text.toString();
    }

    if (options.badge) {
        if (isNaN(options.badge)) {
            options.badge = CDVNotification_defaults.badge;
        } else {
            options.badge = Number(options.badge);
        }
    }

    if (typeof options.at == 'object') {
        options.at = Math.round(options.at.getTime()/1000);
    }

    if (typeof options.data == 'object') {
        options.data = JSON.stringify(options.data);
    }

    return options;
};

CDVNotification_convertIds = function(ids) {
    var convertedIds = [];
    for (var i = 0; i < ids.length; i++) {
	convertedIds.push(ids[i].toString());
    }
    return convertedIds;
};

CDVNotification_fireEvent = function(event) {
    var args = Array.apply(null, arguments).splice(1),
	listener = CDVNotification_listener[event];
    if (!listener) {
	return;
    }

    for (var i = 0; i < listener.length; i++) {
	var fn = listener[i][0],
	    scope = listener[i][1];
	fn.apply(scope, args);
    }
};

CDVNotification_listener = {};
<commit_msg>Implemented showNotification for service worker<commit_after>showNotification = function(title, options) {
    return new Promise(function(resolve, reject) {
	try {
	    new Notification(title, options);
	    resolve();
	} catch(e) {
	    console.log(e);
	    reject();
	}
    });
};

if (typeof cordova === 'undefined') {
    cordova = {};
    cordova.plugins = {};
    cordova.plugins.notification = {};
    cordova.plugins.notification.local = {};
}
cordova.plugins.notification.local.schedule = function(options, callback, scope) {
    var notifications = Array.isArray(options) ? options : [options];
    for (var i = 0; i < notifications.length; i++) {
	var properties = notifications[i];

	CDVNotification_mergeWithDefaults(properties);
	CDVNotification_convertProperties(properties);
    }
    CDVNotification_schedule(notifications, callback);
};

cordova.plugins.notification.local.update = function(options, callback, scope) {
    var notifications = Array.isArray(options) ? options : [options];
    for (var i = 0; i < notifications.length; i++) {
	var properties = notifications[i];
	CDVNotification_convertProperties(properties);
    }
    CDVNotification_update(notifications, callback);
};

cordova.plugins.notification.local.clear = function(ids, callback, scope) {
    ids = Array.isArray(ids) ? ids : [ids];
    ids = CDVNotification_convertIds(ids);
    CDVNotification_clear(ids, callback);
};

cordova.plugins.notification.local.cancel = function(ids, callback, scope) {
    ids = Array.isArray(ids) ? ids : [ids];
    ids = CDVNotification_convertIds(ids);
    CDVNotification_cancel(ids, callback);
};

cordova.plugins.notification.local.on = function(event, callback, scope) {
    if (!CDVNotification_listener[event]) {
	CDVNotification_listener[event] = [];
    }
    var item = [callback, scope || window];
    CDVNotification_listener[event].push(item);
};

CDVNotification_defaults = {
    text:  '',
    title: '',
    sound: 'res://platform_default',
    badge: 0,
    id:    "0",
    data: undefined,
    every: undefined,
    at: undefined
};

CDVNotification_getValueFor = function(options) {
    var keys = Array.apply(null, arguments).slice(1);

    for (var i = 0; i < keys.length; i++) {
	var key = keys[i];

	if (options.hasOwnProperty(key)) {
	    return options[key];
	}
    }
};

CDVNotification_mergeWithDefaults = function(options) {
    var defaults = CDVNotification_defaults;

    options.at   = CDVNotification_getValueFor(options, 'at', 'firstAt', 'date');
    options.text = CDVNotification_getValueFor(options, 'text', 'message');
    options.data = CDVNotification_getValueFor(options, 'data', 'json');

    if (options.at === undefined || options.at === null) {
        options.at = new Date();
    }

    for (var key in defaults) {
        if (options[key] === null || options[key] === undefined) {
            if (options.hasOwnProperty(key) && ['data','sound'].indexOf(key) > -1) {
                options[key] = undefined;
            } else {
                options[key] = defaults[key];
            }
        }
    }

    for (key in options) {
        if (!defaults.hasOwnProperty(key)) {
            delete options[key];
        }
    }

    return options;
};

CDVNotification_convertProperties = function(options) {
    if (options.id) {
        if (isNaN(options.id)) {
            options.id = CDVNotification_defaults;
        } else {
            options.id = options.id.toString();
        }
    }

    if (options.title) {
        options.title = options.title.toString();
    }

    if (options.text) {
        options.text  = options.text.toString();
    }

    if (options.badge) {
        if (isNaN(options.badge)) {
            options.badge = CDVNotification_defaults.badge;
        } else {
            options.badge = Number(options.badge);
        }
    }

    if (typeof options.at == 'object') {
        options.at = Math.round(options.at.getTime()/1000);
    }

    if (typeof options.data == 'object') {
        options.data = JSON.stringify(options.data);
    }

    return options;
};

CDVNotification_convertIds = function(ids) {
    var convertedIds = [];
    for (var i = 0; i < ids.length; i++) {
	convertedIds.push(ids[i].toString());
    }
    return convertedIds;
};

CDVNotification_fireEvent = function(event) {
    var args = Array.apply(null, arguments).splice(1),
	listener = CDVNotification_listener[event];
    if (!listener) {
	return;
    }

    for (var i = 0; i < listener.length; i++) {
	var fn = listener[i][0],
	    scope = listener[i][1];
	fn.apply(scope, args);
    }
};

CDVNotification_listener = {};
<|endoftext|>


<commit_before>/* global ftui:true, Modul_widget:true */

function depends_fullcalview() {
    if (!$.fn.fullCalendar) {
        $('head').append('<link rel="stylesheet" href="fullcalendar/fullcalendar.css" type="text/css" />');
        return ['fullcalendar/lib/moment.min.js',
		'fullcalendar/fullcalendar.js',
		'fullcalendar/lang/de.js',
		'ical/ical.js',
		'ical/ical_events.js',
		'ical/ical_fullcalendar.js'];
    }
}

var Modul_fullcalview = function () {
    var eventsources;
    var eventcolors;
    var eventtextcolors;
    var firstRun = [];

    function data_req(url, callback) {
        var req = new XMLHttpRequest();
        req.addEventListener('load', callback);
        req.open('GET', url);
        req.send();
    }

    function init() {

        var me = this;
        me.elements = $('div[data-type="fullcalview"]');

        me.elements.each(function (index) {
            var elem = $(this);
            elem.data('eventlimit', elem.data('eventlimit') || true); //true, false
            elem.data('defaultview', elem.data('defaultview') || 'basicWeek'); //alle verfügbaren Views auflisten
            elem.data('timeformat', elem.data('timeformat') || 'H:mm'); //Format für Zeitangabe
            elem.data('lang', elem.data('lang') || 'de'); //Länderkürzel für Sprache
            elem.data('weeknumbers', elem.data('weeknumbers') || true); //KW Anzeige
            elem.data('contentheight', elem.data('contentheight') || 350); //Höhe
            elem.data('contentwidth', elem.data('contentwidth') || 600); //Breite
            elem.data('headerleft', elem.data('headerleft') || 'title');
            elem.data('headercenter', elem.data('headercenter') || '');
            elem.data('headerright', elem.data('headerright') || 'today month basicWeek basic-->Week prev,next');

            me.addReading(elem, 'get');

            elem.append('

');
            var eventLimitVar = elem.data('eventlimit');
            var defaultViewVar = elem.data('defaultview');
            var timeFormatVar = elem.data('timeformat');
            var langVar = elem.data('lang');
            var weeknumbersVar = elem.data('weeknumbers');
            var contentHeightVar = elem.data('contentheight');
            var contentWidthVar = elem.data('contentwidth');
            var headerLeftVar = elem.data('headerleft');
            var headerCenterVar = elem.data('headercenter');
            var headerRightVar = elem.data('headerright');
            eventsources = elem.data('eventsources');
            eventcolors = elem.data('eventcolors');
            eventtextcolors = elem.data('eventtextcolors');

            if ((eventsources && eventsources.length > 0 && eventcolors.length > 0 && eventtextcolors.length > 0) &&
                ((eventsources && eventsources.length == eventcolors.length) && (eventsources.length == eventtextcolors.length))) {
                $(document).ready(function () {
                    $('#calendar').fullCalendar({
                        eventLimit: eventLimitVar, // allow "more" link when too many events
                        header: {
                            left: headerLeftVar,
                            center: headerCenterVar,
                            right: headerRightVar
                        },
                        defaultView: defaultViewVar,
                        timeFormat: timeFormatVar,
                        lang: langVar,
                        weekNumbers: weeknumbersVar,
                        contentHeight: contentHeightVar,
                        contentWidth: contentWidthVar
                    });
                });
            } else
                console.log('Invalid number of parameters number - please check your config.');
        });
    }

    function update(dev, par) {
        var me = this;

        for (var n = 0; n < eventsources.length; n++) {

            if (eventsources[n] == dev && !firstRun[n]) {
                (function (clsn) {
                    $('#calendar').fullCalendar('removeEvents', function (event) {
                        return event.eID == eventsources[clsn];
                    });
                })(n);

                (function (clsn) {
                    data_req('/fhem?detail=' + eventsources[clsn] + '&dev.get' + eventsources[clsn] + '=' + eventsources[clsn] + '&cmd.get' + eventsources[clsn] + '=get&arg.get' + eventsources[clsn] + '=vcalendar&val.get' + eventsources[clsn] + '=&XHR=1', function (e, r) {
                        $('#calendar').fullCalendar('addEventSource', {
                            events: fc_events(this.response, eventsources[clsn], eventcolors[clsn], eventtextcolors[clsn]),
                        });
                        if (!e) {}
                    });
                })(n);

                firstRun[n] = true;
            } else {
                firstRun[n] = false;
            }
        }
    }

    var me = $.extend(new Modul_widget(), {
        widgetname: 'fullcalview',
        init: init,
        update: update,
    });

    return me;
};<commit_msg>Updated widget_fullcalview<commit_after>/* global ftui:true, Modul_widget:true, moment:true */

//"use strict";

function depends_fullcalview() {
    if (!$.fn.fullCalendar) {
        $('head').append('<link rel="stylesheet" href="fullcalendar/fullcalendar.css" type="text/css" />');
        $('head').append('<link rel="stylesheet" href="lib/jquery.qtip.min.css" type="text/css" />');
        return ['fullcalendar/lib/moment.min.js',
		'fullcalendar/fullcalendar.js',
		'fullcalendar/lang/de.js',
		'ical/ical.js',
		'ical/ical_events.js',
		'ical/ical_fullcalendar.js',
		'lib/jquery.qtip.min.js',
		'lib/moment.min.js'];
    }
}

var showURL = false;

var Modul_fullcalview = function () {
    var sources_to_load_cnt;
    var eventsources;
    var eventcolors;
    var eventtextcolors;
    var ics_sources = [];
    var firstRun = [];
    var recur_done = false;

    var tooltip = $('
').qtip({
        id: 'fullcalendar',
        prerender: true,
        content: {
            text: ' '
        },
        position: {
            my: 'top center',
            at: 'bottom right',
            target: 'event',
            viewport: $('#fullcalendar'),
            adjust: {
                mouse: false,
                scroll: false
            }
        },
        show: false,
        hide: false,
        style: 'qtip-light'
    }).qtip('api');

    function data_req(url, callback) {
        var req = new XMLHttpRequest();
        req.addEventListener('load', callback);
        req.open('GET', url);
        req.send();
    }

    function add_recur_events() {
        if (sources_to_load_cnt < 1 && !recur_done) {
            $('#calendar').fullCalendar('addEventSource', expand_recur_events);
            recur_done = true;
        } else if (recur_done === false) {
            setTimeout(add_recur_events, 30);
        }
    }

    function load_ics(ics) {
        data_req(ics.url, function () {
            $('#calendar').fullCalendar('addEventSource', fc_events(this.response, ics.eID, ics.event_properties));
            sources_to_load_cnt -= 1;
        });
    }

    function init() {

        me.elements = $('div[data-type="fullcalview"]');

        me.elements.each(function (index) {
            var elem = $(this);
            elem.data('eventlimit', elem.data('eventlimit') || true); //true, false
            elem.data('defaultview', elem.data('defaultview') || 'basicWeek'); //alle verfügbaren Views auflisten
            elem.data('timeformat', elem.data('timeformat') || 'H:mm'); //Format für Zeitangabe
            elem.data('lang', elem.data('lang') || 'de'); //Länderkürzel für Sprache
            elem.data('weeknumbers', elem.data('weeknumbers') || true); //KW Anzeige
            elem.data('contentheight', elem.data('contentheight') || 350); //Höhe
            elem.data('contentwidth', elem.data('contentwidth') || 600); //Breite
            elem.data('headerleft', elem.data('headerleft') || 'title');
            elem.data('headercenter', elem.data('headercenter') || '');
            elem.data('headerright', elem.data('headerright') || 'today month basicWeek basic-->Week prev,next');

            if (elem.data('show_url'))
                showURL = elem.data('show_url'); //elem.data('show_url');  //URLs anzeigen

            elem.append('

');
            var eventLimitVar = elem.data('eventlimit');
            var defaultViewVar = elem.data('defaultview');
            var timeFormatVar = elem.data('timeformat');
            var langVar = elem.data('lang');
            var weeknumbersVar = elem.data('weeknumbers');
            var contentHeightVar = elem.data('contentheight');
            var contentWidthVar = elem.data('contentwidth');
            var headerLeftVar = elem.data('headerleft');
            var headerCenterVar = elem.data('headercenter');
            var headerRightVar = elem.data('headerright');
            var eventdataUpdate = [];
            eventsources = ($.isArray(elem.data('device')) ? elem.data('device') : [elem.data('device')]);
            eventcolors = ($.isArray(elem.data('eventcolors')) ? elem.data('eventcolors') : [elem.data('eventcolors')]);
            eventtextcolors = ($.isArray(elem.data('eventtextcolors')) ? elem.data('eventtextcolors') : [elem.data('eventtextcolors')]);
            var preURL = ($("meta[name='fhemweb_url']").attr("content") ? $("meta[name='fhemweb_url']").attr("content") : '');

            if ((eventsources.length > 0 && eventcolors.length > 0 && eventtextcolors.length > 0) &&
                ((eventsources.length == eventcolors.length) && (eventsources.length == eventtextcolors.length))) {
                for (var n = 0; n < eventsources.length; n++) {
                    eventdataUpdate[n] = eventsources[n] + ':lastUpdate';
                    ics_sources[n] = {
                        eID: '' + eventsources[n] + '',
                        url: preURL + '/fhem?detail=' + eventsources[n] + '&dev.get' + eventsources[n] + '=' + eventsources[n] + '&cmd.get' + eventsources[n] + '=get&arg.get' + eventsources[n] + '=vcalendar&val.get' + eventsources[n] + '=&XHR=1',
                        event_properties: {
                            color: '' + eventcolors[n] + '',
                            textColor: '' + eventtextcolors[n] + ''
                        }
                    };
                }

                sources_to_load_cnt = ics_sources.length;

                elem.data('get', eventdataUpdate);
                me.addReading(elem, 'get');

                $('#calendar').fullCalendar({
                    eventLimit: eventLimitVar, // allow "more" link when too many events
                    header: {
                        left: headerLeftVar,
                        center: headerCenterVar,
                        right: headerRightVar
                    },
                    defaultView: defaultViewVar,
                    timeFormat: timeFormatVar,
                    lang: langVar,
                    weekNumbers: weeknumbersVar,
                    contentHeight: contentHeightVar,
                    contentWidth: contentWidthVar,
                    eventClick: function (data, event, view) {
                        var content = '
' + data.title + '
' +
                            '
Beschreibung: ' + data.description + '
' +
                            '
Ort: ' + data.location + '
' +
                            '
Start: ' + moment(data.start).format('DD.MM.YYYY HH:mm') + '
' +
                            (data.end && '
Ende: ' + moment(data.end).format('DD.MM.YYYY HH:mm') + '
' || '');
                        tooltip.set({
                                'content.text': content
                            })
                            .reposition(event).show(event);
                    },
                    dayClick: function () {
                        tooltip.hide();
                    },
                    eventResizeStart: function () {
                        tooltip.hide();
                    },
                    eventDragStart: function () {
                        tooltip.hide();
                    },
                    viewDisplay: function () {
                        tooltip.hide();
                    }
                });

            } else
                console.log('Invalid number of parameters number - please check your config.');
        });
    }

    function update(dev, par) {

        me.elements.filterDeviceReading('get', dev, par).each(function (index) {

            var recur_events = [];

            for (var n = 0; n < eventsources.length; n++) {
                if (eventsources[n] == dev && !firstRun[n]) {
                    (function (clsn) {
                        $('#calendar').fullCalendar('removeEvents', function (event) {
                            return event.eID == eventsources[clsn];
                        });
                    })(n);

                    (function (clsn) {
                        load_ics(ics_sources[clsn]);
                        add_recur_events();
                    })(n);

                    firstRun[n] = true;
                } else {
                    firstRun[n] = false;
                }
            }
        });
    }

    var me = $.extend(new Modul_widget(), {
        widgetname: 'fullcalview',
        init: init,
        update: update,
    });

    return me;
};
<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
import {a as testgroup} from 'ilios/tests/helpers/test-groups';

moduleForComponent('boolean-check', 'Unit | Component | boolean check ' + testgroup, {
  unit: true
});

test('default properties are correct', function(assert) {
  assert.expect(2);

  const component = this.subject();

  const tagName = component.get('tagName');
  const value = component.get('value');

  assert.equal(tagName, 'span', 'span contains input helper');
  assert.equal(value, false, 'checkbox is unchecked by default');
});

test('`click` event triggers primary action', function(assert) {
  assert.expect(1);

  const component = this.subject();

  component.click = () => {
    assert.ok(true, 'click triggers primary action');
  };

  this.render();
  component.click();
});
<commit_msg>rm obsolete test.<commit_after>import { moduleForComponent, test } from 'ember-qunit';
import {a as testgroup} from 'ilios/tests/helpers/test-groups';

moduleForComponent('boolean-check', 'Unit | Component | boolean check ' + testgroup, {
  unit: true
});

test('default properties are correct', function(assert) {
  assert.expect(2);

  const component = this.subject();

  const tagName = component.get('tagName');
  const value = component.get('value');

  assert.equal(tagName, 'span', 'span contains input helper');
  assert.equal(value, false, 'checkbox is unchecked by default');
});
<|endoftext|>


<commit_before>/* globals girderTest, describe, it, expect, waitsFor, runs */
girderTest.importPlugin('jobs', 'worker', 'slicer_cli_web');

var slicer;
girderTest.promise.done(function () {
    slicer = girder.plugins.slicer_cli_web;
});

girderTest.startApp();

// Creates container and Default Task Folder and sets it in the settings using REST
function createDefaultTaskFolder(name, description, folder) {
    var collectionID = false;
    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/collection',
            method: 'POST',
            data: {
                name: name,
                description: description,
                public: true
            },
            async: false
        });
        collectionID = resp.responseJSON._id;
        return resp && resp.responseJSON;
    }, 'Creating Collection');

    var folderID = false;
    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/folder',
            method: 'POST',
            data: {
                name: folder,
                parentType: 'collection',
                parentId: collectionID,
                public: true
            },
            async: false
        });
        folderID = resp.responseJSON._id;
        return resp && resp.responseJSON;
    }, 'Creating SubFolder');

    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/system/setting',
            method: 'PUT',
            data: {
                key: 'slicer_cli_web.task_folder',
                value: folderID
            },
            async: false
        });
        return resp && resp.responseJSON;
    }, 'Setting Default Task Folder');
}

function navigateToCollectionsFolder(collectionName, folderName) {
    waitsFor(function () {
        return $('a.g-nav-link[g-target="collections"]').length > 0;
    }, 'collection list link to load');
    runs(function () {
        $('a.g-nav-link[g-target="collections"]').click();
    });
    waitsFor(function () {
        return $('.g-collection-create-button').length > 0;
    }, 'collection list screen to load');
    girderTest.waitForLoad();
    waitsFor(function () {
        return $('.g-collection-list-entry').length > 0;
    }, 'collection list to load');

    // NOTE: This is equivalent to indexOf, can have multiple matches
    runs(function () {
        $('.g-collection-link:contains(' + collectionName + ')').first().click();
    });
    girderTest.waitForLoad();

    waitsFor(function () {
        return $('.g-folder-list-link').length > 0;
    }, 'the folder list to load');
    runs(function () {
        $('.g-folder-list-link:contains(' + folderName + ')').first().click();
    });
    girderTest.waitForLoad();
}

describe('UploadDockerImages button and functionality', function () {
    it('login, setup collection/folder and navigate to it', function () {
        it('Login as admin', girderTest.login('admin', 'Admin', 'Admin', 'password'));
        createDefaultTaskFolder('Tasks', 'Default Tasks', true, 'Slicer CLI Web Tasks');
        navigateToCollectionsFolder('Tasks', 'Slicer CLI Web Tasks');
    });

    it('check docker task image upload button', function () {
        waitsFor(function () {
            return ($('.g-upload-slicer-cli-task-button').length > 0);
        }, 'upload docker image button to be visible');
        runs(function () {
            expect($('.g-upload-slicer-cli-task-button').length > 0);
        });
    });

    it('test the upload docker image button', function () {
        runs(function () {
            $('.g-upload-slicer-cli-task-button').click();
        });
        waitsFor(function () {
            return ($('#g-slicer-cli-web-image').length > 0);
        });
        girderTest.waitForDialog();
        runs(function () {
            expect($('#g-slicer-cli-web-image').length > 0);
        }, 'the modal dialog to load');
        runs(function () {
            $('#g-slicer-cli-web-upload-form button.close').click();
        }, 'wait for dialog to close');
        girderTest.waitForLoad();
    });

    it('logout from admin account', girderTest.logout());
    it('register a (normal user)',
        girderTest.createUser('johndoe',
            'john.doe@girder.test',
            'John',
            'Doe',
            'password!'));
    it('navigate to task folder and check icon', function () {
        navigateToCollectionsFolder('Tasks', 'Slicer CLI Web Tasks');
        runs(function () {
            expect($('.g-upload-slicer-cli-task-button').length === 0);
        });
    });
});
<commit_msg>removing nested it in Spectest<commit_after>/* globals girderTest, describe, it, expect, waitsFor, runs */
girderTest.importPlugin('jobs', 'worker', 'slicer_cli_web');

var slicer;
girderTest.promise.done(function () {
    slicer = girder.plugins.slicer_cli_web;
});

girderTest.startApp();

// Creates container and Default Task Folder and sets it in the settings using REST
function createDefaultTaskFolder(name, description, folder) {
    var collectionID = false;
    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/collection',
            method: 'POST',
            data: {
                name: name,
                description: description,
                public: true
            },
            async: false
        });
        collectionID = resp.responseJSON._id;
        return resp && resp.responseJSON;
    }, 'Creating Collection');

    var folderID = false;
    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/folder',
            method: 'POST',
            data: {
                name: folder,
                parentType: 'collection',
                parentId: collectionID,
                public: true
            },
            async: false
        });
        folderID = resp.responseJSON._id;
        return resp && resp.responseJSON;
    }, 'Creating SubFolder');

    runs(function () {
        var resp = girder.rest.restRequest({
            url: '/system/setting',
            method: 'PUT',
            data: {
                key: 'slicer_cli_web.task_folder',
                value: folderID
            },
            async: false
        });
        return resp && resp.responseJSON;
    }, 'Setting Default Task Folder');
}

function navigateToCollectionsFolder(collectionName, folderName) {
    waitsFor(function () {
        return $('a.g-nav-link[g-target="collections"]').length > 0;
    }, 'collection list link to load');
    runs(function () {
        $('a.g-nav-link[g-target="collections"]').click();
    });
    waitsFor(function () {
        return $('.g-collection-create-button').length > 0;
    }, 'collection list screen to load');
    girderTest.waitForLoad();
    waitsFor(function () {
        return $('.g-collection-list-entry').length > 0;
    }, 'collection list to load');

    // NOTE: This is equivalent to indexOf, can have multiple matches
    runs(function () {
        $('.g-collection-link:contains(' + collectionName + ')').first().click();
    });
    girderTest.waitForLoad();

    waitsFor(function () {
        return $('.g-folder-list-link').length > 0;
    }, 'the folder list to load');
    runs(function () {
        $('.g-folder-list-link:contains(' + folderName + ')').first().click();
    });
    girderTest.waitForLoad();
}

describe('UploadDockerImages button and functionality', function () {
    it('login, setup collection/folder and navigate to it', function () {
        girderTest.login('admin', 'Admin', 'Admin', 'password')();
        createDefaultTaskFolder('Tasks', 'Default Tasks', 'Slicer CLI Web Tasks');
        navigateToCollectionsFolder('Tasks', 'Slicer CLI Web Tasks');
    });

    it('check docker task image upload button', function () {
        waitsFor(function () {
            return ($('.g-upload-slicer-cli-task-button').length > 0);
        }, 'upload docker image button to be visible');
        runs(function () {
            expect($('.g-upload-slicer-cli-task-button').length > 0);
        });
    });

    it('test the upload docker image button', function () {
        runs(function () {
            $('.g-upload-slicer-cli-task-button').click();
        });
        waitsFor(function () {
            return ($('#g-slicer-cli-web-image').length > 0);
        });
        girderTest.waitForDialog();
        runs(function () {
            expect($('#g-slicer-cli-web-image').length > 0);
        }, 'the modal dialog to load');
        runs(function () {
            $('#g-slicer-cli-web-upload-form button.close').click();
        }, 'wait for dialog to close');
        girderTest.waitForLoad();
    });

    it('logout from admin account', girderTest.logout());
    it('register a (normal user)',
        girderTest.createUser('johndoe',
            'john.doe@girder.test',
            'John',
            'Doe',
            'password!'));
    it('navigate to task folder and check icon', function () {
        navigateToCollectionsFolder('Tasks', 'Slicer CLI Web Tasks');
        runs(function () {
            expect($('.g-upload-slicer-cli-task-button').length === 0);
        });
    });
});
<|endoftext|>


<commit_before>module.exports = function (Colleague, Conduit) {
    var cadence = require('cadence')
    var abend = require('abend')
    var Destructor = require('destructible')
    var Colleague = require('../compassion.colleague/colleague')
    var Kibitzer = require('kibitz')
    var Conduit = require('../compassion.conduit/conduit')
    var Vizsla = require('vizsla')
    var Signal = require('signal')
    var Procession = require('procession')
    var Recorder = require('./recorder')

    var interrupt = require('interrupt').createInterrupter('compassion.counterfeiter')

    function Counterfeiter (address, port) {
        this._colleagues = {}
        // TODO Getting silly, just expose Denizen.
        this.events = {}
        this.kibitzers = {}
        this.loggers = {}
        this._port = port
        this._address = address
        this._destructible = new Destructor('counterfeiter')
        this._destructible.markDestroyed(this, 'destroyed')
    }

    Counterfeiter.prototype._run = cadence(function (async, options) {
        interrupt.assert(!this.destroyed, 'destroyed', {}, this._destructible.errors[0])
        var kibitzer = this.kibitzer = new Kibitzer({ id: options.id, ping: 1000, timeout: 3000 })

        var colleague = new Colleague(new Vizsla, kibitzer, null, 'island')
        this._colleagues[options.id] = colleague
        this.kibitzers[options.id] = colleague.kibitzer
        this.events[options.id] = colleague.kibitzer.log.shifter()
        this._destructible.addDestructor([ 'colleague', options.id ], colleague, 'destroy')

        // Create loggers suitable for replaying.
        var logger = new Procession

        kibitzer.played.shifter().pump(new Recorder('kibitz', logger), 'push')
        kibitzer.paxos.outbox.shifter().pump(new Recorder('paxos', logger), 'push')
        kibitzer.islander.outbox.shifter().pump(new Recorder('islander', logger), 'push')
        colleague.chatter.shifter().pump(new Recorder('colleague', logger), 'push')

        this._destructible.addDestructor([ 'logger', options.id ], logger, 'push')

        this.loggers[options.id] = logger.shifter()

        // Connect colleague directly to conference.
        colleague.read.shifter().pump(options.conference.write, 'enqueue')
        options.conference.read.shifter().pump(colleague.write, 'enqueue')

        // Start colleague.
        var ready = new Signal

        ready.wait(function () {
            console.log('wait wait', arguments)
        })

        colleague.ready.wait(ready, 'unlatch')
        colleague.listen(this._address, this._port, this._destructible.rescue([ 'colleague', options.id ], ready, 'unlatch'))
        ready.wait(async())
    })

    Counterfeiter.prototype.bootstrap = cadence(function (async, options) {
        async(function () {
            this._run(options, async())
        }, function (colleague) {
            this._colleagues[options.id].bootstrap(async())
        }, function () {
            console.log('done')
        })
    })

    Counterfeiter.prototype.join = cadence(function (async, options) {
        async(function () {
            this._run(options, async())
        }, function (colleague) {
            this._colleagues[options.id].join(options.republic, options.leader, async())
        }, function () {
            console.log('done')
        })
    })

    Counterfeiter.prototype._join = cadence(function (async, options) {
        interrupt.assert(!this.destroyed, 'destroyed', this._destructible.errors[0])
        // TODO Fold UserAgent into Counterfeiter.
        var denizen = this._denizens[options.id] = new Denizen(this, options, new UserAgent(this))
        this.kibitzers[options.id] = denizen.kibitzer

        this._destructible.addDestructor([ 'denizen', options.id ], denizen, 'destroy')
        this.loggers[options.id] = denizen.logger.shifter()
        console.log('JOIN')
        denizen.join(this._port, this._address, options.leader, options.republic, this._destructible.rescue([ 'denizen', options.id ], denizen.ready, 'unlatch'))
        async(function () {
            denizen.ready.wait(async())
        }, function () {
            this.events[options.id] = denizen.shifter
        })
    })

    Counterfeiter.prototype.leave = function (id) {
        interrupt.assert(!this.destroyed, 'destroyed', {}, this._destructible.errors[0])
        this._destructible.invokeDestructor([ 'colleague', id ])
    }

    Counterfeiter.prototype.destroy = function () {
        this._destructible.destroy()
    }

    Counterfeiter.prototype.listen = cadence(function (async, port, address) {
        this._address = address
        this._port = port
        var ready = new Signal
        var conduit = new Conduit
        this._destructible.addDestructor('conduit', conduit, 'destroy')
        conduit.ready.wait(ready, 'unlatch')
        conduit.listen(port, address, this._destructible.monitor('conduit', ready, 'unlatch'))
        ready.wait(async())
    })

    Counterfeiter.prototype.completed = function (callback) {
        this._destructible.completed(5000, callback)
    }

    return Counterfeiter
}
<commit_msg>Remove unused function.<commit_after>module.exports = function (Colleague, Conduit) {
    var cadence = require('cadence')
    var abend = require('abend')
    var Destructor = require('destructible')
    var Colleague = require('../compassion.colleague/colleague')
    var Kibitzer = require('kibitz')
    var Conduit = require('../compassion.conduit/conduit')
    var Vizsla = require('vizsla')
    var Signal = require('signal')
    var Procession = require('procession')
    var Recorder = require('./recorder')

    var interrupt = require('interrupt').createInterrupter('compassion.counterfeiter')

    function Counterfeiter (address, port) {
        this._colleagues = {}
        // TODO Getting silly, just expose Denizen.
        this.events = {}
        this.kibitzers = {}
        this.loggers = {}
        this._port = port
        this._address = address
        this._destructible = new Destructor('counterfeiter')
        this._destructible.markDestroyed(this, 'destroyed')
    }

    Counterfeiter.prototype._run = cadence(function (async, options) {
        interrupt.assert(!this.destroyed, 'destroyed', {}, this._destructible.errors[0])
        var kibitzer = this.kibitzer = new Kibitzer({ id: options.id, ping: 1000, timeout: 3000 })

        var colleague = new Colleague(new Vizsla, kibitzer, null, 'island')
        this._colleagues[options.id] = colleague
        this.kibitzers[options.id] = colleague.kibitzer
        this.events[options.id] = colleague.kibitzer.log.shifter()
        this._destructible.addDestructor([ 'colleague', options.id ], colleague, 'destroy')

        // Create loggers suitable for replaying.
        var logger = new Procession

        kibitzer.played.shifter().pump(new Recorder('kibitz', logger), 'push')
        kibitzer.paxos.outbox.shifter().pump(new Recorder('paxos', logger), 'push')
        kibitzer.islander.outbox.shifter().pump(new Recorder('islander', logger), 'push')
        colleague.chatter.shifter().pump(new Recorder('colleague', logger), 'push')

        this._destructible.addDestructor([ 'logger', options.id ], logger, 'push')

        this.loggers[options.id] = logger.shifter()

        // Connect colleague directly to conference.
        colleague.read.shifter().pump(options.conference.write, 'enqueue')
        options.conference.read.shifter().pump(colleague.write, 'enqueue')

        // Start colleague.
        var ready = new Signal

        ready.wait(function () {
            console.log('wait wait', arguments)
        })

        colleague.ready.wait(ready, 'unlatch')
        colleague.listen(this._address, this._port, this._destructible.rescue([ 'colleague', options.id ], ready, 'unlatch'))
        ready.wait(async())
    })

    Counterfeiter.prototype.bootstrap = cadence(function (async, options) {
        async(function () {
            this._run(options, async())
        }, function (colleague) {
            this._colleagues[options.id].bootstrap(async())
        }, function () {
            console.log('done')
        })
    })

    Counterfeiter.prototype.join = cadence(function (async, options) {
        async(function () {
            this._run(options, async())
        }, function (colleague) {
            this._colleagues[options.id].join(options.republic, options.leader, async())
        }, function () {
            console.log('done')
        })
    })

    Counterfeiter.prototype.leave = function (id) {
        interrupt.assert(!this.destroyed, 'destroyed', {}, this._destructible.errors[0])
        this._destructible.invokeDestructor([ 'colleague', id ])
    }

    Counterfeiter.prototype.destroy = function () {
        this._destructible.destroy()
    }

    Counterfeiter.prototype.listen = cadence(function (async, port, address) {
        this._address = address
        this._port = port
        var ready = new Signal
        var conduit = new Conduit
        this._destructible.addDestructor('conduit', conduit, 'destroy')
        conduit.ready.wait(ready, 'unlatch')
        conduit.listen(port, address, this._destructible.monitor('conduit', ready, 'unlatch'))
        ready.wait(async())
    })

    Counterfeiter.prototype.completed = function (callback) {
        this._destructible.completed(5000, callback)
    }

    return Counterfeiter
}
<|endoftext|>


<commit_before>/*!
 * @package   ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
 *
 * @version 2.0 dev
 */

/**
 * This file contains javascript associated with the @mentions function as it
 * relates to an sceditor invocation
 */
var disableDrafts = false;

(function (sceditor)
{
	'use strict';

	// Editor instance
	var editor;

	function Elk_Mentions(options)
	{
		// All the passed options and defaults are loaded to the opts object
		this.opts = $.extend({}, this.defaults, options);
	}

	Elk_Mentions.prototype.attachAtWho = function (oMentions, $element, oIframeWindow)
	{
		let mentioned = $('#mentioned'),
			corrected_offset = {};

		// Create / use a container to hold the results
		if (mentioned.length === 0)
		{
			$('#' + oMentions.opts.editor_id).after(oMentions.opts._mentioned);
		}
		else
		{
			oMentions.opts._mentioned = mentioned;
		}

		oMentions.opts.cache.mentions = this.opts._mentioned;

		$element.atwho({
			at: "@",
			limit: 8,
			maxLen: 25,
			displayTpl: "
${name}
",
			acceptSpaceBar: true,
			callbacks: {
				filter: function (query, items, search_key)
				{
					// Already cached this query, then use it
					if (typeof oMentions.opts.cache.names[query] !== 'undefined')
					{
						return oMentions.opts.cache.names[query];
					}

					return items;
				},
				// Well then lets make a find member suggest call
				remoteFilter: function (query, callback)
				{
					// Let be easy-ish on the server, don't go looking until we have at least two characters
					if (typeof query === 'undefined' || query.length < 2 || query.length > 25)
					{
						return;
					}

					// No slamming the server either
					let current_call = Math.round(new Date().getTime());
					if (oMentions.opts._last_call !== 0 && oMentions.opts._last_call + 150 > current_call)
					{
						callback(oMentions.opts._names);
						return;
					}

					// What we want
					var obj = {
						"suggest_type": "member",
						"search": query.php_urlencode(),
						"time": current_call
					};

					// Make the request
					suggest(obj, function ()
					{
						// Update the time gate
						oMentions.opts._last_call = current_call;

						// Update the cache with the values for reuse in local filter
						oMentions.opts.cache.names[query] = oMentions.opts._names;

						// Update the query cache for use in revalidateMentions
						oMentions.opts.cache.queries[oMentions.opts.cache.queries.length] = query;

						callback(oMentions.opts._names);
					});
				},
				beforeInsert: function (value, $li)
				{
					// Set up for a new pull-down box/location
					corrected_offset = {};

					oMentions.addUID($li.data('id'), $li.data('value'));

					return value;
				},
				matcher: function (flag, subtext, should_startWithSpace, acceptSpaceBar)
				{
					let match, space, regex_matcher;

					if (!subtext || subtext.length < 3)
						return null;

					if (should_startWithSpace)
					{
						flag = '(?:^|\\s)' + flag;
					}

					// Allow first last name entry?
					space = acceptSpaceBar ? "\ " : "";

					// regexp = new RegExp(flag + '([^ <>&"\'=\\\\\n]*)$|' + flag + '([^\\x00-\\xff]*)$', 'gi');
					regex_matcher = new RegExp(flag + "([\\p{L}0-9_" + space + "\\[\\]\'\.\+\-]*)$", 'u');
					match = regex_matcher.exec(subtext);

					if (match)
					{
						return match[1];
					}
					else
					{
						return null;
					}
				},
				highlighter: function (li, query)
				{
					let regex_highlight;

					if (!query)
					{
						return li;
					}

					// Preg Quote regexp from http://phpjs.org/functions/preg_quote/
					query = query.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&');

					regex_highlight = new RegExp(">\\s*(\\w*)(" + query.replace("+", "\\+") + ")(\\w*)\\s*<", 'ig');
					return li.replace(regex_highlight, function (str, $1, $2, $3)
					{
						return '> ' + $1 + '' + $2 + '' + $3 + ' <';
					});
				},
				beforeReposition: function (offset)
				{
					// We only need to adjust when in wysiwyg
					if (editor.inSourceMode())
					{
						return offset;
					}

					if (Object.keys(corrected_offset).length === 0)
					{
						// Get the caret position, so we can add the mentions box there
						corrected_offset = editor.findCursorPosition('@');
					}

					offset.top = corrected_offset.top;
					offset.left = corrected_offset.left;

					return offset;
				},
				afterMatchFailed: function (at, el)
				{
					// Clear the offset
					corrected_offset = {};
				}
			}
		});

		// Use atwho selection box show/hide events to prevent autosave from firing
		if (Object.keys(oIframeWindow).length)
		{
			$(oIframeWindow).on("shown.atwho", function (event, offset)
			{
				disableDrafts = true;
			});

			$(oIframeWindow).on("hidden.atwho", function (event, offset)
			{
				disableDrafts = false;
			});
		}

		/**
		 * Makes the ajax call for data, returns to callback function when done.
		 *
		 * @param obj values to pass to action suggest
		 * @param callback function to call when we have completed our call
		 */
		function suggest(obj, callback)
		{
			let postString = "jsonString=" + JSON.stringify(obj) + "&" + elk_session_var + "=" + elk_session_id;

			oMentions.opts._names = [];

			$.ajax({
				url: elk_scripturl + "?action=suggest;api=xml",
				type: "post",
				data: postString,
				dataType: "xml"
			})
				.done(function (data)
				{
					$(data).find('item').each(function (idx, item)
					{
						oMentions.opts._names[idx] = {
							"id": $(item).attr('id'),
							"name": $(item).text()
						};
					});

					callback();
				})
				.fail(function (jqXHR, textStatus, errorThrown)
				{
					if ('console' in window)
					{
						window.console.info('Error:', textStatus, errorThrown.name);
						window.console.info(jqXHR.responseText);
					}

					callback();
				});
		}
	};

	Elk_Mentions.prototype.addUID = function (user_id, name)
	{
		this.opts._mentioned.append($('<input type="hidden" name="uid[]" />').val(user_id).attr('data-name', name));
	};

	/**
	 * Private mention vars
	 */
	Elk_Mentions.prototype.defaults = {
		_names: [],
		_last_call: 0,
		_mentioned: $('
')
	};

	/**
	 * Holds all current mention (defaults + passed options)
	 */
	Elk_Mentions.prototype.opts = {};

	/**
	 * Mentioning plugin interface to SCEditor
	 *  - Called from the editor as a plugin
	 *  - Monitors events so we control the elk_mention
	 */
	sceditor.plugins.mention = function ()
	{
		var base = this,
			oMentions;

		base.init = function ()
		{
			// Grab this instance for use use in oMentions
			editor = this;
		};

		/**
		 * Initialize, called when sceditor starts and initializes plugins
		 */
		base.signalReady = function ()
		{
			// Init the mention instance, load in the options
			oMentions = new Elk_Mentions(this.opts.mentionOptions);

			let original_textarea = document.getElementById(oMentions.opts.editor_id),
				instance = sceditor.instance(original_textarea),
				sceditor_textarea = instance.getContentAreaContainer().nextSibling;

			// Adds the selector to the list of known "mentioner"
			add_elk_mention(oMentions.opts.editor_id, {isPlugin: true});
			oMentions.attachAtWho(oMentions, $(sceditor_textarea), {});

			// Using wysiwyg, then lets attach atwho to it
			if (!instance.opts.runWithoutWysiwygSupport)
			{
				// We need to monitor the iframe window and body to text input
				let oIframe = instance.getContentAreaContainer(),
					oIframeWindow = oIframe.contentWindow,
					oIframeBody = oIframe.contentDocument.body;

				oMentions.attachAtWho(oMentions, $(oIframeBody), oIframeWindow);
			}
		};
	};
})(sceditor);
<commit_msg>! that should have m as a modifier<commit_after>/*!
 * @package   ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
 *
 * @version 2.0 dev
 */

/**
 * This file contains javascript associated with the @mentions function as it
 * relates to an sceditor invocation
 */
var disableDrafts = false;

(function (sceditor)
{
	'use strict';

	// Editor instance
	var editor;

	function Elk_Mentions(options)
	{
		// All the passed options and defaults are loaded to the opts object
		this.opts = $.extend({}, this.defaults, options);
	}

	Elk_Mentions.prototype.attachAtWho = function (oMentions, $element, oIframeWindow)
	{
		let mentioned = $('#mentioned'),
			corrected_offset = {};

		// Create / use a container to hold the results
		if (mentioned.length === 0)
		{
			$('#' + oMentions.opts.editor_id).after(oMentions.opts._mentioned);
		}
		else
		{
			oMentions.opts._mentioned = mentioned;
		}

		oMentions.opts.cache.mentions = this.opts._mentioned;

		$element.atwho({
			at: "@",
			limit: 8,
			maxLen: 25,
			displayTpl: "
${name}
",
			acceptSpaceBar: true,
			callbacks: {
				filter: function (query, items, search_key)
				{
					// Already cached this query, then use it
					if (typeof oMentions.opts.cache.names[query] !== 'undefined')
					{
						return oMentions.opts.cache.names[query];
					}

					return items;
				},
				// Well then lets make a find member suggest call
				remoteFilter: function (query, callback)
				{
					// Let be easy-ish on the server, don't go looking until we have at least two characters
					if (typeof query === 'undefined' || query.length < 2 || query.length > 25)
					{
						return;
					}

					// No slamming the server either
					let current_call = Math.round(new Date().getTime());
					if (oMentions.opts._last_call !== 0 && oMentions.opts._last_call + 150 > current_call)
					{
						callback(oMentions.opts._names);
						return;
					}

					// What we want
					var obj = {
						"suggest_type": "member",
						"search": query.php_urlencode(),
						"time": current_call
					};

					// Make the request
					suggest(obj, function ()
					{
						// Update the time gate
						oMentions.opts._last_call = current_call;

						// Update the cache with the values for reuse in local filter
						oMentions.opts.cache.names[query] = oMentions.opts._names;

						// Update the query cache for use in revalidateMentions
						oMentions.opts.cache.queries[oMentions.opts.cache.queries.length] = query;

						callback(oMentions.opts._names);
					});
				},
				beforeInsert: function (value, $li)
				{
					// Set up for a new pull-down box/location
					corrected_offset = {};

					oMentions.addUID($li.data('id'), $li.data('value'));

					return value;
				},
				matcher: function (flag, subtext, should_startWithSpace, acceptSpaceBar)
				{
					let match, space, regex_matcher;

					if (!subtext || subtext.length < 3)
						return null;

					if (should_startWithSpace)
					{
						flag = '(?:^|\\s)' + flag;
					}

					// Allow first last name entry?
					space = acceptSpaceBar ? "\ " : "";

					// regexp = new RegExp(flag + '([^ <>&"\'=\\\\\n]*)$|' + flag + '([^\\x00-\\xff]*)$', 'gi');
					regex_matcher = new RegExp(flag + "([\\p{L}0-9_" + space + "\\[\\]\'\.\+\-]*)$", 'um');
					match = regex_matcher.exec(subtext);

					if (match)
					{
						return match[1];
					}
					else
					{
						return null;
					}
				},
				highlighter: function (li, query)
				{
					let regex_highlight;

					if (!query)
					{
						return li;
					}

					// Preg Quote regexp from http://phpjs.org/functions/preg_quote/
					query = query.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&');

					regex_highlight = new RegExp(">\\s*(\\w*)(" + query.replace("+", "\\+") + ")(\\w*)\\s*<", 'ig');
					return li.replace(regex_highlight, function (str, $1, $2, $3)
					{
						return '> ' + $1 + '' + $2 + '' + $3 + ' <';
					});
				},
				beforeReposition: function (offset)
				{
					// We only need to adjust when in wysiwyg
					if (editor.inSourceMode())
					{
						return offset;
					}

					if (Object.keys(corrected_offset).length === 0)
					{
						// Get the caret position, so we can add the mentions box there
						corrected_offset = editor.findCursorPosition('@');
					}

					offset.top = corrected_offset.top;
					offset.left = corrected_offset.left;

					return offset;
				},
				afterMatchFailed: function (at, el)
				{
					// Clear the offset
					corrected_offset = {};
				}
			}
		});

		// Use atwho selection box show/hide events to prevent autosave from firing
		if (Object.keys(oIframeWindow).length)
		{
			$(oIframeWindow).on("shown.atwho", function (event, offset)
			{
				disableDrafts = true;
			});

			$(oIframeWindow).on("hidden.atwho", function (event, offset)
			{
				disableDrafts = false;
			});
		}

		/**
		 * Makes the ajax call for data, returns to callback function when done.
		 *
		 * @param obj values to pass to action suggest
		 * @param callback function to call when we have completed our call
		 */
		function suggest(obj, callback)
		{
			let postString = "jsonString=" + JSON.stringify(obj) + "&" + elk_session_var + "=" + elk_session_id;

			oMentions.opts._names = [];

			$.ajax({
				url: elk_scripturl + "?action=suggest;api=xml",
				type: "post",
				data: postString,
				dataType: "xml"
			})
				.done(function (data)
				{
					$(data).find('item').each(function (idx, item)
					{
						oMentions.opts._names[idx] = {
							"id": $(item).attr('id'),
							"name": $(item).text()
						};
					});

					callback();
				})
				.fail(function (jqXHR, textStatus, errorThrown)
				{
					if ('console' in window)
					{
						window.console.info('Error:', textStatus, errorThrown.name);
						window.console.info(jqXHR.responseText);
					}

					callback();
				});
		}
	};

	Elk_Mentions.prototype.addUID = function (user_id, name)
	{
		this.opts._mentioned.append($('<input type="hidden" name="uid[]" />').val(user_id).attr('data-name', name));
	};

	/**
	 * Private mention vars
	 */
	Elk_Mentions.prototype.defaults = {
		_names: [],
		_last_call: 0,
		_mentioned: $('
')
	};

	/**
	 * Holds all current mention (defaults + passed options)
	 */
	Elk_Mentions.prototype.opts = {};

	/**
	 * Mentioning plugin interface to SCEditor
	 *  - Called from the editor as a plugin
	 *  - Monitors events so we control the elk_mention
	 */
	sceditor.plugins.mention = function ()
	{
		var base = this,
			oMentions;

		base.init = function ()
		{
			// Grab this instance for use use in oMentions
			editor = this;
		};

		/**
		 * Initialize, called when sceditor starts and initializes plugins
		 */
		base.signalReady = function ()
		{
			// Init the mention instance, load in the options
			oMentions = new Elk_Mentions(this.opts.mentionOptions);

			let original_textarea = document.getElementById(oMentions.opts.editor_id),
				instance = sceditor.instance(original_textarea),
				sceditor_textarea = instance.getContentAreaContainer().nextSibling;

			// Adds the selector to the list of known "mentioner"
			add_elk_mention(oMentions.opts.editor_id, {isPlugin: true});
			oMentions.attachAtWho(oMentions, $(sceditor_textarea), {});

			// Using wysiwyg, then lets attach atwho to it
			if (!instance.opts.runWithoutWysiwygSupport)
			{
				// We need to monitor the iframe window and body to text input
				let oIframe = instance.getContentAreaContainer(),
					oIframeWindow = oIframe.contentWindow,
					oIframeBody = oIframe.contentDocument.body;

				oMentions.attachAtWho(oMentions, $(oIframeBody), oIframeWindow);
			}
		};
	};
})(sceditor);
<|endoftext|>


<commit_before>define(['../string/typecast', '../lang/isString', '../lang/isArray', '../object/hasOwn'], function (typecast, isString, isArray, hasOwn) {

    /**
     * Decode query string into an object of keys => vals.
     */
    function decode(queryStr, shouldTypecast) {
        var queryArr = (queryStr || '').replace('?', '').split('&'),
            count = -1,
            length = queryArr.length,
            obj = {},
            item, pValue, pName, toSet;

        while (++count<length) {
            item = queryArr[count].split('=');
            pName = item[0];
            if (!pName || !pName.length){
                continue;
            }
            pValue = shouldTypecast === false ? item[1] : typecast(item[1]);
            toSet = isString(pValue)? decodeURIComponent(pValue) : pValue;
            if (hasOwn(obj,pName)){
                if(isArray(obj[pName])){
                    obj[pName].push(toSet);
                } else {
                    obj[pName] = [obj[pName],toSet];
                }
            } else {
                obj[pName] = toSet;
           }
        }
        return obj;
    }

    return decode;
});
<commit_msg>jslint friendly<commit_after>define(['../string/typecast', '../lang/isString', '../lang/isArray', '../object/hasOwn'], function (typecast, isString, isArray, hasOwn) {

    /**
     * Decode query string into an object of keys => vals.
     */
    function decode(queryStr, shouldTypecast) {
        var queryArr = (queryStr || '').replace('?', '').split('&'),
            count = -1,
            length = queryArr.length,
            obj = {},
            item, pValue, pName, toSet;

        while (++count < length) {
            item = queryArr[count].split('=');
            pName = item[0];
            if (!pName || !pName.length){
                continue;
            }
            pValue = shouldTypecast === false ? item[1] : typecast(item[1]);
            toSet = isString(pValue) ? decodeURIComponent(pValue) : pValue;
            if (hasOwn(obj,pName)){
                if(isArray(obj[pName])){
                    obj[pName].push(toSet);
                } else {
                    obj[pName] = [obj[pName],toSet];
                }
            } else {
                obj[pName] = toSet;
           }
        }
        return obj;
    }

    return decode;
});
<|endoftext|>


<commit_before>const _ = require('lodash');
const fetch = require('../helpers/fetch');

const UPDATE_FRAME = Symbol('sconce/notebooks/UPDATE_FRAME');
const ADD_NOTEBOOK = Symbol('sconce/notebooks/ADD_NOTEBOOK');
const REMOVE_NOTEBOOK = Symbol('sconce/notebooks/REMOVE_NOTEBOOK');

const initialState = [];

// The reducer function takes the current state and an action, and returns
// the new state after applying the action.
function reducer(state, action) {
  state = state || initialState;
  action = action || {};

  switch(action.type) {
    case UPDATE_FRAME: {
      const notebookIndex = _.findIndex(state, { id: action.frame.notebookId });

      // Ignore update if the frame's notebook hasn't been loaded
      if(!state[notebookIndex].frames) {
        return state;
      }

      let updatedFrame = false;
      const frames = state[notebookIndex].frames.map((frame) => {
        if(frame.id === action.frame.id) {
          updatedFrame = true;
          return _.assign({}, frame, action.frame);
        }
        return frame;
      });
      if(!updatedFrame) {
        frames.push(action.frame);
      }
      const newState = _.clone(state);
      newState[notebookIndex] = _.assign({}, state[notebookIndex], { frames });
      return newState;
    }

    case ADD_NOTEBOOK: {
      const notebookIndex = _.findIndex(state, { id: action.notebook.id });

      let newState;
      if(notebookIndex < 0) {
        newState = state.concat(action.notebook);
      } else {
        newState = _.clone(state);
        newState[notebookIndex] = _.assign({}, newState[notebookIndex], action.notebook);
      }

      return newState;
    }

    case REMOVE_NOTEBOOK: {
      return _.reject(state, { id: action.notebookId });
    }

    default: return state;
  }
}

reducer.updateFrame = (frame) =>
  ({ type: UPDATE_FRAME, frame });

reducer.addNotebook = (notebook) =>
  ({ type: ADD_NOTEBOOK, notebook });

reducer.removeNotebook = (notebookId) =>
  ({ type: REMOVE_NOTEBOOK, notebookId });

reducer.updateNotebook = (notebook) => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch(`/api/notebook/${notebook.id}`, {
      method: 'put',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ notebook })
    }).then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        dispatch(reducer.addNotebook(data.notebook));
        resolve(data.notebook);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to update notebook');
      reject(err);
    });
  });

reducer.loadNotebook = (notebookId) => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch(`/api/notebook/${notebookId}`).then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        dispatch(reducer.addNotebook(data.notebook));
        resolve(data.notebook);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to load notebook');
      reject(err);
    });
  });

reducer.loadNotebooksShallow = () => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch('/api/notebooks').then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        data.notebooks.forEach((notebook) => {
          dispatch(reducer.addNotebook(notebook));
        });
        resolve(data.notebooks);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to load notebook');
      reject(err);
    });
  });

reducer.deleteNotebook = (notebookId) => (dispatch) =>
  fetch(`/api/notebook/${notebookId}`, { method: 'delete' }).then((res) => {
    if(!res.ok) throw new Error(res.statusText);
    dispatch(reducer.removeNotebook(notebookId));
  }).catch((err) => {
    console.error(err);
    alert('Failed to delete notebook');
  });

module.exports = reducer;
<commit_msg>Properly fix frame updates to unloaded notebooks<commit_after>const _ = require('lodash');
const fetch = require('../helpers/fetch');

const UPDATE_FRAME = Symbol('sconce/notebooks/UPDATE_FRAME');
const ADD_NOTEBOOK = Symbol('sconce/notebooks/ADD_NOTEBOOK');
const REMOVE_NOTEBOOK = Symbol('sconce/notebooks/REMOVE_NOTEBOOK');

const initialState = [];

// The reducer function takes the current state and an action, and returns
// the new state after applying the action.
function reducer(state, action) {
  state = state || initialState;
  action = action || {};

  switch(action.type) {
    case UPDATE_FRAME: {
      const notebookIndex = _.findIndex(state, { id: action.frame.notebookId });

      // Ignore update if the frame's notebook hasn't been loaded
      if(!state[notebookIndex] || !state[notebookIndex].frames) {
        return state;
      }

      let updatedFrame = false;
      const frames = state[notebookIndex].frames.map((frame) => {
        if(frame.id === action.frame.id) {
          updatedFrame = true;
          return _.assign({}, frame, action.frame);
        }
        return frame;
      });
      if(!updatedFrame) {
        frames.push(action.frame);
      }
      const newState = _.clone(state);
      newState[notebookIndex] = _.assign({}, state[notebookIndex], { frames });
      return newState;
    }

    case ADD_NOTEBOOK: {
      const notebookIndex = _.findIndex(state, { id: action.notebook.id });

      let newState;
      if(notebookIndex < 0) {
        newState = state.concat(action.notebook);
      } else {
        newState = _.clone(state);
        newState[notebookIndex] = _.assign({}, newState[notebookIndex], action.notebook);
      }

      return newState;
    }

    case REMOVE_NOTEBOOK: {
      return _.reject(state, { id: action.notebookId });
    }

    default: return state;
  }
}

reducer.updateFrame = (frame) =>
  ({ type: UPDATE_FRAME, frame });

reducer.addNotebook = (notebook) =>
  ({ type: ADD_NOTEBOOK, notebook });

reducer.removeNotebook = (notebookId) =>
  ({ type: REMOVE_NOTEBOOK, notebookId });

reducer.updateNotebook = (notebook) => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch(`/api/notebook/${notebook.id}`, {
      method: 'put',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ notebook })
    }).then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        dispatch(reducer.addNotebook(data.notebook));
        resolve(data.notebook);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to update notebook');
      reject(err);
    });
  });

reducer.loadNotebook = (notebookId) => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch(`/api/notebook/${notebookId}`).then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        dispatch(reducer.addNotebook(data.notebook));
        resolve(data.notebook);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to load notebook');
      reject(err);
    });
  });

reducer.loadNotebooksShallow = () => (dispatch) =>
  new Promise((resolve, reject) => {
    fetch('/api/notebooks').then((res) => {
      if(!res.ok) throw new Error(res.statusText);
      res.json().then((data) => {
        data.notebooks.forEach((notebook) => {
          dispatch(reducer.addNotebook(notebook));
        });
        resolve(data.notebooks);
      }).catch(reject);
    }).catch((err) => {
      console.error(err);
      alert('Failed to load notebook');
      reject(err);
    });
  });

reducer.deleteNotebook = (notebookId) => (dispatch) =>
  fetch(`/api/notebook/${notebookId}`, { method: 'delete' }).then((res) => {
    if(!res.ok) throw new Error(res.statusText);
    dispatch(reducer.removeNotebook(notebookId));
  }).catch((err) => {
    console.error(err);
    alert('Failed to delete notebook');
  });

module.exports = reducer;
<|endoftext|>


<commit_before>// @flow

import React from 'react'
import type {CourseType} from '../../../../lib/course-search'
import glamorous from 'glamorous-native'
import {Badge} from '../../../building-hours/detail/badge'
import {TableView, Section, Cell} from 'react-native-tableview-simple'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'
import moment from 'moment-timezone'
import {formatDay} from '../lib/format-day'
import {MultiLineDetailCell} from '../../components/multi-line-cell'
import type {TopLevelViewPropsType} from '../../../types'

const Container = glamorous.scrollView({
	paddingVertical: 6,
})

const Header = glamorous.text({
	fontSize: 36,
	textAlign: 'center',
	marginTop: 20,
	marginHorizontal: 10,
})

const SubHeader = glamorous.text({
	fontSize: 21,
	textAlign: 'center',
	marginTop: 5,
})

const CellText = glamorous.text({
	padding: 5,
})

function Information({course}: {course: CourseType}) {
	const profs = course.instructors ? course.instructors.join(', ') : null
	const instructors = course.instructors ? (
		<Cell cellStyle="LeftDetail" detail="Instructor(s)" title={profs} />
	) : null
	const gereqs = course.gereqs ? course.gereqs.join(', ') : null
	const ges = course.gereqs ? (
		<Cell cellStyle="LeftDetail" detail="GEs" title={gereqs} />
	) : null
	const credits = course.credits ? (
		<Cell cellStyle="LeftDetail" detail="Credits" title={course.credits} />
	) : null
	const type = <Cell cellStyle="LeftDetail" detail="Type" title={course.type} />
	const passFail = course.pn ? 'Yes' : 'No'
	const pn = <Cell cellStyle="LeftDetail" detail="Pass/Fail" title={passFail} />
	const prerequisites = course.prerequisites
		? course.prerequisites.replace('Prerequisite: ', '')
		: 'None'

	const prereqs = (
		<Cell cellStyle="LeftDetail" detail="Prerequisites" title={prerequisites} />
	)
	return (

			{instructors}
			{type}
			{ges}
			{pn}
			{prereqs}
			{credits}

	)
}

function Schedule({course}: {course: CourseType}) {
	if (!course.times) {
		return null
	}
	const times = convertTimeStringsToOfferings({times: course.times})
	const schedule = times.map(time => {
		const hours = time.times.map(time => {
			const start = moment(time.start, 'hmm').format('h:mm A')
			const end = moment(time.end, 'hmm').format('h:mm A')
			return `${start} - ${end}`
		})
		const allHours = hours.join('\n')
		const day = formatDay(time.day)
		return <MultiLineDetailCell key={time.day} detail={allHours} title={day} />
	})

	return
{schedule}

}

function Description({course}: {course: CourseType}) {
	return course.description ? (

			<Cell cellContentView={<CellText>{course.description[0]}</CellText>} />

	) : null
}

type Props = TopLevelViewPropsType & {
	navigation: {state: {params: {course: CourseType}}},
}

export class CourseDetailView extends React.PureComponent<Props> {
	static navigationOptions = ({navigation}: any) => {
		const course = navigation.state.params.course
		return {
			title: course.name,
		}
	}

	render() {
		const course = this.props.navigation.state.params.course
		const status = course.status === 'O' ? 'Open' : 'Closed'
		return (
			<Container>
				{course.name}
				<SubHeader>
					{course.departments[0]} {course.number}
					{course.section}
				</SubHeader>
				<Badge status={status} />
				<TableView>
					<Information course={course} />
					<Schedule course={course} />
					<Description course={course} />
				</TableView>
			</Container>
		)
	}
}
<commit_msg>removed .replace() from prereqs<commit_after>// @flow

import React from 'react'
import type {CourseType} from '../../../../lib/course-search'
import glamorous from 'glamorous-native'
import {Badge} from '../../../building-hours/detail/badge'
import {TableView, Section, Cell} from 'react-native-tableview-simple'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'
import moment from 'moment-timezone'
import {formatDay} from '../lib/format-day'
import {MultiLineDetailCell} from '../../components/multi-line-cell'
import type {TopLevelViewPropsType} from '../../../types'

const Container = glamorous.scrollView({
	paddingVertical: 6,
})

const Header = glamorous.text({
	fontSize: 36,
	textAlign: 'center',
	marginTop: 20,
	marginHorizontal: 10,
})

const SubHeader = glamorous.text({
	fontSize: 21,
	textAlign: 'center',
	marginTop: 5,
})

const CellText = glamorous.text({
	padding: 5,
})

function Information({course}: {course: CourseType}) {
	const profs = course.instructors ? course.instructors.join(', ') : null
	const instructors = course.instructors ? (
		<Cell cellStyle="LeftDetail" detail="Instructor(s)" title={profs} />
	) : null
	const gereqs = course.gereqs ? course.gereqs.join(', ') : null
	const ges = course.gereqs ? (
		<Cell cellStyle="LeftDetail" detail="GEs" title={gereqs} />
	) : null
	const credits = course.credits ? (
		<Cell cellStyle="LeftDetail" detail="Credits" title={course.credits} />
	) : null
	const type = <Cell cellStyle="LeftDetail" detail="Type" title={course.type} />
	const passFail = course.pn ? 'Yes' : 'No'
	const pn = <Cell cellStyle="LeftDetail" detail="Pass/Fail" title={passFail} />
	const prerequisites = course.prerequisites
		? course.prerequisites
		: 'None'

	const prereqs = (
		<Cell cellStyle="LeftDetail" detail="Prerequisites" title={prerequisites} />
	)
	return (

			{instructors}
			{type}
			{ges}
			{pn}
			{prereqs}
			{credits}

	)
}

function Schedule({course}: {course: CourseType}) {
	if (!course.times) {
		return null
	}
	const times = convertTimeStringsToOfferings({times: course.times})
	const schedule = times.map(time => {
		const hours = time.times.map(time => {
			const start = moment(time.start, 'hmm').format('h:mm A')
			const end = moment(time.end, 'hmm').format('h:mm A')
			return `${start} - ${end}`
		})
		const allHours = hours.join('\n')
		const day = formatDay(time.day)
		return <MultiLineDetailCell key={time.day} detail={allHours} title={day} />
	})

	return
{schedule}

}

function Description({course}: {course: CourseType}) {
	return course.description ? (

			<Cell cellContentView={<CellText>{course.description[0]}</CellText>} />

	) : null
}

type Props = TopLevelViewPropsType & {
	navigation: {state: {params: {course: CourseType}}},
}

export class CourseDetailView extends React.PureComponent<Props> {
	static navigationOptions = ({navigation}: any) => {
		const course = navigation.state.params.course
		return {
			title: course.name,
		}
	}

	render() {
		const course = this.props.navigation.state.params.course
		const status = course.status === 'O' ? 'Open' : 'Closed'
		return (
			<Container>
				{course.name}
				<SubHeader>
					{course.departments[0]} {course.number}
					{course.section}
				</SubHeader>
				<Badge status={status} />
				<TableView>
					<Information course={course} />
					<Schedule course={course} />
					<Description course={course} />
				</TableView>
			</Container>
		)
	}
}
<|endoftext|>


<commit_before>export const getComicManager = (state) => state.comics.comicManager;
export const getChapters = (state) => state.comics.chapters;

export const getNextChapterIndex = (chapters, cid) => {
	var index = chapters.findIndex(item => cid == item.cid);
	if (index != 0 && index-1 > -1) {
		return index - 1;
	} else {
		return -1;
	}
};

export const getPreviousChapterIndex = (chapters, cid) => {
	var index = chapters.findIndex(item => cid == item.cid);
	if (index != -1 && index+1 < chapters.length-1) {
		return index + 1;
	} else {
		return -1;
	}
};

export const getSearchState = (state) => state.searchState;
<commit_msg>fix switchPrevious selector<commit_after>export const getComicManager = (state) => state.comics.comicManager;
export const getChapters = (state) => state.comics.chapters;

export const getNextChapterIndex = (chapters, cid) => {
	var index = chapters.findIndex(item => cid == item.cid);
	if (index != 0 && index-1 > -1) {
		return index - 1;
	} else {
		return -1;
	}
};

export const getPreviousChapterIndex = (chapters, cid) => {
	var index = chapters.findIndex(item => cid == item.cid);
	if (index != -1 && index+1 < chapters.length) {
		return index + 1;
	} else {
		return -1;
	}
};

export const getSearchState = (state) => state.searchState;
<|endoftext|>