Changeset 1795661
- Timestamp:
- 01/02/2018 12:05:31 PM (8 years ago)
- Location:
- flip
- Files:
-
- 23 added
- 3 edited
-
tags/1.5.9 (added)
-
tags/1.5.9/engine.js (added)
-
tags/1.5.9/flip.css (added)
-
tags/1.5.9/flip.php (added)
-
tags/1.5.9/flip9.png (added)
-
tags/1.5.9/flip_tinymce.js (added)
-
tags/1.5.9/gram_error.png (added)
-
tags/1.5.9/languages (added)
-
tags/1.5.9/languages/flip-en_US.mo (added)
-
tags/1.5.9/languages/flip-en_US.po (added)
-
tags/1.5.9/languages/flip-pt_BR.mo (added)
-
tags/1.5.9/languages/flip-pt_BR.po (added)
-
tags/1.5.9/languages/flip-pt_PT.mo (added)
-
tags/1.5.9/languages/flip-pt_PT.po (added)
-
tags/1.5.9/languages/flip.pot (added)
-
tags/1.5.9/murmurhash3_gc.js (added)
-
tags/1.5.9/ort_error.png (added)
-
tags/1.5.9/readme.txt (added)
-
tags/1.5.9/settings_page.php (added)
-
tags/1.5.9/status_checking.png (added)
-
tags/1.5.9/status_disabled.png (added)
-
tags/1.5.9/status_haserrors.png (added)
-
tags/1.5.9/status_noerrors.png (added)
-
trunk/engine.js (modified) (1 diff)
-
trunk/flip.php (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
flip/trunk/engine.js
r1665320 r1795661 1 1 (function (DOM) { 2 if (DOM.FLiPEngine) return; 3 DOM.FLiPEngine = function (textContainer) { 4 var self = this; 5 6 /* configs */ 7 var tagsBlock = { 8 "address": true, 9 "area": true, 10 "blockquote": true, 11 "br": true, 12 "center": true, 13 "dir": true, 14 "div": true, 15 "dl": true, 16 "fieldset": true, 17 "form": true, 18 "h1": true, 19 "h2": true, 20 "h3": true, 21 "h4": true, 22 "h5": true, 23 "h6": true, 24 "hr": true, 25 "isindex": true, 26 "menu": true, 27 "noframes": true, 28 "noscript": true, 29 "ol": true, 30 "p": true, 31 "pre": true, 32 "script": true, 33 "td": true, 34 "li": true, 35 "layer": true, 36 "article": true, 37 "aside": true, 38 "details": true, 39 "figcaption": true, 40 "figure": true, 41 "footer": true, 42 "header": true, 43 "hgroup": true, 44 "nav": true, 45 "section": true, 46 "summary": true 2 if (DOM.FLiPEngine) return; 3 DOM.FLiPEngine = function (textContainer) { 4 var self = this; 5 6 /* configs */ 7 var tagsBlock = { 8 "address": true, 9 "area": true, 10 "blockquote": true, 11 "br": true, 12 "center": true, 13 "dir": true, 14 "div": true, 15 "dl": true, 16 "fieldset": true, 17 "form": true, 18 "h1": true, 19 "h2": true, 20 "h3": true, 21 "h4": true, 22 "h5": true, 23 "h6": true, 24 "hr": true, 25 "isindex": true, 26 "menu": true, 27 "noframes": true, 28 "noscript": true, 29 "ol": true, 30 "p": true, 31 "pre": true, 32 "script": true, 33 "td": true, 34 "li": true, 35 "layer": true, 36 "article": true, 37 "aside": true, 38 "details": true, 39 "figcaption": true, 40 "figure": true, 41 "footer": true, 42 "header": true, 43 "hgroup": true, 44 "nav": true, 45 "section": true, 46 "summary": true 47 }; 48 49 /* Options */ 50 51 this.defaultSettings = { 52 NumMaxSugest: 4, 53 ScaytEnable: true, 54 AutoStart: true, 55 Language: 'pt-pt', //'pt-br', 'es' 56 Acordo: true, 57 Webservice: '', 58 GrammarSet: 'Common', 59 CanAddWords: false, 60 Key: '', 61 MaxErrors: 200, 62 IgnoreTags: "", 63 /* Events */ 64 onRequest: null, 65 onSuccess: null, 66 onError: null, 67 onTimeout: null 68 }; 69 70 function extendOptions(d, n) { 71 var r = {}; 72 Object.keys(d).forEach(function (f) { 73 r[f] = n.hasOwnProperty(f) ? n[f] : d[f]; 74 }); 75 return r; 76 } 77 78 var _settings = extendOptions(this.defaultSettings, {}); 79 80 var scaytImediateSpellChars = ' .,;:!?\xA0', 81 _flipRunning = false, 82 _flipStarting = false, 83 _pba_ort_error = 'pba_ort_error', 84 _pba_gram_error = 'pba_gram_error', 85 _pba_hide_error = 'pba_hide_error', 86 _ignoredWords = [], 87 _minVersion = "1.5.0", 88 _wsVersion = "0.0.0", 89 _versionValid = false, 90 _hasInit = false, 91 _ignoretags = {}; 92 93 function calcIgnoreTags() { 94 _ignoretags = {}; 95 _settings.IgnoreTags && _settings.IgnoreTags.constructor === String && _settings.IgnoreTags.split(' ').forEach(function (f) { 96 var split = f.split(/([\.@#][a-z:=\d]+)/gi).filter(function (i) { return i; }); 97 if (split.length === 0) return; 98 var value = {}; 99 if (split.length > 1) { 100 var selector = split[1].substr(1); 101 switch (split[1][0]) { 102 case '.': 103 value.attr = 'class'; 104 value.value = selector; 105 break; 106 case '#': 107 value.attr = 'id'; 108 value.value = selector; 109 break; 110 default: 111 var sa = selector.split('='); 112 value.attr = sa[0]; 113 value.value = sa[1]; 114 break; 115 } 116 } 117 _ignoretags[split[0]] = value; 118 }); 119 } 120 121 calcIgnoreTags(); 122 123 function trim(str) { 124 var trimRegex = /(?:^[\s]+)|(?:[\s]+$)/g; 125 return str.replace(trimRegex, ''); 126 } 127 128 var events = {}; 129 function doEvent(name, obj, args) { 130 for (var i = 0; i < (events[name] && events[name].length || 0); i++) { 131 events[name][i].apply(obj, args); 132 } 133 } 134 135 136 /* métodos publicos */ 137 138 this.setTextContainer = function (tc) { 139 textContainer = tc; 140 spellcheckQueue = []; 141 }; 142 143 this.on = function (event, callback) { 144 if (event && typeof (event) === 'string' && 145 callback && typeof (callback) === 'function') { 146 if (!events[event]) 147 events[event] = []; 148 var e = events[event]; 149 var idx = e.indexOf(callback); 150 if (idx < 0) 151 e.push(callback); 152 } 153 }; 154 155 this.isError = function (element) { 156 return isError(element) && element.className.indexOf(_pba_hide_error) < 0; 157 }; 158 159 this.Settings = function (settings, value) { 160 if (typeof (settings) === 'undefined') 161 return _settings; 162 else if (typeof (settings) === 'object') { 163 Object.keys(settings).forEach(function (f) { 164 self.Settings(f, settings[f]); 165 }); 166 } 167 else if (typeof (settings) === 'string') { 168 if (typeof (value) !== 'undefined') { 169 if (settings.substr(0, 2) === 'on') 170 this.on(settings.substr(2), value); 171 else 172 _settings[settings] = value; 173 174 if (settings === 'IgnoreTags') 175 calcIgnoreTags(); 176 } 177 else 178 return _settings[settings]; 179 } 180 }; 181 182 this.isRunning = function () { 183 return _flipRunning; 184 }; 185 186 this.status = function () { 187 if (_versionValid) 188 return spellcheckQueue.length > 0 || waitingForResponseForBlock ? "checking" : "idle"; 189 else 190 return "verErr"; 191 }; 192 193 this.hasErrors = function () { 194 if (!(textContainer && textContainer.ownerDocument)) 195 return false; 196 var e = textContainer.ownerDocument.getElementsByTagName('span'); 197 for (var i = 0; i < e.length; i++) { 198 if (isError(e[i])) 199 return true; 200 } 201 return false; 202 }; 203 204 this.textContainer = function (textcontainer) { 205 if (typeof (textcontainer) === 'undefined') 206 return textContainer; 207 else 208 textContainer = textcontainer; 209 }; 210 211 this.startEngine = function (coldStart /* false */) { 212 if (_flipRunning || _flipStarting) return; 213 _flipStarting = true; 214 textContainer.setAttribute("spellcheck", false); 215 216 initEngine(function () { 217 _flipRunning = true; 218 _flipStarting = false; 219 if (_versionValid) { 220 removeErrors(textContainer); // para ter a certeza que não fica nada sublinhado 221 doSpellCheck(coldStart); 222 } 223 else 224 console.log("FLiP Api version is " + _wsVersion + " must be atleast " + _minVersion); 225 }); 226 }; 227 228 this.stopEngine = function () { 229 if (!_flipRunning) return; 230 stopSpellCheck(); 231 _flipRunning = false; 232 _flipStarting = false; 233 }; 234 235 this.restartEngine = function (coldStart /* true */) { 236 this.stopEngine(); 237 this.startEngine(coldStart !== false); 238 }; 239 240 this.shutdown = function () { 241 this.stopEngine(); 242 _hasInit = false; 243 }; 244 245 this.spellCheck = function () { 246 if (_flipRunning && _versionValid) 247 doSpellCheck(); 248 249 }; 250 251 this.removeMarkupFromHtml = function (html) { 252 if (!(textContainer && textContainer.ownerDocument && html)) 253 return; 254 var escaped = html.replace(/\ /g, "___NBSP___"); 255 var div = textContainer.ownerDocument.createElement('div'); 256 div.innerHTML = escaped; 257 removeErrors(div, true); 258 259 return div.innerHTML.replace(/\ /g, " ").replace(/___NBSP___/g, " "); 260 }; 261 262 this.replaceWord = function (node, word) { 263 if (!(node && node.error)) return; 264 265 node.textContent = word; 266 addToQueue({ block: node.error.block }); 267 removeNode(node); 268 }; 269 270 this.addWord = function (node) { 271 if (!(node && node.error && trim(node.textContent).length > 0 && _settings.CanAddWords)) return; 272 273 doJSONRequest({ 274 Url: geturl(), 275 Action: ['userdict', _settings.Language, _settings.Acordo ? 'true' : 'false', encodeURIComponent(node.textContent || "")].join("/"), 276 key: _settings.Key, 277 Method: "PUT" 278 }); 279 280 this.ignoreAllWords(node); 281 }; 282 283 this.ignoreWord = function (node) { 284 if (!(node && node.error)) return; 285 _ignoredWords.push(node.textContent); 286 removeNode(node); 287 }; 288 289 this.ignoreAllWords = function (node) { 290 if (!(node && node.error)) return; 291 var word = node.textContent; 292 _ignoredWords.push(word); 293 var p = [].concat.apply([], node.ownerDocument.getElementsByTagName('span')); 294 for (var i = 0; i < p.length; i++) { 295 var el = p[i]; 296 if (isError(el) && el.textContent === word) 297 removeNode(el); 298 } 299 }; 300 301 var onChangeTimer = 0; 302 var lastElement = null; // usado para saber o elemento anterior a uma newline 303 this.GetKeyEventHandler = function () { 304 return function (evt) { 305 var sel = getCaretPosition(); 306 if (_flipRunning && _versionValid && sel) { 307 var key = evt.which || evt.charCode || evt.keyCode || 0; 308 var element = null; 309 if (lastElement && (key === 13 || key === 8 || key === 46)) { 310 element = lastElement; 311 } 312 else { 313 var node = sel.node; 314 while (node && node.parentNode && node !== textContainer) { 315 if (isError(node)) 316 removeNode(node); 317 if (!element && tagsBlock[node.nodeName.toLowerCase()]) 318 element = node; 319 node = node.parentNode; 320 } 321 lastElement = element; 322 } 323 324 if (_settings.ScaytEnable) { 325 clearTimeout(onChangeTimer); 326 327 if (checkErrorCount() && scaytImediateSpellChars.indexOf(String.fromCharCode(key)) >= 0 328 || key === 13 || key === 8 || key === 46) { 329 330 if (ignoreTag(element)) 331 return; 332 var blocks = markBlocks(element); 333 blocks.forEach(function (v) { 334 if (key === 13 || v.checked === 0) { 335 addToQueue({ block: v, options: { newline: key === 13 } }); 336 } 337 }); 338 } 339 } 340 } 341 }; 342 }; 343 344 /* Selection METHODS */ 345 function hasSelection() { 346 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 347 return sel && !sel.isCollapsed; 348 } 349 350 function getCaretPosition() { 351 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 352 if (sel && sel.isCollapsed) { 353 var range = sel && sel.type !== 'None' && sel.getRangeAt && sel.rangeCount ? sel.getRangeAt(0) : null; 354 if (range) { 355 range.collapse(true); 356 return { 357 node: range.startContainer, 358 offset: range.startOffset 359 }; 360 } 361 } 362 return null; 363 } 364 365 function setCaretPosition(p) { 366 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 367 if (sel.isCollapsed) { 368 var range = sel && sel.type !== 'None' && sel.getRangeAt && sel.rangeCount ? sel.getRangeAt(0) : textContainer.ownerDocument.createRange(); 369 if (range && sel && p && p.node && p.node.textContent && p.node.textContent.length >= p.offset) { 370 range.setStart(p.node, p.offset); 371 range.collapse(true); 372 sel.removeAllRanges(); 373 sel.addRange(range); 374 } 375 } 376 } 377 378 /* Selection METHODS END */ 379 380 381 /* Funções internas */ 382 383 function initDone(callback) { 384 return function (result) { 385 _hasInit = true; 386 if (result && !(result.ErrorCode || result.code)) { 387 _wsVersion = result.Application; 388 _versionValid = compareVersions(_wsVersion, _minVersion) >= 0; 389 } 390 callback(); 391 }; 392 } 393 394 function initEngine(callback) { 395 if (_hasInit) 396 callback(); 397 else { 398 var initdone = initDone(callback); 399 doJSONRequest({ 400 Url: geturl(), 401 Action: 'version', 402 Method: 'GET', 403 onRequest: function () { }, 404 onSuccess: initdone, 405 onError: initdone, 406 onTimeout: initdone 407 }); 408 } 409 } 410 411 function versionFromString(str) { 412 var values = []; 413 if (str) { 414 var arr = null; 415 var re = /([0-9]+)\.{0,1}/g; 416 while ((arr = re.exec(str)) !== null) 417 values.push(+arr[1]); 418 } 419 return values; 420 } 421 422 function compareVersions(v1, v2) { 423 var mv1 = versionFromString(v1); 424 var mv2 = versionFromString(v2); 425 var cmp = 0; 426 for (var i = 0; i < Math.min(mv1.length, mv2.length) && cmp === 0; i++) { 427 var r = mv1[i] - mv2[i]; 428 cmp = r !== 0 ? r / Math.abs(r) : 0; 429 } 430 431 return cmp; 432 } 433 434 function ignoreTag(element) { 435 if (_settings.IgnoreTags && element && element.nodeName) { 436 var itag = _ignoretags[element.nodeName.toLowerCase()]; 437 if (itag) { 438 if (itag.attr) { 439 var attrvalue = element.getAttribute(itag.attr); 440 return attrvalue ? attrvalue.indexOf(itag.value) >= 0 : false; 441 } 442 else 443 return true; 444 } 445 } 446 return false; 447 } 448 449 function geturl() { 450 var url = _settings.Webservice; 451 if (url.length > 0 && url[url.length - 1] !== '/') 452 url += '/'; 453 return url; 454 } 455 456 function isError(element) { 457 return (element && element.className && 458 (element.className.indexOf(_pba_ort_error) >= 0 || 459 element.className.indexOf(_pba_gram_error) >= 0)) === true; 460 } 461 462 function stopSpellCheck() { 463 removeErrors(textContainer); 464 } 465 466 var _doingCheck = false; 467 function doSpellCheck(coldStart) { 468 if (_doingCheck) return; 469 _doingCheck = true; 470 471 var p = []; 472 if (_settings.IgnoreTags) { 473 traverseDom(textContainer, function (node) { 474 if (node && node.nodeName) { 475 if (ignoreTag(node)) 476 return false; 477 if (tagsBlock[node.nodeName.toLowerCase()]) 478 p.push(node); 479 return true; 480 } 481 return false; 482 }); 483 } 484 else { 485 Object.keys(tagsBlock).forEach(function (f) { 486 var elements = textContainer.getElementsByTagName(f); 487 if (elements.length) 488 p.push.apply(p, elements); 489 }); 490 } 491 492 p.forEach(function (n) { 493 if (n.nodeType !== 1) 494 return; 495 496 if (coldStart) 497 n.blocks = null; 498 499 if (checkErrorCount()) { 500 var blocks = markBlocks(n); 501 blocks.forEach(function (v) { 502 if (v.checked === 1) { 503 if (!areErrorsMarked(v.erros)) 504 markErrors(v, v.erros); 505 } 506 else if (v.checked === 0) { 507 addToQueue({ block: v }); 508 } 509 }); 510 } 511 }); 512 _doingCheck = false; 513 } 514 515 var spellcheckQueue = []; 516 var waitingForResponseForBlock = null; 517 var _processing = false; 518 519 function addToQueue(b) { 520 if (b && b.block) { 521 if (spellcheckQueue.filter(function (f) { return f.block.hash === b.block.hash; }).length === 0) { 522 spellcheckQueue.push(b); 523 } 524 } 525 } 526 527 var _nextAutoCheck = 0; 528 function processQueue() { 529 if (_flipRunning && _versionValid && waitingForResponseForBlock === null) { 530 var time = (new Date()).getTime(); 531 if (spellcheckQueue.length > 0) { 532 if (_processing) 533 return; 534 _processing = true; 535 536 var element = spellcheckQueue.shift(); 537 538 if (!hasSelection() && checkErrorCount(true) && element && element.block && element.block.element && element.block.element.parentNode && element.block.element.offsetParent) { 539 var block = element.block; 540 541 var hash = getBlockHash(block); 542 if (hash !== block.hash) { 543 block.hash = null; 544 block.checked = 0; 545 } 546 else if (block.checked === 0) { 547 var texto = block.text; 548 if (trim(texto).length > 0) { 549 block.checked = 2; // a correr 550 block.request = { 551 id: doJSONRequest({ 552 Url: geturl(), 553 Action: ['check', _settings.Language, _settings.GrammarSet, _settings.Acordo ? 'true' : 'false'].join("/"), 554 key: _settings.Key, 555 onSuccess: responseReceived, 556 onTimeout: responseTimeout, 557 onError: responseError, 558 Params: { 559 nline: element.options && element.options.newline ? 'true' : 'false' 560 }, 561 Data: texto 562 }), 563 hash: hash 564 }; 565 waitingForResponseForBlock = block; 566 } 567 else 568 block.checked = 1; 569 } 570 } 571 else { 572 setTimeout(processQueue, 0); 573 } 574 _processing = false; 575 _nextAutoCheck = time + 15000; 576 } 577 else if (time > _nextAutoCheck) { 578 doSpellCheck(false); 579 _nextAutoCheck = time + 15000; 580 } 581 } 582 } 583 setInterval(processQueue, 100); 584 585 function doJSONRequest(options) { 586 var url = options.Url || null, 587 action = options.Action || null, 588 params = options.Params || null, 589 data = options.Data || null, 590 method = options.Method || "POST", 591 contenttype = options.contentType || "text/plain", 592 key = options.key || "", 593 // Callbacks - por defeito chama os eventos associados 594 on_r = options.onRequest || function (action, params, data) { doEvent("Request", this, [action, params, data]); }, 595 on_s = options.onSuccess || function (result) { doEvent("Success", this, [result]); }, 596 on_t = options.onTimeout || function () { doEvent("Timeout", this, null); }, 597 on_e = options.onError || function (response) { doEvent("Error", this, [response]); }; 598 599 var xmlhttprequest = new XMLHttpRequest(); 600 if (xmlhttprequest) { 601 var sparams = (params && ("?" + serialize(params))) || ""; 602 xmlhttprequest.open(method, url + action + sparams, true); 603 xmlhttprequest.setRequestHeader("Content-type", contenttype); 604 xmlhttprequest.setRequestHeader("X-Priberam-auth-key", key) 605 xmlhttprequest.responseType = "json"; 606 xmlhttprequest.async = true; 607 xmlhttprequest.ontimeout = on_t; 608 xmlhttprequest.onreadystatechange = function () { 609 if (xmlhttprequest.readyState === 4) { 610 var response; 611 try { 612 response = JSON.parse(xmlhttprequest.responseText); 613 } 614 catch (err) { 615 response = xmlhttprequest.response; 616 } 617 618 if (xmlhttprequest.status === 200) { 619 on_s(response); 620 } 621 else { 622 on_e({ 623 status: xmlhttprequest.status, 624 reason: xmlhttprequest.statusText 625 }); 626 spellcheckQueue = []; 627 } 628 } 629 47 630 }; 48 49 /* Options */ 50 51 this.defaultSettings = { 52 NumMaxSugest: 4, 53 ScaytEnable: true, 54 AutoStart: true, 55 Language: 'pt-pt', //'pt-br', 'es' 56 Acordo: true, 57 Webservice: '', 58 GrammarSet: 'Common', 59 CanAddWords: false, 60 Key: '', 61 MaxErrors: 200, 62 IgnoreTags: "", 63 /* Events */ 64 onRequest: null, 65 onSuccess: null, 66 onError: null, 67 onTimeout: null 631 xmlhttprequest.timeout = 10000; 632 633 on_r(action, params, data); 634 xmlhttprequest.send(data); 635 636 } 637 638 } 639 640 function serialize(obj, prefix) { 641 if (obj) { 642 var pre = (prefix && encodeURIComponent(prefix)) || ""; 643 return Object.keys(obj).map(function (k) { 644 return pre + encodeURIComponent(k) + "=" + encodeURIComponent(obj[k] === undefined ? "" : obj[k].toString()); 645 }).join("&"); 646 } 647 else 648 return ""; 649 } 650 651 function responseReceived(result) { 652 if (!result || (result && (result.ErrorCode || result.code))) { 653 doEvent("Error", this, [result]); 654 spellcheckQueue = []; 655 } 656 else if (waitingForResponseForBlock && !hasSelection()) { 657 doEvent("Success", this, [result]); 658 659 var element = waitingForResponseForBlock; 660 var newhash = getBlockHash(element); 661 // Verifica se a texto não mudou entretanto 662 if (newhash === element.request.hash) { 663 var offset = 0; 664 for (var j = 0; j < result.length; j++) { 665 var json = result[j]; 666 if (json.errors && json.errors.length > 0) { 667 for (var i = 0; i < json.errors.length; i++) { 668 json.errors[i].spos += offset; 669 json.errors[i].block = element; 670 var e1 = json.errors[i].spos; 671 var e2 = e1 + json.errors[i].len; 672 // ignora erros sobrepostos 673 var a = element.erros.filter(function (f) { 674 var f1 = f.spos; 675 var f2 = f.spos + f.len; 676 return f2 >= e1 && f1 <= e2; 677 }); 678 679 if (a.length == 0) 680 element.erros.push(json.errors[i]); 681 } 682 } 683 offset += json.len; 684 markErrors(element, element.erros); 685 element.checked = 1; 686 687 } 688 } 689 } 690 waitingForResponseForBlock = null; 691 } 692 693 function responseTimeout() { 694 doEvent("Timeout", this, null); 695 spellcheckQueue = []; 696 697 if (waitingForResponseForBlock) 698 waitingForResponseForBlock.hash = null; 699 waitingForResponseForBlock = null; 700 } 701 702 function responseError(err) { 703 doEvent("Error", this, [err]); 704 spellcheckQueue = []; 705 706 if (waitingForResponseForBlock) 707 waitingForResponseForBlock.hash = null; 708 waitingForResponseForBlock = null; 709 } 710 711 function checkErrorCount(update) { 712 var spans = [].concat.apply([], textContainer.ownerDocument.getElementsByClassName(_pba_gram_error)); 713 spans = [].concat.apply(spans, textContainer.ownerDocument.getElementsByClassName(_pba_ort_error)); 714 if (update) { 715 spans.forEach(function (v) { 716 v.setAttribute("class", v.getAttribute("class").replace(_pba_hide_error, "")); 717 }); 718 if (spans.length >= _settings.MaxErrors) { 719 spans.forEach(function (v) { 720 v.setAttribute("class", v.getAttribute("class").trim() + " " + _pba_hide_error); 721 }); 722 } 723 } 724 return spans.length < _settings.MaxErrors; 725 } 726 727 function getHash(text) { 728 return murmurhash3_32_gc(text || '', 25); 729 } 730 731 function doRangeIntersect(r1, r2, touch) { 732 if (!r1 || !r2) 733 return false; 734 touch = touch === false ? false : true; 735 try { 736 return r1.compareBoundaryPoints(Range.END_TO_START, r2) * r2.compareBoundaryPoints(Range.END_TO_START, r1) >= (touch ? 0 : 1); 737 } 738 catch (err) { 739 return false; 740 } 741 } 742 743 function traverseDom(node, callback) { 744 if (callback(node)) { 745 node = node.firstChild; 746 while (node) { 747 traverseDom(node, callback); 748 node = node.nextSibling; 749 } 750 } 751 } 752 753 function getBlockTextNodes(block) { 754 755 if (!block || !block.element) return []; 756 757 var textNodes = getAllTextNodes(block.element); 758 if (textNodes.length <= block.index) 759 return []; 760 return textNodes[block.index]; 761 } 762 763 function getAllTextNodes(root) { 764 765 var descendants = []; 766 var current = []; 767 traverseDom(root, function (node) { 768 if (node && node.nodeName && tagsBlock[node.nodeName.toLowerCase()]) { 769 if (current.length > 0) 770 descendants.push(current); 771 current = []; 772 if (node !== root) 773 return false; 774 } 775 else if (node.nodeType === 3) 776 current.push(node); 777 return true; 778 }); 779 if (current.length > 0) 780 descendants.push(current); 781 return descendants; 782 } 783 784 function createTag(tipo) { 785 786 var tag = textContainer.ownerDocument.createElement('span'); 787 tag.className = (tipo === 'spell' ? _pba_ort_error : _pba_gram_error); 788 if (tipo === 1) { 789 tag.onmouseover = function () { 790 //showToolTip(tag); 68 791 }; 69 70 function extendOptions(d, n) { 71 var r = {}; 72 Object.keys(d).forEach(function (f) { 73 r[f] = n.hasOwnProperty(f) ? n[f] : d[f]; 792 tag.onmouseout = function () { 793 //hideToolTip(tag); 794 }; 795 } 796 797 return tag; 798 } 799 800 function surroundNodeTextWithTag(block, offset, length, tag) { 801 802 var textNodes = getBlockTextNodes(block); 803 804 var startNode = null, endNode = null; 805 var start = offset; 806 var i = 0; 807 for (; i < textNodes.length; i++) { 808 if (textNodes[i].length > start) { 809 startNode = textNodes[i]; 810 break; 811 } 812 start -= textNodes[i].length; 813 } 814 815 if (!startNode) 816 return false; 817 818 var end = length + start; 819 for (; i < textNodes.length; i++) { 820 if (textNodes[i].length >= end) { 821 endNode = textNodes[i]; 822 break; 823 } 824 end -= textNodes[i].length; 825 } 826 if (!endNode) 827 return false; 828 829 try { 830 var range = textContainer.ownerDocument.createRange(); 831 range.setStart(startNode, start); 832 range.setEnd(endNode, end); 833 834 var pos = getCaretPosition(); 835 if (pos && pos.node) { 836 837 var caretRange = textContainer.ownerDocument.createRange(); 838 caretRange.setStart(pos.node, pos.offset); 839 caretRange.collapse(true); 840 841 if (range.compareBoundaryPoints(Range.START_TO_END, caretRange) < 0) { 842 if (range.startContainer === pos.node) { 843 tag.appendChild(range.extractContents()); 844 range.insertNode(tag); 845 //setCaretPosition({ 846 // node: tag.nextSibling, 847 // offset: pos.offset - range.startOffset - tag.textContent.length 848 //}); 849 } 850 else { 851 tag.appendChild(range.extractContents()); 852 range.insertNode(tag); 853 } 854 } 855 else if (range.compareBoundaryPoints(Range.START_TO_START, caretRange) > 0) { 856 tag.appendChild(range.extractContents()); 857 range.insertNode(tag); 858 } 859 else if (range.commonAncestorContainer == caretRange.commonAncestorContainer) { 860 var dif = pos.offset - range.startOffset; 861 if (range.startContainer === pos.node) 862 dif = pos.offset - range.startOffset; 863 else { 864 for (i = textNodes.indexOf(range.startContainer); i < textNodes.length && textNodes[i] !== pos.node; i++) { 865 dif += textNodes[i].textContent.length; 866 } 867 } 868 tag.appendChild(range.extractContents()); 869 range.insertNode(tag); 870 var textnode = null, lastnode = null; 871 traverseDom(tag, function (node) { 872 if (!textnode && node.nodeType === 3) { 873 if (node.textContent.length > dif) 874 textnode = node; 875 else 876 dif -= node.textContent.length; 877 lastnode = node; 878 } 879 return true; 74 880 }); 75 return r; 76 } 77 78 var _settings = extendOptions(this.defaultSettings, {}); 79 80 var scaytImediateSpellChars = ' .,;:!?\xA0', 81 _flipRunning = false, 82 _flipStarting = false, 83 _pba_ort_error = 'pba_ort_error', 84 _pba_gram_error = 'pba_gram_error', 85 _pba_hide_error = 'pba_hide_error', 86 _ignoredWords = [], 87 _minVersion = "1.5.0", 88 _wsVersion = "0.0.0", 89 _versionValid = false, 90 _hasInit = false, 91 _ignoretags = {}; 92 93 function calcIgnoreTags() { 94 _ignoretags = {}; 95 _settings.IgnoreTags && _settings.IgnoreTags.constructor === String && _settings.IgnoreTags.split(' ').forEach(function (f) { 96 var split = f.split(/([\.@#][a-z:=\d]+)/gi).filter(function (i) { return i; }); 97 if (split.length === 0) return; 98 var value = {}; 99 if (split.length > 1) { 100 var selector = split[1].substr(1); 101 switch (split[1][0]) { 102 case '.': 103 value.attr = 'class'; 104 value.value = selector; 105 break; 106 case '#': 107 value.attr = 'id'; 108 value.value = selector; 109 break; 110 default: 111 var sa = selector.split('='); 112 value.attr = sa[0]; 113 value.value = sa[1]; 114 break; 115 } 116 } 117 _ignoretags[split[0]] = value; 881 882 setCaretPosition({ 883 node: textnode ? textnode : lastnode, 884 offset: textnode ? dif : lastnode.length 118 885 }); 119 } 120 121 calcIgnoreTags(); 122 123 function trim(str) { 124 var trimRegex = /(?:^[\s]+)|(?:[\s]+$)/g; 125 return str.replace(trimRegex, ''); 126 } 127 128 var events = {}; 129 function doEvent(name, obj, args) { 130 for (var i = 0; i < (events[name] && events[name].length || 0) ; i++) { 131 events[name][i].apply(obj, args); 132 } 133 } 134 135 136 /* métodos publicos */ 137 138 this.setTextContainer = function (tc) { 139 textContainer = tc; 140 spellcheckQueue = []; 141 }; 142 143 this.on = function (event, callback) { 144 if (event && typeof (event) === 'string' && 145 callback && typeof (callback) === 'function') { 146 if (!events[event]) 147 events[event] = []; 148 var e = events[event]; 149 var idx = e.indexOf(callback); 150 if (idx < 0) 151 e.push(callback); 152 } 153 }; 154 155 this.isError = function (element) { 156 return isError(element) && element.className.indexOf(_pba_hide_error) < 0; 157 }; 158 159 this.Settings = function (settings, value) { 160 if (typeof (settings) === 'undefined') 161 return _settings; 162 else if (typeof (settings) === 'object') { 163 Object.keys(settings).forEach(function (f) { 164 self.Settings(f, settings[f]); 165 }); 166 } 167 else if (typeof (settings) === 'string') { 168 if (typeof (value) !== 'undefined') { 169 if (settings.substr(0, 2) === 'on') 170 this.on(settings.substr(2), value); 171 else 172 _settings[settings] = value; 173 174 if (settings === 'IgnoreTags') 175 calcIgnoreTags(); 176 } 177 else 178 return _settings[settings]; 179 } 180 }; 181 182 this.isRunning = function () { 183 return _flipRunning; 184 }; 185 186 this.status = function () { 187 if (_versionValid) 188 return spellcheckQueue.length > 0 || waitingForResponseForBlock ? "checking" : "idle"; 189 else 190 return "verErr"; 191 }; 192 193 this.hasErrors = function () { 194 if (!(textContainer && textContainer.ownerDocument)) 195 return false; 196 var e = textContainer.ownerDocument.getElementsByTagName('span'); 197 for (var i = 0; i < e.length; i++) { 198 if (isError(e[i])) 199 return true; 200 } 201 return false; 202 }; 203 204 this.textContainer = function (textcontainer) { 205 if (typeof (textcontainer) === 'undefined') 206 return textContainer; 207 else 208 textContainer = textcontainer; 209 }; 210 211 this.startEngine = function (coldStart /* false */) { 212 if (_flipRunning || _flipStarting) return; 213 _flipStarting = true; 214 textContainer.setAttribute("spellcheck", false); 215 216 initEngine(function () { 217 _flipRunning = true; 218 _flipStarting = false; 219 if (_versionValid) { 220 removeErrors(textContainer); // para ter a certeza que não fica nada sublinhado 221 doSpellCheck(coldStart); 222 } 223 else 224 console.log("FLiP Api version is " + _wsVersion + " must be atleast " + _minVersion); 225 }); 226 }; 227 228 this.stopEngine = function () { 229 if (!_flipRunning) return; 230 stopSpellCheck(); 231 _flipRunning = false; 232 _flipStarting = false; 233 }; 234 235 this.restartEngine = function (coldStart /* true */) { 236 this.stopEngine(); 237 this.startEngine(coldStart !== false); 238 }; 239 240 this.shutdown = function() { 241 this.stopEngine(); 242 _hasInit = false; 243 }; 244 245 this.spellCheck = function () { 246 if (_flipRunning && _versionValid) 247 doSpellCheck(); 248 249 }; 250 251 this.removeMarkupFromHtml = function (html) { 252 if (!(textContainer && textContainer.ownerDocument && html)) 253 return; 254 var escaped = html.replace(/\ /g, "___NBSP___"); 255 var div = textContainer.ownerDocument.createElement('div'); 256 div.innerHTML = escaped; 257 removeErrors(div, true); 258 259 return div.innerHTML.replace(/\ /g, " ").replace(/___NBSP___/g, " "); 260 }; 261 262 this.replaceWord = function (node, word) { 263 if (!(node && node.error)) return; 264 265 node.textContent = word; 266 addToQueue({ block: node.error.block }); 267 removeNode(node); 268 }; 269 270 this.addWord = function (node) { 271 if (!(node && node.error && trim(node.textContent).length > 0 && _settings.CanAddWords)) return; 272 273 doJSONRequest({ 274 Url: geturl(), 275 Action: ['userdict', _settings.Language, _settings.Acordo ? 'true' : 'false', encodeURIComponent(node.textContent || "")].join("/"), 276 key: _settings.Key, 277 Method: "PUT" 278 }); 279 280 this.ignoreAllWords(node); 281 }; 282 283 this.ignoreWord = function (node) { 284 if (!(node && node.error)) return; 285 _ignoredWords.push(node.textContent); 286 removeNode(node); 287 }; 288 289 this.ignoreAllWords = function (node) { 290 if (!(node && node.error)) return; 291 var word = node.textContent; 292 _ignoredWords.push(word); 293 var p = [].concat.apply([], node.ownerDocument.getElementsByTagName('span')); 294 for (var i = 0; i < p.length; i++) { 295 var el = p[i]; 296 if (isError(el) && el.textContent === word) 297 removeNode(el); 298 } 299 }; 300 301 var onChangeTimer = 0; 302 var lastElement = null; // usado para saber o elemento anterior a uma newline 303 this.GetKeyEventHandler = function () { 304 return function (evt) { 305 var sel = getCaretPosition(); 306 if (_flipRunning && _versionValid && sel) { 307 var key = evt.which || evt.charCode || evt.keyCode || 0; 308 var element = null; 309 if (lastElement && (key === 13 || key === 8 || key === 46)) { 310 element = lastElement; 311 } 312 else { 313 var node = sel.node; 314 while (node && node.parentNode && node !== textContainer) { 315 if (isError(node)) 316 removeNode(node); 317 if (!element && tagsBlock[node.nodeName.toLowerCase()]) 318 element = node; 319 node = node.parentNode; 320 } 321 lastElement = element; 322 } 323 324 if (_settings.ScaytEnable) { 325 clearTimeout(onChangeTimer); 326 327 if (checkErrorCount() && scaytImediateSpellChars.indexOf(String.fromCharCode(key)) >= 0 328 || key === 13 || key === 8 || key === 46) { 329 330 if (ignoreTag(element)) 331 return; 332 var blocks = markBlocks(element); 333 blocks.forEach(function (v) { 334 if (key === 13 || v.checked === 0) { 335 addToQueue({ block: v, options: { newline: key === 13 } }); 336 } 337 }); 338 } 339 } 340 } 341 }; 342 }; 343 344 /* Selection METHODS */ 345 function hasSelection() { 346 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 347 return sel && !sel.isCollapsed; 348 } 349 350 function getCaretPosition() { 351 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 352 if (sel && sel.isCollapsed) { 353 var range = sel && sel.type !== 'None' && sel.getRangeAt && sel.rangeCount ? sel.getRangeAt(0) : null; 354 if (range) { 355 range.collapse(true); 356 return { 357 node: range.startContainer, 358 offset: range.startOffset 359 }; 360 } 361 } 362 return null; 363 } 364 365 function setCaretPosition(p) { 366 var sel = (textContainer.ownerDocument.getSelection ? textContainer.ownerDocument.getSelection() : textContainer.ownerDocument.selection) || null; 367 if (sel.isCollapsed) { 368 var range = sel && sel.type !== 'None' && sel.getRangeAt && sel.rangeCount ? sel.getRangeAt(0) : textContainer.ownerDocument.createRange(); 369 if (range && sel && p && p.node && p.node.textContent && p.node.textContent.length >= p.offset) { 370 range.setStart(p.node, p.offset); 371 range.collapse(true); 372 sel.removeAllRanges(); 373 sel.addRange(range); 374 } 375 } 376 } 377 378 /* Selection METHODS END */ 379 380 381 /* Funções internas */ 382 383 function initDone(callback) { 384 return function (result) { 385 _hasInit = true; 386 if (result && !(result.ErrorCode || result.code)) { 387 _wsVersion = result.Application; 388 _versionValid = compareVersions(_wsVersion, _minVersion) >= 0; 389 } 390 callback(); 391 }; 392 } 393 394 function initEngine(callback) { 395 if (_hasInit) 396 callback(); 397 else { 398 var initdone = initDone(callback); 399 doJSONRequest({ 400 Url: geturl(), 401 Action: 'version', 402 Method: 'GET', 403 onRequest: function () { }, 404 onSuccess: initdone, 405 onError: initdone, 406 onTimeout: initdone 407 }); 408 } 409 } 410 411 function versionFromString(str) { 412 var values = []; 413 if (str) { 414 var arr = null; 415 var re = /([0-9]+)\.{0,1}/g; 416 while ((arr = re.exec(str)) !== null) 417 values.push(+arr[1]); 418 } 419 return values; 420 } 421 422 function compareVersions(v1, v2) { 423 var mv1 = versionFromString(v1); 424 var mv2 = versionFromString(v2); 425 var cmp = 0; 426 for (var i = 0; i < Math.min(mv1.length, mv2.length) && cmp === 0; i++) { 427 var r = mv1[i] - mv2[i]; 428 cmp = r !== 0 ? r / Math.abs(r) : 0; 429 } 430 431 return cmp; 432 } 433 434 function ignoreTag(element) { 435 if (_settings.IgnoreTags && element && element.nodeName) { 436 var itag = _ignoretags[element.nodeName.toLowerCase()]; 437 if (itag) 438 { 439 if (itag.attr) { 440 var attrvalue = element.getAttribute(itag.attr); 441 return attrvalue ? attrvalue.indexOf(itag.value) >= 0 : false; 442 } 443 else 444 return true; 445 } 446 } 447 return false; 448 } 449 450 function geturl() { 451 var url = _settings.Webservice; 452 if (url.length > 0 && url[url.length - 1] !== '/') 453 url += '/'; 454 return url; 455 } 456 457 function isError(element) { 458 return (element && element.className && 459 (element.className.indexOf(_pba_ort_error) >= 0 || 460 element.className.indexOf(_pba_gram_error) >= 0)) === true; 461 } 462 463 function stopSpellCheck() { 464 removeErrors(textContainer); 465 } 466 467 var _doingCheck = false; 468 function doSpellCheck(coldStart) { 469 if (_doingCheck) return; 470 _doingCheck = true; 471 472 var p = []; 473 if (_settings.IgnoreTags) { 474 traverseDom(textContainer, function (node) { 475 if (node && node.nodeName) { 476 if (ignoreTag(node)) 477 return false; 478 if (tagsBlock[node.nodeName.toLowerCase()]) 479 p.push(node); 480 return true; 481 } 482 return false; 483 }); 484 } 485 else { 486 Object.keys(tagsBlock).forEach(function (f) { 487 var elements = textContainer.getElementsByTagName(f); 488 if (elements.length) 489 p.push.apply(p, elements); 490 }); 491 } 492 493 p.forEach(function (n) { 494 if (n.nodeType !== 1) 495 return; 496 497 if (coldStart) 498 n.blocks = null; 499 500 if (checkErrorCount()) { 501 var blocks = markBlocks(n); 502 blocks.forEach(function (v) { 503 if (v.checked === 1) { 504 if (!areErrorsMarked(v.erros)) 505 markErrors(v, v.erros); 506 } 507 else if (v.checked === 0) { 508 addToQueue({ block: v }); 509 } 510 }); 511 } 512 }); 513 _doingCheck = false; 514 } 515 516 var spellcheckQueue = []; 517 var waitingForResponseForBlock = null; 518 var _processing = false; 519 520 function addToQueue(b) { 521 if (b && b.block) { 522 if (spellcheckQueue.filter(function (f) { return f.block.hash === b.block.hash; }).length === 0) { 523 spellcheckQueue.push(b); 524 } 525 } 526 } 527 528 var _nextAutoCheck = 0; 529 function processQueue() { 530 if (_flipRunning && _versionValid && waitingForResponseForBlock === null) { 531 var time = (new Date()).getTime(); 532 if (spellcheckQueue.length > 0) { 533 if (_processing) 534 return; 535 _processing = true; 536 537 var element = spellcheckQueue.shift(); 538 539 if (!hasSelection() && checkErrorCount(true) && element && element.block && element.block.element && element.block.element.parentNode && element.block.element.offsetParent) { 540 var block = element.block; 541 542 var hash = getBlockHash(block); 543 if (hash !== block.hash) { 544 block.hash = null; 545 block.checked = 0; 546 } 547 else if (block.checked === 0) { 548 var texto = block.text; 549 if (trim(texto).length > 0) { 550 block.checked = 2; // a correr 551 block.request = { 552 id: doJSONRequest({ 553 Url: geturl(), 554 Action: ['check', _settings.Language, _settings.GrammarSet, _settings.Acordo ? 'true' : 'false'].join("/"), 555 key: _settings.Key, 556 onSuccess: responseReceived, 557 onTimeout: responseTimeout, 558 onError: responseError, 559 Params: { 560 nline: element.options && element.options.newline ? 'true' : 'false' 561 }, 562 Data: texto 563 }), 564 hash: hash 565 }; 566 waitingForResponseForBlock = block; 567 } 568 else 569 block.checked = 1; 570 } 571 } 572 else { 573 setTimeout(processQueue, 0); 574 } 575 _processing = false; 576 _nextAutoCheck = time + 15000; 577 } 578 else if (time > _nextAutoCheck) { 579 doSpellCheck(false); 580 _nextAutoCheck = time + 15000; 581 } 582 } 583 } 584 setInterval(processQueue, 100); 585 586 function doJSONRequest(options) { 587 var url = options.Url || null, 588 action = options.Action || null, 589 params = options.Params || null, 590 data = options.Data || null, 591 method = options.Method || "POST", 592 contenttype = options.contentType || "text/plain", 593 key = options.key || "", 594 // Callbacks - por defeito chama os eventos associados 595 on_r = options.onRequest || function (action, params, data) { doEvent("Request", this, [action, params, data]); }, 596 on_s = options.onSuccess || function (result) { doEvent("Success", this, [result]); }, 597 on_t = options.onTimeout || function () { doEvent("Timeout", this, null); }, 598 on_e = options.onError || function (response) { doEvent("Error", this, [response]); }; 599 600 var xmlhttprequest = new XMLHttpRequest(); 601 if (xmlhttprequest) { 602 var sparams = (params && ("?" + serialize(params))) || ""; 603 xmlhttprequest.open(method, url + action + sparams, true); 604 xmlhttprequest.setRequestHeader("Content-type", contenttype); 605 xmlhttprequest.setRequestHeader("X-Priberam-auth-key", key) 606 xmlhttprequest.responseType = "json"; 607 xmlhttprequest.async = true; 608 xmlhttprequest.ontimeout = on_t; 609 xmlhttprequest.onreadystatechange = function () { 610 if (xmlhttprequest.readyState === 4) { 611 var response; 612 try { 613 response = JSON.parse(xmlhttprequest.responseText); 614 } 615 catch (err) { 616 response = xmlhttprequest.response; 617 } 618 619 if (xmlhttprequest.status === 200) { 620 on_s(response); 621 } 622 else { 623 on_e({ 624 status: xmlhttprequest.status, 625 reason: xmlhttprequest.statusText 626 }); 627 spellcheckQueue = []; 628 } 629 } 630 631 }; 632 xmlhttprequest.timeout = 10000; 633 634 on_r(action, params, data); 635 xmlhttprequest.send(data); 636 637 } 638 639 } 640 641 function serialize(obj, prefix) { 642 if (obj) { 643 var pre = (prefix && encodeURIComponent(prefix)) || ""; 644 return Object.keys(obj).map(function (k) { 645 return pre + encodeURIComponent(k) + "=" + encodeURIComponent(obj[k] === undefined ? "" : obj[k].toString()); 646 }).join("&"); 647 } 648 else 649 return ""; 650 } 651 652 function responseReceived(result) { 653 if (!result || (result && (result.ErrorCode || result.code))) { 654 doEvent("Error", this, [result]); 655 spellcheckQueue = []; 656 } 657 else if (waitingForResponseForBlock && !hasSelection()) { 658 doEvent("Success", this, [result]); 659 660 var element = waitingForResponseForBlock; 661 var newhash = getBlockHash(element); 662 // Verifica se a texto não mudou entretanto 663 if (newhash === element.request.hash) { 664 var offset = 0; 665 for (var j = 0; j < result.length; j++) { 666 var json = result[j]; 667 if (json.errors && json.errors.length > 0) { 668 for (var i = 0; i < json.errors.length; i++) { 669 json.errors[i].spos += offset; 670 json.errors[i].block = element; 671 var e1 = json.errors[i].spos; 672 var e2 = e1 + json.errors[i].len; 673 // ignora erros sobrepostos 674 var a = element.erros.filter(function (f) { 675 var f1 = f.spos; 676 var f2 = f.spos + f.len; 677 return f2 >= e1 && f1 <= e2; 678 }); 679 680 if (a.length == 0) 681 element.erros.push(json.errors[i]); 682 } 683 } 684 offset += json.len; 685 markErrors(element, element.erros); 686 element.checked = 1; 687 688 } 689 } 690 } 691 waitingForResponseForBlock = null; 692 } 693 694 function responseTimeout() { 695 doEvent("Timeout", this, null); 696 spellcheckQueue = []; 697 698 if (waitingForResponseForBlock) 699 waitingForResponseForBlock.hash = null; 700 waitingForResponseForBlock = null; 701 } 702 703 function responseError(err) { 704 doEvent("Error", this, [err]); 705 spellcheckQueue = []; 706 707 if (waitingForResponseForBlock) 708 waitingForResponseForBlock.hash = null; 709 waitingForResponseForBlock = null; 710 } 711 712 function checkErrorCount(update) { 713 var spans = [].concat.apply([], textContainer.ownerDocument.getElementsByClassName(_pba_gram_error)); 714 spans = [].concat.apply(spans, textContainer.ownerDocument.getElementsByClassName(_pba_ort_error)); 715 if (update) { 716 spans.forEach(function (v) { 717 v.setAttribute("class", v.getAttribute("class").replace(_pba_hide_error, "")); 718 }); 719 if (spans.length >= _settings.MaxErrors) { 720 spans.forEach(function (v) { 721 v.setAttribute("class", v.getAttribute("class").trim() + " " + _pba_hide_error); 722 }); 723 } 724 } 725 return spans.length < _settings.MaxErrors; 726 } 727 728 function getHash(text) { 729 return murmurhash3_32_gc(text || '', 25); 730 } 731 732 function doRangeIntersect(r1, r2, touch) { 733 if (!r1 || !r2) 734 return false; 735 touch = touch === false ? false : true; 736 try { 737 return r1.compareBoundaryPoints(Range.END_TO_START, r2) * r2.compareBoundaryPoints(Range.END_TO_START, r1) >= (touch ? 0 : 1); 738 } 739 catch (err) { 740 return false; 741 } 742 } 743 744 function traverseDom(node, callback) { 745 if (callback(node)) { 746 node = node.firstChild; 747 while (node) { 748 traverseDom(node, callback); 749 node = node.nextSibling; 750 } 751 } 752 } 753 754 function getBlockTextNodes(block) { 755 756 if (!block || !block.element) return []; 757 758 var textNodes = getAllTextNodes(block.element); 759 if (textNodes.length <= block.index) 760 return []; 761 return textNodes[block.index]; 762 } 763 764 function getAllTextNodes(root) { 765 766 var descendants = []; 767 var current = []; 768 traverseDom(root, function (node) { 769 if (node && node.nodeName && tagsBlock[node.nodeName.toLowerCase()]) { 770 if (current.length > 0) 771 descendants.push(current); 772 current = []; 773 if (node !== root) 774 return false; 775 } 776 else if (node.nodeType === 3) 777 current.push(node); 778 return true; 779 }); 780 if (current.length > 0) 781 descendants.push(current); 782 return descendants; 783 } 784 785 function createTag(tipo) { 786 787 var tag = textContainer.ownerDocument.createElement('span'); 788 tag.className = (tipo === 'spell' ? _pba_ort_error : _pba_gram_error); 789 if (tipo === 1) { 790 tag.onmouseover = function () { 791 //showToolTip(tag); 792 }; 793 tag.onmouseout = function () { 794 //hideToolTip(tag); 795 }; 796 } 797 798 return tag; 799 } 800 801 function surroundNodeTextWithTag(block, offset, length, tag) { 802 803 var textNodes = getBlockTextNodes(block); 804 805 var startNode = null, endNode = null; 806 var start = offset; 807 var i = 0; 808 for (; i < textNodes.length; i++) { 809 if (textNodes[i].length > start) { 810 startNode = textNodes[i]; 811 break; 812 } 813 start -= textNodes[i].length; 814 } 815 816 if (!startNode) 817 return false; 818 819 var end = length + start; 820 for (; i < textNodes.length; i++) { 821 if (textNodes[i].length >= end) { 822 endNode = textNodes[i]; 823 break; 824 } 825 end -= textNodes[i].length; 826 } 827 if (!endNode) 828 return false; 829 830 try { 831 var range = textContainer.ownerDocument.createRange(); 832 range.setStart(startNode, start); 833 range.setEnd(endNode, end); 834 835 var pos = getCaretPosition(); 836 if (pos && pos.node) { 837 838 var caretRange = textContainer.ownerDocument.createRange(); 839 caretRange.setStart(pos.node, pos.offset); 840 caretRange.collapse(true); 841 842 if (range.compareBoundaryPoints(Range.START_TO_END, caretRange) < 0) { 843 if (range.startContainer === pos.node) { 844 tag.appendChild(range.extractContents()); 845 range.insertNode(tag); 846 //setCaretPosition({ 847 // node: tag.nextSibling, 848 // offset: pos.offset - range.startOffset - tag.textContent.length 849 //}); 850 } 851 else { 852 tag.appendChild(range.extractContents()); 853 range.insertNode(tag); 854 } 855 } 856 else if (range.compareBoundaryPoints(Range.START_TO_START, caretRange) > 0) { 857 tag.appendChild(range.extractContents()); 858 range.insertNode(tag); 859 } 860 else if (range.commonAncestorContainer == caretRange.commonAncestorContainer) { 861 var dif = pos.offset - range.startOffset; 862 if (range.startContainer === pos.node) 863 dif = pos.offset - range.startOffset; 864 else { 865 for (i = textNodes.indexOf(range.startContainer) ; i < textNodes.length && textNodes[i] !== pos.node; i++) { 866 dif += textNodes[i].textContent.length; 867 } 868 } 869 tag.appendChild(range.extractContents()); 870 range.insertNode(tag); 871 var textnode = null, lastnode = null; 872 traverseDom(tag, function (node) { 873 if (!textnode && node.nodeType === 3) { 874 if (node.textContent.length > dif) 875 textnode = node; 876 else 877 dif -= node.textContent.length; 878 lastnode = node; 879 } 880 return true; 881 }); 882 883 setCaretPosition({ 884 node: textnode ? textnode : lastnode, 885 offset: textnode ? dif : lastnode.length 886 }); 887 } 888 } 889 else { 890 891 tag.appendChild(range.extractContents()); 892 range.insertNode(tag); 893 } 894 //removeErrors(tag); 895 896 897 return tag; 898 } 899 catch (err) { 900 return null; 901 } 902 } 903 904 function removeNode(node, ignorecursor) { 905 if (!node || !node.parentNode) 906 return; 907 var pos = null; 908 if (ignorecursor !== true) 909 pos = getCaretPosition(); 910 while (node.firstChild) { 911 node.parentNode.insertBefore(node.firstChild, node); 912 } 886 } 887 } 888 else { 889 890 tag.appendChild(range.extractContents()); 891 range.insertNode(tag); 892 } 893 //removeErrors(tag); 894 895 896 return tag; 897 } 898 catch (err) { 899 return null; 900 } 901 } 902 903 function removeNode(node, ignorecursor) { 904 if (!node || !node.parentNode) 905 return; 906 var pos = null; 907 if (ignorecursor !== true) 908 pos = getCaretPosition(); 909 while (node.firstChild) { 910 node.parentNode.insertBefore(node.firstChild, node); 911 } 912 node.parentNode.removeChild(node); 913 if (pos) 914 setCaretPosition(pos); 915 } 916 917 function removeErrors(element, ignorecursor) { 918 if (!element) return; 919 920 var spans = [].concat.apply([], element.getElementsByTagName('span')); 921 for (var i = 0; i < spans.length; i++) { 922 if (isError(spans[i])) 923 removeNode(spans[i], ignorecursor); 924 } 925 926 cleanTextNodes(element); 927 } 928 929 930 function getBlockErrors(block) { 931 if (!block || !block.element) 932 return; 933 var erros = []; 934 var r1 = textContainer.ownerDocument.createRange(); 935 var r2 = textContainer.ownerDocument.createRange(); 936 937 var tn = getBlockTextNodes(block); 938 r1.setStartBefore(tn[0]); 939 r1.setEndAfter(tn[tn.length - 1]); 940 941 var spans = [].concat.apply([], block.element.getElementsByTagName('span')); 942 for (var i = 0; i < spans.length; i++) { 943 if (isError(spans[i])) { 944 r2.selectNode(spans[i]); 945 if (doRangeIntersect(r1, r2, false)) 946 erros.push(spans[i]); 947 } 948 } 949 return erros; 950 } 951 952 function removeBlockErrors(block) { 953 if (!block || !block.element) 954 return; 955 var erros = getBlockErrors(block); 956 for (var i = 0; i < erros.length; i++) { 957 removeNode(erros[i]); 958 } 959 960 cleanTextNodes(block.element); 961 } 962 963 function cleanTextNodes(element) { 964 var pos = getCaretPosition(); 965 966 var node = element.firstChild; 967 var ftext = null; 968 while (node != element.lastChild) { 969 if (node && node.nodeType === 3) { 970 if (ftext === null) 971 ftext = node; 972 else { 973 ftext.nodeValue += node.nodeValue; 913 974 node.parentNode.removeChild(node); 914 if (pos) 915 setCaretPosition(pos); 916 } 917 918 function removeErrors(element, ignorecursor) { 919 if (!element) return; 920 921 var spans = [].concat.apply([], element.getElementsByTagName('span')); 922 for (var i = 0; i < spans.length; i++) { 923 if (isError(spans[i])) 924 removeNode(spans[i], ignorecursor); 925 } 926 } 927 928 929 function getBlockErrors(block) { 930 if (!block || !block.element) 931 return; 932 var erros = []; 933 var r1 = textContainer.ownerDocument.createRange(); 934 var r2 = textContainer.ownerDocument.createRange(); 935 936 var tn = getBlockTextNodes(block); 937 r1.setStartBefore(tn[0]); 938 r1.setEndAfter(tn[tn.length - 1]); 939 940 var spans = [].concat.apply([], block.element.getElementsByTagName('span')); 941 for (var i = 0; i < spans.length; i++) { 942 if (isError(spans[i])) { 943 r2.selectNode(spans[i]); 944 if (doRangeIntersect(r1, r2, false)) 945 erros.push(spans[i]); 946 } 947 } 948 return erros; 949 } 950 951 function removeBlockErrors(block) { 952 if (!block || !block.element) 953 return; 954 var erros = getBlockErrors(block); 955 for (var i = 0; i < erros.length; i++) { 956 removeNode(erros[i]); 957 } 958 959 } 960 961 function areErrorsMarked(errors) { 962 for (var i = 0; i < (errors && errors.length || 0) ; i++) { 963 if (!errors[i].tag || !errors[i].tag.parentNode) 964 return false; 965 } 966 return true; 967 } 968 969 function markErrors(block, erros) { 970 if (!block || !block.element) return; 971 972 removeBlockErrors(block); 973 974 if (!erros || erros.length === 0) return; 975 976 for (var i = 0; i < erros.length; i++) { 977 var erro = erros[i]; 978 979 if (_ignoredWords.indexOf(block.text.substr(erro.spos, erro.len)) >= 0) 980 continue; 981 982 var tag = createTag(erro.type); 983 surroundNodeTextWithTag(block, erro.spos, erro.len, tag); 984 tag.error = erro; 985 erro.tag = tag; 986 } 987 } 988 989 function getBlockHash(block) { 990 if (!(block && block.element && block.element.parentNode)) 991 return ''; 992 993 var r = textContainer.ownerDocument.createRange(); 994 r.selectNode(block.element); 995 return getHash(r.toString().substr(block.offset, block.size)); 996 } 997 998 function markBlocks(element) { 999 if (!element) 1000 return []; 1001 1002 var tn = getAllTextNodes(element); 1003 if (tn.length === 0) 1004 return []; 1005 1006 var range = textContainer.ownerDocument.createRange(); 1007 if (!element.blocks) 1008 element.blocks = []; 1009 1010 var blocks = []; 1011 var offset = 0; 1012 for (var i = 0; i < tn.length; i++) { 1013 var n = tn[i]; 1014 range.setStartBefore(n[0]); 1015 range.setEndAfter(n[n.length - 1]); 1016 var str = range.toString(); 1017 var hash = getHash(str); 1018 1019 if (i < element.blocks.length && hash === element.blocks[i].hash) { 1020 blocks.push(element.blocks[i]); 1021 } 1022 else { 1023 var block = { 1024 element: element, 1025 index: i, 1026 offset: offset, 1027 size: str.length, 1028 hash: hash, 1029 text: str, 1030 erros: [], 1031 checked: 0 1032 }; 1033 blocks.push(block); 1034 } 1035 offset += str.length; 1036 } 1037 1038 element.blocks = blocks; 1039 return blocks; 1040 } 1041 }; 975 node = ftext; 976 } 977 } 978 else { 979 ftext = null; 980 } 981 node = node.nextSibling; 982 } 983 984 if (pos) 985 setCaretPosition(pos); 986 } 987 988 function areErrorsMarked(errors) { 989 for (var i = 0; i < (errors && errors.length || 0); i++) { 990 if (!errors[i].tag || !errors[i].tag.parentNode) 991 return false; 992 } 993 return true; 994 } 995 996 function markErrors(block, erros) { 997 if (!block || !block.element) return; 998 999 removeBlockErrors(block); 1000 1001 if (!erros || erros.length === 0) return; 1002 1003 for (var i = 0; i < erros.length; i++) { 1004 var erro = erros[i]; 1005 1006 if (_ignoredWords.indexOf(block.text.substr(erro.spos, erro.len)) >= 0) 1007 continue; 1008 1009 var tag = createTag(erro.type); 1010 surroundNodeTextWithTag(block, erro.spos, erro.len, tag); 1011 tag.error = erro; 1012 erro.tag = tag; 1013 } 1014 } 1015 1016 function getBlockHash(block) { 1017 if (!(block && block.element && block.element.parentNode)) 1018 return ''; 1019 1020 var r = textContainer.ownerDocument.createRange(); 1021 r.selectNode(block.element); 1022 return getHash(r.toString().substr(block.offset, block.size)); 1023 } 1024 1025 function markBlocks(element) { 1026 if (!element) 1027 return []; 1028 1029 var tn = getAllTextNodes(element); 1030 if (tn.length === 0) 1031 return []; 1032 1033 var range = textContainer.ownerDocument.createRange(); 1034 if (!element.blocks) 1035 element.blocks = []; 1036 1037 var blocks = []; 1038 var offset = 0; 1039 for (var i = 0; i < tn.length; i++) { 1040 var n = tn[i]; 1041 range.setStartBefore(n[0]); 1042 range.setEndAfter(n[n.length - 1]); 1043 var str = range.toString(); 1044 var hash = getHash(str); 1045 1046 if (i < element.blocks.length && hash === element.blocks[i].hash) { 1047 blocks.push(element.blocks[i]); 1048 } 1049 else { 1050 var block = { 1051 element: element, 1052 index: i, 1053 offset: offset, 1054 size: str.length, 1055 hash: hash, 1056 text: str, 1057 erros: [], 1058 checked: 0 1059 }; 1060 blocks.push(block); 1061 } 1062 offset += str.length; 1063 } 1064 1065 element.blocks = blocks; 1066 return blocks; 1067 } 1068 }; 1042 1069 })(window); -
flip/trunk/flip.php
r1762083 r1795661 4 4 * Plugin URI: https://www.flip.pt/Produtos/Plugin-do-FLiP-para-WordPress 5 5 * Description: Portuguese spell checker, grammar checker and style checker, with or without the Spelling Reform of 1990. 6 * Version: 1.5. 86 * Version: 1.5.9 7 7 * Author: Priberam 8 8 * Author URI: http://www.priberam.pt -
flip/trunk/readme.txt
r1767893 r1795661 4 4 Requires at least: 4.3 5 5 Tested up to: 4.9 6 Stable tag: 1.5. 86 Stable tag: 1.5.9 7 7 License: GPLv2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 36 36 37 37 == Changelog == 38 = 1.5.9 = 39 * Fix collapsing spaces between errors. 40 38 41 = 1.5.8 = 39 42 * Minor fixes … … 65 68 66 69 == Upgrade Notice == 70 = 1.5.9 = 71 * Fix collapsing spaces between errors. 72 67 73 = 1.5.8 = 68 74 * Minor fixes
Note: See TracChangeset
for help on using the changeset viewer.