PageSourceSearch

https://konakovograd.ru/wp-content/plugins/mistape/assets/js/mistape-front.js?ver=1539160430

js konakovograd.ru collected 2026-10-02 20:45:44 UTC 30,238 bytes, 987 lines download raw bytes

1/**
2 * JavaScript Client Detection
3 * (C) viazenetti GmbH (Christian Ludwig)
4 */
5(function (window) {
6	{
7		var unknown = '-';
8
9		// screen
10		var screenSize = '';
11		if (screen.width) {
12			width = (screen.width) ? screen.width : '';
13			height = (screen.height) ? screen.height : '';
14			screenSize += '' + width + " x " + height;
15		}
16
17		// browser
18		var nVer = navigator.appVersion;
19		var nAgt = navigator.userAgent;
20		var browser = navigator.appName;
21		var version = '' + parseFloat(navigator.appVersion);
22		var majorVersion = parseInt(navigator.appVersion, 10);
23		var nameOffset, verOffset, ix;
24
25		// Opera
26		if ((verOffset = nAgt.indexOf('Opera')) !== -1) {
27			browser = 'Opera';
28			version = nAgt.substring(verOffset + 6);
29			if ((verOffset = nAgt.indexOf('Version')) !== -1) {
30				version = nAgt.substring(verOffset + 8);
31			}
32		}
33		// Opera Next
34		if ((verOffset = nAgt.indexOf('OPR')) !== -1) {
35			browser = 'Opera';
36			version = nAgt.substring(verOffset + 4);
37		}
38		// USbrowser
39		else if ((verOffset = nAgt.indexOf('UCBrowser')) !== -1) {
40			browser = 'UCBrowser';
41			version = nAgt.substring(verOffset + 6);
42			if ((verOffset = nAgt.indexOf('Version')) !== -1) {
43				version = nAgt.substring(verOffset + 8);
44			}
45		}
46		// MSIE
47		else if ((verOffset = nAgt.indexOf('MSIE')) !== -1) {
48			browser = 'Microsoft Internet Explorer';
49			version = nAgt.substring(verOffset + 5);
50		}
51		// MSE
52		else if ((verOffset = nAgt.indexOf('Edge')) !== -1) {
53			browser = 'Edge';
54			version = nAgt.substring(verOffset + 7);
55		}
56		// Chrome
57		else if ((verOffset = nAgt.indexOf('Chrome')) !== -1) {
58			browser = 'Chrome';
59			version = nAgt.substring(verOffset + 7);
60		}
61		// Safari
62		else if ((verOffset = nAgt.indexOf('Safari')) !== -1) {
63			browser = 'Safari';
64			version = nAgt.substring(verOffset + 7);
65			if ((verOffset = nAgt.indexOf('Version')) !== -1) {
66				version = nAgt.substring(verOffset + 8);
67			}
68		}
69		// Firefox
70		else if ((verOffset = nAgt.indexOf('Firefox')) !== -1) {
71			browser = 'Firefox';
72			version = nAgt.substring(verOffset + 8);
73		}
74		// MSIE 11+
75		else if (nAgt.indexOf('Trident/') !== -1) {
76			browser = 'Microsoft Internet Explorer';
77			version = nAgt.substring(nAgt.indexOf('rv:') + 3);
78		}
79		// Other browsers
80		else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
81			browser = nAgt.substring(nameOffset, verOffset);
82			version = nAgt.substring(verOffset + 1);
83			if (browser.toLowerCase() === browser.toUpperCase()) {
84				browser = navigator.appName;
85			}
86		}
87		// trim the version string
88		if ((ix = version.indexOf(';')) !== -1) version = version.substring(0, ix);
89		if ((ix = version.indexOf(' ')) !== -1) version = version.substring(0, ix);
90		if ((ix = version.indexOf(')')) !== -1) version = version.substring(0, ix);
91
92		majorVersion = parseInt('' + version, 10);
93		if (isNaN(majorVersion)) {
94			version = '' + parseFloat(navigator.appVersion);
95			majorVersion = parseInt(navigator.appVersion, 10);
96		}
97
98		// mobile version
99		var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
100
101		// cookie
102		var cookieEnabled = !!(navigator.cookieEnabled);
103
104		if (typeof navigator.cookieEnabled === 'undefined' && !cookieEnabled) {
105			document.cookie = 'testcookie';
106			cookieEnabled = (document.cookie.indexOf('testcookie') !== -1);
107		}
108
109		// system
110		var os = unknown;
111		var clientStrings = [
112			{s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/},
113			{s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/},
114			{s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/},
115			{s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/},
116			{s: 'Windows Vista', r: /Windows NT 6.0/},
117			{s: 'Windows Server 2003', r: /Windows NT 5.2/},
118			{s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/},
119			{s: 'Windows 2000', r: /(Windows NT 5.0|Windows 2000)/},
120			{s: 'Windows ME', r: /(Win 9x 4.90|Windows ME)/},
121			{s: 'Windows 98', r: /(Windows 98|Win98)/},
122			{s: 'Windows 95', r: /(Windows 95|Win95|Windows_95)/},
123			{s: 'Windows NT 4.0', r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/},
124			{s: 'Windows CE', r: /Windows CE/},
125			{s: 'Windows 3.11', r: /Win16/},
126			{s: 'Android', r: /Android/},
127			{s: 'Open BSD', r: /OpenBSD/},
128			{s: 'Sun OS', r: /SunOS/},
129			{s: 'Linux', r: /(Linux|X11)/},
130			{s: 'iOS', r: /(iPhone|iPad|iPod)/},
131			{s: 'Mac OS X', r: /Mac OS X/},
132			{s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},
133			{s: 'QNX', r: /QNX/},
134			{s: 'UNIX', r: /UNIX/},
135			{s: 'BeOS', r: /BeOS/},
136			{s: 'OS/2', r: /OS\/2/},
137			{s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/}
138		];
139		for (var id in clientStrings) {
140			var cs = clientStrings[id];
141			if (cs.r.test(nAgt)) {
142				os = cs.s;
143				break;
144			}
145		}
146
147		var osVersion = unknown;
148
149		if (/Windows/.test(os)) {
150			osVersion = /Windows (.*)/.exec(os)[1];
151			os = 'Windows';
152		}
153
154		switch (os) {
155			case 'Mac OS X':
156				osVersion = /Mac OS X (10[\._\d]+)/.exec(nAgt)[1];
157				break;
158
159			case 'Android':
160				osVersion = /Android ([\._\d]+)/.exec(nAgt)[1];
161				break;
162
163			case 'iOS':
164				osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
165				osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0);
166				break;
167		}
168
169		var flashVersion = 'no check';
170		if (typeof swfobject !== 'undefined') {
171			var fv = swfobject.getFlashPlayerVersion();
172			if (fv.major > 0) {
173				flashVersion = fv.major + '.' + fv.minor + ' r' + fv.release;
174			}
175			else {
176				flashVersion = unknown;
177			}
178		}
179	}
180
181	window.jscd = {
182		screen             : screenSize,
183		browser            : browser,
184		browserVersion     : version,
185		browserMajorVersion: majorVersion,
186		mobile             : mobile,
187		os                 : os,
188		osVersion          : osVersion,
189		cookies            : cookieEnabled,
190		flashVersion       : flashVersion
191	};
192}(window));
193
194/**
195 * dialogFx.js v1.0.0
196 * http://www.codrops.com
197 *
198 * Licensed under the MIT license.
199 * http://www.opensource.org/licenses/mit-license.php
200 *
201 * Copyright 2014, Codrops
202 * http://www.codrops.com
203 */
204
205(function (window) {
206
207	'use strict';
208
209	var support = {animations: Modernizr.cssanimations},
210		animEndEventNames = {
211			'WebkitAnimation': 'webkitAnimationEnd',
212			'OAnimation'     : 'oAnimationEnd',
213			'msAnimation'    : 'MSAnimationEnd',
214			'animation'      : 'animationend'
215		},
216		animEndEventName = animEndEventNames[Modernizr.prefixed('animation')],
217		onEndAnimation = function (el, callback) {
218			var onEndCallbackFn = function (ev) {
219				if (support.animations) {
220					if (ev.target !== this) return;
221					this.removeEventListener(animEndEventName, onEndCallbackFn);
222				}
223				if (callback && typeof callback === 'function') {
224					callback.call();
225				}
226			};
227			if (support.animations) {
228				el.addEventListener(animEndEventName, onEndCallbackFn);
229			}
230			else {
231				onEndCallbackFn();
232			}
233		};
234
235	function extend(a, b) {
236		for (var key in b) {
237			if (b.hasOwnProperty(key)) {
238				a[key] = b[key];
239			}
240		}
241		return a;
242	}
243
244	function DialogFx(el, options) {
245		this.el = el;
246		this.options = extend({}, this.options);
247		extend(this.options, options);
248		this.isOpen = false;
249		this._initEvents();
250	}
251
252	DialogFx.prototype.options = {
253		// callbacks
254		onOpenDialog       : function () {
255			return false;
256		},
257		onCloseDialog      : function () {
258			return false;
259		},
260		onOpenAnimationEnd : function () {
261			return false;
262		},
263		onCloseAnimationEnd: function () {
264			return false;
265		}
266	};
267
268	DialogFx.prototype._initEvents = function () {
269		var self = this;
270
271		// esc key closes dialog
272		document.addEventListener('keydown', function (ev) {
273			var keyCode = ev.keyCode || ev.which;
274			if (keyCode === 27 && self.isOpen) {
275				self.toggle();
276			}
277		});
278
279		this.el.querySelector('.dialog__overlay').addEventListener('click', this.toggle.bind(this));
280	};
281
282	DialogFx.prototype.toggle = function () {
283		var self = this;
284		if (this.isOpen) {
285			jQuery(this.el).removeClass('dialog--open');
286			jQuery(self.el).addClass('dialog--close');
287
288			onEndAnimation(this.el.querySelector('.dialog__content'), function () {
289				jQuery(self.el).removeClass('dialog--close');
290				self.options.onCloseAnimationEnd(self);
291			});
292
293			// callback on close
294			this.options.onCloseDialog(this);
295		}
296		else {
297			jQuery(this.el).addClass('dialog--open');
298
299			// callback on open
300			this.options.onOpenDialog(this);
301
302			onEndAnimation(this.el.querySelector('.dialog__content'), function () {
303				jQuery(self.el).removeClass('dialog--close');
304				self.options.onOpenAnimationEnd(self);
305			});
306		}
307		this.isOpen = !this.isOpen;
308	};
309
310	// add to global namespace
311	window.DialogFx = DialogFx;
312
313})(window);
314
315
316/**
317 * Mistape
318 */
319(function ($) {
320
321	// return if no args passed from backend
322	if (!window.decoMistape) {
323		return;
324	}
325
326	var getPointerEvent = function (event) {
327		return event.originalEvent.targetTouches ? event.originalEvent.targetTouches[0] : event;
328	};
329
330	// var $touchArea = $('body'),
331	// 	touchStarted = false, // detect if a touch event is sarted
332	// 	touchPopupShow = false, // detect if a touch event is sarted
333	// 	currX = 0,
334	// 	currY = 0,
335	// 	cachedX = 0,
336	// 	cachedY = 0;
337	// var timeoutTouch = '';
338
339	// $touchArea.on('touchstart', function (e) {
340	// 	var pointer = getPointerEvent(e);
341	// 	cachedX = currX = pointer.pageX;
342	// 	cachedY = currY = pointer.pageY;
343	//
344	// 	if (touchPopupShow === true) {
345	// 		touchPopupShow = false;
346	// 		var report = decoMistape.getSelectionData();
347	// 		if (report) {
348	// 			decoMistape.showDialog(report);
349	// 		}
350	// 	}
351	//
352	// 	// }, 1000);
353	// });
354	// $touchArea.on('touchend touchcancel', function (e) {
355	// 	touchPopupShow = true;
356	// 	currX = pointer.pageX;
357	// 	currY = pointer.pageY;
358	// });
359	//
360	// $touchArea.on('touchmove', function (e) {
361	// 	// e.preventDefault();
362	// 	var pointer = getPointerEvent(e);
363	// 	currX = pointer.pageX;
364	// 	currY = pointer.pageY;
365	// 	if (touchStarted) {
366	// 		// here you are swiping
367	// 		// alert('Swiping');
368	// 	}
369	//
370	// });
371	// // }
372	//
373	window.decoMistape = $.extend(window.decoMistape, {
374
375		onReady: function () {
376			decoMistape.initDialogFx();
377
378			var $dialog = $(decoMistape.dlg.el);
379
380			$(document).on('click', '.mistape_action', function () {
381				if ($(this).is('[data-action=send]')) {
382					var data;
383					if (!$dialog.data('dry-run') && (data = $dialog.data('report'))) {
384						if ($dialog.data('mode') === 'comment') {
385							data.comment = $dialog.find('#mistape_comment').val();
386							$('#mistape_comment').val('');
387						}
388						data.post_id = $(this).data('id');
389						decoMistape.reportSpellError(data);
390					}
391					decoMistape.animateLetter();
392				}
393				else if ($(this).is('[data-dialog-close]')) {
394					decoMistape.dlg.toggle();
395				}
396			});
397
398			$(document).keyup(function (ev) {
399				if (ev.keyCode === 13 && ev.ctrlKey && ev.target.nodeName.toLowerCase() !== 'textarea' && $('#mistape_dialog.dialog--open').length === 0) {
400					var report = decoMistape.getSelectionData();
401					if (report) {
402						decoMistape.showDialog(report);
403					}
404				}
405			});
406		},
407
408		initDialogFx: function () {
409			decoMistape.dlg = new DialogFx(document.getElementById('mistape_dialog'), {
410				onOpenDialog       : function (dialog) {
411					$(dialog.el).css('display', 'flex');
412				},
413				onCloseAnimationEnd: function (dialog) {
414					$(dialog.el).css('display', 'none');
415					decoMistape.resetDialog();
416				}
417			});
418		},
419
420		animateLetter: function () {
421			var dialog = $(decoMistape.dlg.el),
422				content = dialog.find('.dialog__content'),
423				letterTop = dialog.find('.mistape-letter-top'),
424				letterFront = dialog.find('.mistape-letter-front'),
425				letterBack = dialog.find('.mistape-letter-back'),
426				dialogWrap = dialog.find('.dialog-wrap');
427
428			content.addClass('show-letter');
429
430			setTimeout(function () {
431				var y = (letterTop.offset().top - letterFront.offset().top) + letterTop.outerHeight();
432				letterTop.css({
433					'bottom' : Math.floor(y),
434					'opacity': 1
435				});
436				jQuery('.mistape-letter-back-top').hide();
437				if (content.hasClass('with-comment')) {
438					dialogWrap.css('transform', 'scaleY(0.5) scaleX(0.28)');
439				} else {
440					dialogWrap.css('transform', 'scaleY(0.5) scaleX(0.4)');
441				}
442				setTimeout(function () {
443					if (content.hasClass('with-comment')) {
444						dialogWrap.css('transform', 'translateY(12%) scaleY(0.5) scaleX(0.4)');
445					} else {
446						dialogWrap.css('transform', 'translateY(28%) scaleY(0.5) scaleX(0.45)');
447					}
448					setTimeout(function () {
449						letterTop.css('z-index', '9');
450						letterTop.addClass('close');
451						setTimeout(function () {
452							dialogWrap.css({
453								'visibility': 'hidden',
454								'opacity'   : '0'
455							});
456							letterFront.css('animation', 'send-letter1 0.7s');
457							letterBack.css('animation', 'send-letter1 0.7s');
458							letterTop.css('animation', 'send-letter2 0.7s');
459							setTimeout(function () {
460								decoMistape.dlg.toggle();
461							}, 400)
462						}, 400)
463					}, 400)
464				}, 300)
465			}, 400);
466		},
467
468		showDialog: function (report) {
469			if (report.hasOwnProperty('selection') && report.hasOwnProperty('context')) {
470				var $dialog = $(decoMistape.dlg.el);
471
472				if ($dialog.data('mode') === 'notify') {
473					decoMistape.reportSpellError(report);
474					decoMistape.dlg.toggle();
475				}
476				else {
477					$dialog.data('report', report);
478					$dialog.find('#mistape_reported_text').html(report.preview_text);
479					decoMistape.dlg.toggle();
480				}
481			}
482		},
483
484		resetDialog: function () {
485			var $dialog = $(decoMistape.dlg.el);
486
487			if ($dialog.data('mode') != 'notify') {
488				$dialog.find('#mistape_confirm_dialog').css('display', '');
489				$dialog.find('#mistape_success_dialog').remove();
490			}
491
492			// letter
493			$dialog.find('.dialog__content').removeClass('show-letter');
494			$dialog.find('.mistape-letter-top, .mistape-letter-front, .mistape-letter-back, .dialog-wrap, .mistape-letter-back-top').removeAttr('style');
495			$dialog.find('.mistape-letter-top').removeClass('close');
496		},
497
498		reportSpellError: function (data) {
499			data.action = 'mistape_report_error';
500			$.ajax({
501				type    : 'post',
502				dataType: 'json',
503				url     : decoMistape.ajaxurl,
504				data    : data
505			})
506		},
507
508		getSelectionData: function () {
509			// Check for existence of window.getSelection()
510			if (!window.getSelection) {
511				return false;
512			}
513
514			var parentEl, sel, selChars, selWord, textToHighlight, maxContextLength = 140;
515
516			var stringifyContent = function (string) {
517				return typeof string === 'string' ? string.replace(/\s*(?:(?:\r\n)+|\r+|\n+)\t*/gm, '\r\n').replace(/\s{2,}/gm, ' ') : '';
518			};
519
520			var isSubstrUnique = function (substr, context) {
521				if (typeof context === 'undefined') {
522					context = decoMistape.contextBuffer;
523				}
524				if (typeof substr === 'undefined') {
525					substr = decoMistape.selBuffer;
526				}
527				var split = context.split(substr);
528				var count = split.length - 1;
529				return count === 1;
530
531			};
532
533			var getExactSelPos = function (selection, context) {
534				// if there is only one match, that's it
535				if (isSubstrUnique(selWithContext, context)) {
536					return context.indexOf(selWithContext);
537				}
538				// check if we can get the occurrence match from selection offsets
539				if (!backwards) {
540					// check anchor element
541					if (context.substring(sel.anchorOffset, sel.anchorOffset + selection.length) == selection) {
542						return sel.anchorOffset;
543					}
544					// check anchor parent element
545					var parentElOffset = sel.anchorOffset;
546					var prevEl = sel.anchorNode.previousSibling;
547					while (prevEl !== null) {
548						parentElOffset += prevEl.textContent.length;
549						prevEl = prevEl.previousSibling;
550					}
551					if (context.substring(parentElOffset, parentElOffset + selection.length) == selection) {
552						return parentElOffset;
553					}
554				}
555				if (backwards && context.substring(sel.focusOffset, sel.focusOffset + selection.length) == selection) {
556					return sel.anchorOffset;
557				}
558				return -1;
559			};
560
561			var getExtendedSelection = function (limit, nodeExtensions) {
562
563				limit = parseInt(limit) || 40;
564				nodeExtensions = nodeExtensions || {left: '', right: ''};
565				var i = 0, selContent, selEndNode = sel.focusNode, selEndOffset = sel.focusOffset;
566
567				while (i <= limit) {
568
569					if ((selContent = stringifyContent(sel.toString().trim())).length >= maxContextLength || isSubstrUnique(selContent, context)) {
570						return selContent;
571					}
572
573					// only even iteration
574					if (i % 2 == 0 && sel.anchorOffset > 0 || nodeExtensions.left.length && i < limit / 2) {
575						// reset
576						if (backwards) {
577							sel.collapseToEnd();
578						}
579						else {
580							sel.collapseToStart();
581						}
582						sel.modify("move", direction[1], "character");
583						sel.extend(selEndNode, selEndOffset);
584					}
585					else if (sel.focusOffset < sel.focusNode.length || nodeExtensions.right.length && i < limit / 2) {
586						sel.modify('extend', direction[0], 'character');
587						if (sel.focusOffset === 1) {
588							selEndNode = sel.focusNode;
589							selEndOffset = sel.focusOffset;
590						}
591					}
592					else if (i % 2 === 0) {
593						break;
594					}
595
596					i++;
597				}
598
599				return stringifyContent(sel.toString().trim());
600			};
601
602			var getExtendedContext = function (context, element, method) {
603				var contentPrepend = '', contentAppend = '', e = element, i;
604				method = method || 'textContent';
605
606				for (i = 0; i < 20; i++) {
607					if (contentPrepend || (e = e.previousSibling) === null) {
608						break;
609					}
610
611					if ((contentPrepend = stringifyContent(e[method].trim())).length) {
612						context = contentPrepend + context;
613
614					}
615				}
616
617				// reset element
618				e = element;
619
620				for (i = 0; i < 20; i++) {
621					if (contentAppend || (e = e.nextSibling) === null) {
622						break;
623					}
624					if ((contentAppend = stringifyContent(e[method]).trim()).length) {
625						context += contentAppend;
626					}
627					else if (context.slice(-1) !== ' ') {
628						context += ' ';
629					}
630				}
631
632				return {
633					contents  : context,
634					extensions: {
635						left : contentPrepend,
636						right: contentAppend
637					}
638				};
639			};
640
641			// check that getSelection() has a modify() method. IE has both selection APIs but no modify() method.
642			// this works on modern browsers following standards
643			if ((sel = window.getSelection()).modify) {
644				// check if there is any text selected
645				if (!sel.isCollapsed) {
646
647					/**
648					 * So the first step is to get selection extended to the boundaries of words
649					 *
650					 * e.g. if the sentence is "What a wonderful life!" and selection is "rful li",
651					 * we get "wonderful life" stored in selWord variable
652					 */
653
654					selChars = sel.toString();
655
656					// return early if no selection to work with or if its length exceeds the limit
657					if (!selChars || selChars.length > maxContextLength) {
658						return;
659					}
660
661					// here we get the nearest parent node which is common for the whole selection
662					if (sel.rangeCount) {
663						parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
664						while (parentEl.textContent == sel.toString()) {
665							parentEl = parentEl.parentNode;
666						}
667					}
668
669					// Detect if selection was made backwards
670					// further logic depends on it
671					var range = document.createRange();
672					range.setStart(sel.anchorNode, sel.anchorOffset);
673					range.setEnd(sel.focusNode, sel.focusOffset);
674					var backwards = range.collapsed;
675					range = null;
676
677					// save initial selection to restore in the end
678					var initialSel = {
679						focusNode   : sel.focusNode,
680						focusOffset : sel.focusOffset,
681						anchorNode  : sel.anchorNode,
682						anchorOffset: sel.anchorOffset
683					};
684
685					// modify() works on the focus of the selection (not virtually) so we manipulate it
686					var endNode = sel.focusNode, endOffset = sel.focusOffset;
687
688					// determine second char of selection and the one before last
689					// they will be our starting point for word boundaries detection
690					var direction, secondChar, oneBeforeLastChar;
691					if (backwards) {
692						direction = ['backward', 'forward'];
693						secondChar = selChars.charAt(selChars.length - 1);
694						oneBeforeLastChar = selChars.charAt(0);
695					} else {
696						direction = ['forward', 'backward'];
697						secondChar = selChars.charAt(0);
698						oneBeforeLastChar = selChars.charAt(selChars.length - 1);
699					}
700
701					// collapse the cursor to the first char
702					sel.collapse(sel.anchorNode, sel.anchorOffset);
703					// move it one char forward
704					sel.modify("move", direction[0], "character");
705
706					// if the second character was a letter or digit, move cursor another step further
707					// this way we are certain that we are in the middle of the word
708					if (null === secondChar.match(/'[\w\d]'/)) {
709						sel.modify("move", direction[0], "character");
710					}
711
712					// and now we can determine the beginning position of the word
713					sel.modify("move", direction[1], "word");
714
715					// then extend the selection up to the initial point
716					// thus assure that selection starts with the beginning of the word
717					sel.extend(endNode, endOffset);
718
719					// do the same trick with the ending--extending it precisely up to the end of the word
720					sel.modify("extend", direction[1], "character");
721					if (null === oneBeforeLastChar.match(/'[\w\d]'/)) {
722						sel.modify("extend", direction[1], "character");
723					}
724					sel.modify("extend", direction[0], "word");
725					if (!backwards && sel.focusOffset === 1) {
726						sel.modify("extend", 'backward', "character");
727					}
728
729					// since different browser extend by "word" differently and some of them extend beyond the word
730					// covering spaces and punctuation, we need to collapse the selection back so it ends with the word
731					var i = 0, lengthBefore, lengthAfter;
732					while (i < 5 && (sel.toString().slice(-1).match(/[\s\n\t]/) || '').length) {
733						lengthBefore = sel.toString().length;
734						if (backwards) {
735							endNode = sel.anchorOffset == 0 ? sel.anchorNode.previousSibling : sel.anchorNode;
736							endOffset = sel.anchorOffset == 0 ? sel.anchorNode.previousSibling.length : sel.anchorOffset;
737							sel.modify('move', 'backward', 'character');
738							sel.extend(endNode, endOffset);
739							backwards = false;
740							direction = ['forward', 'backward'];
741						} else {
742							sel.modify('extend', 'backward', 'character');
743						}
744						lengthAfter = sel.toString().length;
745
746						// workaround for WebKit quirk: undo last iteration
747						if (lengthBefore - lengthAfter > 1) {
748							sel.modify('extend', 'forward', 'character');
749							break;
750						}
751					}
752
753					// finally, we've got a modified selection which is bound to words
754					// save it to highlight it later
755					selWord = stringifyContent(sel.toString().trim());
756				}
757			}
758			// this one is for IE11
759			else if (sel = window.getSelection()) {
760				var startOffset, startNode, endNode;
761				selChars = sel.toString();
762				range = document.createRange();
763				if (range.collapsed) {
764					startNode = sel.focusNode;
765					endNode = sel.anchorNode;
766					startOffset = sel.focusOffset;
767					endOffset = sel.anchorOffset;
768				}
769				else {
770					startNode = sel.anchorNode;
771					endNode = sel.focusNode;
772					startOffset = sel.anchorOffset;
773					endOffset = sel.focusOffset;
774				}
775
776				while (startOffset && !startNode.textContent.slice(startOffset - 1, startOffset).match(/[\s\n\t]/)) {
777					startOffset--;
778				}
779				while (endOffset < endNode.length && !endNode.textContent.slice(endOffset, endOffset + 1).match(/[\s\n\t]/)) {
780					endOffset++;
781				}
782
783				// here we get the nearest parent node which is common for the whole selection
784				if (sel.rangeCount) {
785					parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
786					while (parentEl.textContent == sel.toString()) {
787						parentEl = parentEl.parentNode;
788					}
789				}
790
791				selWord = stringifyContent(sel.toString().trim());
792
793				// this logic is for IE<10
794				// } else if ((sel = document.selection) && sel.type != "Control") {
795				//     var textRange = sel.createRange();
796				//
797				//      if (!textRange || textRange.text.length > maxContextLength) {
798				//      return;
799				//      }
800				//
801				//      if (textRange.text) {
802				//      selChars = textRange.text;
803				//      textRange.expand("word");
804				//      // Move the end back to not include the word's trailing space(s), if necessary
805				//      while (/\s$/.test(textRange.text)) {
806				//      textRange.moveEnd("character", -1);
807				//      }
808				//      selWord = textRange.text;
809				//      parentEl = textRange.parentNode;
810				//      }
811
812			}
813
814
815			if (typeof parentEl == 'undefined') {
816				return;
817			}
818
819			var selToFindInContext,
820				contextsToCheck = { // different browsers implement different methods, we try them by turn
821					textContent: parentEl.textContent,
822					innerText  : parentEl.innerText
823				};
824
825			textToHighlight = selWord;
826
827			for (var method in contextsToCheck) {
828				if (contextsToCheck.hasOwnProperty(method) && typeof contextsToCheck[method] != 'undefined') {
829
830					// start with counting selected word occurrences in context
831					var scope = {selection: 'word', context: 'initial'};
832					var context = stringifyContent(contextsToCheck[method].trim());
833					var selWithContext = stringifyContent(sel.toString().trim());
834					decoMistape.contextBuffer = context;
835					decoMistape.selBuffer = selWithContext;
836					var selPos; // this is what we are going to find
837					var selExactMatch = false;
838
839
840					if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
841						selExactMatch = true;
842						selToFindInContext = selWithContext;
843						break;
844					}
845
846					// if there is more than one occurrence, extend the selection
847					selWithContext = getExtendedSelection(40);
848					scope.selection = 'word extended';
849
850					if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
851						selExactMatch = true;
852						selToFindInContext = selWithContext;
853						break;
854					}
855
856					// if still have duplicates, extend the context and selection, and try again
857					var initialContext = context;
858					var extContext = getExtendedContext(context, parentEl, method);
859					context = extContext.contents;
860					selWithContext = getExtendedSelection(40, extContext.extensions);
861					scope.context = 'extended';
862
863					if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
864						selExactMatch = true;
865						selToFindInContext = selWithContext;
866						break;
867					}
868
869					// skip to next context getting method and start over, or exit
870					if (!selWithContext) {
871						continue;
872					}
873
874					if (isSubstrUnique(selWord, selWithContext) || selWord == selChars.trim()) {
875						context = selWithContext;
876						selWithContext = selWord;
877						textToHighlight = selWord;
878						scope.selection = 'word';
879						scope.context = 'extended';
880					}
881					else {
882						context = selWord;
883						selWithContext = selChars.trim();
884						textToHighlight = selChars.trim();
885						scope.selection = 'initial';
886						scope.context = 'word';
887					}
888
889					selPos = context.indexOf(selWithContext);
890
891					if (selPos !== -1) {
892						selToFindInContext = selWithContext;
893					}
894					else if ((selPos = context.indexOf(selWord)) !== -1) {
895						selToFindInContext = selWord;
896					}
897					else if ((selPos = context.indexOf(selChars)) !== -1) {
898						selToFindInContext = selChars;
899					}
900					else {
901						continue;
902					}
903					break;
904				}
905			}
906
907			if (selToFindInContext) {
908				sel.removeAllRanges();
909			}
910			else {
911				decoMistape.restoreInitSelection(sel, initialSel);
912				return;
913			}
914
915			if (scope.context === 'extended') {
916				context = extContext.extensions.left + initialContext + ' ' + extContext.extensions.right;
917			}
918
919			var contExcerptStartPos, contExcerptEndPos, selPosInContext, highlightedChars, previewText;
920			maxContextLength = Math.min(context.length, maxContextLength);
921
922			var truncatedContext = context;
923
924			if (context.length > maxContextLength) {
925
926				if (selPos + selToFindInContext.length / 2 < maxContextLength / 2) {
927					selPosInContext = 'beginning';
928					contExcerptStartPos = 0;
929					contExcerptEndPos = Math.max(selPos + selToFindInContext.length, context.indexOf(' ', maxContextLength - 10));
930				}
931				else if (selPos + selToFindInContext.length / 2 > context.length - maxContextLength / 2) {
932					selPosInContext = 'end';
933					contExcerptStartPos = Math.min(selPos, context.indexOf(' ', context.length - maxContextLength + 10));
934					contExcerptEndPos = context.length;
935				}
936				else {
937					selPosInContext = 'middle';
938					var centerPos = selPos + Math.round(selToFindInContext.length / 2);
939					contExcerptStartPos = Math.min(selPos, context.indexOf(' ', centerPos - maxContextLength / 2 - 10));
940					contExcerptEndPos = Math.max(selPos + selToFindInContext.length, context.indexOf(' ', centerPos + maxContextLength / 2 - 10));
941				}
942
943				truncatedContext = context.substring(contExcerptStartPos, contExcerptEndPos).trim();
944
945				if (selPosInContext !== 'beginning' && context.charAt(contExcerptStartPos - 1) !== '.') {
946					truncatedContext = '... ' + truncatedContext;
947				}
948				if (selPosInContext !== 'end' && context.charAt(contExcerptStartPos + contExcerptEndPos - 1) !== '.') {
949					truncatedContext = truncatedContext + ' ...';
950				}
951			}
952
953			if (isSubstrUnique(selChars, textToHighlight)) {
954				highlightedChars = textToHighlight.replace(selChars, '<span class="mistape_mistake_inner">' + selChars + '</span>')
955			}
956			else {
957				highlightedChars = '<strong class="mistape_mistake_inner">' + textToHighlight + '</strong>';
958			}
959
960			var selWithContextHighlighted = selToFindInContext.replace(textToHighlight, '<span class="mistape_mistake_outer">
960' + highlightedChars + '</span>');
961
962			if (selExactMatch && truncatedContext === context) {
963				previewText = truncatedContext.substring(0, selPos) + selWithContextHighlighted + truncatedContext.substring(selPos + selWithContext.length) || selWithContextHighlighted;
964			}
965			else {
966				previewText = truncatedContext.replace(selWithContext, selWithContextHighlighted) || selWithContextHighlighted;
967			}
968
969			return {
970				selection      : selChars,
971				word           : selWord,
972				replace_context: selToFindInContext,
973				context        : truncatedContext,
974				preview_text   : previewText,
975				// post_id: decoMistape.getPostId()
976			};
977		},
978
979		restoreInitSelection: function (sel, initialSel) {
980			sel.collapse(initialSel.anchorNode, initialSel.anchorOffset);
981			sel.extend(initialSel.focusNode, initialSel.focusOffset);
982		}
983	});
984
985	$(document).ready(decoMistape.onReady);
986
987})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.