...
error_output = error_output + errorindex[i] + "\n"
}
}
////////////////////////////
// 5.2.2. VALID OUTPUT
//
// This is the output if no errors were found
// Output if no errors
if (error_output == "") {
// Output message:
// 1st line: Job title in uppercase, start-time - end-time, date (work day as a string)
// 2nd line: Total working hours
// 3rd line: Payment per hour
// 4th line: Total payment of the work
// 5th line: Job description (details)
// 6th line: Separator
//
outputmsg = "" + outputparts[0] + "
\n"
+ "Total work hours " + getday[3] + " " + worktime[3] + ".\n"
+ "
Work price per hour is " + outputparts[1] + " € on " + getday[0] + ".\n"
+ "
Work total price is " + outputparts[2] + " €.\n"
+ "
Description:
" + outputparts[3] + "\n"
+ "
----------------------------
";
////////////////////////////
// 5.2.2. INVALID OUTPUT
//
// This is the output if errors were found
// Output if errors
} else {
// Error output
// Replace all new line symbols with
tags (globally in this scope)
outputmsg = "ERROR:
" + error_output.replace(/\n/g,"
") + "
----------------------------
"
}
// Get the previous value + add new value
document.getElementById("output").innerHTML += outputmsg;
// Throw all found error messages into console if error_output is not ""
if (error_output != "") {
throw new Error("\n" + error_output); // Stop javascript code execution here
}
}
//-------------
// ERROR MESSAGE STRINGS
//
// Error message definitions are as follows:
/*
errorindex[0] = Length of date string is incorrect
errorindex[1] = Input date string is in incorrect format
errorindex[2] = Accepted day value range is 1-31. User input value is 'input_dd'
errorindex[3] = Accepted month value range is 1-12. User input value is 'input_mm'
errorindex[4] = Input date value can't exceed the current day
errorindex[5] = Input date is too far away in the past. Minimum limit is 'minimumdate'
errorindex[6] = Length of clock start time input is incorrect
errorindex[7] = Length of clock end time input is incorrect
errorindex[8] = Hour value of start clock input string is not in range 0-23
errorindex[9] = Hour value of end clock input string is not in range 0-23
errorindex[10] = Minute value of start clock input string is not in range 0-59 (value is 'clock_min')
errorindex[11] = Minute value of end clock input string is not in range 0-59 (value is 'clock_min')
errorindex[12] = Erroneous start clock input value (value is 'clock')
errorindex[13] = Erroneous end clock input value (value is 'clock')
errorindex[14] = Starting time is greater than or equal to ending time
*/
// 5.2. END - HTML OUTPUT MESSAGE
////////////////////////////////////////////////////////
// 5.3. BEGIN - OUTPUT MESSAGE PARTS
//
// Variables defined in this part retrieve their values from child functions
function outputMsgParts() {
//-------------
// 5.3.1. JOB TITLE
// Generate uppercase job title with clock + day times in brackets
var job = processJobTitle() + "
clock " + worktime[0] + " - " + worktime[1] + ", " + getday[2] + " (" + getday[0] + ")";
//-------------
// 5.3.2. PAYMENT-BY-DAY
// Get payment-by-day value (72 or 48 euros)
var payment_by_day = getday[1].toFixed(2).replace(".",",");
//-------------
// 5.3.3. TOTAL PAYMENT
// Calculate total payment
var totalpayment = (worktime[2] * getday[1]).toFixed(2).replace(".",",");
//-------------
// 5.3.4. JOB DESCRIPTION
// Parse line breaks for output as described here:
// https://stackoverflow.com/questions/863779/javascript-how-to-add-line-breaks-to-an-html-textarea
//
var description = document.getElementById("description").value.replace(/\r?\n/g, '
');
// If "Kuvaus" text area is empty write the following string as output
if (description == "") {
description = "No description";
}
//-------------
// Return of outputMsgParts() function
return [job, payment_by_day, totalpayment, description];
}
// 5.3. END - OUTPUT MESSAGE PARTS
////////////////////////////////////////////////////////
// 5.4. BEGIN - DAY STRING
//
function whichDay() {
// Set new variable for parseDayString() function
var parseday = parseDayString();
///////////////////////////////
// 5.4.1. WORK DAY DEFINITION
//
// Retrieve needed values from parseday variable (function parseDayString())
// Define work day as a human-readable string
// Define work day as an index value
// Returns day-based index value
var datevalue = new Date(parseday[0]).getDay();
// Define day-based index value strings
var weekday = new Array(7);
weekday[0] = "Sunday";
weekday[1] = "Monday";
weekday[2] = "Tuesday";
weekday[3] = "Wednesday";
weekday[4] = "Thursday";
weekday[5] = "Friday";
weekday[6] = "Saturday";
// Workday human-readable string
var workday = weekday[datevalue];
///////////////////////////////
// 5.4.2. PAYMENT MULTIPLIER
//
// Define payment-per-hour multiplier according to parsed day index value
var payment;
// If is sunday
if (datevalue == 0) {
payment = 72; // Set payment multiplier
// else it's not sunday
} else {
payment = 48; // Set payment multiplier
}
///////////////////////////////
// 5.4.3. BEGIN - PARSE DAY STRING
//
// Child function for whichDay() parent function
//
// Does as follows:
//
// A) Check input day string formatting
// B) Prevent future days and limit minimum values
// C) Handle erroneous situations
//
// D 1) Return checked day, month and year values (yyyy,mm,dd)
// D 2) Return user Input Date string
// D 3) Return string which defines is it today (on tänään) or was in the past (oli)
function parseDayString() {
var be_str;
// User input from HTML document
var inputdaystr = document.getElementById("job_day").value;
// Consider this as a string for further .substr() method
var minimumdate = "19000101"; // Min. threshold. Day 01.01.1900
///////////////
// 5.4.3.1. CURRENT DATE VALUE
//
// Set maximum accepted date value from current day
var currentday_int = cur_datefield[3];
///////////////
// 5.4.3.2. SPLIT INPUT DATE
//
// Length & initial format check for input date value
// Date string must be 8-10 chars long
if (inputdaystr.length >= 8 || inputdaystr.length <= 10) {
// Do format checking later, just get the array values now.
// Create a new array, size of 3, fill with zeros
var split_date = new Array(3).fill(0);
// Split date string into another new array. Matching pattern is , . ; or :
// First three values
var daysplitter = inputdaystr.split(/[,.;:]/, 3);
// Fill split_date array with the values of splitter array
for (var i = 0; i < daysplitter.length; i++) {
split_date[i] = daysplitter[i];
}
/* The logic and reason for above event "Create two arrays, fill the other with the values
* of the second one" is that if we create just a new array with split() method and user
* input consist of 2 values, the size of this new array would be 2. As we continue
* executing this javascript code, the code assumes that all 3 array indexes have been
* defined for split_date but that's not always the case (user error). Creating a new zero-filled
* array is kind of "safe buffer" here, which we rely our code on. It is safer add day values
* with split() method to this already existing array. Otherwise we may end up to situations
* where no all expected split_date array indexes have not been defined and the code may misbehave.
About empty values:
Null (empty) array value is not considered as an empty string but actually as 0 value in javascript comparisons by default.
For example, without any input value (split_date[1] is NULL) the null value is treated like this:
split_date[1] == ""; // Returns false -> null is not an empty string
split_date[1] == 0; // Returns true -> null is number 0
We replace empty array value (null) with an empty string in the following loop. (read: replace method works only for 'null' pattern)
*/
// Parse all 'split_date' array indexes
for (var j = 0; j < split_date.length; j++) {
// Returns NaN if contains any letters or symbols
parseInt(split_date[j]);
// Check for null, undefined, NaN etc values as explained here:
// https://stackoverflow.com/questions/6003884/how-do-i-check-for-null-values-in-javascript
//
if (!split_date[j]) {
// Based on the previous statement, replace all null values with empty strings
split_date[j] = split_date[j].replace(null,"");
}
}
// We may want to add "0" in cases the user input is in range 1-10.
// Day value
var input_dd;
if (split_date[0] < 10 && split_date[0] > 0 && split_date[0].length != 2) {
input_dd = "0" + split_date[0];
} else {
input_dd = split_date[0];
}
// Month value
var input_mm;
if (split_date[1] < 10 && split_date[1] > 0 && split_date[1].length != 2) {
input_mm = "0" + split_date[1];
} else {
input_mm = split_date[1];
}
// Year value
var input_year;
// If user input is between 10-99, add prefix 20 so that output follows pattern 2012, 2035 etc.
//
if (split_date[2].length == 2 && split_date[2] >= 10 && split_date[2] <= 99) {
input_year = "20" + split_date[2];
// Years between 2000-2009
} else if (split_date[2].length == 1 && split_date[2] < 10) {
input_year = "200" + split_date[2];
} else {
input_year = split_date[2];
}
// If length of date input string is not 8-10
} else {
errorindex[0] = "Length of the input date value is invalid";
}
///////////////
// 5.4.3.2. CHECK INPUT DATE
//
// Check that input day, month and year values numbers
// Check that characters 2 and 5 are . / : ;
switch(true) {
// date string does not consist of 3 different array values (dd.mm.yyyy)
case split_date.length != 3:
//Day:
case input_dd.length != 2: // size of the first part of the date string (day) is not 2 (dd)
case (input_dd.match(/^[0-9]+$/) == null): // it doesn't consist only on chars 0-9
//Month:
case input_mm.length != 2: // size of the second part of the date string (month) is not 2 (mm)
case (input_mm.match(/^[0-9]+$/) == null): // it doesn't consist only on chars 0-9
//Year:
case input_year.length != 4: //size of the third part of the date string (year) is not 4 (yyyy)
case (input_year.match(/^[0-9]+$/) == null): // it doesn't consist only on chars 0-9
errorindex[1] = "Input date string (" + inputdaystr + ") is not in valid format (dd.mm.yyyy)";
break;
default: // None of the cases above match
// Rearrange splitted date string
// This is considered as a typeof "string" by default so these values are just stacked together
// Example return value: 20150812 (yyyymmdd)
//
var inputday_int = input_year + input_mm + input_dd;
//Is this current day or a day in the past?
if (inputday_int == currentday_int) {
be_str = "is today";
} else {
be_str = "was";
}
//----------------------
// 5.4.3.2.1. CHECK RANGE OF INPUT DATE VALUES
//
// Check range of day, month and year values
// For cases 3 and 4, the date value syntax is yyyymmdd (year-month-day)
// Comparison example: 20171609 < 19000101
// CASE 1
// Input day value range is not in range 1-31
if (input_dd < 1 || input_dd > 31) {
errorindex[2] = "Accepted day value range is 1-31. Your input value is " + input_dd;
}
// CASE 2
// Input month value range is not in range 1-12
if (input_mm < 1 || input_mm > 12) {
errorindex[3] = "Accepted month value range is 1-12. Your input value is " + input_mm;
}
// CASE 3
// Input date is more than the current date
// Check for erroneous day and month values (only "no errors" situation accepted)
if (currentday_int < inputday_int && errorindex[2] == 0 && errorindex[3] == 0) {
errorindex[4] = "Input date value can't exceed the current day";
}
// CASE 4
// Input date value is less than the minimum date
// Convert minimumdate to integer
// Check for erroneous day and month values (only "no errors" situation accepted)
if (inputday_int < Number(minimumdate) && errorindex[2] == 0 && errorindex[3] == 0) {
// Here minimumdate is considered as a string again
errorindex[5] = "Input date is too far away in the past. Minimum limit is " + minimumdate.substr(6, 2) + "." + minimumdate.substr(4, 2) + "." + minimumdate.substr(0, 4) + ".";
}
}
///////////////////////////////
// Return for parseDayString() child function. Return date string in dd.mm.yyyy format
return [(input_year + "," + input_mm + "," + input_dd),input_dd + "." + input_mm + "." + input_year,be_str];
}
// 5.4.3. END - PARSE DAY STRING
//////////////////////////////
// Return for whichDay() function
return [workday,payment,parseday[1],parseday[2]];
}
// 5.4. END - DAY STRING
////////////////////////////////////////////////////////
// 5.5. BEGIN - WORKING TIME
//
// Clock times (start & end)
// Syntax is hh:mm (hours:minutes)
function workingTime() {
//////////////////////////////
// 5.5.1. VARIABLES
//
// Define global variables inside workingTime() function
// These variables can be called by any child function in workingTime() function's scope
// These variables can't be called from outside workingTime() parent function
var clock;
// User input values for start & end times
var begin = document.getElementById("clock_start").value;
var end = document.getElementById("clock_end").value;
//////////////////////////////
// 5.5.2. PARSE CLOCK STRING LENGTHS
//
// Make sure lengths of clock start & end times are correct
// If input length is 4 AND
// If the first character is a number AND
// If the second char is ": . , or ;" THEN
// add 0 prefix.
//
// This is usually a case if user puts a clock value like 8:43 etc.
//
if (begin.length == 4 && begin.charAt(0).match(/[0-9]/) && begin.charAt(1).match(/[:.,;]/)) {
begin = "0" + begin; // typeof is "String"
}
if (end.length == 4 && end.charAt(0).match(/[0-9]/) && end.charAt(1).match(/[:.,;]/)) {
end = "0" + end; // typeof is "String"
}
// If user types in just one number (or two), we assume these numbers mean "hours"
//
if (begin.length == 1 && begin.charAt(0).match(/[0-9]/)) {
begin = "0" + begin + ":00"; // typeof is "String"
} else if (begin.length == 2 && begin.substr(0, 2).match(/[0-9]/)) {
begin = begin + ":00"; // typeof is "String"
}
if (end.length == 1 && end.charAt(0).match(/[0-9]/)) {
end = "0" + end + ":00"; // typeof is "String"
} else if (end.length == 2 && end.substr(0, 2).match(/[0-9]/)) {
end = end + ":00"; // typeof is "String"
}
//////////////////////////////
// 5.5.3. SET START & END TIMES
//
// Define correct clock values for start & end times
// Return hour and minute values for both times
//----------------------
// 5.5.3.1.
// Parse start time
clock = begin; // Set clock variable as 'begin'
var begintime = checkTimes(); // Define begintime variable
// Output as follows:
// begintime[0] = begin hour
// begintime[1] = begin minutes
// begintime[2] = begin time error flag
//----------------------
// 5.5.3.2.
// Parse end time
clock = end; // Set clock variable as 'end'
var endtime = checkTimes(); // Define endtime variable
// Output as follows:
// endtime[0] = end hour
// endtime[1] = end minutes
// endtime[2] = end time error flag
//////////////////////////////
// 5.5.4. BEGIN - START & END CLOCK TIMES FORMATTING
//
// Parse both start & end time values
function checkTimes() {
var clockerror = false; // Default value each time the function is called
// INITIAL LENGTH CHECK
// Clock input values must be 5 characters long before parsing them
if (clock.length != 5) {
if (clock == begin) {
clockerror = true;
errorindex[6] = "Length of the clock start time value is invalid";
}
if (clock == end) {
clockerror = true;
errorindex[7] = "Length of the clock end time value is invalid";
}
}
// Split checked clock input value hh:mm into two parts
//
var clock_hour = clock.substr(0, 2); // Start from index value 0, length is 2
var clock_min = clock.substr(3, 2); // Start from index value 3, length is 2
var clock_separator = clock.charAt(2); // Separator character between hh:mm
// FIRST CHECK
// If both of splitted values contain only numbers and the symbol between them is :
//
if (!isNaN(clock_hour) && !isNaN(clock_min) && clock_separator.match(/[:,.;]/) ) {
// SECOND CHECK PART 1 - HOURS
// If the first part of clock input value is outside of range 0 and 23
//
if (Number(clock_hour) < 0 || Number(clock_hour) > 23) {
// Distinguish error messages
//
// If we use same index value for both begin & end clock times and both of them are incorrect,
// only the latter error would be shown in console log. This is because the previous string (error in begin value) has been overwritten by the latter one (error in end value)
//
if (clock == begin) {
clockerror = true;
errorindex[8] = "Hour value of the start clock input string is not in range 0-23 (value is " + clock_hour + ")";
}
if (clock = end) {
clockerror = true;
errorindex[9] = "Hour value of the end clock input string is not in range 0-23 (value is " + clock_hour + ")";
}
}
// SECOND CHECK PART 2 - MINUTES
// If the second part of clock input value is outside of range 0 and 59
//
if (Number(clock_min) < 0 || Number(clock_min) > 59) {
// Distinguish error messages
//
// If we use same index value for both begin & end clock times and both of them are incorrect,
// only the latter error would be shown in console log. This is because the previous string (error in begin value) has been overwritten by the latter one (error in end value)
//
if (clock == begin) {
clockerror = true;
errorindex[10] = "Minute value of the start clock input string is not in range 0-59 (value is " + clock_min + ")";
}
if (clock == end) {
clockerror = true;
errorindex[11] = "Minute value of the end clock input string is not in range 0-59 (value is " + clock_min + ")";
}
}
// ELSE CONDITION FOR FIRST CHECK
// If there is an general error in formatting of start or end clock string
//
} else {
// Distinguish error messages
//
// If we use same index value for both begin & end clock times and both of them are incorrect,
// only the latter error would be shown in console log. This is because the previous string (error in begin value) has been overwritten by the latter one (error in end value)
//
if (clock == begin) {
clockerror = true;
errorindex[12] = "Erroneous start clock value (value is " + clock + ")";
}
if (clock == end) {
clockerror = true;
errorindex[13] = "Erroneous end clock value (value is " + clock + ")";
}
}
//----------------------
// These are return values generated by child function checkTimes()
return [Number(clock_hour), Number(clock_min), clockerror];
}
// 5.5.4. END - START & END CLOCK TIMES FORMATTING
//////////////////////////////
// 5.5.5. CALCULATE WORKING TIME
//
// Calculate working time from checked and parsed user input
// If both start and end clock times are valid (don't return error)
if (begintime[2] == false && endtime[2] == false) {
var begin_min_total = (begintime[0] * 60) + begintime[1];
var end_min_total = (endtime[0] * 60) + endtime[1];
var totaltime = end_min_total - begin_min_total; // In minutes
var totaltime_in_hours = (totaltime / 60).toFixed(2); // Force two decimals in any case for totaltime_hoursonly variable
var totaltime_hoursonly = totaltime_in_hours.substr(0, totaltime_in_hours.indexOf("."));
// Print this error message only if start clock time is greater than or equal to ending clock time
//
if (begin_min_total >= end_min_total) {
errorindex[14] = "Starting time can't be greater than or equal to the ending time";
}
//////////////////////////////
// 5.5.6. PARSE WORKING TIME TEXT STRING
var totaltime_str;
// If working time exceeds 60 minutes (hour), then we redefine how the total time should be displayed in HTML output.
//
if (totaltime >= 60 && (totaltime % 60 !== 0)) {
totaltime_str = totaltime_hoursonly + " hours and " + ((totaltime_in_hours - totaltime_hoursonly) * 60).toFixed(0) + " minutes";
} else if (totaltime < 60) {
totaltime_str = totaltime + " minutes";
} else if (totaltime == 60) {
totaltime_str = totaltime_hoursonly + " hour";
} else {
totaltime_str = totaltime_hoursonly + " hours";
}
}
//////////////////////////////
// Return values of workingTime() function
return [begin,end,totaltime_in_hours,totaltime_str];
}
// 5.5. END - WORKING TIME
////////////////////////////////////////////////////////
}
// 5. END - WRITE OUTPUT MESSAGE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////