|  |  |  | (function() { | 
					
						
							|  |  |  |   var electron = require('electron'); | 
					
						
							|  |  |  |   var remote = electron.remote; | 
					
						
							|  |  |  |   var app = remote.app; | 
					
						
							|  |  |  |   var webFrame = electron.webFrame; | 
					
						
							|  |  |  |   var path = require('path'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var osLocale = require('os-locale'); | 
					
						
							|  |  |  |   var os = require('os'); | 
					
						
							|  |  |  |   var semver = require('semver'); | 
					
						
							|  |  |  |   var spellchecker = require('spellchecker'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // `remote.require` since `Menu` is a main-process module.
 | 
					
						
							|  |  |  |   var buildEditorContextMenu = remote.require('electron-editor-context-menu'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var EN_VARIANT = /^en/; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Prevent the spellchecker from showing contractions as errors.
 | 
					
						
							|  |  |  |   var ENGLISH_SKIP_WORDS = [ | 
					
						
							|  |  |  |     'ain', | 
					
						
							|  |  |  |     'couldn', | 
					
						
							|  |  |  |     'didn', | 
					
						
							|  |  |  |     'doesn', | 
					
						
							|  |  |  |     'hadn', | 
					
						
							|  |  |  |     'hasn', | 
					
						
							|  |  |  |     'mightn', | 
					
						
							|  |  |  |     'mustn', | 
					
						
							|  |  |  |     'needn', | 
					
						
							|  |  |  |     'oughtn', | 
					
						
							|  |  |  |     'shan', | 
					
						
							|  |  |  |     'shouldn', | 
					
						
							|  |  |  |     'wasn', | 
					
						
							|  |  |  |     'weren', | 
					
						
							|  |  |  |     'wouldn', | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function setupLinux(locale) { | 
					
						
							|  |  |  |     if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { | 
					
						
							|  |  |  |       // apt-get install hunspell-<locale> can be run for easy access to other dictionaries
 | 
					
						
							|  |  |  |       var location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'Detected Linux. Setting up spell check with locale', | 
					
						
							|  |  |  |         locale, | 
					
						
							|  |  |  |         'and dictionary location', | 
					
						
							|  |  |  |         location | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       spellchecker.setDictionary(locale, location); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       console.log('Detected Linux. Using default en_US spell check dictionary'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function setupWin7AndEarlier(locale) { | 
					
						
							|  |  |  |     if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { | 
					
						
							|  |  |  |       var location = process.env.HUNSPELL_DICTIONARIES; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'Detected Windows 7 or below. Setting up spell-check with locale', | 
					
						
							|  |  |  |         locale, | 
					
						
							|  |  |  |         'and dictionary location', | 
					
						
							|  |  |  |         location | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       spellchecker.setDictionary(locale, location); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'Detected Windows 7 or below. Using default en_US spell check dictionary' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // We load locale this way and not via app.getLocale() because this call returns
 | 
					
						
							|  |  |  |   //   'es_ES' and not just 'es.' And hunspell requires the fully-qualified locale.
 | 
					
						
							|  |  |  |   var locale = osLocale.sync().replace('-', '_'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The LANG environment variable is how node spellchecker finds its default language:
 | 
					
						
							|  |  |  |   //   https://github.com/atom/node-spellchecker/blob/59d2d5eee5785c4b34e9669cd5d987181d17c098/lib/spellchecker.js#L29
 | 
					
						
							|  |  |  |   if (!process.env.LANG) { | 
					
						
							|  |  |  |     process.env.LANG = locale; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (process.platform === 'linux') { | 
					
						
							|  |  |  |     setupLinux(locale); | 
					
						
							|  |  |  |   } else if ( | 
					
						
							|  |  |  |     process.platform === 'windows' && | 
					
						
							|  |  |  |     semver.lt(os.release(), '8.0.0') | 
					
						
							|  |  |  |   ) { | 
					
						
							|  |  |  |     setupWin7AndEarlier(locale); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // OSX and Windows 8+ have OS-level spellcheck APIs
 | 
					
						
							|  |  |  |     console.log('Using OS-level spell check API with locale', process.env.LANG); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var simpleChecker = (window.spellChecker = { | 
					
						
							|  |  |  |     spellCheck: function(text) { | 
					
						
							|  |  |  |       return !this.isMisspelled(text); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     isMisspelled: function(text) { | 
					
						
							|  |  |  |       var misspelled = spellchecker.isMisspelled(text); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // The idea is to make this as fast as possible. For the many, many calls which
 | 
					
						
							|  |  |  |       //   don't result in the red squiggly, we minimize the number of checks.
 | 
					
						
							|  |  |  |       if (!misspelled) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Only if we think we've found an error do we check the locale and skip list.
 | 
					
						
							|  |  |  |       if (locale.match(EN_VARIANT) && _.contains(ENGLISH_SKIP_WORDS, text)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     getSuggestions: function(text) { | 
					
						
							|  |  |  |       return spellchecker.getCorrectionsForMisspelling(text); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     add: function(text) { | 
					
						
							|  |  |  |       spellchecker.add(text); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   webFrame.setSpellCheckProvider( | 
					
						
							|  |  |  |     'en-US', | 
					
						
							|  |  |  |     // Not sure what this parameter (`autoCorrectWord`) does: https://github.com/atom/electron/issues/4371
 | 
					
						
							|  |  |  |     // The documentation for `webFrame.setSpellCheckProvider` passes `true` so we do too.
 | 
					
						
							|  |  |  |     true, | 
					
						
							|  |  |  |     simpleChecker | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   window.addEventListener('contextmenu', function(e) { | 
					
						
							|  |  |  |     // Only show the context menu in text editors.
 | 
					
						
							|  |  |  |     if (!e.target.closest('textarea, input, [contenteditable="true"]')) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var selectedText = window.getSelection().toString(); | 
					
						
							|  |  |  |     var isMisspelled = selectedText && simpleChecker.isMisspelled(selectedText); | 
					
						
							|  |  |  |     var spellingSuggestions = | 
					
						
							|  |  |  |       isMisspelled && simpleChecker.getSuggestions(selectedText).slice(0, 5); | 
					
						
							|  |  |  |     var menu = buildEditorContextMenu({ | 
					
						
							|  |  |  |       isMisspelled: isMisspelled, | 
					
						
							|  |  |  |       spellingSuggestions: spellingSuggestions, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The 'contextmenu' event is emitted after 'selectionchange' has fired but possibly before the
 | 
					
						
							|  |  |  |     // visible selection has changed. Try to wait to show the menu until after that, otherwise the
 | 
					
						
							|  |  |  |     // visible selection will update after the menu dismisses and look weird.
 | 
					
						
							|  |  |  |     setTimeout(function() { | 
					
						
							|  |  |  |       menu.popup(remote.getCurrentWindow()); | 
					
						
							|  |  |  |     }, 30); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | })(); |