<commit_before>/**
 * Created by Tomer on 8/30/2016.
 */
$(document).ready(function(){
    $("#createEvent").hide();
    $("#userProfile").hide();
    $("#eventManagerYourEvents").hide();
    $("#addParticipants").hide();
    $("#eventManagerInvitedEvents").hide();
    $("#eventInfo").hide();
    $("#addItemsToEvent").hide();
    $("#addParticipantsToEvent").hide();
    $("#sideTile").hide();
    $("#successContainer").hide();

});
var eventID;

$("#createEventButton").click(function() {
	$("#overviewContent").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#createEvent").fadeIn(600);
	$("#addParticipants").fadeIn(600);
});
$("#overviewContentButton").click(function() {
	$("#createEvent").hide();
	$("#addParticipants").hide();
	$("#eventManager").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#overviewContent").fadeIn(600);

});

$("#eventManagerButton").click(function() {
	$("#overviewContent").hide();
	$("#addParticipants").hide();
	$("#createEvent").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").fadeIn(600);
	$("#eventManagerInvitedEvents").fadeIn(600);
	$("#eventInfo").fadeIn(600);

    $.ajax({
        url: '/api/events',
        method: 'get',
        success: function(obj) {
            obj.success = true;
            if (obj.success) {
                var allEvents = obj.msg;
                for (var i = 0; i < allEvents.length; ++i) {
                    showSingleEvent(allEvents[i])
                }
	$.ajax({
		url: '/api/events',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleEvent(allEvents[i])
				}

			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

    $.ajax({
        url: '/api/events/invited',
        method: 'get',
        success: function(obj) {
            obj.success = true;
            if (obj.success) {
                var allEvents = obj.msg;
                for (var i = 0; i < allEvents.length; ++i) {
                    showSingleInvitedEvent(allEvents[i])
                }
	$.ajax({
		url: '/api/events/invited',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleInvitedEvent(allEvents[i])
				}

			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

});

$("#userProfileButton").click(function() {
	$("#overviewContent").hide();
	$("#addParticipants").hide();
	$("#createEvent").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#eventManager").hide();
	$("#userProfile").fadeIn(600);

});

$("#createEventButtonNext").click(function(e) {
	var eventToCreate = {
		name: $('#eventName').val(),
		startDate: $('#eventStartDate').val(),
		numberOfParticipants: 10 // TODO: ADD PARCITIPANTS NUMBERS (?)
	};

	$.ajax({
		url: '/api/events/create',
		method: 'post',
		data: eventToCreate,
		success: function(obj) {
			if (obj.success) {
				eventID = obj.msg;
				$("#createEvent").hide();
				$("#addItemsToEvent").fadeIn(1500);
				// moveToItemsDialog(eventID); // TODO: Open up a function with ajax call to create items (for loop that do ajax call for each item). after it do the same for partic.
			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
});
$("#createEventButtonNextToParticipants").click(function() {
   // var listOfItems = $('#itemsContainer');
   // var numOfItem = list.find('li').length;
    var eventFullAds = "/api/events/" + eventID + "/addItem";

        var listItems = $('#listItems').find('li');

        for (var i = 0; i < listItems.length; ++i) {
            var itemName = $(listItems[i]).find('span')[0].innerHTML;

                $.ajax({
                    url: eventFullAds,
                    method: 'post',
                    data: { name: itemName },
                    success: function(obj) {
                        if (obj.success) {
                            console.log("happy items was added")
                             } else {
                            $('#msgFromServer').text(obj.msg);
                        }
                    },
                    error: function(obj) {
                        $('#msgFromServer').text(obj.msg);
                    }
                });

    };
    $("#addItemsToEvent").hide();
    $("#addParticipantsToEvent").fadeIn(1500);

	var eventFullAds = "/api/events/" + eventID + "/addItem";
	var listItems = $('#listItems').find('li');
	for (var i = 0; i < listItems.length; ++i) {
		var itemName = $(listItems[i]).find('span')[0].innerHTML;
		$.ajax({
			url: eventFullAds,
			method: 'post',
			data: {name: itemName},
			success: function(obj) {
				if (obj.success) {
					console.log("Item added successfully");
				} else {
					$('#msgFromServer').text(obj.msg);
				}
			},
			error: function(obj) {
				$('#msgFromServer').text(obj.msg);
			}
		});
	}
	$("#addItemsToEvent").hide();
	$("#addParticipantsToEvent").fadeIn(1500);
});

$("#createEventButtonNextToFinish").click(function() {

    var eventFullAds = "/api/events/" + eventID + "/addUser";

    var listParticipants = $('#listParticipants').find('li');

    for (var i = 0; i < listParticipants.length; ++i) {
        var pEmail = $(listParticipants[i]).find('span')[0].innerHTML;
        console.log(pEmail);
        $.ajax({
            url: eventFullAds,
            method: 'post',
            data: { email: pEmail },
            success: function(obj) {
                if (obj.success) {
                    eventID = obj.msg;
	var eventFullAds = "/api/events/" + eventID + "/addUser";
	var listParticipants = $('#listParticipants').find('li');
	for (var i = 0; i < listParticipants.length; ++i) {
		var pEmail = $(listParticipants[i]).find('span')[0].innerHTML; //TODO: FIX THE NAME - WITHOUT SPAN
		console.log(pEmail);
		$.ajax({
			url: eventFullAds,
			method: 'post',
			data: {email: pEmail},
			success: function(obj) {
				if (obj.success) {
					eventID = obj.msg;

					// moveToItemsDialog(eventID); // TODO: Open up a function with ajax call to create items (for loop that do ajax call for each item). after it do the same for partic.
				} else {
					$('#msgFromServer').text(obj.msg);
				}
			},
			error: function(obj) {
				$('#msgFromServer').text(obj.msg);
			}
		});
	}
	$("#addParticipantsToEvent").hide();
	$("#successContainer").fadeIn(1500);
});

function moveToItemsDialog(eventID){
    //Open up a function with ajax call to create items (for loop that do ajax call for each item)

    var ul = document.getElementById("listItems");
    var items = ul.getElementsByTagName("li");
    for (var i = 0; i < items.length; ++i) {
        // do something with items[i], which is a <li> element

    }
}

function saveItem() {
	var itemName = $('#itemInfo').val();
	var closeSpan = $('<span />').text('x').addClass('w3-closebtn w3-margin-right w3-large').on('click', function() {
		$(this.parentElement).remove();
	});
	var textSpan = $('<span />').text(itemName).addClass('itemTextSpan');
	var listItem = $('<li />').append(textSpan, closeSpan);
	$('#listItems').append(listItem);
}

function saveParticipant() {
	var participantEmail = $('#participantEmail').val();
	var closeSpan = $('<span />');
	closeSpan.text('x').addClass('w3-closebtn w3-margin-right w3-large').on('click', function() {
		$(this.parentElement).remove();
	});
	var emailSpan = $('<span>').text(participantEmail);
	var listPartici = $('<li />').append(emailSpan, closeSpan);
	$('#listParticipants').append(listPartici);
}

function showSingleEvent(event) {
	var listItem = $('<li />').text(event.name).data(event).click(showEventInfo);
	$('#eventsListManager').append(listItem);
}

function showSingleInvitedEvent(event) {
	var orangeButton = $('<button class="w3-btn w3-orange" />').text("Going");
	var listItem = $('<li />').text(event.name + orangeButton).data(event).click(showEventInfo);
	$('#invitedeventsListManager').append(listItem);
    var spacing = "          ";
    var eventStatusID = event._id;
    var maybeButton = $('<button />').addClass("w3-btn w3-orange").text("Maybe").click(function(){
            updateStatus('Maybe', eventStatusID);
    });
    var goingButton = $('<button />').addClass("w3-btn w3-green").text("Going").click(function(){
        updateStatus('Going', eventStatusID);
    });
    var notGoingButton = $('<button />').addClass("w3-btn w3-red").text("Not Going").click(function(){
        updateStatus('Not Going', eventStatusID);
    });
    var listItem = $('<li />').text(event.name + spacing).append(goingButton, maybeButton, notGoingButton).data(event).click(showEventInfo);
    $('#invitedeventsListManager').append(listItem);
}

function showEventInfo(oEvent) {
	var elem = $(oEvent.target);
	var specificEvent = elem.data();
	var eventIdToBring = elem.data()._id;
	$("#sideTile").fadeIn(500);
	$("#sideName").text(specificEvent.name);
	$("#sideDate").text(specificEvent.startDate);
	$("#sideDateEnd").text(specificEvent.endDate);

function updateStatus(statusArrival, eventStatusID){

    $.ajax({
        url: 'api/events/updateRSVP/' + eventStatusID,
        method: 'put',
        data: { rsvp: statusArrival },
        success: function(obj) {
            if (obj.success) {
                toastr.info('Event Updated Successfully')
            }
        },
        error: function(obj) {
            $('#msgFromServer').text(obj.msg);
        }
    });

}

function initEventInfo() {
    $('#sideName').text(' ');
    $('#sideDate').text(' ');
    $('#sideDateEnd').text(' ');
    $('#sideParticipants').text(' ');
    $('#sideItems').text(' ');
}

function showEventInfo(oEvent){
    var elem = $(oEvent.target);
    initEventInfo();
    var specificEvent = elem.data();
    var eventIdToBring = elem.data()._id;

    $("#sideTile").fadeIn(500);
    $("#sideName").text(specificEvent.name);
    $("#sideDate").text(specificEvent.startDate);
    $("#sideDateEnd").text(specificEvent.endDate);

	var participantsToEvent;
	var getParticipantsAds = "/api/events/" + eventIdToBring + "/getParticipants";
	$.ajax({
		url: getParticipantsAds,
		method: 'get',
		data: {event_id: eventIdToBring},
		success: function(obj) {
			if (obj.success) {
				participantsToEvent = obj.msg;
				for (var i = 0; i < participantsToEvent.length; ++i) {
					var eventInfoParticipant = $('<div />').addClass('participantOnEventInfo').append($('<span />').text(participantsToEvent[i].email));
					$("#sideParticipants").append(eventInfoParticipant);
				}
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

	var itemsOfEvent;
	var getItemsAds = "/api/events/" + eventIdToBring + "/getItems";
	$.ajax({
		url: getItemsAds,
		method: 'get',
		data: {event_id: eventIdToBring},
		success: function(obj) {
			if (obj.success) {
				itemsOfEvent = obj.msg;
				for (var i = 0; i < itemsOfEvent.length; ++i) {
					var eventInfoItem = $('<div />').addClass('itemOnEventInfo').append($('<span />').text(itemsOfEvent[i].name));
					$("#sideItems").append(eventInfoItem);
				}
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

    var itemsOfEvent;
    var getItemsAds = "/api/events/" + eventIdToBring + "/getItems";
    $.ajax({
        url: getItemsAds,
        method: 'get',
        data: { event_id: eventIdToBring },
        success: function(obj) {
            if (obj.success) {
                obj.msg.length > 0 ?  addItemsToEvent(obj.msg) :  $("#sideItems").append($('<span />').text('There are no items'));
            }
        },
        error: function(obj) {
            $('#msgFromServer').text(obj.msg);
        }
    });

}

var addItemsToEvent = function(items){
    items.forEach(function(item){

        var eventItemID = item._id;

        var itemDiv = $('<div />').addClass('itemDiv');
        var itemText = $('<span />').text(item.name).addClass('itemText');
        var itemSwitch = $('<input />').attr('type', 'checkbox').addClass('itemSwitch').click(function(event){
            $.ajax({
                url: 'api/events/updateItem/' + eventItemID,
                method: 'put',
                data: { isChecked: true },
                success: function(obj) {
                    if (obj.success) {

                    }
                },
                error: function(obj) {
                    $('#msgFromServer').text(obj.msg);
                }
            });
        }).prop('disabled', item.isChecked);

        itemDiv.append(itemSwitch, itemText);
        $("#sideItems").append(itemDiv);
    });

};<commit_msg>shilo finishes<commit_after>/**
 * Created by Tomer on 8/30/2016.
 */
$(document).ready(function() {
	$("#createEvent").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").hide();
	$("#addParticipants").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#addItemsToEvent").hide();
	$("#addParticipantsToEvent").hide();
	$("#sideTile").hide();
	$("#successContainer").hide();
});
var eventID;
$("#createEventButton").click(function() {
	$("#overviewContent").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#createEvent").fadeIn(600);
	$("#addParticipants").fadeIn(600);
});
$("#overviewContentButton").click(function() {
	$("#createEvent").hide();
	$("#addParticipants").hide();
	$("#eventManager").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#overviewContent").fadeIn(600);
});

$("#eventManagerButton").click(function() {
	$("#overviewContent").hide();
	$("#addParticipants").hide();
	$("#createEvent").hide();
	$("#userProfile").hide();
	$("#eventManagerYourEvents").fadeIn(600);
	$("#eventManagerInvitedEvents").fadeIn(600);
	$("#eventInfo").fadeIn(600);
	$.ajax({
		url: '/api/events',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleEvent(allEvents[i])
				}
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
	$.ajax({
		url: '/api/events',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleEvent(allEvents[i])
				}
			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
	$.ajax({
		url: '/api/events/invited',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleInvitedEvent(allEvents[i])
				}
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
	$.ajax({
		url: '/api/events/invited',
		method: 'get',
		success: function(obj) {
			obj.success = true;
			if (obj.success) {
				var allEvents = obj.msg;
				for (var i = 0; i < allEvents.length; ++i) {
					showSingleInvitedEvent(allEvents[i])
				}

			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
});

$("#userProfileButton").click(function() {
	$("#overviewContent").hide();
	$("#addParticipants").hide();
	$("#createEvent").hide();
	$("#eventManagerYourEvents").hide();
	$("#eventManagerInvitedEvents").hide();
	$("#eventInfo").hide();
	$("#eventManager").hide();
	$("#userProfile").fadeIn(600);
});

$("#createEventButtonNext").click(function() {
	var eventToCreate = {
		name: $('#eventName').val(),
		startDate: $('#eventStartDate').val(),
		numberOfParticipants: 10 // TODO: ADD PARCITIPANTS NUMBERS (?)
	};
	$.ajax({
		url: '/api/events/create',
		method: 'post',
		data: eventToCreate,
		success: function(obj) {
			if (obj.success) {
				eventID = obj.msg;
				$("#createEvent").hide();
				$("#addItemsToEvent").fadeIn(1500);
				// moveToItemsDialog(eventID); // TODO: Open up a function with ajax call to create items (for loop that do ajax call for each item). after it do the same for partic.
			} else {
				$('#msgFromServer').text(obj.msg);
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
});

$("#createEventButtonNextToParticipants").click(function() {
	var eventFullAds = "/api/events/" + eventID + "/addItem";
	var listItems = $('#listItems').find('li');
	for (var i = 0; i < listItems.length; ++i) {
		var itemName = $(listItems[i]).find('span')[0].innerHTML;
		$.ajax({
			url: eventFullAds,
			method: 'post',
			data: {name: itemName},
			success: function(obj) {
				if (obj.success) {
					console.log("happy items was added")
				} else {
					$('#msgFromServer').text(obj.msg);
				}
			},
			error: function(obj) {
				$('#msgFromServer').text(obj.msg);
			}
		});

	}

	$("#addItemsToEvent").hide();
	$("#addParticipantsToEvent").fadeIn(1500);
});

$("#createEventButtonNextToFinish").click(function() {
	var eventFullAds = "/api/events/" + eventID + "/addUser";
	var listParticipants = $('#listParticipants').find('li');
	for (var i = 0; i < listParticipants.length; ++i) {
		var pEmail = $(listParticipants[i]).find('span')[0].innerHTML; //TODO: FIX THE NAME - WITHOUT SPAN
		console.log(pEmail);
		$.ajax({
			url: eventFullAds,
			method: 'post',
			data: {email: pEmail},
			success: function(obj) {
				if (obj.success) {
					eventID = obj.msg;

					// moveToItemsDialog(eventID); // TODO: Open up a function with ajax call to create items (for loop that do ajax call for each item). after it do the same for partic.
				} else {
					$('#msgFromServer').text(obj.msg);
				}
			},
			error: function(obj) {
				$('#msgFromServer').text(obj.msg);
			}
		});
		$("#addParticipantsToEvent").hide();
		$("#successContainer").fadeIn(1500);
	}
});

function saveItem() {
	var itemName = $('#itemInfo').val();
	var closeSpan = $('<span />').text('x').addClass('w3-closebtn w3-margin-right w3-large').on('click', function() {
		$(this.parentElement).remove();
	});
	var textSpan = $('<span />').text(itemName).addClass('itemTextSpan');
	var listItem = $('<li />').append(textSpan, closeSpan);
	$('#listItems').append(listItem);
}

function saveParticipant() {
	var participantEmail = $('#participantEmail').val();
	var closeSpan = $('<span />');
	closeSpan.text('x').addClass('w3-closebtn w3-margin-right w3-large').on('click', function() {
		$(this.parentElement).remove();
	});
	var emailSpan = $('<span>').text(participantEmail);
	var listPartici = $('<li />').append(emailSpan, closeSpan);
	$('#listParticipants').append(listPartici);
}

function showSingleEvent(event) {
	var listItem = $('<li />').text(event.name).data(event).click(showEventInfo);
	$('#eventsListManager').append(listItem);
}

function showSingleInvitedEvent(event) {
	var orangeButton = $('<button class="w3-btn w3-orange" />').text("Going");
	var listItem = $('<li />').text(event.name + orangeButton).data(event).click(showEventInfo);
	$('#invitedeventsListManager').append(listItem);
	var spacing = "          ";
	var eventStatusID = event._id;
	var maybeButton = $('<button />').addClass("w3-btn w3-orange").text("Maybe").click(function() {
		updateStatus('Maybe', eventStatusID);
	});
	var goingButton = $('<button />').addClass("w3-btn w3-green").text("Going").click(function() {
		updateStatus('Going', eventStatusID);
	});
	var notGoingButton = $('<button />').addClass("w3-btn w3-red").text("Not Going").click(function() {
		updateStatus('Not Going', eventStatusID);
	});
	var listItem = $('<li />').text(event.name + spacing).append(goingButton, maybeButton, notGoingButton).data(event).click(showEventInfo);
	$('#invitedeventsListManager').append(listItem);
}

function showEventInfo(oEvent) {
	var elem = $(oEvent.target);
	var specificEvent = elem.data();
	var eventIdToBring = elem.data()._id;
	$("#sideTile").fadeIn(500);
	$("#sideName").text(specificEvent.name);
	$("#sideDate").text(specificEvent.startDate);
	$("#sideDateEnd").text(specificEvent.endDate);
}

function updateStatus(statusArrival, eventStatusID) {
	$.ajax({
		url: 'api/events/updateRSVP/' + eventStatusID,
		method: 'put',
		data: {rsvp: statusArrival},
		success: function(obj) {
			if (obj.success) {
				toastr.info('Event Updated Successfully')
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});
}

function initEventInfo() {
	$('#sideName').text(' ');
	$('#sideDate').text(' ');
	$('#sideDateEnd').text(' ');
	$('#sideParticipants').text(' ');
	$('#sideItems').text(' ');
}

function showEventInfo(oEvent) {
	var elem = $(oEvent.target);
	initEventInfo();
	var specificEvent = elem.data();
	var eventIdToBring = elem.data()._id;

	$("#sideTile").fadeIn(500);
	$("#sideName").text(specificEvent.name);
	$("#sideDate").text(specificEvent.startDate);
	$("#sideDateEnd").text(specificEvent.endDate);

	var participantsToEvent;
	var getParticipantsAds = "/api/events/" + eventIdToBring + "/getParticipants";
	$.ajax({
		url: getParticipantsAds,
		method: 'get',
		data: {event_id: eventIdToBring},
		success: function(obj) {
			if (obj.success) {
				participantsToEvent = obj.msg;
				for (var i = 0; i < participantsToEvent.length; ++i) {
					var eventInfoParticipant = $('<div />').addClass('participantOnEventInfo').append($('<span />').text(participantsToEvent[i].email));
					$("#sideParticipants").append(eventInfoParticipant);
				}
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

	var getItemsAds = "/api/events/" + eventIdToBring + "/getItems";
	$.ajax({
		url: getItemsAds,
		method: 'get',
		data: {event_id: eventIdToBring},
		success: function(obj) {
			if (obj.success) {
				obj.msg.length > 0 ? addItemsToEvent(obj.msg) : $("#sideItems").append($('<span />').text('There are no items'));
			}
		},
		error: function(obj) {
			$('#msgFromServer').text(obj.msg);
		}
	});

}

var addItemsToEvent = function(items) {
	items.forEach(function(item) {
		var eventItemID = item._id;
		var itemDiv = $('<div />').addClass('itemDiv');
		var itemText = $('<span />').text(item.name).addClass('itemText');
		var itemSwitch = $('<input />').attr('type', 'checkbox').addClass('itemSwitch').click(function(event) {
			$.ajax({
				url: 'api/events/updateItem/' + eventItemID,
				method: 'put',
				data: {isChecked: true},
				success: function(obj) {
					if (obj.success) {

					}
				},
				error: function(obj) {
					$('#msgFromServer').text(obj.msg);
				}
			});
		}).prop('disabled', item.isChecked);
		itemDiv.append(itemSwitch, itemText);
		$("#sideItems").append(itemDiv);
	});
}<|endoftext|>


<commit_before><commit_msg>Create grocery list constructor that adds items and quantities, deletes items, updates items, and prints a view of the list. Include a reflection.<commit_after><|endoftext|>


<commit_before>var _       = require('underscore')
var async   = require('async')
var jwt    = require('jsonwebtoken')
var mongo   = require('mongodb')
var op      = require('object-path')
var request = require('request')

// returns random v4 UUID
var UUID = function(a) {
    return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, UUID)
}
exports.UUID = UUID

// returns MongoDb Id
var objectId = function(id) {
    try {
        return new mongo.ObjectID(id)
    } catch (e) {

    }
}
exports.objectId = objectId

// returns db connection (creates if not set)
var dbConnection = function(db, callback) {
    if(_.has(APP_ENTU_DBS, db)) {
        callback(null, APP_ENTU_DBS[db])
    } else {
        mongo.MongoClient.connect(APP_MONGODB + db, { server: { autoReconnect: true } }, function(err, connection) {
            if(err) { return callback(err) }

            // connection.collection('entityVersion').createIndex({ _mid: 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })
            // connection.collection('entityVersion').createIndex({ _deleted: 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })
            // connection.collection('entityVersion').createIndex({ 'entu_user.value': 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })

            connection.on('close', function(err) {
                delete APP_ENTU_DBS[db]
            })

            APP_ENTU_DBS[db] = connection
            callback(null, APP_ENTU_DBS[db])
        })
    }
}
exports.dbConnection = dbConnection

// send out JSON
exports.customResponder = function(req, res, next) {
    res.respond = function(body, errorCode) {
        var message = {
            version: APP_VERSION,
            ms: Date.now() - req.startDt,
            auth: !!req.user
        }

        if (errorCode) {
            message.error = body
            res.status(errorCode).send(message)
        } else {
            message.result = body
            res.send(message)
        }
    }
    next(null)
}

// check JWT header
exports.jwtCheck = function(req, res, next) {
    var parts = op.get(req, 'headers.authorization', '').split(' ')

    op.set(req, 'database', req.query.db)

    if(parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') { return next(null) }

    jwt.verify(parts[1], APP_JWT_SECRET, { issuer: req.hostname, audience: req.query.db }, function(err, decoded) {
        if(err) { return next([401, err]) }

        op.set(req, 'user', decoded.sub)
        op.set(req, 'database', decoded.aud)

        next(null)
    })
}

// Create requestlog entry on response finish
exports.requestLog = function(req, res, next) {
    req.startDt = Date.now()

    res.on('finish', function() {
        var request = {
            date: new Date(),
            ip: req.ip,
            ms: Date.now() - req.startDt,
            status: res.statusCode,
            method: req.method,
            host: req.hostname,
            browser: req.headers['user-agent'],
        }
        if(req.path) { request.path = req.path }
        if(!_.isEmpty(req.query)) { request.query = req.query }
        if(!_.isEmpty(req.body)) { request.body = req.body }
        if(req.browser) { request.browser = req.headers['user-agent'] }

        async.waterfall([
            function(callback) {
                dbConnection('entu', callback)
            },
            function(connection, callback) {
                connection.collection('request').insertOne(request, callback)
            },
        ], function(err) {
            if(err) { return next(err) }
        })
    })

    next(null)
}

// Create user session
exports.addUserSession = function(params, callback) {
    if(!params.user) { return callback(new Error('no user')) }

    var session = {
        created: new Date()
    }

    if(op.get(params, 'user.id')) { op.set(session, 'user.id', op.get(params, 'user.id')) }
    if(op.get(params, 'user.provider')) { op.set(session, 'user.provider', op.get(params, 'user.provider')) }
    if(op.get(params, 'user.name')) { op.set(session, 'user.name', op.get(params, 'user.name')) }
    if(op.get(params, 'user.email')) { op.set(session, 'user.email', op.get(params, 'user.email')) }
    if(op.get(params, 'user.picture')) { op.set(session, 'user.picture', op.get(params, 'user.picture')) }
    if(op.get(params, 'request.ip')) { op.set(session, 'ip', op.get(params, 'request.ip')) }
    if(op.get(params, 'request.headers.user-agent')) { op.set(session, 'browser', op.get(params, 'request.headers.user-agent')) }
    if(op.get(params, 'request.cookies.redirect')) { op.set(session, 'redirect', op.get(params, 'request.cookies.redirect')) }

    async.waterfall([
        function(callback) {
            if(!op.get(params, 'request.ip')) { return callback(null) }

            request.get({url: 'https://geoip.entu.eu/json/' + op.get(params, 'request.ip'), strictSSL: true, json: true, timeout: 1000}, function(error, response, body) {
                if(!body) { return callback(null) }

                var geo = _.pick(body, _.identity)
                delete geo.ip

                op.set(session, 'geo', geo)

                callback(null)
            })
        },
        function(callback) {
            dbConnection('entu', callback)
        },
        function(connection, callback) {
            connection.collection('session').insertOne(session, callback)
        },
    ], function(err, r) {
        if(err) { return callback(err) }

        callback(null, r.insertedId)
    })
}
<commit_msg>Error handling fixes<commit_after>var _       = require('underscore')
var async   = require('async')
var jwt    = require('jsonwebtoken')
var mongo   = require('mongodb')
var op      = require('object-path')
var request = require('request')

// returns random v4 UUID
var UUID = function(a) {
    return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, UUID)
}
exports.UUID = UUID

// returns MongoDb Id
var objectId = function(id) {
    try {
        return new mongo.ObjectID(id)
    } catch (e) {

    }
}
exports.objectId = objectId

// returns db connection (creates if not set)
var dbConnection = function(db, callback) {
    if(_.has(APP_ENTU_DBS, db)) {
        callback(null, APP_ENTU_DBS[db])
    } else {
        mongo.MongoClient.connect(APP_MONGODB + db, { server: { autoReconnect: true } }, function(err, connection) {
            if(err) { return callback(err) }

            // connection.collection('entityVersion').createIndex({ _mid: 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })
            // connection.collection('entityVersion').createIndex({ _deleted: 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })
            // connection.collection('entityVersion').createIndex({ 'entu_user.value': 1 }, { background:true }, function(err, indexName) {
            //     if(err) { console.log(db, err) }
            //     if(indexName) { console.log(db, indexName) }
            // })

            connection.on('close', function(err) {
                delete APP_ENTU_DBS[db]
            })

            APP_ENTU_DBS[db] = connection
            callback(null, APP_ENTU_DBS[db])
        })
    }
}
exports.dbConnection = dbConnection

// send out JSON
exports.customResponder = function(req, res, next) {
    res.respond = function(body, errorCode) {
        var message = {
            version: APP_VERSION,
            ms: Date.now() - req.startDt,
            auth: !!req.user
        }

        if (errorCode) {
            message.errorCode = errorCode
            message.error = body
            res.status(errorCode).send(message)
        } else {
            message.result = body
            res.send(message)
        }
    }
    next(null)
}

// check JWT header
exports.jwtCheck = function(req, res, next) {
    var parts = op.get(req, 'headers.authorization', '').split(' ')

    op.set(req, 'database', req.query.db)

    if(parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') { return next(null) }

    jwt.verify(parts[1], APP_JWT_SECRET, { issuer: req.hostname, audience: req.query.db }, function(err, decoded) {
        if(err) { return next([401, err]) }

        op.set(req, 'user', decoded.sub)
        op.set(req, 'database', decoded.aud)

        next(null)
    })
}

// Create requestlog entry on response finish
exports.requestLog = function(req, res, next) {
    req.startDt = Date.now()

    res.on('finish', function() {
        var request = {
            date: new Date(),
            ip: req.ip,
            ms: Date.now() - req.startDt,
            status: res.statusCode,
            method: req.method,
            host: req.hostname,
            browser: req.headers['user-agent'],
        }
        if(req.path) { request.path = req.path }
        if(!_.isEmpty(req.query)) { request.query = req.query }
        if(!_.isEmpty(req.body)) { request.body = req.body }
        if(req.browser) { request.browser = req.headers['user-agent'] }

        async.waterfall([
            function(callback) {
                dbConnection('entu', callback)
            },
            function(connection, callback) {
                connection.collection('request').insertOne(request, callback)
            },
        ], function(err) {
            if(err) {
                console.error(err.toString(), '- Can\'t save request')
                return next(null)
            }
        })
    })

    next(null)
}

// Create user session
exports.addUserSession = function(params, callback) {
    if(!params.user) { return callback(new Error('no user')) }

    var session = {
        created: new Date()
    }

    if(op.get(params, 'user.id')) { op.set(session, 'user.id', op.get(params, 'user.id')) }
    if(op.get(params, 'user.provider')) { op.set(session, 'user.provider', op.get(params, 'user.provider')) }
    if(op.get(params, 'user.name')) { op.set(session, 'user.name', op.get(params, 'user.name')) }
    if(op.get(params, 'user.email')) { op.set(session, 'user.email', op.get(params, 'user.email')) }
    if(op.get(params, 'user.picture')) { op.set(session, 'user.picture', op.get(params, 'user.picture')) }
    if(op.get(params, 'request.ip')) { op.set(session, 'ip', op.get(params, 'request.ip')) }
    if(op.get(params, 'request.headers.user-agent')) { op.set(session, 'browser', op.get(params, 'request.headers.user-agent')) }
    if(op.get(params, 'request.cookies.redirect')) { op.set(session, 'redirect', op.get(params, 'request.cookies.redirect')) }

    async.waterfall([
        function(callback) {
            if(!op.get(params, 'request.ip')) { return callback(null) }

            request.get({url: 'https://geoip.entu.eu/json/' + op.get(params, 'request.ip'), strictSSL: true, json: true, timeout: 1000}, function(error, response, body) {
                if(!body) { return callback(null) }

                var geo = _.pick(body, _.identity)
                delete geo.ip

                op.set(session, 'geo', geo)

                callback(null)
            })
        },
        function(callback) {
            dbConnection('entu', callback)
        },
        function(connection, callback) {
            connection.collection('session').insertOne(session, callback)
        },
    ], function(err, r) {
        if(err) { return callback(err) }

        callback(null, r.insertedId)
    })
}
<|endoftext|>


<commit_before>$('#save-product-button').on('click', function(e)
{
	e.preventDefault();

	var redirectDestination = U('/products');
	var returnTo = GetUriParam('returnto');
	if (returnTo !== undefined)
	{
		redirectDestination = returnTo + '?createdproduct=' + encodeURIComponent($('#name').val());
	}

	var jsonData = $('#product-form').serializeJSON({ checkboxUncheckedValue: "0" });
	Grocy.FrontendHelpers.BeginUiBusy("product-form");

	if ($("#product-picture")[0].files.length > 0)
	{
		var someRandomStuff = Math.random().toString(36).substring(2, 100) + Math.random().toString(36).substring(2, 100);
		jsonData.picture_file_name = someRandomStuff + $("#product-picture")[0].files[0].name;
	}

	if (Grocy.DeleteProductPictureOnSave)
	{
		jsonData.picture_file_name = null;
	}

	if (Grocy.EditMode === 'create')
	{
		Grocy.Api.Post('objects/products', jsonData,
			function(result)
			{
				Grocy.EditObjectId = result.created_object_id;
				Grocy.Components.UserfieldsForm.Save(function()
				{
					if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
					{
						Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
							function(result)
							{
								window.location.href = redirectDestination;
							},
							function (xhr)
							{
								Grocy.FrontendHelpers.EndUiBusy("product-form");
								Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
							}
						);
					}
					else
					{
						window.location.href = redirectDestination;
					}
				});
			},
			function (xhr)
			{
				Grocy.FrontendHelpers.EndUiBusy("product-form");
				Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
			}
		);
	}
	else
	{
		if (Grocy.DeleteProductPictureOnSave)
		{
			Grocy.Api.DeleteFile(Grocy.ProductPictureFileName, 'productpictures', {},
				function(result)
				{
					// Nothing to do
				},
				function(xhr)
				{
					Grocy.FrontendHelpers.EndUiBusy("product-form");
					Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
				}
			);
		};

		Grocy.Api.Put('objects/products/' + Grocy.EditObjectId, jsonData,
			function(result)
			{
				Grocy.Components.UserfieldsForm.Save(function()
				{
					if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
					{
						Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
							function(result)
							{
								window.location.href = redirectDestination;
							},
							function(xhr)
							{
								Grocy.FrontendHelpers.EndUiBusy("product-form");
								Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
							}
						);
					}
					else
					{
						window.location.href = redirectDestination;
					}
				});
			},
			function(xhr)
			{
				Grocy.FrontendHelpers.EndUiBusy("product-form");
				Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
			}
		);
	}
});

$('#barcode-taginput').tagsManager({
	'hiddenTagListName': 'barcode',
	'tagsContainer': '#barcode-taginput-container',
	'tagClass': 'badge badge-secondary'
});

if (Grocy.EditMode === 'edit')
{
	Grocy.Api.Get('objects/products/' + Grocy.EditObjectId,
		function (product)
		{
			if (product.barcode !== null && product.barcode.length > 0)
			{
				product.barcode.split(',').forEach(function(item)
				{
					$('#barcode-taginput').tagsManager('pushTag', item);
				});
			}
		},
		function(xhr)
		{
			console.error(xhr);
		}
	);
}

var prefillName = GetUriParam('prefillname');
if (prefillName !== undefined)
{
	$('#name').val(prefillName);
	$('#name').focus();
}

var prefillBarcode = GetUriParam('prefillbarcode');
if (prefillBarcode !== undefined)
{
	$('#barcode-taginput').tagsManager('pushTag', prefillBarcode);
	$('#name').focus();
}

$('.input-group-qu').on('change', function(e)
{
	var quIdPurchase = $("#qu_id_purchase").val();
	var quIdStock = $("#qu_id_stock").val();
	var factor = $('#qu_factor_purchase_to_stock').val();

	if (quIdPurchase != quIdStock)
	{
		$('#qu_factor_purchase_to_stock').attr("min", 2);
		$("#qu_factor_purchase_to_stock").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '2'));
	}
	else
	{
		$('#qu_factor_purchase_to_stock').attr("min", 1);
		$("#qu_factor_purchase_to_stock").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1'));
	}

	if (factor > 1)
	{
		$('#qu-conversion-info').text(__t('This means 1 %s purchased will be converted into %s %s in stock', $("#qu_id_purchase option:selected").text(), (1 * factor).toString(), $("#qu_id_stock option:selected").text()));
		$('#qu-conversion-info').removeClass('d-none');
	}
	else
	{
		$('#qu-conversion-info').addClass('d-none');
	}

	$("#tare_weight_qu_info").text($("#qu_id_stock option:selected").text());

	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#product-form input').keyup(function(event)
{
	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#location_id').change(function(event)
{
	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#product-form input').keydown(function(event)
{
	if (event.keyCode === 13) //Enter
	{
		event.preventDefault();

		if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error
		{
			return false;
		}
		else
		{
			$('#save-product-button').click();
		}
	}
});

$("#enable_tare_weight_handling").on("click", function()
{
	if (this.checked)
	{
		$("#tare_weight").removeAttr("disabled");
	}
	else
	{
		$("#tare_weight").attr("disabled", "");
	}

	Grocy.FrontendHelpers.ValidateForm("product-form");
});

$("#allow_partial_units_in_stock").on("click", function()
{
	if (this.checked)
	{
		$("#min_stock_amount").attr("min", "0.00");
		$("#min_stock_amount").attr("step", "0.01");
	}
	else
	{
		$("#min_stock_amount").attr("min", "0");
		$("#min_stock_amount").attr("step", "1");
	}

	Grocy.FrontendHelpers.ValidateForm("product-form");
});

Grocy.DeleteProductPictureOnSave = false;
$('#delete-current-product-picture-button').on('click', function (e)
{
	Grocy.DeleteProductPictureOnSave = true;
	$("#current-product-picture").addClass("d-none");
	$("#delete-current-product-picture-on-save-hint").removeClass("d-none");
	$("#delete-current-product-picture-button").addClass("disabled");
});

if (Grocy.EditMode === 'create')
{
	if (Grocy.UserSettings.product_presets_location_id.toString() !== '-1')
	{
		$("#location_id").val(Grocy.UserSettings.product_presets_location_id);
	}

	if (Grocy.UserSettings.product_presets_product_group_id.toString() !== '-1')
	{
		$("#product_group_id").val(Grocy.UserSettings.product_presets_product_group_id);
	}

	if (Grocy.UserSettings.product_presets_qu_id.toString() !== '-1')
	{
		$("select.input-group-qu").val(Grocy.UserSettings.product_presets_qu_id);
	}
}

Grocy.Components.UserfieldsForm.Load();
$('#name').focus();
$('.input-group-qu').trigger('change');
Grocy.FrontendHelpers.ValidateForm('product-form');

// Click twice to trigger on-click but not change the actual checked state
$("#allow_partial_units_in_stock").click();
$("#allow_partial_units_in_stock").click();
<commit_msg>Fixed again a problem with productpicker workflows and disabled URL rewriting (closes #232)<commit_after>$('#save-product-button').on('click', function(e)
{
	e.preventDefault();

	var redirectDestination = U('/products');
	var returnTo = GetUriParam('returnto');
	if (returnTo !== undefined)
	{
		redirectDestination = U(returnTo) + '?createdproduct=' + encodeURIComponent($('#name').val());
	}

	var jsonData = $('#product-form').serializeJSON({ checkboxUncheckedValue: "0" });
	Grocy.FrontendHelpers.BeginUiBusy("product-form");

	if ($("#product-picture")[0].files.length > 0)
	{
		var someRandomStuff = Math.random().toString(36).substring(2, 100) + Math.random().toString(36).substring(2, 100);
		jsonData.picture_file_name = someRandomStuff + $("#product-picture")[0].files[0].name;
	}

	if (Grocy.DeleteProductPictureOnSave)
	{
		jsonData.picture_file_name = null;
	}

	if (Grocy.EditMode === 'create')
	{
		Grocy.Api.Post('objects/products', jsonData,
			function(result)
			{
				Grocy.EditObjectId = result.created_object_id;
				Grocy.Components.UserfieldsForm.Save(function()
				{
					if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
					{
						Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
							function(result)
							{
								window.location.href = redirectDestination;
							},
							function (xhr)
							{
								Grocy.FrontendHelpers.EndUiBusy("product-form");
								Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
							}
						);
					}
					else
					{
						window.location.href = redirectDestination;
					}
				});
			},
			function (xhr)
			{
				Grocy.FrontendHelpers.EndUiBusy("product-form");
				Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
			}
		);
	}
	else
	{
		if (Grocy.DeleteProductPictureOnSave)
		{
			Grocy.Api.DeleteFile(Grocy.ProductPictureFileName, 'productpictures', {},
				function(result)
				{
					// Nothing to do
				},
				function(xhr)
				{
					Grocy.FrontendHelpers.EndUiBusy("product-form");
					Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
				}
			);
		};

		Grocy.Api.Put('objects/products/' + Grocy.EditObjectId, jsonData,
			function(result)
			{
				Grocy.Components.UserfieldsForm.Save(function()
				{
					if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
					{
						Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
							function(result)
							{
								window.location.href = redirectDestination;
							},
							function(xhr)
							{
								Grocy.FrontendHelpers.EndUiBusy("product-form");
								Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
							}
						);
					}
					else
					{
						window.location.href = redirectDestination;
					}
				});
			},
			function(xhr)
			{
				Grocy.FrontendHelpers.EndUiBusy("product-form");
				Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
			}
		);
	}
});

$('#barcode-taginput').tagsManager({
	'hiddenTagListName': 'barcode',
	'tagsContainer': '#barcode-taginput-container',
	'tagClass': 'badge badge-secondary'
});

if (Grocy.EditMode === 'edit')
{
	Grocy.Api.Get('objects/products/' + Grocy.EditObjectId,
		function (product)
		{
			if (product.barcode !== null && product.barcode.length > 0)
			{
				product.barcode.split(',').forEach(function(item)
				{
					$('#barcode-taginput').tagsManager('pushTag', item);
				});
			}
		},
		function(xhr)
		{
			console.error(xhr);
		}
	);
}

var prefillName = GetUriParam('prefillname');
if (prefillName !== undefined)
{
	$('#name').val(prefillName);
	$('#name').focus();
}

var prefillBarcode = GetUriParam('prefillbarcode');
if (prefillBarcode !== undefined)
{
	$('#barcode-taginput').tagsManager('pushTag', prefillBarcode);
	$('#name').focus();
}

$('.input-group-qu').on('change', function(e)
{
	var quIdPurchase = $("#qu_id_purchase").val();
	var quIdStock = $("#qu_id_stock").val();
	var factor = $('#qu_factor_purchase_to_stock').val();

	if (quIdPurchase != quIdStock)
	{
		$('#qu_factor_purchase_to_stock').attr("min", 2);
		$("#qu_factor_purchase_to_stock").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '2'));
	}
	else
	{
		$('#qu_factor_purchase_to_stock').attr("min", 1);
		$("#qu_factor_purchase_to_stock").parent().find(".invalid-feedback").text(__t('The amount cannot be lower than %s', '1'));
	}

	if (factor > 1)
	{
		$('#qu-conversion-info').text(__t('This means 1 %s purchased will be converted into %s %s in stock', $("#qu_id_purchase option:selected").text(), (1 * factor).toString(), $("#qu_id_stock option:selected").text()));
		$('#qu-conversion-info').removeClass('d-none');
	}
	else
	{
		$('#qu-conversion-info').addClass('d-none');
	}

	$("#tare_weight_qu_info").text($("#qu_id_stock option:selected").text());

	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#product-form input').keyup(function(event)
{
	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#location_id').change(function(event)
{
	Grocy.FrontendHelpers.ValidateForm('product-form');
});

$('#product-form input').keydown(function(event)
{
	if (event.keyCode === 13) //Enter
	{
		event.preventDefault();

		if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error
		{
			return false;
		}
		else
		{
			$('#save-product-button').click();
		}
	}
});

$("#enable_tare_weight_handling").on("click", function()
{
	if (this.checked)
	{
		$("#tare_weight").removeAttr("disabled");
	}
	else
	{
		$("#tare_weight").attr("disabled", "");
	}

	Grocy.FrontendHelpers.ValidateForm("product-form");
});

$("#allow_partial_units_in_stock").on("click", function()
{
	if (this.checked)
	{
		$("#min_stock_amount").attr("min", "0.00");
		$("#min_stock_amount").attr("step", "0.01");
	}
	else
	{
		$("#min_stock_amount").attr("min", "0");
		$("#min_stock_amount").attr("step", "1");
	}

	Grocy.FrontendHelpers.ValidateForm("product-form");
});

Grocy.DeleteProductPictureOnSave = false;
$('#delete-current-product-picture-button').on('click', function (e)
{
	Grocy.DeleteProductPictureOnSave = true;
	$("#current-product-picture").addClass("d-none");
	$("#delete-current-product-picture-on-save-hint").removeClass("d-none");
	$("#delete-current-product-picture-button").addClass("disabled");
});

if (Grocy.EditMode === 'create')
{
	if (Grocy.UserSettings.product_presets_location_id.toString() !== '-1')
	{
		$("#location_id").val(Grocy.UserSettings.product_presets_location_id);
	}

	if (Grocy.UserSettings.product_presets_product_group_id.toString() !== '-1')
	{
		$("#product_group_id").val(Grocy.UserSettings.product_presets_product_group_id);
	}

	if (Grocy.UserSettings.product_presets_qu_id.toString() !== '-1')
	{
		$("select.input-group-qu").val(Grocy.UserSettings.product_presets_qu_id);
	}
}

Grocy.Components.UserfieldsForm.Load();
$('#name').focus();
$('.input-group-qu').trigger('change');
Grocy.FrontendHelpers.ValidateForm('product-form');

// Click twice to trigger on-click but not change the actual checked state
$("#allow_partial_units_in_stock").click();
$("#allow_partial_units_in_stock").click();
<|endoftext|>


<commit_before>const cluster = require("cluster");
const os = require("os");
const assert = require("assert");
const fs = require("fs-promise");

const sleep = require("sleep-promise");
const JSONRPC = {
	EndpointBase: require("../EndpointBase"),
	Server: require("../Server"),
	Client: require("../Client"),
	BidirectionalWorkerRouter: require("../BidirectionalWorkerRouter"),
	Plugins: {
		Server: require("../Plugins/Server"),
		Client: require("../Plugins/Client")
	}
};

/**
 * Extend this class to export extra master RPC APIs.
 *
 * Counter-intuitively, this endpoint instantiates its own JSONRPC.Server and JSONRPC.BidirectionalWorkerRouter,
 * inside .start().
 */
class MasterEndpoint extends JSONRPC.EndpointBase
{
	constructor(classReverseCallsClient)
	{
		console.log(`Fired up ${cluster.isWorker ? "worker" : "master"} with PID ${process.pid}`);

		super(
			/*strName*/ "ClusterIPC",
			/*strPath*/ "/api-cluster/IPC",
			/*objReflection*/ {},
			classReverseCallsClient
		);

		if(!cluster.isMaster)
		{
			throw new Error("MasterEndpoint can only be instantiated in the master process.");
		}

		this._bidirectionalWorkerRouter = null;
		this._jsonrpcServer = null;

		this.bShuttingDown = false;
		this.arrFailureTimestamps = [];

		this.objWorkerIDToState = {};

		this._bWorkersStarted = false;
		this._bWatchingForUpgrade = false;

		this._nMaxWorkersCount = Number.MAX_SAFE_INTEGER;
	}

	/**
	 * The object has worker IDs as keys and object values like this: {client: JSONRPC.Client, ready: boolean}.
	 *
	 * @returns {Object}
	 */
	get workerClients()
	{
		return this.objWorkerIDToState;
	}

	/**
	 * This overridable function is called and awaited inside startWorker().
	 *
	 * This mustn't be called through JSONRPC.
	 *
	 * @param {undefined} incomingRequest
	 */
	async _startServices(incomingRequest)
	{
		if(incomingRequest)
		{
			throw new Error("This mustn't be called through JSONRPC.");
		}

		// this.masterClient is available here.
	}

	/**
	 * This overridable function is called and awaited inside gracefulExit().
	 * Careful, gracefulExit() will timeout waiting after services to stop after a while.
	 *
	 * This mustn't be called through JSONRPC.
	 *
	 * @param {undefined} incomingRequest
	 */
	async _stopServices(incomingRequest)
	{
		if(incomingRequest)
		{
			throw new Error("This mustn't be called through JSONRPC.");
		}
	}

	/**
	 * Starts the JSONRPC server over cluster IPC, and forks worker processes.
	 */
	async start()
	{
		if(this._bWorkersStarted)
		{
			throw new Error("Workers have already been started.");
		}
		this._bWorkersStarted = true;

		this._jsonrpcServer = new JSONRPC.Server();

		// By default, JSONRPC.Server rejects all requests as not authenticated and not authorized.
		this._jsonrpcServer.addPlugin(new JSONRPC.Plugins.Server.AuthenticationSkip());
		this._jsonrpcServer.addPlugin(new JSONRPC.Plugins.Server.AuthorizeAll());
		this._jsonrpcServer.registerEndpoint(this);

		this._bidirectionalWorkerRouter = new JSONRPC.BidirectionalWorkerRouter(this._jsonrpcServer);

		cluster.on(
			"fork",
			async (worker) => {
				try
				{
					this.objWorkerIDToState[worker.id] = {
						client: null,
						ready: false
					};

					const nConnectionID = await this._bidirectionalWorkerRouter.addWorker(worker, /*strEndpointPath*/ this.path, 120 * 1000 /*Readiness timeout in milliseconds*/);

					this.objWorkerIDToState[worker.id].client = this._bidirectionalWorkerRouter.connectionIDToSingletonClient(nConnectionID, this.ReverseCallsClientClass);
				}
				catch(error)
				{
					console.error(error);
					console.error("Cluster master process, on fork event handler unexpected error. Don't know how to handle.");
					process.exit(1);
				}
			}
		);

		cluster.on(
			"exit",
			async (worker, nExitCode, nKillSignal) => {
				try
				{
					console.log(`Worker with PID  ${worker.process.pid} died. Exit code: ${nExitCode}. Signal: ${nKillSignal}.`);

					this.arrFailureTimestamps.push(new Date().getTime());
					this.arrFailureTimestamps = this.arrFailureTimestamps.filter((nMillisecondsUnixTime) => {
						return nMillisecondsUnixTime >= new Date().getTime() - (60 * 2 * 1000);
					});

					if(this.arrFailureTimestamps.length / Math.max(os.cpus().length, 1) > 4)
					{
						await this.gracefulExit(null);
					}
					else
					{
						if(!this.bShuttingDown)
						{
							await sleep(500);
							cluster.fork();
						}
					}
				}
				catch(error)
				{
					console.error(error);
					console.error("Cluster master process, on worker exit event handler unexpected error. Don't know how to handle. Exiting...");
					process.exit(1);
				}
			}
		);

		await this._startServices();

		for (let i = 0; i < Math.min(Math.max(os.cpus().length, 1), this._nMaxWorkersCount); i++)
		{
			cluster.fork();
		}
	}

	/**
	 * If the version in package.json changes, this.gracefulExit() will be invoked.
	 *
	 * @param {string} strPackageJSONPath
	 *
	 * @returns {undefined}
	 */
	async watchForUpgrade(strPackageJSONPath)
	{
		assert(typeof strPackageJSONPath === "string");

		if(this._bWatchingForUpgrade)
		{
			return;
		}

		this._bWatchingForUpgrade = true;

		const strVersion = JSON.parse(await fs.readFile(strPackageJSONPath, "utf8")).version;
		let nPackageJSONModificationTime = (await fs.stat(strPackageJSONPath)).mtime.getTime();
		let strVersionNew = strVersion;

		const nIntervalMilliseconds = 10 * 1000;

		const nIntervalID = setInterval(
			async () => {
				try
				{
					if(nPackageJSONModificationTime !== (await fs.stat(strPackageJSONPath)).mtime.getTime())
					{
						nPackageJSONModificationTime = (await fs.stat(strPackageJSONPath)).mtime.getTime();
						strVersionNew = JSON.parse(await fs.readFile(strPackageJSONPath, "utf8")).version;
					}

					if(strVersionNew !== strVersion)
					{
						clearInterval(nIntervalID);

						console.log(`
							Updated.
							Detected new version ${strVersionNew}.
							Old version ${strVersion}.
							Attempting to exit gracefully to allow starting with new version.
						`.replace(/^\t+/gm, ""));

						await this.gracefulExit(null);
					}
				}
				catch(error)
				{
					console.error(error);
				}
			},
			nIntervalMilliseconds
		);
	}

	/**
	 * Signals a worker's JSONRPC endpoint is ready to receive calls.
	 *
	 * @param {JSONRPC.IncomingRequest} incomingRequest
	 * @param {number} nWorkerID
	 */
	async workerServicesReady(incomingRequest, nWorkerID)
	{
		this.objWorkerIDToState[nWorkerID].ready = true;
	}

	/**
	 * @param {JSONRPC.IncomingRequest|null} incomingRequest
	 *
	 * @returns {undefined}
	 */
	async gracefulExit(incomingRequest)
	{
		if(this.bShuttingDown)
		{
			return;
		}

		this.bShuttingDown = true;

		for(const nWorkerID in cluster.workers)
		{
			if(
				cluster.workers[nWorkerID].isConnected()
				&& this.objWorkerIDToState[nWorkerID].ready
			)
			{
				// Do not await, need these in parallel.
				/*await*/ this.objWorkerIDToState[nWorkerID].client.gracefulExit()
				.then(() => { /*gracefulExit should never return.*/ })
				.catch((error) => {
					if(cluster.workers.hasOwnProperty(nWorkerID) && !cluster.workers[nWorkerID].isDead())
					{
						console.error(error);
						cluster.workers[nWorkerID].kill();
					}
				});
			}
			else if(!cluster.workers[nWorkerID].isDead())
			{
				cluster.workers[nWorkerID].kill();
			}
		}

		console.log("Waiting for workers to exit gracefully.");
		let bKeepWaiting = true;
		while(bKeepWaiting)
		{
			bKeepWaiting = false;

			for(const nWorkerID in cluster.workers)
			{
				if(!cluster.workers[nWorkerID].isDead())
				{
					bKeepWaiting = true;
				}
			}

			if(bKeepWaiting)
			{
				await sleep(1000);
			}
		}
		console.log("All workers have exited.");

		await this._stopServices();

		console.log("[" + process.pid + "] Master process exiting gracefully.");
		process.exit(0);
	}

	/**
	 * @param {JSONRPC.IncomingRequest|null} incomingRequest
	 * @param {string} strReturn
	 *
	 * @returns {string}
	 */
	async ping(incomingRequest, strReturn)
	{
		console.log("Worker said: " + JSON.stringify(strReturn));
		return strReturn;
	}
};

module.exports = MasterEndpoint;
<commit_msg>console log<commit_after>const cluster = require("cluster");
const os = require("os");
const assert = require("assert");
const fs = require("fs-promise");

const sleep = require("sleep-promise");
const JSONRPC = {
	EndpointBase: require("../EndpointBase"),
	Server: require("../Server"),
	Client: require("../Client"),
	BidirectionalWorkerRouter: require("../BidirectionalWorkerRouter"),
	Plugins: {
		Server: require("../Plugins/Server"),
		Client: require("../Plugins/Client")
	}
};

/**
 * Extend this class to export extra master RPC APIs.
 *
 * Counter-intuitively, this endpoint instantiates its own JSONRPC.Server and JSONRPC.BidirectionalWorkerRouter,
 * inside .start().
 */
class MasterEndpoint extends JSONRPC.EndpointBase
{
	constructor(classReverseCallsClient)
	{
		console.log(`Fired up ${cluster.isWorker ? "worker" : "master"} with PID ${process.pid}`);

		super(
			/*strName*/ "ClusterIPC",
			/*strPath*/ "/api-cluster/IPC",
			/*objReflection*/ {},
			classReverseCallsClient
		);

		if(!cluster.isMaster)
		{
			throw new Error("MasterEndpoint can only be instantiated in the master process.");
		}

		this._bidirectionalWorkerRouter = null;
		this._jsonrpcServer = null;

		this.bShuttingDown = false;
		this.arrFailureTimestamps = [];

		this.objWorkerIDToState = {};

		this._bWorkersStarted = false;
		this._bWatchingForUpgrade = false;

		this._nMaxWorkersCount = Number.MAX_SAFE_INTEGER;
	}

	/**
	 * The object has worker IDs as keys and object values like this: {client: JSONRPC.Client, ready: boolean}.
	 *
	 * @returns {Object}
	 */
	get workerClients()
	{
		return this.objWorkerIDToState;
	}

	/**
	 * This overridable function is called and awaited inside startWorker().
	 *
	 * This mustn't be called through JSONRPC.
	 *
	 * @param {undefined} incomingRequest
	 */
	async _startServices(incomingRequest)
	{
		if(incomingRequest)
		{
			throw new Error("This mustn't be called through JSONRPC.");
		}

		// this.masterClient is available here.
	}

	/**
	 * This overridable function is called and awaited inside gracefulExit().
	 * Careful, gracefulExit() will timeout waiting after services to stop after a while.
	 *
	 * This mustn't be called through JSONRPC.
	 *
	 * @param {undefined} incomingRequest
	 */
	async _stopServices(incomingRequest)
	{
		if(incomingRequest)
		{
			throw new Error("This mustn't be called through JSONRPC.");
		}
	}

	/**
	 * Starts the JSONRPC server over cluster IPC, and forks worker processes.
	 */
	async start()
	{
		if(this._bWorkersStarted)
		{
			throw new Error("Workers have already been started.");
		}
		this._bWorkersStarted = true;

		this._jsonrpcServer = new JSONRPC.Server();

		// By default, JSONRPC.Server rejects all requests as not authenticated and not authorized.
		this._jsonrpcServer.addPlugin(new JSONRPC.Plugins.Server.AuthenticationSkip());
		this._jsonrpcServer.addPlugin(new JSONRPC.Plugins.Server.AuthorizeAll());
		this._jsonrpcServer.registerEndpoint(this);

		this._bidirectionalWorkerRouter = new JSONRPC.BidirectionalWorkerRouter(this._jsonrpcServer);

		cluster.on(
			"fork",
			async (worker) => {
				try
				{
					this.objWorkerIDToState[worker.id] = {
						client: null,
						ready: false
					};

					console.log("Adding worker ID " + worker.id + " to BidirectionalWorkerRouter.");
					const nConnectionID = await this._bidirectionalWorkerRouter.addWorker(worker, /*strEndpointPath*/ this.path, 120 * 1000 /*Readiness timeout in milliseconds*/);

					this.objWorkerIDToState[worker.id].client = this._bidirectionalWorkerRouter.connectionIDToSingletonClient(nConnectionID, this.ReverseCallsClientClass);
				}
				catch(error)
				{
					console.error(error);
					console.error("Cluster master process, on fork event handler unexpected error. Don't know how to handle.");
					process.exit(1);
				}
			}
		);

		cluster.on(
			"exit",
			async (worker, nExitCode, nKillSignal) => {
				try
				{
					console.log(`Worker with PID  ${worker.process.pid} died. Exit code: ${nExitCode}. Signal: ${nKillSignal}.`);

					this.arrFailureTimestamps.push(new Date().getTime());
					this.arrFailureTimestamps = this.arrFailureTimestamps.filter((nMillisecondsUnixTime) => {
						return nMillisecondsUnixTime >= new Date().getTime() - (60 * 2 * 1000);
					});

					if(this.arrFailureTimestamps.length / Math.max(os.cpus().length, 1) > 4)
					{
						await this.gracefulExit(null);
					}
					else
					{
						if(!this.bShuttingDown)
						{
							await sleep(500);
							cluster.fork();
						}
					}
				}
				catch(error)
				{
					console.error(error);
					console.error("Cluster master process, on worker exit event handler unexpected error. Don't know how to handle. Exiting...");
					process.exit(1);
				}
			}
		);

		await this._startServices();

		for (let i = 0; i < Math.min(Math.max(os.cpus().length, 1), this._nMaxWorkersCount); i++)
		{
			cluster.fork();
		}
	}

	/**
	 * If the version in package.json changes, this.gracefulExit() will be invoked.
	 *
	 * @param {string} strPackageJSONPath
	 *
	 * @returns {undefined}
	 */
	async watchForUpgrade(strPackageJSONPath)
	{
		assert(typeof strPackageJSONPath === "string");

		if(this._bWatchingForUpgrade)
		{
			return;
		}

		this._bWatchingForUpgrade = true;

		const strVersion = JSON.parse(await fs.readFile(strPackageJSONPath, "utf8")).version;
		let nPackageJSONModificationTime = (await fs.stat(strPackageJSONPath)).mtime.getTime();
		let strVersionNew = strVersion;

		const nIntervalMilliseconds = 10 * 1000;

		const nIntervalID = setInterval(
			async () => {
				try
				{
					if(nPackageJSONModificationTime !== (await fs.stat(strPackageJSONPath)).mtime.getTime())
					{
						nPackageJSONModificationTime = (await fs.stat(strPackageJSONPath)).mtime.getTime();
						strVersionNew = JSON.parse(await fs.readFile(strPackageJSONPath, "utf8")).version;
					}

					if(strVersionNew !== strVersion)
					{
						clearInterval(nIntervalID);

						console.log(`
							Updated.
							Detected new version ${strVersionNew}.
							Old version ${strVersion}.
							Attempting to exit gracefully to allow starting with new version.
						`.replace(/^\t+/gm, ""));

						await this.gracefulExit(null);
					}
				}
				catch(error)
				{
					console.error(error);
				}
			},
			nIntervalMilliseconds
		);
	}

	/**
	 * Signals a worker's JSONRPC endpoint is ready to receive calls.
	 *
	 * @param {JSONRPC.IncomingRequest} incomingRequest
	 * @param {number} nWorkerID
	 */
	async workerServicesReady(incomingRequest, nWorkerID)
	{
		this.objWorkerIDToState[nWorkerID].ready = true;
	}

	/**
	 * @param {JSONRPC.IncomingRequest|null} incomingRequest
	 *
	 * @returns {undefined}
	 */
	async gracefulExit(incomingRequest)
	{
		if(this.bShuttingDown)
		{
			return;
		}

		this.bShuttingDown = true;

		for(const nWorkerID in cluster.workers)
		{
			if(
				cluster.workers[nWorkerID].isConnected()
				&& this.objWorkerIDToState[nWorkerID].ready
			)
			{
				// Do not await, need these in parallel.
				/*await*/ this.objWorkerIDToState[nWorkerID].client.gracefulExit()
				.then(() => { /*gracefulExit should never return.*/ })
				.catch((error) => {
					if(cluster.workers.hasOwnProperty(nWorkerID) && !cluster.workers[nWorkerID].isDead())
					{
						console.error(error);
						cluster.workers[nWorkerID].kill();
					}
				});
			}
			else if(!cluster.workers[nWorkerID].isDead())
			{
				cluster.workers[nWorkerID].kill();
			}
		}

		console.log("Waiting for workers to exit gracefully.");
		let bKeepWaiting = true;
		while(bKeepWaiting)
		{
			bKeepWaiting = false;

			for(const nWorkerID in cluster.workers)
			{
				if(!cluster.workers[nWorkerID].isDead())
				{
					bKeepWaiting = true;
				}
			}

			if(bKeepWaiting)
			{
				await sleep(1000);
			}
		}
		console.log("All workers have exited.");

		await this._stopServices();

		console.log("[" + process.pid + "] Master process exiting gracefully.");
		process.exit(0);
	}

	/**
	 * @param {JSONRPC.IncomingRequest|null} incomingRequest
	 * @param {string} strReturn
	 *
	 * @returns {string}
	 */
	async ping(incomingRequest, strReturn)
	{
		console.log("Worker said: " + JSON.stringify(strReturn));
		return strReturn;
	}
};

module.exports = MasterEndpoint;
<|endoftext|>


<commit_before>import React from 'react';
import moment from 'moment';
import { connect } from 'react-redux';

import { getTodaysGames } from '../reducers/games.js';

import DonutChart from '../components/charts/donutChart.js';

function mapStateToProps(state) {
  return {
    games: state.games.get('games')
  };
}

function mapDispatchToProps(dispatch) {
  return {
    apiGetTodaysGames: () => dispatch(getTodaysGames())
  };
}

const wrapperStyle = {
  backgroundColor: '#FFFFFF',
  borderRadius: '3px',
  boxShadow: '0px 2px 4px #4A4A4A',
  borderTop: '3px solid #3498DB',
  padding: '1rem',
  marginBottom: '2rem',
  marginTop: '2rem'
};

const teamWrapperStyle = {
  display: 'flex'
};

const teamStyle = {
  height: '4rem',
  width: '10rem',
  marginRight: '2rem'
};

const graphStyle = {
  flexGrow: '1',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center'
};

export class Games extends React.Component {

  componentDidMount() {
    const { apiGetTodaysGames } = this.props;
    apiGetTodaysGames();
  }

  render() {
    const { games } = this.props;

    const gamesHTML = games.map((game) => {
      const away = game.get('teams').get('away');
      const awayRecord = away.get('leagueRecord').get('wins') + ' - '
        + away.get('leagueRecord').get('losses') + ' - '
        + away.get('leagueRecord').get('ot');

      const home = game.get('teams').get('home');
      const homeRecord = home.get('leagueRecord').get('wins') + ' - '
        + home.get('leagueRecord').get('losses') + ' - '
        + home.get('leagueRecord').get('ot');

      return (

{moment(game.get('gameDate')).format('h:mm A')}

{away.get('team').get('name')}

{awayRecord}

{home.get('team').get('name')}

{homeRecord}

              <DonutChart height={300}
                  radius={150}
                  width={460}
              />

      );
    });

    return (

{gamesHTML}

    );
  }

}

export default connect(mapStateToProps, mapDispatchToProps)(Games);
<commit_msg>test links<commit_after>import React from 'react';
import moment from 'moment';
import { connect } from 'react-redux';

import { getTodaysGames } from '../reducers/games.js';

import DonutChart from '../components/charts/donutChart.js';

function mapStateToProps(state) {
  return {
    games: state.games.get('games')
  };
}

function mapDispatchToProps(dispatch) {
  return {
    apiGetTodaysGames: () => dispatch(getTodaysGames())
  };
}

const wrapperStyle = {
  backgroundColor: '#FFFFFF',
  borderRadius: '3px',
  boxShadow: '0px 2px 4px #4A4A4A',
  borderTop: '3px solid #3498DB',
  padding: '1rem',
  marginBottom: '2rem',
  marginTop: '2rem'
};

const teamWrapperStyle = {
  display: 'flex'
};

const teamStyle = {
  height: '4rem',
  width: '10rem',
  marginRight: '2rem'
};

const graphStyle = {
  flexGrow: '1',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center'
};

export class Games extends React.Component {

  componentDidMount() {
    const { apiGetTodaysGames } = this.props;
    apiGetTodaysGames();
  }

  render() {
    const { games } = this.props;

    const gamesHTML = games.map((game) => {
      const away = game.get('teams').get('away');
      const awayRecord = away.get('leagueRecord').get('wins') + ' - '
        + away.get('leagueRecord').get('losses') + ' - '
        + away.get('leagueRecord').get('ot');

      const home = game.get('teams').get('home');
      const homeRecord = home.get('leagueRecord').get('wins') + ' - '
        + home.get('leagueRecord').get('losses') + ' - '
        + home.get('leagueRecord').get('ot');

      return (

          {'ALEXANDER'}

{moment(game.get('gameDate')).format('h:mm A')}

{away.get('team').get('name')}

{awayRecord}

{home.get('team').get('name')}

{homeRecord}

              <DonutChart height={300}
                  radius={150}
                  width={460}
              />

      );
    });

    return (

{gamesHTML}

    );
  }

}

export default connect(mapStateToProps, mapDispatchToProps)(Games);
<|endoftext|>


<commit_before>define(function() {

  'use strict';

  var _BYTESOURCE = Symbol('byteSource');

  var itemObjectModel = {
    createItem: function(title) {
      var itemElement = document.createElement('SECTION');
      itemElement.classList.add('item');

      itemElement.appendChild(itemElement.titleElement = document.createElement('HEADER'));

      itemElement.titleTextElement = itemElement.titleElement;

      Object.defineProperties(itemElement, this.itemProperties);

      if (title) itemElement.itemTitle = title;

      itemElement.addEventListener('click', clickItem);

      return itemElement;
    },
    itemProperties: {
      itemTitle: {
        get: function() {
          return this.titleTextElement.innerText;
        },
        set: function(text) {
          this.titleTextElement.innerText = text;
        },
        enumerable: true,
      },
      byteSource: {
        get: function() {
          return this[_BYTESOURCE];
        },
        set: function(byteSource) {
          this[_BYTESOURCE] = byteSource;
          if (!byteSource) {
            this.titleElement.innerText = this.titleTextElement.innerText;
            this.titleTextElement = this.titleElement;
          }
          else {
            var link = document.createElement('A');
            link.href = '#';
            link.classList.add('item-download');
            link.download = this.itemTitle.replace(/[\\\/:"<>\*\?\|]/g, '_');
            link.innerText = this.titleTextElement.innerText;
            this.titleElement.innerHTML = '';
            this.titleElement.appendChild(this.titleTextElement = link);
          }
        },
      },
      startAddingItems: {
        value: function() {
          if (!this.classList.contains('has-subitems')) {
            this.classList.add('has-subitems');
            this.appendChild(this.subitemsElement = document.createElement('SECTION'));
            this.subitemsElement.classList.add('subitems');
          }
        },
      },
      addItem: {
        value: function(item) {
          this.startAddingItems();
          this.classList.add('itemizing');
          this.subitemsElement.appendChild(item);
        },
      },
      confirmAllItemsAdded: {
        value: function() {
          this.classList.remove('itemizing');
          this.classList.add('itemized');
        },
      },
      text: {
        set: function(text) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(text));
          this.addItem(textContainer);
        },
      },
      getBytes: {
        value: function() {
          return this.byteSource.getBytes();
        },
      },
      setDataObject: {
        value: function(value) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(JSON.stringify(value, 2)));
          this.addItem(textContainer);
        },
      },
      withPixels: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          var imageData = ctx.createImageData(width, height);
          callback(imageData.data);
          ctx.putImageData(imageData, 0, 0);
        },
      },
      with2DContext: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          callback(ctx);
        },
      },
      setHotspot: {
        value: function(x, y) {
          this.dataset.hotspotX = x;
          this.dataset.hotspotY = y;
        },
      },
    },
  };

  function clickItem(e) {
    e.stopPropagation();
    if (e.target.classList.contains('item-download')) {
      if (!e.target.classList.contains('prepared')) {
        e.preventDefault();
        if (!e.target.classList.contains('preparing')) {
          e.target.classList.add('preparing');
          this.byteSource.getURL().then(function(url) {
            e.target.classList.remove('preparing');
            e.target.classList.add('prepared');
            e.target.href = url;
            e.target.click();
          });
        }
      }
      return;
    }
    if (this.classList.contains('has-subitems')) {
      if (this.classList.toggle('open')) {
        this.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE));
      }
    }
  }

  Object.defineProperties(itemObjectModel, {
    EVT_POPULATE: {value:'item-populate'},
  });

  return itemObjectModel;

});
<commit_msg>Add setOffset method<commit_after>define(function() {

  'use strict';

  var _BYTESOURCE = Symbol('byteSource');

  var itemObjectModel = {
    createItem: function(title) {
      var itemElement = document.createElement('SECTION');
      itemElement.classList.add('item');

      itemElement.appendChild(itemElement.titleElement = document.createElement('HEADER'));

      itemElement.titleTextElement = itemElement.titleElement;

      Object.defineProperties(itemElement, this.itemProperties);

      if (title) itemElement.itemTitle = title;

      itemElement.addEventListener('click', clickItem);

      return itemElement;
    },
    itemProperties: {
      itemTitle: {
        get: function() {
          return this.titleTextElement.innerText;
        },
        set: function(text) {
          this.titleTextElement.innerText = text;
        },
        enumerable: true,
      },
      byteSource: {
        get: function() {
          return this[_BYTESOURCE];
        },
        set: function(byteSource) {
          this[_BYTESOURCE] = byteSource;
          if (!byteSource) {
            this.titleElement.innerText = this.titleTextElement.innerText;
            this.titleTextElement = this.titleElement;
          }
          else {
            var link = document.createElement('A');
            link.href = '#';
            link.classList.add('item-download');
            link.download = this.itemTitle.replace(/[\\\/:"<>\*\?\|]/g, '_');
            link.innerText = this.titleTextElement.innerText;
            this.titleElement.innerHTML = '';
            this.titleElement.appendChild(this.titleTextElement = link);
          }
        },
      },
      startAddingItems: {
        value: function() {
          if (!this.classList.contains('has-subitems')) {
            this.classList.add('has-subitems');
            this.appendChild(this.subitemsElement = document.createElement('SECTION'));
            this.subitemsElement.classList.add('subitems');
          }
        },
      },
      addItem: {
        value: function(item) {
          this.startAddingItems();
          this.classList.add('itemizing');
          this.subitemsElement.appendChild(item);
        },
      },
      confirmAllItemsAdded: {
        value: function() {
          this.classList.remove('itemizing');
          this.classList.add('itemized');
        },
      },
      text: {
        set: function(text) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(text));
          this.addItem(textContainer);
        },
      },
      getBytes: {
        value: function() {
          return this.byteSource.getBytes();
        },
      },
      setDataObject: {
        value: function(value) {
          var textContainer = document.createElement('PRE');
          textContainer.appendChild(document.createTextNode(JSON.stringify(value, 2)));
          this.addItem(textContainer);
        },
      },
      withPixels: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          var imageData = ctx.createImageData(width, height);
          callback(imageData.data);
          ctx.putImageData(imageData, 0, 0);
        },
      },
      with2DContext: {
        value: function(width, height, callback) {
          var canvas = document.createElement('CANVAS');
          canvas.width = width;
          canvas.height = height;
          this.addItem(canvas);
          var ctx = canvas.getContext('2d');
          callback(ctx);
        },
      },
      setHotspot: {
        value: function(x, y) {
          this.dataset.hotspotX = x;
          this.dataset.hotspotY = y;
        },
      },
      setOffset: {
        value: function(x, y) {
          this.dataset.offsetX = x;
          this.dataset.offsetY = y;
        },
      },
    },
  };

  function clickItem(e) {
    e.stopPropagation();
    if (e.target.classList.contains('item-download')) {
      if (!e.target.classList.contains('prepared')) {
        e.preventDefault();
        if (!e.target.classList.contains('preparing')) {
          e.target.classList.add('preparing');
          this.byteSource.getURL().then(function(url) {
            e.target.classList.remove('preparing');
            e.target.classList.add('prepared');
            e.target.href = url;
            e.target.click();
          });
        }
      }
      return;
    }
    if (this.classList.contains('has-subitems')) {
      if (this.classList.toggle('open')) {
        this.dispatchEvent(new Event(itemObjectModel.EVT_POPULATE));
      }
    }
  }

  Object.defineProperties(itemObjectModel, {
    EVT_POPULATE: {value:'item-populate'},
  });

  return itemObjectModel;

});
<|endoftext|>


<commit_before>var templates = [
    "root/externallib/text!root/plugins/mootes14/theme.css",
    "root/externallib/text!root/plugins/mootes14/login.html",
    "root/externallib/text!root/plugins/mootes14/program.html"
];

define(templates, function (theme, loginForm, program) {
    var plugin = {
        settings: {
            name: "mootes14",
            type: "general",
            menuURL: "#mootes14",
            icon: "plugins/events/icon.png",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["mootes14", "show_program", "showProgram"]
        ],

        showProgram: function() {
            var tpl = {};
            var html = MM.tpl.render(program, tpl);
            MM.panels.show('center', html, {title: MM.lang.s("mootes14")});
        }
    };

    // Inject allways our custom theme.
    $("#mobilecssurl").html(theme);

    // Replace the sign-up form with our custom template.
    $("#add-site_template").html(loginForm);

    // Automatically use the URL of the moodle moot without further checks.
    MM.checkSite = function(e) {
        MM.addSite(e);
    };

    // Inject allways our custom theme.
    MM.loadCachedRemoteCSS = function(e) {
        $("#mobilecssurl").html(theme);
    };

    // Do not display the manage accounts page.
    MM._displayManageAccounts = function() {
        MM._displayAddSite();
    };

    MM.registerPlugin(plugin);

});<commit_msg>mis cambios<commit_after>var templates = [
    "root/externallib/text!root/plugins/mootes14/login.html",
    "root/externallib/text!root/plugins/mootes14/program.html"
];

define(templates, function (loginForm, program) {
    var plugin = {
        settings: {
            name: "mootes14",
            type: "general",
            menuURL: "#mootes14",
            icon: "plugins/events/icon.png",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["mootes14", "show_program", "showProgram"]
        ],

        showProgram: function() {
            var tpl = {};
            var html = MM.tpl.render(program, tpl);
            MM.panels.show('center', html, {title: MM.lang.s("mootes14")});
        }
    };

    // Replace the sign-up form with our custom template.
    $("#add-site_template").html(loginForm);

    // Automatically use the URL of the moodle moot without further checks.
    MM.checkSite = function(e) {
        MM.addSite(e);
    };

    // Do not display the manage accounts page.
    MM._displayManageAccounts = function() {
        MM._displayAddSite();
    };

    MM.registerPlugin(plugin);

});<|endoftext|>


<commit_before>// A robot is located at the top-left corner of a m x n grid.

// The robot can only move either down or right at any point in time.
// The robot is trying to reach the bottom-right corner of the grid.

// How many possible unique paths are there?

// Note: m and n will be at most 100.

// Input: Integer, Integer
// ex. 2, 3
// ex. 3, 3

// Output: Integer
// ex. 3
// ex. 6
<commit_msg>add solution<commit_after>// A robot is located at the top-left corner of a m x n grid.

// The robot can only move either down or right at any point in time.
// The robot is trying to reach the bottom-right corner of the grid.

// How many possible unique paths are there?

// Note: m and n will be at most 100.

// Input: Integer, Integer
// ex. 2, 3
// ex. 3, 3

// Output: Integer
// ex. 3
// ex. 6

var uniquePaths = function(m, n) {
    var map = [];
    for(var i = 0; i < m; i++) {
        map[i] = [];
        for (var j = 0; j < n; j++) {
            map[i][j] = 0;
        }
    }
    for(i = 0; i<m; i++){
        map[i][0] = 1;
    }
    for(j= 0; j<n; j++){
        map[0][j] = 1;
    }
    for(i = 1; i<m; i++){
        for(j = 1; j<n; j++){
            map[i][j] = map[i-1][j]+map[i][j-1];
        }
    }
    return map[m-1][n-1];
};
<|endoftext|>


<commit_before>process.stdin.resume();process.stdin.setEncoding('utf8');m="\n";c="";o=process.stdout;function P(h,f){r=s.split("").reverse().join("");if(s<=r){u=0;v=1;while((v=h.indexOf(r,v+1))>0)u++;if(u>0){if(s===r){if(u%2==1){if (c===""||c>r)c=r;u--;}u/=2;}for(i=0;i<u;i++)o.write(f?s:r);}}}process.stdin.on('data',function(h){j=0;k=h.indexOf(m);n=h.substring(j,k)-0;j=k+1;k=h.indexOf(m,j);l=k-j;k=h.indexOf(m,k+1);q=k-j-l;y=h.substring(j,j+q);x=y;for(i=0;i<n;i++){k=j+i*q;t=h.substring(k,k+l);if(t<y) y=t;if(t>x) x=t;}s=y;for(;;){P(h,1);if(s===x) break;r=s;s=x;for(i=0;i<n;i++){k=j+i*q;t=h.substring(k,k+l);if(t>r&&t<s) s=t;}}o.write(c);s=x;for(;;){P(h,0);if(s===y)break;r=s;s=y;for(i=0;i<n;i++){k=j+i*q;t=h.substring(k,k+l);if(t>s&&t<r)s=t;}}});
<commit_msg>俺が馬鹿だった<commit_after>process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function (chunk) {
    function strrev(s) { return s.split("").reverse().join(""); }
    var w = chunk.toString().trim().split('\n');
    var n = parseInt(w[0]);
    var s = "", c = "";
    w.sort();
    for (var i = 1; i <= n; i++) {
        if (w[i] === "") {
            continue;
        }
        var r = strrev(w[i]);
        var j = w.indexOf(r, i + 1);
        if (j > 0 && j != i) {
            s += w[i];
            w[j] = "";
        } else if (w[i] === r && (c === "" || c > r)) {
            c = r;
        }
    }
    console.log(s+c+strrev(s));
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';

import { Col, Input, Row, Spin } from 'antd';
import MovieInfoModal from './MovieInfoModal';

import { emitAPIRequest } from '../../actions/api.actions';

class MoviesList extends Component {

    constructor(props){
        super(props);

        this.state = {
            currentSearchString: "",
            infomodalIsVisible: false,
            infomodalMovie: {},
            isLoadingMovies: false,
            movies: []
        };
    }

    componentWillMount(){
        this._getShows();
    }

    _getShows(){
        const { emitAPIRequest } = this.props;

        this.setState({
            isLoadingMovies: true
        });

        emitAPIRequest("movies.movies.get_all", {}, this._moviesReceived.bind(this), false);
    }

    _moviesReceived(movies){
        let processedMovies = [];
        movies.forEach((movie)=>{
            movie['is_visible'] = true;
            movie['searchable_text'] = this._prepStringForFilter(movie.title);
            processedMovies.push(movie);
        });
        this.setState({
            isLoadingMovies: false,
            movies: processedMovies
        });
    }

    _handleClickMovie(movie){
        this.setState({
            infomodalIsVisible: true,
            infomodalMovie: movie,
        });
    }

    _handleCloseModal(shouldReload = false){

        this.setState({
            infomodalIsVisible: false,
            infomodalMovie: {}
        });

        if(shouldReload){
            this._getShows();
        }
    }

    _handleChangeFilter(evt){
        const { movies } = this.state;

        const currentSearchString = evt.currentTarget.value;
        const filterText = this._prepStringForFilter(currentSearchString);

        let filteredMovies = [];
        movies.forEach((movie)=>{
            if(filterText === ""){
                movie.is_visible = true;
            } else {
                movie.is_visible = true;
                if(movie.searchable_text.search(filterText) === -1){
                    movie.is_visible = false;
                }
            }
            filteredMovies.push(movie);
        });

        this.setState({
            currentSearchString,
            movies: filteredMovies
        });
    }

    _prepStringForFilter(title){
        const lowerTitle = title.toLowerCase();
        return lowerTitle.replace(/[^a-z0-9]/g, "");
    }

    _buildMovieList(){
        const { movies } = this.state;

        let movieList = [];
        movies.forEach((movie)=>{
            if(movie.is_visible){
                const details = <Col
                                    key={movie.id}
                                    className="ib-movies-thumbnail-box"
                                    span={4}>

                                </Col>;
                movieList.push(details);
            }
        });

        return movieList;
    }

    render() {
        const {
            currentSearchString,
            infomodalIsVisible,
            infomodalMovie,
            isLoadingMovies
        } = this.state;

        let content = "";
        if(isLoadingMovies){
            content = <Spin />;
        } else {
            content = this._buildMovieList();
        }

        return (

                <Row>

Movies

                    <Input.Search
                        autoFocus
                        value={currentSearchString}
                        onChange={this._handleChangeFilter.bind(this)}
                        style={{ width: 400 }}
                        onSearch={this._handleChangeFilter.bind(this)}
                    />
                </Row>
                <Row>
                    {content}
                </Row>
                <MovieInfoModal
                    onClose={this._handleCloseModal.bind(this)}
                    movie={infomodalMovie}
                    isVisible={infomodalIsVisible}
                />

        );
    }
}

const mapStateToProps = (state)=>{
    return {};
}

const mapDispatchToProps = (dispatch) => {
    return {
        emitAPIRequest: (endpoint, params, callback, shouldDispatch)=>dispatch(emitAPIRequest(endpoint, params, callback, shouldDispatch))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(MoviesList);<commit_msg>Added play button to the movie list.<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';

import { Col, Input, Row, Spin } from 'antd';
import MovieInfoModal from './MovieInfoModal';

import { emitAPIRequest } from '../../actions/api.actions';

import PlayButton from '../PlayButton';
class MoviesList extends Component {

    constructor(props){
        super(props);

        this.state = {
            currentSearchString: "",
            infomodalIsVisible: false,
            infomodalMovie: {},
            isLoadingMovies: false,
            movies: []
        };
    }

    componentWillMount(){
        this._getShows();
    }

    _getShows(){
        const { emitAPIRequest } = this.props;

        this.setState({
            isLoadingMovies: true
        });

        emitAPIRequest("movies.movies.get_all_with_file_info", {}, this._moviesReceived.bind(this), false);
    }

    _moviesReceived(movies){
        let processedMovies = [];
        movies.forEach((movie)=>{
            movie['is_visible'] = true;
            movie['searchable_text'] = this._prepStringForFilter(movie.title);
            processedMovies.push(movie);
        });
        this.setState({
            isLoadingMovies: false,
            movies: processedMovies
        });
    }

    _handleClickMovie(movie){
        this.setState({
            infomodalIsVisible: true,
            infomodalMovie: movie,
        });
    }

    _handleCloseModal(shouldReload = false){

        this.setState({
            infomodalIsVisible: false,
            infomodalMovie: {}
        });

        if(shouldReload){
            this._getShows();
        }
    }

    _handleChangeFilter(evt){
        const { movies } = this.state;

        const currentSearchString = evt.currentTarget.value;
        const filterText = this._prepStringForFilter(currentSearchString);

        let filteredMovies = [];
        movies.forEach((movie)=>{
            if(filterText === ""){
                movie.is_visible = true;
            } else {
                movie.is_visible = true;
                if(movie.searchable_text.search(filterText) === -1){
                    movie.is_visible = false;
                }
            }
            filteredMovies.push(movie);
        });

        this.setState({
            currentSearchString,
            movies: filteredMovies
        });
    }

    _prepStringForFilter(title){
        const lowerTitle = title.toLowerCase();
        return lowerTitle.replace(/[^a-z0-9]/g, "");
    }

    _buildMovieList(){
        const { movies } = this.state;

        let movieList = [];
        movies.forEach((movie)=>{
            if(movie.is_visible){
                console.log(movie);
                const playButton = this._buildPlayButton(movie);
                const details = <Col
                                    key={movie.id}
                                    className="ib-movies-thumbnail-box"
                                    span={4}>

                                        {playButton}

                                </Col>;
                movieList.push(details);
            }
        });

        return movieList;
    }

    _buildPlayButton(movie){
        const { directories } = this.props;
        if(movie.file_info.hasOwnProperty('id')){
            const fullPath = directories.Movies + "/" + movie.file_info.subpath;
            return <PlayButton filename={movie.file_info.filename} fullPath={fullPath} />;
        }
        return "";
    }

    render() {
        const {
            currentSearchString,
            infomodalIsVisible,
            infomodalMovie,
            isLoadingMovies
        } = this.state;

        let content = "";
        if(isLoadingMovies){
            content = <Spin />;
        } else {
            content = this._buildMovieList();
        }

        return (

                <Row>

Movies

                    <Input.Search
                        autoFocus
                        value={currentSearchString}
                        onChange={this._handleChangeFilter.bind(this)}
                        style={{ width: 400 }}
                        onSearch={this._handleChangeFilter.bind(this)}
                    />
                </Row>
                <Row>
                    {content}
                </Row>
                <MovieInfoModal
                    onClose={this._handleCloseModal.bind(this)}
                    movie={infomodalMovie}
                    isVisible={infomodalIsVisible}
                />

        );
    }
}

const mapStateToProps = (state)=>{
    const { settings } = state;
    return {
        directories: settings.settings.directories
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        emitAPIRequest: (endpoint, params, callback, shouldDispatch)=>dispatch(emitAPIRequest(endpoint, params, callback, shouldDispatch))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(MoviesList);<|endoftext|>