1 2 3 4document.addEventListener('DOMContentLoaded', function () { 5 6 app = { 7 clean: true, 8 grammar: true, 9 langs: [ 10 { "name": "Auto detect language", "code": "auto", "longCode": "auto" }, 11 { "name": "Belarusian", "code": "be", "longCode": "be-BY" }, 12 { "name": "Breton", "code": "br", "longCode": "br-FR" }, 13 { "name": "Catalan", "code": "ca", "longCode": "ca-ES" }, 14 { "name": "Catalan (Valencian)", "code": "ca", "longCode": "ca-ES-valencia" }, 15 { "name": "Chinese", "code": "zh", "longCode": "zh-CN" }, 16 { "name": "Danish", "code": "da", "longCode": "da-DK" }, 17 { "name": "Dutch", "code": "nl", "longCode": "nl" }, 18 { "name": "English", "code": "en", "longCode": "en" }, 19 { "name": "English (Australian)", "code": "en", "longCode": "en-AU" }, 20 { "name": "English (Canadian)", "code": "en", "longCode": "en-CA" }, 21 { "name": "English (GB)", "code": "en", "longCode": "en-GB" }, 22 { "name": "English (New Zealand)", "code": "en", "longCode": "en-NZ" }, 23 { "name": "English (South African)", "code": "en", "longCode": "en-ZA" }, 24 { "name": "English (US)", "code": "en", "longCode": "en-US" }, 25 { "name": "Esperanto", "code": "eo", "longCode": "eo" }, 26 { "name": "French", "code": "fr", "longCode": "fr" }, 27 { "name": "Galician", "code": "gl", "longCode": "gl-ES" }, 28 { "name": "German", "code": "de", "longCode": "de" }, 29 { "name": "German (Austria)", "code": "de", "longCode": "de-AT" }, 30 { "name": "German (Germany)", "code": "de", "longCode": "de-DE" }, 31 { "name": "German (Swiss)", "code": "de", "longCode": "de-CH" }, 32 { "name": "Greek", "code": "el", "longCode": "el-GR" }, 33 { "name": "Italian", "code": "it", "longCode": "it" }, 34 { "name": "Japanese", "code": "ja", "longCode": "ja-JP" }, 35 { "name": "Khmer", "code": "km", "longCode": "km-KH" }, 36 { "name": "Persian", "code": "fa", "longCode": "fa" }, 37 { "name": "Polish", "code": "pl", "longCode": "pl-PL" }, 38 { "name": "Portuguese", "code": "pt", "longCode": "pt" }, 39 { "name": "Portuguese (Angola preAO)", "code": "pt", "longCode": "pt-AO" }, 40 { "name": "Portuguese (Brazil)", "code": "pt", "longCode": "pt-BR" }, 41 { "name": "Portuguese (Mozambique preAO)", "code": "pt", "longCode": "pt-MZ" }, 42 { "name": "Portuguese (Portugal)", "code": "pt", "longCode": "pt-PT" }, 43 { "name": "Romanian", "code": "ro", "longCode": "ro-RO" }, 44 { "name": "Russian", "code": "ru", "longCode": "ru-RU" }, 45 { "name": "Simple German", "code": "de-DE-x-simple-language", "longCode": "de-DE-x-simple-language" }, 46 { "name": "Slovak", "code": "sk", "longCode": "sk-SK" }, 47 { "name": "Slovenian", "code": "sl", "longCode": "sl-SI" }, 48 { "name": "Spanish", "code": "es", "longCode": "es" }, 49 { "name": "Swedish", "code": "sv", "longCode": "sv" }, 50 { "name": "Tagalog", "code": "tl", "longCode": "tl-PH" }, 51 { "name": "Tamil", "code": "ta", "longCode": "ta-IN" }, 52 { "name": "Ukrainian", "code": "uk", "longCode": "uk-UA" }], 53 54 selectedLanguage: "auto", 55 defaultConvertedText: "Converted text appears here:", 56 setLastState: function () { 57 try { 58 // $("#convertedcode").find('div').append(" "); 59 var textCompressed = $("#convertedcode") 60 .clone() //clone the element 61 .find('.dropdown-content').remove() //select/remove .dropdown-content 62 .end().text(); //go back to selected element 63 64 // console.log('textCompressed', textCompressed) 65 66 textCompressed = LZString.compressToEncodedURIComponent(textCompressed); 67 var grammarState = { 68 lang: $("#langs").val() || 'auto', 69 text: textCompressed 70 }; 71 // console.log('grammarState', grammarState) 72 localStorage.setItem("grammarChecker", JSON.stringify(grammarState)); 73 } catch (err) { 74 console.log(err); 75 $("#langs").val('auto') 76 $('#convertedcode').text(''); 77 app.setLastState(); 78 } 79 }, 80 81 getLastState: function () { 82 try { 83 var grammarState = localStorage.getItem("grammarChecker"); 84 if (grammarState) { 85 grammarState = JSON.parse(grammarState); 86 var textStr = LZString.decompressFromEncodedURIComponent(grammarState.text); 87 $('#convertedcode').text(textStr); 88 $("#langs").val(grammarState.lang); 89 } else if (!$("#langs").val()) { 90 $("#langs").val('auto'); 91 } 92 app.setCode(); 93 } catch (err) { 94 console.log(err) 95 app.setLastState(); 96 } 97 }, 98 99 openInNewTab: function (url) { 100 var win = window.open(url, '_blank'); 101 win.focus(); 102 }, 103 104 setCode: function (mode, ele) { 105 if (!_.isUndefined(ele)) { 106 this.clearButtons(ele); 107 } 108 try { 109 $('#convertedcode').find('.dropdown-content').remove(); 110 var textContent = $('#convertedcode').text(); 111 // Trim to a maximum length before sending to the server 112 var length = 19900; 113 textContent = textContent.substring(0, length); 114 // https://stackoverflow.com/questions/36408015/regex-for-adding-a-space-or-period-for-new-sentence-under-certain-conditions 115 textContent = textContent.replace(/([^0-9.])\.([^0-9 ])/g, '$1. $2');
115 // Add space after period if there is none 116 var clean = _.escape(textContent); 117 if (clean) { 118 $('#convertedcode').html(s.clean(clean)); 119 } 120 textContent = $('#convertedcode').text(); 121 122 switch (mode) { 123 case "grammar": 124 //https://languagetool.org/api/v2/check - post! 125 var formData = { 126 disabledRules: "WHITESPACE_RULE", 127 allowIncompleteResults: true, 128 text: textContent, 129 language: $("#langs").val(), 130 }; 131 if ($("#langs").val() === 'auto') { 132 formData.preferredVariants = navigator.language || 'en-US'; //Get from browser lang 133 } 134 135 var jqxhr = $.ajax({ 136 type: "POST" 137 , url: "https://languagetool.org/api/v2/check" 138 , data: formData 139 }).done(function (data) { 140 141 var $context = $('#convertedcode'), 142 results = [], 143 ranges = []; 144 145 $.each(data.matches, function (key, value) { 146 results.push(value); 147 ranges.push({ start: value.offset, length: value.length }); 148 }); 149 150 //If nranges empty then let user know nothing found 151 if (_.isEmpty(ranges)) { 152 Swal.fire({ 153 title: 'Awessome!', 154 text: "We didn't find any suggestions for you. Awesome!", 155 icon: 'success', 156 confirmButtonText: 'Ok' 157 }) 158 159 } else { 160 Swal.fire({ 161 title: 'Some mistakes', 162 text: 'We found some suggestions for you! Hover over any highlighted phrases for details on potential errors. Blue buttons on the popups provide a quick way to correct mistakes. Click the Check Grammar button again if you want to recheck your corrections.', 163 icon: 'error', 164 confirmButtonText: 'Ok' 165 }) 166 167 } 168 169 $context.markRanges(ranges, { 170 debug: true, 171 each: function (node, range) { 172 var start = range.start; 173 var found = results.find(function (el) { 174 return el.offset === start; 175 }) || null; 176 if (found) { 177 node.classList.add(found.rule.issueType); 178 179 var replacements = found.replacements; 180 var replacement = (!_.isEmpty(replacements)) ? replacements[0].value : ""; 181 182 var urls = found.rule.urls; 183 var url = (!_.isEmpty(urls)) ? urls[0].value : ""; 184 185 var dropDownHTML = '<div class="dropdown-content">'; 186 dropDownHTML += ' <div class="dropdown-short">' + found.shortMessage + '</div>'; 187 dropDownHTML += ' <div class="dropdown-message">' + found.message + '</div>'; 188 189 if (replacement != "") { 190 dropDownHTML += '<div class="dropdown-row"><span class="dropdown-heading replacement">';
191 dropDownHTML += 'Replace with:'; 192 for (let index = 0; index < replacements.length; index++) { 193 if (index < replacements.length) { 194 dropDownHTML += '</br>'; 195 } 196 dropDownHTML += '</span><span class="btn btn-xs btn-primary replace">' + replacements[index].value + '</span>' 197 } 198 199 if (url != "") { 200 dropDownHTML += `<div class="explain-container"><a class="btn btn-xs btn-success explain" href="' + url + '">${chrome.i18n.getMessage("msg_12") || "Explain this suggestion..."}</a></div>`; 201 } 202 203 dropDownHTML += '</div>' 204 } 205 dropDownHTML += '<div class="dropdown-footer"> </div>'; 206 207 dropDownHTML += ' </div>'; 208 $(node).append(dropDownHTML); 209 210 $(node).find('.replace').on("click", function (e) { 211 // console.log('Replace....', $(this).text()); 212 $(node).text($(this).text()) 213 $(node).addClass("fixed") 214 app.setLastState(); 215 }); 216 217 $(node).find('.explain').on("click", function (e) { 218 // console.log('Explain....', $(this).attr('href')); 219 app.openInNewTab($(this).attr('href')) 220 }); 221 222 $(node).on("mouseenter", function (e) { 223 // console.log("scroll"); 224 $(this).find(".dropdown-content").scrollintoview(); 225 226 var markLeftPos = $(this).offset().left; 227 // console.log("markLeftPos", markLeftPos); 228 if (markLeftPos > 300) { 229 $(this).find(".dropdown-content").css({ 230 "right": 0 231 }); 232 } else { 233 $(this).find(".dropdown-content").css({ 234 "left": 0 235 }); 236 } 237 238 }) 239 } 240 } 241 }); 242 }) 243 break; 244 default: 245 break; 246 } 247 248 //Set counts 249 var regex = /\s+/gi; 250 var wordCount = textContent.trim().replace(regex, ' ').split(' ').length; 251 var totalChars = textContent.length; 252 var charCount = textContent.trim().length; 253 var charCountNoSpace = textContent.replace(regex, '').length; 254 var charCountClean = s.clean(textContent).length; 255 var counts = ` 256 <b title="Words, space delimited">Words</b>: <span class='countNum'>${wordCount}</span> | 257 <b title="Total characters">Characters</b>: <span class='countNum'>${totalChars}</span> | 258 <b title="Leading|trailing spaces removed">W/O leading spaces</b>: <span class='countNum'>${charCount}</span> | 259 <b title="Leading|trailing|multiple spaces removed">"Clean spaces</b>: <span class='countNum'>${charCountClean}</span> | 260 <b title="Without all spaces">W/O all spaces</b>: <span class='countNum'>${charCountNoSpace}</span> 261 `; 262 263 $('#counts').html(counts); 264 265 $('code').each(function (i, block) { 266 hljs.highlightBlock(block); 267 }); 268 app.setLastState(); 269 } catch (e) { 270 console.log(e) 271 } 272 }, 273 274 copyToClipBoard: function (str, mimetype) { 275 document.oncopy = function (event) { 276 event.clipboardData.setData(mimetype, str); 277 event.preventDefault(); 278 }; 279 try { 280 var successful = document.execCommand("Copy", false, null); 281 var msg = successful ? 'successful' : 'unsuccessful'; 282 //console.log('Copying text command was ' + msg); 283 } catch (err) {
284 //console.log('Oops, unable to copy'); 285 } 286 }, 287 288 clearButtons: function (activeButton) { 289 $("button[data-toggle='button']").not(activeButton).removeClass("active"); 290 $("button[data-toggle='button']").not(activeButton).addClass("btn-default"); 291 $("button[data-toggle='button']").not(activeButton).removeClass("btn-info"); 292 $("button[data-toggle='button']").not(activeButton).attr("aria-pressed", false); 293 }, 294 295 getQueryParams: function () { 296 //Check url location params 297 var qs = window.location.search.replace('?', '').split('&'), 298 qp = {}; 299 $.each(qs, function (i, v) { 300 var initial, pair = v.split('='); 301 if (initial = qp[pair[0]]) { 302 if (!$.isArray(initial)) { 303 qp[pair[0]] = [initial] 304 } 305 qp[pair[0]].push(pair[1]); 306 } else { 307 qp[pair[0]] = pair[1]; 308 } 309 return; 310 }); 311 return qp; 312 } 313 }; 314 315 //Load languages
316 app.langs.forEach(function (lang) { 317 var o = new Option(lang.name, lang.longCode); 318 //console.log(lang.name + " : " + lang.longCode) 319 $("#langs").append(o); 320 }); 321 322 $('#langs').on("click", function (e) { 323 // console.log($(this).val()); 324 }); 325 326 $('#grammar').on("click", function (e) { 327 var ele = $(this) 328 if (!ele.hasClass("active")) { 329 app.grammar = true; 330 //ele.addClass("btn-info").removeClass("btn-success") 331 $("#encodeLabel").html("Your grammar checked here:") 332 } else { 333 app.grammar = false; 334 //ele.addClass("btn-success").removeClass("btn-info") 335 $("#encodeLabel").html(app.defaultConvertedText) 336 } 337 app.setCode("grammar", $(this)); 338 }); 339 340 $('#copy').on("click", function (e) { 341 var copy = $('#convertedcode').text(); 342 app.copyToClipBoard(copy, 'text/plain'); 343 }); 344 345 $('#clear').on("click", function (e) { 346 $('#convertedcode').text(''); 347 app.setCode(); 348 }) 349 $('#convertedcode').on("input", function (e) { 350 // console.log('convertedcode') 351 app.setLastState(); 352 }); 353 354 var qp = app.getQueryParams(); 355 var textStr = ''; 356 if (qp.text) { 357 textStr = LZString.decompressFromEncodedURIComponent(qp.text); 358 $('#convertedcode').text(textStr); 359 $("#langs").val("auto"); 360 app.setCode(); 361 window.history.pushState({}, "Grammar Checker", window.location.href.split("?")[0]); //Object passes state props 362 $('#grammar').trigger('click', {}); 363 } else { 364 app.getLastState(); 365 } 366 367 368});
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.