Parcourir la source

Merge de la branche 'validation-forms' après ajout exemples pour cours jQuery (historique détaillé: voir branche en question)

Benoît Hubert il y a 8 ans
Parent
commit
2fbf35f28c
75 fichiers modifiés avec 65022 ajouts et 88 suppressions
  1. 5 1
      .gitignore
  2. 3 0
      .gitmodules
  3. 1 0
      ace
  4. 7 0
      css/bootstrap.min.css
  5. BIN
      css/fonts/icomoon.eot
  6. 14 0
      css/fonts/icomoon.svg
  7. BIN
      css/fonts/icomoon.ttf
  8. BIN
      css/fonts/icomoon.woff
  9. 436 0
      css/qunit-2.4.1.css
  10. 306 5
      css/styles.css
  11. 2 0
      exemples/ajax-donnees-page-produit/contenu.html
  12. 15 0
      exemples/ajax-donnees-page-produit/script.js
  13. 10 0
      exemples/ajax-envoi-formulaire-par-get/contenu.html
  14. 17 0
      exemples/ajax-envoi-formulaire-par-get/script.js
  15. 11 0
      exemples/ajax-envoi-formulaire-par-post/contenu.html
  16. 29 0
      exemples/ajax-envoi-formulaire-par-post/script.js
  17. 4 0
      exemples/ajax-exemple-simple/contenu.html
  18. 3 0
      exemples/ajax-exemple-simple/script.js
  19. 17 0
      exemples/ajax-requete-randomuserme/contenu.html
  20. 13 0
      exemples/ajax-requete-randomuserme/script.js
  21. 10 0
      exemples/ajax-requete-themoviedborg/contenu.html
  22. 33 0
      exemples/ajax-requete-themoviedborg/script.js
  23. 16 0
      exemples/evenements-1-click-submit/contenu.html
  24. 31 0
      exemples/evenements-1-click-submit/script.js
  25. 14 0
      exemples/evenements-2-change/contenu.html
  26. 14 0
      exemples/evenements-2-change/script.js
  27. 9 0
      exemples/evenements-3-keyup/contenu.html
  28. 25 0
      exemples/evenements-3-keyup/script.js
  29. 12 0
      exemples/exercice-1-verifier-un-formulaire/contenu.html
  30. 20 0
      exemples/exercice-1-verifier-un-formulaire/script.js
  31. 24 0
      exemples/exercice-1-verifier-un-formulaire/test.js
  32. 25 0
      exemples/liste.json
  33. 20 0
      exemples/onglets/contenu.html
  34. 10 0
      exemples/onglets/script.js
  35. 17 0
      exemples/selecteurs-basiques/contenu.html
  36. 28 0
      exemples/selecteurs-basiques/script.js
  37. 16 0
      exemples/selecteurs-filtres-2/contenu.html
  38. 13 0
      exemples/selecteurs-filtres-2/script.js
  39. 19 0
      exemples/selecteurs-filtres/contenu.html
  40. 34 0
      exemples/selecteurs-filtres/script.js
  41. 19 0
      exemples/selecteurs-multiples/contenu.html
  42. 11 0
      exemples/selecteurs-multiples/script.js
  43. 54 0
      exemples/validation-de-formulaires-bootstrap/contenu.html
  44. 107 0
      exemples/validation-de-formulaires-bootstrap/script.js
  45. 0 0
      html5bp/LICENSE.txt
  46. 0 0
      html5bp/browserconfig.xml
  47. 0 0
      html5bp/humans.txt
  48. 0 0
      html5bp/icon.png
  49. 0 0
      html5bp/robots.txt
  50. 0 0
      html5bp/site.webmanifest
  51. 0 0
      html5bp/tile-wide.png
  52. 0 0
      html5bp/tile.png
  53. BIN
      img/vsizegrip.png
  54. 63 80
      index.html
  55. 77 0
      js/editor-local-storage.js
  56. 271 0
      js/editor.js
  57. 20270 0
      js/vendor/ace/ace.js
  58. 699 0
      js/vendor/ace/mode-css.js
  59. 2480 0
      js/vendor/ace/mode-html.js
  60. 789 0
      js/vendor/ace/mode-javascript.js
  61. 319 0
      js/vendor/ace/mode-json.js
  62. 98 0
      js/vendor/ace/theme-eclipse.js
  63. 105 0
      js/vendor/ace/theme-monokai.js
  64. 8761 0
      js/vendor/ace/worker-css.js
  65. 11605 0
      js/vendor/ace/worker-html.js
  66. 12528 0
      js/vendor/ace/worker-javascript.js
  67. 2 0
      js/vendor/jquery-resizable.min.js
  68. 22 0
      js/vendor/loadJS.js
  69. 136 0
      js/vendor/lodash.min.js
  70. 5061 0
      js/vendor/qunit-2.4.1.js
  71. 11 0
      package.json
  72. 43 0
      produits.json
  73. 57 0
      sandboxApp.js
  74. 151 0
      server.js
  75. 0 2
      some-text.html

+ 5 - 1
.gitignore

@@ -1,4 +1,8 @@
 # Include your project-specific ignores in this file
 # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
 # Useful .gitignore templates: https://github.com/github/gitignore
-doc/
+doc/
+node_modules/
+package-lock.json
+exemples/ajax-requete-themoviedborg/tmdb-key.json
+_stuff/

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "ace"]
+	path = ace
+	url = git://github.com/ajaxorg/ace.git

+ 1 - 0
ace

@@ -0,0 +1 @@
+Subproject commit 243cb467fce7ebee0dd98691bd9d472a861fda8e

Fichier diff supprimé car celui-ci est trop grand
+ 7 - 0
css/bootstrap.min.css


BIN
css/fonts/icomoon.eot


Fichier diff supprimé car celui-ci est trop grand
+ 14 - 0
css/fonts/icomoon.svg


BIN
css/fonts/icomoon.ttf


BIN
css/fonts/icomoon.woff


+ 436 - 0
css/qunit-2.4.1.css

@@ -0,0 +1,436 @@
+/*!
+ * QUnit 2.4.1
+ * https://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2017-10-22T05:12Z
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
+	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+	margin: 0;
+	padding: 0;
+}
+
+
+/** Header (excluding toolbar) */
+
+#qunit-header {
+	padding: 0.5em 0 0.5em 1em;
+
+	color: #8699A4;
+	background-color: #0D3349;
+
+	font-size: 1.5em;
+	line-height: 1em;
+	font-weight: 400;
+
+	border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+	text-decoration: none;
+	color: #C2CCD1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+	color: #FFF;
+}
+
+#qunit-banner {
+	height: 5px;
+}
+
+#qunit-filteredTest {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #366097;
+	background-color: #F4FF77;
+}
+
+#qunit-userAgent {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #FFF;
+	background-color: #2B81AF;
+	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #5E740B;
+	background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+	height: 0;
+	clear: both;
+}
+
+#qunit-testrunner-toolbar label {
+	display: inline-block;
+}
+
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+	margin: 3px;
+	vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+	box-sizing: border-box;
+	height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+	display: inline-block;
+	line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
+	float: right;
+	position: relative;
+	margin-left: 1em;
+}
+
+.qunit-url-config label {
+	margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+	box-sizing: border-box;
+	width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+	position: absolute;
+	right: 0.3em;
+	content: "\25bc";
+	color: black;
+}
+
+#qunit-modulefilter-dropdown {
+	/* align with #qunit-modulefilter-search */
+	box-sizing: border-box;
+	width: 400px;
+	position: absolute;
+	right: 0;
+	top: 50%;
+	margin-top: 0.8em;
+
+	border: 1px solid #D3D3D3;
+	border-top: none;
+	border-radius: 0 0 .25em .25em;
+	color: #000;
+	background-color: #F5F5F5;
+	z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+	color: inherit;
+	text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+	font-weight: bold;
+	color: #000;
+	background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+	color: #FFF;
+	background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+	display: block;
+	overflow: auto;
+
+	/* align with #qunit-modulefilter-dropdown-list */
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+	box-sizing: border-box;
+	max-height: 2.8em;
+	display: block;
+	padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+	float: right;
+	font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+	/* insert padding to align with checkbox margins */
+	padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+	max-height: 200px;
+	overflow-y: auto;
+	margin: 0;
+	border-top: 2px groove threedhighlight;
+	padding: 0.4em 0 0;
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+	display: block;
+	padding-left: 0.15em;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+	list-style-position: inside;
+}
+
+#qunit-tests li {
+	padding: 0.4em 1em 0.4em 1em;
+	border-bottom: 1px solid #FFF;
+	list-style-position: inside;
+}
+
+#qunit-tests > li {
+	display: none;
+}
+
+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
+	display: list-item;
+}
+
+#qunit-tests.hidepass {
+	position: relative;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass:not(.todo) {
+	visibility: hidden;
+	position: absolute;
+	width:   0;
+	height:  0;
+	padding: 0;
+	border:  0;
+	margin:  0;
+}
+
+#qunit-tests li strong {
+	cursor: pointer;
+}
+
+#qunit-tests li.skipped strong {
+	cursor: default;
+}
+
+#qunit-tests li a {
+	padding: 0.5em;
+	color: #C2CCD1;
+	text-decoration: none;
+}
+
+#qunit-tests li p a {
+	padding: 0.25em;
+	color: #6B6464;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+	color: #000;
+}
+
+#qunit-tests li .runtime {
+	float: right;
+	font-size: smaller;
+}
+
+.qunit-assert-list {
+	margin-top: 0.5em;
+	padding: 0.5em;
+
+	background-color: #FFF;
+
+	border-radius: 5px;
+}
+
+.qunit-source {
+	margin: 0.6em 0 0.3em;
+}
+
+.qunit-collapsed {
+	display: none;
+}
+
+#qunit-tests table {
+	border-collapse: collapse;
+	margin-top: 0.2em;
+}
+
+#qunit-tests th {
+	text-align: right;
+	vertical-align: top;
+	padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+	vertical-align: top;
+}
+
+#qunit-tests pre {
+	margin: 0;
+	white-space: pre-wrap;
+	word-wrap: break-word;
+}
+
+#qunit-tests del {
+	color: #374E0C;
+	background-color: #E0F2BE;
+	text-decoration: none;
+}
+
+#qunit-tests ins {
+	color: #500;
+	background-color: #FFCACA;
+	text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts                       { color: #000; }
+#qunit-tests b.passed                       { color: #5E740B; }
+#qunit-tests b.failed                       { color: #710909; }
+
+#qunit-tests li li {
+	padding: 5px;
+	background-color: #FFF;
+	border-bottom: none;
+	list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+	color: #3C510C;
+	background-color: #FFF;
+	border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name               { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected           { color: #999; }
+
+#qunit-banner.qunit-pass                    { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+	color: #710909;
+	background-color: #FFF;
+	border-left: 10px solid #EE5757;
+	white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+	border-radius: 0 0 5px 5px;
+}
+
+#qunit-tests .fail                          { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name             { color: #000; }
+
+#qunit-tests .fail .test-actual             { color: #EE5757; }
+#qunit-tests .fail .test-expected           { color: #008000; }
+
+#qunit-banner.qunit-fail                    { background-color: #EE5757; }
+
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
+/*** Skipped tests */
+
+#qunit-tests .skipped {
+	background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-todo-label,
+#qunit-tests .qunit-skipped-label {
+	background-color: #F4FF77;
+	display: inline-block;
+	font-style: normal;
+	color: #366097;
+	line-height: 1.8em;
+	padding: 0 0.5em;
+	margin: -0.4em 0.4em -0.4em 0;
+}
+
+#qunit-tests .qunit-todo-label {
+	background-color: #EEE;
+}
+
+/** Result */
+
+#qunit-testresult {
+	color: #2B81AF;
+	background-color: #D2E0E6;
+
+	border-bottom: 1px solid #FFF;
+}
+#qunit-testresult .clearfix {
+	height: 0;
+	clear: both;
+}
+#qunit-testresult .module-name {
+	font-weight: 700;
+}
+#qunit-testresult-display {
+	padding: 0.5em 1em 0.5em 1em;
+	width: 85%;
+	float:left;
+}
+#qunit-testresult-controls {
+	padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+	float:left;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+	position: absolute;
+	top: -10000px;
+	left: -10000px;
+	width: 1000px;
+	height: 1000px;
+}

