diff --git a/download_parse.js b/download_parse.js
new file mode 100644
index 0000000..a541790
--- /dev/null
+++ b/download_parse.js
@@ -0,0 +1,88 @@
+function keyValuesFunction(data){
+let selection = extractKeyValues(parsedata);
+let filters = {}; // Empty object to store our active filters in
+// Loop over all groups with inputs
+ $(".selectiongroup").each(function(){
+
+ // Find all inputs within the current group that are checked
+ let checked = $(this).find("input:checked");
+
+ // If there's inputs that are checked
+ if(checked.length>0) {
+
+ // Create empty array to hold the values from the inputs (value=.. attribute)
+ let values = [];
+
+ // For each checked input (using Jquery each()), put the value in the above array
+ checked.each(function() {
+ values.push(parseInt($(this).val(),10));
+ });
+
+ // Create a new key in the object with the correct name and give it an array as value
+ filters[$(this).find("h4").text()] = [...values];
+ }
+ });
+ // Map/loop over all keys in the filter object (so val is equal to the key value)
+ Object.keys(filters).map((val,i) => {
+
+ // For each key, lookup what string the numbers are in the selection list
+ filters[val] = filters[val].map((sub) => {
+ return selection[val][sub];
+ });
+ });
+ return filters;
+}
+
+function addKeyValues(filteredData){
+ //gets the selected options and values
+ let tempKeys = keyValuesFunction(filteredData);
+ let keyValues = Object.keys(tempKeys);
+
+ //extracts those values in a new array
+ let values = Object.keys(tempKeys).map((val)=>{
+ return tempKeys[val];
+ });
+
+ //add data to object Associaties
+ function SubFilters(data){
+ this.Associations = data;
+ };
+
+ //map over filtered data and create an array of objects
+ let csvData= filteredData.map((val) =>{
+ return new SubFilters(val);
+ });
+
+ //map over selected options and add these to the array of object (csvData)
+ values.map((val,i) =>
+ val.map((sub) =>
+ keyValues[i]+": "+sub))
+ .map((val) => {
+ return val.map((sub)=>{
+ return csvData.unshift({Associations: sub});
+ });
+ });
+
+ return csvData;
+}
+
+//download the csv file
+function downloadCsv(options){
+ var config = {
+ delimiter: "",
+ skipEmptyLines: true,
+ header: false,
+ };
+ let csv = Papa.unparse(options, config);
+ var blob= new Blob(["\ufeff", csv]);
+ var pom = document.createElement('a');
+ var url = URL.createObjectURL(blob);
+ pom.href = url;
+ pom.setAttribute('download', 'test.csv');
+ pom.click();
+}
+
+document.getElementById('downloadbtn').addEventListener('click', function clickSave(e) {
+ let csvOptions = addKeyValues(wordleData);
+ downloadCsv(csvOptions);
+});
diff --git a/jquery.loading.min.js b/jquery.loading.min.js
new file mode 100644
index 0000000..0de2735
--- /dev/null
+++ b/jquery.loading.min.js
@@ -0,0 +1,10 @@
+;(function($){var L=$.loading=function(show,opts){return $('body').loading(show,opts,true);};$.fn.loading=function(show,opts,page){opts=toOpts(show,opts);var base=page?$.extend(true,{},L,L.pageOptions):L;return this.each(function(){var $el=$(this),o=$.extend(true,{},base,$.metadata?$el.metadata():null,opts);if(typeof o.onAjax=="boolean"){L.setAjax.call($el,o);}else{L.toggle.call($el,o);}});};var fixed={position:$.browser.msie?'absolute':'fixed'};$.extend(L,{version:"1.4",align:'top-left',pulse:'working error',mask:false,img:null,element:null,text:'Loading...',onAjax:undefined,classname:'loading',imgClass:'loading-img',elementClass:'loading-element',maskClass:'loading-mask',css:{position:'absolute',whiteSpace:'nowrap',zIndex:1001},maskCss:{position:'absolute',opacity:.15,background:'#333',zIndex:101,display:'block',cursor:'wait'},cloneEvents:true,pageOptions:{page:true,align:'top-center',css:fixed,maskCss:fixed},html:'
',maskHtml:'
',maskedClass:'loading-masked',maskEvents:'mousedown mouseup keydown keypress',resizeEvents:'resize',working:{time:10000,text:'Still working...',run:function(opts){var w=opts.working,self=this;w.timeout=setTimeout(function(){self.height('auto').width('auto').text(opts.text=w.text);opts.place.call(self,opts);},w.time);}},error:{time:100000,text:'Task may have failed...',classname:'loading-error',run:function(opts){var e=opts.error,self=this;e.timeout=setTimeout(function(){self.height('auto').width('auto').text(opts.text=e.text).addClass(e.classname);opts.place.call(self,opts);},e.time);}},fade:{time:800,speed:'slow',run:function(opts){var f=opts.fade,s=f.speed,self=this;f.interval=setInterval(function(){self.fadeOut(s).fadeIn(s);},f.time);}},ellipsis:{time:300,run:function(opts){var e=opts.ellipsis,self=this;e.interval=setInterval(function(){var et=self.text(),t=opts.text,i=dotIndex(t);self.text((et.length-i)<3?et+'.':t.substring(0,i));},e.time);function dotIndex(t){var x=t.indexOf('.');return x<0?t.length:x;}}},type:{time:100,run:function(opts){var t=opts.type,self=this;t.interval=setInterval(function(){var e=self.text(),el=e.length,txt=opts.text;self.text(el==txt.length?txt.charAt(0):txt.substring(0,el+1));},t.time);}},toggle:function(opts){var old=this.data('loading');if(old){if(opts.show!==true)old.off.call(this,old,opts);}else{if(opts.show!==false)opts.on.call(this,opts);}},setAjax:function(opts){if(opts.onAjax){var self=this,A=opts.ajax={count:0};A.start=function(e){if(A.count++<1)opts.on.call(self,opts);};A.stop=function(e){if(--A.count<1)opts.off.call(self,opts,opts);};this.bind('ajaxStart.loading',A.start).bind('ajaxStop.loading',A.stop);}else{this.unbind('ajaxStart.loading').unbind('ajaxStop.loading');}},on:function(opts){opts.parent=this;if(opts.mask)opts.mask=opts.createMask.call(this,opts);opts.display=opts.create.call(this,opts);if(opts.img){opts.initImg.call(this,opts);}else if(opts.element){opts.initElement.call(this,opts);}else{opts.init.call(this,opts);}
+this.trigger('loadingStart',[opts]);},initImg:function(opts){var self=this;opts.img=$(' ').bind('load',function(){opts.init.call(self,opts);});opts.display.addClass(opts.imgClass).append(opts.img);},initElement:function(opts){opts.element=$(opts.element).clone(opts.cloneEvents).show();opts.display.addClass(opts.elementClass).append(opts.element);opts.init.call(this,opts);},init:function(opts){opts.place.call(opts.display,opts);this.data('loading',opts);if(opts.pulse)opts.initPulse.call(this,opts);},initPulse:function(opts){$.each(opts.pulse.split(' '),function(){opts[this].run.call(opts.display,opts);});},create:function(opts){var el=$(opts.html).addClass(opts.classname).css(opts.css).appendTo(this);if(opts.text&&!opts.img&&!opts.element)el.text(opts.text);$(window).bind(opts.resizeEvents,opts.resizer=function(){opts.resize(opts);});return el;},resize:function(opts){opts.parent.box=null;if(opts.mask)opts.mask.hide();opts.place.call(opts.display.hide(),opts);if(opts.mask)opts.mask.show().css(opts.parent.box);},createMask:function(opts){var box=opts.measure.call(this.addClass(opts.maskedClass),opts);opts.handler=function(e){return opts.maskHandler(e,opts);};$(document).bind(opts.maskEvents,opts.handler);return $(opts.maskHtml).addClass(opts.maskClass).css(box).css(opts.maskCss).appendTo(this);},maskHandler:function(e,opts){var $els=$(e.target).parents().andSelf();if($els.filter('.'+opts.classname).length!=0)return true;return!opts.page&&$els.filter('.'+opts.maskedClass).length==0;},place:function(opts){var box=opts.align,v='top',h='left';if(typeof box=="object"){box=$.extend(opts.calc.call(this,v,h,opts),box);}else{if(box!='top-left'){var s=box.split('-');if(s.length==1){v=h=s[0];}else{v=s[0];h=s[1];}}
+if(!this.hasClass(v))this.addClass(v);if(!this.hasClass(h))this.addClass(h);box=opts.calc.call(this,v,h,opts);}
+this.show().css(opts.box=box);},calc:function(v,h,opts){var box=$.extend({},opts.measure.call(opts.parent,opts)),H=$.boxModel?this.height():this.innerHeight(),W=$.boxModel?this.width():this.innerWidth();if(v!='top'){var d=box.height-H;if(v=='center'){d/=2;}else if(v!='bottom'){d=0;}else if($.boxModel){d-=css(this,'paddingTop')+css(this,'paddingBottom');}
+box.top+=d;}
+if(h!='left'){var d=box.width-W;if(h=='center'){d/=2;}else if(h!='right'){d=0;}else if($.boxModel){d-=css(this,'paddingLeft')+css(this,'paddingRight');}
+box.left+=d;}
+box.height=H;box.width=W;return box;},measure:function(opts){return this.box||(this.box=opts.page?opts.pageBox(opts):opts.elementBox(this,opts));},elementBox:function(e,opts){var box=e.position();box.top+=css(e,'marginTop');box.left+=css(e,'marginLeft');box.height=e.outerHeight();box.width=e.outerWidth();return box;},pageBox:function(opts){var d=document,b=d.body,full=$.boxModel&&opts.css.position!='fixed',h=full?$(d).height():b.clientHeight,w=full?$(d).width():b.clientWidth;return{top:0,left:0,height:h,width:w};},off:function(old,opts){this.data('loading',null);if(old.pulse)old.stopPulse.call(this,old,opts);if(old.mask)old.stopMask.call(this,old,opts);$(window).unbind(old.resizeEvents,old.resizer);old.display.remove();old.parent.trigger('loadingEnd',[old]);},stopPulse:function(old,opts){$.each(old.pulse.split(' '),function(){var p=old[this];if(p.end)p.end.call(opts.display,old,opts);if(p.interval)clearInterval(p.interval);if(p.timeout)clearTimeout(p.timeout);});},stopMask:function(old,opts){this.removeClass(opts.maskedClass);$(document).unbind(old.maskEvents,old.handler);old.mask.remove();}});L.onAjax=function(opts){$.loading($.extend({onAjax:true},opts));};function toOpts(s,o){if(o===undefined){o=(typeof s=="boolean")?{show:s}:s;}else{o.show=s;}
+if(o&&(o.img||o.element)&&!o.pulse)o.pulse=false;if(o&&o.onAjax!==undefined&&o.show===undefined)o.show=false;return o;}
+function css(el,prop){var val=el.css(prop);return val=='auto'?0:parseFloat(val,10);}})(jQuery);
\ No newline at end of file
diff --git a/papaparse.min.js b/papaparse.min.js
new file mode 100644
index 0000000..661e101
--- /dev/null
+++ b/papaparse.min.js
@@ -0,0 +1,6 @@
+/*!
+ Papa Parse
+ v4.3.2
+ https://github.com/mholt/PapaParse
+*/
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof module&&module.exports?module.exports=b():a.Papa=b()}(this,function(){"use strict";function a(a,b){b=b||{};var c=b.dynamicTyping||!1;if(r(c)&&(b.dynamicTypingFunction=c,c={}),b.dynamicTyping=c,b.worker&&z.WORKERS_SUPPORTED){var h=k();return h.userStep=b.step,h.userChunk=b.chunk,h.userComplete=b.complete,h.userError=b.error,b.step=r(b.step),b.chunk=r(b.chunk),b.complete=r(b.complete),b.error=r(b.error),delete b.worker,void h.postMessage({input:a,config:b,workerId:h.id})}var i=null;return"string"==typeof a?i=b.download?new d(b):new f(b):a.readable===!0&&r(a.read)&&r(a.on)?i=new g(b):(t.File&&a instanceof File||a instanceof Object)&&(i=new e(b)),i.stream(a)}function b(a,b){function c(){"object"==typeof b&&("string"==typeof b.delimiter&&1===b.delimiter.length&&z.BAD_DELIMITERS.indexOf(b.delimiter)===-1&&(j=b.delimiter),("boolean"==typeof b.quotes||b.quotes instanceof Array)&&(h=b.quotes),"string"==typeof b.newline&&(k=b.newline),"string"==typeof b.quoteChar&&(l=b.quoteChar),"boolean"==typeof b.header&&(i=b.header))}function d(a){if("object"!=typeof a)return[];var b=[];for(var c in a)b.push(c);return b}function e(a,b){var c="";"string"==typeof a&&(a=JSON.parse(a)),"string"==typeof b&&(b=JSON.parse(b));var d=a instanceof Array&&a.length>0,e=!(b[0]instanceof Array);if(d&&i){for(var g=0;g0&&(c+=j),c+=f(a[g],g);b.length>0&&(c+=k)}for(var h=0;h0&&(c+=j);var n=d&&e?a[m]:m;c+=f(b[h][n],m)}h-1||" "===a.charAt(0)||" "===a.charAt(a.length-1);return c?l+a+l:a}function g(a,b){for(var c=0;c-1)return!0;return!1}var h=!1,i=!0,j=",",k="\r\n",l='"';c();var m=new RegExp(l,"g");if("string"==typeof a&&(a=JSON.parse(a)),a instanceof Array){if(!a.length||a[0]instanceof Array)return e(null,a);if("object"==typeof a[0])return e(d(a[0]),a)}else if("object"==typeof a)return"string"==typeof a.data&&(a.data=JSON.parse(a.data)),a.data instanceof Array&&(a.fields||(a.fields=a.meta&&a.meta.fields),a.fields||(a.fields=a.data[0]instanceof Array?a.fields:d(a.data[0])),a.data[0]instanceof Array||"object"==typeof a.data[0]||(a.data=[a.data])),e(a.fields||[],a.data||[]);throw"exception: Unable to serialize unrecognized input"}function c(a){function b(a){var b=p(a);b.chunkSize=parseInt(b.chunkSize),a.step||a.chunk||(b.chunkSize=null),this._handle=new h(b),this._handle.streamer=this,this._config=b}this._handle=null,this._paused=!1,this._finished=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},b.call(this,a),this.parseChunk=function(a){if(this.isFirstChunk&&r(this._config.beforeFirstChunk)){var b=this._config.beforeFirstChunk(a);void 0!==b&&(a=b)}this.isFirstChunk=!1;var c=this._partialLine+a;this._partialLine="";var d=this._handle.parse(c,this._baseIndex,!this._finished);if(!this._handle.paused()&&!this._handle.aborted()){var e=d.meta.cursor;this._finished||(this._partialLine=c.substring(e-this._baseIndex),this._baseIndex=e),d&&d.data&&(this._rowCount+=d.data.length);var f=this._finished||this._config.preview&&this._rowCount>=this._config.preview;if(v)t.postMessage({results:d,workerId:z.WORKER_ID,finished:f});else if(r(this._config.chunk)){if(this._config.chunk(d,this._handle),this._paused)return;d=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(d.data),this._completeResults.errors=this._completeResults.errors.concat(d.errors),this._completeResults.meta=d.meta),!f||!r(this._config.complete)||d&&d.meta.aborted||this._config.complete(this._completeResults,this._input),f||d&&d.meta.paused||this._nextChunk(),d}},this._sendError=function(a){r(this._config.error)?this._config.error(a):v&&this._config.error&&t.postMessage({workerId:z.WORKER_ID,error:a,finished:!1})}}function d(a){function b(a){var b=a.getResponseHeader("Content-Range");return null===b?-1:parseInt(b.substr(b.lastIndexOf("/")+1))}a=a||{},a.chunkSize||(a.chunkSize=z.RemoteChunkSize),c.call(this,a);var d;u?this._nextChunk=function(){this._readChunk(),this._chunkLoaded()}:this._nextChunk=function(){this._readChunk()},this.stream=function(a){this._input=a,this._nextChunk()},this._readChunk=function(){if(this._finished)return void this._chunkLoaded();if(d=new XMLHttpRequest,this._config.withCredentials&&(d.withCredentials=this._config.withCredentials),u||(d.onload=q(this._chunkLoaded,this),d.onerror=q(this._chunkError,this)),d.open("GET",this._input,!u),this._config.downloadRequestHeaders){var a=this._config.downloadRequestHeaders;for(var b in a)d.setRequestHeader(b,a[b])}if(this._config.chunkSize){var c=this._start+this._config.chunkSize-1;d.setRequestHeader("Range","bytes="+this._start+"-"+c),d.setRequestHeader("If-None-Match","webkit-no-cache")}try{d.send()}catch(a){this._chunkError(a.message)}u&&0===d.status?this._chunkError():this._start+=this._config.chunkSize},this._chunkLoaded=function(){if(4==d.readyState){if(d.status<200||d.status>=400)return void this._chunkError();this._finished=!this._config.chunkSize||this._start>b(d),this.parseChunk(d.responseText)}},this._chunkError=function(a){var b=d.statusText||a;this._sendError(b)}}function e(a){a=a||{},a.chunkSize||(a.chunkSize=z.LocalChunkSize),c.call(this,a);var b,d,e="undefined"!=typeof FileReader;this.stream=function(a){this._input=a,d=a.slice||a.webkitSlice||a.mozSlice,e?(b=new FileReader,b.onload=q(this._chunkLoaded,this),b.onerror=q(this._chunkError,this)):b=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(a.target.result)},this._chunkError=function(){this._sendError(b.error)}}function f(a){a=a||{},c.call(this,a);var b,d;this.stream=function(a){return b=a,d=a,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var a=this._config.chunkSize,b=a?d.substr(0,a):d;return d=a?d.substr(a):"",this._finished=!d,this.parseChunk(b)}}}function g(a){a=a||{},c.call(this,a);var b=[],d=!0;this.stream=function(a){this._input=a,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._nextChunk=function(){b.length?this.parseChunk(b.shift()):d=!0},this._streamData=q(function(a){try{b.push("string"==typeof a?a:a.toString(this._config.encoding)),d&&(d=!1,this.parseChunk(b.shift()))}catch(a){this._streamError(a)}},this),this._streamError=q(function(a){this._streamCleanUp(),this._sendError(a.message)},this),this._streamEnd=q(function(){this._streamCleanUp(),this._finished=!0,this._streamData("")},this),this._streamCleanUp=q(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}function h(a){function b(){if(x&&o&&(l("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+z.DefaultDelimiter+"'"),o=!1),a.skipEmptyLines)for(var b=0;b=w.length?"__parsed_extra":w[d]),g=f(e,g),"__parsed_extra"===e?(c[e]=c[e]||[],c[e].push(g)):c[e]=g}x.data[b]=c,a.header&&(d>w.length?l("FieldMismatch","TooManyFields","Too many fields: expected "+w.length+" fields but parsed "+d,b):d1&&(k+=Math.abs(o-f),f=o):f=o}m.data.length>0&&(l/=m.data.length),("undefined"==typeof e||k1.99&&(e=k,d=j)}return a.delimiter=d,{successful:!!d,bestDelimiter:d}}function j(a){a=a.substr(0,1048576);var b=a.split("\r"),c=a.split("\n"),d=c.length>1&&c[0].length=b.length/2?"\r\n":"\r"}function k(a){var b=q.test(a);return b?parseFloat(a):a}function l(a,b,c,d){x.errors.push({type:a,code:b,message:c,row:d})}var m,n,o,q=/^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i,s=this,t=0,u=!1,v=!1,w=[],x={data:[],errors:[],meta:{}};if(r(a.step)){var y=a.step;a.step=function(d){if(x=d,c())b();else{if(b(),0===x.data.length)return;t+=d.data.length,a.preview&&t>a.preview?n.abort():y(x,s)}}}this.parse=function(c,d,e){if(a.newline||(a.newline=j(c)),o=!1,a.delimiter)r(a.delimiter)&&(a.delimiter=a.delimiter(c),x.meta.delimiter=a.delimiter);else{var f=h(c,a.newline);f.successful?a.delimiter=f.bestDelimiter:(o=!0,a.delimiter=z.DefaultDelimiter),x.meta.delimiter=a.delimiter}var g=p(a);return a.preview&&a.header&&g.preview++,m=c,n=new i(g),x=n.parse(m,d,e),b(),u?{meta:{paused:!0}}:x||{meta:{paused:!1}}},this.paused=function(){return u},this.pause=function(){u=!0,n.abort(),m=m.substr(n.getCharIndex())},this.resume=function(){u=!1,s.streamer.parseChunk(m)},this.aborted=function(){return v},this.abort=function(){v=!0,n.abort(),x.meta.aborted=!0,r(a.complete)&&a.complete(x),m=""}}function i(a){a=a||{};var b=a.delimiter,c=a.newline,d=a.comments,e=a.step,f=a.preview,g=a.fastMode,h=a.quoteChar||'"';if(("string"!=typeof b||z.BAD_DELIMITERS.indexOf(b)>-1)&&(b=","),d===b)throw"Comment character same as delimiter";d===!0?d="#":("string"!=typeof d||z.BAD_DELIMITERS.indexOf(d)>-1)&&(d=!1),"\n"!=c&&"\r"!=c&&"\r\n"!=c&&(c="\n");var i=0,j=!1;this.parse=function(a,k,l){function m(a){x.push(a),A=i}function n(b){return l?p():("undefined"==typeof b&&(b=a.substr(i)),z.push(b),i=s,m(z),w&&q(),p())}function o(b){i=b,m(z),z=[],E=a.indexOf(c,i)}function p(a){return{data:x,errors:y,meta:{delimiter:b,linebreak:c,aborted:j,truncated:!!a,cursor:A+(k||0)}}}function q(){e(p()),x=[],y=[]}if("string"!=typeof a)throw"Input must be a string";var s=a.length,t=b.length,u=c.length,v=d.length,w=r(e);i=0;var x=[],y=[],z=[],A=0;if(!a)return p();if(g||g!==!1&&a.indexOf(h)===-1){for(var B=a.split(c),C=0;C=f)return x=x.slice(0,f),p(!0)}}return p()}for(var D=a.indexOf(b,i),E=a.indexOf(c,i),F=new RegExp(h+h,"g");;)if(a[i]!==h)if(d&&0===z.length&&a.substr(i,v)===d){if(E===-1)return p();i=E+u,E=a.indexOf(c,i),D=a.indexOf(b,i)}else if(D!==-1&&(D=f)return p(!0)}else{var G=i;for(i++;;){var G=a.indexOf(h,G+1);if(G===-1)return l||y.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:x.length,index:i}),n();if(G===s-1){var H=a.substring(i,G).replace(F,h);return n(H)}if(a[G+1]!==h){if(a[G+1]===b){z.push(a.substring(i,G).replace(F,h)),i=G+1+t,D=a.indexOf(b,i),E=a.indexOf(c,i);break}if(a.substr(G+1,u)===c){if(z.push(a.substring(i,G).replace(F,h)),o(G+1+u),D=a.indexOf(b,i),w&&(q(),j))return p();if(f&&x.length>=f)return p(!0);break}}else G++}}return n()},this.abort=function(){j=!0},this.getCharIndex=function(){return i}}function j(){var a=document.getElementsByTagName("script");return a.length?a[a.length-1].src:""}function k(){if(!z.WORKERS_SUPPORTED)return!1;if(!w&&null===z.SCRIPT_PATH)throw new Error("Script path cannot be determined automatically when Papa Parse is loaded asynchronously. You need to set Papa.SCRIPT_PATH manually.");var a=z.SCRIPT_PATH||s;a+=(a.indexOf("?")!==-1?"&":"?")+"papaworker";var b=new t.Worker(a);return b.onmessage=l,b.id=y++,x[b.id]=b,b}function l(a){var b=a.data,c=x[b.workerId],d=!1;if(b.error)c.userError(b.error,b.file);else if(b.results&&b.results.data){var e=function(){d=!0,m(b.workerId,{data:[],errors:[],meta:{aborted:!0}})},f={abort:e,pause:n,resume:n};if(r(c.userStep)){for(var g=0;g
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Step 2:
+
Please select your preferred output options by clicking the buttons below:
+
+
Step 3:
+
Made your selections? Please confirm by clicking the button below
+
Confirm Selection
+
+
+
Step 4:
+
Generate/re-render or save the worlde
+
Generate wordle
+
Re-render wordle
+
Save wordle (.png)
+
+
+
+
+
+
(Optional) Step 6:
+ Choose specific wordle options to get the worlde you prefer
+ Number of words:
+
+
+
+
+
+ Language:
+
+ Dutch
+ English
+ German
+ French
+ None
+
+
+
+
+
+
+
+ Color scheme:
+
+ Gray
+ Green
+ Red
+ Shooting Star
+
+
+
+
+
+
+
+
+
+
+
+
+
Red alert! Some words are removed from the wordle! Please adjust the scale or proceed with caution.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parse_click_functions.js b/parse_click_functions.js
new file mode 100644
index 0000000..8b69639
--- /dev/null
+++ b/parse_click_functions.js
@@ -0,0 +1,55 @@
+
+ //If document is ready and upload id has been clicked start parsing
+$(document).ready(function(){
+ $('#submit-file').on("click", function(e){
+ $('#return-choice li').remove();
+ e.preventDefault();
+ $('#files').parse({
+ config: {
+ delimiter: "",
+ skipEmptyLines: true,
+ header: true,
+ encoding: "utf-8",
+ complete: doneParse,
+ },
+ before: function(file, inputElem)
+ {
+ console.log("Parsing file...", file);
+ },
+ error: function(err, file)
+ {
+ console.log("ERROR:", err, file);
+ },
+ complete: function()
+ {
+ }
+ });
+ console.log('parse worked');
+ });
+
+$(document).on('click', '#selection .dropdown-menu', function (e) {
+ e.stopPropagation();
+});
+});
+
+$('#changewords').on('click', function(e){
+ e.preventDefault;
+ $('body').loading('toggle');
+ setTimeout(function(){
+ let replaceWord = document.getElementById("replacewords").value.split(' ');
+ let replaceWith = document.getElementById("replaced").value;
+ wordleData = changeWords(replaceWord, replaceWith);
+ }, 100);
+ $('body').loading('stop');
+});
+
+$("#check").click(function(e) {
+ e.preventDefault;
+ $('body').loading('toggle');
+ setTimeout(function(){
+ $('#association-container').html('');
+ wordleData = checkFilters(parsedata);
+ }, 100);
+ $('body').loading('stop');
+});
+
diff --git a/text_cleanup.js b/text_cleanup.js
new file mode 100644
index 0000000..d65713e
--- /dev/null
+++ b/text_cleanup.js
@@ -0,0 +1,72 @@
+
+//functions for text cleaning!
+//removes all special characters (this does not include words, whitespaces or any of the following characters: (),’'"!?.)
+// function removeSpecialSign(el){
+// return el.replace(/[^\s\w(),’'"&!?.ïöéëüóòèìíáà][\W]*/g, "");
+// }
+// removes remaining special characters, if more than 3 are in consectutive order. Characters include: (),’'"!?.
+function removeMultipleSign(el){
+ return el.replace(/[^\s\w]{1,}/g, function(match) {
+ if (match.length > 3 ) {
+ return match.slice(0,3);
+ } else {
+ return match;
+ }
+ });
+}
+//inserts a whitespace after a special character. Special character includes: (),’'"!?.
+function spaceAfterSign(el){
+ return el.replace(/[^\s\w]{1,}[\W]{1,}/g, function(match){
+ if (match == /[\W\s]/g ){
+ return match;
+ } else{
+ return match.replace(match, ""+match);
+ }
+ })
+}
+//removes isntances with more than 1 whitespace
+function multipleSpaces(el){
+ return el.replace(/\s{2,}/g, " ");
+}
+function placeDotEndSentence(el){
+ if (el.slice(-1)==="." || el.slice(-1)===" "){
+ return el;
+ } else {
+ return el.replace(el, el+". ");
+ }
+}
+
+// function applySentenceCase(str) {
+// return str.replace(/.+?[\.\?\!](\s|$)/g, function (txt) {
+// return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
+// });
+// }
+function removeDots (str){
+ return str.replace(/\./g, '').toLowerCase();
+}
+function removeSignsOnly(str){
+ if(str.length < 4){
+ return str.replace(/[^\s\w]/g, '');
+ }else {
+ return str;
+ }
+}
+
+ //runs all the cleanup functions and returns the cleaned-up text
+function cleanUp(value){
+ // var textTemp=removeSpecialSign(value);
+ var textTemp=removeMultipleSign(value);
+ textTemp=multipleSpaces(textTemp);
+ textTemp=spaceAfterSign(textTemp);
+ textTemp=placeDotEndSentence(textTemp);
+ // textTemp=applySentenceCase(textTemp);
+ textTemp=removeDots(textTemp);
+ textTemp=removeSignsOnly(textTemp);
+ return textTemp;
+}
+
+// sentiment = new Sentimood();
+
+// var analyze = sentiment.analyze(),
+// positivity = sentiment.positivity(),
+// negativity = sentiment.negativity();
diff --git a/upload_papaparse_v2.js b/upload_papaparse_v2.js
new file mode 100644
index 0000000..4758a45
--- /dev/null
+++ b/upload_papaparse_v2.js
@@ -0,0 +1,156 @@
+let parsedata= {};
+let wordleData = [];
+function doneParse(results){
+ parsedata = results.data;
+ const selection = extractKeyValues(parsedata);
+ console.log(parsedata);
+ // const spelling = trainSpelling(parsedata);
+ showSelections(selection);
+ return parsedata;
+ }
+
+function previewFile(){
+ var file = document.querySelector('#files').files[0];
+ var reader = new FileReader();
+ if (file) {
+ reader.readAsDataURL(file);
+ }
+}
+
+// Initialize a high resolution timer (time in ms)
+const time1 = performance.now();
+
+// Run our function to get all possible keys and all possible values
+
+
+// Second high res timer
+const time2 = performance.now();
+
+// Subtract time1 from time2 to determine how long it took our code to run
+const elapsedTime = time2-time1;
+
+console.log(elapsedTime, time1, time2);
+
+// Returns an object with all keys (excludes first key) with an array of possible values for each key
+function extractKeyValues(data) {
+ // data = doneParse();
+ // Get all possible keys; delete empty keys and ignore column 1
+ let keys = Object.keys(data[0]).filter((val) => val !== "");
+ if (keys.length > 1){
+ keys = keys.slice(1,keys.length);
+ }
+ // Create global object to hold the selection
+ let selection = {};
+
+ // Get all possible values for each key, then put the unique array into selection object
+ // Result: selection = {key1: [value1, value2, value3], key2: [value1, value2, value3]}
+ keys.map((val) => {
+ let tempdata = data.map((sub) => sub[val]);
+ let tempset = new Set(tempdata);
+ selection[val] = [...tempset];
+ });
+ return selection;
+}
+
+
+// Output checkboxes for user selection
+function showSelections(selection) {
+ $('.selectiongroup').html('');
+ const keys = Object.keys(selection);
+ keys.map((key,j) => {
+ let html = `${key}
+
`;
+ $("#selection").append(html);
+ });
+}
+
+// Filters data object based on user selection
+function checkFilters(data) {
+ let selection = extractKeyValues(parsedata);
+ let tempData = data; // Create a local copy of data
+ let filters = {}; // Empty object to store our active filters in
+ // Loop over all groups with inputs
+ $(".selectiongroup").each(function(){
+
+ // Find all inputs within the current group that are checked
+ let checked = $(this).find("input:checked");
+
+ // If there's inputs that are checked
+ if(checked.length>0) {
+
+ // Create empty array to hold the values from the inputs (value=.. attribute)
+ let values = [];
+
+ // For each checked input (using Jquery each()), put the value in the above array
+ checked.each(function() {
+ values.push(parseInt($(this).val(),10));
+ });
+
+ // Create a new key in the object with the correct name and give it an array as value
+ filters[$(this).find("h4").text()] = [...values];
+ }
+ });
+ // Map/loop over all keys in the filter object (so val is equal to the key value)
+ Object.keys(filters).map((val,i) => {
+
+ // For each key, lookup what string the numbers are in the selection list
+ filters[val] = filters[val].map((sub) => {
+ return selection[val][sub];
+ });
+ // Filters is now an object with arrays of strings from selected checkboxes as values
+
+ // Filter the data by checking if the value of each key (we're doing this for each checked key) is in the value array
+ tempData = tempData.filter((item) => {
+ return (filters[val].indexOf(item[val]) !== -1);
+ });
+ });
+
+
+ //checks if the array has multiple keys or not and extracts relevant data (Associations)
+ if(Object.keys(tempData[0]).length == 1){
+ tempData = tempData.map((val) => {
+ return val[(Object.keys(tempData[0]))];
+ });
+ } else {
+ tempData = tempData.map((val)=> {
+ return val[(Object.keys(tempData[0])[0])];
+ });
+ }
+
+ //runs the tempData through a cleanup function
+ let filteredStory = tempData.map((val) => {
+ return cleanUp(val)
+ });
+ // All done, just write the data to the DOM.
+ filteredStory.map((val) => {
+ $("#association-container").append(val+", ");
+ });
+ return filteredStory;
+}
+//function to replace multiple words in to one
+function changeWords(replacee, replaced){
+ let allWords= wordleData;
+ $('#association-container').html('');
+ //maps over the to be replaced words and stores these in a new array
+ let mapObj = replacee.map((val) =>{
+ return {[val]: replaced,};
+ });
+ let merged = Object.assign(...mapObj);
+ let alteredWords = replaceAll(allWords, merged);
+ $('#association-container').append(alteredWords);
+ return alteredWords;
+}
+//function that finds and replaces the words given by changeWords
+function replaceAll(str,mapObj){
+ var re = new RegExp(Object.keys(mapObj).join("|"),"gi");
+ return str.map((val)=>{
+ return val.replace(re, function(matched){
+ return mapObj[matched.toLowerCase()];
+ });
+ });
+}
+
diff --git a/wordcloud2.js b/wordcloud2.js
new file mode 100644
index 0000000..5230465
--- /dev/null
+++ b/wordcloud2.js
@@ -0,0 +1,1183 @@
+/*!
+ * wordcloud2.js
+ * http://timdream.org/wordcloud2.js/
+ *
+ * Copyright 2011 - 2013 Tim Chien
+ * Released under the MIT license
+ */
+
+'use strict';
+
+// setImmediate
+if (!window.setImmediate) {
+ window.setImmediate = (function setupSetImmediate() {
+ return window.msSetImmediate ||
+ window.webkitSetImmediate ||
+ window.mozSetImmediate ||
+ window.oSetImmediate ||
+ (function setupSetZeroTimeout() {
+ if (!window.postMessage || !window.addEventListener) {
+ return null;
+ }
+
+ var callbacks = [undefined];
+ var message = 'zero-timeout-message';
+
+ // Like setTimeout, but only takes a function argument. There's
+ // no time argument (always zero) and no arguments (you have to
+ // use a closure).
+ var setZeroTimeout = function setZeroTimeout(callback) {
+ var id = callbacks.length;
+ callbacks.push(callback);
+ window.postMessage(message + id.toString(36), '*');
+
+ return id;
+ };
+
+ window.addEventListener('message', function setZeroTimeoutMessage(evt) {
+ // Skipping checking event source, retarded IE confused this window
+ // object with another in the presence of iframe
+ if (typeof evt.data !== 'string' ||
+ evt.data.substr(0, message.length) !== message/* ||
+ evt.source !== window */) {
+ return;
+ }
+
+ evt.stopImmediatePropagation();
+
+ var id = parseInt(evt.data.substr(message.length), 36);
+ if (!callbacks[id]) {
+ return;
+ }
+
+ callbacks[id]();
+ callbacks[id] = undefined;
+ }, true);
+
+ /* specify clearImmediate() here since we need the scope */
+ window.clearImmediate = function clearZeroTimeout(id) {
+ if (!callbacks[id]) {
+ return;
+ }
+
+ callbacks[id] = undefined;
+ };
+
+ return setZeroTimeout;
+ })() ||
+ // fallback
+ function setImmediateFallback(fn) {
+ window.setTimeout(fn, 0);
+ };
+ })();
+}
+
+if (!window.clearImmediate) {
+ window.clearImmediate = (function setupClearImmediate() {
+ return window.msClearImmediate ||
+ window.webkitClearImmediate ||
+ window.mozClearImmediate ||
+ window.oClearImmediate ||
+ // "clearZeroTimeout" is implement on the previous block ||
+ // fallback
+ function clearImmediateFallback(timer) {
+ window.clearTimeout(timer);
+ };
+ })();
+}
+
+(function(global) {
+
+ // Check if WordCloud can run on this browser
+ var isSupported = (function isSupported() {
+ var canvas = document.createElement('canvas');
+ if (!canvas || !canvas.getContext) {
+ return false;
+ }
+
+ var ctx = canvas.getContext('2d');
+ if (!ctx) {
+ return false;
+ }
+ if (!ctx.getImageData) {
+ return false;
+ }
+ if (!ctx.fillText) {
+ return false;
+ }
+
+ if (!Array.prototype.some) {
+ return false;
+ }
+ if (!Array.prototype.push) {
+ return false;
+ }
+
+ return true;
+ }());
+
+ // Find out if the browser impose minium font size by
+ // drawing small texts on a canvas and measure it's width.
+ var minFontSize = (function getMinFontSize() {
+ if (!isSupported) {
+ return;
+ }
+
+ var ctx = document.createElement('canvas').getContext('2d');
+
+ // start from 20
+ var size = 20;
+
+ // two sizes to measure
+ var hanWidth, mWidth;
+
+ while (size) {
+ ctx.font = size.toString(10) + 'px sans-serif';
+ if ((ctx.measureText('\uFF37').width === hanWidth) &&
+ (ctx.measureText('m').width) === mWidth) {
+ return (size + 1);
+ }
+
+ hanWidth = ctx.measureText('\uFF37').width;
+ mWidth = ctx.measureText('m').width;
+
+ size--;
+ }
+
+ return 0;
+ })();
+
+ // Based on http://jsfromhell.com/array/shuffle
+ var shuffleArray = function shuffleArray(arr) {
+ for (var j, x, i = arr.length; i;
+ j = Math.floor(Math.random() * i),
+ x = arr[--i], arr[i] = arr[j],
+ arr[j] = x) {}
+ return arr;
+ };
+
+ var WordCloud = function WordCloud(elements, options) {
+ if (!isSupported) {
+ return;
+ }
+
+ if (!Array.isArray(elements)) {
+ elements = [elements];
+ }
+
+ elements.forEach(function(el, i) {
+ if (typeof el === 'string') {
+ elements[i] = document.getElementById(el);
+ if (!elements[i]) {
+ throw 'The element id specified is not found.';
+ }
+ } else if (!el.tagName && !el.appendChild) {
+ throw 'You must pass valid HTML elements, or ID of the element.';
+ }
+ });
+
+ /* Default values to be overwritten by options object */
+ var settings = {
+ list: [],
+ fontFamily: '"Trebuchet MS", "Heiti TC", "微軟正黑體", ' +
+ '"Arial Unicode MS", "Droid Fallback Sans", sans-serif',
+ fontWeight: 'normal',
+ color: 'random-dark',
+ minSize: 0, // 0 to disable
+ weightFactor: 1,
+ clearCanvas: true,
+ backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1)
+
+ gridSize: 8,
+ drawOutOfBound: false,
+ origin: null,
+
+ drawMask: false,
+ maskColor: 'rgba(255,0,0,0.3)',
+ maskGapWidth: 0.3,
+
+ wait: 0,
+ abortThreshold: 0, // disabled
+ abort: function noop() {},
+
+ minRotation: - Math.PI / 2,
+ maxRotation: Math.PI / 2,
+ rotationSteps: 0,
+
+ shuffle: true,
+ rotateRatio: 0.1,
+
+ shape: 'circle',
+ ellipticity: 0.65,
+
+ classes: null,
+
+ hover: null,
+ click: null
+ };
+
+ if (options) {
+ for (var key in options) {
+ if (key in settings) {
+ settings[key] = options[key];
+ }
+ }
+ }
+
+ /* Convert weightFactor into a function */
+ if (typeof settings.weightFactor !== 'function') {
+ var factor = settings.weightFactor;
+ settings.weightFactor = function weightFactor(pt) {
+ return pt * factor; //in px
+ };
+ }
+
+ /* Convert shape into a function */
+ if (typeof settings.shape !== 'function') {
+ switch (settings.shape) {
+ case 'circle':
+ /* falls through */
+ default:
+ // 'circle' is the default and a shortcut in the code loop.
+ settings.shape = 'circle';
+ break;
+
+ case 'cardioid':
+ settings.shape = function shapeCardioid(theta) {
+ return 1 - Math.sin(theta);
+ };
+ break;
+
+ /*
+
+ To work out an X-gon, one has to calculate "m",
+ where 1/(cos(2*PI/X)+m*sin(2*PI/X)) = 1/(cos(0)+m*sin(0))
+ http://www.wolframalpha.com/input/?i=1%2F%28cos%282*PI%2FX%29%2Bm*sin%28
+ 2*PI%2FX%29%29+%3D+1%2F%28cos%280%29%2Bm*sin%280%29%29
+
+ Copy the solution into polar equation r = 1/(cos(t') + m*sin(t'))
+ where t' equals to mod(t, 2PI/X);
+
+ */
+
+ case 'diamond':
+ case 'square':
+ // http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
+ // %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D
+ // +0+..+2*PI
+ settings.shape = function shapeSquare(theta) {
+ var thetaPrime = theta % (2 * Math.PI / 4);
+ return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime));
+ };
+ break;
+
+ case 'triangle-forward':
+ // http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
+ // %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29
+ // %29%29%2C+t+%3D+0+..+2*PI
+ settings.shape = function shapeTriangle(theta) {
+ var thetaPrime = theta % (2 * Math.PI / 3);
+ return 1 / (Math.cos(thetaPrime) +
+ Math.sqrt(3) * Math.sin(thetaPrime));
+ };
+ break;
+
+ case 'triangle':
+ case 'triangle-upright':
+ settings.shape = function shapeTriangle(theta) {
+ var thetaPrime = (theta + Math.PI * 3 / 2) % (2 * Math.PI / 3);
+ return 1 / (Math.cos(thetaPrime) +
+ Math.sqrt(3) * Math.sin(thetaPrime));
+ };
+ break;
+
+ case 'pentagon':
+ settings.shape = function shapePentagon(theta) {
+ var thetaPrime = (theta + 0.955) % (2 * Math.PI / 5);
+ return 1 / (Math.cos(thetaPrime) +
+ 0.726543 * Math.sin(thetaPrime));
+ };
+ break;
+
+ case 'star':
+ settings.shape = function shapeStar(theta) {
+ var thetaPrime = (theta + 0.955) % (2 * Math.PI / 10);
+ if ((theta + 0.955) % (2 * Math.PI / 5) - (2 * Math.PI / 10) >= 0) {
+ return 1 / (Math.cos((2 * Math.PI / 10) - thetaPrime) +
+ 3.07768 * Math.sin((2 * Math.PI / 10) - thetaPrime));
+ } else {
+ return 1 / (Math.cos(thetaPrime) +
+ 3.07768 * Math.sin(thetaPrime));
+ }
+ };
+ break;
+ }
+ }
+
+ /* Make sure gridSize is a whole number and is not smaller than 4px */
+ settings.gridSize = Math.max(Math.floor(settings.gridSize), 4);
+
+ /* shorthand */
+ var g = settings.gridSize;
+ var maskRectWidth = g - settings.maskGapWidth;
+
+ /* normalize rotation settings */
+ var rotationRange = Math.abs(settings.maxRotation - settings.minRotation);
+ var rotationSteps = Math.abs(Math.floor(settings.rotationSteps));
+ var minRotation = Math.min(settings.maxRotation, settings.minRotation);
+
+ /* information/object available to all functions, set when start() */
+ var grid, // 2d array containing filling information
+ ngx, ngy, // width and height of the grid
+ center, // position of the center of the cloud
+ maxRadius;
+
+ /* timestamp for measuring each putWord() action */
+ var escapeTime;
+
+ /* function for getting the color of the text */
+ var getTextColor;
+ function random_hsl_color(min, max) {
+ return 'hsl(' +
+ (Math.random() * 360).toFixed() + ',' +
+ (Math.random() * 30 + 70).toFixed() + '%,' +
+ (Math.random() * (max - min) + min).toFixed() + '%)';
+ }
+ switch (settings.color) {
+ case 'random-dark':
+ getTextColor = function getRandomDarkColor() {
+ return random_hsl_color(10, 50);
+ };
+ break;
+
+ case 'random-light':
+ getTextColor = function getRandomLightColor() {
+ return random_hsl_color(50, 90);
+ };
+ break;
+
+ default:
+ if (typeof settings.color === 'function') {
+ getTextColor = settings.color;
+ }
+ break;
+ }
+
+ /* function for getting the classes of the text */
+ var getTextClasses = null;
+ if (typeof settings.classes === 'function') {
+ getTextClasses = settings.classes;
+ }
+
+ /* Interactive */
+ var interactive = false;
+ var infoGrid = [];
+ var hovered;
+
+ var getInfoGridFromMouseTouchEvent =
+ function getInfoGridFromMouseTouchEvent(evt) {
+ var canvas = evt.currentTarget;
+ var rect = canvas.getBoundingClientRect();
+ var clientX;
+ var clientY;
+ /** Detect if touches are available */
+ if (evt.touches) {
+ clientX = evt.touches[0].clientX;
+ clientY = evt.touches[0].clientY;
+ } else {
+ clientX = evt.clientX;
+ clientY = evt.clientY;
+ }
+ var eventX = clientX - rect.left;
+ var eventY = clientY - rect.top;
+
+ var x = Math.floor(eventX * ((canvas.width / rect.width) || 1) / g);
+ var y = Math.floor(eventY * ((canvas.height / rect.height) || 1) / g);
+
+ return infoGrid[x][y];
+ };
+
+ var wordcloudhover = function wordcloudhover(evt) {
+ var info = getInfoGridFromMouseTouchEvent(evt);
+
+ if (hovered === info) {
+ return;
+ }
+
+ hovered = info;
+ if (!info) {
+ settings.hover(undefined, undefined, evt);
+
+ return;
+ }
+
+ settings.hover(info.item, info.dimension, evt);
+
+ };
+
+ var wordcloudclick = function wordcloudclick(evt) {
+ var info = getInfoGridFromMouseTouchEvent(evt);
+ if (!info) {
+ return;
+ }
+
+ settings.click(info.item, info.dimension, evt);
+ evt.preventDefault();
+ };
+
+ /* Get points on the grid for a given radius away from the center */
+ var pointsAtRadius = [];
+ var getPointsAtRadius = function getPointsAtRadius(radius) {
+ if (pointsAtRadius[radius]) {
+ return pointsAtRadius[radius];
+ }
+
+ // Look for these number of points on each radius
+ var T = radius * 8;
+
+ // Getting all the points at this radius
+ var t = T;
+ var points = [];
+
+ if (radius === 0) {
+ points.push([center[0], center[1], 0]);
+ }
+
+ while (t--) {
+ // distort the radius to put the cloud in shape
+ var rx = 1;
+ if (settings.shape !== 'circle') {
+ rx = settings.shape(t / T * 2 * Math.PI); // 0 to 1
+ }
+
+ // Push [x, y, t]; t is used solely for getTextColor()
+ points.push([
+ center[0] + radius * rx * Math.cos(-t / T * 2 * Math.PI),
+ center[1] + radius * rx * Math.sin(-t / T * 2 * Math.PI) *
+ settings.ellipticity,
+ t / T * 2 * Math.PI]);
+ }
+
+ pointsAtRadius[radius] = points;
+ return points;
+ };
+
+ /* Return true if we had spent too much time */
+ var exceedTime = function exceedTime() {
+ return ((settings.abortThreshold > 0) &&
+ ((new Date()).getTime() - escapeTime > settings.abortThreshold));
+ };
+
+ /* Get the deg of rotation according to settings, and luck. */
+ var getRotateDeg = function getRotateDeg() {
+ if (settings.rotateRatio === 0) {
+ return 0;
+ }
+
+ if (Math.random() > settings.rotateRatio) {
+ return 0;
+ }
+
+ if (rotationRange === 0) {
+ return minRotation;
+ }
+
+ if (rotationSteps > 0) {
+ // Min rotation + zero or more steps * span of one step
+ return minRotation +
+ Math.floor(Math.random() * rotationSteps) *
+ rotationRange / (rotationSteps - 1);
+ }
+ else {
+ return minRotation + Math.random() * rotationRange;
+ }
+ };
+
+ var getTextInfo = function getTextInfo(word, weight, rotateDeg) {
+ // calculate the acutal font size
+ // fontSize === 0 means weightFactor function wants the text skipped,
+ // and size < minSize means we cannot draw the text.
+ var debug = false;
+ var fontSize = settings.weightFactor(weight);
+ if (fontSize <= settings.minSize) {
+ return false;
+ }
+
+ // Scale factor here is to make sure fillText is not limited by
+ // the minium font size set by browser.
+ // It will always be 1 or 2n.
+ var mu = 1;
+ if (fontSize < minFontSize) {
+ mu = (function calculateScaleFactor() {
+ var mu = 2;
+ while (mu * fontSize < minFontSize) {
+ mu += 2;
+ }
+ return mu;
+ })();
+ }
+
+ var fcanvas = document.createElement('canvas');
+ var fctx = fcanvas.getContext('2d', { willReadFrequently: true });
+
+ fctx.font = settings.fontWeight + ' ' +
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily;
+
+ // Estimate the dimension of the text with measureText().
+ var fw = fctx.measureText(word).width / mu;
+ var fh = Math.max(fontSize * mu,
+ fctx.measureText('m').width,
+ fctx.measureText('\uFF37').width) / mu;
+
+ // Create a boundary box that is larger than our estimates,
+ // so text don't get cut of (it sill might)
+ var boxWidth = fw + fh * 2;
+ var boxHeight = fh * 3;
+ var fgw = Math.ceil(boxWidth / g);
+ var fgh = Math.ceil(boxHeight / g);
+ boxWidth = fgw * g;
+ boxHeight = fgh * g;
+
+ // Calculate the proper offsets to make the text centered at
+ // the preferred position.
+
+ // This is simply half of the width.
+ var fillTextOffsetX = - fw / 2;
+ // Instead of moving the box to the exact middle of the preferred
+ // position, for Y-offset we move 0.4 instead, so Latin alphabets look
+ // vertical centered.
+ var fillTextOffsetY = - fh * 0.4;
+
+ // Calculate the actual dimension of the canvas, considering the rotation.
+ var cgh = Math.ceil((boxWidth * Math.abs(Math.sin(rotateDeg)) +
+ boxHeight * Math.abs(Math.cos(rotateDeg))) / g);
+ var cgw = Math.ceil((boxWidth * Math.abs(Math.cos(rotateDeg)) +
+ boxHeight * Math.abs(Math.sin(rotateDeg))) / g);
+ var width = cgw * g;
+ var height = cgh * g;
+
+ fcanvas.setAttribute('width', width);
+ fcanvas.setAttribute('height', height);
+
+ if (debug) {
+ // Attach fcanvas to the DOM
+ document.body.appendChild(fcanvas);
+ // Save it's state so that we could restore and draw the grid correctly.
+ fctx.save();
+ }
+
+ // Scale the canvas with |mu|.
+ fctx.scale(1 / mu, 1 / mu);
+ fctx.translate(width * mu / 2, height * mu / 2);
+ fctx.rotate(- rotateDeg);
+
+ // Once the width/height is set, ctx info will be reset.
+ // Set it again here.
+ fctx.font = settings.fontWeight + ' ' +
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily;
+
+ // Fill the text into the fcanvas.
+ // XXX: We cannot because textBaseline = 'top' here because
+ // Firefox and Chrome uses different default line-height for canvas.
+ // Please read https://bugzil.la/737852#c6.
+ // Here, we use textBaseline = 'middle' and draw the text at exactly
+ // 0.5 * fontSize lower.
+ fctx.fillStyle = '#000';
+ fctx.textBaseline = 'middle';
+ fctx.fillText(word, fillTextOffsetX * mu,
+ (fillTextOffsetY + fontSize * 0.5) * mu);
+
+ // Get the pixels of the text
+ var imageData = fctx.getImageData(0, 0, width, height).data;
+
+ if (exceedTime()) {
+ return false;
+ }
+
+ if (debug) {
+ // Draw the box of the original estimation
+ fctx.strokeRect(fillTextOffsetX * mu,
+ fillTextOffsetY, fw * mu, fh * mu);
+ fctx.restore();
+ }
+
+ // Read the pixels and save the information to the occupied array
+ var occupied = [];
+ var gx = cgw, gy, x, y;
+ var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2];
+ while (gx--) {
+ gy = cgh;
+ while (gy--) {
+ y = g;
+ singleGridLoop: {
+ while (y--) {
+ x = g;
+ while (x--) {
+ if (imageData[((gy * g + y) * width +
+ (gx * g + x)) * 4 + 3]) {
+ occupied.push([gx, gy]);
+
+ if (gx < bounds[3]) {
+ bounds[3] = gx;
+ }
+ if (gx > bounds[1]) {
+ bounds[1] = gx;
+ }
+ if (gy < bounds[0]) {
+ bounds[0] = gy;
+ }
+ if (gy > bounds[2]) {
+ bounds[2] = gy;
+ }
+
+ if (debug) {
+ fctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
+ fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5);
+ }
+ break singleGridLoop;
+ }
+ }
+ }
+ if (debug) {
+ fctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
+ fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5);
+ }
+ }
+ }
+ }
+
+ if (debug) {
+ fctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+ fctx.fillRect(bounds[3] * g,
+ bounds[0] * g,
+ (bounds[1] - bounds[3] + 1) * g,
+ (bounds[2] - bounds[0] + 1) * g);
+ }
+
+ // Return information needed to create the text on the real canvas
+ return {
+ mu: mu,
+ occupied: occupied,
+ bounds: bounds,
+ gw: cgw,
+ gh: cgh,
+ fillTextOffsetX: fillTextOffsetX,
+ fillTextOffsetY: fillTextOffsetY,
+ fillTextWidth: fw,
+ fillTextHeight: fh,
+ fontSize: fontSize
+ };
+ };
+
+ /* Determine if there is room available in the given dimension */
+ var canFitText = function canFitText(gx, gy, gw, gh, occupied) {
+ // Go through the occupied points,
+ // return false if the space is not available.
+ var i = occupied.length;
+ while (i--) {
+ var px = gx + occupied[i][0];
+ var py = gy + occupied[i][1];
+
+ if (px >= ngx || py >= ngy || px < 0 || py < 0) {
+ if (!settings.drawOutOfBound) {
+ return false;
+ }
+ continue;
+ }
+
+ if (!grid[px][py]) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ /* Actually draw the text on the grid */
+ var drawText = function drawText(gx, gy, info, word, weight,
+ distance, theta, rotateDeg, attributes) {
+
+ var fontSize = info.fontSize;
+ var color;
+ if (getTextColor) {
+ color = getTextColor(word, weight, fontSize, distance, theta);
+ } else {
+ color = settings.color;
+ }
+
+ var classes;
+ if (getTextClasses) {
+ classes = getTextClasses(word, weight, fontSize, distance, theta);
+ } else {
+ classes = settings.classes;
+ }
+
+ var dimension;
+ var bounds = info.bounds;
+ dimension = {
+ x: (gx + bounds[3]) * g,
+ y: (gy + bounds[0]) * g,
+ w: (bounds[1] - bounds[3] + 1) * g,
+ h: (bounds[2] - bounds[0] + 1) * g
+ };
+
+ elements.forEach(function(el) {
+ if (el.getContext) {
+ var ctx = el.getContext('2d');
+ var mu = info.mu;
+
+ // Save the current state before messing it
+ ctx.save();
+ ctx.scale(1 / mu, 1 / mu);
+
+ ctx.font = settings.fontWeight + ' ' +
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily;
+ ctx.fillStyle = color;
+
+ // Translate the canvas position to the origin coordinate of where
+ // the text should be put.
+ ctx.translate((gx + info.gw / 2) * g * mu,
+ (gy + info.gh / 2) * g * mu);
+
+ if (rotateDeg !== 0) {
+ ctx.rotate(- rotateDeg);
+ }
+
+ // Finally, fill the text.
+
+ // XXX: We cannot because textBaseline = 'top' here because
+ // Firefox and Chrome uses different default line-height for canvas.
+ // Please read https://bugzil.la/737852#c6.
+ // Here, we use textBaseline = 'middle' and draw the text at exactly
+ // 0.5 * fontSize lower.
+ ctx.textBaseline = 'middle';
+ ctx.fillText(word, info.fillTextOffsetX * mu,
+ (info.fillTextOffsetY + fontSize * 0.5) * mu);
+
+ // The below box is always matches how s are positioned
+ /* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY,
+ info.fillTextWidth, info.fillTextHeight); */
+
+ // Restore the state.
+ ctx.restore();
+ } else {
+ // drawText on DIV element
+ var span = document.createElement('span');
+ var transformRule = '';
+ transformRule = 'rotate(' + (- rotateDeg / Math.PI * 180) + 'deg) ';
+ if (info.mu !== 1) {
+ transformRule +=
+ 'translateX(-' + (info.fillTextWidth / 4) + 'px) ' +
+ 'scale(' + (1 / info.mu) + ')';
+ }
+ var styleRules = {
+ 'position': 'absolute',
+ 'display': 'block',
+ 'font': settings.fontWeight + ' ' +
+ (fontSize * info.mu) + 'px ' + settings.fontFamily,
+ 'left': ((gx + info.gw / 2) * g + info.fillTextOffsetX) + 'px',
+ 'top': ((gy + info.gh / 2) * g + info.fillTextOffsetY) + 'px',
+ 'width': info.fillTextWidth + 'px',
+ 'height': info.fillTextHeight + 'px',
+ 'lineHeight': fontSize + 'px',
+ 'whiteSpace': 'nowrap',
+ 'transform': transformRule,
+ 'webkitTransform': transformRule,
+ 'msTransform': transformRule,
+ 'transformOrigin': '50% 40%',
+ 'webkitTransformOrigin': '50% 40%',
+ 'msTransformOrigin': '50% 40%'
+ };
+ if (color) {
+ styleRules.color = color;
+ }
+ span.textContent = word;
+ for (var cssProp in styleRules) {
+ span.style[cssProp] = styleRules[cssProp];
+ }
+ if (attributes) {
+ for (var attribute in attributes) {
+ span.setAttribute(attribute, attributes[attribute]);
+ }
+ }
+ if (classes) {
+ span.className += classes;
+ }
+ el.appendChild(span);
+ }
+ });
+ };
+
+ /* Help function to updateGrid */
+ var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) {
+ if (x >= ngx || y >= ngy || x < 0 || y < 0) {
+ return;
+ }
+
+ grid[x][y] = false;
+
+ if (drawMask) {
+ var ctx = elements[0].getContext('2d');
+ ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth);
+ }
+
+ if (interactive) {
+ infoGrid[x][y] = { item: item, dimension: dimension };
+ }
+ };
+
+ /* Update the filling information of the given space with occupied points.
+ Draw the mask on the canvas if necessary. */
+ var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) {
+ var occupied = info.occupied;
+ var drawMask = settings.drawMask;
+ var ctx;
+ if (drawMask) {
+ ctx = elements[0].getContext('2d');
+ ctx.save();
+ ctx.fillStyle = settings.maskColor;
+ }
+
+ var dimension;
+ if (interactive) {
+ var bounds = info.bounds;
+ dimension = {
+ x: (gx + bounds[3]) * g,
+ y: (gy + bounds[0]) * g,
+ w: (bounds[1] - bounds[3] + 1) * g,
+ h: (bounds[2] - bounds[0] + 1) * g
+ };
+ }
+
+ var i = occupied.length;
+ while (i--) {
+ var px = gx + occupied[i][0];
+ var py = gy + occupied[i][1];
+
+ if (px >= ngx || py >= ngy || px < 0 || py < 0) {
+ continue;
+ }
+
+ fillGridAt(px, py, drawMask, dimension, item);
+ }
+
+ if (drawMask) {
+ ctx.restore();
+ }
+ };
+
+ /* putWord() processes each item on the list,
+ calculate it's size and determine it's position, and actually
+ put it on the canvas. */
+ var putWord = function putWord(item) {
+ var word, weight, attributes;
+ if (Array.isArray(item)) {
+ word = item[0];
+ weight = item[1];
+ } else {
+ word = item.word;
+ weight = item.weight;
+ attributes = item.attributes;
+ }
+ var rotateDeg = getRotateDeg();
+
+ // get info needed to put the text onto the canvas
+ var info = getTextInfo(word, weight, rotateDeg);
+
+ // not getting the info means we shouldn't be drawing this one.
+ if (!info) {
+ return false;
+ }
+
+ if (exceedTime()) {
+ return false;
+ }
+
+ // If drawOutOfBound is set to false,
+ // skip the loop if we have already know the bounding box of
+ // word is larger than the canvas.
+ if (!settings.drawOutOfBound) {
+ var bounds = info.bounds;
+ if ((bounds[1] - bounds[3] + 1) > ngx ||
+ (bounds[2] - bounds[0] + 1) > ngy) {
+ return false;
+ }
+ }
+
+ // Determine the position to put the text by
+ // start looking for the nearest points
+ var r = maxRadius + 1;
+
+ var tryToPutWordAtPoint = function(gxy) {
+ var gx = Math.floor(gxy[0] - info.gw / 2);
+ var gy = Math.floor(gxy[1] - info.gh / 2);
+ var gw = info.gw;
+ var gh = info.gh;
+
+ // If we cannot fit the text at this position, return false
+ // and go to the next position.
+ if (!canFitText(gx, gy, gw, gh, info.occupied)) {
+ return false;
+ }
+
+ // Actually put the text on the canvas
+ drawText(gx, gy, info, word, weight,
+ (maxRadius - r), gxy[2], rotateDeg, attributes);
+
+ // Mark the spaces on the grid as filled
+ updateGrid(gx, gy, gw, gh, info, item);
+
+ // Return true so some() will stop and also return true.
+ return true;
+ };
+
+ while (r--) {
+ var points = getPointsAtRadius(maxRadius - r);
+
+ if (settings.shuffle) {
+ points = [].concat(points);
+ shuffleArray(points);
+ }
+
+ // Try to fit the words by looking at each point.
+ // array.some() will stop and return true
+ // when putWordAtPoint() returns true.
+ // If all the points returns false, array.some() returns false.
+ var drawn = points.some(tryToPutWordAtPoint);
+
+ if (drawn) {
+ // leave putWord() and return true
+ return true;
+ }
+ }
+ // we tried all distances but text won't fit, return false
+ return false;
+ };
+
+ /* Send DOM event to all elements. Will stop sending event and return
+ if the previous one is canceled (for cancelable events). */
+ var sendEvent = function sendEvent(type, cancelable, detail) {
+ if (cancelable) {
+ return !elements.some(function(el) {
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent(type, true, cancelable, detail || {});
+ return !el.dispatchEvent(evt);
+ }, this);
+ } else {
+ elements.forEach(function(el) {
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent(type, true, cancelable, detail || {});
+ el.dispatchEvent(evt);
+ }, this);
+ }
+ };
+
+ /* Start drawing on a canvas */
+ var start = function start() {
+ // For dimensions, clearCanvas etc.,
+ // we only care about the first element.
+ var canvas = elements[0];
+
+ if (canvas.getContext) {
+ ngx = Math.ceil(canvas.width / g);
+ ngy = Math.ceil(canvas.height / g);
+ } else {
+ var rect = canvas.getBoundingClientRect();
+ ngx = Math.ceil(rect.width / g);
+ ngy = Math.ceil(rect.height / g);
+ }
+
+ // Sending a wordcloudstart event which cause the previous loop to stop.
+ // Do nothing if the event is canceled.
+ if (!sendEvent('wordcloudstart', true)) {
+ return;
+ }
+
+ // Determine the center of the word cloud
+ center = (settings.origin) ?
+ [settings.origin[0]/g, settings.origin[1]/g] :
+ [ngx / 2, ngy / 2];
+
+ // Maxium radius to look for space
+ maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy));
+
+ /* Clear the canvas only if the clearCanvas is set,
+ if not, update the grid to the current canvas state */
+ grid = [];
+
+ var gx, gy, i;
+ if (!canvas.getContext || settings.clearCanvas) {
+ elements.forEach(function(el) {
+ if (el.getContext) {
+ var ctx = el.getContext('2d');
+ ctx.fillStyle = settings.backgroundColor;
+ ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1));
+ ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1));
+ } else {
+ el.textContent = '';
+ el.style.backgroundColor = settings.backgroundColor;
+ el.style.position = 'relative';
+ }
+ });
+
+ /* fill the grid with empty state */
+ gx = ngx;
+ while (gx--) {
+ grid[gx] = [];
+ gy = ngy;
+ while (gy--) {
+ grid[gx][gy] = true;
+ }
+ }
+ } else {
+ /* Determine bgPixel by creating
+ another canvas and fill the specified background color. */
+ var bctx = document.createElement('canvas').getContext('2d');
+
+ bctx.fillStyle = settings.backgroundColor;
+ bctx.fillRect(0, 0, 1, 1);
+ var bgPixel = bctx.getImageData(0, 0, 1, 1).data;
+
+ /* Read back the pixels of the canvas we got to tell which part of the
+ canvas is empty.
+ (no clearCanvas only works with a canvas, not divs) */
+ var imageData =
+ canvas.getContext('2d').getImageData(0, 0, ngx * g, ngy * g).data;
+
+ gx = ngx;
+ var x, y;
+ while (gx--) {
+ grid[gx] = [];
+ gy = ngy;
+ while (gy--) {
+ y = g;
+ singleGridLoop: while (y--) {
+ x = g;
+ while (x--) {
+ i = 4;
+ while (i--) {
+ if (imageData[((gy * g + y) * ngx * g +
+ (gx * g + x)) * 4 + i] !== bgPixel[i]) {
+ grid[gx][gy] = false;
+ break singleGridLoop;
+ }
+ }
+ }
+ }
+ if (grid[gx][gy] !== false) {
+ grid[gx][gy] = true;
+ }
+ }
+ }
+
+ imageData = bctx = bgPixel = undefined;
+ }
+
+ // fill the infoGrid with empty state if we need it
+ if (settings.hover || settings.click) {
+
+ interactive = true;
+
+ /* fill the grid with empty state */
+ gx = ngx + 1;
+ while (gx--) {
+ infoGrid[gx] = [];
+ }
+
+ if (settings.hover) {
+ canvas.addEventListener('mousemove', wordcloudhover);
+ }
+
+ var touchend = function (e) {
+ e.preventDefault();
+ };
+
+ if (settings.click) {
+ canvas.addEventListener('click', wordcloudclick);
+ canvas.addEventListener('touchstart', wordcloudclick);
+ canvas.addEventListener('touchend', touchend);
+ canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)';
+ }
+
+ canvas.addEventListener('wordcloudstart', function stopInteraction() {
+ canvas.removeEventListener('wordcloudstart', stopInteraction);
+
+ canvas.removeEventListener('mousemove', wordcloudhover);
+ canvas.removeEventListener('click', wordcloudclick);
+ canvas.removeEventListener('touchstart', wordcloudclick);
+ canvas.removeEventListener('touchend', touchend);
+ hovered = undefined;
+ });
+ }
+
+ i = 0;
+ var loopingFunction, stoppingFunction;
+ if (settings.wait !== 0) {
+ loopingFunction = window.setTimeout;
+ stoppingFunction = window.clearTimeout;
+ } else {
+ loopingFunction = window.setImmediate;
+ stoppingFunction = window.clearImmediate;
+ }
+
+ var addEventListener = function addEventListener(type, listener) {
+ elements.forEach(function(el) {
+ el.addEventListener(type, listener);
+ }, this);
+ };
+
+ var removeEventListener = function removeEventListener(type, listener) {
+ elements.forEach(function(el) {
+ el.removeEventListener(type, listener);
+ }, this);
+ };
+
+ var anotherWordCloudStart = function anotherWordCloudStart() {
+ removeEventListener('wordcloudstart', anotherWordCloudStart);
+ stoppingFunction(timer);
+ };
+
+ addEventListener('wordcloudstart', anotherWordCloudStart);
+
+ var timer = loopingFunction(function loop() {
+ if (i >= settings.list.length) {
+ stoppingFunction(timer);
+ sendEvent('wordcloudstop', false);
+ removeEventListener('wordcloudstart', anotherWordCloudStart);
+
+ return;
+ }
+ escapeTime = (new Date()).getTime();
+ var drawn = putWord(settings.list[i]);
+ var canceled = !sendEvent('wordclouddrawn', true, {
+ item: settings.list[i], drawn: drawn });
+ if (exceedTime() || canceled) {
+ stoppingFunction(timer);
+ settings.abort();
+ sendEvent('wordcloudabort', false);
+ sendEvent('wordcloudstop', false);
+ removeEventListener('wordcloudstart', anotherWordCloudStart);
+ return;
+ }
+ i++;
+ timer = loopingFunction(loop, settings.wait);
+ }, settings.wait);
+ };
+
+ // All set, start the drawing
+ start();
+ };
+
+ WordCloud.isSupported = isSupported;
+ WordCloud.minFontSize = minFontSize;
+
+ // Expose the library as an AMD module
+ if (typeof define === 'function' && define.amd) {
+ global.WordCloud = WordCloud;
+ define('wordcloud', [], function() { return WordCloud; });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = WordCloud;
+ } else {
+ global.WordCloud = WordCloud;
+ }
+
+})(this); //jshint ignore:line
diff --git a/wordle_code.js b/wordle_code.js
new file mode 100644
index 0000000..9593eff
--- /dev/null
+++ b/wordle_code.js
@@ -0,0 +1,255 @@
+/* global WordCloud */
+
+let wordcloud;
+
+// Blacklist with ignored words for each language (Dutch, English, German and French for now)
+const blacklist = {
+ none: [],
+ nl: ['de', 'en', 'van', 'ik', 'te', 'dat', 'die', 'in', 'een', 'hij', 'het',
+ 'niet', 'zijn', 'is', 'was', 'op', 'aan', 'met', 'als', 'voor', 'had', 'er', 'maar', 'om', 'hem',
+ 'dan', 'zou', 'of', 'wat', 'mijn', 'men', 'dit', 'zo', 'door', 'over', 'ze', 'zich', 'bij',
+ 'ook', 'tot', 'je', 'mij', 'uit', 'der', 'daar', 'haar', 'naar', 'heb', 'hoe', 'heeft', 'hebben',
+ 'deze', 'u', 'want', 'nog', 'zal', 'me', 'zij', 'nu', 'ge', 'geen', 'omdat', 'iets', 'worden',
+ 'toch', 'al', 'waren', 'veel', 'meer', 'doen', 'toen', 'moet', 'ben', 'zonder', 'kan', 'hun',
+ 'dus', 'alles', 'onder', 'ja', 'eens', 'hier', 'wie', 'werd', 'altijd', 'doch', 'wordt', 'wezen',
+ 'kunnen', 'ons', 'zelf', 'tegen', 'na', 'reeds', 'wil', 'kon', 'niets', 'uw', 'iemand', 'geweest',
+ 'andere', 'wij', 'we', 'z\'n', 'z\'n', 'zo\'n', 'zo\'n'],
+ en: ['i', 'me', 'my', 'myself', 'we', 'us', 'our', 'ours', 'ourselves', 'you', 'your', 'yours',
+ 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', 'it',
+ 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom',
+ 'whose', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been',
+ 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'will', 'would', 'should',
+ 'can', 'could', 'ought', 'i\'m', 'you\'re', 'he\'s', 'she\'s', 'it\'s', 'we\'re', 'they\'re',
+ 'i\'ve', 'you\'ve', 'we\'ve', 'they\'ve', 'i\'d', 'you\'d', 'he\'d', 'she\'d', 'we\'d', 'they\'d',
+ 'i\'ll', 'you\'ll', 'he\'ll', 'she\'ll', 'we\'ll', 'they\'ll', 'isn\'t', 'aren\'t', 'wasn\'t',
+ 'weren\'t', 'hasn\'t', 'haven\'t', 'hadn\'t', 'doesn\'t', 'don\'t', 'didn\'t', 'won\'t',
+ 'wouldn\'t', 'shan\'t', 'shouldn\'t', 'can\'t', 'cannot', 'couldn\'t', 'mustn\'t', 'let\'s',
+ 'that\'s', 'who\'s', 'what\'s', 'here\'s', 'there\'s', 'when\'s', 'where\'s', 'why\'s', 'how\'s',
+ 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by',
+ 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after',
+ 'above', 'below', 'to', 'from', 'up', 'upon', 'down', 'in', 'out', 'on', 'off', 'over', 'under',
+ 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any',
+ 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own',
+ 'same', 'so', 'than', 'too', 'very', 'say', 'says', 'said', 'shall'],
+ de: ['aber', 'alle', 'allem', 'allen', 'aller', 'alles', 'als', 'also', 'am', 'an', 'ander',
+ 'andere', 'anderem', 'anderen', 'anderer', 'anderes', 'anderm', 'andern', 'anders', 'auch', 'auf',
+ 'aus', 'bei', 'bin', 'bis', 'bist', 'da', 'damit', 'dann', 'das', 'dass', 'daß', 'dasselbe',
+ 'dazu', 'dein', 'deine', 'deinem', 'deinen', 'deiner', 'deines', 'dem', 'demselben', 'den',
+ 'denn', 'denselben', 'der', 'derer', 'derselbe', 'derselben', 'des', 'desselben', 'dessen',
+ 'dich', 'die', 'dies', 'diese', 'dieselbe', 'dieselben', 'diesem', 'diesen', 'dieser', 'dieses',
+ 'dir', 'doch', 'dort', 'du', 'durch', 'ein', 'eine', 'einem', 'einen', 'einer', 'eines', 'einige',
+ 'einigem', 'einigen', 'einiger', 'einiges', 'einmal', 'er', 'es', 'etwas', 'euch', 'euer', 'eure',
+ 'eurem', 'euren', 'eurer', 'eures', 'für', 'gegen', 'gewesen', 'hab', 'habe', 'haben', 'hat',
+ 'hatte', 'hatten', 'hier', 'hin', 'hinter', 'ich', 'ihm', 'ihm', 'ihn', 'ihnen', 'ihr', 'ihre',
+ 'ihrem', 'ihren', 'ihrer', 'ihres', 'im', 'in', 'indem', 'ins', 'ist', 'jede', 'jedem', 'jeden',
+ 'jeder', 'jedes', 'jene', 'jenem', 'jenen', 'jener', 'jenes', 'jetzt', 'kann', 'kein', 'keine',
+ 'keinem', 'keinen', 'keiner', 'keines', 'können', 'könnte', 'machen', 'man', 'manche', 'manchem',
+ 'manchen', 'mancher', 'manches', 'mein', 'meine', 'meinem', 'meinen', 'meiner', 'meines', 'mich',
+ 'mir', 'mit', 'muss', 'musste', 'nach', 'nicht', 'nichts', 'noch', 'nun', 'nur', 'ob', 'oder',
+ 'ohne', 'sehr', 'sein', 'seine', 'seinem', 'seinen', 'seiner', 'seines', 'selbst', 'sich', 'sie',
+ 'sind', 'so', 'solche', 'solchem', 'solchen', 'solcher', 'solches', 'soll', 'sollte', 'sondern',
+ 'sonst', 'über', 'um', 'und', 'uns', 'unser', 'unsere', 'unserem', 'unseren', 'unseres', 'unter',
+ 'viel', 'vom', 'von', 'vor', 'während', 'war', 'waren', 'warst', 'was', 'weg', 'weil', 'weiter',
+ 'welche', 'welchem', 'welchen', 'welcher', 'welches', 'wenn', 'werde', 'werden', 'wie', 'wieder',
+ 'will', 'wir', 'wird', 'wirst', 'wo', 'wollen', 'wollte', 'würde', 'würden', 'zu', 'zum', 'zur',
+ 'zwar', 'zwischen'],
+ fr: ['ai', 'au', 'aux', 'avec', 'ce', 'ces', 'dans', 'de', 'des', 'du', 'elle', 'en', 'et', 'eux',
+ 'il', 'je', 'la', 'le', 'les', 'leur', 'lui', 'ma', 'mais', 'me', 'même', 'mes', 'moi', 'mon',
+ 'ne', 'nos', 'notre', 'nous', 'on', 'ou', 'par', 'pas', 'pour', 'qu', 'que', 'qui', 'sa', 'se',
+ 'ses', 'son', 'sur', 'ta', 'te', 'tes', 'toi', 'ton', 'tu', 'un', 'une', 'vos', 'votre', 'vous',
+ 'c', 'd', 'j', 'l', 'à ', 'm', 'n', 's', 't', 'y', 'été', 'étée', 'étées', 'étés', 'étant',
+ 'étante', 'étants', 'étantes', 'suis', 'es', 'est', 'sommes', 'êtes', 'sont', 'serai', 'seras',
+ 'sera', 'serons', 'serez', 'seront', 'serais', 'serait', 'serions', 'seriez', 'seraient', 'étais',
+ 'était', 'étions', 'étiez', 'étaient', 'fus', 'fut', 'fûmes', 'fûtes', 'furent', 'sois', 'soit',
+ 'soyons', 'soyez', 'soient', 'fusse', 'fusses', 'fût', 'fussions', 'fussiez', 'fussent', 'ayant',
+ 'ayante', 'ayantes', 'ayants', 'eu', 'eue', 'eues', 'eus', 'ai', 'as', 'avons', 'avez', 'ont',
+ 'aurai', 'auras', 'aura', 'aurons', 'aurez', 'auront', 'aurais', 'aurait', 'aurions', 'auriez',
+ 'auraient', 'avais', 'avait', 'avions', 'aviez', 'avaient', 'eut', 'eûmes', 'eûtes', 'eurent',
+ 'aie', 'aies', 'ait', 'ayons', 'ayez', 'aient', 'eusse', 'eusses', 'eût', 'eussions', 'eussiez',
+ 'eussent'],
+};
+
+// Count the occurence of items in the array by sorting them and seeing if the next iteration of the
+// loop matches the previous one
+function makeCountArray(arr) {
+ const a = [];
+ const b = [];
+ let prev;
+ arr.sort();
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i] !== prev) {
+ a.push(arr[i]);
+ b.push(1);
+ } else {
+ b[b.length - 1] ++;
+ }
+ prev = arr[i];
+ }
+ return [a, b];
+}
+
+// Function to format the words into a counted, sorted and sliced array
+function makeWordArray(words) {
+ const tempWords = words
+ .filter(word => { // Filter the words: remove if the word is on the blacklist,
+ // remove if the word is empty and remove if length of word is 1 or less
+ const lang = document.getElementById('language').value;
+ return (blacklist[lang].indexOf(word) === -1) && (word.length > 1) &&
+ (document.getElementById('ignored')
+ .value.replace(' ', '')
+ .split(',')
+ .map(val => val.toLowerCase().trim())
+ .indexOf(word) === -1);
+ });
+ const wordCount = makeCountArray(tempWords); // Create an array with counts
+ const max = Math.max.apply(null, wordCount[1]); // Figure out the max count
+ // const maxVal = 50; // Set the max value we want in final array
+ const minVal = 4; // Set the min value we want in final array
+ // Calculate the values with a quadratic function and a set max
+ const counts = wordCount[1].map(val => Math.round((val / max) * 46 + minVal));
+
+ const result = []; // Can probably skip and work directly on wordCount
+ for (let i = 0; i < wordCount[0].length; i++) { // See above
+ result.push([wordCount[0][i], counts[i]]);
+ }
+ result // See above
+ .sort((a, b) => b[1] - a[1]); // Sort descending based on count
+
+ // Next few lines are to introduce a slight degree of randomness in the first three items
+ const temp = result.splice(0, 3); // Get the first three elements
+ temp.sort(() => 0.5 - Math.random()); // And shuffle them in the most basic way possible
+ result.unshift(...temp); // Put the shuffled front 3 items back in the array
+ return result;
+}
+
+function getRandomColor(col) {
+ if (col === 'red' || col === 'green' || col === 'shootingstar') {
+ // var letters + color are the variables for randomizing green and red
+ const letters = 'abc56789';
+ let color = '';
+ // var shootingStarColor is the variable for the shooting star effect
+ const shootingStarColor = ['#954F12', '#955A12', '#952512', '#682B21', '#953B12', '#481D2F', '#1E102E', '#141040', '#481D36'];
+ let shootingStar = '';
+ // generates letters to use in allgreen + allred
+ for (let i = 0; i < 2; i++) {
+ color += letters[Math.floor(Math.random() * letters.length)];
+ }
+ // get all colors from shootingStarColor array and randomize them
+ if (col === 'shootingstar') {
+ for (let x = 0; x < shootingStarColor.length; x++) {
+ shootingStar = shootingStarColor[Math.floor(Math.random() * shootingStarColor.length)];
+ }
+ return shootingStar;
+ }
+ // If statement that return the correct color when function getRandomColor is
+ // defined with red/green
+
+ if (col === 'red') {
+ return `#${color}2222`;
+ }
+ return `#22${color}22`;
+ }
+ // Picks a random number and put it in the var random
+ const random = Math.floor(Math.random() * (99 - 20 + 1) + 20);
+ return `#${random}${random}${random}`;
+}
+// Function to draw the wordcloud. Feed it a properly formatted wordArray (sort descending, make
+// an array like ["word",5],["word2",3]). Canvas is the id of the target canvas element.
+
+function makeWordCloud(canvas) {
+ const cloud = makeWordArray(wordcloud).slice(0,
+ parseInt(document.getElementById('wordnumber').value, 10));
+ const scale = parseInt(document.getElementById('scalefactor').value, 10) / 100;
+ document.getElementById('error').style.display = 'none';
+ WordCloud(document.getElementById(canvas), {
+ list: cloud,
+ rotateRatio: 0, // Odds of word rotation
+ fontFamily: 'Raleway', // CSS font
+ gridSize: 7, // Spacing between words
+ ellipticity: 0.4, // Higher = rounder
+ minSize: 4, // Minimum pixel size for a word
+ origin: [250, 150], // Starting point in the canvas element
+ shape: 'cardioid', // Kidney-shaped
+ shuffle: true,
+ drawOutOfBound: false, // Permission to go over the edge
+ backgroundColor: 'transparent',
+ color: () => getRandomColor(document.getElementById('colortype').value),
+ weightFactor: (size) => (((size * 50) / 2500) * size + size) * scale,
+ // Based on a max initial size of 50, this makes differences more extreme.
+ // Returns max double the size.
+ click: (item) => {
+ wordcloud = wordcloud.filter(word => (word !== item[0]));
+ // When a word is clicked, filter the array for the clicked word to remove it
+ makeWordCloud(canvas); // And call itself to re-draw with the modified data.
+ },
+ });
+} // End of function makeWordCloud
+
+// Create function to perform basic array cleaning and cache the result
+function cleanWordInput(element) {
+ const words =
+ $('#'+element).text() // Get the relevant input
+ .replace(/[^\w~'().\-=?!\u00C0-\u017F]+/g, ' ')
+ // Replace whitespace with a space, except ~ (that signifies a
+ // combination of words)
+ .split(' ') // Split with each space to seperate words.
+ .map((word) => // Clean up the words
+ word
+ .trim()
+ .toLowerCase()
+ .replace(/~/g, ' ')
+ );
+ const words2 = words.filter(v=>v!='');
+ return words2;
+}
+
+function trainSpellCheck (data){
+ var lines = data;
+ console.log('lines', lines);
+ var count = lines.length;
+ lines.forEach(function (line) {
+ setTimeout(function () {
+ speller.train(line);
+ count--;
+ }, 0);
+ });
+ console.log('speller', speller);
+}
+function spellCheck(data){
+ var word = data;
+ // let correctWord;
+ console.log('spellcheckword', word);
+ // setTimeout(function () {
+ let correctWord = speller.correct(word);
+ // }, 0);
+ console.log('correctword', correctWord);
+ return correctWord;
+}
+
+document.getElementById('btn-save').addEventListener('click', function clickSave(e) {
+ const url = document.getElementById('wordle-canvas').toDataURL();
+ if ('download' in document.createElement('a')) {
+ this.href = url;
+ } else {
+ e.preventDefault();
+ alert('Please right click and choose "Save As..." to save the generated image.');
+ window.open(url, '_blank', 'width=500,height=300,menubar=yes');
+ }
+});
+
+document.getElementById('inputwords').addEventListener('click', () => {
+ wordcloud = cleanWordInput('association-container');
+ // var postSpellCheck = spellCheck(tempWords);
+ // console.log(spellCheck(wordleData));
+ // let temptemp = ["angezond", "cafeinn"];
+ // let temp2= spellCheck(temptemp);
+ makeWordCloud('wordle-canvas');
+});
+
+document.getElementById('rerender').addEventListener('click', () => {
+ if (wordcloud !== '') {
+ makeWordCloud('wordle-canvas');
+ } else {
+ // do nothing
+ }
+});
\ No newline at end of file
diff --git a/wordle_stylesheet.css b/wordle_stylesheet.css
new file mode 100644
index 0000000..4d78938
--- /dev/null
+++ b/wordle_stylesheet.css
@@ -0,0 +1,89 @@
+.output-containers {
+ border: 1px solid black;
+ border-radius: 10px;
+ min-height: 450px;
+ max-height: 450px;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+body::-webkit-scrollbar-track
+{
+ -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
+ border-radius: 10px;
+ background-color: #F5F5F5;
+}
+h2{
+ text-align: center;
+}
+h3{
+ color: #D62929;
+}
+#selection{
+ border: 1px solid #D62929;
+ border-radius: 10px;
+ box-shadow: 0 0 5px rgb(0,0,0,.5);
+ min-height: 195px;
+ min-width: 450px;
+}
+.selectiongroup{
+ margin-left: 20px;
+}
+.selectiongroup ul li .btn{
+ width: 95%;
+ text-align: center;
+}
+.btn{
+ margin: 5px;
+}
+.changeword{
+ position: relative;
+}
+#changebtn{
+ position: absolute;
+ top: 20px;
+ right: 0px;
+}
+.wordlebtn{
+ margin-top: 10px;
+}
+/* #wordle-canvas{
+ padding: 10px;
+} */
+ #association-container{
+ height: 480px;
+}
+body::-webkit-scrollbar
+{
+ width: 12px;
+ background-color: #F5F5F5;
+}
+
+body::-webkit-scrollbar-thumb
+{
+ border-radius: 10px;
+ -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
+ background-color: #D62929;
+}
+/* .wordle-button-container{
+ position: relative;
+ height: 100px;
+} */
+ /* #category-button{
+ padding-left: 50px;
+} */
+#output-button{
+ margin-left: 63px;
+}
+#category-button{
+ margin-left: 98px;
+}
+#scalefactor{
+ margin-left: 60px;
+}
+#language{
+ margin-left: 26px;
+}
+#wordle-container{
+ max-height: 450px;
+ max-width: 900px;
+}
\ No newline at end of file