├── icon-192.png ├── manifest.json ├── README.md ├── LICENSE ├── sw.js ├── index.html ├── game.js └── twemoji.js /icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muan/emoji-minesweeper/HEAD/icon-192.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Minesweeper", 3 | "name": "Emoji Minesweeper", 4 | "icons": [ 5 | { 6 | "src": "/icon-192.png", 7 | "type": "image/png", 8 | "sizes": "192x192" 9 | } 10 | ], 11 | "start_url": "/emoji-minesweeper/", 12 | "background_color": "#ffffff", 13 | "display": "standalone", 14 | "scope": "/emoji-minesweeper/" 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # emoji-minesweeper [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 2 | 3 | ### How to play 4 | 5 | - Left click to step on a spot 6 | - Right click to mark a spot as a bomb 7 | - Double click to open all 8 spots nearby a target (except ones already marked as bombs using right clicks) 8 | 9 | **This is a work in progress** 10 | 11 | ![emoji minesweeper game play demo](https://cloud.githubusercontent.com/assets/1153134/7797311/19c09214-031d-11e5-99c3-2a380ac7984e.gif) 12 | 13 | ### API 14 | 15 | ```javascript 16 | // to start a new game 17 | new Game(cols, rows, bombs, [emptyemoji, bombemoji, flagemoji, starteremoji], twemojiOrNot) 18 | 19 | // for example: 20 | new Game(10, 10, 10, ["🌱", "💥", "🚩", "◻️"], true) 21 | new Game(16, 16, 30, ["🐱", "📛", "💣", "🔍"], false) 22 | ``` 23 | 24 | ### Todos 25 | 26 | - Mobile! 27 | 28 | ### Zap :zap: 29 | 30 | :heart: https://github.com/twitter/twemoji 31 | 32 | ### Why 33 | 34 | [WHY IS THIS A QUESTION?!](https://twitter.com/muanchiou/status/601633821012856832) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mu-An Chiou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | var APP_PREFIX = 'EmojiMinesweeper' 2 | var VERSION = 'v0.0.2' 3 | var CACHE_NAME = APP_PREFIX + VERSION 4 | var URLS = [ 5 | '/emoji-minesweeper/', 6 | '/emoji-minesweeper/index.html', 7 | '/emoji-minesweeper/game.js', 8 | '/emoji-minesweeper/twemoji.js' 9 | 10 | ] 11 | 12 | // Respond with cached resources 13 | self.addEventListener('fetch', function (e) { 14 | console.log('fetch request : ' + e.request.url) 15 | e.respondWith( 16 | caches.match(e.request).then(function (request) { 17 | if (request) { // if cache is available, respond with cache 18 | console.log('responding with cache : ' + e.request.url) 19 | return request 20 | } else { // if there are no cache, try fetching request 21 | console.log('file is not cached, fetching : ' + e.request.url) 22 | return fetch(e.request) 23 | } 24 | }) 25 | ) 26 | }) 27 | 28 | // Cache resources 29 | self.addEventListener('install', function (e) { 30 | e.waitUntil( 31 | caches.open(CACHE_NAME).then(function (cache) { 32 | console.log('installing cache : ' + CACHE_NAME) 33 | return cache.addAll(URLS) 34 | }) 35 | ) 36 | }) 37 | 38 | // Delete outdated caches 39 | self.addEventListener('activate', function (e) { 40 | e.waitUntil( 41 | caches.keys().then(function (keyList) { 42 | var cacheWhitelist = keyList.filter(function (key) { 43 | return key.indexOf(APP_PREFIX) 44 | }) 45 | cacheWhitelist.push(CACHE_NAME) 46 | 47 | return Promise.all(keyList.map(function (key, i) { 48 | if (cacheWhitelist.indexOf(key) === -1) { 49 | console.log('deleting cache : ' + keyList[i] ) 50 | return caches.delete(keyList[i]) 51 | } 52 | })) 53 | }) 54 | ) 55 | }) 56 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 💥 5 | 6 | 7 | 8 | 9 | 10 | 267 | 268 | 269 |
270 | 271 |
272 |
273 | 274 | 275 |
276 |
277 | 278 |
×
279 |
280 |
281 |
💣
282 | 283 | 290 | 291 |
292 | 296 |
297 |
298 |
299 |
0
BOMBS
300 |
MOVES
301 |
TIME
302 |
303 |
304 | [Code] 305 |
306 | 307 | 310 | 311 | 312 | 313 | 314 | 315 | 340 | 349 | 350 | 351 | 352 | -------------------------------------------------------------------------------- /game.js: -------------------------------------------------------------------------------- 1 | /* global twemoji, alert, MouseEvent, game */ 2 | const numbers = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣'] 3 | const iDevise = navigator.platform.match(/^iP/) 4 | const feedback = document.querySelector('.feedback') 5 | 6 | var Game = function (cols, rows, number_of_bombs, set, usetwemoji) { 7 | this.number_of_cells = cols * rows 8 | this.map = document.getElementById('map') 9 | this.cols = Number(cols) 10 | this.rows = Number(rows) 11 | this.number_of_bombs = Number(number_of_bombs) 12 | this.rate = number_of_bombs / this.number_of_cells 13 | 14 | this.emojiset = set 15 | this.numbermoji = [this.emojiset[0]].concat(numbers) 16 | this.usetwemoji = usetwemoji || false 17 | 18 | this.init() 19 | } 20 | 21 | Game.prototype.init = function () { 22 | this.prepareEmoji() 23 | 24 | if (this.number_of_cells > 2500) { alert('too big, go away, have less than 2500 cells'); return false } 25 | if (this.number_of_cells <= this.number_of_bombs) { alert('more bombs than cells, can\'t do it'); return false } 26 | var that = this 27 | this.moveIt(true) 28 | this.map.innerHTML = '' 29 | var grid_data = this.bomb_array() 30 | 31 | function getIndex (x, y) { 32 | if (x > that.cols || x <= 0) return -1 33 | if (y > that.cols || y <= 0) return -1 34 | return that.cols * (y - 1 ) + x - 1 35 | } 36 | 37 | var row = document.createElement('div') 38 | row.setAttribute('role', 'row') 39 | grid_data.forEach(function (isBomb, i) { 40 | var cell = document.createElement('span') 41 | cell.setAttribute('role', 'gridcell') 42 | var mine = that.mine(isBomb) 43 | var x = Math.floor((i + 1) % that.cols) || that.cols 44 | var y = Math.ceil((i + 1) / that.cols) 45 | var neighbors_cords = [[x, y - 1], [x, y + 1], [x - 1, y - 1], [x - 1, y], [x - 1, y + 1], [x + 1, y - 1], [x + 1, y], [x + 1, y + 1]] 46 | if(!isBomb) { 47 | var neighbors = neighbors_cords.map(function (xy) { return grid_data[getIndex(xy[0], xy[1])] }) 48 | mine.mine_count = neighbors.filter(function (neighbor_bomb) { return neighbor_bomb }).length 49 | } 50 | mine.classList.add('x' + x, 'y' + y) 51 | mine.neighbors = neighbors_cords.map(function (xy) { return `.x${xy[0]}.y${xy[1]}` }) 52 | 53 | cell.appendChild(mine) 54 | row.appendChild(cell) 55 | if (x === that.cols) { 56 | that.map.appendChild(row) 57 | row = document.createElement('div') 58 | row.setAttribute('role', 'row') 59 | } 60 | }) 61 | 62 | this.resetMetadata() 63 | this.bindEvents() 64 | this.updateBombsLeft() 65 | } 66 | 67 | Game.prototype.bindEvents = function () { 68 | var that = this 69 | var cells = document.getElementsByClassName('cell') 70 | 71 | Array.prototype.forEach.call(cells, function (target) { 72 | // clicking on a cell and revealing cell 73 | target.addEventListener('click', function (evt) { 74 | if (!target.isMasked || target.isFlagged) return 75 | if (document.getElementsByClassName('unmasked').length === 0) { 76 | that.startTimer() 77 | 78 | if (target.isBomb) { 79 | that.restart(that.usetwemoji) 80 | var targetClasses = target.className.replace('unmasked', '') 81 | document.getElementsByClassName(targetClasses)[0].click() 82 | return 83 | } 84 | } 85 | if (evt.view) that.moveIt() 86 | 87 | target.reveal() 88 | that.updateFeedback(target.getAttribute('aria-label')) 89 | 90 | if (target.mine_count === 0 && !target.isBomb) { 91 | that.revealNeighbors(target) 92 | } 93 | that.game() 94 | }) 95 | 96 | // double clicking on a cell and opening the cell and all 8 of its neighbors 97 | target.addEventListener('dblclick', function () { 98 | if (target.isFlagged) return 99 | that.moveIt() 100 | 101 | target.reveal() 102 | that.revealNeighbors(target) 103 | that.game() 104 | }) 105 | 106 | // marking a cell as a potential bomb 107 | target.addEventListener('contextmenu', function (evt) { 108 | var emoji 109 | evt.preventDefault() 110 | if (!target.isMasked) { return } 111 | if (target.isFlagged) { 112 | target.setAttribute('aria-label','Field') 113 | that.updateFeedback('Unflagged as potential bomb') 114 | emoji = that.emojiset[3].cloneNode() 115 | target.isFlagged = false 116 | } else { 117 | target.setAttribute('aria-label', 'Flagged as potential bomb') 118 | that.updateFeedback('Flagged as potential bomb') 119 | emoji = that.emojiset[2].cloneNode() 120 | target.isFlagged = true 121 | } 122 | target.childNodes[0].remove() 123 | target.appendChild(emoji) 124 | that.updateBombsLeft() 125 | }) 126 | 127 | // support to HOLD to mark bomb, works in Android by default 128 | if (iDevise) { 129 | target.addEventListener('touchstart', function (evt) { 130 | that.holding = setTimeout(function () { 131 | target.dispatchEvent(new Event('contextmenu')) 132 | }, 500) 133 | }) 134 | 135 | target.addEventListener('touchend', function (evt) { 136 | clearTimeout(that.holding) 137 | }) 138 | } 139 | }) 140 | 141 | window.addEventListener('keydown', function (evt) { 142 | if (evt.key == 'r' || evt.which == 'R'.charCodeAt()) { 143 | that.restart(that.usetwemoji) 144 | } 145 | }) 146 | } 147 | 148 | Game.prototype.game = function () { 149 | if (this.result) return 150 | var cells = document.getElementsByClassName('cell') 151 | var masked = Array.prototype.filter.call(cells, function (cell) { 152 | return cell.isMasked 153 | }) 154 | var bombs = Array.prototype.filter.call(cells, function (cell) { 155 | return cell.isBomb && !cell.isMasked 156 | }) 157 | 158 | if (bombs.length > 0) { 159 | Array.prototype.forEach.call(masked, function (cell) { cell.reveal() }) 160 | this.result = 'lost' 161 | this.showMessage() 162 | } else if (masked.length === this.number_of_bombs) { 163 | Array.prototype.forEach.call(masked, function (cell) { cell.reveal(true) }) 164 | this.result = 'won' 165 | this.showMessage() 166 | } 167 | } 168 | 169 | Game.prototype.restart = function (usetwemoji) { 170 | clearInterval(this.timer) 171 | this.result = false 172 | this.timer = false 173 | this.usetwemoji = usetwemoji 174 | this.init() 175 | } 176 | 177 | Game.prototype.resetMetadata = function () { 178 | document.getElementById('timer').textContent = '0.00' 179 | document.querySelector('.wrapper').classList.remove('won', 'lost') 180 | document.querySelector('.result-emoji').textContent = '' 181 | document.querySelector('.default-emoji').innerHTML = this.usetwemoji ? twemoji.parse('😀') : '😀' 182 | document.querySelector('.js-settings').innerHTML = this.usetwemoji ? twemoji.parse('🔧') : '🔧' 183 | } 184 | 185 | Game.prototype.startTimer = function () { 186 | if (this.timer) return 187 | this.startTime = new Date() 188 | this.timer = setInterval(function () { 189 | document.getElementById('timer').textContent = ((new Date() - game.startTime) / 1000).toFixed(2) 190 | }, 100) 191 | } 192 | 193 | Game.prototype.mine = function (bomb) { 194 | var that = this 195 | var base = document.createElement('button') 196 | base.type = 'button' 197 | base.setAttribute('aria-label', 'Field') 198 | base.className = 'cell' 199 | base.appendChild(this.emojiset[3].cloneNode()) 200 | base.isMasked = true 201 | if (bomb) base.isBomb = true 202 | base.reveal = function (won) { 203 | var emoji = base.isBomb ? (won ? that.emojiset[2] : that.emojiset[1]) : that.numbermoji[base.mine_count] 204 | var text = base.isBomb ? (won ? "Bomb discovered" : "Boom!") : (base.mine_count === 0 ? "Empty field" : base.mine_count + " bombs nearby") 205 | this.childNodes[0].remove() 206 | this.setAttribute('aria-label', text) 207 | this.appendChild(emoji.cloneNode()) 208 | this.isMasked = false 209 | this.classList.add('unmasked') 210 | } 211 | return base 212 | } 213 | 214 | Game.prototype.revealNeighbors = function (mine) { 215 | var neighbors = document.querySelectorAll(mine.neighbors) 216 | for(var i = 0; i < neighbors.length; i++) { 217 | if (neighbors[i].isMasked && !neighbors[i].isFlagged) { 218 | neighbors[i].reveal() 219 | 220 | if (neighbors[i].mine_count === 0 && !neighbors[i].isBomb) { 221 | this.revealNeighbors(neighbors[i]) 222 | } 223 | } 224 | } 225 | } 226 | 227 | Game.prototype.prepareEmoji = function () { 228 | var that = this 229 | function makeEmojiElement (emoji) { 230 | var ele 231 | if(that.usetwemoji) { 232 | if (emoji.src) { 233 | ele = emoji 234 | } else { 235 | ele = document.createElement('img') 236 | ele.className = 'emoji' 237 | ele.setAttribute('aria-hidden', 'true') 238 | ele.src = twemoji.parse(emoji).match(/src=\"(.+)\">/)[1] 239 | } 240 | } else { 241 | ele = document.createTextNode(emoji.alt || emoji.data || emoji) 242 | } 243 | return ele 244 | } 245 | 246 | this.emojiset = this.emojiset.map(makeEmojiElement) 247 | this.numbermoji = this.numbermoji.map(makeEmojiElement) 248 | } 249 | 250 | Game.prototype.bomb_array = function () { 251 | var chance = Math.floor(this.rate * this.number_of_cells) 252 | var arr = [] 253 | for (var i = 0; i < chance; i++) { 254 | arr.push(true) 255 | } 256 | for (var n = 0; n < (this.number_of_cells - chance); n++) { 257 | arr.push(false) 258 | } 259 | return this.shuffle(arr) 260 | } 261 | 262 | Game.prototype.shuffle = function (array) { 263 | var currentIndex = array.length, temporaryValue, randomIndex 264 | while (currentIndex !== 0) { 265 | randomIndex = Math.floor(Math.random() * currentIndex) 266 | currentIndex -= 1 267 | temporaryValue = array[currentIndex] 268 | array[currentIndex] = array[randomIndex] 269 | array[randomIndex] = temporaryValue 270 | } 271 | return array 272 | } 273 | 274 | Game.prototype.moveIt = function (zero) { 275 | zero ? this.moves = 0 : this.moves++ 276 | document.getElementById('moves').textContent = this.moves 277 | } 278 | 279 | Game.prototype.updateBombsLeft = function () { 280 | var flagged = Array.prototype.filter.call(document.getElementsByClassName('cell'), function (target) { return target.isFlagged }) 281 | document.getElementById('bombs-left').textContent = `${this.number_of_bombs - flagged.length}/${this.number_of_bombs}` 282 | } 283 | 284 | Game.prototype.updateFeedback = function (text) { 285 | feedback.textContent = text 286 | // Toggle period to force voiceover to read out the same content 287 | if (this.feedbackToggle) feedback.textContent += "." 288 | this.feedbackToggle = !this.feedbackToggle 289 | } 290 | 291 | Game.prototype.showMessage = function () { 292 | clearInterval(this.timer) 293 | var seconds = ((new Date() - this.startTime) / 1000).toFixed(2) 294 | var winner = this.result === 'won' 295 | var emoji = winner ? '😎' : '😵' 296 | this.updateFeedback(winner ? "Yay, you won!" : "Boom! you lost.") 297 | document.querySelector('.wrapper').classList.add(this.result) 298 | document.getElementById('timer').textContent = seconds 299 | document.getElementById('result').innerHTML = this.usetwemoji ? twemoji.parse(emoji) : emoji 300 | } 301 | 302 | // console documentation 303 | 304 | console.log('Use: `new Game(cols, rows, bombs, [emptyemoji, bombemoji, flagemoji, starteremoji], twemojiOrNot)` to start a new game with customizations.') 305 | console.log(' Eg: `game = new Game(10, 10, 10, ["🌱", "💥", "🚩", "◻️"], false)`') 306 | console.log(' Or: `game = new Game(16, 16, 30, ["🐣", "💣", "🚧", "◻️"], true)`') 307 | -------------------------------------------------------------------------------- /twemoji.js: -------------------------------------------------------------------------------- 1 | /*! Copyright Twitter Inc. and other contributors. Licensed under MIT */ 2 | var twemoji=function(){"use strict";var twemoji={base:(location.protocol==="https:"?"https:":"http:")+"//twemoji.maxcdn.com/",ext:".png",size:"36x36",className:"emoji",convert:{fromCodePoint:fromCodePoint,toCodePoint:toCodePoint},onerror:function onerror(){if(this.parentNode){this.parentNode.replaceChild(createText(this.alt),this)}},parse:parse,replace:replace,test:test},escaper={"&":"&","<":"<",">":">","'":"'",'"':"""},re=/((?:\ud83c\udde8\ud83c\uddf3|\ud83c\uddfa\ud83c\uddf8|\ud83c\uddf7\ud83c\uddfa|\ud83c\uddf0\ud83c\uddf7|\ud83c\uddef\ud83c\uddf5|\ud83c\uddee\ud83c\uddf9|\ud83c\uddec\ud83c\udde7|\ud83c\uddeb\ud83c\uddf7|\ud83c\uddea\ud83c\uddf8|\ud83c\udde9\ud83c\uddea|\u0039\ufe0f?\u20e3|\u0038\ufe0f?\u20e3|\u0037\ufe0f?\u20e3|\u0036\ufe0f?\u20e3|\u0035\ufe0f?\u20e3|\u0034\ufe0f?\u20e3|\u0033\ufe0f?\u20e3|\u0032\ufe0f?\u20e3|\u0031\ufe0f?\u20e3|\u0030\ufe0f?\u20e3|\u0023\ufe0f?\u20e3|\ud83d\udeb3|\ud83d\udeb1|\ud83d\udeb0|\ud83d\udeaf|\ud83d\udeae|\ud83d\udea6|\ud83d\udea3|\ud83d\udea1|\ud83d\udea0|\ud83d\ude9f|\ud83d\ude9e|\ud83d\ude9d|\ud83d\ude9c|\ud83d\ude9b|\ud83d\ude98|\ud83d\ude96|\ud83d\ude94|\ud83d\ude90|\ud83d\ude8e|\ud83d\ude8d|\ud83d\ude8b|\ud83d\ude8a|\ud83d\ude88|\ud83d\ude86|\ud83d\ude82|\ud83d\ude81|\ud83d\ude36|\ud83d\ude34|\ud83d\ude2f|\ud83d\ude2e|\ud83d\ude2c|\ud83d\ude27|\ud83d\ude26|\ud83d\ude1f|\ud83d\ude1b|\ud83d\ude19|\ud83d\ude17|\ud83d\ude15|\ud83d\ude11|\ud83d\ude10|\ud83d\ude0e|\ud83d\ude08|\ud83d\ude07|\ud83d\ude00|\ud83d\udd67|\ud83d\udd66|\ud83d\udd65|\ud83d\udd64|\ud83d\udd63|\ud83d\udd62|\ud83d\udd61|\ud83d\udd60|\ud83d\udd5f|\ud83d\udd5e|\ud83d\udd5d|\ud83d\udd5c|\ud83d\udd2d|\ud83d\udd2c|\ud83d\udd15|\ud83d\udd09|\ud83d\udd08|\ud83d\udd07|\ud83d\udd06|\ud83d\udd05|\ud83d\udd04|\ud83d\udd02|\ud83d\udd01|\ud83d\udd00|\ud83d\udcf5|\ud83d\udcef|\ud83d\udced|\ud83d\udcec|\ud83d\udcb7|\ud83d\udcb6|\ud83d\udcad|\ud83d\udc6d|\ud83d\udc6c|\ud83d\udc65|\ud83d\udc2a|\ud83d\udc16|\ud83d\udc15|\ud83d\udc13|\ud83d\udc10|\ud83d\udc0f|\ud83d\udc0b|\ud83d\udc0a|\ud83d\udc09|\ud83d\udc08|\ud83d\udc07|\ud83d\udc06|\ud83d\udc05|\ud83d\udc04|\ud83d\udc03|\ud83d\udc02|\ud83d\udc01|\ud83d\udc00|\ud83c\udfe4|\ud83c\udfc9|\ud83c\udfc7|\ud83c\udf7c|\ud83c\udf50|\ud83c\udf4b|\ud83c\udf33|\ud83c\udf32|\ud83c\udf1e|\ud83c\udf1d|\ud83c\udf1c|\ud83c\udf1a|\ud83c\udf18|\ud83c\udccf|\ud83c\udd8e|\ud83c\udd91|\ud83c\udd92|\ud83c\udd93|\ud83c\udd94|\ud83c\udd95|\ud83c\udd96|\ud83c\udd97|\ud83c\udd98|\ud83c\udd99|\ud83c\udd9a|\ud83d\udc77|\ud83d\udec5|\ud83d\udec4|\ud83d\udec3|\ud83d\udec2|\ud83d\udec1|\ud83d\udebf|\ud83d\udeb8|\ud83d\udeb7|\ud83d\udeb5|\ud83c\ude01|\ud83c\ude32|\ud83c\ude33|\ud83c\ude34|\ud83c\ude35|\ud83c\ude36|\ud83c\ude38|\ud83c\ude39|\ud83c\ude3a|\ud83c\ude50|\ud83c\ude51|\ud83c\udf00|\ud83c\udf01|\ud83c\udf02|\ud83c\udf03|\ud83c\udf04|\ud83c\udf05|\ud83c\udf06|\ud83c\udf07|\ud83c\udf08|\ud83c\udf09|\ud83c\udf0a|\ud83c\udf0b|\ud83c\udf0c|\ud83c\udf0f|\ud83c\udf11|\ud83c\udf13|\ud83c\udf14|\ud83c\udf15|\ud83c\udf19|\ud83c\udf1b|\ud83c\udf1f|\ud83c\udf20|\ud83c\udf30|\ud83c\udf31|\ud83c\udf34|\ud83c\udf35|\ud83c\udf37|\ud83c\udf38|\ud83c\udf39|\ud83c\udf3a|\ud83c\udf3b|\ud83c\udf3c|\ud83c\udf3d|\ud83c\udf3e|\ud83c\udf3f|\ud83c\udf40|\ud83c\udf41|\ud83c\udf42|\ud83c\udf43|\ud83c\udf44|\ud83c\udf45|\ud83c\udf46|\ud83c\udf47|\ud83c\udf48|\ud83c\udf49|\ud83c\udf4a|\ud83c\udf4c|\ud83c\udf4d|\ud83c\udf4e|\ud83c\udf4f|\ud83c\udf51|\ud83c\udf52|\ud83c\udf53|\ud83c\udf54|\ud83c\udf55|\ud83c\udf56|\ud83c\udf57|\ud83c\udf58|\ud83c\udf59|\ud83c\udf5a|\ud83c\udf5b|\ud83c\udf5c|\ud83c\udf5d|\ud83c\udf5e|\ud83c\udf5f|\ud83c\udf60|\ud83c\udf61|\ud83c\udf62|\ud83c\udf63|\ud83c\udf64|\ud83c\udf65|\ud83c\udf66|\ud83c\udf67|\ud83c\udf68|\ud83c\udf69|\ud83c\udf6a|\ud83c\udf6b|\ud83c\udf6c|\ud83c\udf6d|\ud83c\udf6e|\ud83c\udf6f|\ud83c\udf70|\ud83c\udf71|\ud83c\udf72|\ud83c\udf73|\ud83c\udf74|\ud83c\udf75|\ud83c\udf76|\ud83c\udf77|\ud83c\udf78|\ud83c\udf79|\ud83c\udf7a|\ud83c\udf7b|\ud83c\udf80|\ud83c\udf81|\ud83c\udf82|\ud83c\udf83|\ud83c\udf84|\ud83c\udf85|\ud83c\udf86|\ud83c\udf87|\ud83c\udf88|\ud83c\udf89|\ud83c\udf8a|\ud83c\udf8b|\ud83c\udf8c|\ud83c\udf8d|\ud83c\udf8e|\ud83c\udf8f|\ud83c\udf90|\ud83c\udf91|\ud83c\udf92|\ud83c\udf93|\ud83c\udfa0|\ud83c\udfa1|\ud83c\udfa2|\ud83c\udfa3|\ud83c\udfa4|\ud83c\udfa5|\ud83c\udfa6|\ud83c\udfa7|\ud83c\udfa8|\ud83c\udfa9|\ud83c\udfaa|\ud83c\udfab|\ud83c\udfac|\ud83c\udfad|\ud83c\udfae|\ud83c\udfaf|\ud83c\udfb0|\ud83c\udfb1|\ud83c\udfb2|\ud83c\udfb3|\ud83c\udfb4|\ud83c\udfb5|\ud83c\udfb6|\ud83c\udfb7|\ud83c\udfb8|\ud83c\udfb9|\ud83c\udfba|\ud83c\udfbb|\ud83c\udfbc|\ud83c\udfbd|\ud83c\udfbe|\ud83c\udfbf|\ud83c\udfc0|\ud83c\udfc1|\ud83c\udfc2|\ud83c\udfc3|\ud83c\udfc4|\ud83c\udfc6|\ud83c\udfc8|\ud83c\udfca|\ud83c\udfe0|\ud83c\udfe1|\ud83c\udfe2|\ud83c\udfe3|\ud83c\udfe5|\ud83c\udfe6|\ud83c\udfe7|\ud83c\udfe8|\ud83c\udfe9|\ud83c\udfea|\ud83c\udfeb|\ud83c\udfec|\ud83c\udfed|\ud83c\udfee|\ud83c\udfef|\ud83c\udff0|\ud83d\udc0c|\ud83d\udc0d|\ud83d\udc0e|\ud83d\udc11|\ud83d\udc12|\ud83d\udc14|\ud83d\udc17|\ud83d\udc18|\ud83d\udc19|\ud83d\udc1a|\ud83d\udc1b|\ud83d\udc1c|\ud83d\udc1d|\ud83d\udc1e|\ud83d\udc1f|\ud83d\udc20|\ud83d\udc21|\ud83d\udc22|\ud83d\udc23|\ud83d\udc24|\ud83d\udc25|\ud83d\udc26|\ud83d\udc27|\ud83d\udc28|\ud83d\udc29|\ud83d\udc2b|\ud83d\udc2c|\ud83d\udc2d|\ud83d\udc2e|\ud83d\udc2f|\ud83d\udc30|\ud83d\udc31|\ud83d\udc32|\ud83d\udc33|\ud83d\udc34|\ud83d\udc35|\ud83d\udc36|\ud83d\udc37|\ud83d\udc38|\ud83d\udc39|\ud83d\udc3a|\ud83d\udc3b|\ud83d\udc3c|\ud83d\udc3d|\ud83d\udc3e|\ud83d\udc40|\ud83d\udc42|\ud83d\udc43|\ud83d\udc44|\ud83d\udc45|\ud83d\udc46|\ud83d\udc47|\ud83d\udc48|\ud83d\udc49|\ud83d\udc4a|\ud83d\udc4b|\ud83d\udc4c|\ud83d\udc4d|\ud83d\udc4e|\ud83d\udc4f|\ud83d\udc50|\ud83d\udc51|\ud83d\udc52|\ud83d\udc53|\ud83d\udc54|\ud83d\udc55|\ud83d\udc56|\ud83d\udc57|\ud83d\udc58|\ud83d\udc59|\ud83d\udc5a|\ud83d\udc5b|\ud83d\udc5c|\ud83d\udc5d|\ud83d\udc5e|\ud83d\udc5f|\ud83d\udc60|\ud83d\udc61|\ud83d\udc62|\ud83d\udc63|\ud83d\udc64|\ud83d\udc66|\ud83d\udc67|\ud83d\udc68|\ud83d\udc69|\ud83d\udc6a|\ud83d\udc6b|\ud83d\udc6e|\ud83d\udc6f|\ud83d\udc70|\ud83d\udc71|\ud83d\udc72|\ud83d\udc73|\ud83d\udc74|\ud83d\udc75|\ud83d\udc76|\ud83d\udeb4|\ud83d\udc78|\ud83d\udc79|\ud83d\udc7a|\ud83d\udc7b|\ud83d\udc7c|\ud83d\udc7d|\ud83d\udc7e|\ud83d\udc7f|\ud83d\udc80|\ud83d\udc81|\ud83d\udc82|\ud83d\udc83|\ud83d\udc84|\ud83d\udc85|\ud83d\udc86|\ud83d\udc87|\ud83d\udc88|\ud83d\udc89|\ud83d\udc8a|\ud83d\udc8b|\ud83d\udc8c|\ud83d\udc8d|\ud83d\udc8e|\ud83d\udc8f|\ud83d\udc90|\ud83d\udc91|\ud83d\udc92|\ud83d\udc93|\ud83d\udc94|\ud83d\udc95|\ud83d\udc96|\ud83d\udc97|\ud83d\udc98|\ud83d\udc99|\ud83d\udc9a|\ud83d\udc9b|\ud83d\udc9c|\ud83d\udc9d|\ud83d\udc9e|\ud83d\udc9f|\ud83d\udca0|\ud83d\udca1|\ud83d\udca2|\ud83d\udca3|\ud83d\udca4|\ud83d\udca5|\ud83d\udca6|\ud83d\udca7|\ud83d\udca8|\ud83d\udca9|\ud83d\udcaa|\ud83d\udcab|\ud83d\udcac|\ud83d\udcae|\ud83d\udcaf|\ud83d\udcb0|\ud83d\udcb1|\ud83d\udcb2|\ud83d\udcb3|\ud83d\udcb4|\ud83d\udcb5|\ud83d\udcb8|\ud83d\udcb9|\ud83d\udcba|\ud83d\udcbb|\ud83d\udcbc|\ud83d\udcbd|\ud83d\udcbe|\ud83d\udcbf|\ud83d\udcc0|\ud83d\udcc1|\ud83d\udcc2|\ud83d\udcc3|\ud83d\udcc4|\ud83d\udcc5|\ud83d\udcc6|\ud83d\udcc7|\ud83d\udcc8|\ud83d\udcc9|\ud83d\udcca|\ud83d\udccb|\ud83d\udccc|\ud83d\udccd|\ud83d\udcce|\ud83d\udccf|\ud83d\udcd0|\ud83d\udcd1|\ud83d\udcd2|\ud83d\udcd3|\ud83d\udcd4|\ud83d\udcd5|\ud83d\udcd6|\ud83d\udcd7|\ud83d\udcd8|\ud83d\udcd9|\ud83d\udcda|\ud83d\udcdb|\ud83d\udcdc|\ud83d\udcdd|\ud83d\udcde|\ud83d\udcdf|\ud83d\udce0|\ud83d\udce1|\ud83d\udce2|\ud83d\udce3|\ud83d\udce4|\ud83d\udce5|\ud83d\udce6|\ud83d\udce7|\ud83d\udce8|\ud83d\udce9|\ud83d\udcea|\ud83d\udceb|\ud83d\udcee|\ud83d\udcf0|\ud83d\udcf1|\ud83d\udcf2|\ud83d\udcf3|\ud83d\udcf4|\ud83d\udcf6|\ud83d\udcf7|\ud83d\udcf9|\ud83d\udcfa|\ud83d\udcfb|\ud83d\udcfc|\ud83d\udd03|\ud83d\udd0a|\ud83d\udd0b|\ud83d\udd0c|\ud83d\udd0d|\ud83d\udd0e|\ud83d\udd0f|\ud83d\udd10|\ud83d\udd11|\ud83d\udd12|\ud83d\udd13|\ud83d\udd14|\ud83d\udd16|\ud83d\udd17|\ud83d\udd18|\ud83d\udd19|\ud83d\udd1a|\ud83d\udd1b|\ud83d\udd1c|\ud83d\udd1d|\ud83d\udd1e|\ud83d\udd1f|\ud83d\udd20|\ud83d\udd21|\ud83d\udd22|\ud83d\udd23|\ud83d\udd24|\ud83d\udd25|\ud83d\udd26|\ud83d\udd27|\ud83d\udd28|\ud83d\udd29|\ud83d\udd2a|\ud83d\udd2b|\ud83d\udd2e|\ud83d\udd2f|\ud83d\udd30|\ud83d\udd31|\ud83d\udd32|\ud83d\udd33|\ud83d\udd34|\ud83d\udd35|\ud83d\udd36|\ud83d\udd37|\ud83d\udd38|\ud83d\udd39|\ud83d\udd3a|\ud83d\udd3b|\ud83d\udd3c|\ud83d\udd3d|\ud83d\udd50|\ud83d\udd51|\ud83d\udd52|\ud83d\udd53|\ud83d\udd54|\ud83d\udd55|\ud83d\udd56|\ud83d\udd57|\ud83d\udd58|\ud83d\udd59|\ud83d\udd5a|\ud83d\udd5b|\ud83d\uddfb|\ud83d\uddfc|\ud83d\uddfd|\ud83d\uddfe|\ud83d\uddff|\ud83d\ude01|\ud83d\ude02|\ud83d\ude03|\ud83d\ude04|\ud83d\ude05|\ud83d\ude06|\ud83d\ude09|\ud83d\ude0a|\ud83d\ude0b|\ud83d\ude0c|\ud83d\ude0d|\ud83d\ude0f|\ud83d\ude12|\ud83d\ude13|\ud83d\ude14|\ud83d\ude16|\ud83d\ude18|\ud83d\ude1a|\ud83d\ude1c|\ud83d\ude1d|\ud83d\ude1e|\ud83d\ude20|\ud83d\ude21|\ud83d\ude22|\ud83d\ude23|\ud83d\ude24|\ud83d\ude25|\ud83d\ude28|\ud83d\ude29|\ud83d\ude2a|\ud83d\ude2b|\ud83d\ude2d|\ud83d\ude30|\ud83d\ude31|\ud83d\ude32|\ud83d\ude33|\ud83d\ude35|\ud83d\ude37|\ud83d\ude38|\ud83d\ude39|\ud83d\ude3a|\ud83d\ude3b|\ud83d\ude3c|\ud83d\ude3d|\ud83d\ude3e|\ud83d\ude3f|\ud83d\ude40|\ud83d\ude45|\ud83d\ude46|\ud83d\ude47|\ud83d\ude48|\ud83d\ude49|\ud83d\ude4a|\ud83d\ude4b|\ud83d\ude4c|\ud83d\ude4d|\ud83d\ude4e|\ud83d\ude4f|\ud83d\ude80|\ud83d\ude83|\ud83d\ude84|\ud83d\ude85|\ud83d\ude87|\ud83d\ude89|\ud83d\ude8c|\ud83d\ude8f|\ud83d\ude91|\ud83d\ude92|\ud83d\ude93|\ud83d\ude95|\ud83d\ude97|\ud83d\ude99|\ud83d\ude9a|\ud83d\udea2|\ud83d\udea4|\ud83d\udea5|\ud83d\udea7|\ud83d\udea8|\ud83d\udea9|\ud83d\udeaa|\ud83d\udeab|\ud83d\udeac|\ud83d\udead|\ud83d\udeb2|\ud83d\udeb6|\ud83d\udeb9|\ud83d\udeba|\ud83d\udebb|\ud83d\udebc|\ud83d\udebd|\ud83d\udebe|\ud83d\udec0|\ud83c\udde6|\ud83c\udde7|\ud83c\udde8|\ud83c\udde9|\ud83c\uddea|\ud83c\uddeb|\ud83c\uddec|\ud83c\udded|\ud83c\uddee|\ud83c\uddef|\ud83c\uddf0|\ud83c\uddf1|\ud83c\uddf2|\ud83c\uddf3|\ud83c\uddf4|\ud83c\uddf5|\ud83c\uddf6|\ud83c\uddf7|\ud83c\uddf8|\ud83c\uddf9|\ud83c\uddfa|\ud83c\uddfb|\ud83c\uddfc|\ud83c\uddfd|\ud83c\uddfe|\ud83c\uddff|\ud83c\udf0d|\ud83c\udf0e|\ud83c\udf10|\ud83c\udf12|\ud83c\udf16|\ud83c\udf17|\ue50a|\u27b0|\u2797|\u2796|\u2795|\u2755|\u2754|\u2753|\u274e|\u274c|\u2728|\u270b|\u270a|\u2705|\u26ce|\u23f3|\u23f0|\u23ec|\u23eb|\u23ea|\u23e9|\u27bf|\u00a9|\u00ae)|(?:(?:\ud83c\udc04|\ud83c\udd70|\ud83c\udd71|\ud83c\udd7e|\ud83c\udd7f|\ud83c\ude02|\ud83c\ude1a|\ud83c\ude2f|\ud83c\ude37|\u3299|\u303d|\u3030|\u2b55|\u2b50|\u2b1c|\u2b1b|\u2b07|\u2b06|\u2b05|\u2935|\u2934|\u27a1|\u2764|\u2757|\u2747|\u2744|\u2734|\u2733|\u2716|\u2714|\u2712|\u270f|\u270c|\u2709|\u2708|\u2702|\u26fd|\u26fa|\u26f5|\u26f3|\u26f2|\u26ea|\u26d4|\u26c5|\u26c4|\u26be|\u26bd|\u26ab|\u26aa|\u26a1|\u26a0|\u2693|\u267f|\u267b|\u3297|\u2666|\u2665|\u2663|\u2660|\u2653|\u2652|\u2651|\u2650|\u264f|\u264e|\u264d|\u264c|\u264b|\u264a|\u2649|\u2648|\u263a|\u261d|\u2615|\u2614|\u2611|\u260e|\u2601|\u2600|\u25fe|\u25fd|\u25fc|\u25fb|\u25c0|\u25b6|\u25ab|\u25aa|\u24c2|\u231b|\u231a|\u21aa|\u21a9|\u2199|\u2198|\u2197|\u2196|\u2195|\u2194|\u2139|\u2122|\u2049|\u203c|\u2668)([\uFE0E\uFE0F]?)))/g,rescaper=/[&<>'"]/g,shouldntBeParsed=/IFRAME|NOFRAMES|NOSCRIPT|SCRIPT|SELECT|STYLE|TEXTAREA|[a-z]/,fromCharCode=String.fromCharCode;return twemoji;function createText(text){return document.createTextNode(text)}function escapeHTML(s){return s.replace(rescaper,replacer)}function defaultImageSrcGenerator(icon,options){return"".concat(options.base,options.size,"/",icon,options.ext)}function grabAllTextNodes(node,allText){var childNodes=node.childNodes,length=childNodes.length,subnode,nodeType;while(length--){subnode=childNodes[length];nodeType=subnode.nodeType;if(nodeType===3){allText.push(subnode)}else if(nodeType===1&&!shouldntBeParsed.test(subnode.nodeName)){grabAllTextNodes(subnode,allText)}}return allText}function grabTheRightIcon(icon,variant){return toCodePoint(variant==="️"?icon.slice(0,-1):icon.length===3&&icon.charAt(1)==="️"?icon.charAt(0)+icon.charAt(2):icon)}function parseNode(node,options){var allText=grabAllTextNodes(node,[]),length=allText.length,attrib,attrname,modified,fragment,subnode,text,match,i,index,img,alt,icon,variant,src;while(length--){modified=false;fragment=document.createDocumentFragment();subnode=allText[length];text=subnode.nodeValue;i=0;while(match=re.exec(text)){index=match.index;if(index!==i){fragment.appendChild(createText(text.slice(i,index)))}alt=match[0];icon=match[1];variant=match[2];i=index+alt.length;if(variant!=="︎"){src=options.callback(grabTheRightIcon(icon,variant),options,variant);if(src){img=new Image;img.onerror=options.onerror;img.setAttribute("draggable","false");attrib=options.attributes(icon,variant);for(attrname in attrib){if(attrib.hasOwnProperty(attrname)&&attrname.indexOf("on")!==0&&!img.hasAttribute(attrname)){img.setAttribute(attrname,attrib[attrname])}}img.className=options.className;img.alt=alt;img.src=src;modified=true;fragment.appendChild(img)}}if(!img)fragment.appendChild(createText(alt));img=null}if(modified){if(i")}}return ret})}function replacer(m){return escaper[m]}function returnNull(){return null}function toSizeSquaredAsset(value){return typeof value==="number"?value+"x"+value:value}function fromCodePoint(codepoint){var code=typeof codepoint==="string"?parseInt(codepoint,16):codepoint;if(code<65536){return fromCharCode(code)}code-=65536;return fromCharCode(55296+(code>>10),56320+(code&1023))}function parse(what,how){if(!how||typeof how==="function"){how={callback:how}}return(typeof what==="string"?parseString:parseNode)(what,{callback:how.callback||defaultImageSrcGenerator,attributes:typeof how.attributes==="function"?how.attributes:returnNull,base:typeof how.base==="string"?how.base:twemoji.base,ext:how.ext||twemoji.ext,size:how.folder||toSizeSquaredAsset(how.size||twemoji.size),className:how.className||twemoji.className,onerror:how.onerror||twemoji.onerror})}function replace(text,callback){return String(text).replace(re,callback)}function test(text){re.lastIndex=0;var result=re.test(text);re.lastIndex=0;return result}function toCodePoint(unicodeSurrogates,sep){var r=[],c=0,p=0,i=0;while(i