+ 306 - 5
css/styles.css

@@ -1,14 +1,214 @@
+/** 
+ * Pack d'icônes
+ */
+@font-face {
+  font-family: 'icomoon';
+  src:  url('fonts/icomoon.eot?xjk54g');
+  src:  url('fonts/icomoon.eot?xjk54g#iefix') format('embedded-opentype'),
+    url('fonts/icomoon.ttf?xjk54g') format('truetype'),
+    url('fonts/icomoon.woff?xjk54g') format('woff'),
+    url('fonts/icomoon.svg?xjk54g#icomoon') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+
+[class^="icon-"], [class*=" icon-"] {
+  /* use !important to prevent issues with browser extensions that change fonts */
+  font-family: 'icomoon' !important;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-cloud-upload:before {
+  content: "\e9c3";
+}
+.icon-plus:before {
+  content: "\ea0a";
+}
+.icon-cross:before {
+  content: "\ea0f";
+}
+.icon-checkmark:before {
+  content: "\ea10";
+}
+
+
+/**
+ * Styles généraux
+ */
+
+/**
+ * Grosse font pour le vidéo-proj
+ */
 body {
-	background: #f4f4f4;
+  font-size: 130%;
+}
+
+/**
+ * Styles des panneaux gauche-droite
+ */
+/*#left-panel,
+#right-panel {
 	font-family: Arial, Helvetica;
+  position: absolute;
+  top: 0;
+  bottom: 0;
+}
+#left-panel {
+  background: #fff;
+  border-right: 1px solid #aaa;
+  right: 39%;
+  left: 0;
+}
+#right-panel {
+  background: #f4f4f4;
+  left: 59%;
+  right: 0;
+}*/
+
+/* jQuery resizable: https://codepen.io/rstrahl/pen/eJZQej */
+/* horizontal panel*/
+
+.panel-container {
+  display: flex;
+  flex-direction: row;
+  border: 1px solid silver;
+  overflow: hidden;
+  height: 100%;
+
+  
+  /* avoid browser level touch actions */
+  xtouch-action: none;
+}
+
+.panel-left {
+  flex: 0 0 auto;
+  /* only manually resize */
+  padding: 10px;
+  width: 50%;
+  /*min-height: 200px;*/
+  height: 100%;
+  min-width: 150px;
+  white-space: nowrap;
+  background: #fff;
+  color: white;
+}
+
+.splitter {
+  flex: 0 0 auto;
+  width: 18px;
+  background: url(../img/vsizegrip.png) center center no-repeat #ddd;
+  min-height: 200px;
+  cursor: col-resize;  
+}
+
+.panel-right {
+  flex: 1 1 auto;
+  /* resizable */
+  padding: 10px;
+  width: 100%;
+  height: 100%;
+  overflow-y: scroll;
+
+  min-width: 200px;
+  background: #eee;
+}
+
+
+.panel-inner {
+  padding: 10px;
+}
+.panel-left nav,
+#add-example-form {
+  display: inline-block;
+}
+.panel-left nav ul {
+  padding-left: 0;
+  margin: 0;
+}
+.panel-left nav ul li {
+  display: inline-block;
+}
+#notification {
+  color: black;
+  position: absolute;
+  top: -100px;
+  right: 10px;
+  transition: top 1s;
+  border-radius: 3px;
+  padding: 3px;
+}
+#notification.active {
+  top: 5px;
+}
+#notification.error {
+  border: 1px solid #d42;
+  background: #ffe8e0;
+}
+#notification.success {
+  border: 1px solid #2d4;
+  background: #e0ffe8;
+}
+
+/**
+ * Styles généraux: liens, boutons, ...
+ */
+a {
+  text-decoration: none;
+  color: #59d;
+}
+a:hover {
+  text-decoration: none;
+  color: #37b;
+}
+button a {
+  color: #fff;
+}
+button.active {
+  background: #9ad;
 }
+button:hover {
+  opacity: 0.6;
+}
+#editor-js,#editor-html {
+  /*display: none;*/
+  position: absolute;
+  top: 510px;
+
+}
+/**
+ * Styles pour l'éditeur Ace
+ */
+#editor {
+  font-size: 90%;
+  position: absolute;
+  top: 90px;
+  right: 500px;
+  bottom: 0;
+  left: 0;
+  border-top: 1px solid #aaa;
+  border-bottom: 1px solid #aaa;
+}
+
 .container {
-  margin: 150px auto;
+  margin: 20px auto;
 	background: #fff;
-	border: 1px solid #eee;
+	border: 1px solid #ddd;
 	padding: 30px;
-	width: 600px;
+  width: 96%;
+  border-radius: 4px;
+  box-sizing: border-box;
+	/*max-width: 600px;*/
 }
+input[type="submit"],
 button {
   padding: 10px;
   color: white;
@@ -21,8 +221,109 @@ button {
 .red {
   background: #d42;
 }
+.green {
+  background: #4b2;
+}
+textarea,
+select,
 input {
   border: 1px solid #ddd;  
-  padding: 8px;
+  padding: 12px;
   border-radius: 3px;
+}
+input.input-sm {
+  padding: 6px;
+  font-size: 70%;
+}
+select {
+  background: #fcfcfc;
+}
+a,
+button {
+  margin: 5px;
+  box-sizing: border-box;
+}
+.bordure-flashy {
+  border: 2px solid red;
+}
+
+#html-content h5 {
+  margin: 20px 0 5px 0;
+}
+#html-content p {
+  margin: 10px 0;
+}
+button.d {
+  padding: 0;
+  width: 32px;
+  height: 32px;
+  border-radius: 16px;
+}
+
+/**
+ * Styles communs pour les différents exemples
+ */
+a.active {
+  font-weight: bold;
+  color: #48c;
+}
+.cyan {
+  background: #6ff;
+}
+.magenta {
+  background: #f6f;
+}
+.yellow {
+  background: #ff6;
+  color: #444;
+}
+.orange {
+  background: #eb6;
+}
+.bold-text {
+  font-weight: bold;
+}
+.underlined-text {
+  text-decoration: underline;
+}
+.text-green {
+  color: #4b2;
+}
+.text-red {
+  color: #b42;
+}
+.table {
+  border-collapse: collapse;
+}
+.table tr td {
+  border: 1px solid #aaa;
+  padding: 2px;
+}
+ul.tab-nav {
+   padding-left: 5px;
+   padding-bottom: 4px;
+   border-bottom: 1px solid #eee;
+}
+ul.tab-nav li {
+  display:inline;
+  padding: 6px 10px;
+  margin-right: 5px;
+  border-top:1px solid #ccc;
+  border-left:1px solid #ccc;
+  border-right:1px solid #ccc;
+}
+
+#qunit-header,
+#qunit-testrunner-toolbar {
+  display: none;
+}
+#revert-editor {
+  background: none;
+  border: none;
+  position: absolute;
+  padding: 0 3px;
+  top: 0;
+  left: 0;
+  color: red;
+  font-size: 11px;
 }

+ 2 - 0
exemples/ajax-donnees-page-produit/contenu.html

@@ -0,0 +1,2 @@
+<h3>Produits</h3>
+<div id="produits" class="row"></div>

+ 15 - 0
exemples/ajax-donnees-page-produit/script.js

@@ -0,0 +1,15 @@
+$.get('/produits.json', function(produits) {
+   var divProduits = $('#produits');
+   
+    produits.forEach(function(produit) {
+        divProduits.append('<div class="col-md-6 col-sm-12">' +
+            '<img class="img-thumbnail" src="' +
+                produit.image + '" />' +
+            '<div>' + produit.titre + '</div>' +
+        '</div>');
+    });
+}, 'json');
+
+$('#produits').scroll(function(e) {
+    console.log('scroll', e)
+})

+ 10 - 0
exemples/ajax-envoi-formulaire-par-get/contenu.html

@@ -0,0 +1,10 @@
+<h1>AJAX - Envoi formulaire par GET</h1>
+
+<form id="ajax-form-get" method="GET" action="/ajax-form-get">
+    <input type="text" name="name" placeholder="Nom ?" />
+    <input type="text" name="birthdate" placeholder="Date de naissance ?" />
+    <input type="submit" class="btn btn-primary" value="Envoyer" />
+</form>
+
+<h5>Résultat (HTML) renvoyé par le serveur:</h5>
+<div id="ajax-html"></div>

+ 17 - 0
exemples/ajax-envoi-formulaire-par-get/script.js

@@ -0,0 +1,17 @@
+$('#ajax-form-get').submit(function(e) {
+    e.preventDefault();
+    var form = $(this);
+    var name = form.find('input[name="name"]').val();
+    var birthdate = form.find('input[name="birthdate"]').val();
+    $.ajax({
+        method: 'GET',
+        url: form.prop('action'),
+        data: {
+            name: name, birthdate: birthdate
+        },
+        success: function(data) {
+            $('#ajax-html').html(data);
+        },
+        dataType: 'html'
+    });
+});

+ 11 - 0
exemples/ajax-envoi-formulaire-par-post/contenu.html

@@ -0,0 +1,11 @@
+<h1>AJAX - Envoi formulaire par POST</h1>
+
+<p>Presque la même chose que l'exemple précédent !</p>
+<form id="ajax-form-post" method="POST" action="/ajax-form-post">
+    <input type="text" name="title" placeholder="Titre" /><br>
+    <textarea name="text" rows="10" placeholder="Collez du texte"></textarea><br>
+    <input type="submit" class="btn btn-primary" value="Envoyer" />
+</form>
+
+<h5>Résultat (HTML) renvoyé par le serveur:</h5>
+<div id="ajax-html"></div>

+ 29 - 0
exemples/ajax-envoi-formulaire-par-post/script.js

@@ -0,0 +1,29 @@
+$.ajaxSetup({
+    headers: {
+        'content-type': 'application/x-www-form-urlencoded'
+    }
+});
+
+
+$('#ajax-form-post').submit(function(e) {
+    e.preventDefault();
+    var form = $(this);
+    var title = form.find('input[name="title"]').val();
+    var text = form.find('textarea[name="text"]').val();
+    $.ajax({
+        method: 'POST',
+        url: form.prop('action'),
+        data: {
+            title: title, text: text
+        },
+        success: function(data) {
+            console.log('received', data);
+            $('#ajax-html').html(data);
+        },
+        error: function(jqXHR) {
+            console.log('received', jqXHR);
+            $('#ajax-html').html(jqXHR);
+        },
+        dataType: 'html'
+    });
+});

+ 4 - 0
exemples/ajax-exemple-simple/contenu.html

@@ -0,0 +1,4 @@
+<h1>AJAX - Exemple simple</h1>
+
+<p>Résultat de la requête ci-dessous:</p>
+<div id="ajax-html"></div>

+ 3 - 0
exemples/ajax-exemple-simple/script.js

@@ -0,0 +1,3 @@
+$.get('/ajax-example', function(data) {
+   $('#ajax-html').html(data); 
+});

