Thanks :-) Looks better now. I did a first round of proofreading and added some comments, mainly nits.
I am not sure if we should show the 'Scheduling' in the main menu, which has already many entrys. Since both links in that view needs logging in, i think showing 'Scheduling' should also be only shown if a user has logged in? Not sure though. I have no time left for now... Diff comments: > > === added file 'media/css/scheduling.css' > --- media/css/scheduling.css 1970-01-01 00:00:00 +0000 > +++ media/css/scheduling.css 2018-01-17 19:07:26 +0000 > @@ -0,0 +1,230 @@ > +/* Main */ > +.main-choices { > + display: flex; > + justify-content: space-around; > + margin-top: 40px; > +} > + > +.main-choices button { > + padding: 40px; > + font-size: 1em; > +} > + > +/* actual scheduling module */ > +#calandar { shouldn't this be calender? > + display: flex; > + justify-content: center; > +} > + > +/* Hours of each day display for the user */ > +.day { > + margin-bottom: 15px; > + background-image: url("../img/black50.png"); > + padding: 10px; > +} > + > +.day-title h3{ > + color:white; Please add a space after the colon. Also on some places below. > + font-weight: bold; > + margin-top: 0; > +} > + > +.hours-wrapper { > + display: flex; > +} > + > +.hours-title-wrapper { > + display: flex; > + width: 100%; > +} > + > +.hours-title-wrapper p{ > + width: 42px; > + user-select: none; > +} > + > +.hours { > + border: 1px solid #909090; > + height:40px; > + width: 40px; > + cursor: pointer; > +} > + > +.hours.selected { > + background-color: #118811; > +} > + > +.hours:hover { > + background-color: lightgreen; > +} > + > +.hidden-hour { > + height: 40px; > + width: 40px; > +} > + > + > +/* Display for the other users */ > +#other-users-wrapper { > + display: flex; > + flex-wrap: wrap; > + padding: 1%; > +} > + > +.other-user-div{ > + width: 29%; > + border: 1px solid black; > + background-image: url("../img/black50.png"); > + padding: 11px; > + margin-top: 5px; > + margin-right: 5px; > +} > + > +.other-user-div > div { > + margin-top: 14px; > +} > + > +.other-user-div .hours{ > + height: 10px; > + width: 20px; > +} > + > +.other-user-div .title { > + height: 36px; > + display: flex; > + justify-content: space-between; > + margin-top: 0; > +} > + > +.other-user-div .title p { > + font-size: 20px; > + margin-top: 5px; > + text-transform: capitalize; > + white-space: nowrap; > + overflow: hidden !important; > + text-overflow: ellipsis; > + color: #feea72;; > + font-weight: bold; > +} > + > +.other-user-div .title button { > + min-width: 114px; > +} > + > +.other-user-div .title button:hover { > + background-color:#118811;; Double semi-colon > +} > + > +.other-user-div .title img { > + margin-right: 4px; > +} > + > + > +/* btn */ > +#validate-btn { > + margin-top: 10px; > +} > + > + > +/**************************/ > +/* CSS for the datepicker */ > +/**************************/ > + > +.ui-datepicker { > + background-image: url("../img/wood.png"); > + border: 1px solid black; > + border-radius: 4px; > + box-shadow: 4px 4px 4px 0px rgba(0, 0, 0, 0.7); > + padding: 0.8em; > + display: none; > +} > + > +.ui-datepicker-header { > + text-align: center; > + border: 1px inset darkgray; > + background-image: url("../img/black50.png"); > + margin-bottom: 5px; > +} > + > +.ui-datepicker-calendar { > + border: 1px inset darkgray; > + background-image: url("../img/black20.png"); > +} > + > +.ui-datepicker-prev { > + padding: 0 3em 0 0; > + cursor: pointer; > +} > + > +.ui-datepicker-next { > + padding: 0 0 0 3em; > + cursor: pointer; > +} > + > +.ui-widget-content { > + display: flex !important; > + justify-content: space-around; > + width: 540px !important; > +} > + > +.ui-datepicker-header { > + height: 42px; > + position: relative; > +} > + > +.ui-datepicker-title { > + position: absolute; > + transform: translateX(-50%); > + left: 50%; > + bottom: 0; > +} > + > + > + > + > +/********** clock **********/ > + > +#clocks-wrapper { > + display:flex; > + height: 210px; > + margin-top: 20px; > + background-image: url("../img/black50.png"); > +} > + > +#clocks-wrapper h1{ > + text-align: center; > +} > + > +svg.clock-svg { > + width: 150px; > + height: 150px; > +} > + > +.hour-area { > + stroke-linejoin:round; > + stroke-width:8; > + fill: #312925; > + stroke: black; > +} > + > +.hour-area.active { > + fill: #118811; > +} > + > + > +.number-wrapper { > + line-height:100%; > + fill: white; > + > + stroke:black; > + stroke-width:2.7; > +} > + > +.number { > + -inkscape-font-specification: "sans-serif"; > + font-family:"sans-serif"; > +} > + > +.circle { > + fill: #312925; > +} > \ No newline at end of file > > === added file 'media/js/scheduling.js' > --- media/js/scheduling.js 1970-01-01 00:00:00 +0000 > +++ media/js/scheduling.js 2018-01-17 19:07:26 +0000 > @@ -0,0 +1,296 @@ > +// global variables > +var lastSelectedDates = [] > + > +// Create calandar and return it as an object. Useful for callbacks. > +function createCalandar() { calander -> calendar? In both lines above. > + $('#scheduling-datepicker').multiDatesPicker({ > + dateFormat: "yy-mm-dd", > + showAnim: "slideDown", > + numberOfMonths: 3, > + minDate: 0, > + onSelect: function(date) { > + selectedDate = date; > + updateAvailableDate(date) > + }, > + }); > +} > + > + > +//Add warning in case of disparency between browser and profil timezone disparency -> difference? Please also add a space after // and the comments text. Also on some other places. This improves readability. > +function addTimeZoneWarningIfNeeded() { > + var userTimeZone = > document.getElementById('django-data').getAttribute('user-time-zone') > + var browserTimeZone = - new Date().getTimezoneOffset()/60 > + if ( browserTimeZone != userTimeZone) { > + document.getElementById('timezone-error').removeAttribute("hidden"); > + profilTime = document.getElementsByClassName('profil-time'); > + for (var element in profilTime) { > + profilTime[element].innerHTML = cleanAndAddSign(userTimeZone); > + } > + document.getElementById('browser-time').innerHTML = > cleanAndAddSign(browserTimeZone); > + } > +} > + > +function addPreviousDateFromUser(calendar) { > + //Populate the current date with already filled date by the user > + old_availabilities_string_JSON = > document.getElementById('django-data').getAttribute('day-to-fill') > + old_availabilities_list = > stringJSONtoJSList(old_availabilities_string_JSON) > + if (old_availabilities_list == "") { > + return; > + } > + dateToAdd = [] > + for (var date in old_availabilities_list) { > + // Extract day of the date. See format in view.py. > + dateString = old_availabilities_list[date].substring(1,11); > + if (!existInList(dateToAdd, dateString)){ > + dateToAdd.push(dateString) > + updateAvailableDate(dateString) > + } > + } > + $('#scheduling-datepicker').multiDatesPicker('addDates', dateToAdd); > + for (var date in old_availabilities_list) { > + // Extract hour of the date. See format in view.py. > + hourString = old_availabilities_list[date].substring(12,14); > + // Extract day of the date. See format in view.py. > + dateString = old_availabilities_list[date].substring(1,11); > + hourString = removeZeroIfUnderTen(hourString); > + displayHourForDate(dateString, hourString); > + } > +} > + > +function addOtherUsersAvailabilities() { > + //Populate the result area showing other users and their disponibilities > + if > (document.getElementById('django-data').getAttribute('users-to-fill')) { > + var otherPlayerAvailabilitiesJSON = > document.getElementById('django-data').getAttribute('users-to-fill'); > + otherPlayerAvailabilities = JSON.parse(otherPlayerAvailabilitiesJSON) > + } > + noOtherUser = true; > + for (var user in otherPlayerAvailabilities) { > + noOtherUser = false; > + dateList = otherPlayerAvailabilities[user] > + for (var date in dateList) { > + createUserDivOrUpdateIt(user, dateList[date]) > + } > + } > + if (noOtherUser) { > + > document.getElementById('no-user-to-display').removeAttribute("hidden"); > + } > +} > + > +function sendDataAsForm(calendar) { > + //Get informations from selected hours > + var selectedDates = $( "#scheduling-datepicker" ).multiDatesPicker( > "getDates" ); > + var selectedDatesList = []; > + for (var d in selectedDates) { > + //remove whitespace > + var dateID = 'day-' + selectedDates[d]; > + dateObj = document.getElementById(dateID); > + if (dateObj) { > + hoursList = dateObj.getElementsByClassName('hours'); > + selectedHours = dateObj.getElementsByClassName('selected'); > + if (selectedHours[0]){ > + for (var h in hoursList) { > + if (hasClass(hoursList[h] , 'selected')){ > + var hourAsDate = selectedDates[d] + "T" + > addZeroIfUnderTen(h); > + selectedDatesList.push(hourAsDate); > + } > + } > + } > + } > + } > + //Send informations to server > + console.log(selectedDatesList); > + post('.', selectedDatesList) > +} > + > +//Add or remove available dates in the ui. > +function updateAvailableDate(date) { > + newDateID = "day-" + date > + dateAlreadyExist = !!document.getElementById(newDateID); > + document.getElementById('second-step').removeAttribute('hidden'); > + if (dateAlreadyExist) { > + document.getElementById(newDateID).remove(); > + } else { > + var original = document.getElementById('day-template'); > + // We clone the date and fix different attributes > + var newDate = original.cloneNode(true); > + newDate.id = "day-" + date; > + newDate.removeAttribute("hidden"); > + var textDate = new Date(date); > + textDate = textDate.toDateString(); > + newDate.getElementsByClassName('day-title')[0].innerHTML = '<h3>' + > textDate + '</h3>'; > + //We add the listeners to each hours One for the click event, the > other for the hover when the mouse is pressed > + hoursObj = newDate.getElementsByClassName('hours'); > + for (var i = 0; i < hoursObj.length; i++) { > + hoursObj[i].addEventListener('click', updateHour, false); > + hoursObj[i].addEventListener("mouseover", function(e){ > + if(e.buttons == 1 || e.buttons == 3){ > + updateHour(e); > + } > + }) > + } > + //we look for the order the new date should be in > + daysList = > document.getElementById('days-wrapper').getElementsByClassName('days'); > + //We finally add the new date > + document.getElementById('days-wrapper').appendChild(newDate); > + } > + > +} > + > +function updateHour (event) { > + var div = (event.fromElement ? event.fromElement : event.currentTarget); > + isAlreadySelected = hasClass(div, 'selected'); > + if (isAlreadySelected) { > + div.className = 'hours'; > + } else { > + div.className += ' selected'; > + } > +} > + > +function displayHourForDate(date, hour, user) { > + var dateDivID = 'day-' + date > + if (user) { > + dateDivID = user + '-day-' + date > + } > + dateDiv = document.getElementById(dateDivID); > + > + // for the current user hours display > + hourDiv = dateDiv.getElementsByClassName('hours'); > + for (var hourInDiv in hourDiv) { > + if (hourInDiv == hour) { > + hourDiv[hourInDiv].className += ' selected'; > + } > + } > + > + // for the other users hours display (the svg clock) > + hoursArea = dateDiv.getElementsByClassName('hour-area'); > + for (var hourArea in hoursArea) { > + if (hourArea == hour) { > + hoursArea[hourArea].className.baseVal += ' active'; > + } > + } > +} > + > +function createUserDivOrUpdateIt(user, availTime) { > + if (!document.getElementById("user-" + user)){ > + var original = document.getElementById('other-user-template'); > + // We clone the date and fix different attributes > + var otherUser = original.cloneNode(true); > + otherUser.id = "user-" + user; > + otherUser.removeAttribute("hidden"); > + var jsEventHTML = "/messages/compose/" + user > + var imageHTML = '<img src="/wlmedia/forum/img/send_pm.png" alt="" > class="middle"><span class="middle">Send PM</span>' > + var userTitle = otherUser.children[0] > + var button = document.createElement("button"); > + button.innerHTML = imageHTML; > + button.onclick = function(){ > + window.location.href = '/messages/compose/' + user; > + } > + var usernameP = document.createElement('p') > + usernameP.innerHTML = user; > + userTitle.appendChild(usernameP) > + userTitle.appendChild(button) > + } else { > + otherUser = document.getElementById("user-" + user); > + } > + var dtavailTime = new Date(availTime + ":00:00"); > + //Remove timezone offset which js automatically add... > + js_offset = dtavailTime.getTimezoneOffset()/60 > + dtavailTime = dtavailTime.addHours(js_offset); > + textDate = dtavailTime.toDateString(); > + var dateFormated = dtavailTime.getFullYear() + "-" + > dtavailTime.getMonth() + '-' + dtavailTime.getDay() > + var availTimeFormated = dtavailTime.getHours() > + var originalDay = document.getElementById('other-day-template') > + if (!document.getElementById(user + "-day-" + dateFormated)) { > + var day = originalDay.cloneNode(true); > + day.id = user + "-day-" + dateFormated; > + day.removeAttribute("hidden"); > + day.getElementsByClassName('day-title')[0].innerHTML = '<h3>' + > textDate + '</h3>'; > + otherUser.appendChild(day); > + } > + document.getElementById('other-users-wrapper').appendChild(otherUser); > + displayHourForDate(dateFormated, availTimeFormated, user); > +} > + > +/*****************************/ > +/********* utilities *********/ > +/*****************************/ > +// read as "Stackoverflow coded this" > +function hasClass(element, cls) { > + return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; > +} > + > +//We need a custom function to submit a form because our data isn't formated > as a form. > +function post(path, params, method) { > + method = method || "post"; // Set method to post by default if not > specified. > + // The rest of this code assumes you are not using a library. > + // It can be made less wordy if you use one. > + var form = document.createElement("form"); > + form.setAttribute("method", method); > + form.setAttribute("action", path); > + for(var key in params) { > + var hiddenField = document.createElement("input"); > + hiddenField.setAttribute("type", "hidden"); > + hiddenField.setAttribute("name", key); > + hiddenField.setAttribute("value", params[key]); > + > + form.appendChild(hiddenField); > + } > + //CSRF token > + var csrf_div = document.createElement("div"); > + csrf_div.innerHTML = > document.getElementById('django-data').getAttribute('csrf-token') > + form.appendChild(csrf_div); > + document.body.appendChild(form); > + form.submit(); > +} > + > +function cleanJSONfromWhiteSpace(json) { > + var name, newName; > + for (var name in json) { > + // Get the name without spaces > + newName = name.replace(/ /g, ""); > + // If that's different... > + if (newName != name) { > + // Create the new property > + json[newName] = json[name]; > + // Delete the old one > + delete json[name]; > + } > + } > + return json; > +} > + > +function stringJSONtoJSList(json) { > + //removes brackets > + json = json.substring(1, json.length-1); > + var jsonList= json.split(",") > + for (var i in jsonList){ > + jsonList[i] = jsonList[i].replace(/\s/g, ''); > + } > + return jsonList > + > +} > + > +function addZeroIfUnderTen(number) { > + return ('0' + number).slice(-2) > +} > + > +function removeZeroIfUnderTen(number) { > + return parseInt(number, 10); > +} > + > +Date.prototype.addHours = function(h) { > + this.setTime(this.getTime() + (h*60*60*1000)); > + return this; > +} > + > +function existInList(list, value) { > + return list.indexOf(value) > -1 > +} > + > +function cleanAndAddSign(number) { > + if (number < 0) { > + return ' ' + parseInt(number) > + } else { > + return '+ ' + parseInt(number) > + } > +} > \ No newline at end of file > > === added file 'templates/wlscheduling/find.html' > --- templates/wlscheduling/find.html 1970-01-01 00:00:00 +0000 > +++ templates/wlscheduling/find.html 2018-01-17 19:07:26 +0000 > @@ -0,0 +1,37 @@ > + > + > +{% extends "wlscheduling/base.html" %} > +{% comment %} > + vim:ft=htmldjango > +{% endcomment %} > + > +{% block content %} > +<script type="text/javascript"> > +document.addEventListener('DOMContentLoaded', function(){ > + addTimeZoneWarningIfNeeded(); > + addOtherUsersAvailabilities(); > +}, false); > +</script> > + > +<div class="blogEntry"> > + <h1>Times other players are available</h1> > + {% include "wlscheduling/timezone-msg.html" %} > + <div id="other-users-wrapper"></div> > + <div id="no-user-to-display" hidden="hidden">Sorry currently there isn't > anybody who has scheduled a gaming session. Don't hesitate to signal to other > when you'll be available <a href="{% url 'scheduling_scheduling' > %}">here</a></div> > + > +</div> > + > +<!-- Templates for the js script --> > +{% include "wlscheduling/other-users-date.html" %} > + > +<div id="other-user-template" class="other-user-div" hidden="hidden"> > + <div class="title"></div> > +</div> > + > + > + Two empty lines. I think one empty line is enough? Same below. > +<!-- Div to send django data to js file --> > +<div id="django-data" user-time-zone="{{user.wlprofile.time_zone}}" > users-to-fill="{{other_users_availabilities}}"></div> > + > + > +{% endblock %} > \ No newline at end of file > > === added file 'templates/wlscheduling/scheduling.html' > --- templates/wlscheduling/scheduling.html 1970-01-01 00:00:00 +0000 > +++ templates/wlscheduling/scheduling.html 2018-01-17 19:07:26 +0000 > @@ -0,0 +1,70 @@ > + > +{% extends "wlscheduling/base.html" %} > +{% comment %} > + vim:ft=htmldjango > +{% endcomment %} > + > + > + > +{% block content %} > +<script type="text/javascript"> > +document.addEventListener('DOMContentLoaded', function(){ > + var calendar = createCalandar(); createCalander -> createCalendar? > + addTimeZoneWarningIfNeeded(); > + addPreviousDateFromUser(calendar); > + addOtherUsersAvailabilities(); > + > + //Validate btn > + document.getElementById('validate-btn').onclick = function () { > + sendDataAsForm(calendar); > + } > +}, false); > +</script> > + > +<h1>Multiplayer</h1> > + > +<div class="blogEntry"> > + <h2>Select the dates you'll be available on</h2> > + {% include "wlscheduling/timezone-msg.html" %} > + <div id="scheduling-datepicker"></div> > + > + <div id="second-step" hidden="hidden"> > + <h2>Select the hours you'll be available on these dates</h2> > + <div id="days-wrapper"></div> > + <h2>Players that are currently available at the same hours as > you</h2> > + <div id="other-users-wrapper"></div> > + <div id="no-user-to-display" hidden="hidden">Sorry, there are > currently no players available at these hours. But your availabilities are > noted, and another user might want to play with you!</div> > + <button id="validate-btn">Update</button> > + </div> > +</div> > + > +<!-- Templates for the js script --> > +<!-- Template for the days of avaibility of the current user --> > +<div id="day-template" class="day" hidden="hidden"> > + <div class="day-title"></div> > + <div class="hours-title-wrapper"> > + {% for i in "xxxxxxxxxxxxxxxxxxxxxxxxx" %} > + <p>{{ forloop.counter0 }}</p> > + {% endfor %} > + </div> > + <div class="hours-wrapper"> > + {% for i in "xxxxxxxxxxxxxxxxxxxxxxxx" %} > + <div class="hours"></div> > + {% endfor %} > + <div class="hidden-hour"></div> > + </div> > +</div> > + > + > +<!-- Template for the days of avaibility of the other users --> > +{% include "wlscheduling/other-users-date.html" %} > + > +<div id="other-user-template" class="other-user-div" hidden="hidden"> > + <div class="title"></div> > +</div> > + > +<!-- Div to send django data to js file --> > +<div id="django-data" user-time-zone="{{user.wlprofile.time_zone}}" > day-to-fill = "{{ current_user_availabilities }}" > users-to-fill="{{other_users_availabilities}}" csrf-token = "{% csrf_token > %}"></div> > + > + > +{% endblock %} > > === added file 'wlscheduling/migrations/__init__.py' > === added file 'wlscheduling/models.py' > --- wlscheduling/models.py 1970-01-01 00:00:00 +0000 > +++ wlscheduling/models.py 2018-01-17 19:07:26 +0000 > @@ -0,0 +1,11 @@ > +#!/usr/bin/env python > +# encoding: utf-8 > + > +from django.db import models > +from django.contrib.auth.models import User > + > + > +class Availabilities(models.Model): > + user = models.ForeignKey(User, related_name='availabilities') > + avail_time = models.DateTimeField( > + ('one hour of availability'), default=0) ('one hour of availability') -> I do not understand what this is intended to do? Maybe should be: help_text="one hour of availability" ? > \ No newline at end of file -- https://code.launchpad.net/~trimardio/widelands-website/module_scheduling/+merge/335570 Your team Widelands Developers is requested to review the proposed merge of lp:~trimardio/widelands-website/module_scheduling into lp:widelands-website. _______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp