From 70d889dab736b25e161c84f4b7ea855104cfd8e5 Mon Sep 17 00:00:00 2001 From: Ceesjansen Date: Fri, 8 Sep 2017 11:02:54 +0200 Subject: [PATCH] Add files via upload --- download_parse.js | 88 +++ jquery.loading.min.js | 10 + papaparse.min.js | 6 + papaparse_html.html | 186 ++++++ parse_click_functions.js | 55 ++ text_cleanup.js | 72 +++ upload_papaparse_v2.js | 156 +++++ wordcloud2.js | 1183 ++++++++++++++++++++++++++++++++++++++ wordle_code.js | 255 ++++++++ wordle_stylesheet.css | 89 +++ 10 files changed, 2100 insertions(+) create mode 100644 download_parse.js create mode 100644 jquery.loading.min.js create mode 100644 papaparse.min.js create mode 100644 papaparse_html.html create mode 100644 parse_click_functions.js create mode 100644 text_cleanup.js create mode 100644 upload_papaparse_v2.js create mode 100644 wordcloud2.js create mode 100644 wordle_code.js create mode 100644 wordle_stylesheet.css 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 + + + + + + + + + + + + + + + + + + + +
+
+
+

DVJ WORDLE TOOL

+
+
+
+
+
+
+

Step 1:

+ +
+ +
+
+
+
+ +
+
+
+

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

+ +
+
+

Step 4:

+

Generate/re-render or save the worlde

+ + + Save wordle (.png) +
+
+
+
+

Step 5:

+

Download filtered data

+ Download(.csv) +
+
+
+
+

(Optional) Step 6:

+

Choose specific wordle options to get the worlde you prefer

+ +
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ %
+
+
+
+
+ +
+
+
+
+

(Optional) Step 7:

+

Change multiple words to one word

+ + +
+
+ +
+
+
+
+
+ +
Associations +
+ +
+
+ +
Wordles +
+ + +
+
+
+
+ + + + + + + + + + + + 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 = `
+
`; + $("#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