+ 17 - 0
exemples/ajax-requete-randomuserme/contenu.html

@@ -0,0 +1,17 @@
+<h5>API randomuser.me</h5>
+<p>Le terme "API" recouvre plusieurs choses:</p>
+<ul>
+    <li>Une "librairie", càd un ensemble de fonctions qu'on peut utiliser dans nos programmes (ex: API DOM en JavaScript)</li>
+    <li>Un service sur Internet qui fournit des informations</li>
+</ul>
+<p><a target="_blank" href="https://randomuser.me/">randomuser.me</a> est une API du 2ème type, qui permet de générer des faux utilisateurs.</p>
+
+<button class="blue" id="requete-randomuser">Générer un utilisateur</button>
+<div style="border: 1px solid #888; padding: 10px; margin: 10px;">
+    <img id="picture" style="float:right;" />
+    <h5 id="user-name"></h5>
+    <ul>
+        <li>Email: <span id="email"></span></li>
+        <li>Adresse: <span id="adresse"></span></li>
+    </ul>
+</div>

+ 13 - 0
exemples/ajax-requete-randomuserme/script.js

@@ -0,0 +1,13 @@
+$('#requete-randomuser').click(function() {
+   $.get('https://api.randomuser.me', function(data) {
+     var user = data.results[0];
+     var name = user.name;
+     var loc = user.location;
+     $('#user-name').html(name.first +
+        ' ' + name.last);
+     $('#email').html(user.email);
+     $('#adresse').html(loc.city + ', ' +
+        loc.postcode + ' ' + loc.state);
+     $('#picture').prop('src', user.picture.large);
+   });
+});

+ 10 - 0
exemples/ajax-requete-themoviedborg/contenu.html

@@ -0,0 +1,10 @@
+<h5>API themoviedb.org</h5>
+
+<form id="movie-title">
+    <p>Entrez un titre de film:</p>
+    <input type="text" name="title" placeholder="Titre" />
+    <button type="submit" class="blue" disabled="disabled">Go</button>
+</form>
+
+<div style="border: 1px solid #888; padding: 10px; margin: 10px;" id="tmdb-results">
+</div>

+ 33 - 0
exemples/ajax-requete-themoviedborg/script.js

@@ -0,0 +1,33 @@
+// Gère la soumission du formulaire:
+// récupère la valeur entrée et s'en sert pour interroger
+// l'API de themoviedb.org
+var form = $('#movie-title');
+var apiKey;
+var tmdbUrl;
+var tmdbImageUrl = 'http://image.tmdb.org/t/p/w185/';
+form.submit(function(evt) {
+    evt.preventDefault();
+    var inputTitle = form.find('input[name="title"]');
+    var title = inputTitle.val();
+    $.get(tmdbUrl + encodeURIComponent(title),
+    function(data) {
+          var movies = data.results;
+          for(i=0; i<movies.length;i++) {
+            var m = movies[i];
+            $('#tmdb-results').append('<div style="border-bottom: 1px solid #ddd">' + 
+                '<img style="float:right;" src="' + tmdbImageUrl + m.poster_path + '" />' +
+                '<h3>' + m.title + '</h3>' +
+                '<p>' + m.overview + '</p>' +
+            '</div><div style="clear:both"></div>');  
+          }
+    });
+})
+
+// Charge la clé d'API
+// (ne PAS la sauver dans Git)
+$.get('/exemples/ajax-requete-themoviedborg/tmdb-key.json', function(data) {
+    apiKey = data.key;
+    tmdbUrl = 'http://api.themoviedb.org/3/search/movie?api_key=' + apiKey + '&include_adult=false&page=1&language=en-US&query=';
+    form.find('button')
+    .prop('disabled', false);
+})

+ 16 - 0
exemples/evenements-1-click-submit/contenu.html

@@ -0,0 +1,16 @@
+<h5>Evènement click</h5>
+<button id="events1-btn1">Click me!</button>
+
+<h5>Evènement submit</h5>
+<form id="events1-form1">
+    <input type="text" name="name" placeholder="Votre nom" />
+    <input type="text" name="email" placeholder="Votre email" />
+    <div style="color:red;display:none;">Email invalide</div>
+    <button type="submit">Envoyer &raquo;</button>
+</form>
+<div id="events1-form1-resultat" style="display:none">
+    Vous avez entré:<ul>
+        <li>Nom: <span id="result-name"></span></li>
+        <li>Email: <span id="result-email"></span></li>
+    </ul>
+</div>

+ 31 - 0
exemples/evenements-1-click-submit/script.js

@@ -0,0 +1,31 @@
+// Déjà vu : simple gestionnaire de click
+$('#events1-btn1').click(function() {
+    $(this).toggleClass('blue');
+});
+
+// Gestionnaire de submit
+$('#events1-form1').submit(function(e) {
+    // On empêche la soumission du formulaire à un serveur
+    e.preventDefault();
+    // On sauvegarde la référence au formulaire car on va s'en servir plusieurs fois
+    var form = $(this);
+    // Encore un sélecteur: input avec attribut ayant la valeur demandée
+    var name = form.find('input[name="name"]').val();
+    var inputEmail = form.find('input[name="email"]');
+    var email = inputEmail.val();
+    // Vérification TRES basique de l'email
+    // Si elle échoue on affiche une erreur
+    if(! email.includes('@') || ! email.includes('.')) {
+        inputEmail.css('border', '1px solid red');
+        form.find('input[name="email"] + div').show();
+    }
+    // Sinon, on remplit les résultats dans
+    // la div prévue pour, on montre cette
+    // div, et on cache le formulaire
+    else {
+        $('#result-name').html(name);
+        $('#result-email').html(email);
+        $('#events1-form1-resultat').show();
+        form.hide();
+    }
+})

+ 14 - 0
exemples/evenements-2-change/contenu.html

@@ -0,0 +1,14 @@
+<h5>Evenement change sur input "text"</h5>
+<p>A noter que cet évènement n'a lieu, pour un input de type "text", que si on enlève le focus du champ de texte (en pressant Tab ou en cliquant en dehors).</p>
+<input type="text" id="events2-change-text" />
+<div id="events2-notification-text" style="color:green;"></div>
+
+
+<h5>Evenement change sur input "radio"</h5>
+<p>PC ou Mac ?</p>
+<input id="radio1" type="radio" name="events2-change-radio" value="PC" />
+<label for="radio1">PC ! J'aime bien les bugs de Windows !</label>
+<br>
+<input id="radio2" type="radio" name="events2-change-radio" value="Mac" />
+<label for="radio2">Mac ! J'aime bien payer des accessoires à 50€.</label>
+<div id="events2-notification-radio" style="color:green;"></div>

+ 14 - 0
exemples/evenements-2-change/script.js

@@ -0,0 +1,14 @@
+// Gestion de l'évènement change
+$('#events2-change-text').change(function(e) {
+    $('#events2-notification-text').html(
+        'Nouvelle valeur : ' +
+        $(this).val()
+    );
+});
+
+$('input[type="radio"][name="events2-change-radio"]').change(function(e) {
+    console.log($(this));
+    $('#events2-notification-radio').html(
+        'Vous avez choisi : ' + $(this).val()
+    );
+});

+ 9 - 0
exemples/evenements-3-keyup/contenu.html

@@ -0,0 +1,9 @@
+<h5>Evènements keydown et keyup</h5>
+<p>Plus immédiat que l'évènement change pour un input "text" : l'évènement keyup</p>
+<input type="text" id="events3-text" />
+<ul>
+    <li>Touche pressée/relâchée: <span id="key-name"></span></li>
+    <li>Touche ctrl: <span id="ctrl-status"></span></li>
+    <li>Touche shift: <span id="shift-status"></span></li>
+    <li>Touche alt: <span id="alt-status"></span></li>
+</ul>

+ 25 - 0
exemples/evenements-3-keyup/script.js

@@ -0,0 +1,25 @@
+// Gestion de l'évènement keyup
+var ctrlSt  = $('#ctrl-status');
+var shiftSt = $('#shift-status');
+var altSt   = $('#alt-status');
+var keyName = $('#key-name');
+
+// Ici ça devient intéressant de se pencher sur le contenu de l'objet event
+function onKeyChange(event) {
+    ctrlSt.removeClass()
+    .addClass(event.ctrlKey ? 'text-green' : 'text-red')
+    .html(event.ctrlKey ? 'ON' : 'OFF');
+
+    shiftSt.removeClass()
+    .addClass(event.shiftKey ? 'text-green' : 'text-red')
+    .html(event.shiftKey ? 'ON' : 'OFF');
+
+    altSt.removeClass()
+    .addClass(event.altKey ? 'text-green' : 'text-red')
+    .html(event.altKey ? 'ON' : 'OFF');
+    
+    keyName.html(event.key);
+}
+
+$('#events3-text').keydown(onKeyChange);
+$('#events3-text').keyup(onKeyChange);

+ 12 - 0
exemples/exercice-1-verifier-un-formulaire/contenu.html

@@ -0,0 +1,12 @@
+<h5>Vérifier un formulaire</h5>
+<p>Le code HTML est prêt, il faut écrire le code jQuery !</p>
+<ul>
+    <li>Ne pas oublier d'empêcher la soumission du formulaire !</li>
+</ul>
+<form id="exo1-form">
+    <input type="text" name="nom" placeholder="Nom complet" />
+    <div id="nom-statut" style="display:none"></div>
+    <input type="password" name="mdp" placeholder="mot de passe" />
+    <div id="mdp-statut" style="display:none"></div>
+    <button type="submit">Go</button>
+</form>

+ 20 - 0
exemples/exercice-1-verifier-un-formulaire/script.js

@@ -0,0 +1,20 @@
+$('#exo1-form').submit(function(e) {
+    e.preventDefault();
+    var form = $(this);
+    var inputPass = form.find('input[type="password"]');
+    var mdp = inputPass.val();
+    if(mdp.length < 6) {
+        $('#mdp-statut')
+        .show()
+        .addClass('text-red')
+        .html('Mot de passe trop court (6 caractères minimum)');
+    }
+    var inputName = form.find('input[name="nom"]');
+    var nom = inputName.val();
+    if(! nom || (nom.split(' ').length < 2)) {
+        $('#nom-statut')
+        .show()
+        .addClass('text-red')
+        .html('Le nom doit être rempli et comporter un espace');
+    }
+});

+ 24 - 0
exemples/exercice-1-verifier-un-formulaire/test.js

@@ -0,0 +1,24 @@
+var form = $('#exo1-form');
+var inputPass = form.find('input[type="password"]');
+var inputName = form.find('input[name="nom"]');
+var statusPass = $('#mdp-statut');
+var statusName = $('#nom-statut');
+QUnit.test( "Test résultat du formulaire", function( assert ) {
+  var done = assert.async();
+  inputPass.val('');
+  form.trigger('submit');
+  setTimeout(function() {
+    assert.ok(statusPass.is(':visible'), "la div #mdp-statut doit être visible");
+    assert.ok(statusPass.hasClass('text-red'), "la div #mdp-statut doit avoir la classe text-red");
+    assert.equal(statusPass.html(), 'Mot de passe trop court (6 caractères minimum)', "la div #mdp-statut doit afficher 'Mot de passe trop court (6 caractères minimum)'");
+
+    assert.ok(statusName.is(':visible'), "la div #nom-statut doit être visible");
+    assert.ok(statusName.hasClass('text-red'), "la div #nom-statut doit avoir la classe text-red");
+    assert.equal(statusName.html(), 'Le nom doit être rempli et comporter un espace', "la div #mdp-statut doit afficher 'Le nom doit être rempli et comporter un espace'");
+
+    done();
+    // var password = form.find('input[type="password"]').val();
+    // assert.ok(password.length > 0, "Le mot de passe n'est pas vide");
+    // assert.ok( 1 == "1", "Passed!" );
+  }, 1000);
+});

