diff --git a/assets/bundle.js b/assets/bundle.js index c639e17..3318f06 100644 --- a/assets/bundle.js +++ b/assets/bundle.js @@ -1,19 +1,23 @@ -function htmlToElement(html) { +const $$ = function(selector) { + return document.querySelectorAll(selector) || []; +}; + +const makeEl = function(html) { const template = document.createElement('template'); template.innerHTML = html.trim(); return template.content.firstChild; -} +}; -function escapeHtml(unsafe) { +const escape = function(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); -} +}; class TagsInput { constructor(element, options = {}) { @@ -29,12 +33,19 @@ class TagsInput { attach() { this.element.style.display = 'none'; - this.containerNode = htmlToElement('
'); - this.inputNode = htmlToElement(''); + this.containerNode = makeEl('
'); + this.inputNode = makeEl(''); this.containerNode.appendChild(this.inputNode); this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling); + /* Load existing tags from input */ + if (this.element.value) { + for (const tag of this.element.value.split(',')) { + this.addTag(tag); + } + } + /* Handle addition and removal of tags via key-presses */ this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this)); @@ -70,7 +81,7 @@ class TagsInput { this.tags.push(tagValue.toLowerCase()); this.inputNode.parentNode.insertBefore( - htmlToElement('' + escapeHtml(tagValue) + ''), + makeEl('' + escape(tagValue) + ''), this.inputNode ); @@ -109,18 +120,14 @@ class TagsInput { } } -class Meme { - constructor() { - alert('xss'); - } +const setupSite = function() { + Array.prototype.forEach.call($$('.js-tag-input'), (el) => { + new TagsInput(el).attach(); + }); +}; - meme() { - console.log('meme'); - } +if (document.readyState !== 'loading') { + setupSite(); +} else { + document.addEventListener('DOMContentLoaded', setupSite); } - -const meme = new Meme(); - -meme.meme(); - -new TagsInput(null); diff --git a/assets/bundle.min.js b/assets/bundle.min.js index 07602c0..31de244 100644 --- a/assets/bundle.min.js +++ b/assets/bundle.min.js @@ -1,2 +1,2 @@ -function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;n/g,">").replace(/"/g,""").replace(/'/g,"'")}var s=function(){function t(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e(this,t),this.element=n,this.tags=[],this.options=i,this.maxTags=i.maxTags||10,this.inputNode=null,this.containerNode=null}return n(t,[{key:"attach",value:function(){this.element.style.display="none",this.containerNode=i('
'),this.inputNode=i(''),this.containerNode.appendChild(this.inputNode),this.element.parentNode.insertBefore(this.containerNode,this.element.nextSibling),this.containerNode.addEventListener("keydown",this._handleInputKeyUp.bind(this)),this.containerNode.addEventListener("click",this._handleContainerClick.bind(this))}},{key:"detach",value:function(){this.tags.clear(),this.containerNode.remove(),this.element.style.display="inline-block"}},{key:"updateHiddenInputValue",value:function(){this.element.value=this.tags.join(",")}},{key:"deleteTagNode",value:function(e){this.tags.splice(this.tags.indexOf(e.dataset.value.toLowerCase()),1),e.remove(),this.tags.length'+a(e)+''),this.inputNode),this.tags.length>=this.maxTags&&(this.inputNode.disabled=!0))}},{key:"_handleInputKeyUp",value:function(e){var t=this.inputNode.value;"Backspace"===e.key&&""===t?this.inputNode.previousSibling&&(this.deleteTagNode(this.inputNode.previousSibling),this.updateHiddenInputValue()):","===e.key&&(this.addTag(t),this.inputNode.value="",this.updateHiddenInputValue(),e.preventDefault())}},{key:"_handleContainerClick",value:function(e){e.target&&e.target.classList.contains("delete")&&(this.deleteTagNode(e.target.closest(".tag")),this.updateHiddenInputValue())}}]),t}();(new(function(){function t(){e(this,t),alert("xss")}return n(t,[{key:"meme",value:function(){console.log("meme")}}]),t}())).meme(),new s(null); +function e(e,n){var i="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!i){if(Array.isArray(e)||(i=function(e,n){if(!e)return;if("string"==typeof e)return t(e,n);var i=Object.prototype.toString.call(e).slice(8,-1);"Object"===i&&e.constructor&&(i=e.constructor.name);if("Map"===i||"Set"===i)return Array.from(e);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return t(e,n)}(e))||n&&e&&"number"==typeof e.length){i&&(e=i);var a=0,r=function(){};return{s:r,n:function(){return a>=e.length?{done:!0}:{done:!1,value:e[a++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,l=!1;return{s:function(){i=i.call(e)},n:function(){var e=i.next();return s=e.done,e},e:function(e){l=!0,o=e},f:function(){try{s||null==i.return||i.return()}finally{if(l)throw o}}}}function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n/g,">").replace(/"/g,""").replace(/'/g,"'")},o=function(){function t(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};n(this,t),this.element=e,this.tags=[],this.options=i,this.maxTags=i.maxTags||10,this.inputNode=null,this.containerNode=null}var o,s,l;return o=t,(s=[{key:"attach",value:function(){if(this.element.style.display="none",this.containerNode=a('
'),this.inputNode=a(''),this.containerNode.appendChild(this.inputNode),this.element.parentNode.insertBefore(this.containerNode,this.element.nextSibling),this.element.value){var t,n=e(this.element.value.split(","));try{for(n.s();!(t=n.n()).done;){var i=t.value;this.addTag(i)}}catch(e){n.e(e)}finally{n.f()}}this.containerNode.addEventListener("keydown",this._handleInputKeyUp.bind(this)),this.containerNode.addEventListener("click",this._handleContainerClick.bind(this))}},{key:"detach",value:function(){this.tags.clear(),this.containerNode.remove(),this.element.style.display="inline-block"}},{key:"updateHiddenInputValue",value:function(){this.element.value=this.tags.join(",")}},{key:"deleteTagNode",value:function(e){this.tags.splice(this.tags.indexOf(e.dataset.value.toLowerCase()),1),e.remove(),this.tags.length'+r(e)+''),this.inputNode),this.tags.length>=this.maxTags&&(this.inputNode.disabled=!0))}},{key:"_handleInputKeyUp",value:function(e){var t=this.inputNode.value;"Backspace"===e.key&&""===t?this.inputNode.previousSibling&&(this.deleteTagNode(this.inputNode.previousSibling),this.updateHiddenInputValue()):","===e.key&&(this.addTag(t),this.inputNode.value="",this.updateHiddenInputValue(),e.preventDefault())}},{key:"_handleContainerClick",value:function(e){e.target&&e.target.classList.contains("delete")&&(this.deleteTagNode(e.target.closest(".tag")),this.updateHiddenInputValue())}}])&&i(o.prototype,s),l&&i(o,l),t}(),s=function(){var e;Array.prototype.forEach.call((e=".js-tag-input",document.querySelectorAll(e)||[]),(function(e){new o(e).attach()}))};"loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s); //# sourceMappingURL=bundle.min.js.map diff --git a/assets/bundle.min.js.map b/assets/bundle.min.js.map index 70a8a93..c000156 100644 --- a/assets/bundle.min.js.map +++ b/assets/bundle.min.js.map @@ -1 +1 @@ -{"version":3,"file":"bundle.min.js","sources":["../js/tag_input.js","../js/main.js"],"sourcesContent":["function htmlToElement(html) {\n const template = document.createElement('template');\n\n template.innerHTML = html.trim();\n\n return template.content.firstChild;\n}\n\nfunction escapeHtml(unsafe) {\n return unsafe\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nclass TagsInput {\n constructor(element, options = {}) {\n this.element = element;\n this.tags = [];\n this.options = options\n\n this.maxTags = options.maxTags || 10;\n this.inputNode = null;\n this.containerNode = null;\n }\n\n attach() {\n this.element.style.display = 'none';\n\n this.containerNode = htmlToElement('
');\n this.inputNode = htmlToElement('');\n this.containerNode.appendChild(this.inputNode);\n\n this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);\n\n /* Handle addition and removal of tags via key-presses */\n this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));\n\n /* Handle deletions by clicking the delete button */\n this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));\n }\n\n detach() {\n this.tags.clear();\n this.containerNode.remove();\n this.element.style.display = 'inline-block';\n }\n\n updateHiddenInputValue() {\n this.element.value = this.tags.join(',');\n }\n\n deleteTagNode(node) {\n this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);\n node.remove();\n\n /* Below the limit? Make sure the input is enabled. */\n if (this.tags.length < this.maxTags) {\n this.inputNode.disabled = false;\n }\n }\n\n addTag(tagValue) {\n tagValue = tagValue.trim();\n\n /* Tag value is probably not empty and we don't already have the same tag. */\n if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {\n this.tags.push(tagValue.toLowerCase());\n\n this.inputNode.parentNode.insertBefore(\n htmlToElement('' + escapeHtml(tagValue) + ''),\n this.inputNode\n );\n\n /* Too many tags, disable the input for now. */\n if (this.tags.length >= this.maxTags) {\n this.inputNode.disabled = true;\n }\n }\n }\n\n _handleInputKeyUp(evt) {\n let tagValue = this.inputNode.value;\n\n if (evt.key === 'Backspace' && tagValue === '') {\n // Remove the child\n if (this.inputNode.previousSibling) {\n this.deleteTagNode(this.inputNode.previousSibling);\n\n this.updateHiddenInputValue();\n }\n } else if (evt.key === ',') {\n this.addTag(tagValue);\n\n this.inputNode.value = ''\n this.updateHiddenInputValue();\n\n evt.preventDefault();\n }\n }\n\n _handleContainerClick(evt) {\n if (evt.target && evt.target.classList.contains('delete')) {\n this.deleteTagNode(evt.target.closest('.tag'));\n this.updateHiddenInputValue();\n }\n }\n}\n\nexport { TagsInput };","import { TagsInput } from './tag_input';\n\nclass Meme {\n constructor() {\n alert('xss');\n }\n\n meme() {\n console.log('meme');\n }\n}\n\nconst meme = new Meme();\n\nmeme.meme();\n\nnew TagsInput(null);"],"names":["htmlToElement","html","template","document","createElement","innerHTML","trim","content","firstChild","escapeHtml","unsafe","replace","TagsInput","element","options","tags","maxTags","inputNode","containerNode","style","display","appendChild","this","parentNode","insertBefore","nextSibling","addEventListener","_handleInputKeyUp","bind","_handleContainerClick","clear","remove","value","join","node","splice","indexOf","dataset","toLowerCase","length","disabled","tagValue","push","evt","key","previousSibling","deleteTagNode","updateHiddenInputValue","addTag","preventDefault","target","classList","contains","closest","alert","console","log","meme"],"mappings":"6TAAA,SAASA,EAAcC,OACbC,EAAWC,SAASC,cAAc,mBAExCF,EAASG,UAAYJ,EAAKK,OAEnBJ,EAASK,QAAQC,WAG5B,SAASC,EAAWC,UACTA,EACFC,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,cAGjBC,wBACUC,OAASC,yDAAU,kBACtBD,QAAUA,OACVE,KAAO,QACPD,QAAUA,OAEVE,QAAUF,EAAQE,SAAW,QAC7BC,UAAY,UACZC,cAAgB,qCAGzB,gBACSL,QAAQM,MAAMC,QAAU,YAExBF,cAAgBlB,EAAc,uCAC9BiB,UAAYjB,EAAc,mFAC1BkB,cAAcG,YAAYC,KAAKL,gBAE/BJ,QAAQU,WAAWC,aAAaF,KAAKJ,cAAeI,KAAKT,QAAQY,kBAGjEP,cAAcQ,iBAAiB,UAAWJ,KAAKK,kBAAkBC,KAAKN,YAGtEJ,cAAcQ,iBAAiB,QAASJ,KAAKO,sBAAsBD,KAAKN,6BAGjF,gBACSP,KAAKe,aACLZ,cAAca,cACdlB,QAAQM,MAAMC,QAAU,qDAGjC,gBACSP,QAAQmB,MAAQV,KAAKP,KAAKkB,KAAK,kCAGxC,SAAcC,QACLnB,KAAKoB,OAAOb,KAAKP,KAAKqB,QAAQF,EAAKG,QAAQL,MAAMM,eAAgB,GACtEJ,EAAKH,SAGDT,KAAKP,KAAKwB,OAASjB,KAAKN,eACnBC,UAAUuB,UAAW,yBAIlC,SAAOC,GAIc,MAHjBA,EAAWA,EAASnC,UAGkD,IAA/CgB,KAAKP,KAAKqB,QAAQK,EAASH,sBACzCvB,KAAK2B,KAAKD,EAASH,oBAEnBrB,UAAUM,WAAWC,aACtBxB,EAAc,yCAA2CS,EAAWgC,GAAY,KAAOhC,EAAWgC,GAAY,2CAC9GnB,KAAKL,WAILK,KAAKP,KAAKwB,QAAUjB,KAAKN,eACpBC,UAAUuB,UAAW,qCAKtC,SAAkBG,OACVF,EAAWnB,KAAKL,UAAUe,MAEd,cAAZW,EAAIC,KAAoC,KAAbH,EAEvBnB,KAAKL,UAAU4B,uBACVC,cAAcxB,KAAKL,UAAU4B,sBAE7BE,0BAEU,MAAZJ,EAAIC,WACNI,OAAOP,QAEPxB,UAAUe,MAAQ,QAClBe,yBAELJ,EAAIM,uDAIZ,SAAsBN,GACdA,EAAIO,QAAUP,EAAIO,OAAOC,UAAUC,SAAS,iBACvCN,cAAcH,EAAIO,OAAOG,QAAQ,cACjCN,oCC9FJ,sCARLO,MAAM,qCAGV,WACIC,QAAQC,IAAI,mBAMfC,OAEL,IAAI7C,EAAU"} \ No newline at end of file +{"version":3,"file":"bundle.min.js","sources":["../js/dom.js","../js/tag_input.js","../js/main.js"],"sourcesContent":["const $ = function(selector) {\n return document.querySelector(selector);\n};\n\nconst $$ = function(selector) {\n return document.querySelectorAll(selector) || [];\n};\n\nconst makeEl = function(html) {\n const template = document.createElement('template');\n\n template.innerHTML = html.trim();\n\n return template.content.firstChild;\n};\n\nconst escape = function(unsafe) {\n return unsafe\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n\nexport { $, $$, makeEl, escape };","import { makeEl, escape } from \"./dom\";\n\nclass TagsInput {\n constructor(element, options = {}) {\n this.element = element;\n this.tags = [];\n this.options = options\n\n this.maxTags = options.maxTags || 10;\n this.inputNode = null;\n this.containerNode = null;\n }\n\n attach() {\n this.element.style.display = 'none';\n\n this.containerNode = makeEl('
');\n this.inputNode = makeEl('');\n this.containerNode.appendChild(this.inputNode);\n\n this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);\n\n /* Load existing tags from input */\n if (this.element.value) {\n for (const tag of this.element.value.split(',')) {\n this.addTag(tag);\n }\n }\n\n /* Handle addition and removal of tags via key-presses */\n this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));\n\n /* Handle deletions by clicking the delete button */\n this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));\n }\n\n detach() {\n this.tags.clear();\n this.containerNode.remove();\n this.element.style.display = 'inline-block';\n }\n\n updateHiddenInputValue() {\n this.element.value = this.tags.join(',');\n }\n\n deleteTagNode(node) {\n this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);\n node.remove();\n\n /* Below the limit? Make sure the input is enabled. */\n if (this.tags.length < this.maxTags) {\n this.inputNode.disabled = false;\n }\n }\n\n addTag(tagValue) {\n tagValue = tagValue.trim();\n\n /* Tag value is probably not empty and we don't already have the same tag. */\n if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {\n this.tags.push(tagValue.toLowerCase());\n\n this.inputNode.parentNode.insertBefore(\n makeEl('' + escape(tagValue) + ''),\n this.inputNode\n );\n\n /* Too many tags, disable the input for now. */\n if (this.tags.length >= this.maxTags) {\n this.inputNode.disabled = true;\n }\n }\n }\n\n _handleInputKeyUp(evt) {\n let tagValue = this.inputNode.value;\n\n if (evt.key === 'Backspace' && tagValue === '') {\n // Remove the child\n if (this.inputNode.previousSibling) {\n this.deleteTagNode(this.inputNode.previousSibling);\n\n this.updateHiddenInputValue();\n }\n } else if (evt.key === ',') {\n this.addTag(tagValue);\n\n this.inputNode.value = ''\n this.updateHiddenInputValue();\n\n evt.preventDefault();\n }\n }\n\n _handleContainerClick(evt) {\n if (evt.target && evt.target.classList.contains('delete')) {\n this.deleteTagNode(evt.target.closest('.tag'));\n this.updateHiddenInputValue();\n }\n }\n}\n\nexport { TagsInput };\n","import { $$ } from './dom';\nimport { TagsInput } from \"./tag_input\";\n\nconst setupSite = function() {\n Array.prototype.forEach.call($$('.js-tag-input'), (el) => {\n new TagsInput(el).attach();\n });\n};\n\nif (document.readyState !== 'loading') {\n setupSite();\n} else {\n document.addEventListener('DOMContentLoaded', setupSite);\n}\n"],"names":["makeEl","html","template","document","createElement","innerHTML","trim","content","firstChild","escape","unsafe","replace","TagsInput","element","options","tags","maxTags","inputNode","containerNode","style","display","appendChild","this","parentNode","insertBefore","nextSibling","value","split","tag","addTag","addEventListener","_handleInputKeyUp","bind","_handleContainerClick","clear","remove","join","node","splice","indexOf","dataset","toLowerCase","length","disabled","tagValue","push","evt","key","previousSibling","deleteTagNode","updateHiddenInputValue","preventDefault","target","classList","contains","closest","setupSite","selector","Array","prototype","forEach","call","querySelectorAll","el","attach","readyState"],"mappings":"wxCAIA,IAIMA,EAAS,SAASC,OACdC,EAAWC,SAASC,cAAc,mBAExCF,EAASG,UAAYJ,EAAKK,OAEnBJ,EAASK,QAAQC,YAGtBC,EAAS,SAASC,UACbA,EACFC,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,WCpBjBC,wBACUC,OAASC,yDAAU,kBACtBD,QAAUA,OACVE,KAAO,QACPD,QAAUA,OAEVE,QAAUF,EAAQE,SAAW,QAC7BC,UAAY,UACZC,cAAgB,kDAGzB,mBACSL,QAAQM,MAAMC,QAAU,YAExBF,cAAgBlB,EAAO,uCACvBiB,UAAYjB,EAAO,mFACnBkB,cAAcG,YAAYC,KAAKL,gBAE/BJ,QAAQU,WAAWC,aAAaF,KAAKJ,cAAeI,KAAKT,QAAQY,aAGlEH,KAAKT,QAAQa,MAAO,WACFJ,KAAKT,QAAQa,MAAMC,MAAM,qCAAM,KAAtCC,eACFC,OAAOD,wCAKfV,cAAcY,iBAAiB,UAAWR,KAAKS,kBAAkBC,KAAKV,YAGtEJ,cAAcY,iBAAiB,QAASR,KAAKW,sBAAsBD,KAAKV,6BAGjF,gBACSP,KAAKmB,aACLhB,cAAciB,cACdtB,QAAQM,MAAMC,QAAU,qDAGjC,gBACSP,QAAQa,MAAQJ,KAAKP,KAAKqB,KAAK,kCAGxC,SAAcC,QACLtB,KAAKuB,OAAOhB,KAAKP,KAAKwB,QAAQF,EAAKG,QAAQd,MAAMe,eAAgB,GACtEJ,EAAKF,SAGDb,KAAKP,KAAK2B,OAASpB,KAAKN,eACnBC,UAAU0B,UAAW,yBAIlC,SAAOC,GAIc,MAHjBA,EAAWA,EAAStC,UAGkD,IAA/CgB,KAAKP,KAAKwB,QAAQK,EAASH,sBACzC1B,KAAK8B,KAAKD,EAASH,oBAEnBxB,UAAUM,WAAWC,aACtBxB,EAAO,yCAA2CS,EAAOmC,GAAY,KAAOnC,EAAOmC,GAAY,2CAC/FtB,KAAKL,WAILK,KAAKP,KAAK2B,QAAUpB,KAAKN,eACpBC,UAAU0B,UAAW,qCAKtC,SAAkBG,OACVF,EAAWtB,KAAKL,UAAUS,MAEd,cAAZoB,EAAIC,KAAoC,KAAbH,EAEvBtB,KAAKL,UAAU+B,uBACVC,cAAc3B,KAAKL,UAAU+B,sBAE7BE,0BAEU,MAAZJ,EAAIC,WACNlB,OAAOe,QAEP3B,UAAUS,MAAQ,QAClBwB,yBAELJ,EAAIK,uDAIZ,SAAsBL,GACdA,EAAIM,QAAUN,EAAIM,OAAOC,UAAUC,SAAS,iBACvCL,cAAcH,EAAIM,OAAOG,QAAQ,cACjCL,+DC/FXM,EAAY,WFCP,IAASC,EEAhBC,MAAMC,UAAUC,QAAQC,MFARJ,EEAgB,gBFCzBtD,SAAS2D,iBAAiBL,IAAa,KEDI,SAACM,OAC3CnD,EAAUmD,GAAIC,aAIE,YAAxB7D,SAAS8D,WACTT,IAEArD,SAAS2B,iBAAiB,mBAAoB0B"} \ No newline at end of file diff --git a/includes/common.php b/includes/common.php index 72ede7c..0c84bce 100644 --- a/includes/common.php +++ b/includes/common.php @@ -26,6 +26,28 @@ function urlForMember(string $member_name) : string { return '/user.php?name=' . urlencode($member_name); } +function optionsForSelect(array $displays, array $values, string $currentSelection = null) : string { + $size = count($displays); + + if (count($values) !== $size) { + throw new Exception('Option names and option values must be the same count'); + } + + $html = ''; + + for ($i = 0; $i < $size; $i++) { + $html .= '