(function(){ 'use strict'; // Extend the element method Element.prototype.wordSearch = function(settings) { return new WordSearch(this, settings); } /** * Word seach * * @param {Element} wrapWl the game's wrap element * @param {Array} settings * constructor */ function WordSearch(wrapEl, settings) { this.wrapEl = wrapEl; // Add `.ws-area` to wrap element this.wrapEl.classList.add('ws-area'); //Words solved. this.solved = 0; // Default settings var default_settings = { 'directions': ['W', 'N', 'WN', 'EN'], 'gridSize': 10, 'words': ['one', 'two', 'three', 'four', 'five'], 'wordsList' : [], 'debug': false } this.settings = Object.merge(settings, default_settings); // Check the words' length if it is overflow the grid if (this.parseWords(this.settings.gridSize)) { // Add words into the matrix data var isWorked = false; while (isWorked == false) { // initialize the application this.initialize(); isWorked = this.addWords(); } // Fill up the remaining blank items if (!this.settings.debug) { this.fillUpFools(); } // Draw the matrix into wrap element this.drawmatrix(); } } /** * Parse words * @param {Number} Max size * @return {Boolean} */ WordSearch.prototype.parseWords = function(maxSize) { var itWorked = true; for (var i = 0; i < this.settings.words.length; i++) { // Convert all the letters to upper case this.settings.wordsList[i] = this.settings.words[i]; this.settings.words[i] = removeDiacritics(this.settings.wordsList[i].toUpperCase()); var word = this.settings.words[i]; if (word.length > maxSize) { alert('The length of word `' + word + '` is overflow the gridSize.'); console.error('The length of word `' + word + '` is overflow the gridSize.'); itWorked = false; } } return itWorked; } /** * Put the words into the matrix */ WordSearch.prototype.addWords = function() { var keepGoing = true, counter = 0, isWorked = true; while (keepGoing) { // Getting random direction var dir = this.settings.directions[Math.rangeInt(this.settings.directions.length - 1)], result = this.addWord(this.settings.words[counter], dir), isWorked = true; if (result == false) { keepGoing = false; isWorked = false; } counter++; if (counter >= this.settings.words.length) { keepGoing = false; } } return isWorked; } /** * Add word into the matrix * * @param {String} word * @param {Number} direction */ WordSearch.prototype.addWord = function(word, direction) { var itWorked = true, directions = { 'W': [0, 1], // Horizontal (From left to right) 'N': [1, 0], // Vertical (From top to bottom) 'WN': [1, 1], // From top left to bottom right 'EN': [1, -1] // From top right to bottom left }, row, col; // y, x switch (direction) { case 'W': // Horizontal (From left to right) var row = Math.rangeInt(this.settings.gridSize - 1), col = Math.rangeInt(this.settings.gridSize - word.length); break; case 'N': // Vertical (From top to bottom) var row = Math.rangeInt(this.settings.gridSize - word.length), col = Math.rangeInt(this.settings.gridSize - 1); break; case 'WN': // From top left to bottom right var row = Math.rangeInt(this.settings.gridSize - word.length), col = Math.rangeInt(this.settings.gridSize - word.length); break; case 'EN': // From top right to bottom left var row = Math.rangeInt(this.settings.gridSize - word.length), col = Math.rangeInt(word.length - 1, this.settings.gridSize - 1); break; default: var error = 'UNKNOWN DIRECTION ' + direction + '!'; alert(error); console.log(error); break; } // Add words to the matrix for (var i = 0; i < word.length; i++) { var newRow = row + i * directions[direction][0], newCol = col + i * directions[direction][1]; // The letter on the board var origin = this.matrix[newRow][newCol].letter; if (origin == '.' || origin == word[i]) { this.matrix[newRow][newCol].letter = word[i]; } else { itWorked = false; } } return itWorked; } /** * Initialize the application */ WordSearch.prototype.initialize = function() { /** * Letter matrix * * param {Array} */ this.matrix = []; /** * Selection from * @Param {Object} */ this.selectFrom = null; /** * Selected items */ this.selected = []; this.initmatrix(this.settings.gridSize); } /** * Fill default items into the matrix * @param {Number} size Grid size */ WordSearch.prototype.initmatrix = function(size) { for (var row = 0; row < size; row++) { for (var col = 0; col < size; col++) { var item = { letter: '.', // Default value row: row, col: col } if (!this.matrix[row]) { this.matrix[row] = []; } this.matrix[row][col] = item; } } } /** * Draw the matrix */ WordSearch.prototype.drawmatrix = function() { for (var row = 0; row < this.settings.gridSize; row++) { // New row var divEl = document.createElement('div'); divEl.setAttribute('class', 'ws-row'); this.wrapEl.appendChild(divEl); for (var col = 0; col < this.settings.gridSize; col++) { var cvEl = document.createElement('canvas'); cvEl.setAttribute('class', 'ws-col'); cvEl.setAttribute('width', 40); cvEl.setAttribute('height', 40); // Fill text in middle center var x = cvEl.width / 2, y = cvEl.height / 2; var ctx = cvEl.getContext('2d'); ctx.font = '400 28px Calibri'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#333'; // Text color ctx.fillText(this.matrix[row][col].letter, x, y); // Add event listeners cvEl.addEventListener('mousedown', this.onMousedown(this.matrix[row][col])); cvEl.addEventListener('mouseover', this.onMouseover(this.matrix[row][col])); cvEl.addEventListener('mouseup', this.onMouseup()); divEl.appendChild(cvEl); } } } /** * Fill up the remaining items */ WordSearch.prototype.fillUpFools = function() { var rangeLanguage = searchLanguage(this.settings.words[0].split('')[0]); for (var row = 0; row < this.settings.gridSize; row++) { for (var col = 0; col < this.settings.gridSize; col++) { if (this.matrix[row][col].letter == '.') { // Math.rangeInt(65, 90) => A ~ Z this.matrix[row][col].letter = String.fromCharCode(Math.rangeInt(rangeLanguage[0], rangeLanguage[1])); } } } } /** * Returns matrix items * @param rowFrom * @param colFrom * @param rowTo * @param colTo * @return {Array} */ WordSearch.prototype.getItems = function(rowFrom, colFrom, rowTo, colTo) { var items = []; if ( rowFrom === rowTo || colFrom === colTo || Math.abs(rowTo - rowFrom) == Math.abs(colTo - colFrom) ) { var shiftY = (rowFrom === rowTo) ? 0 : (rowTo > rowFrom) ? 1 : -1, shiftX = (colFrom === colTo) ? 0 : (colTo > colFrom) ? 1 : -1, row = rowFrom, col = colFrom; items.push(this.getItem(row, col)); do { row += shiftY; col += shiftX; items.push(this.getItem(row, col)); } while( row !== rowTo || col !== colTo ); } return items; } /** * Returns matrix item * @param {Number} row * @param {Number} col * @return {*} */ WordSearch.prototype.getItem = function(row, col) { return (this.matrix[row] ? this.matrix[row][col] : undefined); } /** * Clear the exist highlights */ WordSearch.prototype.clearHighlight = function() { var selectedEls = document.querySelectorAll('.ws-selected'); for (var i = 0; i < selectedEls.length; i++) { selectedEls[i].classList.remove('ws-selected'); } } /** * Lookup if the wordlist contains the selected * @param {Array} selected */ WordSearch.prototype.lookup = function(selected) { var words = ['']; for (var i = 0; i < selected.length; i++) { words[0] += selected[i].letter; } words.push(words[0].split('').reverse().join('')); if (this.settings.words.indexOf(words[0]) > -1 || this.settings.words.indexOf(words[1]) > -1) { for (var i = 0; i < selected.length; i++) { var row = selected[i].row + 1, col = selected[i].col + 1, el = document.querySelector('.ws-area .ws-row:nth-child(' + row + ') .ws-col:nth-child(' + col + ')'); el.classList.add('ws-found'); } //Cross word off list. var wordList = document.querySelector(".ws-words"); var wordListItems = wordList.getElementsByTagName("li"); for(var i=0; i") { //Check the word is never found wordListItems[i].innerHTML = ""+wordListItems[i].innerHTML+""; //Increment solved words. this.solved++; } } } //Game over? if(this.solved == this.settings.words.length){ this.gameOver(); } } } /** * Game Over */ WordSearch.prototype.gameOver = function() { //Create overlay. var overlay = document.createElement("div"); overlay.setAttribute("id", "ws-game-over-outer"); overlay.setAttribute("class", "ws-game-over-outer"); this.wrapEl.parentNode.appendChild(overlay); //Create overlay content. var overlay = document.getElementById("ws-game-over-outer"); overlay.innerHTML = "
"+ "
"+ "

Congratulations!

"+ "

You've found all of the words!

"+ "
"+ "
"; } /** * Mouse event - Mouse down * @param {Object} item */ WordSearch.prototype.onMousedown = function(item) { var _this = this; return function() { _this.selectFrom = item; } } /** * Mouse event - Mouse move * @param {Object} */ WordSearch.prototype.onMouseover = function(item) { var _this = this; return function() { if (_this.selectFrom) { _this.selected = _this.getItems(_this.selectFrom.row, _this.selectFrom.col, item.row, item.col); _this.clearHighlight(); for (var i = 0; i < _this.selected.length; i ++) { var current = _this.selected[i], row = current.row + 1, col = current.col + 1, el = document.querySelector('.ws-area .ws-row:nth-child(' + row + ') .ws-col:nth-child(' + col + ')'); el.className += ' ws-selected'; } } } } /** * Mouse event - Mouse up */ WordSearch.prototype.onMouseup = function() { var _this = this; return function() { _this.selectFrom = null; _this.clearHighlight(); _this.lookup(_this.selected); _this.selected = []; } } })(); //-----------------------------Remove accent for latin/hebrew letters---------------------------------------------------// var defaultDiacriticsRemovalMap = [{ 'base': "A", 'letters': /(A|Ⓐ|A|À|Á|Â|Ầ|Ấ|Ẫ|Ẩ|Ã|Ā|Ă|Ằ|Ắ|Ẵ|Ẳ|Ȧ|Ǡ|Ä|Ǟ|Ả|Å|Ǻ|Ǎ|Ȁ|Ȃ|Ạ|Ậ|Ặ|Ḁ|Ą|Ⱥ|Ɐ|[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F])/g }, { 'base': "AA", 'letters': /(Ꜳ|[\uA732])/g }, { 'base': "AE", 'letters': /(Æ|Ǽ|Ǣ|[\u00C6\u01FC\u01E2])/g }, { 'base': "AO", 'letters': /(Ꜵ|[\uA734])/g }, { 'base': "AU", 'letters': /(Ꜷ|[\uA736])/g }, { 'base': "AV", 'letters': /(Ꜹ|Ꜻ|[\uA738\uA73A])/g }, { 'base': "AY", 'letters': /(Ꜽ|[\uA73C])/g }, { 'base': "B", 'letters': /(B|Ⓑ|B|Ḃ|Ḅ|Ḇ|Ƀ|Ƃ|Ɓ|[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181])/g }, { 'base': "C", 'letters': /(C|Ⓒ|C|Ć|Ĉ|Ċ|Č|Ç|Ḉ|Ƈ|Ȼ|Ꜿ|[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E])/g }, { 'base': "D", 'letters': /(D|Ⓓ|D|Ḋ|Ď|Ḍ|Ḑ|Ḓ|Ḏ|Đ|Ƌ|Ɗ|Ɖ|Ꝺ|Ð|[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0])/g }, { 'base': "DZ", 'letters': /(DZ|DŽ|[\u01F1\u01C4])/g }, { 'base': "Dz", 'letters': /(Dz|Dž|[\u01F2\u01C5])/g }, { 'base': "E", 'letters': /(E|Ⓔ|E|È|É|Ê|Ề|Ế|Ễ|Ể|Ẽ|Ē|Ḕ|Ḗ|Ĕ|Ė|Ë|Ẻ|Ě|Ȅ|Ȇ|Ẹ|Ệ|Ȩ|Ḝ|Ę|Ḙ|Ḛ|Ɛ|Ǝ|[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E])/g }, { 'base': "F", 'letters': /(F|Ⓕ|F|Ḟ|Ƒ|Ꝼ|[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B])/g }, { 'base': "G", 'letters': /(G|Ⓖ|G|Ǵ|Ĝ|Ḡ|Ğ|Ġ|Ǧ|Ģ|Ǥ|Ɠ|Ꞡ|Ᵹ|Ꝿ|[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E])/g }, { 'base': "H", 'letters': /(H|Ⓗ|H|Ĥ|Ḣ|Ḧ|Ȟ|Ḥ|Ḩ|Ḫ|Ħ|Ⱨ|Ⱶ|Ɥ|[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D])/g }, { 'base': "I", 'letters': /(I|Ⓘ|I|Ì|Í|Î|Ĩ|Ī|Ĭ|İ|Ï|Ḯ|Ỉ|Ǐ|Ȉ|Ȋ|Ị|Į|Ḭ|Ɨ|[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197])/g }, { 'base': "J", 'letters': /(J|Ⓙ|J|Ĵ|Ɉ|[\u004A\u24BF\uFF2A\u0134\u0248])/g }, { 'base': "K", 'letters': /(K|Ⓚ|K|Ḱ|Ǩ|Ḳ|Ķ|Ḵ|Ƙ|Ⱪ|Ꝁ|Ꝃ|Ꝅ|Ꞣ|[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2])/g }, { 'base': "L", 'letters': /(L|Ⓛ|L|Ŀ|Ĺ|Ľ|Ḷ|Ḹ|Ļ|Ḽ|Ḻ|Ł|Ƚ|Ɫ|Ⱡ|Ꝉ|Ꝇ|Ꞁ|[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780])/g }, { 'base': "LJ", 'letters': /(LJ|[\u01C7])/g }, { 'base': "Lj", 'letters': /(Lj|[\u01C8])/g }, { 'base': "M", 'letters': /(M|Ⓜ|M|Ḿ|Ṁ|Ṃ|Ɱ|Ɯ|[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C])/g }, { 'base': "N", 'letters': /(N|Ⓝ|N|Ǹ|Ń|Ñ|Ṅ|Ň|Ṇ|Ņ|Ṋ|Ṉ|Ƞ|Ɲ|Ꞑ|Ꞥ|Ŋ|[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4\u014A])/g }, { 'base': "NJ", 'letters': /(NJ|[\u01CA])/g }, { 'base': "Nj", 'letters': /(Nj|[\u01CB])/g }, { 'base': "O", 'letters': /(O|Ⓞ|O|Ò|Ó|Ô|Ồ|Ố|Ỗ|Ổ|Õ|Ṍ|Ȭ|Ṏ|Ō|Ṑ|Ṓ|Ŏ|Ȯ|Ȱ|Ö|Ȫ|Ỏ|Ő|Ǒ|Ȍ|Ȏ|Ơ|Ờ|Ớ|Ỡ|Ở|Ợ|Ọ|Ộ|Ǫ|Ǭ|Ø|Ǿ|Ɔ|Ɵ|Ꝋ|Ꝍ|[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C])/g }, { 'base': "OE", 'letters': /(Œ|[\u0152])/g }, { 'base': "OI", 'letters': /(Ƣ|[\u01A2])/g }, { 'base': "OO", 'letters': /(Ꝏ|[\uA74E])/g }, { 'base': "OU", 'letters': /(Ȣ|[\u0222])/g }, { 'base': "P", 'letters': /(P|Ⓟ|P|Ṕ|Ṗ|Ƥ|Ᵽ|Ꝑ|Ꝓ|Ꝕ|[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754])/g }, { 'base': "Q", 'letters': /(Q|Ⓠ|Q|Ꝗ|Ꝙ|Ɋ|[\u0051\u24C6\uFF31\uA756\uA758\u024A])/g }, { 'base': "R", 'letters': /(R|Ⓡ|R|Ŕ|Ṙ|Ř|Ȑ|Ȓ|Ṛ|Ṝ|Ŗ|Ṟ|Ɍ|Ɽ|Ꝛ|Ꞧ|Ꞃ|[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782])/g }, { 'base': "S", 'letters': /(S|Ⓢ|S|ẞ|Ś|Ṥ|Ŝ|Ṡ|Š|Ṧ|Ṣ|Ṩ|Ș|Ş|Ȿ|Ꞩ|Ꞅ|[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784])/g }, { 'base': "T", 'letters': /(T|Ⓣ|T|Ṫ|Ť|Ṭ|Ț|Ţ|Ṱ|Ṯ|Ŧ|Ƭ|Ʈ|Ⱦ|Ꞇ|[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786])/g }, { 'base': "TH", 'letters': /(Þ|[\u00DE])/g }, { 'base': "TZ", 'letters': /(Ꜩ|[\uA728])/g }, { 'base': "U", 'letters': /(U|Ⓤ|U|Ù|Ú|Û|Ũ|Ṹ|Ū|Ṻ|Ŭ|Ü|Ǜ|Ǘ|Ǖ|Ǚ|Ủ|Ů|Ű|Ǔ|Ȕ|Ȗ|Ư|Ừ|Ứ|Ữ|Ử|Ự|Ụ|Ṳ|Ų|Ṷ|Ṵ|Ʉ|[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244])/g }, { 'base': "V", 'letters': /(V|Ⓥ|V|Ṽ|Ṿ|Ʋ|Ꝟ|Ʌ|[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245])/g }, { 'base': "VY", 'letters': /(Ꝡ|[\uA760])/g }, { 'base': "W", 'letters': /(W|Ⓦ|W|Ẁ|Ẃ|Ŵ|Ẇ|Ẅ|Ẉ|Ⱳ|[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72])/g }, { 'base': "X", 'letters': /(X|Ⓧ|X|Ẋ|Ẍ|[\u0058\u24CD\uFF38\u1E8A\u1E8C])/g }, { 'base': "Y", 'letters': /(Y|Ⓨ|Y|Ỳ|Ý|Ŷ|Ỹ|Ȳ|Ẏ|Ÿ|Ỷ|Ỵ|Ƴ|Ɏ|Ỿ|[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE])/g }, { 'base': "Z", 'letters': /(Z|Ⓩ|Z|Ź|Ẑ|Ż|Ž|Ẓ|Ẕ|Ƶ|Ȥ|Ɀ|Ⱬ|Ꝣ|[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762])/g }, { 'base': "", //Niqqud in Hebrew 'letters': /[\u0591-\u05C7]/g }] function removeDiacritics(str) { for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) { str = str.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base); } return str; } //------------------------------Search language--------------------------------------------------// // Determine what letters injected on grid function searchLanguage(firstLetter) { codefirstLetter = firstLetter.charCodeAt(); console.log(codefirstLetter); var codeLetter = [65,90]; if((codefirstLetter>=65) && (codefirstLetter<=90)) { // Latin return codeLetter = [65,90]; } if((codefirstLetter>=1488) && (codefirstLetter<=1514)) { //Hebrew א -> ת return codeLetter = [1488,1514]; } if((codefirstLetter>=913) && (codefirstLetter<=937)) { //Greek Α -> Ω return codeLetter = [913,929]; //930 is blank } //----------No certain-----------------------------------------------------// if((codefirstLetter>=1040) && (codefirstLetter<=1071)) { //Cyrillic А -> Я return codeLetter = [1040,1071]; //930 is blank } if((codefirstLetter>=1569) && (codefirstLetter<=1610)) { //Arab return codeLetter = [1569,1594]; //Between 1595 and 1600, no letter } if((codefirstLetter>=19969) && (codefirstLetter<=40891)) { //Chinese return codeLetter = [19969,40891]; } if((codefirstLetter>=12354) && (codefirstLetter<=12436)) { //Japan Hiragana return codeLetter = [12388,12418]; //Only no small letter } console.log("Letter not detected : "+firstLetter+":"+codefirstLetter); return codeLetter; }