+ 25 - 0
exemples/liste.json

@@ -0,0 +1,25 @@
+[
+  { "slug": "selecteurs-basiques", "title": "Sélecteurs basiques" },
+  { "slug": "selecteurs-multiples", "title": "Sélecteurs multiples" },
+  { "slug": "selecteurs-filtres", "title": "Sélecteurs : filtres" },
+  { "slug": "selecteurs-filtres-2", "title": "Sélecteurs : filtres 2" },
+  { "slug": "evenements-1-click-submit", "title": "Evènements 1 : click et submit" },
+  { "slug": "evenements-2-change", "title": "Evènements 2 : change" },
+  { "slug": "evenements-3-keyup", "title": "Evènements 3 : keyup" },
+  { "slug": "ajax-requete-randomuserme", "title": "AJAX - Requête randomuser.me" },
+  { "slug": "ajax-requete-themoviedborg", "title": "AJAX - Requête themoviedb.org" },
+  {
+    "slug": "exercice-1-verifier-un-formulaire",
+    "title": "Exercice 1 : vérifier un formulaire",
+    "test": true
+  },
+  { "slug": "onglets", "title": "Onglets" },
+  {
+    "slug": "validation-de-formulaires-bootstrap",
+    "title": "Validation de formulaires Bootstrap"
+  },
+  { "slug": "ajax-exemple-simple", "title": "AJAX - Exemple simple" },
+  { "slug": "ajax-envoi-formulaire-par-get", "title": "AJAX - Envoi formulaire par GET" },
+  { "slug": "ajax-envoi-formulaire-par-post", "title": "AJAX - Envoi formulaire par POST" },
+  { "slug": "ajax-donnees-page-produit", "title": "AJAX - Données - page produit" }
+]

+ 20 - 0
exemples/onglets/contenu.html

@@ -0,0 +1,20 @@
+<ul id="onglets">
+    <li style="display:inline;">
+        <a data-panneau-id="panneau1" id="onglet1" class="active" href="#onglet1">Onglet 1</a>
+    </li>
+    <li style="display:inline;">
+        <a data-panneau-id="panneau2" id="onglet2" href="#onglet2">Onglet 2</a>
+    </li>
+    <li style="display:inline;">
+        <a data-panneau-id="panneau3" id="onglet3" href="#onglet3">Onglet 3</a>
+    </li>
+</ul>
+<div class="panneau" id="panneau1">
+    <h2>Panneau 1</h2>
+</div>
+<div class="panneau" id="panneau2" style="display:none">
+    <h2>Panneau 2</h2>
+</div>
+<div class="panneau" id="panneau3" style="display:none">
+    <h2>Panneau 3</h2>
+</div>

+ 10 - 0
exemples/onglets/script.js

@@ -0,0 +1,10 @@
+var onglets = $('#onglets li a');
+
+onglets.click(function(e) {
+    var link = $(this);
+    onglets.removeClass('active');
+    link.addClass('active');
+    var idPanneau = link.data('panneau-id');
+    $('.panneau').hide();
+    $('#' + idPanneau).show();
+});

+ 17 - 0
exemples/selecteurs-basiques/contenu.html

@@ -0,0 +1,17 @@
+<h5>Sélection par id</h5>
+<button id="ex1-button1">Ajouter/enlever "red"</button>
+
+<h5>Sélection par classe</h5>
+<button class="ex1-buttons">Gris</button>
+<button class="ex1-buttons">Gris</button>
+<button class="ex1-buttons">Gris</button>
+
+<h5>Sélection par tag</h5>
+<p>Cliquer sur un de ces liens lui ajoutera la classe "active" et l'enlèvera aux autres</p>
+<a href="#">Lien 1</a> |
+<a href="#">Lien 2</a> |
+<a href="#">Lien 3</a>
+
+<h5>"Envelopper" un élément DOM</h5>
+<p>Même chose que le 1er bouton, sauf pour la manière de récupérer l'élément</p>
+<button id="ex1-button2">Ajouter/enlever "red"</button>

+ 28 - 0
exemples/selecteurs-basiques/script.js

@@ -0,0 +1,28 @@
+// Sélection par id
+$('#ex1-button1').click(function() {
+  $(this).toggleClass('red');
+});
+
+// Sélection par classe
+$('.ex1-buttons').click(function() {
+  var clickedButton = $(this);
+  clickedButton.toggleClass('blue');
+  var text = clickedButton.hasClass('blue') ?
+    'Bleu' : 'Gris';
+  clickedButton.html(text);
+});
+
+// Sélection par tag
+$('a').click(function(e) {
+  e.preventDefault(); // Empêche le lien de fonctionner
+  $('a').removeClass('active');
+  $(this).addClass('active');
+});
+
+// Envelopper un élément DOM
+var button2 = document.getElementById('ex1-button2');
+// Ici button2 n'est pas une chaîne de caractères
+// mais un élément DOM
+$(button2).click(function() {
+  $(this).toggleClass('red');
+});

+ 16 - 0
exemples/selecteurs-filtres-2/contenu.html

@@ -0,0 +1,16 @@
+<div id="selecteurs-filtres2">
+    <h5>Filtres spéciaux pour détecter qu'une checkbox est cochée</h5>
+    <p><strong>Notez bien dans ce code HTML</strong> que sous chaque div (parent) il y a deux enfants adjacents, input puis span. Ce sont des "siblings" (terme générique pour frères et soeurs... bref de mêmes parents !)</p>
+    <div>
+      <input type="checkbox" name="a">
+      <span>Mary</span>
+    </div>
+    <div>
+      <input type="checkbox" name="b">
+      <span>Wendy</span>
+    </div>
+    <div>
+      <input type="checkbox" name="c" checked="checked">
+      <span>Peter</span>
+    </div>
+</div>

+ 13 - 0
exemples/selecteurs-filtres-2/script.js

@@ -0,0 +1,13 @@
+// Attention ça se complique un peu...
+// Un nouveau filtre :checked ET un nouveau sélecteur avec le +
+// $( "input:checked" ) prendrait tous les checkbox cochés.
+// :not(:checked) inverse le filtre.
+
+// $( "selecteur1 + selecteur2" ) va sélectionner les éléments correspondant au selecteur2, UNIQUEMENT s'ils sont adjacents à un élément correspondant au selecteur1
+// Donc ici seuls les span se trouvant après un checkbox non coché
+// sont sélectionnés
+$( "input:not(:checked) + span" ).css( "background-color", "yellow" );
+
+// Sur tous les inputs on met l'attribut disabled => on ne peut plus
+// les cocher ou les décocher
+$( "#selecteurs-filtres2 input").attr( "disabled", "disabled" );

+ 19 - 0
exemples/selecteurs-filtres/contenu.html

@@ -0,0 +1,19 @@
+<h5>Filtres :first, :last</h5>
+<table class="table" id="filtres-table1">
+    <tr>
+        <td>1ère ligne, 1ère cellule</td>
+        <td>1ère ligne, 2ème cellule</td>
+    </tr>
+    <tr>
+        <td>2ème ligne, 1ère cellule</td>
+        <td>2ème ligne, 2ème cellule</td>
+    </tr>
+    <tr>
+        <td>3ème ligne, 1ère cellule</td>
+        <td>3ème ligne, 2ème cellule</td>
+    </tr>
+    <tr>
+        <td>4ème ligne, 1ère cellule</td>
+        <td>4ème ligne, 2ème cellule</td>
+    </tr>
+</table>

+ 34 - 0
exemples/selecteurs-filtres/script.js

@@ -0,0 +1,34 @@
+// FILTRES :first et :last
+// Ici un intérêt de find: sans find, on aurait écrit
+// $('#filtres-table1 tr:first,#filtres-table1 tr:last')
+// On a donc fait une première sélection (#filtres-table1)
+// Puis une 2ème au sein de cette première
+$('#filtres-table1')
+.find('tr:first,tr:last')
+.addClass('yellow');
+
+// FILTRES :odd et :even
+// Ici le 1er sélecteur :odd prend les lignes IMPAIRES:
+// 1 et 3 donc, en comptant de zéro, les 2ème et 4ème lignes
+// Puis dans chaque ligne de cette sélection il trouve la
+// dernière cellule et la colore en rouge
+$('#filtres-table1 tr:odd')
+.find('td:last')
+.css('color', '#f00');
+// Même idée avec :even et 1ère cellule, color en bleu
+$('#filtres-table1 tr:even')
+.find('td:first')
+.css('color', '#00f');
+
+
+// FILTRE :eq(n) sélectionne l'élément à l'index n
+$('#filtres-table1 tr:eq(2)')
+.find('td:eq(1)')
+.addClass('green');
+
+// FILTRE :not() = négation
+// Ici :not(.green) va sélectionner toutes les cellules
+// n'ayant pas la classe green et les mettre en gras
+$('#filtres-table1 td:not(.green)')
+.removeClass()
+.css('font-weight', 'bold');

+ 19 - 0
exemples/selecteurs-multiples/contenu.html

@@ -0,0 +1,19 @@
+<h5>Sélection de boutons </h5>
+<p>Boutons cyan et magenta&nbsp;: enlève toutes les classes sur event clic</p>
+<div>
+  <button class="cyan bold-text">Cyan 1</button>
+  <button class="cyan">Cyan 2</button>
+</div>
+<div>
+  <button class="magenta underlined-text">Magenta 1</button>
+  <button class="magenta">Magenta 2</button>
+</div>
+<p>Boutons jaune et orange&nbsp;: enlève la classe de couleur, garde le reste</p>
+<div>
+  <button class="yellow underlined-text">Jaune 1</button>
+  <button class="yellow underlined-text">Jaune 2</button>
+</div>
+<div>
+  <button class="orange underlined-text">Orange 1</button>
+  <button class="orange underlined-text">Orange 2</button>
+</div>

+ 11 - 0
exemples/selecteurs-multiples/script.js

@@ -0,0 +1,11 @@
+// Cliquer sur ces boutons enlève leur classe:
+// removeClass() sans argument enlève tout!
+$('.cyan,.magenta').click(function() {
+    $(this).removeClass();
+});
+
+// Cliquer sur ces boutons n'enlève que leur classe yellow ou orange mais garde underline-text:
+// removeClass() peut prendre plusieurs classes comme arguments
+$('.yellow,.orange').click(function() {
+    $(this).removeClass('yellow orange');
+})

+ 54 - 0
exemples/validation-de-formulaires-bootstrap/contenu.html

