1;(function($) { 2 3/** 4 * @author <github.com/tarampampam> 5 * @weblog http://blog.kplus.pro/ 6 * @project https://github.com/tarampampam/jquery.textmistake 7 * 8 * @version 0.1 9 * 10 * @licensy Licensed under the MIT, license text: http://goo.gl/JsVjCF 11 */ 12 13$.fn.textmistake = function(options) { 14 // Default settings 15 var defaults = { 16 'l10n': { 17 'title': 'Report a typo author:', 18 'urlHint': 'Url of the page with error:', 19 'errTextHint': 'Text with the error:', 20 'yourComment': 'Your comment:', 21 'userComment': 'Comment by user:', 22 'commentPlaceholder': 'Type comment', 23 'cancel': 'Cancel', 24 'send': 'Send', 25 26 'mailSubject': 'Typo on the site', 27 'mailTitle': 'Typo on the site', 28 29 'mailSended': 'Notification sent', 30 'mailSendedDesc': 'Your notification has been sent successfully. Thank you for your feedback!', 31 'mailNotSended': 'Sending error', 32 'mailNotSendedDesc': 'Your message has not been sent, sorry.', 33 }, 34 'debug': true, // fet 'false' if all tested and works fine 35 'initCss': true, 36 'initHtml': true, 37 38 'overlayColor': '#666', 39 'overlayOpacity': 0.5, 40 'windowZindex': 10001, 41 'hideBodyScroll': true, 42 43 'textLimit': 400, 44 'contextLength': 40, 45 'closeOnEsc': true, 46 47 'mailTo': '', // [email protected] 48 'mailFrom': 'test@'+window.location.hostname, 49 50 'mandrillKey': '', // Get your - https://mandrill.com/signup/ 51 'sendmailUrl': '', 52 53 'animateSpeed': 0, 54 'autocloseTime': 10000, 55 56 // Callbacks 57 'onShow': function(state){}, 58 'onHide': function(state){}, 59 'onLoadingShow': function(state){}, 60 'onLoadingHide': function(state){}, 61 'onCtrlEnter': function(){}, 62 'onEscPressed': function(){}, 63 'onSendMail': function(response){}, 64 'onAjaxDone': function(response){}, 65 'onAjaxResultError': function(response){}, 66 'onAjaxSendError': function(response){}, 67 }, 68 // Apply user settings to defaults 69 settings = $.extend({}, defaults, options), 70 log = function(text) { 71 if(settings.debug) { 72 var now = new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1"); 73 return console.log('[' + now + '] ' + text); 74 } 75 }, 76 html = $('html').first(), 77 head = $('head').first(), 78 body = $('body').first(); 79 80 // Add styles to head 81 if(settings.initCss && (head.find('#mt_s').length === 0)) head.append('<style id="mt_s" type="text/css">\ 82 html.mistake-open,html.mistake-open body{\ 83 min-height:100%}\ 84 html.mistake-open body{\ 85 display:block;position:relative;overflow:hidden;top:0;left:0}\ 86 #mt_o{\ 87 position:fixed;padding:0;margin:0;top:0;left:0;width:100%;height:100%;\ 88 background:'+settings.overlayColor+';z-index:'+settings.windowZindex.toString()+';-moz-opacity:'+settings.overlayOpacity.toString()+';opacity:'+settings.overlayOpacity.toString()+';zoom:1;display:none}\ 89 #mt_c{\ 90 position:fixed;top:50%;left:50%;width:454px;height:auto;padding:30px 42px;\ 91 background:#fff;border:1px solid #777;outline:0;box-shadow:0 4px 16px rgba(0,0,0,.2);\ 92 font-family:Arial,sans-serif;font-size:13px;line-height:18px;\ 93 word-wrap:break-word;z-index:'+(settings.windowZindex+1).toString()+';display:none}\ 94 #mt_c div.loading, #mt_c div.loading div.overlay{\ 95 position:absolute;top:0;left:0;width:100%;height:100%;}\ 96 #mt_c div.loading{\ 97 z-index:'+(settings.windowZindex+2).toString()+';display:none\ 98 }\ 99 #mt_c div.loading div.overlay{\ 100 background:#666;-moz-opacity:0.1;opacity:0.1;\ 101 }\ 102 #mt_c div.loading div.spinner{\ 103 position:absolute;
103left:50%;top:50%;width:30px;height:30px;margin:-15px 0 0 -15px;background-color:#222;\ 104 -webkit-animation:rotateplane 1.2s infinite ease-in-out;\ 105 animation:rotateplane 1.2s infinite ease-in-out}\ 106 @-webkit-keyframes rotateplane\ 107 {0%{-webkit-transform:perspective(120px)}\ 108 50%{-webkit-transform:perspective(120px) rotateY(180deg)}\ 109 100%{-webkit-transform:perspective(120px) rotateY(180deg) rotateX(180deg)}}\ 110 @keyframes rotateplane\ 111 {0%{transform:perspective(120px) rotateX(0deg) rotateY(0deg);-webkit-transform:perspective(120px) rotateX(0deg) rotateY(0deg)}\ 112 50%{transform:perspective(120px) rotateX(-180.1deg) rotateY(0deg);-webkit-transform:perspective(120px) rotateX(-180.1deg) rotateY(0deg)}\ 113 100%{transform:perspective(120px) rotateX(-180deg) rotateY(-179.9deg);-webkit-transform:perspective(120px) rotateX(-180deg) rotateY(-179.9deg)}}\ 114 #mt_c .close{\ 115 position:absolute;right:0;top:0;margin:0;padding:17px;width:11px;height:11px;\ 116 background:url(\'https://i.imgur.com/U3EnhFo.png\') no-repeat center center;\ 117 -moz-opacity:.7;opacity:.7;cursor:pointer;z-index:'+(settings.windowZindex+10).toString()+'\}\ 118 #mt_c .close:hover{\ 119 -moz-opacity:1;opacity:1}\ 120 #mt_c div.title{\ 121 height:32px;padding:0 0 0 40px;margin:0 0 16px;background-repeat:no-repeat;background-position:left center}\ 122 #mt_c div.title.feedback{\ 123 background-image:url(\'https://i.imgur.com/BCvESIh.png\')}\ 124 #mt_c div.title.fire{\ 125 background-image:url(\'http://i.imgur.com/CHwbq2g.png\')}\ 126 #mt_c div.title.mail{\ 127 background-image:url(\'http://i.imgur.com/Mo8R3H8.png\')}\ 128 #mt_c div.title.star{\ 129 background-image:url(\'http://i.imgur.com/wgTKfJF.png\')}\ 130 #mt_c div.title.cross{\ 131 background-image:url(\'http://i.imgur.com/5nx776T.png\')}\ 132 #mt_c div.title h1{\ 133 color:#000;display:inline;font-family:Arial,sans-serif;font-size:14px;font-weight:400;line-height:32px}\ 134 #mt_c p{\ 135 margin:0 0 13px;padding:0}\ 136 #mt_c p.nowrap{\ 137 white-space:nowrap;overflow:hidden;position:relative}\ 138 #mt_c p.nowrap::after{\ 139 content:\'\';position:absolute;right:0;top:0;width:40px;height:100%;\ 140 background:-moz-linear-gradient(left,rgba(255,255,255,0.2),#fff 100%);\ 141 background:-webkit-linear-gradient(left,rgba(255,255,255,0.2),#fff 100%);\ 142 background:-o-linear-gradient(left,rgba(255,255,255,0.2),#fff 100%);\ 143 background:-ms-linear-gradient(left,rgba(255,255,255,0.2),#fff 100%);\ 144 background:linear-gradient(to right,rgba(255,255,255,0.2),#fff 100%)}\ 145 #mt_c p.nopadding{\ 146 margin:0;padding:0\ 147 }\ 148 #mt_c p .url{\ 149 color:#0f5bd9;text-decoration:underline}\ 150 #mt_c blockquote{\ 151 font-family:Arial,sans-serif;font-size:13px;line-height:18px;\ 152 padding:0;margin:6px 25px 20px 25px;background-image:none;background:transperent}\ 153 #mt_c blockquote strong{\ 154 font-weight:700;color:#d31;text-decoration:underline}\ 155 #mt_c input[type=\'text\']{\ 156 width:100%;background-color:#fff;border:1px solid #d9d9d9;\ 157 border-radius:1px;box-sizing:border-box;font-size:13px;padding:3px 8px;\ 158 resize:none;text-align:start;word-wrap:break-word}\ 159 #mt_c input[type=\'text\']:focus{\ 160 outline:0;border-color:#4d90fe}\ 161 #mt_c div.buttons{\ 162 margin:22px 0 0;text-align:right}\ 163 #mt_c input[type=\'button\']{\ 164 background-color:#f5f5f5;background-image:-webkit-linear-gradient(top,#f5f5f5,#f1f1f1);\ 165 border-radius:2px;border:1px solid #e2e2e2;color:#444;\ 166 font-family:Arial,sans-serif;font-size:11px;font-weight:700;\ 167 height:29px;letter-spacing:normal;line-height:27px;\
168 margin:0 0 0 16px;padding:0 15px;text-align:center;outline:0}\ 169 #mt_c input[type=\'button\']:hover{\ 170 border-color:#d2d2d2}\ 171 </style>'); 172 173 // Add html to body end 174 if(settings.initHtml && (body.find('#mt_c').length === 0)) body.append('<div id="mt_c">'+ 175 '<div class="loading"><div class="spinner"></div><div class="overlay"></div></div>'+ 176 '<div class="close mt_cl"></div>'+ 177 '<div class="title feedback"><h1>'+settings.l10n.title+'</h1></div>'+ 178 '<p class="msg"></p>'+ 179 '<p class="nowrap">'+settings.l10n.urlHint+' <span class="url"></span></p>'+ 180 '<p class="nopadding">'+settings.l10n.errTextHint+'</p>'+ 181 '<blockquote></blockquote>'+ 182 '<p>'+settings.l10n.yourComment+'</p>'+ 183 '<p><input type="text" maxlength="256" value="" placeholder="'+settings.l10n.commentPlaceholder+'" /></p>'+ 184 '<div class="buttons">'+ 185 '<input type="button" class="mt_cl" value="'+settings.l10n.cancel+'" />'+ 186 '<input type="button" class="mt_snd" value="'+settings.l10n.send+'" />'+ 187 '</div>'+ 188 '</div><div id="mt_o"></div>'); 189 190 var overlay = body.find('#mt_o'), 191 content = body.find('#mt_c'), 192 loading = content.find('div.loading').first(), 193 title = content.find('div.title').first(), 194 message = content.find('p.msg').first(), 195 url = content.find('span.url').first(), 196 textdata = content.find('blockquote').first(), 197 comment = content.find('input[type=text]').first(), 198 close = content.find('.mt_cl'), 199 send = content.find('.mt_snd'), 200 201 autocloseTimer = null, 202 203 // Get selected text 204 getSelectionText = function() { 205 var text = ''; 206 if (window.getSelection) { 207 text = window.getSelection().toString(); 208 } else if (document.selection && document.selection.type != 'Control') { 209 text = document.selection.createRange().text; 210 } 211 return text; 212 }, 213 214 // Get all unselected text (return {before:N1,after:N2}) 215 // http://stackoverflow.com/a/9000719 216 getUnselectedText = function(e){var t,n,o,a="",r="";return"undefined"!=typeof window.getSelection?(t=window.getSelection(),t.rangeCount?n=t.getRangeAt(0):(n=document.createRange(),n.collapse(!0)),o=document.createRange(),o.selectNodeContents(e),o.setEnd(n.startContainer,n.startOffset),a=o.toString(),o.selectNodeContents(e),o.setStart(n.endContainer,n.endOffset),r=o.toString()):(t=document.selection)&&"Control"!=t.type&&(n=t.createRange(),o=document.body.createTextRange(),o.moveToElementText(e),o.setEndPoint("EndToStart",n),a=o.text,o.moveToElementText(e),o.setEndPoint("StartToEnd",n),r=o.text),{before:a,after:r}}, 217 // Make string escape (html chars) 218 // http://stackoverflow.com/a/12034334 219 escapeHtml = function(string) { 220 var entityMap = {"&": "&","<": "<",">": ">",'"': '"',"'": ''',"/": '/'}; 221 return String(string).replace(/[&<>"'\/]/g, function (s) { 222 return entityMap[s]; 223 }); 224 }, 225 226 // Clear string from any 'invalid' chars and empty spaces 227 clearString = function(s) { 228 return s.replace(/\s+/g, ' ').replace(/[^a-zа-ÑÑ0-9\.\,\ \_\-\(\)\[\]\{\}\`\~\@\#\$\%\^\:\*]/gi, ''); 229 }, 230 231 // Move window to screen center 232 centerWindow = function(){ 233 content.css({ 234 'margin-top' : -(content.height()/2 + parseInt(content.css('padding-top'))), 235 'margin-left' : -((content.width()/2) + parseInt(content.css('padding-left'))) 236 }); 237 }, 238 239 // Show loading splash 240 showLoading = function(visible){ 241 if(typeof visible === 'boolean') 242 if(visible) { 243 if($.isFunction(settings.onLoadingShow)) settings.onLoadingShow(visible); // callback 244 loading.show().find('*').show(); // show 'loading' container 245 } else { 246 if($.isFunction(settings.onLoadingHide)) settings.onLoadingHide(visible); // callback 247 loading.hide(); // hide 'loading' 248 } 249 }, 250 251 // Show mistake window 252 showWindow = function(visible) { 253 if(typeof visible === 'boolean') 254 if(visible) { // If we need to show 255 if($.isFunction(settings.onShow)) settings.onShow(visible); // callback 256 if(settings.hideBodyScroll) html.addClass('mistake-open'); // hide body scroll 257 content.find('*').show(); // show all inside objects 258 title.removeClass().addClass('title feedback'); // setup default title classes 259 message.html('').hide(); // reset and hide 'msg' container 260 loading.hide(); // hide 'loading' container 261 overlay.show(settings.animateSpeed); // show overlay 262 centerWindow(); // center message window (text must be setted before this function call) 263 content.show(settings.animateSpeed); // and show window 264 return true; 265 } else { 266 if($.isFunction(settings.onHide)) settings.onHide(visible); // callback 267 if(settings.hideBodyScroll) html.removeClass("mistake-open"); 268 content.hide(settings.animateSpeed); // hide window 269 overlay.hide(settings.animateSpeed); // hide overlay 270 return false; 271 } 272 return null; 273 }, 274 275 // Get mistake window state 276 windowIsOpen = function() { 277 return (overlay.is(':visible') && content.is(':visible'));
278 }, 279 280 // Modify window for message output, and show this shit 281 showMessage = function(caption, text, cssclass){ 282 if(!overlay.is(':visible')) overlay.show(); // show overlay if needed 283 content.find('*').hide(); // hide all inside window 284 showLoading(false); // hide loading screen (heed this line for callback) 285 content.find('div.close').show(); // show close cross 286 title.show().find('h1').html(caption).show(); // setup and show title 287 if(cssclass) title.removeClass().addClass('title '+cssclass); // setup title class 288 message.html(text).show(); // setup and show message 289 centerWindow(); // center window 290 291 if(settings.autocloseTime > 0) { 292 clearInterval(autocloseTimer); 293 autocloseTimer = setInterval(function() { 294 showWindow(false); 295 clearInterval(autocloseTimer); 296 }, settings.autocloseTime); 297 } 298 299 if(!content.is(':visible')) content.show(); // show content if needed 300 }, 301 302 // Make email addr validation 303 // http://stackoverflow.com/a/46181/2252921 304 validEmail = function(addr) { 305 return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(addr); 306 }, 307 308 // Send main function 309 // ------------------ 310 sendMail = function(){ 311 if(!validEmail(settings.mailTo)) 312 return log('Email "'+settings.mailTo+'" is not valid'); 313 314 if(!validEmail(settings.mailFrom)) 315 return log('Declare valid "Mail From" address'); 316 317 var mailBody = '<html lang="en"><head><meta charset="utf-8" /></head><body>'+ 318 '<h2>'+settings.l10n.mailTitle+'</h2>'+"\n"+ 319 '<p><small>'+settings.l10n.urlHint+' <a href="'+url.text()+'" target="_blank">'+url.text()+'</a></small></p>'+"\n\n"+ 320 '<p>'+settings.l10n.errTextHint+'</p>'+"\n"+ 321 '<blockquote>'+textdata.html()+'</blockquote>'+"\n\n"; 322 if(comment.val()) 323 mailBody += '<p>'+settings.l10n.userComment+'<br />'+"\n"+'<em>'+comment.val()+'</em></p>'; 324 mailBody += '</body></html>'; 325 326 var apiUrl = '', 327 mailData = { 328 'key': '', 329 'message': { 330 'from_email': settings.mailFrom, 331 'to': [{'email': settings.mailTo, 'type': 'to'}], 332 'autotext': 'true', 333 'subject': clearString(settings.l10n.mailSubject), 334 'html': mailBody 335 } 336 }; 337 338 // I think api key length forever eq. 22 339 if(settings.sendmailUrl.length > 0){ 340 mailData.key = settings.mandrillKey; 341 apiUrl = settings.sendmailUrl; 342 } 343 344 // I think api key length forever eq. 22 345 if(settings.mandrillKey && settings.mandrillKey.length == 22){ 346 mailData.key = settings.mandrillKey; 347 // Docs - https://mandrillapp.com/api/docs/messages.JSON.html#method=send 348 apiUrl = 'https://mandrillapp.com/api/1.0/messages/send.json'; 349 } 350 351 if(apiUrl.length == 0) { 352 showMessage('Wrong settings', 'Check plugin settings', 'fire'); 353 return false; 354 } 355 356 $.ajax({
357 type: 'POST', 358 url: apiUrl, 359 data: mailData 360 }).done(function(response) { 361 if($.isFunction(settings.onAjaxDone)) settings.onAjaxDone(response); // callback 362 if(((typeof response[0] !== 'undefined') && (response[0].status === 'sent')) 363 || ((typeof response.code !== 'undefined') && (response.code == 1))) { 364 if($.isFunction(settings.onSendMail)) settings.onSendMail(response); // callback 365 showMessage(settings.l10n.mailSended, settings.l10n.mailSendedDesc, 'star'); 366 } else { 367 if($.isFunction(settings.onAjaxResultError)) settings.onAjaxResultError(response); // callback 368 showMessage(settings.l10n.mailNotSended, settings.l10n.mailNotSendedDesc, 'fire'); 369 log('Request was sended, but server answer is not valid'); 370 } 371 }).error(function(response){ 372 if($.isFunction(settings.onAjaxSendError)) settings.onAjaxSendError(response); // callback 373 showMessage(settings.l10n.mailNotSended, settings.l10n.mailNotSendedDesc, 'fire'); 374 log('Ajax request error with status "'+response.status+'"'); 375 }); 376 377 showLoading(true); 378 }; 379 380 // Event on objects with close .class 381 close.on('click', function(){ 382 showWindow(false); 383 }); 384 385 // Event on objects with send .class 386 send.on('click', function(){ 387 sendMail(); 388 }); 389 390 // Event on Ctrl + Enter 391 body.keydown(function (e) { 392 if (e.ctrlKey && e.keyCode == 13) { 393 if($.isFunction(settings.onCtrlEnter)) settings.onCtrlEnter(); // callback 394 var unselected = getUnselectedText(document.body), 395 atStart = clearString(unselected.before.slice(-settings.contextLength)), 396 atEnd = clearString(unselected.after.slice(0, settings.contextLength)), 397 selectedText = escapeHtml(getSelectionText()).replace(/(\r\n|\n|\r)/gm, ' '); 398 if(selectedText.length < 1) 399 return false; 400 if(selectedText.length > settings.textLimit) { 401 log('Too many text'); 402 return false; 403 } 404 405 comment.val(''); // clear comment input 406 407 url.text(window.location.href); 408 textdata.html('…' + atStart + '<strong>' + selectedText + '</strong>' + atEnd + '…'); 409 410 showWindow(true); 411 } 412 }); 413 414 // event on ESC key pressed 415 body.keyup(function(e) { 416 if (settings.closeOnEsc && windowIsOpen() && e.keyCode == 27) { 417 if($.isFunction(settings.onEscPressed)) settings.onEscPressed(); // callback 418 showWindow(false); 419 } 420 }); 421}; 422 423})(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.