little enhancements
This commit is contained in:
		
							
								
								
									
										31
									
								
								style.css
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								style.css
									
									
									
									
									
								
							| @ -110,14 +110,13 @@ input[type="number"]::-webkit-calendar-picker-indicator { | |||||||
|     font-size: 0.9rem !important; |     font-size: 0.9rem !important; | ||||||
| } | } | ||||||
| .grid input:disabled { | .grid input:disabled { | ||||||
|     color: white; |     color: white !important; | ||||||
|     background: #6666ff; |     background: #6666ff !important; | ||||||
|     cursor: not-allowed !important; |     cursor: not-allowed !important; | ||||||
| } | } | ||||||
| .grid input:disabled, |  | ||||||
| .grid input.forbidden:enabled { | .grid input.forbidden:enabled { | ||||||
|     background: #ffffaa; |     background: #ffffaa !important; | ||||||
|     cursor: not-allowed !important; |     cursor: not-allowed; | ||||||
| } | } | ||||||
| .grid input.same-value:enabled { | .grid input.same-value:enabled { | ||||||
|     background: #ffff33; |     background: #ffff33; | ||||||
| @ -129,7 +128,7 @@ input[type="number"]::-webkit-calendar-picker-indicator { | |||||||
| } | } | ||||||
| .grid input.same-value:disabled, | .grid input.same-value:disabled, | ||||||
| .tools button.same-value:enabled, | .tools button.same-value:enabled, | ||||||
| .tools input:enabled:checked+label { | .tools input:enabled:checked + label { | ||||||
|     color: #ffffaa !important; |     color: #ffffaa !important; | ||||||
|     background: #00b359 !important; |     background: #00b359 !important; | ||||||
| } | } | ||||||
| @ -138,7 +137,7 @@ input[type="number"]::-webkit-calendar-picker-indicator { | |||||||
| } | } | ||||||
|  |  | ||||||
| .tools button, | .tools button, | ||||||
| .tools input+label { | .tools input + label { | ||||||
|     color: white; |     color: white; | ||||||
|     text-shadow: -1px -1px #5b6c9e; |     text-shadow: -1px -1px #5b6c9e; | ||||||
|     background: #8ca6f2; |     background: #8ca6f2; | ||||||
| @ -156,22 +155,28 @@ input[type="number"]::-webkit-calendar-picker-indicator { | |||||||
|     height: 24px; |     height: 24px; | ||||||
| } | } | ||||||
| .tools input { | .tools input { | ||||||
|     display:none; |     position: fixed; | ||||||
|  |     opacity: 0; | ||||||
|  |     width: 0; | ||||||
|  |     pointer-events: none; | ||||||
| } | } | ||||||
| .tools button:enabled:hover, | .tools button:enabled:hover, | ||||||
| .tools input:enabled:hover+label { | .tools button:enabled:focus, | ||||||
|  | .tools input:enabled:hover + label, | ||||||
|  | .tools input:enabled:focus + label { | ||||||
|     border-width: 1px; |     border-width: 1px; | ||||||
|     border-style: outset; |     border-style: outset; | ||||||
|     padding: 5px 5px 5px 6px; |     padding: 5px 5px 5px 6px; | ||||||
|     margin:  1px 1px 1px 2px; |     margin:  1px 1px 1px 2px; | ||||||
| } | } | ||||||
| .tools input:enabled:checked:hover+label { | .tools input:enabled:checked:hover + label, | ||||||
|  | .tools input:enabled:checked:focus + label { | ||||||
|     border-width: 3px; |     border-width: 3px; | ||||||
|     border-style: inset; |     border-style: inset; | ||||||
|     padding: 4px 2px 2px 5px; |     padding: 4px 2px 2px 5px; | ||||||
|     margin:  1px 1px 1px 2px; |     margin:  1px 1px 1px 2px; | ||||||
| } | } | ||||||
| .tools input:enabled:checked+label { | .tools input:enabled:checked + label { | ||||||
|     text-shadow: -1px -1px #005f2f; |     text-shadow: -1px -1px #005f2f; | ||||||
|     border: 2px inset #00b359; |     border: 2px inset #00b359; | ||||||
|     background: #00b359; |     background: #00b359; | ||||||
| @ -179,14 +184,14 @@ input[type="number"]::-webkit-calendar-picker-indicator { | |||||||
|     margin:  1px 1px 0px 2px; |     margin:  1px 1px 0px 2px; | ||||||
| } | } | ||||||
| .tools button:enabled:active, | .tools button:enabled:active, | ||||||
| .tools input:enabled:active+label { | .tools input:enabled:active + label { | ||||||
|     border-width: 4px !important; |     border-width: 4px !important; | ||||||
|     border-style: inset !important; |     border-style: inset !important; | ||||||
|     padding: 4px 0px 0px 5px !important; |     padding: 4px 0px 0px 5px !important; | ||||||
|     margin:  0px 1px 0px 2px !important; |     margin:  0px 1px 0px 2px !important; | ||||||
| } | } | ||||||
| .tools button:disabled, | .tools button:disabled, | ||||||
| .tools input:disabled+label { | .tools input:disabled + label { | ||||||
|     text-shadow: -1px -1px #555; |     text-shadow: -1px -1px #555; | ||||||
|     color: #ccc; |     color: #ccc; | ||||||
|     background: darkgrey; |     background: darkgrey; | ||||||
|  | |||||||
							
								
								
									
										191
									
								
								sudoku.js
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								sudoku.js
									
									
									
									
									
								
							| @ -52,6 +52,35 @@ window.onload = function () { | |||||||
|         rowId++ |         rowId++ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     loadSavedGame() | ||||||
|  |  | ||||||
|  |     boxes.forEach(box => { | ||||||
|  |         box.neighbourhood = new Set(rows[box.rowId].concat(columns[box.columnId]).concat(regions[box.regionId])) | ||||||
|  |         box.neighbourhood.delete(box) | ||||||
|  |         box.neighbourhood = Array.from(box.neighbourhood) | ||||||
|  |         searchCandidatesOf(box) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |     for (label of document.getElementsByTagName("label")) { | ||||||
|  |         label.control.label = label | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (/Win/.test(navigator.platform) || /Linux/.test(navigator.platform)) accessKeyModifiers = "Alt+Maj+" | ||||||
|  |     else if (/Mac/.test(navigator.platform)) accessKeyModifiers = "⌃⌥" | ||||||
|  |     for (node of document.querySelectorAll("*[accesskey]")) { | ||||||
|  |         shortcut = ` [${node.accessKeyLabel||(accessKeyModifiers+node.accessKey)}]` | ||||||
|  |         if (node.title) node.title += shortcut | ||||||
|  |         else if (node.label) node.label.title += shortcut | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     refreshUI() | ||||||
|  |  | ||||||
|  |     if ("serviceWorker" in navigator) { | ||||||
|  |         navigator.serviceWorker.register(`service-worker.php?location=${location.pathname}`) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function loadSavedGame() { | ||||||
|     const savedGame = localStorage[location.pathname] |     const savedGame = localStorage[location.pathname] | ||||||
|     if (savedGame) { |     if (savedGame) { | ||||||
|         boxes.forEach((box, i) => { |         boxes.forEach((box, i) => { | ||||||
| @ -62,25 +91,6 @@ window.onload = function () { | |||||||
|         }) |         }) | ||||||
|         fixGridLink.href = savedGame |         fixGridLink.href = savedGame | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     boxes.forEach(box => { |  | ||||||
|         box.neighbourhood = new Set(rows[box.rowId].concat(columns[box.columnId]).concat(regions[box.regionId])) |  | ||||||
|         box.neighbourhood.delete(box) |  | ||||||
|         box.neighbourhood = Array.from(box.neighbourhood) |  | ||||||
|         searchCandidatesOf(box) |  | ||||||
|     }) |  | ||||||
|  |  | ||||||
|     if (/Win/.test(navigator.platform) || /Linux/.test(navigator.platform)) accessKeyModifiers = "Alt+Maj+" |  | ||||||
|     else if (/Mac/.test(navigator.platform)) accessKeyModifiers = "⌃⌥" |  | ||||||
|     for (node of document.querySelectorAll("*[accesskey]")) { |  | ||||||
|         node.title += " [" + (node.accessKeyLabel || accessKeyModifiers + node.accessKey) + "]" |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     refreshUI() |  | ||||||
|  |  | ||||||
|     if ("serviceWorker" in navigator) { |  | ||||||
|         navigator.serviceWorker.register(`service-worker.php?location=${location.pathname}`) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| function searchCandidatesOf(box) { | function searchCandidatesOf(box) { | ||||||
| @ -111,10 +121,6 @@ function onfocus() { | |||||||
| } | } | ||||||
|  |  | ||||||
| function onclick() { | function onclick() { | ||||||
|     if (this.value == "" && this.candidates.size == 1) { |  | ||||||
|         valueToInsert = this.candidates.values().next().value |  | ||||||
|         document.getElementById("insertRadio" + valueToInsert).checked = true |  | ||||||
|     } |  | ||||||
|     if (inkPenRadio.checked) { |     if (inkPenRadio.checked) { | ||||||
|         if (valueToInsert) { |         if (valueToInsert) { | ||||||
|             this.value = valueToInsert |             this.value = valueToInsert | ||||||
| @ -145,20 +151,95 @@ function oninput() { | |||||||
|         this.previousPlaceholder = this.placeholder |         this.previousPlaceholder = this.placeholder | ||||||
|         refreshBox(this) |         refreshBox(this) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (suggestionTimer) clearTimeout(suggestionTimer) | ||||||
|  |     suggestionTimer = setTimeout(showSuggestion, SUGESTION_DELAY) | ||||||
| } | } | ||||||
|  |  | ||||||
| function refreshBox(box) { | function refreshBox(box) { | ||||||
|  |     saveGame() | ||||||
|  |     checkBox(box) | ||||||
|  |     refreshUI() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function saveGame() { | ||||||
|     let saveGame = boxes.map(box => box.value || UNKNOWN).join("") |     let saveGame = boxes.map(box => box.value || UNKNOWN).join("") | ||||||
|     localStorage[location.pathname] = saveGame |     localStorage[location.pathname] = saveGame | ||||||
|     fixGridLink.href = saveGame |     fixGridLink.href = saveGame | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function refreshUI() { | ||||||
|  |     enableRadio() | ||||||
|  |     highlight() | ||||||
|  |     showEasyBoxes() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function enableRadio() { | ||||||
|  |     for (radio of insertRadioGroup.getElementsByTagName("input")) { | ||||||
|  |         if (boxes.filter(box => box.value == "").some(box => box.candidates.has(radio.value))) { | ||||||
|  |             radio.disabled = false | ||||||
|  |             radio.label.title = `Insérer un ${radio.value} [${radio.accessKeyLabel||(accessKeyModifiers+radio.accessKey)}]` | ||||||
|  |         } else { | ||||||
|  |             radio.disabled = true | ||||||
|  |             radio.label.title = `Tous les ${radio.value} sont posés.` | ||||||
|  |             if (valueToInsert == radio.value) { | ||||||
|  |                 let nextRadio = document.querySelector(".insertRadioGroup :checked ~ input:enabled") || document.querySelector(".insertRadioGroup :enabled") | ||||||
|  |                 if (nextRadio) { | ||||||
|  |                     nextRadio.click() | ||||||
|  |                     nextRadio.onfocus() | ||||||
|  |                 } else { | ||||||
|  |                     valueToInsert = "" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function highlight() { | ||||||
|  |     boxes.forEach(box => { | ||||||
|  |         if (valueToInsert && box.value == valueToInsert) { | ||||||
|  |             box.classList.add("same-value") | ||||||
|  |             box.tabIndex = -1 | ||||||
|  |         } else { | ||||||
|  |             box.classList.remove("same-value") | ||||||
|  |             if (box.disabled) { | ||||||
|  |                 box.classList.add("forbidden") | ||||||
|  |             } else { | ||||||
|  |                 if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) { | ||||||
|  |                     box.classList.add("forbidden") | ||||||
|  |                     box.tabIndex = -1 | ||||||
|  |                 } else { | ||||||
|  |                     box.classList.remove("forbidden") | ||||||
|  |                     box.tabIndex = 0 | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }) | ||||||
|  |     highlighterCheckbox.label.title = "Surligner les lignes, colonnes et régions contenant déjà " + (valueToInsert? "un " + valueToInsert: "le chiffre sélectionné") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function showEasyBoxes() { | ||||||
|  |     boxes.filter(box => !box.disabled).forEach(box => { | ||||||
|  |         if (!box.value && box.candidates.size == 1) { | ||||||
|  |             box.classList.add("one-candidate") | ||||||
|  |             box.onclick = function() { | ||||||
|  |                 valueToInsert = this.candidates.values().next().value | ||||||
|  |                 document.getElementById("insertRadio" + valueToInsert).checked = true | ||||||
|  |                 onclick.apply(box) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             box.classList.remove("one-candidate") | ||||||
|  |             box.onclick = onclick | ||||||
|  |         } | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function checkBox(box) { | ||||||
|     box.neighbourhood.concat([box]).forEach(neighbour => { |     box.neighbourhood.concat([box]).forEach(neighbour => { | ||||||
|         searchCandidatesOf(neighbour) |         searchCandidatesOf(neighbour) | ||||||
|         neighbour.setCustomValidity("") |         neighbour.setCustomValidity("") | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|     refreshUI() |  | ||||||
|  |  | ||||||
|     for (neighbour1 of box.neighbourhood) { |     for (neighbour1 of box.neighbourhood) { | ||||||
|         if (neighbour1.value) { |         if (neighbour1.value) { | ||||||
|             for (area of [ |             for (area of [ | ||||||
| @ -187,56 +268,6 @@ function refreshBox(box) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function refreshUI() { |  | ||||||
|     for (radio of insertRadioGroup.getElementsByTagName("input")) { |  | ||||||
|         const label = radio.nextElementSibling |  | ||||||
|         if (boxes.filter(box => box.value == "").some(box => box.candidates.has(radio.value))) { |  | ||||||
|             radio.disabled = false |  | ||||||
|             if (radio.previousTitle) { |  | ||||||
|                 label.title = radio.previousTitle |  | ||||||
|                 label.previousTitle = null |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             radio.disabled = true |  | ||||||
|             label.previousTitle = label.title |  | ||||||
|             label.title = `Tous les ${radio.value} sont posés` |  | ||||||
|             if (valueToInsert == radio.value) valueToInsert = "" |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     highlight() |  | ||||||
|  |  | ||||||
|     boxes.filter(box => !box.disabled).forEach(box => { |  | ||||||
|         if (!box.value && box.candidates.size == 1) box.classList.add("one-candidate") |  | ||||||
|         else box.classList.remove("one-candidate") |  | ||||||
|     }) |  | ||||||
|     if (suggestionTimer) clearTimeout(suggestionTimer) |  | ||||||
|     suggestionTimer = setTimeout(showSuggestion, SUGESTION_DELAY) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function highlight() { |  | ||||||
|     boxes.forEach(box => { |  | ||||||
|         if (valueToInsert && box.value == valueToInsert) { |  | ||||||
|             box.classList.add("same-value") |  | ||||||
|             box.tabIndex = -1 |  | ||||||
|         } else { |  | ||||||
|             box.classList.remove("same-value") |  | ||||||
|             if (box.disabled) { |  | ||||||
|                 box.classList.add("forbidden") |  | ||||||
|             } else { |  | ||||||
|                 if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) { |  | ||||||
|                     box.classList.add("forbidden") |  | ||||||
|                     box.tabIndex = -1 |  | ||||||
|                 } else { |  | ||||||
|                     box.classList.remove("forbidden") |  | ||||||
|                     box.tabIndex = 0 |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }) |  | ||||||
|     highlighterCheckbox.nextElementSibling.title = "Surligner les lignes, colonnes et régions contenant déjà " + (valueToInsert? "un " + valueToInsert: "le chiffre sélectionné") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function onblur() { | function onblur() { | ||||||
|     if (this.classList.contains("pencil")) { |     if (this.classList.contains("pencil")) { | ||||||
|         this.placeholder = this.value |         this.placeholder = this.value | ||||||
| @ -289,7 +320,9 @@ function restart() { | |||||||
| function showSuggestion() { | function showSuggestion() { | ||||||
|     const easyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1) |     const easyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1) | ||||||
|     if (easyBoxes.length) { |     if (easyBoxes.length) { | ||||||
|         shuffle(easyBoxes)[0].placeholder = "💡" |         let randomEasyBox = shuffle(easyBoxes)[0] | ||||||
|  |         randomEasyBox.placeholder = "💡" | ||||||
|  |         randomEasyBox.focus() | ||||||
|     } else { |     } else { | ||||||
|         clearTimeout(suggestionTimer) |         clearTimeout(suggestionTimer) | ||||||
|         suggestionTimer = null |         suggestionTimer = null | ||||||
| @ -321,16 +354,18 @@ function oncontextmenu(event) { | |||||||
|     } |     } | ||||||
|     contextMenu.style.left = `${event.pageX}px` |     contextMenu.style.left = `${event.pageX}px` | ||||||
|     contextMenu.style.top = `${event.pageY}px` |     contextMenu.style.top = `${event.pageY}px` | ||||||
|  |     console.log(event.target) | ||||||
|     contextMenu.style.display = "block" |     contextMenu.style.display = "block" | ||||||
|     return false |     return false | ||||||
| } | } | ||||||
|  |  | ||||||
| document.onclick = function (event) { | document.onclick = function (event) { | ||||||
|     contextMenu.style.display = "none" |     if (contextMenu.style.display == "block") | ||||||
|  |         contextMenu.style.display = "none" | ||||||
| } | } | ||||||
|  |  | ||||||
| document.onkeydown = function(event) { | document.onkeydown = function(event) { | ||||||
|     if (event.key == "Escape") { |     if (event.key == "Escape" && contextMenu.style.display == "block") { | ||||||
|         event.preventDefault() |         event.preventDefault() | ||||||
|         contextMenu.style.display = "none" |         contextMenu.style.display = "none" | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								sudoku.php
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								sudoku.php
									
									
									
									
									
								
							| @ -90,26 +90,17 @@ | |||||||
|             <div id='insertRadioGroup' class='insertRadioGroup'> |             <div id='insertRadioGroup' class='insertRadioGroup'> | ||||||
| <?php | <?php | ||||||
|         for($value=1; $value<=9; $value++) { |         for($value=1; $value<=9; $value++) { | ||||||
|             echo "                <input type='radio' id='insertRadio$value' value='$value' name='insertRadioGroup' onclick='insert(this)' accesskey='$value'/>\n"; |             echo "                <input type='radio' id='insertRadio$value' value='$value' name='insertRadioGroup' onclick='insert(this)' accesskey='$value'/><label for='insertRadio$value' title='Insérer un $value'>$value</label>\n"; | ||||||
|             echo "                <label for='insertRadio$value' title='Insérer un $value'>$value</label>\n"; |  | ||||||
|         } |         } | ||||||
| ?> | ?> | ||||||
|             </div> |             </div> | ||||||
|             <div> |             <div> | ||||||
|                 <input id='highlighterCheckbox' type="checkbox" onclick='highlight()'/> |                 <input id='highlighterCheckbox' type="checkbox" onclick='highlight()'/><label for='highlighterCheckbox' title='Surligner les lignes, colonnes et régions contenant déjà le chiffre sélectionné'><img src='img/highlighter.svg' alt='Surligneur'></label> | ||||||
|                 <label for='highlighterCheckbox' title='Surligner les lignes, colonnes et régions contenant déjà le chiffre sélectionné'><img src='img/highlighter.svg' alt='Surligneur'></label> |                 <input type='radio' id='inkPenRadio' name='tool' onclick='grid.style.cursor = "url(img/ink-pen.svg) 2 22, auto"' checked/><label for='inkPenRadio' title='Écrire un chiffre'><img src='img/ink-pen.svg' alt='Stylo indélébile'/></label> | ||||||
|                 <input type='radio' id='inkPenRadio' name='tool' onclick='grid.style.cursor = "url(img/ink-pen.svg) 2 22, auto"' checked/> |                 <input type='radio' id='pencilRadio' name='tool' onclick='grid.style.cursor = "url(img/pencil.svg) 2 22, auto"'/><label for='pencilRadio' title='Prendre des notes'><img src='img/pencil.svg' alt='Crayon'/></label> | ||||||
|                 <label for='inkPenRadio' title='Écrire un chiffre'><img src='img/ink-pen.svg' alt='Stylo indélébile'/></label> |                 <input type='radio' id='eraserRadio' name='tool' onclick='grid.style.cursor = "url(img/eraser.svg) 2 22, auto"'/><label for='eraserRadio' title='Effacer une case'><img src='img/eraser.svg' alt='Gomme'/></label> | ||||||
|                 <input type='radio' id='pencilRadio' name='tool' onclick='grid.style.cursor = "url(img/pencil.svg) 2 22, auto"'/> |                 <button type='button' class='warning' onclick='restart()' title='Recommencer'><img src='img/restart.svg' alt='Recommencer'/></button> | ||||||
|                 <label for='pencilRadio' title='Prendre des notes'><img src='img/pencil.svg' alt='Crayon'/></label> |                 <button id='undoButton' type='button' onclick='undo()' disabled title='Annuler' accesskey='z'><img src='img/undo.svg' alt='Annuler'/></button> | ||||||
|                 <input type='radio' id='eraserRadio' name='tool' onclick='grid.style.cursor = "url(img/eraser.svg) 2 22, auto"'/> |  | ||||||
|                 <label for='eraserRadio' title='Effacer une case'><img src='img/eraser.svg' alt='Gomme'/></label> |  | ||||||
|                 <button type='button' class='warning' onclick='restart()' title='Recommencer'> |  | ||||||
|                     <img src='img/restart.svg' alt='Recommencer'/> |  | ||||||
|                 </button> |  | ||||||
|                 <button id='undoButton' type='button' onclick='undo()' disabled title='Annuler' accesskey='z'> |  | ||||||
|                     <img src='img/undo.svg' alt='Annuler'/> |  | ||||||
|                 </button> |  | ||||||
|             </div> |             </div> | ||||||
|         </section> |         </section> | ||||||
|         <section> |         <section> | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user