@@ -0,0 +1,54 @@
+<ul id="onglets" class="tab-nav">
+    <li>
+        <a data-tab-id="tab-login" id="onglet1" class="active" href="#">Login</a>
+    </li>
+    <li class="tab-nav">
+        <a data-tab-id="tab-register" id="onglet2" href="#">Register</a>
+    </li>
+
+</ul>
+
+<div id="alert-box" class="alert alert-danger hidden" role="alert">
+  
+</div>
+
+<div class="tab" id="tab-login">
+    <form id="form-login">
+      <div class="form-group">
+        <label for="login-email">Email address</label>
+        <input type="email" class="form-control" id="login-email" aria-describedby="emailHelp" placeholder="Enter email">
+        <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
+      </div>
+      <div class="form-group">
+        <label for="login-password">Password</label>
+        <input type="password" class="form-control" id="login-password" placeholder="Password">
+      </div>
+      <div class="form-check">
+        <label class="form-check-label">
+          <input type="checkbox" class="form-check-input">
+          Check me out
+        </label>
+      </div>
+      <button type="submit" class="btn btn-primary">Submit</button>
+    </form>
+</div>
+<div class="tab" id="tab-register" style="display:none">
+    <form id="form-register">
+      <div class="form-group">
+        <label for="register-username">Username</label>
+        <input type="text" class="form-control" id="register-username" aria-describedby="usernameHelp" placeholder="Enter username" />
+      </div>
+      <div class="form-group">
+        <label for="register-email">Email address</label>
+        <input type="email" class="form-control" id="register-email" aria-describedby="emailHelp" placeholder="Enter email">
+        <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
+      </div>
+      <div class="form-group">
+        <label for="register-password">Password</label>
+        <input type="password" class="form-control" id="register-password" placeholder="Password">
+      </div>
+
+      <button type="submit" class="btn btn-primary">Register</button>
+    </form>
+</div>
+

+ 107 - 0
exemples/validation-de-formulaires-bootstrap/script.js

@@ -0,0 +1,107 @@
+// Paramètres pour les envois par AJAX
+$.ajaxSetup({
+    headers: {
+        'content-type': 'application/json'
+    }
+});
+
+// Gestion des erreurs
+$( document ).ajaxError(function(event, jqXHR,  ajaxSettings, thrownError) {
+    var data = JSON.parse(jqXHR.responseText);
+    console.log('parsed', data);
+    $('#alert-box')
+    .removeClass('hidden')
+    .removeClass('alert-success')
+    .addClass('alert-danger')
+    .html(data.message);
+});
+
+$('#register-username')
+.change(function(e) {
+    var inputUsername = $(this);
+    var username = inputUsername.val();
+    var re = /^[A-Za-z][A-Za-z0-9_]+$/;
+    var isUsernameValid = username.match(re);
+    if(! isUsernameValid) {
+        inputUsername
+        .addClass('is-invalid')
+        .removeClass('is-valid');
+        return;
+    }
+
+    $.get(
+        'http://localhost:3000/username-check?username=' + username,
+        function(response) {
+            console.log(response.success)
+            if(response.success) {
+                inputUsername
+                .addClass('is-valid')
+                .removeClass('is-invalid');
+            }
+            else {
+                inputUsername
+                .addClass('is-invalid')
+                .removeClass('is-valid');
+                return;
+            }
+        }
+    );
+});
+
+// Soumission du formulaire d'inscription vers le serveur
+$('#form-register').submit(function(e) {
+    var username = $('#register-username').val();
+    var email    = $('#register-email').val();
+    var password = $('#register-password').val();
+    var user = {
+        username: username,
+        email: email,
+        password: password
+    };
+    var userJSON = JSON.stringify(user);
+    console.log(user);
+    console.log(userJSON);
+    e.preventDefault();
+    $(this).find('input').val('');
+    $.post('/register', userJSON, 'json');
+})
+
+
+$('#form-login').submit(function(e) {
+    var email    = $('#login-email').val();
+    var password = $('#login-password').val();
+    var user = {
+        email: email,
+        password: password
+    };
+    var userJSON = JSON.stringify(user);
+    e.preventDefault();
+    $(this).find('input').val('');
+    $.post(
+      '/login',
+      userJSON,
+      function(data) {
+        $('#alert-box')
+        .removeClass('alert-danger')
+        .addClass('alert-success')
+        .removeClass('hidden')
+        .html(data.message);
+      },
+      'json'
+    );
+})
+
+
+
+
+
+var onglets = $('#onglets li a');
+
+onglets.click(function(e) {
+    var link = $(this);
+    onglets.removeClass('active');
+    link.addClass('active');
+    var idPanneau = link.data('tab-id');
+    $('.tab').hide();
+    $('#' + idPanneau).show();
+});

LICENSE.txt → html5bp/LICENSE.txt


browserconfig.xml → html5bp/browserconfig.xml


humans.txt → html5bp/humans.txt


icon.png → html5bp/icon.png


robots.txt → html5bp/robots.txt


site.webmanifest → html5bp/site.webmanifest


tile-wide.png → html5bp/tile-wide.png


tile.png → html5bp/tile.png


BIN
img/vsizegrip.png


+ 63 - 80
index.html

@@ -1,88 +1,71 @@
-<!doctype html>
-<html class="no-js" lang="">
-    <head>
-        <meta charset="utf-8">
-        <meta http-equiv="x-ua-compatible" content="ie=edge">
-        <title>Formation jQuery</title>
-        <meta name="description" content="">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Formation jQuery</title>
+    <link rel="stylesheet" href="css/normalize.css">
+    <link rel="stylesheet" href="css/main.css">
+<!--     <link rel="stylesheet" href="css/qunit-2.4.1.css"> -->
+    <link rel="stylesheet" href="css/bootstrap.min.css">
+    <link rel="stylesheet" href="css/styles.css">
+    <link rel="stylesheet" href="css/bootstrap.min.css">
+<style type="text/css" media="screen">
 
-        <link rel="manifest" href="site.webmanifest">
-        <link rel="apple-touch-icon" href="icon.png">
-        <!-- Place favicon.ico in the root directory -->
+</style>
+</head>
+<body>
 
-        <link rel="stylesheet" href="css/normalize.css">
-        <link rel="stylesheet" href="css/main.css">
-        <link rel="stylesheet" href="css/styles.css">
-    </head>
-    <body>
-        <!--[if lte IE 9]>
-            <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
-        <![endif]-->
+<div class="panel-container">
 
-        <!-- Add your site or application content here -->
-        <div class="container">
-            <p>Hello world! This is HTML5 Boilerplate.</p>
-            <p>Today's date: <span id="todaysDate"></span>.</p>
-            <p>Enter your name please:</p>
-            <form id="helloForm">
-                <input id="inputName" value="" />
-                <button class="blue" type="submit">Say hello again</button>
+    <div class="panel-left">
+        <div id="notification"></div>
+        <div class="panel-inner">
+            <button id="add-example-btn" class="icon-plus rounded blue"></button>
+            <form id="add-example-form" style="display: none;">
+                <input type="text" name="title" class="input-sm" value="" />
+                <button type="button" id="add-example-cancel" class="icon-cross rounded red"></button><!--
+                --><button type="submit" id="add-example-save" class="icon-checkmark rounded green"></button>
             </form>
-            <button class="red" id="clearForm" type="button">Clear form</button>
-
-            <div id="ajax"></div>
-            <button class="blue" id="getAjax" type="button">Get HTML with Ajax</button>
+            <nav id="selector">
+                <select id="file-select">
+                    <option value="-">&mdash;</option>
+                </select>
+            </nav>
+            <nav id="tabs">
+                <ul>
+                    <li><button id="show-html">HTML</button></li>
+                    <li><button id="show-javascript">JS</button></li>
+                </ul>
+            </nav>
+            <button id="save-changes" class="icon-cloud-upload green"></button>
         </div>
-        <script src="js/vendor/modernizr-3.5.0.min.js"></script>
-        <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
-        <script>window.jQuery || document.write('<script src="js/vendor/jquery-3.2.1.min.js"><\/script>')</script>
-        <script src="js/plugins.js"></script>
-        <script src="js/main.js"></script>
-
-        <!-- Google Analytics: change UA-XXXXX-Y to be your site's ID. -->
-        <script>
-            window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;
-            ga('create','UA-XXXXX-Y','auto');ga('send','pageview')
-        </script>
-        <script src="https://www.google-analytics.com/analytics.js" async defer></script>
-        <script>
-          $(document).ready(function() {
-
-            var form = $('#helloForm');
-            var input = $('#inputName');
-            var clearButton = $('#clearForm');
-            var dateSpan = $('#todaysDate');
-            var ajaxButton = $('#getAjax');
-
-            function sayHelloAgain(e) {
-                e.preventDefault();
-                alert('Hello ' + input.val());
-            }
+        <div id="editor"></div>
+        <script type="text/html" id="editor-javascript"></script>
+        <script type="text/html" id="editor-html"></script>
+        <button id="revert-editor">!</button>
+    </div>
 
-            function clearForm() {
-                input.val('');
-            }
+    <div class="splitter">
+    </div>
 
-            function showDate() {
-              var date = new Date().toString();
-              dateSpan.html(date);
-            }
-
-            showDate();
-            setInterval(showDate, 1000);
-
-            function loadDoc() {
-              $.get('some-text.html', function(html) {
-                $('#ajax').html(html);
-              });
-            }
-
-            form.submit(sayHelloAgain);
-            clearButton.click(clearForm);
-            ajaxButton.click(loadDoc);
+    <div class="panel-right">
+        <div id="html-content" class="container"></div>
+        <div id="tests" style="display:none">
+            <div id="qunit"></div>
+            <div id="qunit-fixture"></div>
+        </div>
+    </div>
+</div>
 
-          });
-        </script>
-    </body>
-</html>
+<script src="js/vendor/modernizr-3.5.0.min.js"></script>
+<script src="js/vendor/jquery-3.2.1.min.js" ></script>
+<script src="js/vendor/jquery-resizable.min.js" ></script>
+<!-- <script src="js/vendor/qunit-2.4.1.js" ></script> -->
+<script src="js/vendor/lodash.min.js" ></script>
+<script src="js/vendor/loadJS.js" ></script>
+<script src="js/plugins.js"></script>
+<script src="js/main.js"></script>
+<script src="js/vendor/ace/ace.js" type="text/javascript" charset="utf-8"></script>
+<script src="js/editor-local-storage.js"></script>
+<script src="js/editor.js"></script>
+</body>
+</html>

+ 77 - 0
js/editor-local-storage.js

@@ -0,0 +1,77 @@
+function LocalStorageDraft() {
+  this.storageKey = 'ace_sandbox_draft';
+  this.supportedTypes = ['html', 'javascript', 'css'];
+}
+
+
+
+LocalStorageDraft.prototype.init = function(slug, sources) {
+  this.slug = slug;
+  this.sources = sources || {};
+}
+
+LocalStorageDraft.prototype.getSources = function() {
+  return this.sources;
+}
+
+LocalStorageDraft.prototype.getSource = function(key) {
+  return this.sources[key];
+}
+
+LocalStorageDraft.prototype.hasSource = function(key) {
+  return ! _.isEmpty(this.sources) && this.sources[key];
+}
+
+LocalStorageDraft.prototype.restore = function(slug) {
+  var draft = this.unserialize();
+  if(! draft) {
+    // console.log('no draft found for slug `' + slug + '`');
+    return false;
+  }
+  if(draft.slug !== slug) {
+    // console.log('Saved draft id `' + draft.slug + '` differs from required `' + slug + '`');
+    return false;
+  }
+  // console.log('restore', draft);
+  this.init(draft.slug, draft.sources);
+  return draft;
+}
+
+LocalStorageDraft.prototype.unserialize = function() {
+  var draftJSON = localStorage.getItem(this.storageKey);
+  var draft;
+  if(! draftJSON) {
+    return false;
+  }
+  try {
+    draft = JSON.parse(draftJSON);
+  }
+  catch(e) {
+    // console.log('could not unserialize!! ' + e.message);
+    return false;
+  }
+  return draft;
+}
+
+LocalStorageDraft.prototype.reset = function() {
+  this.slug = undefined;
+  this.sources = undefined;
+  localStorage.removeItem(this.storageKey);
+}
+
+LocalStorageDraft.prototype.serialize = function() {
+  return JSON.stringify({
+    slug: this.slug, sources: this.sources
+  });
+}
+
+
+LocalStorageDraft.prototype.saveSource = function(type, source) {
+  // console.log(this);
+  if(this.supportedTypes.indexOf(type) === -1) {
+    throw new Error('Unsupported type `' + type +
+      '`, allowed types are: ' + this.supportedTypes.join(', '));
+  }
+  this.sources[type] = source;
+  localStorage.setItem(this.storageKey, this.serialize());
+}

