1/** 2 * Copyright (c) 2006-2015, JGraph Ltd 3 * Copyright (c) 2006-2015, Gaudenz Alder 4 */ 5/** 6 * Voice plugin for draw.io 7 * 8 * Documentation: 9 * 10 * https://www.diagrams.net/doc/faq/voice-plugin 11 * 12 * TODO: Use grammer https://msdn.microsoft.com/en-us/library/ee800145.aspx 13 */ 14Draw.loadPlugin(function(ui) { 15 // Speech recognition never supported without synthesis 16 if (!('speechSynthesis' in window)) 17 { 18 ui.showError('Error', 'Speech output not supported in this browser.', 'OK'); 19 20 return; 21 } 22 else 23 { 24 // Triggers loading of voices 25 speechSynthesis.getVoices(); 26 } 27 28 // Do no use in chromeless mode 29 if (ui.editor.isChromelessView()) 30 { 31 return; 32 } 33 34 // Mic PNG image 35 var outputImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBNDU1RDkxODcxREIxMUU0OTU3Qjg3REYyOTYxQzc0QiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBNDU1RDkxOTcxREIxMUU0OTU3Qjg3REYyOTYxQzc0QiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkE0NTVEOTE2NzFEQjExRTQ5NTdCODdERjI5NjFDNzRCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkE0NTVEOTE3NzFEQjExRTQ5NTdCODdERjI5NjFDNzRCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+QsVUnQAAAX5JREFUeNqck00oRFEUx+eNyYSFUj7KgpfPBUlJWZgFw3IWihpRFpZia4e1hZSlNDsLopSNfBTFQqSwkKmZ5SA2ylfq+Z3pXL15ehlu/fqf9849555777mW4ziBfEZ9rd2InEEsmU4dmP/WHxKEkT1oglaSZOR/0DvRssIVUOQKjEASsxoGoQDmjT/oDUaOdRUzbkASrsMdzMIwSe2cBASXIjtQpyvHYQvzGeLQDkOwDG8w8p2A4GJEJre5Vk5DFBbZ7yG6D+PYL3oW0WwCgh/Re4i4t8PEE2QOxqikDN2AbuwQKr4WU4E4S3wOfw1CWtktFEI5ZDTu5y34DMvPIQl24dTHL9f2CRfQAB/wAFXwlE3gOO990Im95GmcLmQGEpyHTB6AI2xJKL4r7xYmYdX1betpT0kzoT1yhdhyY71aeW4rcyNySJswTVWXWkklcq5N1AETsCAuqkn9+hZ09RXoh1e4hm2CR//7mJqlB8xjCgXyHzXaDzETLONLgAEAxwd5e6Mz+S4AAAAASUVORK5CYII='; 36 var micImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA5UlEQVR4Xr3SMYrCQBTGcSfIQiAgRIS9hOANBCurPUAuIAp7A7FVsFkIbLGYA9gKtoKNYG3jll5AFNKG518YMD6SISD4wY9J4MvkMYwRkZqOMSZkifGFe1b4pnvW3TqK8oMo14twxUgXPRSlDxU7TcUNPqATlG7wCi93cA2Iq2x7l7IJsgofB6UTiEjKklFqsabQSdFA5jqDAzrYQGeNNv5d9yDBEAME6NreFmP8Yuma4A8hFpiLSFNAYYYYn0jwCIUnxMcER4h1whS+7hseXKcu9ifGeQ+qeO8GjN7DPve+Q6+oewPhmE63Qfsb6AAAAABJRU5ErkJggg=='; 37 38 // True if we're on ChromOs 39 var chromeOs = mxClient.IS_CHROMEOS; 40 41 // Maximum length of message to speak 42 var maxMessageLength = 1000; 43 44 // Maximum length of the label before the cell 45 // is called by its shapename 46 var maxLabelLength = 15; 47 48 // Maximum length of output queue. 49 var maxQueueLength = 3; 50 51 // Specifies if speech output is enabled. 52 var speechOutputEnabled = true; 53 54 // Specifies if speech output is enabled. 55 var speechInputEnabled = true; 56 57 // Last message is never repeated 58 var lastMessage = null; 59 60 // Timestamp of last message 61 var lastMessageTimestamp = null; 62 63 // Sets global recognition language 64 var lang = 'en-US'; 65 66 // Caches action names 67 var actions = null; 68 var actionList = null; 69 70 // Caches shape names 71 var shapeList = null; 72 73 // Last inserted cell 74 var lastInserted = null; 75 76 // Current voice 77 var currentVoice = 10; 78 79 // Current recognition thread 80 var recognizing = null; 81 82 // Adds menu 83 mxResources.parse('voiceType=Voice Type'); 84 mxResources.parse('speechOutput=Speech Output'); 85 mxResources.parse('speechListen=Listen'); 86 mxResources.parse('speechInstalled=Start with draw.io'); 87 mxResources.parse('speechListenContinuous=Start/Stop Listen'); 88 mxResources.parse('speechHint=Hint'); 89 mxResources.parse('speechHelp=Help'); 90 mxResources.parse('speechQuit=Quit'); 91 92 // Installs footer click handler 93 function getOrCreateVoiceButton(ui) 94 { 95 if (ui.voiceButton == null) 96 { 97 ui.voiceButton = document.createElement('div'); 98 ui.voiceButton.className = 'geBtn'; 99 ui.voiceButton.style.width = '140px'; 100 ui.voiceButton.style.minWidth = '140px'; 101 ui.voiceButton.style.textOverflow = 'ellipsis'; 102 ui.voiceButton.style.overflowX = 'hidden'; 103 ui.voiceButton.style.fontWeight = 'bold'; 104 ui.voiceButton.style.textAlign = 'center'; 105 ui.voiceButton.style.display = 'inline-block'; 106 ui.voiceButton.style.padding = '0 10px 0 10px'; 107 ui.voiceButton.style.marginTop = '-4px'; 108 ui.voiceButton.style.height = '28px'; 109 ui.voiceButton.style.lineHeight = '28px'; 110 ui.voiceButton.style.color = '#235695'; 111 112 if (ui.buttonContainer.firstChild != null) 113 { 114 ui.buttonContainer.insertBefore(ui.voiceButton, ui.buttonContainer.firstChild); 115 } 116 else 117 { 118 ui.buttonContainer.appendChild(ui.voiceButton); 119 } 120 } 121 122 return ui.voiceButton; 123 }; 124 125 var td = getOrCreateVoiceButton(ui); 126 127 if (td != null) 128 { 129 mxEvent.addGestureListeners(td, function(evt) 130 { 131 ui.editor.graph.popupMenuHandler.hideMenu(); 132 133 if (ui.menubar == null && mxEvent.isPopupTrigger(evt)) 134 { 135 ui.editor.graph.popupMenuHandler.hideMenu(); 136 var menu = new mxPopupMenu(ui.menus.get('voice').funct); 137 menu.div.className += ' geMenubarMenu'; 138 menu.smartSeparators = true; 139 menu.showDisabled = true; 140 menu.autoExpand = true; 141 142 // Disables autoexpand and destroys menu when hidden 143 menu.hideMenu = mxUtils.bind(this, function() 144 { 145 mxPopupMenu.prototype.hideMenu.apply(menu, arguments); 146 menu.destroy(); 147 }); 148 149 var offset = mxUtils.getOffset(td); 150 menu.popup(offset.x, offset.y + td.offsetHeight, null, evt); 151 152 // Allows hiding by clicking on document 153 ui.setCurrentMenu(menu); 154 mxEvent.consume(evt); 155 } 156 }, null, function(evt) 157 { 158 if (!mxEvent.isPopupTrigger(evt)) 159 { 160 if (speechSynthesis.speaking) 161 { 162 speechSynthesis.cancel(); 163 } 164 165 App.listen(true); 166 } 167 168 mxEvent.consume(evt); 169 }); 170 171 mxEvent.disableContextMenu(td); 172 } 173 174 function setPluginInstalled(value) 175 { 176 if (mxSettings != null) 177 { 178 var plugins = mxSettings.getPlugins(); 179 var installed = mxUtils.indexOf(plugins, '/plugins/voice.js') >= 0; 180 181 if (value != installed) 182 { 183 if (installed) 184 { 185 mxUtils.remove('/plugins/voice.js', plugins); 186 } 187 else 188 { 189 plugins.push('/plugins/voice.js'); 190 } 191 192 mxSettings.setPlugins(plugins); 193 mxSettings.save(); 194 } 195 } 196 }; 197 198 // Shows initial status message if only output is enable 199 if (!('webkitSpeechRecognition' in window)) 200 { 201 if (td != null) 202 { 203 td.innerHTML = '<img style="margin-right:4px;" align="absmiddle" border="0" src="' + outputImage + '"/> Ready'; 204 td.style.color = '#235695'; 205 } 206 } 207 208 function updateStatusMessage() 209 { 210 if ('webkitSpeechRecognition' in window) 211 { 212 if (td != null) 213 { 214 if (recognizing != null) 215 { 216 td.innerHTML = '<img style="margin-right:4px;" align="absmiddle" border="0" src="' + micImage + '"/> Listening...'; 217 td.setAttribute('title', 'Click to Stop (' + Editor.ctrlKey + '+O)'); 218 td.style.color = 'darkGray'; 219 } 220 else 221 { 222 td.innerHTML = '<img style="margin-right:4px;" align="absmiddle" border="0" src="' + micImage + '"/> Click to Speak'; 223 td.setAttribute('title', 'Click to Speak (' + Editor.ctrlKey + '+O)'); 224 td.style.color = '#235695'; 225 } 226 } 227 } 228 }; 229 230 updateStatusMessage(); 231 232 var action = ui.actions.addAction('speechOutput', function() 233 { 234 speechOutputEnabled = !speechOutputEnabled; 235 }, null, null, 'Ctrl/AltGr+Shift+Esc'); 236 action.setToggleAction(true); 237 action.setSelectedCallback(function() { return speechOutputEnabled; }); 238 239 var action = ui.actions.addAction('speechInstalled', function() 240 { 241 setPluginInstalled(); 242 }); 243 action.setToggleAction(true); 244 action.setSelectedCallback(function() { return mxUtils.indexOf(mxSettings.getPlugins(), '/plugins/voice.js') >= 0; }); 245 246 ui.actions.addAction('speechListen', function() 247 { 248 App.listen(); 249 }, null, null, 'Ctrl/AltGr+Esc'); 250 251 ui.actions.addAction('speechListenContinuous', function() 252 { 253 App.listen(true); 254 }, null, null, Editor.ctrlKey + '+O'); 255 256 ui.actions.addAction('speechHint', function() 257 { 258 App.sayHint(); 259 }, null, null, 'Shift+Esc'); 260 261 ui.actions.addAction('speechHelp', function() 262 { 263 window.open('https://www.diagrams.net/doc/faq/voice-plugin'); 264 }); 265 266 // Hijacks the settings for storing current voice 267 if (mxSettings != null) 268 { 269 var tmp = mxSettings.settings.voice; 270 271 if (tmp != null) 272 { 273 currentVoice = parseInt(tmp); 274 } 275 } 276 277 ui.menus.put('voiceType', new Menu(mxUtils.bind(this, function(menu, parent) 278 { 279 var voices = speechSynthesis.getVoices(); 280 281 if (voices.length == 0) 282 { 283 menu.addItem('Loading...', null, function() {}, parent, null, false); 284 } 285 else 286 { 287 for (var i = 0; i < voices.length; i++) 288 { 289 (function(index) 290 { 291 var item = menu.addItem(voices[index].name + ' (' + voices[i].lang + ')', null, function() 292 { 293 currentVoice = index; 294 App.say('hello'); 295 296 if (mxSettings != null) 297 { 298 mxSettings.settings.voice = currentVoice; 299 mxSettings.save(); 300 } 301 }, parent); 302 303 if (index == currentVoice) 304 { 305 menu.addCheckmark(item, Editor.checkmarkImage); 306 } 307 })(i); 308 } 309 310 parent.div.style.overflowX = 'hidden'; 311 parent.div.style.overflowY = 'auto'; 312 parent.div.style.maxHeight = '100%'; 313 // Workaround for document scrollbars with 100% max height in Chrome 314 parent.div.style.marginBottom = '-20px'; 315 } 316 }))); 317 318 ui.actions.addAction('speechQuit', function() 319 { 320 // Hides UI 321 speechOutputEnabled = false; 322 td.style.display = 'none'; 323 324 if (menu != null) 325 { 326 menu.style.display = 'none'; 327 } 328 }); 329 330 ui.menus.put('voice', new Menu(function(menu, parent) 331 { 332 ui.menus.addSubmenu('voiceType', menu, parent); 333 ui.menus.addMenuItems(menu, ['-', 'speechOutput', 'speechHint', '-', 'speechListen', 334 'speechListenContinuous', '-', 'speechInstalled', 335 'speechHelp', '-', 'speechQuit']); 336 })); 337 338 if (ui.menubar != null) 339 { 340 var menu = ui.menubar.addMenu('Voice', ui.menus.get('voice').funct); 341 342 // Inserts voice menu before help menu 343 menu.parentNode.insertBefore(menu, menu.previousSibling.previousSibling.previousSibling); 344 } 345 346 function insertShape(shape, done) 347 { 348 var searchTerm = mxUtils.trim(shape); 349 350 ui.sidebar.searchEntries(searchTerm, 1, 0, function(results, len, more) 351 { 352 if (results.length > 0) 353 { 354 var elt = results[0](); 355 356 // Click is blocked, must use mousedown/-up sequence 357 // LATER: Use touchstart or pointerEvents depending on system 358 dispatchEvent(elt, mouseEvent('mousedown', 1, 50, 1, 50)); 359 dispatchEvent(document.body, mouseEvent('mouseup', 1, 50, 1, 50)); 360 } 361 else 362 { 363 App.say('{1} not found', [searchTerm]); 364 } 365 366 if (done != null) 367 { 368 done(); 369 } 370 }); 371 }; 372 373 // http://stackoverflow.com/questions/11919065/sort-an-array-by-the-levenshtein-distance-with-best-performance-in-javascript 374 //http://www.merriampark.com/ld.htm, http://www.mgilleland.com/ld/ldjavascript.htm, Damerau–Levenshtein distance (Wikipedia) 375 var levenshteinDist = function(s, t) { 376 var d = []; //2d matrix 377 378 // Step 1 379 var n = s.length; 380 var m = t.length; 381 382 if (n == 0) return m; 383 if (m == 0) return n; 384 385 //Create an array of arrays in javascript (a descending loop is quicker) 386 for (var i = n; i >= 0; i--) d[i] = []; 387 388 // Step 2 389 for (var i = n; i >= 0; i--) d[i][0] = i; 390 for (var j = m; j >= 0; j--) d[0][j] = j; 391 392 // Step 3 393 for (var i = 1; i <= n; i++) { 394 var s_i = s.charAt(i - 1); 395 396 // Step 4 397 for (var j = 1; j <= m; j++) { 398 399 //Check the jagged ld total so far 400 if (i == j && d[i][j] > 4) return n; 401 402 var t_j = t.charAt(j - 1); 403 var cost = (s_i == t_j) ? 0 : 1; // Step 5 404 405 //Calculate the minimum 406 var mi = d[i - 1][j] + 1; 407 var b = d[i][j - 1] + 1; 408 var c = d[i - 1][j - 1] + cost; 409 410 if (b < mi) mi = b; 411 if (c < mi) mi = c; 412 413 d[i][j] = mi; // Step 6 414 415 //Damerau transposition 416 if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) { 417 d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost); 418 } 419 } 420 } 421 422 // Step 7 423 return d[n][m]; 424 } 425 426 function naiveHammingDistance(str1, str2) { 427 var dist = 0; 428 429 str1 = str1.toLowerCase(); 430 str2 = str2.toLowerCase(); 431 432 for(var i = 0; i < str1.length; i++) 433 { 434 if (str2[i] && str2[i] !== str1[i]) 435 { 436 dist += Math.abs(str1.charCodeAt(i) - str2.charCodeAt(i)) + Math.abs(str2.indexOf( str1[i] )) * 2; 437 } 438 else if (!str2[i]) 439 { 440 // If there's no letter in the comparing string 441 dist += dist; 442 } 443 } 444 445 return dist; 446 }; 447 448 function getBestWord(str1, words, useLevenshteinDist) 449 { 450 if (words == null || words.length == 0) 451 { 452 return str1; 453 } 454 455 useLevenshteinDist = (useLevenshteinDist != null) ? useLevenshteinDist : true; 456 457 var bestWord = words[0]; 458 var minDist = ((useLevenshteinDist) ? levenshteinDist(str1, bestWord) : 459 naiveHammingDistance(str1, bestWord)); 460 461 for (var i = 1; i < words.length; i++) 462 { 463 var tmp = ((useLevenshteinDist) ? levenshteinDist(str1, words[i]) : 464 ((str1 == words[i]) ? 0 : naiveHammingDistance(str1, words[i]))); 465 466 if (tmp < minDist || (tmp == minDist && 467 str1.length > bestWord.length && 468 bestWord.length < words[i].length)) 469 { 470 bestWord = words[i]; 471 minDist = tmp; 472 } 473 474 if (bestWord == str1) 475 { 476 break; 477 } 478 } 479 480 return bestWord; 481 } 482 483 function mouseEvent(type, sx, sy, cx, cy, shift) 484 { 485 var evt; 486 var e = { 487 bubbles: true, 488 cancelable: (type != "mousemove"), 489 view: window, 490 detail: 0, 491 screenX: sx, 492 screenY: sy, 493 clientX: cx, 494 clientY: cy, 495 ctrlKey: false, 496 altKey: false, 497 shiftKey: (shift != null) ? shift : false, 498 metaKey: false, 499 button: 0, 500 relatedTarget: undefined 501 }; 502 503 if (typeof( document.createEvent ) == "function") 504 { 505 evt = document.createEvent("MouseEvents"); 506 evt.initMouseEvent(type, 507 e.bubbles, e.cancelable, e.view, e.detail, 508 e.screenX, e.screenY, e.clientX, e.clientY, 509 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 510 e.button, document.body.parentNode); 511 } 512 else if (document.createEventObject) 513 { 514 evt = document.createEventObject(); 515 for (prop in e) 516 { 517 evt[prop] = e[prop]; 518 } 519 520 evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button; 521 } 522 523 return evt; 524 }; 525 526 function dispatchEvent (el, evt) 527 { 528 if (el.dispatchEvent) 529 { 530 el.dispatchEvent(evt); 531 } 532 else if (el.fireEvent) 533 { 534 el.fireEvent('on' + type, evt); 535 } 536 537 return evt; 538 }; 539 540 var keyHandlerEscape = ui.keyHandler.escape; 541 ui.keyHandler.escape = function(evt) 542 { 543 if ((!mxClient.IS_MAC && mxEvent.isAltDown(evt)) || 544 ((mxClient.IS_MAC || chromeOs) && mxEvent.isControlDown(evt))) 545 { 546 if (speechOutputEnabled) 547 { 548 App.say('Speech output disabled'); 549 } 550 551 speechOutputEnabled = !speechOutputEnabled; 552 553 if (speechOutputEnabled) 554 { 555 App.say('Speech output enabled'); 556 } 557 558 mxEvent.consume(evt); 559 } 560 else if (mxEvent.isShiftDown(evt)) 561 { 562 App.sayHint(); 563 mxEvent.consume(evt); 564 } 565 else 566 { 567 keyHandlerEscape.apply(this, arguments); 568 } 569 }; 570 571 ui.keyHandler.bindAction(32, true, 'speechListen'); // Ctrl+SPACE 572 ui.keyHandler.bindAction(79, true, 'speechListenContinuous'); // Ctrl+O 573 574 /** 575 * Plays a beep. 576 */ 577 var beep = new Audio('data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU='); 578 var beep2 = new Audio('data:audio/wav;base64,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'); 579 580 App.beep = function(wav) 581 { 582 wav = (wav != null) ? wav : beep; 583 584 wav.play(); 585 }; 586 587 // Thread to reset the label 588 var resetStatus = null; 589 590 /** 591 * 592 * Static method for speech output. 593 */ 594 App.say = function(message, params) 595 { 596 if ('speechSynthesis' in window && message != null) 597 { 598 var text = mxResources.replacePlaceholders(message, params || []); 599 lastMessageTimestamp = null; 600 lastMessage = text; 601 602 if (text != null && text.length > 0) 603 { 604 if (td != null) 605 { 606 var tmp = text; 607 608 // Capitalize string 609 if (tmp != null && tmp.length > 1) 610 { 611 tmp = tmp.charAt(0).toUpperCase() + tmp.slice(1); 612 } 613 614 td.innerHTML = ((speechOutputEnabled) ? '<img style="margin-right:4px;" align="absmiddle" border="0" src="' + 615 outputImage + '"/>' : '') + ' ' + tmp; 616 td.style.color = '#235695'; 617 618 if (resetStatus != null) 619 { 620 window.clearTimeout(resetStatus); 621 resetStatus = null; 622 } 623 624 resetStatus = window.setTimeout(function() 625 { 626 updateStatusMessage(); 627 }, (recognizing != null) ? 1000 : 3000); 628 } 629 630 // Workaround for talking too much 631 if (speechOutputEnabled && (!speechSynthesis.speaking || !speechSynthesis.pending)) 632 { 633 if (text.length < maxMessageLength) 634 { 635 var msg = new SpeechSynthesisUtterance(); 636 637 // Picks random voice with same main locale 638 var voices = speechSynthesis.getVoices(); 639 640 // Say "again" for same last message except more than 10 secs ago or shorter than again 641 if (lastMessageTimestamp != null && text == lastMessage && text != null && 642 text.length > 5 && lastMessage != 'again') 643 { 644 if (lastMessageTimestamp != null && 645 new Date().getTime() - lastMessageTimestamp.getTime() < 10000) 646 { 647 text = 'repeat'; 648 } 649 } 650 651 msg.voice = voices[currentVoice]; 652 msg.voiceURI = 'native'; 653 //msg.lang = lang; 654 msg.text = text; 655 656 console.log('App.say speak:', msg.text); 657 speechSynthesis.speak(msg); 658 659 lastMessageTimestamp = new Date(); 660 } 661 else 662 { 663 console.log('App.say ignored:', text); 664 } 665 } 666 else 667 { 668 console.log('App.say skipped:', message); 669 } 670 } 671 } 672 }; 673 674 /** 675 * Static method for speech output. 676 */ 677 App.listen = function(continuous) 678 { 679 if ('webkitSpeechRecognition' in window && speechInputEnabled) 680 { 681 if (recognizing != null) 682 { 683 recognizing.stop(); 684 } 685 else 686 { 687 var recognition = new webkitSpeechRecognition(); 688 recognition.interimResults = true; 689 690 // TODO: Should use grammar instead of trying more alternatives 691 recognition.maxAlternatives = 5; 692 693 recognition.lang = lang; 694 695 if (continuous != null) 696 { 697 recognition.continuous = continuous; 698 } 699 700 recognition.onstart = function(event) 701 { 702 updateStatusMessage(); 703 App.beep(); 704 }; 705 706 recognition.onresult = function(event) 707 { 708 for (var i = event.resultIndex; i < event.results.length; ++i) 709 { 710 if (td != null) 711 { 712 td.innerHTML = '<img style="margin-right:4px;" align="absmiddle" border="0" src="' + micImage + 713 '"/> ' + event.results[i][0].transcript; 714 td.style.color = (event.results[i].isFinal) ? '#235695' : 'darkGray'; 715 } 716 717 if (event.results[i].isFinal) 718 { 719 var ok = false; 720 721 for (var j = 0; j < event.results[i].length; j++) 722 { 723 if (App.executeVoiceCommand(event.results[i][j].transcript, recognition)) 724 { 725 ok = true; 726 break; 727 } 728 } 729 730 if (!ok) 731 { 732 App.say('{1} not found', [event.results[i][0].transcript]); 733 } 734 735 if (td != null) 736 { 737 if (resetStatus != null) 738 { 739 window.clearTimeout(resetStatus); 740 resetStatus = null; 741 } 742 743 resetStatus = window.setTimeout(function() 744 { 745 updateStatusMessage(); 746 }, 1000); 747 } 748 } 749 } 750 }; 751 752 recognition.onend = function(event) 753 { 754 // Overrides footer 755 recognizing = null; 756 updateStatusMessage(); 757 App.beep(beep2); 758 }; 759 760 recognition.start(); 761 recognizing = recognition; 762 } 763 } 764 else 765 { 766 speechOutputEnabled = !speechOutputEnabled; 767 lastMessageTimestamp = null; 768 App.say(lastMessage || 'Ready'); 769 lastMessageTimestamp = null; 770 } 771 }; 772 773 /** 774 * Executes the given voice command. 775 */ 776 App.executeVoiceCommand = function(command, recognition) 777 { 778 console.log('App.execute:', mxUtils.trim(command)); 779 var tokens = mxUtils.trim(command).split(' '); 780 781 if (tokens.length > 0 && graph.isEnabled()) 782 { 783 // Ask for Mic permissions 784 // FIXME: Dialog seems to be hidden until tab is changed 785 function resolveStylename(token, styles) 786 { 787 var tmp = token.toLowerCase().replace(/ /g, ''); 788 var style = null; 789 790 for (var i = 0; i < styles.length; i++) 791 { 792 if (styles[i].toLowerCase() == tmp) 793 { 794 style = styles[i]; 795 break; 796 } 797 } 798 799 return style; 800 }; 801 802 // Main command 803 tokens[0] = tokens[0].toLowerCase(); 804 805 // TODO: Use hamming distance for best match command but include all possible actions 806 // which might be too slow 807 // console.log('connect', naiveHammingDistance(tokens[0], 'connect'), naiveHammingDistance('disable', 'connect'), naiveHammingDistance('change', 'connect')); 808 809 if (graph.isEditing()) 810 { 811 if (tokens.length == 1 && tokens[0] == 'apply') 812 { 813 graph.stopEditing(); 814 } 815 else if (tokens.length == 1 && tokens[0] == 'undo') 816 { 817 document.execCommand('undo', false, null); 818 } 819 else if (tokens.length == 1 && tokens[0] == 'redo') 820 { 821 document.execCommand('redo', false, null); 822 } 823 else 824 { 825 document.execCommand('insertHTML', false, command); 826 } 827 828 return true; 829 } 830 else if (tokens[0] == 'edit' && tokens[1] == 'text') 831 { 832 var cells = graph.getSelectionCells(); 833 834 if (cells.length == 1) 835 { 836 graph.startEditingAtCell(cells[0]); 837 } 838 839 return true; 840 } 841 else if (tokens[0] == 'hello' || tokens[0] == 'hi') 842 { 843 App.say('Hello! Try "Help", "Help Topic" or "Quick Start".'); 844 845 return true; 846 } 847 else if (tokens[0] == 'help') 848 { 849 var wnd = ui.openLink('https://www.diagrams.net/doc/faq/voice-plugin'); 850 851 if (wnd == null) 852 { 853 App.say('Popup blocked'); 854 } 855 else if (tokens.length > 1) 856 { 857 // Just used to check if popup windows are allowed 858 wnd.close(); 859 var searchTerm = mxUtils.trim(command.substring(tokens[0].length)); 860 861 if (searchTerm != null && searchTerm.length > 0) 862 { 863 ui.openLink('https://www.google.com/search?q=site%3Adiagrams.net+inurl%3A%2Fdoc%2Ffaq%2F+' + 864 encodeURIComponent(searchTerm)); 865 App.say(command); 866 } 867 } 868 else 869 { 870 App.say('help'); 871 } 872 873 return true; 874 } 875 else if ((tokens[0] == 'info' && tokens.length == 1) || mxUtils.trim(command).toLowerCase() == 'what\'s this') 876 { 877 App.sayHint(); 878 879 return true; 880 } 881 else if (tokens[0] == 'install' && tokens.length == 1) 882 { 883 setPluginInstalled(true); 884 App.say('Installed'); 885 886 return true; 887 } 888 else if (tokens[0] == 'uninstall' && tokens.length == 1) 889 { 890 setPluginInstalled(false); 891 App.say('Uninstalled'); 892 893 return true; 894 } 895 else if (tokens[0] == 'quick' && tokens.length > 0 && tokens[1].toLowerCase() == 'start') 896 { 897 var wnd = window.open('https://youtu.be/8OaMWa4R1SE?t=1'); 898 899 if (wnd == null) 900 { 901 App.say('Popup blocked'); 902 } 903 904 return true; 905 } 906 else if (tokens[0] == 'search' && tokens.length > 1) 907 { 908 var searchToken = tokens.slice(1, tokens.length).join(' ').toLowerCase(); 909 var wnd = window.open('https://www.google.ch/search?q=' + encodeURIComponent(searchToken) + '&tbm=isch', 'voicePluginSearchResult'); 910 911 if (wnd == null) 912 { 913 App.say('Popup blocked'); 914 } 915 916 return true; 917 } 918 else if (tokens[0] == 'shrink') 919 { 920 var cell = graph.getSelectionCell(); 921 var geo = graph.getCellGeometry(cell); 922 923 if (graph.getModel().isVertex(cell) && geo != null) 924 { 925 geo = geo.clone(); 926 927 if (tokens.length == 1 || tokens[1] == 'height') 928 { 929 geo.height = graph.snap(Math.round(geo.height * 0.5)); 930 } 931 932 if (tokens.length == 1 || tokens[1] != 'height') 933 { 934 geo.width = graph.snap(Math.round(geo.width * 0.5)); 935 } 936 937 if (geo.width > graph.tolerance && geo.height > graph.tolerance) 938 { 939 graph.getModel().setGeometry(cell, geo); 940 App.say('Resized'); 941 } 942 else 943 { 944 App.say('Too small'); 945 } 946 } 947 else 948 { 949 App.say('No cell to resize'); 950 } 951 952 return true; 953 } 954 else if (tokens[0] == 'double') 955 { 956 var cell = graph.getSelectionCell(); 957 var geo = graph.getCellGeometry(cell); 958 959 if (graph.getModel().isVertex(cell) && geo != null) 960 { 961 geo = geo.clone(); 962 963 if (tokens.length == 1 || tokens[1] == 'height') 964 { 965 geo.height *= 2; 966 } 967 968 if (tokens.length == 1 || tokens[1] != 'height') 969 { 970 geo.width *= 2; 971 } 972 973 if (geo.width > graph.tolerance && geo.height > graph.tolerance) 974 { 975 graph.getModel().setGeometry(cell, geo); 976 App.say('Resized'); 977 } 978 else 979 { 980 App.say('Too small'); 981 } 982 } 983 else 984 { 985 App.say('No cell to resize'); 986 } 987 988 return true; 989 } 990 else if (tokens[0] == 'width' || tokens[0] == 'with' || tokens[0] == 'height') 991 { 992 // Try numeric stylename if last token is numeric 993 var lastToken = tokens[tokens.length - 1].toLowerCase(); 994 995 // Fixes some special cases for recoginition of short phrases 996 // like "stroke width 2" where it tries to be clever 997 if (lastToken == 'tool' || lastToken == 'tune' || lastToken == 'tomb' || lastToken == 'tube') 998 { 999 lastToken = '2'; 1000 } 1001 else if (lastToken == 'd3') 1002 { 1003 lastToken = '3'; 1004 } 1005 else if (lastToken == 'v') 1006 { 1007 lastToken = '5'; 1008 } 1009 else 1010 { 1011 // Converts numeric words to numbers (system only seems to return written numbers for <= 4) 1012 var numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; 1013 var tmpIndex = mxUtils.indexOf(numbers, lastToken); 1014 1015 if (tmpIndex >= 0) 1016 { 1017 lastToken = tmpIndex; 1018 } 1019 } 1020 1021 var lastValue = parseFloat(tokens[1].toLowerCase()); 1022 1023 if (tokens.length >= 2 && !isNaN(lastValue)) 1024 { 1025 var cell = graph.getSelectionCell(); 1026 var geo = graph.getCellGeometry(cell); 1027 1028 if (graph.getModel().isVertex(cell) && geo != null) 1029 { 1030 geo = geo.clone(); 1031 1032 if (tokens[0] == 'height') 1033 { 1034 geo.height = lastValue; 1035 } 1036 else 1037 { 1038 geo.width = lastValue; 1039 } 1040 1041 graph.getModel().setGeometry(cell, geo); 1042 App.say('Resized'); 1043 } 1044 else 1045 { 1046 App.say('No cell to resize'); 1047 } 1048 } 1049 1050 return true; 1051 } 1052 else if (tokens[0] == 'insert' && tokens.length > 1) 1053 { 1054 // Fixes some common mistakes 1055 if (tokens[1].toLowerCase() == 'edits') 1056 { 1057 tokens[1] = 'ellipse'; 1058 } 1059 1060 var searchTerm = mxUtils.trim(tokens.slice(1, tokens.length).join(' ')); 1061 var current = graph.getSelectionCell(); 1062 1063 // Clears selection to disable built-in connecting 1064 graph.clearSelection(); 1065 1066 insertShape(searchTerm, function() 1067 { 1068 var cell = graph.getSelectionCell(); 1069 1070 // Connects dangling edge of previously selected edge and moves cell 1071 if (graph.model.isVertex(cell) && graph.model.isEdge(current) && 1072 (graph.model.getTerminal(current, true) == null || 1073 graph.model.getTerminal(current, false) == null)) 1074 { 1075 var edgeState = graph.view.getState(current); 1076 var vertexState = graph.view.getState(cell); 1077 1078 if (vertexState != null && edgeState != null && edgeState.absolutePoints != null && 1079 edgeState.absolutePoints.length > 1) 1080 { 1081 var source = graph.model.getTerminal(current, true) == null; 1082 var pts = edgeState.absolutePoints; 1083 var pt = pts[(source) ? 0 : pts.length - 1]; 1084 var loc = new mxPoint(vertexState.getCenterX(), vertexState.getCenterY()); 1085 1086 if (loc != null && edgeState != null) 1087 { 1088 var s = graph.view.scale; 1089 var dx = pt.x - loc.x; 1090 var dy = pt.y - loc.y; 1091 1092 // TODO: Should add insert to transaction but need absolute position 1093 graph.model.beginUpdate(); 1094 try 1095 { 1096 graph.moveCells(graph.getSelectionCells(), dx / s, dy / s); 1097 graph.model.setTerminal(current, cell, source); 1098 } 1099 finally 1100 { 1101 graph.model.endUpdate(); 1102 } 1103 } 1104 } 1105 } 1106 }); 1107 1108 return true; 1109 } 1110 else if (tokens[0] == 'connect' || tokens[0] == 'kinect' || (tokens[0] == 'clone' && tokens.length > 1)) 1111 { 1112 var cell = graph.getSelectionCell(); 1113 1114 if (graph.getModel().isVertex(cell)) 1115 { 1116 // Uses east direction if token not understood 1117 var direction = mxConstants.DIRECTION_EAST; 1118 1119 if (tokens.length > 1) 1120 { 1121 tokens[1] = tokens[1].toLowerCase(); 1122 1123 // Guessing direction based on minimum hamming distance for given set 1124 var guess = getBestWord(tokens[1], ['up', 'left', 'down', 'right', 'north', 'south', 'east', 'west']); 1125 1126 if (guess == 'up' || guess == mxConstants.DIRECTION_NORTH) 1127 { 1128 direction = mxConstants.DIRECTION_NORTH; 1129 } 1130 else if (guess == 'left' || guess == mxConstants.DIRECTION_WEST) 1131 { 1132 direction = mxConstants.DIRECTION_WEST; 1133 } 1134 else if (guess == 'down' || guess == mxConstants.DIRECTION_SOUTH) 1135 { 1136 direction = mxConstants.DIRECTION_SOUTH; 1137 } 1138 } 1139 1140 var length = graph.defaultEdgeLength; 1141 var cloneSource = tokens[0] == 'clone'; 1142 var evt = mouseEvent('click', 1, 50, 1, 50, !cloneSource); 1143 var cells = graph.connectVertex(cell, direction, length, evt); 1144 graph.selectCellsForConnectVertex(cells, evt, ui.hoverIcons); 1145 } 1146 1147 return true; 1148 } 1149 // Fixes drive and ride common mistakes for right 1150 else if (mxUtils.indexOf(['and', 'drive', 'ride', 'move', 'up', 'left', 'down', 'right', 'north', 'south', 'east', 'west', 'downright'], tokens[0]) >= 0) 1151 { 1152 var cell = graph.getSelectionCell(); 1153 1154 if (!graph.isSelectionEmpty()) 1155 { 1156 // Uses east direction if token not understood 1157 var dx = 0; 1158 var dy = 0; 1159 1160 for (var i = 0; i < tokens.length; i++) 1161 { 1162 tokens[i] = tokens[i].toLowerCase(); 1163 var direction = null; 1164 1165 // Downright is a single word needs special handling 1166 if (tokens[i].toLowerCase() == 'downright') 1167 { 1168 dx += graph.defaultEdgeLength; 1169 dy += graph.defaultEdgeLength; 1170 } 1171 else 1172 { 1173 var guess = null; 1174 1175 // Guessing direction based on minimum hamming distance for given set 1176 // Handle some common cases 1177 if (tokens[i] == 'drive' || tokens[i] == 'ride') 1178 { 1179 guess = 'right'; 1180 } 1181 else 1182 { 1183 guess = getBestWord(tokens[i], ['move', 'and', 'up', 'left', 'right', 'north', 'south', 'east', 'west', 'down', 'downright']); 1184 } 1185 1186 if (guess == 'up' || guess == mxConstants.DIRECTION_NORTH) 1187 { 1188 direction = mxConstants.DIRECTION_NORTH; 1189 } 1190 else if (guess == 'left' || guess == mxConstants.DIRECTION_WEST) 1191 { 1192 direction = mxConstants.DIRECTION_WEST; 1193 } 1194 else if (guess == 'down' || guess == mxConstants.DIRECTION_SOUTH) 1195 { 1196 direction = mxConstants.DIRECTION_SOUTH; 1197 } 1198 else if (guess == 'right' || guess == mxConstants.DIRECTION_EAST) 1199 { 1200 direction = mxConstants.DIRECTION_EAST; 1201 } 1202 1203 if (direction != null) 1204 { 1205 if (direction == mxConstants.DIRECTION_NORTH) 1206 { 1207 dy += -graph.defaultEdgeLength; 1208 } 1209 else if (direction == mxConstants.DIRECTION_WEST) 1210 { 1211 dx += -graph.defaultEdgeLength; 1212 } 1213 else if (direction == mxConstants.DIRECTION_SOUTH) 1214 { 1215 dy += graph.defaultEdgeLength; 1216 } 1217 else if (direction == mxConstants.DIRECTION_EAST) 1218 { 1219 dx += graph.defaultEdgeLength; 1220 } 1221 } 1222 } 1223 } 1224 1225 if (dx != 0 || dy != null) 1226 { 1227 graph.moveCells(graph.getSelectionCells(), dx, dy); 1228 App.say('Moved'); 1229 } 1230 1231 return true; 1232 } 1233 } 1234 else if (tokens[0] == 'text') 1235 { 1236 var cells = graph.getSelectionCells(); 1237 1238 if (cells.length == 0 && graph.model.contains(lastInserted)) 1239 { 1240 cells = [lastInserted]; 1241 } 1242 1243 if (cells.length == 0) 1244 { 1245 App.say('No cell for text'); 1246 } 1247 else if (tokens[0].length >= 2) 1248 { 1249 var value = tokens.slice(1, tokens.length).join(' '); 1250 1251 if (value.length > 0) 1252 { 1253 // Capitalize string 1254 if (value.length > 1) 1255 { 1256 value = value.charAt(0).toUpperCase() + value.slice(1); 1257 } 1258 1259 graph.labelChanged(cells[0], value); 1260 } 1261 } 1262 1263 return true; 1264 } 1265 else if (tokens[0] == 'disconnect' && tokens.length == 1) 1266 { 1267 var cells = graph.getAllEdges(graph.getSelectionCells()); 1268 1269 if (cells.length == 0) 1270 { 1271 App.say('No cell to disconnect'); 1272 } 1273 else 1274 { 1275 graph.removeCells(cells); 1276 } 1277 1278 return true; 1279 } 1280 else if (tokens[0] == 'deselect' && tokens.length == 1) 1281 { 1282 graph.clearSelection(); 1283 1284 return true; 1285 } 1286 else if (tokens[0] === 'select' && (tokens.length == 1 || mxUtils.indexOf( 1287 ['vertices', 'edges', 'none', 'all'], tokens[1].toLowerCase()) < 0)) 1288 { 1289 // Handles some other shortcuts 1290 if (tokens.length == 1 || mxUtils.indexOf(['last'], tokens[1].toLowerCase()) >= 0) 1291 { 1292 if (graph.model.contains(lastInserted)) 1293 { 1294 graph.setSelectionCell(lastInserted); 1295 App.say('{1} selected', [graph.getWordForCell(lastInserted).replace(/([A-Z])/g, ' $1')]); 1296 return true; 1297 } 1298 } 1299 // Handles alternative cases of edges 1300 else if (mxUtils.indexOf(['connection', 'connections', 'inches'], tokens[1].toLowerCase()) >= 0) 1301 { 1302 var edges = graph.getAllEdges(graph.getSelectionCells()); 1303 1304 if (edges.length > 0) 1305 { 1306 graph.setSelectionCells(edges); 1307 } 1308 else 1309 { 1310 ui.actions.get('selectEdges').funct(); 1311 } 1312 1313 App.sayHint(); 1314 return true; 1315 } 1316 // Handles alternative version of vertices 1317 else if (mxUtils.indexOf(['shapes'], tokens[1].toLowerCase()) >= 0) 1318 { 1319 ui.actions.get('selectVertices').funct(); 1320 App.sayHint(); 1321 return true; 1322 } 1323 else if (mxUtils.indexOf(['previous'], tokens[1].toLowerCase()) >= 0) 1324 { 1325 if (graph.isSelectionEmpty()) 1326 { 1327 // Selects the first vertex 1328 var parent = graph.getDefaultParent(); 1329 var childCount = graph.model.getChildCount(parent); 1330 1331 for (var i = childCount - 1; i >= 0; i--) 1332 { 1333 var child = graph.model.getChildAt(parent, i); 1334 1335 if (graph.model.isVertex(child)) 1336 { 1337 graph.setSelectionCell(child); 1338 App.say('{1} selected', [graph.getWordForCell(child).replace(/([A-Z])/g, ' $1')]); 1339 1340 return true; 1341 } 1342 } 1343 } 1344 else 1345 { 1346 var cell = graph.getSelectionCell(); 1347 var model = graph.getModel(); 1348 var index = model.getParent(cell).getIndex(cell); 1349 var childCount = model.getChildCount(model.getParent(cell)); 1350 1351 if (index >= 0) 1352 { 1353 var next = model.getParent(cell).getChildAt(((index == 0) ? childCount : index) - 1); 1354 1355 if (next != null) 1356 { 1357 graph.setSelectionCell(next); 1358 App.say('{1} selected', [graph.getWordForCell(next)]); 1359 1360 return true; 1361 } 1362 } 1363 1364 App.say('Previous not found'); 1365 } 1366 } 1367 else if (mxUtils.indexOf(['source', 'target'], tokens[1].toLowerCase()) >= 0) 1368 { 1369 var cell = graph.getSelectionCell(); 1370 1371 if (graph.model.isEdge(cell)) 1372 { 1373 var terminal = graph.model.getTerminal(cell, tokens[1].toLowerCase() == 'source'); 1374 1375 if (terminal != null) 1376 { 1377 graph.setSelectionCell(terminal); 1378 return true; 1379 } 1380 } 1381 } 1382 else if (mxUtils.indexOf(['next'], tokens[1].toLowerCase()) >= 0) 1383 { 1384 if (graph.isSelectionEmpty()) 1385 { 1386 // Selects the first vertex 1387 var parent = graph.getDefaultParent(); 1388 var childCount = graph.model.getChildCount(parent); 1389 1390 for (var i = 0; i < childCount; i++) 1391 { 1392 var child = graph.model.getChildAt(parent, i); 1393 1394 if (graph.model.isVertex(child)) 1395 { 1396 graph.setSelectionCell(child); 1397 App.say('{1} selected', [graph.getWordForCell(child).replace(/([A-Z])/g, ' $1')]); 1398 1399 return true; 1400 } 1401 } 1402 } 1403 else 1404 { 1405 var cell = graph.getSelectionCell(); 1406 var model = graph.getModel(); 1407 var index = model.getParent(cell).getIndex(cell); 1408 var childCount = model.getChildCount(model.getParent(cell)); 1409 1410 if (index < childCount) 1411 { 1412 var next = model.getParent(cell).getChildAt(((index == childCount - 1) ? 0 : index + 1)); 1413 1414 if (next != null) 1415 { 1416 graph.setSelectionCell(next); 1417 App.say('{1} selected', [graph.getWordForCell(next).replace(/([A-Z])/g, ' $1')]); 1418 1419 return true; 1420 } 1421 } 1422 } 1423 } 1424 else 1425 { 1426 var states = graph.view.states.getValues(); 1427 var token = tokens[1].toLowerCase(); 1428 var searchToken = tokens.slice(1, tokens.length).join('').toLowerCase(); 1429 1430 for (var i = states.length - 1; i > 0; i--) 1431 { 1432 // Simple matching for shape name or label 1433 if (!graph.isCellSelected(states[i].cell)) 1434 { 1435 // Tries label search first 1436 var lab = graph.getLabel(states[i].cell); 1437 1438 if (lab != null && lab.length > 0) 1439 { 1440 lab = lab.toLowerCase().replace(/ /g, ''); 1441 1442 // Some common mistakes 1443 if (lab.charAt(lab.length - 1) == '2' && tokens[tokens.length - 1].toLowerCase() == 'to') 1444 { 1445 lab = lab.substring(0, lab.length - 1) + 'to'; 1446 } 1447 1448 var min = Math.min(searchToken.length, lab.length); 1449 1450 if (searchToken.substring(0, min) == lab.substring(0, min)) 1451 { 1452 graph.setSelectionCell(states[i].cell); 1453 App.say('{1} selected', [graph.getWordForCell(states[i].cell)]); 1454 1455 return true; 1456 } 1457 } 1458 1459 // Then tries shapename search 1460 if (tokens.length == 2) 1461 { 1462 // Some common names 1463 if (mxUtils.indexOf(['circle'], token) >= 0) 1464 { 1465 searchToken = 'ellipse'; 1466 } 1467 else if (mxUtils.indexOf(['condition', 'decision'], token) >= 0) 1468 { 1469 searchToken = 'rhombus'; 1470 } 1471 else if (mxUtils.indexOf(['preparation'], token) >= 0) 1472 { 1473 searchToken = 'hexagon'; 1474 } 1475 else if (mxUtils.indexOf(['trapez'], token) >= 0) 1476 { 1477 searchToken = 'parallelogram'; 1478 } 1479 } 1480 1481 var wrd = graph.getWordForCell(states[i].cell, true); 1482 var min = Math.min(wrd.length, searchToken.length); 1483 1484 if (searchToken.substring(0, min) == wrd.substring(0, min)) 1485 { 1486 graph.setSelectionCell(states[i].cell); 1487 App.say('{1} selected', [graph.getWordForCell(states[i].cell)]); 1488 1489 return true; 1490 } 1491 } 1492 } 1493 } 1494 } 1495 else if (tokens[0] === 'remove') 1496 { 1497 var cells = graph.getSelectionCells(); 1498 1499 if (cells.length == 0 && graph.model.contains(lastInserted)) 1500 { 1501 cells = [lastInserted]; 1502 } 1503 1504 if (cells.length == 0) 1505 { 1506 App.say('No cells'); 1507 } 1508 else if (tokens[1].toLowerCase() == 'text') 1509 { 1510 graph.labelChanged(cells[0], ''); 1511 } 1512 else 1513 { 1514 // Remove style 1515 var styleToken = mxUtils.trim(tokens.slice(1, tokens.length).join(' ').toLowerCase()); 1516 1517 // Fixes common recognition errors 1518 styleToken = styleToken.replace(/a line/g, 'align') 1519 1520 // Merges to single word 1521 styleToken = styleToken.replace(/ /g, '') 1522 1523 // Fixes common speech errors that make no sense in the context 1524 var keys = [mxConstants.STYLE_FILLCOLOR, mxConstants.STYLE_GRADIENTCOLOR, 1525 mxConstants.STYLE_STROKECOLOR, mxConstants.STYLE_FONTCOLOR, 1526 mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, mxConstants.STYLE_LABEL_BORDERCOLOR, 1527 mxConstants.STYLE_STROKEWIDTH, mxConstants.STYLE_FONTSIZE, mxConstants.STYLE_SPACING, 1528 mxConstants.STYLE_SPACING_TOP, mxConstants.STYLE_SPACING_LEFT, mxConstants.STYLE_SPACING_RIGHT, 1529 mxConstants.STYLE_SPACING_BOTTOM, mxConstants.STYLE_PERIMETER_SPACING, 1530 mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, mxConstants.STYLE_OPACITY, 1531 mxConstants.STYLE_TEXT_OPACITY, mxConstants.STYLE_ROTATION, 1532 mxConstants.STYLE_ALIGN, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.STYLE_FONTFAMILY, 1533 mxConstants.STYLE_LABEL_POSITION, mxConstants.STYLE_VERTICAL_LABEL_POSITION, 1534 mxConstants.STYLE_GRADIENT_DIRECTION]; 1535 1536 // Guesses best word 1537 var style = getBestWord(styleToken, keys); 1538 1539 if (style != null) 1540 { 1541 graph.setCellStyles(style, null, cells); 1542 ui.fireEvent(new mxEventObject('styleChanged', 'keys', [style], 'values', [null], 'cells', cells)); 1543 } 1544 else 1545 { 1546 App.say('Unknown style {1}', [changeTokens[0]]); 1547 } 1548 } 1549 1550 return true; 1551 } 1552 else 1553 { 1554 var cells = graph.getSelectionCells(); 1555 1556 if (cells.length == 0 && graph.model.contains(lastInserted)) 1557 { 1558 cells = [lastInserted]; 1559 } 1560 1561 // Try numeric stylename if last token is numeric 1562 var lastToken = tokens[tokens.length - 1].toLowerCase(); 1563 1564 // Fixes some special cases for recoginition of short phrases 1565 // like "stroke width 2" where it tries to be clever 1566 if (lastToken == 'tool' || lastToken == 'tune' || lastToken == 'tomb' || lastToken == 'tube') 1567 { 1568 lastToken = '2'; 1569 } 1570 else if (lastToken == 'd3') 1571 { 1572 lastToken = '3'; 1573 } 1574 else if (lastToken == 'v') 1575 { 1576 lastToken = '5'; 1577 } 1578 else 1579 { 1580 // Converts numeric words to numbers (system only seems to return written numbers for <= 4) 1581 var numbers = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; 1582 var tmpIndex = mxUtils.indexOf(numbers, lastToken); 1583 1584 if (tmpIndex >= 0) 1585 { 1586 lastToken = tmpIndex; 1587 } 1588 } 1589 1590 // If last token is numeric 1591 var lastValue = parseFloat(lastToken); 1592 1593 if (tokens.length >= 2 && !isNaN(lastValue)) 1594 { 1595 var styleToken = tokens.slice(0, tokens.length - 1).join('').toLowerCase(); 1596 1597 // Numeric styles 1598 var keys = [mxConstants.STYLE_STROKEWIDTH, mxConstants.STYLE_FONTSIZE, mxConstants.STYLE_SPACING, 1599 mxConstants.STYLE_SPACING_TOP, mxConstants.STYLE_SPACING_LEFT, mxConstants.STYLE_SPACING_RIGHT, 1600 mxConstants.STYLE_SPACING_BOTTOM, mxConstants.STYLE_PERIMETER_SPACING, 1601 mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, mxConstants.STYLE_OPACITY, 1602 mxConstants.STYLE_TEXT_OPACITY, mxConstants.STYLE_ROTATION] 1603 var style = null; 1604 1605 // Workaround for problem with "font size" is to say "size" ("font" is near "phone") 1606 if (styleToken == 'size') 1607 { 1608 style = mxConstants.STYLE_FONTSIZE; 1609 } 1610 else 1611 { 1612 // Guesses best word 1613 style = getBestWord(styleToken, keys); 1614 } 1615 1616 if (style != null) 1617 { 1618 if (cells.length == 0) 1619 { 1620 App.say('No cell for {1}', [style]); 1621 } 1622 // Plausibility check 1623 else if (lastValue <= 400) 1624 { 1625 graph.setCellStyles(style, lastValue, cells); 1626 ui.fireEvent(new mxEventObject('styleChanged', 'keys', [style], 'values', [lastValue], 'cells', cells)); 1627 } 1628 else 1629 { 1630 App.say('{1} ignored', [lastValue]); 1631 } 1632 1633 // Terminate 1634 return true; 1635 } 1636 } 1637 1638 // If "color" appears in the command 1639 if (tokens.length >= 2) 1640 { 1641 // Replaces GB spelling with US spelling for internal lookups 1642 var colorCommand = mxUtils.trim(command.toLowerCase().replace(/colour/g, 'color')); 1643 var colorIndex = colorCommand.indexOf('color'); 1644 1645 // "Color" (word alone) matches to fill color 1646 if (colorIndex >= 0) 1647 { 1648 // Text from first space after color* word 1649 var colorToken = mxUtils.trim(colorCommand.substring( 1650 colorCommand.indexOf(' ', colorIndex))).replace(/ /g, ''); 1651 var color = null; 1652 1653 // Checks for color code of token using ntc 1654 for (var i = 0; i < ntc.names.length; i++) 1655 { 1656 if (ntc.names[i][1].toLowerCase().replace(/ /g, '') == colorToken) 1657 { 1658 color = '#' + ntc.names[i][0]; 1659 break; 1660 } 1661 } 1662 1663 // Color is known 1664 if (color != null) 1665 { 1666 var styleToken = colorCommand.substring(0, colorIndex + 'color'.length).replace(/ /g, ''); 1667 1668 keys = [mxConstants.STYLE_GRADIENTCOLOR, 1669 mxConstants.STYLE_STROKECOLOR, mxConstants.STYLE_FONTCOLOR, 1670 mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, mxConstants.STYLE_LABEL_BORDERCOLOR, 1671 mxConstants.STYLE_FILLCOLOR]; 1672 var style = null; 1673 1674 // Handles some special cases 1675 if (styleToken == 'thecolor' || styleToken == 'color') 1676 { 1677 if (graph.model.isEdge(graph.getSelectionCell())) 1678 { 1679 style = mxConstants.STYLE_STROKECOLOR; 1680 } 1681 else 1682 { 1683 style = mxConstants.STYLE_FILLCOLOR; 1684 } 1685 } 1686 else 1687 { 1688 // Guesses best word 1689 style = getBestWord(styleToken, keys); 1690 } 1691 1692 if (style != null) 1693 { 1694 if (cells.length == 0) 1695 { 1696 App.say('No cell for {1}', [style]); 1697 } 1698 else 1699 { 1700 graph.setCellStyles(style, color, cells); 1701 ui.fireEvent(new mxEventObject('styleChanged', 'keys', [style], 'values', [color], 'cells', cells)); 1702 } 1703 1704 // Terminate 1705 return true; 1706 } 1707 } 1708 } 1709 } 1710 1711 // If the first token is a general stylename 1712 if (tokens.length >= 2) 1713 { 1714 var keys = [mxConstants.STYLE_ALIGN, mxConstants.STYLE_VERTICAL_ALIGN, 1715 mxConstants.STYLE_FONTFAMILY, mxConstants.STYLE_LABEL_POSITION, 1716 mxConstants.STYLE_VERTICAL_LABEL_POSITION, 1717 mxConstants.STYLE_GRADIENT_DIRECTION]; 1718 1719 var styleToken = mxUtils.trim(tokens.slice(0, tokens.length - 1).join(' ').toLowerCase()); 1720 1721 // Fixes common recognition errors 1722 styleToken = styleToken.replace(/a line/g, 'align') 1723 1724 // Merges to single word 1725 styleToken = styleToken.replace(/ /g, '') 1726 1727 var style = resolveStylename(styleToken, keys); 1728 var value = mxUtils.trim(tokens[tokens.length - 1].toLowerCase()); 1729 1730 if (style != null && value != null && value.length > 0) 1731 { 1732 if (cells.length == 0) 1733 { 1734 App.say('No cell for {1}', [style]); 1735 } 1736 else 1737 { 1738 graph.setCellStyles(style, value, cells); 1739 ui.fireEvent(new mxEventObject('styleChanged', 'keys', [style], 'values', [value], 'cells', cells)); 1740 } 1741 1742 // Terminate 1743 return true; 1744 } 1745 } 1746 1747 // Checks if the command is a shape name 1748 var vertices = []; 1749 1750 for (var i = 0; i < cells.length; i++) 1751 { 1752 var cell = cells[i]; 1753 1754 if (graph.model.isVertex(cell)) 1755 { 1756 vertices.push(cell); 1757 } 1758 } 1759 1760 var shapenameToken = mxUtils.trim(tokens.join('')).toLowerCase(); 1761 1762 // Searches for registered shape names 1763 // LATER: Using search like insert requires access to 1764 // the cells of the search result items 1765 if (shapeList == null) 1766 { 1767 shapeList = []; 1768 1769 for (var tmp in mxCellRenderer.defaultShapes) 1770 { 1771 shapeList.push(tmp.toLowerCase()); 1772 } 1773 } 1774 1775 // Only exact matches allowed here 1776 // LATER: Support rounded style 1777 var shape = null; 1778 1779 // Some common mappings 1780 if (mxUtils.indexOf(['cirlce', 'event', 'start', 'end'], shapenameToken) >= 0) 1781 { 1782 shapenameToken = shape = 'ellipse'; 1783 } 1784 else if (mxUtils.indexOf(['condition', 'decision'], shapenameToken) >= 0) 1785 { 1786 shapenameToken = shape = 'rhombus'; 1787 } 1788 else if (mxUtils.indexOf(['preparation'], shapenameToken) >= 0) 1789 { 1790 shapenameToken = shape = 'hexagon'; 1791 } 1792 else if (mxUtils.indexOf(['trapez'], shapenameToken) >= 0) 1793 { 1794 shapenameToken = shape = 'parallelogram'; 1795 } 1796 else 1797 { 1798 shape = getBestWord(shapenameToken, shapeList); 1799 } 1800 1801 // LATER: Ignore all edge shapes 1802 if (shape != null && shape.toLowerCase() == shapenameToken && shape != 'connector' && 1803 shape != 'arrow' && shape != 'flexarrow' && shape != 'arrowconnector' && 1804 shape != 'link') 1805 { 1806 if (cells.length == 0) 1807 { 1808 App.say('No cell for {1}', [shape]); 1809 } 1810 else if (vertices.length == 0) 1811 { 1812 App.say('Connections ignored'); 1813 } 1814 else 1815 { 1816 // LATER: Add perimeter style etc 1817 graph.setCellStyles(mxConstants.STYLE_SHAPE, shapenameToken, vertices); 1818 ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_SHAPE], 1819 'values', [shapenameToken], 'cells', vertices)); 1820 1821 // Terminate 1822 return true; 1823 } 1824 } 1825 1826 // Lazy creation of cache for action names 1827 if (actions == null) 1828 { 1829 actions = {}; 1830 actionList = []; 1831 1832 for (var name in ui.actions.actions) 1833 { 1834 var shortname = mxUtils.trim(name).toLowerCase().replace(/ /g, ''); 1835 actions[shortname] = ui.actions.actions[name]; 1836 actionList.push(shortname); 1837 } 1838 } 1839 1840 var name = mxUtils.trim(command).toLowerCase().replace(/ /g, ''); 1841 1842 if (urlParams['dev'] == '1') 1843 { 1844 var guess = getBestWord(name, actionList, true); 1845 console.log('App.say guess:', name, '=>', guess, '(' + 1846 naiveHammingDistance(name, guess) + ', ' + 1847 levenshteinDist(name, guess) + 1848 ')'); 1849 } 1850 1851 // Some common redirects and mistakes 1852 if (tokens[0] == 'back') 1853 { 1854 command = name = 'undo'; 1855 } 1856 else if (tokens[0] == 'restore') 1857 { 1858 command = name = 'redo'; 1859 } 1860 else if (tokens[0] == 'clone') 1861 { 1862 command = name = 'duplicate'; 1863 } 1864 else if (mxUtils.indexOf(['all', 'small', 'wall', 'call'], name) >= 0) 1865 { 1866 command = name = 'selectall'; 1867 } 1868 else if (mxUtils.indexOf(['both', 'boat', 'phone', 'don\'t', 'food', 'bolt', 'bull'], tokens[0]) >= 0) 1869 { 1870 command = name = 'bold'; 1871 } 1872 1873 var action = actions[name]; 1874 1875 if (action != null) 1876 { 1877 if (action.isEnabled()) 1878 { 1879 App.say(name); 1880 action.funct(); 1881 } 1882 else 1883 { 1884 // Cannot use name here as it has no spaces 1885 App.say('{1} disabled', [command]); 1886 } 1887 1888 return true; 1889 } 1890 1891 return false; 1892 } 1893 } 1894 }; 1895 1896 if ('speechSynthesis' in window) 1897 { 1898 // Shows dialog for mic access 1899 if (navigator.webkitGetUserMedia) 1900 { 1901 window.addEventListener('load', function() 1902 { 1903 navigator.webkitGetUserMedia({audio:true}, function() 1904 { 1905 //console.log('access to mic ok'); 1906 }, function(e) 1907 { 1908 //alert('Error getting audio'); 1909 console.log(e); 1910 }); 1911 }); 1912 } 1913 1914 // Installs helper methods and listeners for speech output 1915 var graph = ui.editor.graph; 1916 1917 App.reloadVoicePlugin = function() 1918 { 1919 // Shows UI 1920 speechOutputEnabled = true; 1921 menu.style.display = ''; 1922 td.style.display = 'inline-block'; 1923 }; 1924 1925 App.sayHint = function() 1926 { 1927 if (graph.getSelectionCount() == 0) 1928 { 1929 App.say('No cell selected.' + ((ui.isDiagramEmpty()) ? ' Diagram is empty.' : '')); 1930 } 1931 else if (graph.getSelectionCount() == 1) 1932 { 1933 var cell = graph.getSelectionCell(); 1934 1935 if (graph.getModel().isVertex(cell)) 1936 { 1937 var geo = graph.getCellGeometry(cell) 1938 1939 if (geo != null) 1940 { 1941 App.say('{1} at {2} and {3} is {4} times {5} pixels', [graph.getWordForCell(cell, true), 1942 geo.x, geo.y, geo.width, geo.height]); 1943 } 1944 } 1945 else 1946 { 1947 App.say('One connection selected'); 1948 } 1949 1950 var lab = mxUtils.trim(graph.getLabel(cell)); 1951 1952 if (lab != null && lab.length > 0 && lab.length < 20) 1953 { 1954 App.say('Label is {1}', [lab]); 1955 } 1956 } 1957 else 1958 { 1959 var cells = graph.getSelectionCells(); 1960 var vertexCount = 0; 1961 var edgeCount = 0; 1962 1963 for (var i = 0; i < cells.length; i++) 1964 { 1965 var cell = cells[i]; 1966 1967 if (graph.model.isEdge(cell)) 1968 { 1969 edgeCount++; 1970 } 1971 else if (graph.model.isVertex(cell)) 1972 { 1973 vertexCount++; 1974 } 1975 } 1976 1977 if (vertexCount > 0 && edgeCount > 0) 1978 { 1979 var tmp = ''; 1980 1981 if (vertexCount == 1) 1982 { 1983 tmp += 'One shape'; 1984 } 1985 else 1986 { 1987 tmp += '{1} shapes'; 1988 } 1989 1990 tmp += ' and '; 1991 1992 if (edgeCount == 1) 1993 { 1994 tmp += 'one connection'; 1995 } 1996 else 1997 { 1998 tmp += '{2} connections'; 1999 } 2000 2001 App.say(tmp + ' selected', [vertexCount, edgeCount]); 2002 } 2003 else if (vertexCount > 0) 2004 { 2005 App.say('{1} shapes selected', [vertexCount]); 2006 } 2007 else 2008 { 2009 App.say('{1} connections selected', [edgeCount]); 2010 } 2011 } 2012 }; 2013 2014 // Can return more than first word 2015 function firstWord(value) 2016 { 2017 // TODO Use regex 2018 if (value != null && value.length > maxLabelLength) 2019 { 2020 var space = value.indexOf(' '); 2021 2022 // Add second word if label is short 2023 var tmp = value.indexOf(' ', space + 1); 2024 2025 if (tmp >= 0 && tmp <= maxLabelLength) 2026 { 2027 space = tmp; 2028 2029 // Add third word if label is short 2030 var tmp = value.indexOf(' ', space + 1); 2031 2032 if (tmp >= 0 && tmp <= maxLabelLength) 2033 { 2034 space = tmp; 2035 } 2036 } 2037 2038 if (space >= 0) 2039 { 2040 value = value.substring(0, space); 2041 } 2042 } 2043 2044 return value; 2045 }; 2046 2047 graph.getWordForCell = function(cell, ignoreLabel) 2048 { 2049 var label = 'no'; 2050 2051 if (cell != null) 2052 { 2053 label = (ignoreLabel) ? null : firstWord(this.getLabel(cell)); 2054 2055 if (label == null || label.length == 0 || label.length > maxLabelLength || mxUtils.isNumeric(label)) 2056 { 2057 var style = this.getCurrentCellStyle(cell); 2058 var tmp = style[mxConstants.STYLE_SHAPE]; 2059 2060 if (tmp == 'label') 2061 { 2062 label = 'rectangle'; 2063 } 2064 else if (tmp != null) 2065 { 2066 var dot = tmp.lastIndexOf('.'); 2067 2068 if (dot >= 0) 2069 { 2070 tmp = tmp.substring(dot + 1); 2071 } 2072 2073 label = tmp 2074 } 2075 } 2076 2077 var div = document.createElement('div'); 2078 div.innerHTML = label; 2079 label = div.innerText; 2080 } 2081 2082 return label; 2083 }; 2084 2085 ui.addListener('styleChanged', function(sender, evt) 2086 { 2087 //console.log('styleChanged', evt, evt.getProperty('keys')); 2088 var cells = evt.getProperty('cells'); 2089 var keys = evt.getProperty('keys'); 2090 var values = evt.getProperty('values'); 2091 2092 if (cells != null && keys != null && keys.length == 1 && values.length == 1) 2093 { 2094 var tmp = values[0]; 2095 2096 if (typeof tmp === 'string' && tmp.charAt(0) == '#') 2097 { 2098 var n_match = ntc.name(tmp); 2099 2100 //if (n_match[2]) /* exact match */ 2101 { 2102 tmp = n_match[1]; 2103 } 2104 } 2105 2106 if (tmp == mxConstants.NONE || tmp == null) 2107 { 2108 App.say('Removed {1}', [keys[0]]); 2109 } 2110 else 2111 { 2112 // Replaces camel case notation with spaces 2113 var key = keys[0].replace(/([A-Z])/g, ' $1') 2114 App.say('{1} {2}', [key, tmp]); 2115 } 2116 } 2117 }); 2118 graph.addListener(mxEvent.LABEL_CHANGED, function(sender, evt) 2119 { 2120 //console.log('CELL_CONNECTED', evt); 2121 var cell = evt.getProperty('cell'); 2122 var old = evt.getProperty('old'); 2123 2124 // Resolves placeholders in new label 2125 var value = this.getLabel(cell); 2126 2127 if ((value != null && value.length > 20) || 2128 (old != null && old.length > 20)) 2129 { 2130 App.say('Label changed'); 2131 } 2132 else if (value != null && value.length == 0) 2133 { 2134 App.say('Label removed'); 2135 } 2136 else if (value != null) 2137 { 2138 App.say('{1}', [value]); 2139 } 2140 }); 2141 2142 graph.addListener(mxEvent.CELL_CONNECTED, function(sender, evt) 2143 { 2144// console.log('CELL_CONNECTED', evt); 2145// var edge = evt.getProperty('edge'); 2146// var terminal = evt.getProperty('terminal'); 2147// var other = graph.model.getTerminal(edge, !evt.getProperty('source')); 2148// 2149// if (other != null && terminal != null) 2150// { 2151// App.say('connected {1} to {2}', [getWordForCell(other), getWordForCell(terminal)]); 2152// } 2153 }); 2154 2155 graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) 2156 { 2157 //console.log('CELLS_ADDED', evt); 2158 var cells = evt.getProperty('cells'); 2159 2160 if (cells.length > 1) 2161 { 2162 var vertexCount = 0; 2163 var edgeCount = 0; 2164 2165 for (var i = 0; i < cells.length; i++) 2166 { 2167 var cell = cells[i]; 2168 2169 if (graph.model.isEdge(cell)) 2170 { 2171 edgeCount++; 2172 } 2173 else if (graph.model.isVertex(cell)) 2174 { 2175 vertexCount++; 2176 } 2177 } 2178 2179 if (vertexCount > 0 && edgeCount > 0) 2180 { 2181 var tmp = ''; 2182 2183 if (vertexCount == 1) 2184 { 2185 tmp += 'One shape'; 2186 } 2187 else 2188 { 2189 tmp += '{1} shapes'; 2190 } 2191 2192 tmp += ' and '; 2193 2194 if (edgeCount == 1) 2195 { 2196 tmp += 'one connection'; 2197 } 2198 else 2199 { 2200 tmp += '{2} connections'; 2201 } 2202 2203 App.say(tmp + ' inserted', [vertexCount, edgeCount]); 2204 } 2205 else if (vertexCount > 0) 2206 { 2207 App.say('{1} shapes', [vertexCount]); 2208 } 2209 else 2210 { 2211 App.say('{1} connections', [edgeCount]); 2212 } 2213 } 2214 else 2215 { 2216 cell = cells[0]; 2217 2218 if (graph.model.isEdge(cell) && graph.model.getTerminal(cell, true) != null && 2219 graph.model.getTerminal(cell, false) != null) 2220 { 2221 if (graph.getWordForCell(graph.model.getTerminal(cell, true)) == 2222 graph.getWordForCell(graph.model.getTerminal(cell, false))) 2223 { 2224 App.say('Connected'); 2225 } 2226 else 2227 { 2228 App.say('{1} connected to {2}', [graph.getWordForCell(graph.model.getTerminal(cell, true)), 2229 graph.getWordForCell(graph.model.getTerminal(cell, false))]); 2230 } 2231 } 2232 else if (graph.model.isVertex(cell)) 2233 { 2234 lastInserted = cell; 2235 2236 // Replaces camel case notation with spaces 2237 var word = graph.getWordForCell(cell).replace(/([A-Z])/g, ' $1'); 2238 2239 App.say('{1}', [word]); 2240 } 2241 } 2242 }); 2243 graph.addListener(mxEvent.CELLS_REMOVED, function(sender, evt) 2244 { 2245 //console.log('CELLS_REMOVED', evt); 2246 var cells = evt.getProperty('cells'); 2247 2248 if (cells.length > 1) 2249 { 2250 App.say('{1} cells deleted', [cells.length]); 2251 } 2252 else 2253 { 2254 cell = cells[0]; 2255 App.say('{1} deleted', [graph.getWordForCell(cell)]); 2256 } 2257 }); 2258 } 2259}); 2260 2261/** Code for color to voice based on ntc (name that color) 2262 2263/* 2264 2265+-----------------------------------------------------------------+ 2266| Created by Chirag Mehta - http://chir.ag/projects/ntc | 2267|-----------------------------------------------------------------| 2268| ntc js (Name that Color JavaScript) | 2269+-----------------------------------------------------------------+ 2270 2271All the functions, code, lists etc. have been written specifically 2272for the Name that Color JavaScript by Chirag Mehta unless otherwise 2273specified. 2274 2275This script is released under the: Creative Commons License: 2276Attribution 2.5 http://creativecommons.org/licenses/by/2.5/ 2277 2278Sample Usage: 2279 2280 <script type="text/javascript" src="ntc.js"></script> 2281 2282 <script type="text/javascript"> 2283 2284 var n_match = ntc.name("#6195ED"); 2285 n_rgb = n_match[0]; // This is the RGB value of the closest matching color 2286 n_name = n_match[1]; // This is the text string for the name of the match 2287 n_exactmatch = n_match[2]; // True if exact color match, False if close-match 2288 2289 alert(n_match); 2290 2291 </script> 2292 2293*/ 2294 2295var ntc = { 2296 2297 init: function() { 2298 var color, rgb, hsl; 2299 for(var i = 0; i < ntc.names.length; i++) 2300 { 2301 color = "#" + ntc.names[i][0]; 2302 rgb = ntc.rgb(color); 2303 hsl = ntc.hsl(color); 2304 ntc.names[i].push(rgb[0], rgb[1], rgb[2], hsl[0], hsl[1], hsl[2]); 2305 } 2306 }, 2307 2308 name: function(color) { 2309 2310 color = color.toUpperCase(); 2311 if(color.length < 3 || color.length > 7) 2312 return ["#000000", "Invalid Color: " + color, false]; 2313 if(color.length % 3 == 0) 2314 color = "#" + color; 2315 if(color.length == 4) 2316 color = "#" + color.substr(1, 1) + color.substr(1, 1) + color.substr(2, 1) + color.substr(2, 1) + color.substr(3, 1) + color.substr(3, 1); 2317 2318 var rgb = ntc.rgb(color); 2319 var r = rgb[0], g = rgb[1], b = rgb[2]; 2320 var hsl = ntc.hsl(color); 2321 var h = hsl[0], s = hsl[1], l = hsl[2]; 2322 var ndf1 = 0; ndf2 = 0; ndf = 0; 2323 var cl = -1, df = -1; 2324 2325 for(var i = 0; i < ntc.names.length; i++) 2326 { 2327 if(color == "#" + ntc.names[i][0]) 2328 return ["#" + ntc.names[i][0], ntc.names[i][1], true]; 2329 2330 ndf1 = Math.pow(r - ntc.names[i][2], 2) + Math.pow(g - ntc.names[i][3], 2) + Math.pow(b - ntc.names[i][4], 2); 2331 ndf2 = Math.pow(h - ntc.names[i][5], 2) + Math.pow(s - ntc.names[i][6], 2) + Math.pow(l - ntc.names[i][7], 2); 2332 ndf = ndf1 + ndf2 * 2; 2333 if(df < 0 || df > ndf) 2334 { 2335 df = ndf; 2336 cl = i; 2337 } 2338 } 2339 2340 return (cl < 0 ? ["#000000", "Invalid Color: " + color, false] : ["#" + ntc.names[cl][0], ntc.names[cl][1], false]); 2341 }, 2342 2343 // adopted from: Farbtastic 1.2 2344 // http://acko.net/dev/farbtastic 2345 hsl: function (color) { 2346 2347 var rgb = [parseInt('0x' + color.substring(1, 3)) / 255, parseInt('0x' + color.substring(3, 5)) / 255, parseInt('0x' + color.substring(5, 7)) / 255]; 2348 var min, max, delta, h, s, l; 2349 var r = rgb[0], g = rgb[1], b = rgb[2]; 2350 2351 min = Math.min(r, Math.min(g, b)); 2352 max = Math.max(r, Math.max(g, b)); 2353 delta = max - min; 2354 l = (min + max) / 2; 2355 2356 s = 0; 2357 if(l > 0 && l < 1) 2358 s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l)); 2359 2360 h = 0; 2361 if(delta > 0) 2362 { 2363 if (max == r && max != g) h += (g - b) / delta; 2364 if (max == g && max != b) h += (2 + (b - r) / delta); 2365 if (max == b && max != r) h += (4 + (r - g) / delta); 2366 h /= 6; 2367 } 2368 return [parseInt(h * 255), parseInt(s * 255), parseInt(l * 255)]; 2369 }, 2370 2371 // adopted from: Farbtastic 1.2 2372 // http://acko.net/dev/farbtastic 2373 rgb: function(color) { 2374 return [parseInt('0x' + color.substring(1, 3)), parseInt('0x' + color.substring(3, 5)), parseInt('0x' + color.substring(5, 7))]; 2375 }, 2376 2377 names: [ 2378 2379 // Added all standard HTML color names / gaudenz, oct-2015 2380["F0F8FF", "aliceblue"], 2381["FAEBD7", "antiquewhite"], 2382["00FFFF", "aqua"], 2383["7FFFD4", "aquamarine"], 2384["F0FFFF", "azure"], 2385["F5F5DC", "beige"], 2386["FFE4C4", "bisque"], 2387["000000", "black"], 2388["FFEBCD", "blanchedalmond"], 2389["0000FF", "blue"], 2390["8A2BE2", "blueviolet"], 2391["A52A2A", "brown"], 2392["DEB887", "burlywood"], 2393["5F9EA0", "cadetblue"], 2394["7FFF00", "chartreuse"], 2395["D2691E", "chocolate"], 2396["FF7F50", "coral"], 2397["6495ED", "cornflowerblue"], 2398["FFF8DC", "cornsilk"], 2399["DC143C", "crimson"], 2400["00FFFF", "cyan"], 2401["00008B", "darkblue"], 2402["008B8B", "darkcyan"], 2403["B8860B", "darkgoldenrod"], 2404["A9A9A9", "darkgray"], 2405["A9A9A9", "darkgrey"], 2406["006400", "darkgreen"], 2407["BDB76B", "darkkhaki"], 2408["8B008B", "darkmagenta"], 2409["556B2F", "darkolivegreen"], 2410["FF8C00", "darkorange"], 2411["9932CC", "darkorchid"], 2412["8B0000", "darkred"], 2413["E9967A", "darksalmon"], 2414["8FBC8F", "darkseagreen"], 2415["483D8B", "darkslateblue"], 2416["2F4F4F", "darkslategray"], 2417["2F4F4F", "darkslategrey"], 2418["00CED1", "darkturquoise"], 2419["9400D3", "darkviolet"], 2420["FF1493", "deeppink"], 2421["00BFFF", "deepskyblue"], 2422["696969", "dimgray"], 2423["696969", "dimgrey"], 2424["1E90FF", "dodgerblue"], 2425["B22222", "firebrick"], 2426["FFFAF0", "floralwhite"], 2427["228B22", "forestgreen"], 2428["FF00FF", "fuchsia"], 2429["DCDCDC", "gainsboro"], 2430["F8F8FF", "ghostwhite"], 2431["FFD700", "gold"], 2432["DAA520", "goldenrod"], 2433["808080", "gray"], 2434["808080", "grey"], 2435["008000", "green"], 2436["ADFF2F", "greenyellow"], 2437["F0FFF0", "honeydew"], 2438["FF69B4", "hotpink"], 2439["CD5C5C", "indianred "], 2440["4B0082", "indigo "], 2441["FFFFF0", "ivory"], 2442["F0E68C", "khaki"], 2443["E6E6FA", "lavender"], 2444["FFF0F5", "lavenderblush"], 2445["7CFC00", "lawngreen"], 2446["FFFACD", "lemonchiffon"], 2447["ADD8E6", "lightblue"], 2448["F08080", "lightcoral"], 2449["E0FFFF", "lightcyan"], 2450["FAFAD2", "lightgoldenrodyellow"], 2451["D3D3D3", "lightgray"], 2452["D3D3D3", "lightgrey"], 2453["90EE90", "lightgreen"], 2454["FFB6C1", "lightpink"], 2455["FFA07A", "lightsalmon"], 2456["20B2AA", "lightseagreen"], 2457["87CEFA", "lightskyblue"], 2458["778899", "lightslategray"], 2459["778899", "lightslategrey"], 2460["B0C4DE", "lightsteelblue"], 2461["FFFFE0", "lightyellow"], 2462["00FF00", "lime"], 2463["32CD32", "limegreen"], 2464["FAF0E6", "linen"], 2465["FF00FF", "magenta"], 2466["800000", "maroon"], 2467["66CDAA", "mediumaquamarine"], 2468["0000CD", "mediumblue"], 2469["BA55D3", "mediumorchid"], 2470["9370DB", "mediumpurple"], 2471["3CB371", "mediumseagreen"], 2472["7B68EE", "mediumslateblue"], 2473["00FA9A", "mediumspringgreen"], 2474["48D1CC", "mediumturquoise"], 2475["C71585", "mediumvioletred"], 2476["191970", "midnightblue"], 2477["F5FFFA", "mintcream"], 2478["FFE4E1", "mistyrose"], 2479["FFE4B5", "moccasin"], 2480["FFDEAD", "navajowhite"], 2481["000080", "navy"], 2482["FDF5E6", "oldlace"], 2483["808000", "olive"], 2484["6B8E23", "olivedrab"], 2485["FFA500", "orange"], 2486["FF4500", "orangered"], 2487["DA70D6", "orchid"], 2488["EEE8AA", "palegoldenrod"], 2489["98FB98", "palegreen"], 2490["AFEEEE", "paleturquoise"], 2491["DB7093", "palevioletred"], 2492["FFEFD5", "papayawhip"], 2493["FFDAB9", "peachpuff"], 2494["CD853F", "peru"], 2495["FFC0CB", "pink"], 2496["DDA0DD", "plum"], 2497["B0E0E6", "powderblue"], 2498["800080", "purple"], 2499["FF0000", "red"], 2500["BC8F8F", "rosybrown"], 2501["4169E1", "royalblue"], 2502["8B4513", "saddlebrown"], 2503["FA8072", "salmon"], 2504["F4A460", "sandybrown"], 2505["2E8B57", "seagreen"], 2506["FFF5EE", "seashell"], 2507["A0522D", "sienna"], 2508["C0C0C0", "silver"], 2509["87CEEB", "skyblue"], 2510["6A5ACD", "slateblue"], 2511["708090", "slategray"], 2512["708090", "slategrey"], 2513["FFFAFA", "snow"], 2514["00FF7F", "springgreen"], 2515["4682B4", "steelblue"], 2516["D2B48C", "tan"], 2517["008080", "teal"], 2518["D8BFD8", "thistle"], 2519["FF6347", "tomato"], 2520["40E0D0", "turquoise"], 2521["EE82EE", "violet"], 2522["F5DEB3", "wheat"], 2523["FFFFFF", "white"], 2524["F5F5F5", "whitesmoke"], 2525["FFFF00", "yellow"], 2526["9ACD32", "yellowgreen"], 2527 2528// Existing table 2529 2530["000000", "Black"], 2531["000080", "Navy Blue"], 2532["0000C8", "Dark Blue"], 2533["0000FF", "Blue"], 2534["000741", "Stratos"], 2535["001B1C", "Swamp"], 2536["002387", "Resolution Blue"], 2537["002900", "Deep Fir"], 2538["002E20", "Burnham"], 2539["002FA7", "International Klein Blue"], 2540["003153", "Prussian Blue"], 2541["003366", "Midnight Blue"], 2542["003399", "Smalt"], 2543["003532", "Deep Teal"], 2544["003E40", "Cyprus"], 2545["004620", "Kaitoke Green"], 2546["0047AB", "Cobalt"], 2547["004816", "Crusoe"], 2548["004950", "Sherpa Blue"], 2549["0056A7", "Endeavour"], 2550["00581A", "Camarone"], 2551["0066CC", "Science Blue"], 2552["0066FF", "Blue Ribbon"], 2553["00755E", "Tropical Rain Forest"], 2554["0076A3", "Allports"], 2555["007BA7", "Deep Cerulean"], 2556["007EC7", "Lochmara"], 2557["007FFF", "Azure Radiance"], 2558["008080", "Teal"], 2559["0095B6", "Bondi Blue"], 2560["009DC4", "Pacific Blue"], 2561["00A693", "Persian Green"], 2562["00A86B", "Jade"], 2563["00CC99", "Caribbean Green"], 2564["00CCCC", "Robin's Egg Blue"], 2565["00FF00", "Green"], 2566["00FF7F", "Spring Green"], 2567["00FFFF", "Cyan / Aqua"], 2568["010D1A", "Blue Charcoal"], 2569["011635", "Midnight"], 2570["011D13", "Holly"], 2571["012731", "Daintree"], 2572["01361C", "Cardin Green"], 2573["01371A", "County Green"], 2574["013E62", "Astronaut Blue"], 2575["013F6A", "Regal Blue"], 2576["014B43", "Aqua Deep"], 2577["015E85", "Orient"], 2578["016162", "Blue Stone"], 2579["016D39", "Fun Green"], 2580["01796F", "Pine Green"], 2581["017987", "Blue Lagoon"], 2582["01826B", "Deep Sea"], 2583["01A368", "Green Haze"], 2584["022D15", "English Holly"], 2585["02402C", "Sherwood Green"], 2586["02478E", "Congress Blue"], 2587["024E46", "Evening Sea"], 2588["026395", "Bahama Blue"], 2589["02866F", "Observatory"], 2590["02A4D3", "Cerulean"], 2591["03163C", "Tangaroa"], 2592["032B52", "Green Vogue"], 2593["036A6E", "Mosque"], 2594["041004", "Midnight Moss"], 2595["041322", "Black Pearl"], 2596["042E4C", "Blue Whale"], 2597["044022", "Zuccini"], 2598["044259", "Teal Blue"], 2599["051040", "Deep Cove"], 2600["051657", "Gulf Blue"], 2601["055989", "Venice Blue"], 2602["056F57", "Watercourse"], 2603["062A78", "Catalina Blue"], 2604["063537", "Tiber"], 2605["069B81", "Gossamer"], 2606["06A189", "Niagara"], 2607["073A50", "Tarawera"], 2608["080110", "Jaguar"], 2609["081910", "Black Bean"], 2610["082567", "Deep Sapphire"], 2611["088370", "Elf Green"], 2612["08E8DE", "Bright Turquoise"], 2613["092256", "Downriver"], 2614["09230F", "Palm Green"], 2615["09255D", "Madison"], 2616["093624", "Bottle Green"], 2617["095859", "Deep Sea Green"], 2618["097F4B", "Salem"], 2619["0A001C", "Black Russian"], 2620["0A480D", "Dark Fern"], 2621["0A6906", "Japanese Laurel"], 2622["0A6F75", "Atoll"], 2623["0B0B0B", "Cod Gray"], 2624["0B0F08", "Marshland"], 2625["0B1107", "Gordons Green"], 2626["0B1304", "Black Forest"], 2627["0B6207", "San Felix"], 2628["0BDA51", "Malachite"], 2629["0C0B1D", "Ebony"], 2630["0C0D0F", "Woodsmoke"], 2631["0C1911", "Racing Green"], 2632["0C7A79", "Surfie Green"], 2633["0C8990", "Blue Chill"], 2634["0D0332", "Black Rock"], 2635["0D1117", "Bunker"], 2636["0D1C19", "Aztec"], 2637["0D2E1C", "Bush"], 2638["0E0E18", "Cinder"], 2639["0E2A30", "Firefly"], 2640["0F2D9E", "Torea Bay"], 2641["10121D", "Vulcan"], 2642["101405", "Green Waterloo"], 2643["105852", "Eden"], 2644["110C6C", "Arapawa"], 2645["120A8F", "Ultramarine"], 2646["123447", "Elephant"], 2647["126B40", "Jewel"], 2648["130000", "Diesel"], 2649["130A06", "Asphalt"], 2650["13264D", "Blue Zodiac"], 2651["134F19", "Parsley"], 2652["140600", "Nero"], 2653["1450AA", "Tory Blue"], 2654["151F4C", "Bunting"], 2655["1560BD", "Denim"], 2656["15736B", "Genoa"], 2657["161928", "Mirage"], 2658["161D10", "Hunter Green"], 2659["162A40", "Big Stone"], 2660["163222", "Celtic"], 2661["16322C", "Timber Green"], 2662["163531", "Gable Green"], 2663["171F04", "Pine Tree"], 2664["175579", "Chathams Blue"], 2665["182D09", "Deep Forest Green"], 2666["18587A", "Blumine"], 2667["19330E", "Palm Leaf"], 2668["193751", "Nile Blue"], 2669["1959A8", "Fun Blue"], 2670["1A1A68", "Lucky Point"], 2671["1AB385", "Mountain Meadow"], 2672["1B0245", "Tolopea"], 2673["1B1035", "Haiti"], 2674["1B127B", "Deep Koamaru"], 2675["1B1404", "Acadia"], 2676["1B2F11", "Seaweed"], 2677["1B3162", "Biscay"], 2678["1B659D", "Matisse"], 2679["1C1208", "Crowshead"], 2680["1C1E13", "Rangoon Green"], 2681["1C39BB", "Persian Blue"], 2682["1C402E", "Everglade"], 2683["1C7C7D", "Elm"], 2684["1D6142", "Green Pea"], 2685["1E0F04", "Creole"], 2686["1E1609", "Karaka"], 2687["1E1708", "El Paso"], 2688["1E385B", "Cello"], 2689["1E433C", "Te Papa Green"], 2690["1E90FF", "Dodger Blue"], 2691["1E9AB0", "Eastern Blue"], 2692["1F120F", "Night Rider"], 2693["1FC2C2", "Java"], 2694["20208D", "Jacksons Purple"], 2695["202E54", "Cloud Burst"], 2696["204852", "Blue Dianne"], 2697["211A0E", "Eternity"], 2698["220878", "Deep Blue"], 2699["228B22", "Forest Green"], 2700["233418", "Mallard"], 2701["240A40", "Violet"], 2702["240C02", "Kilamanjaro"], 2703["242A1D", "Log Cabin"], 2704["242E16", "Black Olive"], 2705["24500F", "Green House"], 2706["251607", "Graphite"], 2707["251706", "Cannon Black"], 2708["251F4F", "Port Gore"], 2709["25272C", "Shark"], 2710["25311C", "Green Kelp"], 2711["2596D1", "Curious Blue"], 2712["260368", "Paua"], 2713["26056A", "Paris M"], 2714["261105", "Wood Bark"], 2715["261414", "Gondola"], 2716["262335", "Steel Gray"], 2717["26283B", "Ebony Clay"], 2718["273A81", "Bay of Many"], 2719["27504B", "Plantation"], 2720["278A5B", "Eucalyptus"], 2721["281E15", "Oil"], 2722["283A77", "Astronaut"], 2723["286ACD", "Mariner"], 2724["290C5E", "Violent Violet"], 2725["292130", "Bastille"], 2726["292319", "Zeus"], 2727["292937", "Charade"], 2728["297B9A", "Jelly Bean"], 2729["29AB87", "Jungle Green"], 2730["2A0359", "Cherry Pie"], 2731["2A140E", "Coffee Bean"], 2732["2A2630", "Baltic Sea"], 2733["2A380B", "Turtle Green"], 2734["2A52BE", "Cerulean Blue"], 2735["2B0202", "Sepia Black"], 2736["2B194F", "Valhalla"], 2737["2B3228", "Heavy Metal"], 2738["2C0E8C", "Blue Gem"], 2739["2C1632", "Revolver"], 2740["2C2133", "Bleached Cedar"], 2741["2C8C84", "Lochinvar"], 2742["2D2510", "Mikado"], 2743["2D383A", "Outer Space"], 2744["2D569B", "St Tropaz"], 2745["2E0329", "Jacaranda"], 2746["2E1905", "Jacko Bean"], 2747["2E3222", "Rangitoto"], 2748["2E3F62", "Rhino"], 2749["2E8B57", "Sea Green"], 2750["2EBFD4", "Scooter"], 2751["2F270E", "Onion"], 2752["2F3CB3", "Governor Bay"], 2753["2F519E", "Sapphire"], 2754["2F5A57", "Spectra"], 2755["2F6168", "Casal"], 2756["300529", "Melanzane"], 2757["301F1E", "Cocoa Brown"], 2758["302A0F", "Woodrush"], 2759["304B6A", "San Juan"], 2760["30D5C8", "Turquoise"], 2761["311C17", "Eclipse"], 2762["314459", "Pickled Bluewood"], 2763["315BA1", "Azure"], 2764["31728D", "Calypso"], 2765["317D82", "Paradiso"], 2766["32127A", "Persian Indigo"], 2767["32293A", "Blackcurrant"], 2768["323232", "Mine Shaft"], 2769["325D52", "Stromboli"], 2770["327C14", "Bilbao"], 2771["327DA0", "Astral"], 2772["33036B", "Christalle"], 2773["33292F", "Thunder"], 2774["33CC99", "Shamrock"], 2775["341515", "Tamarind"], 2776["350036", "Mardi Gras"], 2777["350E42", "Valentino"], 2778["350E57", "Jagger"], 2779["353542", "Tuna"], 2780["354E8C", "Chambray"], 2781["363050", "Martinique"], 2782["363534", "Tuatara"], 2783["363C0D", "Waiouru"], 2784["36747D", "Ming"], 2785["368716", "La Palma"], 2786["370202", "Chocolate"], 2787["371D09", "Clinker"], 2788["37290E", "Brown Tumbleweed"], 2789["373021", "Birch"], 2790["377475", "Oracle"], 2791["380474", "Blue Diamond"], 2792["381A51", "Grape"], 2793["383533", "Dune"], 2794["384555", "Oxford Blue"], 2795["384910", "Clover"], 2796["394851", "Limed Spruce"], 2797["396413", "Dell"], 2798["3A0020", "Toledo"], 2799["3A2010", "Sambuca"], 2800["3A2A6A", "Jacarta"], 2801["3A686C", "William"], 2802["3A6A47", "Killarney"], 2803["3AB09E", "Keppel"], 2804["3B000B", "Temptress"], 2805["3B0910", "Aubergine"], 2806["3B1F1F", "Jon"], 2807["3B2820", "Treehouse"], 2808["3B7A57", "Amazon"], 2809["3B91B4", "Boston Blue"], 2810["3C0878", "Windsor"], 2811["3C1206", "Rebel"], 2812["3C1F76", "Meteorite"], 2813["3C2005", "Dark Ebony"], 2814["3C3910", "Camouflage"], 2815["3C4151", "Bright Gray"], 2816["3C4443", "Cape Cod"], 2817["3C493A", "Lunar Green"], 2818["3D0C02", "Bean "], 2819["3D2B1F", "Bistre"], 2820["3D7D52", "Goblin"], 2821["3E0480", "Kingfisher Daisy"], 2822["3E1C14", "Cedar"], 2823["3E2B23", "English Walnut"], 2824["3E2C1C", "Black Marlin"], 2825["3E3A44", "Ship Gray"], 2826["3EABBF", "Pelorous"], 2827["3F2109", "Bronze"], 2828["3F2500", "Cola"], 2829["3F3002", "Madras"], 2830["3F307F", "Minsk"], 2831["3F4C3A", "Cabbage Pont"], 2832["3F583B", "Tom Thumb"], 2833["3F5D53", "Mineral Green"], 2834["3FC1AA", "Puerto Rico"], 2835["3FFF00", "Harlequin"], 2836["401801", "Brown Pod"], 2837["40291D", "Cork"], 2838["403B38", "Masala"], 2839["403D19", "Thatch Green"], 2840["405169", "Fiord"], 2841["40826D", "Viridian"], 2842["40A860", "Chateau Green"], 2843["410056", "Ripe Plum"], 2844["411F10", "Paco"], 2845["412010", "Deep Oak"], 2846["413C37", "Merlin"], 2847["414257", "Gun Powder"], 2848["414C7D", "East Bay"], 2849["4169E1", "Royal Blue"], 2850["41AA78", "Ocean Green"], 2851["420303", "Burnt Maroon"], 2852["423921", "Lisbon Brown"], 2853["427977", "Faded Jade"], 2854["431560", "Scarlet Gum"], 2855["433120", "Iroko"], 2856["433E37", "Armadillo"], 2857["434C59", "River Bed"], 2858["436A0D", "Green Leaf"], 2859["44012D", "Barossa"], 2860["441D00", "Morocco Brown"], 2861["444954", "Mako"], 2862["454936", "Kelp"], 2863["456CAC", "San Marino"], 2864["45B1E8", "Picton Blue"], 2865["460B41", "Loulou"], 2866["462425", "Crater Brown"], 2867["465945", "Gray Asparagus"], 2868["4682B4", "Steel Blue"], 2869["480404", "Rustic Red"], 2870["480607", "Bulgarian Rose"], 2871["480656", "Clairvoyant"], 2872["481C1C", "Cocoa Bean"], 2873["483131", "Woody Brown"], 2874["483C32", "Taupe"], 2875["49170C", "Van Cleef"], 2876["492615", "Brown Derby"], 2877["49371B", "Metallic Bronze"], 2878["495400", "Verdun Green"], 2879["496679", "Blue Bayoux"], 2880["497183", "Bismark"], 2881["4A2A04", "Bracken"], 2882["4A3004", "Deep Bronze"], 2883["4A3C30", "Mondo"], 2884["4A4244", "Tundora"], 2885["4A444B", "Gravel"], 2886["4A4E5A", "Trout"], 2887["4B0082", "Pigment Indigo"], 2888["4B5D52", "Nandor"], 2889["4C3024", "Saddle"], 2890["4C4F56", "Abbey"], 2891["4D0135", "Blackberry"], 2892["4D0A18", "Cab Sav"], 2893["4D1E01", "Indian Tan"], 2894["4D282D", "Cowboy"], 2895["4D282E", "Livid Brown"], 2896["4D3833", "Rock"], 2897["4D3D14", "Punga"], 2898["4D400F", "Bronzetone"], 2899["4D5328", "Woodland"], 2900["4E0606", "Mahogany"], 2901["4E2A5A", "Bossanova"], 2902["4E3B41", "Matterhorn"], 2903["4E420C", "Bronze Olive"], 2904["4E4562", "Mulled Wine"], 2905["4E6649", "Axolotl"], 2906["4E7F9E", "Wedgewood"], 2907["4EABD1", "Shakespeare"], 2908["4F1C70", "Honey Flower"], 2909["4F2398", "Daisy Bush"], 2910["4F69C6", "Indigo"], 2911["4F7942", "Fern Green"], 2912["4F9D5D", "Fruit Salad"], 2913["4FA83D", "Apple"], 2914["504351", "Mortar"], 2915["507096", "Kashmir Blue"], 2916["507672", "Cutty Sark"], 2917["50C878", "Emerald"], 2918["514649", "Emperor"], 2919["516E3D", "Chalet Green"], 2920["517C66", "Como"], 2921["51808F", "Smalt Blue"], 2922["52001F", "Castro"], 2923["520C17", "Maroon Oak"], 2924["523C94", "Gigas"], 2925["533455", "Voodoo"], 2926["534491", "Victoria"], 2927["53824B", "Hippie Green"], 2928["541012", "Heath"], 2929["544333", "Judge Gray"], 2930["54534D", "Fuscous Gray"], 2931["549019", "Vida Loca"], 2932["55280C", "Cioccolato"], 2933["555B10", "Saratoga"], 2934["556D56", "Finlandia"], 2935["5590D9", "Havelock Blue"], 2936["56B4BE", "Fountain Blue"], 2937["578363", "Spring Leaves"], 2938["583401", "Saddle Brown"], 2939["585562", "Scarpa Flow"], 2940["587156", "Cactus"], 2941["589AAF", "Hippie Blue"], 2942["591D35", "Wine Berry"], 2943["592804", "Brown Bramble"], 2944["593737", "Congo Brown"], 2945["594433", "Millbrook"], 2946["5A6E9C", "Waikawa Gray"], 2947["5A87A0", "Horizon"], 2948["5B3013", "Jambalaya"], 2949["5C0120", "Bordeaux"], 2950["5C0536", "Mulberry Wood"], 2951["5C2E01", "Carnaby Tan"], 2952["5C5D75", "Comet"], 2953["5D1E0F", "Redwood"], 2954["5D4C51", "Don Juan"], 2955["5D5C58", "Chicago"], 2956["5D5E37", "Verdigris"], 2957["5D7747", "Dingley"], 2958["5DA19F", "Breaker Bay"], 2959["5E483E", "Kabul"], 2960["5E5D3B", "Hemlock"], 2961["5F3D26", "Irish Coffee"], 2962["5F5F6E", "Mid Gray"], 2963["5F6672", "Shuttle Gray"], 2964["5FA777", "Aqua Forest"], 2965["5FB3AC", "Tradewind"], 2966["604913", "Horses Neck"], 2967["605B73", "Smoky"], 2968["606E68", "Corduroy"], 2969["6093D1", "Danube"], 2970["612718", "Espresso"], 2971["614051", "Eggplant"], 2972["615D30", "Costa Del Sol"], 2973["61845F", "Glade Green"], 2974["622F30", "Buccaneer"], 2975["623F2D", "Quincy"], 2976["624E9A", "Butterfly Bush"], 2977["625119", "West Coast"], 2978["626649", "Finch"], 2979["639A8F", "Patina"], 2980["63B76C", "Fern"], 2981["6456B7", "Blue Violet"], 2982["646077", "Dolphin"], 2983["646463", "Storm Dust"], 2984["646A54", "Siam"], 2985["646E75", "Nevada"], 2986["6495ED", "Cornflower Blue"], 2987["64CCDB", "Viking"], 2988["65000B", "Rosewood"], 2989["651A14", "Cherrywood"], 2990["652DC1", "Purple Heart"], 2991["657220", "Fern Frond"], 2992["65745D", "Willow Grove"], 2993["65869F", "Hoki"], 2994["660045", "Pompadour"], 2995["660099", "Purple"], 2996["66023C", "Tyrian Purple"], 2997["661010", "Dark Tan"], 2998["66B58F", "Silver Tree"], 2999["66FF00", "Bright Green"], 3000["66FF66", "Screamin' Green"], 3001["67032D", "Black Rose"], 3002["675FA6", "Scampi"], 3003["676662", "Ironside Gray"], 3004["678975", "Viridian Green"], 3005["67A712", "Christi"], 3006["683600", "Nutmeg Wood Finish"], 3007["685558", "Zambezi"], 3008["685E6E", "Salt Box"], 3009["692545", "Tawny Port"], 3010["692D54", "Finn"], 3011["695F62", "Scorpion"], 3012["697E9A", "Lynch"], 3013["6A442E", "Spice"], 3014["6A5D1B", "Himalaya"], 3015["6A6051", "Soya Bean"], 3016["6B2A14", "Hairy Heath"], 3017["6B3FA0", "Royal Purple"], 3018["6B4E31", "Shingle Fawn"], 3019["6B5755", "Dorado"], 3020["6B8BA2", "Bermuda Gray"], 3021["6B8E23", "Olive Drab"], 3022["6C3082", "Eminence"], 3023["6CDAE7", "Turquoise Blue"], 3024["6D0101", "Lonestar"], 3025["6D5E54", "Pine Cone"], 3026["6D6C6C", "Dove Gray"], 3027["6D9292", "Juniper"], 3028["6D92A1", "Gothic"], 3029["6E0902", "Red Oxide"], 3030["6E1D14", "Moccaccino"], 3031["6E4826", "Pickled Bean"], 3032["6E4B26", "Dallas"], 3033["6E6D57", "Kokoda"], 3034["6E7783", "Pale Sky"], 3035["6F440C", "Cafe Royale"], 3036["6F6A61", "Flint"], 3037["6F8E63", "Highland"], 3038["6F9D02", "Limeade"], 3039["6FD0C5", "Downy"], 3040["701C1C", "Persian Plum"], 3041["704214", "Sepia"], 3042["704A07", "Antique Bronze"], 3043["704F50", "Ferra"], 3044["706555", "Coffee"], 3045["708090", "Slate Gray"], 3046["711A00", "Cedar Wood Finish"], 3047["71291D", "Metallic Copper"], 3048["714693", "Affair"], 3049["714AB2", "Studio"], 3050["715D47", "Tobacco Brown"], 3051["716338", "Yellow Metal"], 3052["716B56", "Peat"], 3053["716E10", "Olivetone"], 3054["717486", "Storm Gray"], 3055["718080", "Sirocco"], 3056["71D9E2", "Aquamarine Blue"], 3057["72010F", "Venetian Red"], 3058["724A2F", "Old Copper"], 3059["726D4E", "Go Ben"], 3060["727B89", "Raven"], 3061["731E8F", "Seance"], 3062["734A12", "Raw Umber"], 3063["736C9F", "Kimberly"], 3064["736D58", "Crocodile"], 3065["737829", "Crete"], 3066["738678", "Xanadu"], 3067["74640D", "Spicy Mustard"], 3068["747D63", "Limed Ash"], 3069["747D83", "Rolling Stone"], 3070["748881", "Blue Smoke"], 3071["749378", "Laurel"], 3072["74C365", "Mantis"], 3073["755A57", "Russett"], 3074["7563A8", "Deluge"], 3075["76395D", "Cosmic"], 3076["7666C6", "Blue Marguerite"], 3077["76BD17", "Lima"], 3078["76D7EA", "Sky Blue"], 3079["770F05", "Dark Burgundy"], 3080["771F1F", "Crown of Thorns"], 3081["773F1A", "Walnut"], 3082["776F61", "Pablo"], 3083["778120", "Pacifika"], 3084["779E86", "Oxley"], 3085["77DD77", "Pastel Green"], 3086["780109", "Japanese Maple"], 3087["782D19", "Mocha"], 3088["782F16", "Peanut"], 3089["78866B", "Camouflage Green"], 3090["788A25", "Wasabi"], 3091["788BBA", "Ship Cove"], 3092["78A39C", "Sea Nymph"], 3093["795D4C", "Roman Coffee"], 3094["796878", "Old Lavender"], 3095["796989", "Rum"], 3096["796A78", "Fedora"], 3097["796D62", "Sandstone"], 3098["79DEEC", "Spray"], 3099["7A013A", "Siren"], 3100["7A58C1", "Fuchsia Blue"], 3101["7A7A7A", "Boulder"], 3102["7A89B8", "Wild Blue Yonder"], 3103["7AC488", "De York"], 3104["7B3801", "Red Beech"], 3105["7B3F00", "Cinnamon"], 3106["7B6608", "Yukon Gold"], 3107["7B7874", "Tapa"], 3108["7B7C94", "Waterloo "], 3109["7B8265", "Flax Smoke"], 3110["7B9F80", "Amulet"], 3111["7BA05B", "Asparagus"], 3112["7C1C05", "Kenyan Copper"], 3113["7C7631", "Pesto"], 3114["7C778A", "Topaz"], 3115["7C7B7A", "Concord"], 3116["7C7B82", "Jumbo"], 3117["7C881A", "Trendy Green"], 3118["7CA1A6", "Gumbo"], 3119["7CB0A1", "Acapulco"], 3120["7CB7BB", "Neptune"], 3121["7D2C14", "Pueblo"], 3122["7DA98D", "Bay Leaf"], 3123["7DC8F7", "Malibu"], 3124["7DD8C6", "Bermuda"], 3125["7E3A15", "Copper Canyon"], 3126["7F1734", "Claret"], 3127["7F3A02", "Peru Tan"], 3128["7F626D", "Falcon"], 3129["7F7589", "Mobster"], 3130["7F76D3", "Moody Blue"], 3131["7FFF00", "Chartreuse"], 3132["7FFFD4", "Aquamarine"], 3133["800000", "Maroon"], 3134["800B47", "Rose Bud Cherry"], 3135["801818", "Falu Red"], 3136["80341F", "Red Robin"], 3137["803790", "Vivid Violet"], 3138["80461B", "Russet"], 3139["807E79", "Friar Gray"], 3140["808000", "Olive"], 3141["808080", "Gray"], 3142["80B3AE", "Gulf Stream"], 3143["80B3C4", "Glacier"], 3144["80CCEA", "Seagull"], 3145["81422C", "Nutmeg"], 3146["816E71", "Spicy Pink"], 3147["817377", "Empress"], 3148["819885", "Spanish Green"], 3149["826F65", "Sand Dune"], 3150["828685", "Gunsmoke"], 3151["828F72", "Battleship Gray"], 3152["831923", "Merlot"], 3153["837050", "Shadow"], 3154["83AA5D", "Chelsea Cucumber"], 3155["83D0C6", "Monte Carlo"], 3156["843179", "Plum"], 3157["84A0A0", "Granny Smith"], 3158["8581D9", "Chetwode Blue"], 3159["858470", "Bandicoot"], 3160["859FAF", "Bali Hai"], 3161["85C4CC", "Half Baked"], 3162["860111", "Red Devil"], 3163["863C3C", "Lotus"], 3164["86483C", "Ironstone"], 3165["864D1E", "Bull Shot"], 3166["86560A", "Rusty Nail"], 3167["868974", "Bitter"], 3168["86949F", "Regent Gray"], 3169["871550", "Disco"], 3170["87756E", "Americano"], 3171["877C7B", "Hurricane"], 3172["878D91", "Oslo Gray"], 3173["87AB39", "Sushi"], 3174["885342", "Spicy Mix"], 3175["886221", "Kumera"], 3176["888387", "Suva Gray"], 3177["888D65", "Avocado"], 3178["893456", "Camelot"], 3179["893843", "Solid Pink"], 3180["894367", "Cannon Pink"], 3181["897D6D", "Makara"], 3182["8A3324", "Burnt Umber"], 3183["8A73D6", "True V"], 3184["8A8360", "Clay Creek"], 3185["8A8389", "Monsoon"], 3186["8A8F8A", "Stack"], 3187["8AB9F1", "Jordy Blue"], 3188["8B00FF", "Electric Violet"], 3189["8B0723", "Monarch"], 3190["8B6B0B", "Corn Harvest"], 3191["8B8470", "Olive Haze"], 3192["8B847E", "Schooner"], 3193["8B8680", "Natural Gray"], 3194["8B9C90", "Mantle"], 3195["8B9FEE", "Portage"], 3196["8BA690", "Envy"], 3197["8BA9A5", "Cascade"], 3198["8BE6D8", "Riptide"], 3199["8C055E", "Cardinal Pink"], 3200["8C472F", "Mule Fawn"], 3201["8C5738", "Potters Clay"], 3202["8C6495", "Trendy Pink"], 3203["8D0226", "Paprika"], 3204["8D3D38", "Sanguine Brown"], 3205["8D3F3F", "Tosca"], 3206["8D7662", "Cement"], 3207["8D8974", "Granite Green"], 3208["8D90A1", "Manatee"], 3209["8DA8CC", "Polo Blue"], 3210["8E0000", "Red Berry"], 3211["8E4D1E", "Rope"], 3212["8E6F70", "Opium"], 3213["8E775E", "Domino"], 3214["8E8190", "Mamba"], 3215["8EABC1", "Nepal"], 3216["8F021C", "Pohutukawa"], 3217["8F3E33", "El Salva"], 3218["8F4B0E", "Korma"], 3219["8F8176", "Squirrel"], 3220["8FD6B4", "Vista Blue"], 3221["900020", "Burgundy"], 3222["901E1E", "Old Brick"], 3223["907874", "Hemp"], 3224["907B71", "Almond Frost"], 3225["908D39", "Sycamore"], 3226["92000A", "Sangria"], 3227["924321", "Cumin"], 3228["926F5B", "Beaver"], 3229["928573", "Stonewall"], 3230["928590", "Venus"], 3231["9370DB", "Medium Purple"], 3232["93CCEA", "Cornflower"], 3233["93DFB8", "Algae Green"], 3234["944747", "Copper Rust"], 3235["948771", "Arrowtown"], 3236["950015", "Scarlett"], 3237["956387", "Strikemaster"], 3238["959396", "Mountain Mist"], 3239["960018", "Carmine"], 3240["964B00", "Brown"], 3241["967059", "Leather"], 3242["9678B6", "Purple Mountain's Majesty"], 3243["967BB6", "Lavender Purple"], 3244["96A8A1", "Pewter"], 3245["96BBAB", "Summer Green"], 3246["97605D", "Au Chico"], 3247["9771B5", "Wisteria"], 3248["97CD2D", "Atlantis"], 3249["983D61", "Vin Rouge"], 3250["9874D3", "Lilac Bush"], 3251["98777B", "Bazaar"], 3252["98811B", "Hacienda"], 3253["988D77", "Pale Oyster"], 3254["98FF98", "Mint Green"], 3255["990066", "Fresh Eggplant"], 3256["991199", "Violet Eggplant"], 3257["991613", "Tamarillo"], 3258["991B07", "Totem Pole"], 3259["996666", "Copper Rose"], 3260["9966CC", "Amethyst"], 3261["997A8D", "Mountbatten Pink"], 3262["9999CC", "Blue Bell"], 3263["9A3820", "Prairie Sand"], 3264["9A6E61", "Toast"], 3265["9A9577", "Gurkha"], 3266["9AB973", "Olivine"], 3267["9AC2B8", "Shadow Green"], 3268["9B4703", "Oregon"], 3269["9B9E8F", "Lemon Grass"], 3270["9C3336", "Stiletto"], 3271["9D5616", "Hawaiian Tan"], 3272["9DACB7", "Gull Gray"], 3273["9DC209", "Pistachio"], 3274["9DE093", "Granny Smith Apple"], 3275["9DE5FF", "Anakiwa"], 3276["9E5302", "Chelsea Gem"], 3277["9E5B40", "Sepia Skin"], 3278["9EA587", "Sage"], 3279["9EA91F", "Citron"], 3280["9EB1CD", "Rock Blue"], 3281["9EDEE0", "Morning Glory"], 3282["9F381D", "Cognac"], 3283["9F821C", "Reef Gold"], 3284["9F9F9C", "Star Dust"], 3285["9FA0B1", "Santas Gray"], 3286["9FD7D3", "Sinbad"], 3287["9FDD8C", "Feijoa"], 3288["A02712", "Tabasco"], 3289["A1750D", "Buttered Rum"], 3290["A1ADB5", "Hit Gray"], 3291["A1C50A", "Citrus"], 3292["A1DAD7", "Aqua Island"], 3293["A1E9DE", "Water Leaf"], 3294["A2006D", "Flirt"], 3295["A23B6C", "Rouge"], 3296["A26645", "Cape Palliser"], 3297["A2AAB3", "Gray Chateau"], 3298["A2AEAB", "Edward"], 3299["A3807B", "Pharlap"], 3300["A397B4", "Amethyst Smoke"], 3301["A3E3ED", "Blizzard Blue"], 3302["A4A49D", "Delta"], 3303["A4A6D3", "Wistful"], 3304["A4AF6E", "Green Smoke"], 3305["A50B5E", "Jazzberry Jam"], 3306["A59B91", "Zorba"], 3307["A5CB0C", "Bahia"], 3308["A62F20", "Roof Terracotta"], 3309["A65529", "Paarl"], 3310["A68B5B", "Barley Corn"], 3311["A69279", "Donkey Brown"], 3312["A6A29A", "Dawn"], 3313["A72525", "Mexican Red"], 3314["A7882C", "Luxor Gold"], 3315["A85307", "Rich Gold"], 3316["A86515", "Reno Sand"], 3317["A86B6B", "Coral Tree"], 3318["A8989B", "Dusty Gray"], 3319["A899E6", "Dull Lavender"], 3320["A8A589", "Tallow"], 3321["A8AE9C", "Bud"], 3322["A8AF8E", "Locust"], 3323["A8BD9F", "Norway"], 3324["A8E3BD", "Chinook"], 3325["A9A491", "Gray Olive"], 3326["A9ACB6", "Aluminium"], 3327["A9B2C3", "Cadet Blue"], 3328["A9B497", "Schist"], 3329["A9BDBF", "Tower Gray"], 3330["A9BEF2", "Perano"], 3331["A9C6C2", "Opal"], 3332["AA375A", "Night Shadz"], 3333["AA4203", "Fire"], 3334["AA8B5B", "Muesli"], 3335["AA8D6F", "Sandal"], 3336["AAA5A9", "Shady Lady"], 3337["AAA9CD", "Logan"], 3338["AAABB7", "Spun Pearl"], 3339["AAD6E6", "Regent St Blue"], 3340["AAF0D1", "Magic Mint"], 3341["AB0563", "Lipstick"], 3342["AB3472", "Royal Heath"], 3343["AB917A", "Sandrift"], 3344["ABA0D9", "Cold Purple"], 3345["ABA196", "Bronco"], 3346["AC8A56", "Limed Oak"], 3347["AC91CE", "East Side"], 3348["AC9E22", "Lemon Ginger"], 3349["ACA494", "Napa"], 3350["ACA586", "Hillary"], 3351["ACA59F", "Cloudy"], 3352["ACACAC", "Silver Chalice"], 3353["ACB78E", "Swamp Green"], 3354["ACCBB1", "Spring Rain"], 3355["ACDD4D", "Conifer"], 3356["ACE1AF", "Celadon"], 3357["AD781B", "Mandalay"], 3358["ADBED1", "Casper"], 3359["ADDFAD", "Moss Green"], 3360["ADE6C4", "Padua"], 3361["ADFF2F", "Green Yellow"], 3362["AE4560", "Hippie Pink"], 3363["AE6020", "Desert"], 3364["AE809E", "Bouquet"], 3365["AF4035", "Medium Carmine"], 3366["AF4D43", "Apple Blossom"], 3367["AF593E", "Brown Rust"], 3368["AF8751", "Driftwood"], 3369["AF8F2C", "Alpine"], 3370["AF9F1C", "Lucky"], 3371["AFA09E", "Martini"], 3372["AFB1B8", "Bombay"], 3373["AFBDD9", "Pigeon Post"], 3374["B04C6A", "Cadillac"], 3375["B05D54", "Matrix"], 3376["B05E81", "Tapestry"], 3377["B06608", "Mai Tai"], 3378["B09A95", "Del Rio"], 3379["B0E0E6", "Powder Blue"], 3380["B0E313", "Inch Worm"], 3381["B10000", "Bright Red"], 3382["B14A0B", "Vesuvius"], 3383["B1610B", "Pumpkin Skin"], 3384["B16D52", "Santa Fe"], 3385["B19461", "Teak"], 3386["B1E2C1", "Fringy Flower"], 3387["B1F4E7", "Ice Cold"], 3388["B20931", "Shiraz"], 3389["B2A1EA", "Biloba Flower"], 3390["B32D29", "Tall Poppy"], 3391["B35213", "Fiery Orange"], 3392["B38007", "Hot Toddy"], 3393["B3AF95", "Taupe Gray"], 3394["B3C110", "La Rioja"], 3395["B43332", "Well Read"], 3396["B44668", "Blush"], 3397["B4CFD3", "Jungle Mist"], 3398["B57281", "Turkish Rose"], 3399["B57EDC", "Lavender"], 3400["B5A27F", "Mongoose"], 3401["B5B35C", "Olive Green"], 3402["B5D2CE", "Jet Stream"], 3403["B5ECDF", "Cruise"], 3404["B6316C", "Hibiscus"], 3405["B69D98", "Thatch"], 3406["B6B095", "Heathered Gray"], 3407["B6BAA4", "Eagle"], 3408["B6D1EA", "Spindle"], 3409["B6D3BF", "Gum Leaf"], 3410["B7410E", "Rust"], 3411["B78E5C", "Muddy Waters"], 3412["B7A214", "Sahara"], 3413["B7A458", "Husk"], 3414["B7B1B1", "Nobel"], 3415["B7C3D0", "Heather"], 3416["B7F0BE", "Madang"], 3417["B81104", "Milano Red"], 3418["B87333", "Copper"], 3419["B8B56A", "Gimblet"], 3420["B8C1B1", "Green Spring"], 3421["B8C25D", "Celery"], 3422["B8E0F9", "Sail"], 3423["B94E48", "Chestnut"], 3424["B95140", "Crail"], 3425["B98D28", "Marigold"], 3426["B9C46A", "Wild Willow"], 3427["B9C8AC", "Rainee"], 3428["BA0101", "Guardsman Red"], 3429["BA450C", "Rock Spray"], 3430["BA6F1E", "Bourbon"], 3431["BA7F03", "Pirate Gold"], 3432["BAB1A2", "Nomad"], 3433["BAC7C9", "Submarine"], 3434["BAEEF9", "Charlotte"], 3435["BB3385", "Medium Red Violet"], 3436["BB8983", "Brandy Rose"], 3437["BBD009", "Rio Grande"], 3438["BBD7C1", "Surf"], 3439["BCC9C2", "Powder Ash"], 3440["BD5E2E", "Tuscany"], 3441["BD978E", "Quicksand"], 3442["BDB1A8", "Silk"], 3443["BDB2A1", "Malta"], 3444["BDB3C7", "Chatelle"], 3445["BDBBD7", "Lavender Gray"], 3446["BDBDC6", "French Gray"], 3447["BDC8B3", "Clay Ash"], 3448["BDC9CE", "Loblolly"], 3449["BDEDFD", "French Pass"], 3450["BEA6C3", "London Hue"], 3451["BEB5B7", "Pink Swan"], 3452["BEDE0D", "Fuego"], 3453["BF5500", "Rose of Sharon"], 3454["BFB8B0", "Tide"], 3455["BFBED8", "Blue Haze"], 3456["BFC1C2", "Silver Sand"], 3457["BFC921", "Key Lime Pie"], 3458["BFDBE2", "Ziggurat"], 3459["BFFF00", "Lime"], 3460["C02B18", "Thunderbird"], 3461["C04737", "Mojo"], 3462["C08081", "Old Rose"], 3463["C0C0C0", "Silver"], 3464["C0D3B9", "Pale Leaf"], 3465["C0D8B6", "Pixie Green"], 3466["C1440E", "Tia Maria"], 3467["C154C1", "Fuchsia Pink"], 3468["C1A004", "Buddha Gold"], 3469["C1B7A4", "Bison Hide"], 3470["C1BAB0", "Tea"], 3471["C1BECD", "Gray Suit"], 3472["C1D7B0", "Sprout"], 3473["C1F07C", "Sulu"], 3474["C26B03", "Indochine"], 3475["C2955D", "Twine"], 3476["C2BDB6", "Cotton Seed"], 3477["C2CAC4", "Pumice"], 3478["C2E8E5", "Jagged Ice"], 3479["C32148", "Maroon Flush"], 3480["C3B091", "Indian Khaki"], 3481["C3BFC1", "Pale Slate"], 3482["C3C3BD", "Gray Nickel"], 3483["C3CDE6", "Periwinkle Gray"], 3484["C3D1D1", "Tiara"], 3485["C3DDF9", "Tropical Blue"], 3486["C41E3A", "Cardinal"], 3487["C45655", "Fuzzy Wuzzy Brown"], 3488["C45719", "Orange Roughy"], 3489["C4C4BC", "Mist Gray"], 3490["C4D0B0", "Coriander"], 3491["C4F4EB", "Mint Tulip"], 3492["C54B8C", "Mulberry"], 3493["C59922", "Nugget"], 3494["C5994B", "Tussock"], 3495["C5DBCA", "Sea Mist"], 3496["C5E17A", "Yellow Green"], 3497["C62D42", "Brick Red"], 3498["C6726B", "Contessa"], 3499["C69191", "Oriental Pink"], 3500["C6A84B", "Roti"], 3501["C6C3B5", "Ash"], 3502["C6C8BD", "Kangaroo"], 3503["C6E610", "Las Palmas"], 3504["C7031E", "Monza"], 3505["C71585", "Red Violet"], 3506["C7BCA2", "Coral Reef"], 3507["C7C1FF", "Melrose"], 3508["C7C4BF", "Cloud"], 3509["C7C9D5", "Ghost"], 3510["C7CD90", "Pine Glade"], 3511["C7DDE5", "Botticelli"], 3512["C88A65", "Antique Brass"], 3513["C8A2C8", "Lilac"], 3514["C8A528", "Hokey Pokey"], 3515["C8AABF", "Lily"], 3516["C8B568", "Laser"], 3517["C8E3D7", "Edgewater"], 3518["C96323", "Piper"], 3519["C99415", "Pizza"], 3520["C9A0DC", "Light Wisteria"], 3521["C9B29B", "Rodeo Dust"], 3522["C9B35B", "Sundance"], 3523["C9B93B", "Earls Green"], 3524["C9C0BB", "Silver Rust"], 3525["C9D9D2", "Conch"], 3526["C9FFA2", "Reef"], 3527["C9FFE5", "Aero Blue"], 3528["CA3435", "Flush Mahogany"], 3529["CABB48", "Turmeric"], 3530["CADCD4", "Paris White"], 3531["CAE00D", "Bitter Lemon"], 3532["CAE6DA", "Skeptic"], 3533["CB8FA9", "Viola"], 3534["CBCAB6", "Foggy Gray"], 3535["CBD3B0", "Green Mist"], 3536["CBDBD6", "Nebula"], 3537["CC3333", "Persian Red"], 3538["CC5500", "Burnt Orange"], 3539["CC7722", "Ochre"], 3540["CC8899", "Puce"], 3541["CCCAA8", "Thistle Green"], 3542["CCCCFF", "Periwinkle"], 3543["CCFF00", "Electric Lime"], 3544["CD5700", "Tenn"], 3545["CD5C5C", "Chestnut Rose"], 3546["CD8429", "Brandy Punch"], 3547["CDF4FF", "Onahau"], 3548["CEB98F", "Sorrell Brown"], 3549["CEBABA", "Cold Turkey"], 3550["CEC291", "Yuma"], 3551["CEC7A7", "Chino"], 3552["CFA39D", "Eunry"], 3553["CFB53B", "Old Gold"], 3554["CFDCCF", "Tasman"], 3555["CFE5D2", "Surf Crest"], 3556["CFF9F3", "Humming Bird"], 3557["CFFAF4", "Scandal"], 3558["D05F04", "Red Stage"], 3559["D06DA1", "Hopbush"], 3560["D07D12", "Meteor"], 3561["D0BEF8", "Perfume"], 3562["D0C0E5", "Prelude"], 3563["D0F0C0", "Tea Green"], 3564["D18F1B", "Geebung"], 3565["D1BEA8", "Vanilla"], 3566["D1C6B4", "Soft Amber"], 3567["D1D2CA", "Celeste"], 3568["D1D2DD", "Mischka"], 3569["D1E231", "Pear"], 3570["D2691E", "Hot Cinnamon"], 3571["D27D46", "Raw Sienna"], 3572["D29EAA", "Careys Pink"], 3573["D2B48C", "Tan"], 3574["D2DA97", "Deco"], 3575["D2F6DE", "Blue Romance"], 3576["D2F8B0", "Gossip"], 3577["D3CBBA", "Sisal"], 3578["D3CDC5", "Swirl"], 3579["D47494", "Charm"], 3580["D4B6AF", "Clam Shell"], 3581["D4BF8D", "Straw"], 3582["D4C4A8", "Akaroa"], 3583["D4CD16", "Bird Flower"], 3584["D4D7D9", "Iron"], 3585["D4DFE2", "Geyser"], 3586["D4E2FC", "Hawkes Blue"], 3587["D54600", "Grenadier"], 3588["D591A4", "Can Can"], 3589["D59A6F", "Whiskey"], 3590["D5D195", "Winter Hazel"], 3591["D5F6E3", "Granny Apple"], 3592["D69188", "My Pink"], 3593["D6C562", "Tacha"], 3594["D6CEF6", "Moon Raker"], 3595["D6D6D1", "Quill Gray"], 3596["D6FFDB", "Snowy Mint"], 3597["D7837F", "New York Pink"], 3598["D7C498", "Pavlova"], 3599["D7D0FF", "Fog"], 3600["D84437", "Valencia"], 3601["D87C63", "Japonica"], 3602["D8BFD8", "Thistle"], 3603["D8C2D5", "Maverick"], 3604["D8FCFA", "Foam"], 3605["D94972", "Cabaret"], 3606["D99376", "Burning Sand"], 3607["D9B99B", "Cameo"], 3608["D9D6CF", "Timberwolf"], 3609["D9DCC1", "Tana"], 3610["D9E4F5", "Link Water"], 3611["D9F7FF", "Mabel"], 3612["DA3287", "Cerise"], 3613["DA5B38", "Flame Pea"], 3614["DA6304", "Bamboo"], 3615["DA6A41", "Red Damask"], 3616["DA70D6", "Orchid"], 3617["DA8A67", "Copperfield"], 3618["DAA520", "Golden Grass"], 3619["DAECD6", "Zanah"], 3620["DAF4F0", "Iceberg"], 3621["DAFAFF", "Oyster Bay"], 3622["DB5079", "Cranberry"], 3623["DB9690", "Petite Orchid"], 3624["DB995E", "Di Serria"], 3625["DBDBDB", "Alto"], 3626["DBFFF8", "Frosted Mint"], 3627["DC143C", "Crimson"], 3628["DC4333", "Punch"], 3629["DCB20C", "Galliano"], 3630["DCB4BC", "Blossom"], 3631["DCD747", "Wattle"], 3632["DCD9D2", "Westar"], 3633["DCDDCC", "Moon Mist"], 3634["DCEDB4", "Caper"], 3635["DCF0EA", "Swans Down"], 3636["DDD6D5", "Swiss Coffee"], 3637["DDF9F1", "White Ice"], 3638["DE3163", "Cerise Red"], 3639["DE6360", "Roman"], 3640["DEA681", "Tumbleweed"], 3641["DEBA13", "Gold Tips"], 3642["DEC196", "Brandy"], 3643["DECBC6", "Wafer"], 3644["DED4A4", "Sapling"], 3645["DED717", "Barberry"], 3646["DEE5C0", "Beryl Green"], 3647["DEF5FF", "Pattens Blue"], 3648["DF73FF", "Heliotrope"], 3649["DFBE6F", "Apache"], 3650["DFCD6F", "Chenin"], 3651["DFCFDB", "Lola"], 3652["DFECDA", "Willow Brook"], 3653["DFFF00", "Chartreuse Yellow"], 3654["E0B0FF", "Mauve"], 3655["E0B646", "Anzac"], 3656["E0B974", "Harvest Gold"], 3657["E0C095", "Calico"], 3658["E0FFFF", "Baby Blue"], 3659["E16865", "Sunglo"], 3660["E1BC64", "Equator"], 3661["E1C0C8", "Pink Flare"], 3662["E1E6D6", "Periglacial Blue"], 3663["E1EAD4", "Kidnapper"], 3664["E1F6E8", "Tara"], 3665["E25465", "Mandy"], 3666["E2725B", "Terracotta"], 3667["E28913", "Golden Bell"], 3668["E292C0", "Shocking"], 3669["E29418", "Dixie"], 3670["E29CD2", "Light Orchid"], 3671["E2D8ED", "Snuff"], 3672["E2EBED", "Mystic"], 3673["E2F3EC", "Apple Green"], 3674["E30B5C", "Razzmatazz"], 3675["E32636", "Alizarin Crimson"], 3676["E34234", "Cinnabar"], 3677["E3BEBE", "Cavern Pink"], 3678["E3F5E1", "Peppermint"], 3679["E3F988", "Mindaro"], 3680["E47698", "Deep Blush"], 3681["E49B0F", "Gamboge"], 3682["E4C2D5", "Melanie"], 3683["E4CFDE", "Twilight"], 3684["E4D1C0", "Bone"], 3685["E4D422", "Sunflower"], 3686["E4D5B7", "Grain Brown"], 3687["E4D69B", "Zombie"], 3688["E4F6E7", "Frostee"], 3689["E4FFD1", "Snow Flurry"], 3690["E52B50", "Amaranth"], 3691["E5841B", "Zest"], 3692["E5CCC9", "Dust Storm"], 3693["E5D7BD", "Stark White"], 3694["E5D8AF", "Hampton"], 3695["E5E0E1", "Bon Jour"], 3696["E5E5E5", "Mercury"], 3697["E5F9F6", "Polar"], 3698["E64E03", "Trinidad"], 3699["E6BE8A", "Gold Sand"], 3700["E6BEA5", "Cashmere"], 3701["E6D7B9", "Double Spanish White"], 3702["E6E4D4", "Satin Linen"], 3703["E6F2EA", "Harp"], 3704["E6F8F3", "Off Green"], 3705["E6FFE9", "Hint of Green"], 3706["E6FFFF", "Tranquil"], 3707["E77200", "Mango Tango"], 3708["E7730A", "Christine"], 3709["E79F8C", "Tonys Pink"], 3710["E79FC4", "Kobi"], 3711["E7BCB4", "Rose Fog"], 3712["E7BF05", "Corn"], 3713["E7CD8C", "Putty"], 3714["E7ECE6", "Gray Nurse"], 3715["E7F8FF", "Lily White"], 3716["E7FEFF", "Bubbles"], 3717["E89928", "Fire Bush"], 3718["E8B9B3", "Shilo"], 3719["E8E0D5", "Pearl Bush"], 3720["E8EBE0", "Green White"], 3721["E8F1D4", "Chrome White"], 3722["E8F2EB", "Gin"], 3723["E8F5F2", "Aqua Squeeze"], 3724["E96E00", "Clementine"], 3725["E97451", "Burnt Sienna"], 3726["E97C07", "Tahiti Gold"], 3727["E9CECD", "Oyster Pink"], 3728["E9D75A", "Confetti"], 3729["E9E3E3", "Ebb"], 3730["E9F8ED", "Ottoman"], 3731["E9FFFD", "Clear Day"], 3732["EA88A8", "Carissma"], 3733["EAAE69", "Porsche"], 3734["EAB33B", "Tulip Tree"], 3735["EAC674", "Rob Roy"], 3736["EADAB8", "Raffia"], 3737["EAE8D4", "White Rock"], 3738["EAF6EE", "Panache"], 3739["EAF6FF", "Solitude"], 3740["EAF9F5", "Aqua Spring"], 3741["EAFFFE", "Dew"], 3742["EB9373", "Apricot"], 3743["EBC2AF", "Zinnwaldite"], 3744["ECA927", "Fuel Yellow"], 3745["ECC54E", "Ronchi"], 3746["ECC7EE", "French Lilac"], 3747["ECCDB9", "Just Right"], 3748["ECE090", "Wild Rice"], 3749["ECEBBD", "Fall Green"], 3750["ECEBCE", "Aths Special"], 3751["ECF245", "Starship"], 3752["ED0A3F", "Red Ribbon"], 3753["ED7A1C", "Tango"], 3754["ED9121", "Carrot Orange"], 3755["ED989E", "Sea Pink"], 3756["EDB381", "Tacao"], 3757["EDC9AF", "Desert Sand"], 3758["EDCDAB", "Pancho"], 3759["EDDCB1", "Chamois"], 3760["EDEA99", "Primrose"], 3761["EDF5DD", "Frost"], 3762["EDF5F5", "Aqua Haze"], 3763["EDF6FF", "Zumthor"], 3764["EDF9F1", "Narvik"], 3765["EDFC84", "Honeysuckle"], 3766["EE82EE", "Lavender Magenta"], 3767["EEC1BE", "Beauty Bush"], 3768["EED794", "Chalky"], 3769["EED9C4", "Almond"], 3770["EEDC82", "Flax"], 3771["EEDEDA", "Bizarre"], 3772["EEE3AD", "Double Colonial White"], 3773["EEEEE8", "Cararra"], 3774["EEEF78", "Manz"], 3775["EEF0C8", "Tahuna Sands"], 3776["EEF0F3", "Athens Gray"], 3777["EEF3C3", "Tusk"], 3778["EEF4DE", "Loafer"], 3779["EEF6F7", "Catskill White"], 3780["EEFDFF", "Twilight Blue"], 3781["EEFF9A", "Jonquil"], 3782["EEFFE2", "Rice Flower"], 3783["EF863F", "Jaffa"], 3784["EFEFEF", "Gallery"], 3785["EFF2F3", "Porcelain"], 3786["F091A9", "Mauvelous"], 3787["F0D52D", "Golden Dream"], 3788["F0DB7D", "Golden Sand"], 3789["F0DC82", "Buff"], 3790["F0E2EC", "Prim"], 3791["F0E68C", "Khaki"], 3792["F0EEFD", "Selago"], 3793["F0EEFF", "Titan White"], 3794["F0F8FF", "Alice Blue"], 3795["F0FCEA", "Feta"], 3796["F18200", "Gold Drop"], 3797["F19BAB", "Wewak"], 3798["F1E788", "Sahara Sand"], 3799["F1E9D2", "Parchment"], 3800["F1E9FF", "Blue Chalk"], 3801["F1EEC1", "Mint Julep"], 3802["F1F1F1", "Seashell"], 3803["F1F7F2", "Saltpan"], 3804["F1FFAD", "Tidal"], 3805["F1FFC8", "Chiffon"], 3806["F2552A", "Flamingo"], 3807["F28500", "Tangerine"], 3808["F2C3B2", "Mandys Pink"], 3809["F2F2F2", "Concrete"], 3810["F2FAFA", "Black Squeeze"], 3811["F34723", "Pomegranate"], 3812["F3AD16", "Buttercup"], 3813["F3D69D", "New Orleans"], 3814["F3D9DF", "Vanilla Ice"], 3815["F3E7BB", "Sidecar"], 3816["F3E9E5", "Dawn Pink"], 3817["F3EDCF", "Wheatfield"], 3818["F3FB62", "Canary"], 3819["F3FBD4", "Orinoco"], 3820["F3FFD8", "Carla"], 3821["F400A1", "Hollywood Cerise"], 3822["F4A460", "Sandy brown"], 3823["F4C430", "Saffron"], 3824["F4D81C", "Ripe Lemon"], 3825["F4EBD3", "Janna"], 3826["F4F2EE", "Pampas"], 3827["F4F4F4", "Wild Sand"], 3828["F4F8FF", "Zircon"], 3829["F57584", "Froly"], 3830["F5C85C", "Cream Can"], 3831["F5C999", "Manhattan"], 3832["F5D5A0", "Maize"], 3833["F5DEB3", "Wheat"], 3834["F5E7A2", "Sandwisp"], 3835["F5E7E2", "Pot Pourri"], 3836["F5E9D3", "Albescent White"], 3837["F5EDEF", "Soft Peach"], 3838["F5F3E5", "Ecru White"], 3839["F5F5DC", "Beige"], 3840["F5FB3D", "Golden Fizz"], 3841["F5FFBE", "Australian Mint"], 3842["F64A8A", "French Rose"], 3843["F653A6", "Brilliant Rose"], 3844["F6A4C9", "Illusion"], 3845["F6F0E6", "Merino"], 3846["F6F7F7", "Black Haze"], 3847["F6FFDC", "Spring Sun"], 3848["F7468A", "Violet Red"], 3849["F77703", "Chilean Fire"], 3850["F77FBE", "Persian Pink"], 3851["F7B668", "Rajah"], 3852["F7C8DA", "Azalea"], 3853["F7DBE6", "We Peep"], 3854["F7F2E1", "Quarter Spanish White"], 3855["F7F5FA", "Whisper"], 3856["F7FAF7", "Snow Drift"], 3857["F8B853", "Casablanca"], 3858["F8C3DF", "Chantilly"], 3859["F8D9E9", "Cherub"], 3860["F8DB9D", "Marzipan"], 3861["F8DD5C", "Energy Yellow"], 3862["F8E4BF", "Givry"], 3863["F8F0E8", "White Linen"], 3864["F8F4FF", "Magnolia"], 3865["F8F6F1", "Spring Wood"], 3866["F8F7DC", "Coconut Cream"], 3867["F8F7FC", "White Lilac"], 3868["F8F8F7", "Desert Storm"], 3869["F8F99C", "Texas"], 3870["F8FACD", "Corn Field"], 3871["F8FDD3", "Mimosa"], 3872["F95A61", "Carnation"], 3873["F9BF58", "Saffron Mango"], 3874["F9E0ED", "Carousel Pink"], 3875["F9E4BC", "Dairy Cream"], 3876["F9E663", "Portica"], 3877["F9EAF3", "Amour"], 3878["F9F8E4", "Rum Swizzle"], 3879["F9FF8B", "Dolly"], 3880["F9FFF6", "Sugar Cane"], 3881["FA7814", "Ecstasy"], 3882["FA9D5A", "Tan Hide"], 3883["FAD3A2", "Corvette"], 3884["FADFAD", "Peach Yellow"], 3885["FAE600", "Turbo"], 3886["FAEAB9", "Astra"], 3887["FAECCC", "Champagne"], 3888["FAF0E6", "Linen"], 3889["FAF3F0", "Fantasy"], 3890["FAF7D6", "Citrine White"], 3891["FAFAFA", "Alabaster"], 3892["FAFDE4", "Hint of Yellow"], 3893["FAFFA4", "Milan"], 3894["FB607F", "Brink Pink"], 3895["FB8989", "Geraldine"], 3896["FBA0E3", "Lavender Rose"], 3897["FBA129", "Sea Buckthorn"], 3898["FBAC13", "Sun"], 3899["FBAED2", "Lavender Pink"], 3900["FBB2A3", "Rose Bud"], 3901["FBBEDA", "Cupid"], 3902["FBCCE7", "Classic Rose"], 3903["FBCEB1", "Apricot Peach"], 3904["FBE7B2", "Banana Mania"], 3905["FBE870", "Marigold Yellow"], 3906["FBE96C", "Festival"], 3907["FBEA8C", "Sweet Corn"], 3908["FBEC5D", "Candy Corn"], 3909["FBF9F9", "Hint of Red"], 3910["FBFFBA", "Shalimar"], 3911["FC0FC0", "Shocking Pink"], 3912["FC80A5", "Tickle Me Pink"], 3913["FC9C1D", "Tree Poppy"], 3914["FCC01E", "Lightning Yellow"], 3915["FCD667", "Goldenrod"], 3916["FCD917", "Candlelight"], 3917["FCDA98", "Cherokee"], 3918["FCF4D0", "Double Pearl Lusta"], 3919["FCF4DC", "Pearl Lusta"], 3920["FCF8F7", "Vista White"], 3921["FCFBF3", "Bianca"], 3922["FCFEDA", "Moon Glow"], 3923["FCFFE7", "China Ivory"], 3924["FCFFF9", "Ceramic"], 3925["FD0E35", "Torch Red"], 3926["FD5B78", "Wild Watermelon"], 3927["FD7B33", "Crusta"], 3928["FD7C07", "Sorbus"], 3929["FD9FA2", "Sweet Pink"], 3930["FDD5B1", "Light Apricot"], 3931["FDD7E4", "Pig Pink"], 3932["FDE1DC", "Cinderella"], 3933["FDE295", "Golden Glow"], 3934["FDE910", "Lemon"], 3935["FDF5E6", "Old Lace"], 3936["FDF6D3", "Half Colonial White"], 3937["FDF7AD", "Drover"], 3938["FDFEB8", "Pale Prim"], 3939["FDFFD5", "Cumulus"], 3940["FE28A2", "Persian Rose"], 3941["FE4C40", "Sunset Orange"], 3942["FE6F5E", "Bittersweet"], 3943["FE9D04", "California"], 3944["FEA904", "Yellow Sea"], 3945["FEBAAD", "Melon"], 3946["FED33C", "Bright Sun"], 3947["FED85D", "Dandelion"], 3948["FEDB8D", "Salomie"], 3949["FEE5AC", "Cape Honey"], 3950["FEEBF3", "Remy"], 3951["FEEFCE", "Oasis"], 3952["FEF0EC", "Bridesmaid"], 3953["FEF2C7", "Beeswax"], 3954["FEF3D8", "Bleach White"], 3955["FEF4CC", "Pipi"], 3956["FEF4DB", "Half Spanish White"], 3957["FEF4F8", "Wisp Pink"], 3958["FEF5F1", "Provincial Pink"], 3959["FEF7DE", "Half Dutch White"], 3960["FEF8E2", "Solitaire"], 3961["FEF8FF", "White Pointer"], 3962["FEF9E3", "Off Yellow"], 3963["FEFCED", "Orange White"], 3964["FF0000", "Red"], 3965["FF007F", "Rose"], 3966["FF00CC", "Purple Pizzazz"], 3967["FF00FF", "Magenta / Fuchsia"], 3968["FF2400", "Scarlet"], 3969["FF3399", "Wild Strawberry"], 3970["FF33CC", "Razzle Dazzle Rose"], 3971["FF355E", "Radical Red"], 3972["FF3F34", "Red Orange"], 3973["FF4040", "Coral Red"], 3974["FF4D00", "Vermilion"], 3975["FF4F00", "International Orange"], 3976["FF6037", "Outrageous Orange"], 3977["FF6600", "Blaze Orange"], 3978["FF66FF", "Pink Flamingo"], 3979["FF681F", "Orange"], 3980["FF69B4", "Hot Pink"], 3981["FF6B53", "Persimmon"], 3982["FF6FFF", "Blush Pink"], 3983["FF7034", "Burning Orange"], 3984["FF7518", "Pumpkin"], 3985["FF7D07", "Flamenco"], 3986["FF7F00", "Flush Orange"], 3987["FF7F50", "Coral"], 3988["FF8C69", "Salmon"], 3989["FF9000", "Pizazz"], 3990["FF910F", "West Side"], 3991["FF91A4", "Pink Salmon"], 3992["FF9933", "Neon Carrot"], 3993["FF9966", "Atomic Tangerine"], 3994["FF9980", "Vivid Tangerine"], 3995["FF9E2C", "Sunshade"], 3996["FFA000", "Orange Peel"], 3997["FFA194", "Mona Lisa"], 3998["FFA500", "Web Orange"], 3999["FFA6C9", "Carnation Pink"], 4000["FFAB81", "Hit Pink"], 4001["FFAE42", "Yellow Orange"], 4002["FFB0AC", "Cornflower Lilac"], 4003["FFB1B3", "Sundown"], 4004["FFB31F", "My Sin"], 4005["FFB555", "Texas Rose"], 4006["FFB7D5", "Cotton Candy"], 4007["FFB97B", "Macaroni and Cheese"], 4008["FFBA00", "Selective Yellow"], 4009["FFBD5F", "Koromiko"], 4010["FFBF00", "Amber"], 4011["FFC0A8", "Wax Flower"], 4012["FFC0CB", "Pink"], 4013["FFC3C0", "Your Pink"], 4014["FFC901", "Supernova"], 4015["FFCBA4", "Flesh"], 4016["FFCC33", "Sunglow"], 4017["FFCC5C", "Golden Tainoi"], 4018["FFCC99", "Peach Orange"], 4019["FFCD8C", "Chardonnay"], 4020["FFD1DC", "Pastel Pink"], 4021["FFD2B7", "Romantic"], 4022["FFD38C", "Grandis"], 4023["FFD700", "Gold"], 4024["FFD800", "School bus Yellow"], 4025["FFD8D9", "Cosmos"], 4026["FFDB58", "Mustard"], 4027["FFDCD6", "Peach Schnapps"], 4028["FFDDAF", "Caramel"], 4029["FFDDCD", "Tuft Bush"], 4030["FFDDCF", "Watusi"], 4031["FFDDF4", "Pink Lace"], 4032["FFDEAD", "Navajo White"], 4033["FFDEB3", "Frangipani"], 4034["FFE1DF", "Pippin"], 4035["FFE1F2", "Pale Rose"], 4036["FFE2C5", "Negroni"], 4037["FFE5A0", "Cream Brulee"], 4038["FFE5B4", "Peach"], 4039["FFE6C7", "Tequila"], 4040["FFE772", "Kournikova"], 4041["FFEAC8", "Sandy Beach"], 4042["FFEAD4", "Karry"], 4043["FFEC13", "Broom"], 4044["FFEDBC", "Colonial White"], 4045["FFEED8", "Derby"], 4046["FFEFA1", "Vis Vis"], 4047["FFEFC1", "Egg White"], 4048["FFEFD5", "Papaya Whip"], 4049["FFEFEC", "Fair Pink"], 4050["FFF0DB", "Peach Cream"], 4051["FFF0F5", "Lavender blush"], 4052["FFF14F", "Gorse"], 4053["FFF1B5", "Buttermilk"], 4054["FFF1D8", "Pink Lady"], 4055["FFF1EE", "Forget Me Not"], 4056["FFF1F9", "Tutu"], 4057["FFF39D", "Picasso"], 4058["FFF3F1", "Chardon"], 4059["FFF46E", "Paris Daisy"], 4060["FFF4CE", "Barley White"], 4061["FFF4DD", "Egg Sour"], 4062["FFF4E0", "Sazerac"], 4063["FFF4E8", "Serenade"], 4064["FFF4F3", "Chablis"], 4065["FFF5EE", "Seashell Peach"], 4066["FFF5F3", "Sauvignon"], 4067["FFF6D4", "Milk Punch"], 4068["FFF6DF", "Varden"], 4069["FFF6F5", "Rose White"], 4070["FFF8D1", "Baja White"], 4071["FFF9E2", "Gin Fizz"], 4072["FFF9E6", "Early Dawn"], 4073["FFFACD", "Lemon Chiffon"], 4074["FFFAF4", "Bridal Heath"], 4075["FFFBDC", "Scotch Mist"], 4076["FFFBF9", "Soapstone"], 4077["FFFC99", "Witch Haze"], 4078["FFFCEA", "Buttery White"], 4079["FFFCEE", "Island Spice"], 4080["FFFDD0", "Cream"], 4081["FFFDE6", "Chilean Heath"], 4082["FFFDE8", "Travertine"], 4083["FFFDF3", "Orchid White"], 4084["FFFDF4", "Quarter Pearl Lusta"], 4085["FFFEE1", "Half and Half"], 4086["FFFEEC", "Apricot White"], 4087["FFFEF0", "Rice Cake"], 4088["FFFEF6", "Black White"], 4089["FFFEFD", "Romance"], 4090["FFFF00", "Yellow"], 4091["FFFF66", "Laser Lemon"], 4092["FFFF99", "Pale Canary"], 4093["FFFFB4", "Portafino"], 4094["FFFFF0", "Ivory"], 4095["FFFFFF", "White"] 4096] 4097 4098} 4099 4100ntc.init(); 4101