├── LICENSE.md
├── README.md
├── autodict.js
├── icons
├── icon.png
└── icon@2x.png
├── lodash.throttle.js
└── manifest.json
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Jacob Bundgaard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Automatic Spell Checking Language Selection Extension for Firefox
2 | Automatically changes the dictionary used for spell checking based on the language of text in input fields.
3 | Uses the [Compact Language Detection framework](https://github.com/CLD2Owners/cld2) built into Firefox to determine the text language. No text or other data is sent to any external services.
4 | Icon by A’ Design Award & Competition, Onur Müştak Çobanlı and Farhat Datta through http://www.languageicon.org/
5 |
6 | Install from https://addons.mozilla.org/firefox/addon/automatic-spelling-language/
7 |
8 | ### Supported Languages
9 | The following languages [are supported](https://github.com/CLD2Owners/cld2#supported-languages):
10 | Afrikaans, Albanian, Arabic, Armenian, Azerbaijani, Basque, Belarusian, Bengali, Bihari, Bulgarian, Catalan, Cebuano, Cherokee, Croatian, Czech, Chinese (traditional and simplified), Danish, Dhivehi, Dutch, English, Estonian, Finnish, French, Galician, Ganda, Georgian, German, Greek, Gujarati, Haitian Creole, Hebrew, Hindi, Hmong, Hungarian, Icelandic, Indonesian, Inuktitut, Irish, Italian, Javanese, Japanese, Kannada, Khmer, Kinyarwanda, Korean, Laothian, Latvian, Limbu, Lithuanian, Macedonian, Malay, Malayalam, Maltese, Marathi, Nepali, Norwegian (bokmål), Oriya, Persian, Polish, Portuguese, Punjabi, Romanian, Russian, Scottish Gaelic, Serbian, Sinhalese, Slovak, Slovenian, Spanish, Swahili, Swedish, Syriac, Tagalog, Tamil, Telugu, Thai, Turkish, Ukrainian, Urdu, Vietnamese, Welsh, and Yiddish.
11 |
12 | ## Tested on sites
13 | - GitHub
14 | - Gmail
15 | - Reddit
16 | - Stack Exchange
17 | - Trello
18 | - Wikipedia
19 |
20 | ## Content Preferences
21 | If this extension doesn't work for you for a specific site, you probably have a content preference that overwrites the dictionary for that site.
22 | This content preference is automatically added if you manually set the dictionary for an input field on a site.
23 | In order to clear it, try manually setting the dictionary to the same language as the site, e.g., English for Reddit.
24 |
25 | If that doesn't work, you can manually clear all content preferences by deleting the `content-prefs.sqlite` in your profile directory.
26 | On Windows, this can be found in a subfolder of `%AppData%\Mozilla\Firefox\Profiles\`.
27 | Please note that this will also delete other content preferences you might have set for different sites, such as the zoom level and the default download folder.
28 |
--------------------------------------------------------------------------------
/autodict.js:
--------------------------------------------------------------------------------
1 | // Firefox doesn't change the dictionary until the element loses focus, then gains it again. This is a workaround.
2 | function forceDictionaryChange(node) {
3 | if (node.nodeName == "INPUT" || node.nodeName == "TEXTAREA") {
4 | node.spellcheck = false;
5 | node.spellcheck = true;
6 | } else {
7 | node.contentEditable = false;
8 | node.contentEditable = true;
9 | }
10 | }
11 |
12 | function getContent(node) {
13 | if (node.nodeName == "INPUT" || node.nodeName == "TEXTAREA") {
14 | return node.value;
15 | } else {
16 | return node.innerText;
17 | }
18 | }
19 |
20 | async function detectLanguage(event) {
21 | const node = event.target;
22 | const content = getContent(node);
23 |
24 | // console.log("Detecting language for", node, "with content", content)
25 | const detectedLanguage = await browser.i18n.detectLanguage(content);
26 | if (detectedLanguage.isReliable && detectedLanguage.languages.length > 0) {
27 | const language = detectedLanguage.languages[0].language;
28 | if (node.lang !== language) {
29 | //console.log("Setting language to", language, "for", node);
30 | node.lang = language;
31 | forceDictionaryChange(node);
32 | }
33 | }
34 | }
35 |
36 | const throttledDetectLanguages = _.throttle(detectLanguage, 2000);
37 |
38 | function detectChanges(node) {
39 | if(!node.spellcheck) {
40 | return;
41 | }
42 |
43 | if (!(node.nodeName === "INPUT"
44 | || node.nodeName === "TEXTAREA"
45 | || (node.nodeName === "DIV" && node.contentEditable === "true"))) {
46 | return;
47 | }
48 |
49 | // console.log("Adding listener for", node);
50 |
51 | node.addEventListener("input", throttledDetectLanguages);
52 |
53 | detectLanguage({ target: node });
54 | }
55 |
56 | function detectChildChanges(root) {
57 | if (!root.querySelectorAll) {
58 | return;
59 | }
60 |
61 | const nodes = root.querySelectorAll("input, textarea, div[contenteditable]");
62 | for (const node of nodes) {
63 | detectChanges(node);
64 | }
65 | }
66 |
67 | const observer = new MutationObserver(mutations => {
68 | for (const mutation of mutations) {
69 | switch(mutation.type) {
70 | case "attributes":
71 | detectChanges(mutation.target);
72 | break;
73 |
74 | case "childList":
75 | for (const newNode of mutation.addedNodes) {
76 | detectChanges(newNode);
77 | detectChildChanges(newNode);
78 | }
79 | break;
80 | }
81 | };
82 | });
83 | observer.observe(document.body, {
84 | childList: true,
85 | subtree: true,
86 | attributes: true,
87 | attributeFilter: ["contenteditable"]
88 | });
89 |
90 | detectChildChanges(document);
91 |
--------------------------------------------------------------------------------
/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kimsey0/FirefoxAutoDict/35300ee8ea87ac323e8b7e3fa69d09f61efe19bf/icons/icon.png
--------------------------------------------------------------------------------
/icons/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kimsey0/FirefoxAutoDict/35300ee8ea87ac323e8b7e3fa69d09f61efe19bf/icons/icon@2x.png
--------------------------------------------------------------------------------
/lodash.throttle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Lodash (Custom Build)
4 | * Build: `lodash include="throttle"`
5 | * Copyright JS Foundation and other contributors
6 | * Released under MIT license
7 | * Based on Underscore.js 1.8.3
8 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
9 | */
10 | ;(function() {
11 |
12 | /** Used as a safe reference for `undefined` in pre-ES5 environments. */
13 | var undefined;
14 |
15 | /** Used as the semantic version number. */
16 | var VERSION = '4.17.5';
17 |
18 | /** Error message constants. */
19 | var FUNC_ERROR_TEXT = 'Expected a function';
20 |
21 | /** Used as references for various `Number` constants. */
22 | var NAN = 0 / 0;
23 |
24 | /** `Object#toString` result references. */
25 | var nullTag = '[object Null]',
26 | symbolTag = '[object Symbol]',
27 | undefinedTag = '[object Undefined]';
28 |
29 | /** Used to match leading and trailing whitespace. */
30 | var reTrim = /^\s+|\s+$/g;
31 |
32 | /** Used to detect bad signed hexadecimal string values. */
33 | var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
34 |
35 | /** Used to detect binary string values. */
36 | var reIsBinary = /^0b[01]+$/i;
37 |
38 | /** Used to detect octal string values. */
39 | var reIsOctal = /^0o[0-7]+$/i;
40 |
41 | /** Built-in method references without a dependency on `root`. */
42 | var freeParseInt = parseInt;
43 |
44 | /** Detect free variable `global` from Node.js. */
45 | var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
46 |
47 | /** Detect free variable `self`. */
48 | var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
49 |
50 | /** Used as a reference to the global object. */
51 | var root = freeGlobal || freeSelf || Function('return this')();
52 |
53 | /** Detect free variable `exports`. */
54 | var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
55 |
56 | /** Detect free variable `module`. */
57 | var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
58 |
59 | /*--------------------------------------------------------------------------*/
60 |
61 | /** Used for built-in method references. */
62 | var objectProto = Object.prototype;
63 |
64 | /** Used to check objects for own properties. */
65 | var hasOwnProperty = objectProto.hasOwnProperty;
66 |
67 | /**
68 | * Used to resolve the
69 | * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
70 | * of values.
71 | */
72 | var nativeObjectToString = objectProto.toString;
73 |
74 | /** Built-in value references. */
75 | var Symbol = root.Symbol,
76 | symToStringTag = Symbol ? Symbol.toStringTag : undefined;
77 |
78 | /* Built-in method references for those with the same name as other `lodash` methods. */
79 | var nativeMax = Math.max,
80 | nativeMin = Math.min;
81 |
82 | /** Used to lookup unminified function names. */
83 | var realNames = {};
84 |
85 | /*------------------------------------------------------------------------*/
86 |
87 | /**
88 | * Creates a `lodash` object which wraps `value` to enable implicit method
89 | * chain sequences. Methods that operate on and return arrays, collections,
90 | * and functions can be chained together. Methods that retrieve a single value
91 | * or may return a primitive value will automatically end the chain sequence
92 | * and return the unwrapped value. Otherwise, the value must be unwrapped
93 | * with `_#value`.
94 | *
95 | * Explicit chain sequences, which must be unwrapped with `_#value`, may be
96 | * enabled using `_.chain`.
97 | *
98 | * The execution of chained methods is lazy, that is, it's deferred until
99 | * `_#value` is implicitly or explicitly called.
100 | *
101 | * Lazy evaluation allows several methods to support shortcut fusion.
102 | * Shortcut fusion is an optimization to merge iteratee calls; this avoids
103 | * the creation of intermediate arrays and can greatly reduce the number of
104 | * iteratee executions. Sections of a chain sequence qualify for shortcut
105 | * fusion if the section is applied to an array and iteratees accept only
106 | * one argument. The heuristic for whether a section qualifies for shortcut
107 | * fusion is subject to change.
108 | *
109 | * Chaining is supported in custom builds as long as the `_#value` method is
110 | * directly or indirectly included in the build.
111 | *
112 | * In addition to lodash methods, wrappers have `Array` and `String` methods.
113 | *
114 | * The wrapper `Array` methods are:
115 | * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
116 | *
117 | * The wrapper `String` methods are:
118 | * `replace` and `split`
119 | *
120 | * The wrapper methods that support shortcut fusion are:
121 | * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
122 | * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
123 | * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
124 | *
125 | * The chainable wrapper methods are:
126 | * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
127 | * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
128 | * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
129 | * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
130 | * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
131 | * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
132 | * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
133 | * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
134 | * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
135 | * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
136 | * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
137 | * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
138 | * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
139 | * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
140 | * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
141 | * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
142 | * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
143 | * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
144 | * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
145 | * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
146 | * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
147 | * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
148 | * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
149 | * `zipObject`, `zipObjectDeep`, and `zipWith`
150 | *
151 | * The wrapper methods that are **not** chainable by default are:
152 | * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
153 | * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
154 | * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
155 | * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
156 | * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
157 | * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
158 | * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
159 | * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
160 | * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
161 | * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
162 | * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
163 | * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
164 | * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
165 | * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
166 | * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
167 | * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
168 | * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
169 | * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
170 | * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
171 | * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
172 | * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
173 | * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
174 | * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
175 | * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
176 | * `upperFirst`, `value`, and `words`
177 | *
178 | * @name _
179 | * @constructor
180 | * @category Seq
181 | * @param {*} value The value to wrap in a `lodash` instance.
182 | * @returns {Object} Returns the new `lodash` wrapper instance.
183 | * @example
184 | *
185 | * function square(n) {
186 | * return n * n;
187 | * }
188 | *
189 | * var wrapped = _([1, 2, 3]);
190 | *
191 | * // Returns an unwrapped value.
192 | * wrapped.reduce(_.add);
193 | * // => 6
194 | *
195 | * // Returns a wrapped value.
196 | * var squares = wrapped.map(square);
197 | *
198 | * _.isArray(squares);
199 | * // => false
200 | *
201 | * _.isArray(squares.value());
202 | * // => true
203 | */
204 | function lodash() {
205 | // No operation performed.
206 | }
207 |
208 | /*------------------------------------------------------------------------*/
209 |
210 | /**
211 | * The base implementation of `getTag` without fallbacks for buggy environments.
212 | *
213 | * @private
214 | * @param {*} value The value to query.
215 | * @returns {string} Returns the `toStringTag`.
216 | */
217 | function baseGetTag(value) {
218 | if (value == null) {
219 | return value === undefined ? undefinedTag : nullTag;
220 | }
221 | return (symToStringTag && symToStringTag in Object(value))
222 | ? getRawTag(value)
223 | : objectToString(value);
224 | }
225 |
226 | /**
227 | * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
228 | *
229 | * @private
230 | * @param {*} value The value to query.
231 | * @returns {string} Returns the raw `toStringTag`.
232 | */
233 | function getRawTag(value) {
234 | var isOwn = hasOwnProperty.call(value, symToStringTag),
235 | tag = value[symToStringTag];
236 |
237 | try {
238 | value[symToStringTag] = undefined;
239 | var unmasked = true;
240 | } catch (e) {}
241 |
242 | var result = nativeObjectToString.call(value);
243 | if (unmasked) {
244 | if (isOwn) {
245 | value[symToStringTag] = tag;
246 | } else {
247 | delete value[symToStringTag];
248 | }
249 | }
250 | return result;
251 | }
252 |
253 | /**
254 | * Converts `value` to a string using `Object.prototype.toString`.
255 | *
256 | * @private
257 | * @param {*} value The value to convert.
258 | * @returns {string} Returns the converted string.
259 | */
260 | function objectToString(value) {
261 | return nativeObjectToString.call(value);
262 | }
263 |
264 | /*------------------------------------------------------------------------*/
265 |
266 | /**
267 | * Gets the timestamp of the number of milliseconds that have elapsed since
268 | * the Unix epoch (1 January 1970 00:00:00 UTC).
269 | *
270 | * @static
271 | * @memberOf _
272 | * @since 2.4.0
273 | * @category Date
274 | * @returns {number} Returns the timestamp.
275 | * @example
276 | *
277 | * _.defer(function(stamp) {
278 | * console.log(_.now() - stamp);
279 | * }, _.now());
280 | * // => Logs the number of milliseconds it took for the deferred invocation.
281 | */
282 | var now = function() {
283 | return root.Date.now();
284 | };
285 |
286 | /*------------------------------------------------------------------------*/
287 |
288 | /**
289 | * Creates a debounced function that delays invoking `func` until after `wait`
290 | * milliseconds have elapsed since the last time the debounced function was
291 | * invoked. The debounced function comes with a `cancel` method to cancel
292 | * delayed `func` invocations and a `flush` method to immediately invoke them.
293 | * Provide `options` to indicate whether `func` should be invoked on the
294 | * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
295 | * with the last arguments provided to the debounced function. Subsequent
296 | * calls to the debounced function return the result of the last `func`
297 | * invocation.
298 | *
299 | * **Note:** If `leading` and `trailing` options are `true`, `func` is
300 | * invoked on the trailing edge of the timeout only if the debounced function
301 | * is invoked more than once during the `wait` timeout.
302 | *
303 | * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
304 | * until to the next tick, similar to `setTimeout` with a timeout of `0`.
305 | *
306 | * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
307 | * for details over the differences between `_.debounce` and `_.throttle`.
308 | *
309 | * @static
310 | * @memberOf _
311 | * @since 0.1.0
312 | * @category Function
313 | * @param {Function} func The function to debounce.
314 | * @param {number} [wait=0] The number of milliseconds to delay.
315 | * @param {Object} [options={}] The options object.
316 | * @param {boolean} [options.leading=false]
317 | * Specify invoking on the leading edge of the timeout.
318 | * @param {number} [options.maxWait]
319 | * The maximum time `func` is allowed to be delayed before it's invoked.
320 | * @param {boolean} [options.trailing=true]
321 | * Specify invoking on the trailing edge of the timeout.
322 | * @returns {Function} Returns the new debounced function.
323 | * @example
324 | *
325 | * // Avoid costly calculations while the window size is in flux.
326 | * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
327 | *
328 | * // Invoke `sendMail` when clicked, debouncing subsequent calls.
329 | * jQuery(element).on('click', _.debounce(sendMail, 300, {
330 | * 'leading': true,
331 | * 'trailing': false
332 | * }));
333 | *
334 | * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
335 | * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
336 | * var source = new EventSource('/stream');
337 | * jQuery(source).on('message', debounced);
338 | *
339 | * // Cancel the trailing debounced invocation.
340 | * jQuery(window).on('popstate', debounced.cancel);
341 | */
342 | function debounce(func, wait, options) {
343 | var lastArgs,
344 | lastThis,
345 | maxWait,
346 | result,
347 | timerId,
348 | lastCallTime,
349 | lastInvokeTime = 0,
350 | leading = false,
351 | maxing = false,
352 | trailing = true;
353 |
354 | if (typeof func != 'function') {
355 | throw new TypeError(FUNC_ERROR_TEXT);
356 | }
357 | wait = toNumber(wait) || 0;
358 | if (isObject(options)) {
359 | leading = !!options.leading;
360 | maxing = 'maxWait' in options;
361 | maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
362 | trailing = 'trailing' in options ? !!options.trailing : trailing;
363 | }
364 |
365 | function invokeFunc(time) {
366 | var args = lastArgs,
367 | thisArg = lastThis;
368 |
369 | lastArgs = lastThis = undefined;
370 | lastInvokeTime = time;
371 | result = func.apply(thisArg, args);
372 | return result;
373 | }
374 |
375 | function leadingEdge(time) {
376 | // Reset any `maxWait` timer.
377 | lastInvokeTime = time;
378 | // Start the timer for the trailing edge.
379 | timerId = setTimeout(timerExpired, wait);
380 | // Invoke the leading edge.
381 | return leading ? invokeFunc(time) : result;
382 | }
383 |
384 | function remainingWait(time) {
385 | var timeSinceLastCall = time - lastCallTime,
386 | timeSinceLastInvoke = time - lastInvokeTime,
387 | timeWaiting = wait - timeSinceLastCall;
388 |
389 | return maxing
390 | ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
391 | : timeWaiting;
392 | }
393 |
394 | function shouldInvoke(time) {
395 | var timeSinceLastCall = time - lastCallTime,
396 | timeSinceLastInvoke = time - lastInvokeTime;
397 |
398 | // Either this is the first call, activity has stopped and we're at the
399 | // trailing edge, the system time has gone backwards and we're treating
400 | // it as the trailing edge, or we've hit the `maxWait` limit.
401 | return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
402 | (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
403 | }
404 |
405 | function timerExpired() {
406 | var time = now();
407 | if (shouldInvoke(time)) {
408 | return trailingEdge(time);
409 | }
410 | // Restart the timer.
411 | timerId = setTimeout(timerExpired, remainingWait(time));
412 | }
413 |
414 | function trailingEdge(time) {
415 | timerId = undefined;
416 |
417 | // Only invoke if we have `lastArgs` which means `func` has been
418 | // debounced at least once.
419 | if (trailing && lastArgs) {
420 | return invokeFunc(time);
421 | }
422 | lastArgs = lastThis = undefined;
423 | return result;
424 | }
425 |
426 | function cancel() {
427 | if (timerId !== undefined) {
428 | clearTimeout(timerId);
429 | }
430 | lastInvokeTime = 0;
431 | lastArgs = lastCallTime = lastThis = timerId = undefined;
432 | }
433 |
434 | function flush() {
435 | return timerId === undefined ? result : trailingEdge(now());
436 | }
437 |
438 | function debounced() {
439 | var time = now(),
440 | isInvoking = shouldInvoke(time);
441 |
442 | lastArgs = arguments;
443 | lastThis = this;
444 | lastCallTime = time;
445 |
446 | if (isInvoking) {
447 | if (timerId === undefined) {
448 | return leadingEdge(lastCallTime);
449 | }
450 | if (maxing) {
451 | // Handle invocations in a tight loop.
452 | timerId = setTimeout(timerExpired, wait);
453 | return invokeFunc(lastCallTime);
454 | }
455 | }
456 | if (timerId === undefined) {
457 | timerId = setTimeout(timerExpired, wait);
458 | }
459 | return result;
460 | }
461 | debounced.cancel = cancel;
462 | debounced.flush = flush;
463 | return debounced;
464 | }
465 |
466 | /**
467 | * Creates a throttled function that only invokes `func` at most once per
468 | * every `wait` milliseconds. The throttled function comes with a `cancel`
469 | * method to cancel delayed `func` invocations and a `flush` method to
470 | * immediately invoke them. Provide `options` to indicate whether `func`
471 | * should be invoked on the leading and/or trailing edge of the `wait`
472 | * timeout. The `func` is invoked with the last arguments provided to the
473 | * throttled function. Subsequent calls to the throttled function return the
474 | * result of the last `func` invocation.
475 | *
476 | * **Note:** If `leading` and `trailing` options are `true`, `func` is
477 | * invoked on the trailing edge of the timeout only if the throttled function
478 | * is invoked more than once during the `wait` timeout.
479 | *
480 | * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
481 | * until to the next tick, similar to `setTimeout` with a timeout of `0`.
482 | *
483 | * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
484 | * for details over the differences between `_.throttle` and `_.debounce`.
485 | *
486 | * @static
487 | * @memberOf _
488 | * @since 0.1.0
489 | * @category Function
490 | * @param {Function} func The function to throttle.
491 | * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
492 | * @param {Object} [options={}] The options object.
493 | * @param {boolean} [options.leading=true]
494 | * Specify invoking on the leading edge of the timeout.
495 | * @param {boolean} [options.trailing=true]
496 | * Specify invoking on the trailing edge of the timeout.
497 | * @returns {Function} Returns the new throttled function.
498 | * @example
499 | *
500 | * // Avoid excessively updating the position while scrolling.
501 | * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
502 | *
503 | * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
504 | * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
505 | * jQuery(element).on('click', throttled);
506 | *
507 | * // Cancel the trailing throttled invocation.
508 | * jQuery(window).on('popstate', throttled.cancel);
509 | */
510 | function throttle(func, wait, options) {
511 | var leading = true,
512 | trailing = true;
513 |
514 | if (typeof func != 'function') {
515 | throw new TypeError(FUNC_ERROR_TEXT);
516 | }
517 | if (isObject(options)) {
518 | leading = 'leading' in options ? !!options.leading : leading;
519 | trailing = 'trailing' in options ? !!options.trailing : trailing;
520 | }
521 | return debounce(func, wait, {
522 | 'leading': leading,
523 | 'maxWait': wait,
524 | 'trailing': trailing
525 | });
526 | }
527 |
528 | /*------------------------------------------------------------------------*/
529 |
530 | /**
531 | * Checks if `value` is the
532 | * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
533 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
534 | *
535 | * @static
536 | * @memberOf _
537 | * @since 0.1.0
538 | * @category Lang
539 | * @param {*} value The value to check.
540 | * @returns {boolean} Returns `true` if `value` is an object, else `false`.
541 | * @example
542 | *
543 | * _.isObject({});
544 | * // => true
545 | *
546 | * _.isObject([1, 2, 3]);
547 | * // => true
548 | *
549 | * _.isObject(_.noop);
550 | * // => true
551 | *
552 | * _.isObject(null);
553 | * // => false
554 | */
555 | function isObject(value) {
556 | var type = typeof value;
557 | return value != null && (type == 'object' || type == 'function');
558 | }
559 |
560 | /**
561 | * Checks if `value` is object-like. A value is object-like if it's not `null`
562 | * and has a `typeof` result of "object".
563 | *
564 | * @static
565 | * @memberOf _
566 | * @since 4.0.0
567 | * @category Lang
568 | * @param {*} value The value to check.
569 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
570 | * @example
571 | *
572 | * _.isObjectLike({});
573 | * // => true
574 | *
575 | * _.isObjectLike([1, 2, 3]);
576 | * // => true
577 | *
578 | * _.isObjectLike(_.noop);
579 | * // => false
580 | *
581 | * _.isObjectLike(null);
582 | * // => false
583 | */
584 | function isObjectLike(value) {
585 | return value != null && typeof value == 'object';
586 | }
587 |
588 | /**
589 | * Checks if `value` is classified as a `Symbol` primitive or object.
590 | *
591 | * @static
592 | * @memberOf _
593 | * @since 4.0.0
594 | * @category Lang
595 | * @param {*} value The value to check.
596 | * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
597 | * @example
598 | *
599 | * _.isSymbol(Symbol.iterator);
600 | * // => true
601 | *
602 | * _.isSymbol('abc');
603 | * // => false
604 | */
605 | function isSymbol(value) {
606 | return typeof value == 'symbol' ||
607 | (isObjectLike(value) && baseGetTag(value) == symbolTag);
608 | }
609 |
610 | /**
611 | * Converts `value` to a number.
612 | *
613 | * @static
614 | * @memberOf _
615 | * @since 4.0.0
616 | * @category Lang
617 | * @param {*} value The value to process.
618 | * @returns {number} Returns the number.
619 | * @example
620 | *
621 | * _.toNumber(3.2);
622 | * // => 3.2
623 | *
624 | * _.toNumber(Number.MIN_VALUE);
625 | * // => 5e-324
626 | *
627 | * _.toNumber(Infinity);
628 | * // => Infinity
629 | *
630 | * _.toNumber('3.2');
631 | * // => 3.2
632 | */
633 | function toNumber(value) {
634 | if (typeof value == 'number') {
635 | return value;
636 | }
637 | if (isSymbol(value)) {
638 | return NAN;
639 | }
640 | if (isObject(value)) {
641 | var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
642 | value = isObject(other) ? (other + '') : other;
643 | }
644 | if (typeof value != 'string') {
645 | return value === 0 ? value : +value;
646 | }
647 | value = value.replace(reTrim, '');
648 | var isBinary = reIsBinary.test(value);
649 | return (isBinary || reIsOctal.test(value))
650 | ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
651 | : (reIsBadHex.test(value) ? NAN : +value);
652 | }
653 |
654 | /*------------------------------------------------------------------------*/
655 |
656 | // Add methods that return wrapped values in chain sequences.
657 | lodash.debounce = debounce;
658 | lodash.throttle = throttle;
659 |
660 | /*------------------------------------------------------------------------*/
661 |
662 | // Add methods that return unwrapped values in chain sequences.
663 | lodash.isObject = isObject;
664 | lodash.isObjectLike = isObjectLike;
665 | lodash.isSymbol = isSymbol;
666 | lodash.now = now;
667 | lodash.toNumber = toNumber;
668 |
669 | /*------------------------------------------------------------------------*/
670 |
671 | /**
672 | * The semantic version number.
673 | *
674 | * @static
675 | * @memberOf _
676 | * @type {string}
677 | */
678 | lodash.VERSION = VERSION;
679 |
680 | /*--------------------------------------------------------------------------*/
681 |
682 | // Some AMD build optimizers, like r.js, check for condition patterns like:
683 | if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
684 | // Expose Lodash on the global object to prevent errors when Lodash is
685 | // loaded by a script tag in the presence of an AMD loader.
686 | // See http://requirejs.org/docs/errors.html#mismatch for more details.
687 | // Use `_.noConflict` to remove Lodash from the global object.
688 | root._ = lodash;
689 |
690 | // Define as an anonymous module so, through path mapping, it can be
691 | // referenced as the "underscore" module.
692 | define(function() {
693 | return lodash;
694 | });
695 | }
696 | // Check for `exports` after `define` in case a build optimizer adds it.
697 | else if (freeModule) {
698 | // Export for Node.js.
699 | (freeModule.exports = lodash)._ = lodash;
700 | // Export for CommonJS support.
701 | freeExports._ = lodash;
702 | }
703 | else {
704 | // Export to the global object.
705 | root._ = lodash;
706 | }
707 | }.call(this));
708 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Automatic Spell Checking Language Selection",
4 | "description": "Automatically changes the dictionary used for spell checking based on the language of text in input fields.",
5 | "homepage_url": "https://github.com/kimsey0/FirefoxAutoDict",
6 | "version": "1.0",
7 | "icons": {
8 | "48": "icons/icon.png",
9 | "96": "icons/icon@2x.png"
10 | },
11 |
12 | "content_scripts": [
13 | {
14 | "matches": [""],
15 | "js": ["lodash.throttle.js", "./autodict.js"]
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------