+ 271 - 0
js/editor.js

@@ -0,0 +1,271 @@
+$(document).ready(function() {
+
+  var $editor        = $('#editor');
+  var $editorJs      = $('#editor-javascript');
+  var $editorHtml    = $('#editor-html');
+  var $htmlContent   = $('#html-content');
+  var $selectorNav   = $('#selector');
+  var $fileSelect    = $('#file-select');
+  var $addExampleBtn = $('#add-example-btn');
+  var $exampleForm   = $('#add-example-form');
+  var $exampleSave   = $('#add-example-save');
+  var $exampleCancel = $('#add-example-cancel');
+  var $saveChanges   = $('#save-changes');
+  var $notification  = $('#notification');
+  var $revertEditor  = $('#revert-editor');
+  var $panelLeft     = $('.panel-left');
+  var $panelRight    = $('.panel-right');
+  var $window        = $(window);
+  var activeMode     = 'html';
+  var currentHash; 
+  var editor;
+  var editorStorage = new LocalStorageDraft();
+  var saveTimeout1;
+  var saveTimeout2;
+  var exampleList;
+
+  $panelLeft.resizable({
+    handleSelector: ".splitter",
+    resizeHeight: false,
+    onDrag: function(e) {
+      $editor.width($panelLeft.width());
+    }
+  });
+  $window.resize(function() {
+    $editor.width($panelLeft.width());
+    $(".panel-container").height($(window).height());
+  });
+  $(".panel-container").height($(window).height());
+  $editor.width($panelLeft.width());
+
+
+  editor = ace.edit("editor");
+  editor.setTheme("ace/theme/eclipse");
+  editor.$blockScrolling = Infinity;
+  editor.getSession().setUseWrapMode(true);
+
+  function setCurrentHash(slug) {
+    // console.log('setCurrentHash', slug)
+    if(slug) {
+      // console.log('save current hash', slug);
+      window.location.hash = currentHash = slug;
+    }
+    else {
+      currentHash = window.location.hash ?
+          window.location.hash.substr(1) : undefined;
+      // if(currentHash) console.log('restored current hash', currentHash);
+    }
+  }
+
+  function setEditorMode(mode) {
+    editor.getSession().setMode("ace/mode/" + mode);
+  }
+
+  function saveToLocalStorage() {
+    var editorContent =  editor.getSession().getValue();
+    // console.log('saveToLocalStorage', activeMode, editorContent.substr(0, 10) + '[...]');
+    editorStorage.saveSource(activeMode, editorContent);
+    saveTimeout = undefined;
+  }
+
+  function editorContentChanged() {
+    // console.log('editorContent changed')
+    if(saveTimeout1 || saveTimeout2) {
+      clearTimeout(saveTimeout1);
+      clearTimeout(saveTimeout2);
+    }
+    saveTimeout1 = setTimeout(saveToLocalStorage, 500);
+    // saveTimeout2 = setTimeout(saveChanges, 1000);
+  }
+
+  editor.getSession().on('change', editorContentChanged);
+
+  function setActiveTab(mode) {
+    // console.log('setting mode', mode);
+    var elementId = 'show-' + mode;
+    $('#show-' + activeMode).removeClass('active');
+    activeMode = mode;
+    $('#' + elementId).addClass('active');
+    var ed = $('#editor-' + mode);
+    setEditorMode(mode);
+    editor.getSession().off('change');
+    editor.getSession().setValue(ed[0].innerHTML);
+    editor.getSession().on('change', editorContentChanged);
+  }
+
+  $('#tabs button').click(function() {
+    saveToLocalStorage();
+    var mode = $(this).prop('id').substr(5);
+    setActiveTab(mode);
+  })
+
+  function loadAsync(url, dataType) {
+    return new Promise(function(resolve, reject) {
+      $.ajax({
+        type: 'GET',
+        url: url,
+        success: function(data) {
+          resolve(data);
+        },
+        error: function(jqXHR) {
+          reject(new Error(jqXHR.responseText));
+        }
+      }, dataType);
+    });
+  }
+
+  function loadExample(exampleSlug) {
+    // console.log('loadExample', exampleSlug);
+    var serverPath = 'exemples/' + exampleSlug + '/';
+    loadAsync(serverPath + 'script.js', 'text')
+    .then(javascript => $editorJs.html(javascript))
+    .then(() => loadAsync(serverPath + '/contenu.html', 'text'))
+    .then(html => {
+      $editorHtml.html(html);
+      setHtmlContent(html);
+      setActiveTab('html');
+      setCurrentHash(exampleSlug);
+      var sources = {
+        html: $editorHtml.html(),
+        javascript: $editorJs.html()
+      };
+      editorStorage.init(exampleSlug, sources);
+      loadJS(serverPath + 'script.js');
+    })
+    .then(() => {
+      var item = _.find(exampleList, { slug: exampleSlug });
+      // console.log(item.test ? 'test' : 'no test');
+      // loadJS('exemples/' + item.slug + '/test.js', function() {
+      //   $('#tests').show();
+      // });
+    });
+
+  }
+
+  function addFileSelectItem(item) {
+    $fileSelect.append(
+      '<option value="' + item.slug + '">' +
+        item.title +
+      '</option>'
+    );
+  }
+
+  function loadExampleList() {
+    $.get('exemples/liste.json', function(_exampleList) {
+      exampleList = _exampleList;
+      var restoredDraft;
+      exampleList.forEach(addFileSelectItem);
+      if(currentHash) {
+        $fileSelect.val(currentHash);
+        var item = _.find(exampleList, { slug: currentHash });
+        if( ! item) {
+          return;
+        }
+        restoredDraft = editorStorage.restore(item.slug);
+        if(! restoredDraft) {
+          loadExample(item.slug);
+        }
+        else {
+          $editorHtml.html(restoredDraft.sources.html);
+          setHtmlContent(restoredDraft.sources.html);
+          $editorJs.html(restoredDraft.sources.javascript);
+          loadJS('exemples/' + item.slug + '/script.js');
+          // if(item.test) {
+          //   loadJS('exemples/' + item.slug + '/test.js', function() {
+          //     $('#tests').show();
+          //   });
+          // }
+          setActiveTab('html');
+        }
+      }
+
+    }, 'json');
+  }
+
+  function setHtmlContent(html) {
+    $htmlContent.empty();
+    $htmlContent.html(html);
+  }
+
+  function notify(type, text) {
+    $notification
+    .addClass(type)
+    .addClass('active');
+    $notification.html(text);
+    setTimeout(function() {
+      $notification.removeClass('active');
+    }, 2000);
+    setTimeout(function() {
+      $notification.removeClass(type);
+    }, 3000);
+  }
+
+  function toggleEditor() {
+    $addExampleBtn.toggle();
+    $selectorNav.toggle();
+    $exampleForm.toggle();
+  }
+
+  function saveExample(e) {
+    e.preventDefault();
+    var title = $(this).find('input[name="title"]').val();
+    $.ajax({
+      type: 'POST',
+      url: '/examples',
+      data: JSON.stringify({ title }),
+      success: function(newExample) {
+        clearAndCloseEditor();
+        addFileSelectItem(newExample);
+        $fileSelect.val(newExample.slug);
+        $fileSelect.trigger('change');
+        notify('success', "Exemple créé !");
+      },
+      error: function(jqXHR, textStatus, errorThrown ) {
+        notify('error', 'Erreur: ' + jqXHR.responseText);
+      },
+      contentType: 'application/json',
+      dataType: 'json'
+    });
+  }
+
+  function clearAndCloseEditor() {
+    $exampleForm.find('input').val('');
+    toggleEditor();
+  }
+
+  function revertEditor() {
+    editorStorage.reset();
+    location.reload();
+  }
+
+  function saveChanges() {
+    var payload = editorStorage.getSources();
+
+    $.ajax({
+      type: 'PUT',
+      url: '/examples/' + currentHash,
+      data: JSON.stringify(payload),
+      success: function(newExample) {
+        notify('success', "Exemple sauvegardé !");
+        loadExample(currentHash);
+      },
+      error: function(jqXHR, textStatus, errorThrown ) {
+        notify('error', 'Erreur: ' + jqXHR.responseText);
+      },
+      contentType: 'application/json',
+      dataType: 'json'
+    });
+  }
+
+  setCurrentHash();
+
+  $fileSelect.change(function() {
+    loadExample($(this).val());
+  });
+  $addExampleBtn.click(toggleEditor);
+  $exampleCancel.click(clearAndCloseEditor);
+  $saveChanges.click(saveChanges);
+  $exampleForm.submit(saveExample);
+  $revertEditor.click(revertEditor);
+  loadExampleList();
+});

Fichier diff supprimé car celui-ci est trop grand
+ 20270 - 0
js/vendor/ace/ace.js


Fichier diff supprimé car celui-ci est trop grand
+ 699 - 0
js/vendor/ace/mode-css.js


Fichier diff supprimé car celui-ci est trop grand
+ 2480 - 0
js/vendor/ace/mode-html.js


Fichier diff supprimé car celui-ci est trop grand
+ 789 - 0
js/vendor/ace/mode-javascript.js


+ 319 - 0
js/vendor/ace/mode-json.js

@@ -0,0 +1,319 @@
+define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
+
+var JsonHighlightRules = function() {
+    this.$rules = {
+        "start" : [
+            {
+                token : "variable", // single line
+                regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'
+            }, {
+                token : "string", // single line
+                regex : '"',
+                next  : "string"
+            }, {
+                token : "constant.numeric", // hex
+                regex : "0[xX][0-9a-fA-F]+\\b"
+            }, {
+                token : "constant.numeric", // float
+                regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
+            }, {
+                token : "constant.language.boolean",
+                regex : "(?:true|false)\\b"
+            }, {
+                token : "text", // single quoted strings are not allowed
+                regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
+            }, {
+                token : "comment", // comments are not allowed, but who cares?
+                regex : "\\/\\/.*$"
+            }, {
+                token : "comment.start", // comments are not allowed, but who cares?
+                regex : "\\/\\*",
+                next  : "comment"
+            }, {
+                token : "paren.lparen",
+                regex : "[[({]"
+            }, {
+                token : "paren.rparen",
+                regex : "[\\])}]"
+            }, {
+                token : "text",
+                regex : "\\s+"
+            }
+        ],
+        "string" : [
+            {
+                token : "constant.language.escape",
+                regex : /\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/
+            }, {
+                token : "string",
+                regex : '"|$',
+                next  : "start"
+            }, {
+                defaultToken : "string"
+            }
+        ],
+        "comment" : [
+            {
+                token : "comment.end", // comments are not allowed, but who cares?
+                regex : "\\*\\/",
+                next  : "start"
+            }, {
+                defaultToken: "comment"
+            }
+        ]
+    };
+    
+};
+
+oop.inherits(JsonHighlightRules, TextHighlightRules);
+
+exports.JsonHighlightRules = JsonHighlightRules;
+});
+
+define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(require, exports, module) {
+"use strict";
+
+var Range = require("../range").Range;
+
+var MatchingBraceOutdent = function() {};
+
+(function() {
+
+    this.checkOutdent = function(line, input) {
+        if (! /^\s+$/.test(line))
+            return false;
+
+        return /^\s*\}/.test(input);
+    };
+
+    this.autoOutdent = function(doc, row) {
+        var line = doc.getLine(row);
+        var match = line.match(/^(\s*\})/);
+
+        if (!match) return 0;
+
+        var column = match[1].length;
+        var openBracePos = doc.findMatchingBracket({row: row, column: column});
+
+        if (!openBracePos || openBracePos.row == row) return 0;
+
+        var indent = this.$getIndent(doc.getLine(openBracePos.row));
+        doc.replace(new Range(row, 0, row, column-1), indent);
+    };
+
+    this.$getIndent = function(line) {
+        return line.match(/^\s*/)[0];
+    };
+
+}).call(MatchingBraceOutdent.prototype);
+
+exports.MatchingBraceOutdent = MatchingBraceOutdent;
+});
+
+define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(require, exports, module) {
+"use strict";
+
+var oop = require("../../lib/oop");
+var Range = require("../../range").Range;
+var BaseFoldMode = require("./fold_mode").FoldMode;
+
+var FoldMode = exports.FoldMode = function(commentRegex) {
+    if (commentRegex) {
+        this.foldingStartMarker = new RegExp(
+            this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
+        );
+        this.foldingStopMarker = new RegExp(
+            this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
+        );
+    }
+};
+oop.inherits(FoldMode, BaseFoldMode);
+
+(function() {
+    
+    this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/;
+    this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/;
+    this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/;
+    this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
+    this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
+    this._getFoldWidgetBase = this.getFoldWidget;
+    this.getFoldWidget = function(session, foldStyle, row) {
+        var line = session.getLine(row);
+    
+        if (this.singleLineBlockCommentRe.test(line)) {
+            if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
+                return "";
+        }
+    
+        var fw = this._getFoldWidgetBase(session, foldStyle, row);
+    
+        if (!fw && this.startRegionRe.test(line))
+            return "start"; // lineCommentRegionStart
+    
+        return fw;
+    };
+
+    this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
+        var line = session.getLine(row);
+        
+        if (this.startRegionRe.test(line))
+            return this.getCommentRegionBlock(session, line, row);
+        
+        var match = line.match(this.foldingStartMarker);
+        if (match) {
+            var i = match.index;
+
+            if (match[1])
+                return this.openingBracketBlock(session, match[1], row, i);
+                
+            var range = session.getCommentFoldRange(row, i + match[0].length, 1);
+            
+            if (range && !range.isMultiLine()) {
+                if (forceMultiline) {
+                    range = this.getSectionRange(session, row);
+                } else if (foldStyle != "all")
+                    range = null;
+            }
+            
+            return range;
+        }
+
+        if (foldStyle === "markbegin")
+            return;
+
+        var match = line.match(this.foldingStopMarker);
+        if (match) {
+            var i = match.index + match[0].length;
+
+            if (match[1])
+                return this.closingBracketBlock(session, match[1], row, i);
+
+            return session.getCommentFoldRange(row, i, -1);
+        }
+    };
+    
+    this.getSectionRange = function(session, row) {
+        var line = session.getLine(row);
+        var startIndent = line.search(/\S/);
+        var startRow = row;
+        var startColumn = line.length;
+        row = row + 1;
+        var endRow = row;
+        var maxRow = session.getLength();
+        while (++row < maxRow) {
+            line = session.getLine(row);
+            var indent = line.search(/\S/);
+            if (indent === -1)
+                continue;
+            if  (startIndent > indent)
+                break;
+            var subRange = this.getFoldWidgetRange(session, "all", row);
+            
+            if (subRange) {
+                if (subRange.start.row <= startRow) {
+                    break;
+                } else if (subRange.isMultiLine()) {
+                    row = subRange.end.row;
+                } else if (startIndent == indent) {
+                    break;
+                }
+            }
+            endRow = row;
+        }
+        
+        return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
+    };
+    this.getCommentRegionBlock = function(session, line, row) {
+        var startColumn = line.search(/\s*$/);
+        var maxRow = session.getLength();
+        var startRow = row;
+        
+        var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
+        var depth = 1;
+        while (++row < maxRow) {
+            line = session.getLine(row);
+            var m = re.exec(line);
+            if (!m) continue;
+            if (m[1]) depth--;
+            else depth++;
+
+            if (!depth) break;
+        }
+
+        var endRow = row;
+        if (endRow > startRow) {
+            return new Range(startRow, startColumn, endRow, line.length);
+        }
+    };
+
+}).call(FoldMode.prototype);
+
+});
+
+define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"], function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var TextMode = require("./text").Mode;
+var HighlightRules = require("./json_highlight_rules").JsonHighlightRules;
+var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
+var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
+var CStyleFoldMode = require("./folding/cstyle").FoldMode;
+var WorkerClient = require("../worker/worker_client").WorkerClient;
+
+var Mode = function() {
+    this.HighlightRules = HighlightRules;
+    this.$outdent = new MatchingBraceOutdent();
+    this.$behaviour = new CstyleBehaviour();
+    this.foldingRules = new CStyleFoldMode();
+};
+oop.inherits(Mode, TextMode);
+
+(function() {
+
+    this.getNextLineIndent = function(state, line, tab) {
+        var indent = this.$getIndent(line);
+
+        if (state == "start") {
+            var match = line.match(/^.*[\{\(\[]\s*$/);
+            if (match) {
+                indent += tab;
+            }
+        }
+
+        return indent;
+    };
+
+    this.checkOutdent = function(state, line, input) {
+        return this.$outdent.checkOutdent(line, input);
+    };
+
+    this.autoOutdent = function(state, doc, row) {
+        this.$outdent.autoOutdent(doc, row);
+    };
+
+    this.createWorker = function(session) {
+        var worker = new WorkerClient(["ace"], "ace/mode/json_worker", "JsonWorker");
+        worker.attachToDocument(session.getDocument());
+
+        worker.on("annotate", function(e) {
+            session.setAnnotations(e.data);
+        });
+
+        worker.on("terminate", function() {
+            session.clearAnnotations();
+        });
+
+        return worker;
+    };
+
+
+    this.$id = "ace/mode/json";
+}).call(Mode.prototype);
+
+exports.Mode = Mode;
+});

+ 98 - 0
js/vendor/ace/theme-eclipse.js

@@ -0,0 +1,98 @@
+define("ace/theme/eclipse",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
+"use strict";
+
+exports.isDark = false;
+exports.cssText = ".ace-eclipse .ace_gutter {\
+background: #ebebeb;\
+border-right: 1px solid rgb(159, 159, 159);\
+color: rgb(136, 136, 136);\
+}\
+.ace-eclipse .ace_print-margin {\
+width: 1px;\
+background: #ebebeb;\
+}\
+.ace-eclipse {\
+background-color: #FFFFFF;\
+color: black;\
+}\
+.ace-eclipse .ace_fold {\
+background-color: rgb(60, 76, 114);\
+}\
+.ace-eclipse .ace_cursor {\
+color: black;\
+}\
+.ace-eclipse .ace_storage,\
+.ace-eclipse .ace_keyword,\
+.ace-eclipse .ace_variable {\
+color: rgb(127, 0, 85);\
+}\
+.ace-eclipse .ace_constant.ace_buildin {\
+color: rgb(88, 72, 246);\
+}\
+.ace-eclipse .ace_constant.ace_library {\
+color: rgb(6, 150, 14);\
+}\
+.ace-eclipse .ace_function {\
+color: rgb(60, 76, 114);\
+}\
+.ace-eclipse .ace_string {\
+color: rgb(42, 0, 255);\
+}\
+.ace-eclipse .ace_comment {\
+color: rgb(113, 150, 130);\
+}\
+.ace-eclipse .ace_comment.ace_doc {\
+color: rgb(63, 95, 191);\
+}\
+.ace-eclipse .ace_comment.ace_doc.ace_tag {\
+color: rgb(127, 159, 191);\
+}\
+.ace-eclipse .ace_constant.ace_numeric {\
+color: darkblue;\
+}\
+.ace-eclipse .ace_tag {\
+color: rgb(25, 118, 116);\
+}\
+.ace-eclipse .ace_type {\
+color: rgb(127, 0, 127);\
+}\
+.ace-eclipse .ace_xml-pe {\
+color: rgb(104, 104, 91);\
+}\
+.ace-eclipse .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-eclipse .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-eclipse .ace_meta.ace_tag {\
+color:rgb(25, 118, 116);\
+}\
+.ace-eclipse .ace_invisible {\
+color: #ddd;\
+}\
+.ace-eclipse .ace_entity.ace_other.ace_attribute-name {\
+color:rgb(127, 0, 127);\
+}\
+.ace-eclipse .ace_marker-layer .ace_step {\
+background: rgb(255, 255, 0);\
+}\
+.ace-eclipse .ace_active-line {\
+background: rgb(232, 242, 254);\
+}\
+.ace-eclipse .ace_gutter-active-line {\
+background-color : #DADADA;\
+}\
+.ace-eclipse .ace_marker-layer .ace_selected-word {\
+border: 1px solid rgb(181, 213, 255);\
+}\
+.ace-eclipse .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}";
+
+exports.cssClass = "ace-eclipse";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});

+ 105 - 0
js/vendor/ace/theme-monokai.js

@@ -0,0 +1,105 @@
+define("ace/theme/monokai",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
+
+exports.isDark = true;
+exports.cssClass = "ace-monokai";
+exports.cssText = ".ace-monokai .ace_gutter {\
+background: #2F3129;\
+color: #8F908A\
+}\
+.ace-monokai .ace_print-margin {\
+width: 1px;\
+background: #555651\
+}\
+.ace-monokai {\
+background-color: #272822;\
+color: #F8F8F2\
+}\
+.ace-monokai .ace_cursor {\
+color: #F8F8F0\
+}\
+.ace-monokai .ace_marker-layer .ace_selection {\
+background: #49483E\
+}\
+.ace-monokai.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #272822;\
+}\
+.ace-monokai .ace_marker-layer .ace_step {\
+background: rgb(102, 82, 0)\
+}\
+.ace-monokai .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #49483E\
+}\
+.ace-monokai .ace_marker-layer .ace_active-line {\
+background: #202020\
+}\
+.ace-monokai .ace_gutter-active-line {\
+background-color: #272727\
+}\
+.ace-monokai .ace_marker-layer .ace_selected-word {\
+border: 1px solid #49483E\
+}\
+.ace-monokai .ace_invisible {\
+color: #52524d\
+}\
+.ace-monokai .ace_entity.ace_name.ace_tag,\
+.ace-monokai .ace_keyword,\
+.ace-monokai .ace_meta.ace_tag,\
+.ace-monokai .ace_storage {\
+color: #F92672\
+}\
+.ace-monokai .ace_punctuation,\
+.ace-monokai .ace_punctuation.ace_tag {\
+color: #fff\
+}\
+.ace-monokai .ace_constant.ace_character,\
+.ace-monokai .ace_constant.ace_language,\
+.ace-monokai .ace_constant.ace_numeric,\
+.ace-monokai .ace_constant.ace_other {\
+color: #AE81FF\
+}\
+.ace-monokai .ace_invalid {\
+color: #F8F8F0;\
+background-color: #F92672\
+}\
+.ace-monokai .ace_invalid.ace_deprecated {\
+color: #F8F8F0;\
+background-color: #AE81FF\
+}\
+.ace-monokai .ace_support.ace_constant,\
+.ace-monokai .ace_support.ace_function {\
+color: #66D9EF\
+}\
+.ace-monokai .ace_fold {\
+background-color: #A6E22E;\
+border-color: #F8F8F2\
+}\
+.ace-monokai .ace_storage.ace_type,\
+.ace-monokai .ace_support.ace_class,\
+.ace-monokai .ace_support.ace_type {\
+font-style: italic;\
+color: #66D9EF\
+}\
+.ace-monokai .ace_entity.ace_name.ace_function,\
+.ace-monokai .ace_entity.ace_other,\
+.ace-monokai .ace_entity.ace_other.ace_attribute-name,\
+.ace-monokai .ace_variable {\
+color: #A6E22E\
+}\
+.ace-monokai .ace_variable.ace_parameter {\
+font-style: italic;\
+color: #FD971F\
+}\
+.ace-monokai .ace_string {\
+color: #E6DB74\
+}\
+.ace-monokai .ace_comment {\
+color: #75715E\
+}\
+.ace-monokai .ace_indent-guide {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});

Fichier diff supprimé car celui-ci est trop grand
+ 8761 - 0
js/vendor/ace/worker-css.js


Fichier diff supprimé car celui-ci est trop grand
+ 11605 - 0
js/vendor/ace/worker-html.js


Fichier diff supprimé car celui-ci est trop grand
+ 12528 - 0
js/vendor/ace/worker-javascript.js


Fichier diff supprimé car celui-ci est trop grand
+ 2 - 0
js/vendor/jquery-resizable.min.js


+ 22 - 0
js/vendor/loadJS.js

@@ -0,0 +1,22 @@
+/*! loadJS: load a JS file asynchronously. [c]2014 @scottjehl, Filament Group, Inc. (Based on http://goo.gl/REQGQ by Paul Irish). Licensed MIT */
+(function( w ){
+	var loadJS = function( src, cb ){
+		"use strict";
+		var ref = w.document.getElementsByTagName( "script" )[ 0 ];
+		var script = w.document.createElement( "script" );
+		script.src = src;
+		script.async = true;
+		ref.parentNode.insertBefore( script, ref );
+		if (cb && typeof(cb) === "function") {
+			script.onload = cb;
+		}
+		return script;
+	};
+	// commonjs
+	if( typeof module !== "undefined" ){
+		module.exports = loadJS;
+	}
+	else {
+		w.loadJS = loadJS;
+	}
+}( typeof global !== "undefined" ? global : this ));

Fichier diff supprimé car celui-ci est trop grand
+ 136 - 0
js/vendor/lodash.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 5061 - 0
js/vendor/qunit-2.4.1.js


+ 11 - 0
package.json

@@ -0,0 +1,11 @@
+{
+  "name": "ipi-inline-editor",
+  "dependencies": {
+    "body-parser": "^1.18.2",
+    "cors": "^2.8.4",
+    "express": "^4.16.2",
+    "json-beautify": "^1.0.1",
+    "lodash": "^4.17.4",
+    "slug": "^0.9.1"
+  }
+}

+ 43 - 0
produits.json

@@ -0,0 +1,43 @@
+[
+  {
+    "id": 1,
+    "titre": "Carte mère Socket 1150",
+    "image": "https://www.topachat.com/images/interface/category/w_/w_cm_1150/w_cm_1150-200x200.jpg"
+  },
+  {
+    "id": 2,
+    "titre": "ASRock E3C236D2I ",
+    "image": "https://media.ldlc.com/ld/products/00/03/46/13/LD0003461325_2.jpg"
+  },
+  {
+    "id": 3,
+    "titre": "Asus K8v",
+    "image": "https://www.config-gamer.fr/media/k2/galleries/2943/00060014-photo-carte-mere-asus-k8v.jpg"
+  },
+  {
+    "id": 4,
+    "titre": "ASUS P8B75-M LE",
+    "image": "https://media.ldlc.com/ld/products/00/01/05/08/LD0001050880_2.jpg"
+  },
+  {
+    "id": 5,
+    "titre": "ASRock E3C236D4U",
+    "image": "https://media.ldlc.com/ld/products/00/03/46/18/LD0003461887_2.jpg"
+  },
+  {
+    "id": 6,
+    "titre": "Apple iPhone SE 32Go",
+    "image": "https://boulanger.scene7.com/is/image/Boulanger/bfr_overlay?layer=comp&$t1=&$product_id=Boulanger/0190198292230_h_f_l_0"
+  },
+  {
+    "id": 7,
+    "titre": "Smartphone Ordissimo",
+    "image": "http://www.ordissimo.com/698-thickbox_default/smartphone-ordissimo.jpg"
+  },
+  {
+    "id": 8,
+    "titre": "Random Smartphone",
+    "image": "https://media.wired.com/photos/593284aeaef9a462de98365f/master/w_1000,c_limit/2014-09-23-iphone6-gallery-1.jpg"
+  }
+
+]

+ 57 - 0
sandboxApp.js

@@ -0,0 +1,57 @@
+var express      = require('express');
+var bodyParser   = require('body-parser');
+var slug         = require('slug');
+var beautify     = require("json-beautify");
+var _            = require('lodash');
+var fs           = require('fs');
+var app          = express();
+var examplesJSON = __dirname + '/exemples/liste.json';
+var examples     = require(examplesJSON);
+
+app.use(express.static(__dirname));
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: true }));
+
+function addExample(slug, title) {
+  examples.push({ slug: slug, title: title });
+  fs.writeFileSync(examplesJSON, beautify(examples, null, 2, 100));
+}
+
+app.post('/examples', function(req, res) {
+  var title = req.body.title;
+  if(! req.body.title) {
+    res.status(400).send('Le titre ne peut pas être vide !');
+  }
+  var existingTitle = _.find(examples, { title: title });
+  if(existingTitle) {
+    res.status(400).send("L'exemple '" + title + "' existe déjà !");
+  }
+  var exampleSlug = slug(req.body.title.toLowerCase());
+  var targetDir = __dirname + '/exemples/' + exampleSlug;
+
+  fs.mkdirSync(targetDir);
+  fs.writeFileSync(targetDir + '/contenu.html', '');
+  fs.writeFileSync(targetDir + '/script.js', '');
+  addExample(exampleSlug, title);
+  res.json({ slug: exampleSlug, title: title });
+});
+
+app.put('/examples/:slug', function(req, res) {
+  var slug = req.params.slug;
+  var existing = _.find(examples, { slug: slug });
+  if(! existing) {
+    res.status(404).send("L'exemple avec l'identifiant '" + slug + "' est introuvable !");
+  }
+  var targetDir = __dirname + '/exemples/' + slug;
+  if(req.body.html) {
+    fs.writeFileSync(targetDir + '/contenu.html', req.body.html);
+  }
+  if(req.body.javascript) {
+    fs.writeFileSync(targetDir + '/script.js', req.body.javascript);
+  }
+  var theDate = new Date();
+  console.log(theDate.getHours() + ':' + theDate.getMinutes() + " - Sauvegarde de l'exemple '" + existing.title + " effectuée'");
+  res.json({ success: true });
+});
+
+module.exports = app;

+ 151 - 0
server.js

@@ -0,0 +1,151 @@
+/**
+ * ATTENTION le fichier sandboxApp.js est assez complexe.
+ * Il sert à initialiser l'appli permettant d'avoir l'éditeur HTML&JavaScript en ligne
+ */
+var app = require('./sandboxApp');
+
+
+
+/*-------------------------------------------------------*
+ | Déclaration de gestionnaires pour les exemples AJAX
+ *-------------------------------------------------------*
+ |
+ */
+
+// 1er exemple: envoie du HTML généré dynamiquement.
+// On utilise app.get pour attacher un gestionnaire à l'URL /ajax-example
+// uniquement avec la méthode GET
+app.get('/ajax-example', function(req, res) {
+  var date = new Date();
+  var exampleHtml = '<p>Un peu de HTML retourné par le serveur.</p>' +
+    '<p><em>Généré le: ' + date.toString() + '</em></p>';
+  res.send(exampleHtml);
+});
+
+// 2ème exemple : envoi formulaire par méthode GET
+// Notez bien d'où on extrait les paramètres passé par le client (req.query)
+app.get('/ajax-form-get', function(req, res) {
+  var name = req.query.name;
+  var birthdate = req.query.birthdate;
+  var exampleHtml = '<p>Salutations, <em>' + name +
+    '</em>, né(e) le <em>' + birthdate + '</em>.</p>';
+  res.send(exampleHtml);
+});
+
+// 3ème exemple : envoi formulaire par méthode POST
+// Ici les paramètres viennent de req.body
+app.post('/ajax-form-post', function(req, res) {
+  var title = req.body.title;
+  var text = req.body.text;
+  var exampleHtml = '<h2>' + title + '</h2>' +
+    '<p>' + text + '</p>';
+  res.send(exampleHtml);
+});
+
+
+/**
+ * Création d'un tableau vide où on va stocker les utilisateurs
+ */
+var userList = [];
+var userId = 1;
+
+/**
+ * Création d'un utilisateur : on le stocke dans le tableau userList
+ * ATTENTION ! Dans la "vraie vie", on utiliserait une base de données !
+ */
+function createNewUser(user) {
+
+  // On vérifie que les données de l'utilisateur sont renseignées
+  // Si non, on renvoie false
+  if(! user || ! user.username || ! user.email || ! user.password) {
+    return false;
+  }
+
+  // L'étape de vérification a réussi, on insère l'utilisateur dans le tableau
+  // On crée un faux "id" pour simuler une insertion SQL. En SQL les "id" sont
+  // incrémentés à chaque insertion. On simule cela en incrémentant un compteur
+  // "userId" à chaque insertion
+  userList.push({
+    id: userId++,
+    username: user.username,
+    email:    user.email,
+    password: user.password
+  });
+  return true;
+}
+
+
+app.post('/login', function(req, res) {
+
+  // Récupérer email et password
+  var identifiants = req.body;
+  if(! identifiants.email || ! identifiants.password) {
+    return res.status(400).json({
+      message: 'paramètre requis manquant'
+    });
+  }
+
+  for(var i = 0 ; i < userList.length ; i++) {
+    var user = userList[i];
+
+    if(identifiants.email == user.email) {
+
+
+      if(identifiants.password == user.password) {
+        return res.json({
+          message: 'Vous avez été identifié',
+        });
+      }
+      else {
+        return res.status(401).json({
+          message: 'Mot de passe incorrect'
+        });
+      }
+
+    }
+
+  }
+  return res.status(404).json({
+    message: 'Utilisateur non trouvé'
+  });
+
+});
+
+
+/**
+ * Ce code va gérer la requête POST vers l'URL /register de notre micro-serveur
+ */
+app.post('/register', function(req, res) {
+
+  // Les données envoyées par le client (navigateur) sont dans la propriété "body"
+  // de l'objet req (pour request)
+  var user = req.body;
+
+  // createNewUser() va nous renvoyer true ou false
+  var success = createNewUser(user);
+
+  if(success) {
+    // Envoyer un message sur la console du serveur
+    console.log('Utilisateur enregistré: ', user, '\nListe des utilisateurs', userList);
+    res.json({ user: user });
+  }
+  else {
+    res.status(400).json({ error: 'Champs manquants dans la requête' });
+  }
+});
+
+app.get('/username-check', function(req, res) {
+  var username = req.query.username;
+  for(u = 0 ; u < userList.length ; u++) {
+    if(username === userList[u].username) {
+      return res.json({ success: false });
+    }
+  }
+  res.json({ success: true });
+});
+
+
+
+
+console.log('Le serveur écoute sur le port 3000. Laissez cette console ouverte !')
+app.listen(3000);

+ 0 - 2
some-text.html

@@ -1,2 +0,0 @@
-<h2>This is an example</h2>
-<p>I got this text with an AJAX request</p>