מדיה ויקי:Gadget-Checkty.js: הבדלים בין גרסאות בדף
קפיצה לניווט
קפיצה לחיפוש
(ניסוי) תגית: החלפה |
קער א וועלט (שיחה | תרומות) (תקלדה) |
||
(4 גרסאות ביניים של 2 משתמשים אינן מוצגות) | |||
שורה 1: | שורה 1: | ||
[[ | // הוספת כפתור "בדיקה" שמבצע החלפות נפוצות של בוט ההחלפות וכן מתריע על בעיות סגנון ועיצוב שונות | ||
// נכתב על ידי [[משתמש:ערן]], [[משתמש:קיפודנחש]] מויקיפדיה והותאם לאתר ע"י [[משתמש:חלוקת קונטרסים]] מחב"דפדיה | |||
// לעזרה ראו [[mediawiki:Gadget-Checkty.js/הוראות]] | |||
mw.messages.set({ | |||
'checkty-large-element': 'בערך קיים אלמנט גדול, רצוי להקטין כדי שיתאים לרזולוציות נמוכות', | |||
'checkty-wikilink-in-extlink': 'בערך קיים קישור פנימי בתוך קישור חיצוני. רצוי לבטל את הקישור הפנימי או לצמצם את המסגרת של הקישור החיצוני', | |||
'checkty-long-list': 'רשימה ארוכה - נראה כי בערך רשימה של מעל 20 פריטים. כדאי לשקול לפצלה לשני טורים באמצעות <a href="' + (new mw.Title('תבנית:טורים')).getUrl() + '">תבנית:טורים</a>', | |||
'checkty-long-gallery': 'גלריה ארוכה - הערך מכיל גלריה ארוכה. מומלץ לשקול צמצום שלה או העברה לתבנית נפרדת.', | |||
'checkty-waiting-disambig-query': 'ממתין לרשימת פירושונים מהשרת...', | |||
'checkty-warnings-title': 'הערות לבדיקה:', | |||
'checkty-no-replacements': 'הדף מכיל תבנית "ללא בוט" ולכן לא יבוצעו החלפות', | |||
'checkty-replace-summary': '[[וק:רה|החלפות]] ($1)', | |||
'checkty-disambig-suffix': ' (פירושונים)', | |||
'checkty-disambig-no-links': 'קישורים לפירושונים - לא נמצאו', | |||
'checkty-disambig-success': 'קישורים לפירושונים - הבדיקה הסתיימה בהצלחה.', | |||
'checkty-disambig-links-title': 'הגרסה השמורה האחרונה של הדף מקשרת לדפי פירושונים. אנא תקנו את הקישורים לדפים הבאים: ', | |||
'checkty-page-doesnt-exist': ' (הדף אינו קיים)', | |||
'checkty-dismabig-dialog-title': 'תיקון פירושונים', | |||
'checkty-remove-link': 'הסרת קישור', | |||
'checkty-disambig-meaning': 'מה הכוונה ב "$1" במשפט: ', | |||
'checkty-dismabig-fix-summary': 'תיקון קישור לפירושונים', | |||
'checkty-search': 'חיפוש', | |||
'checkty-fix-numberRangeDash': 'תיקון קווים מפרידים', | |||
'checkty-missing-commons-link': 'בערך זה חסר קישור לוויקישיתוף. ניתן להוסיף קישור באמצעות בחירת המקום הנכון להוספתו בערך ולחיצה על הקישור הבא. ', | |||
'checkty-fairuse-img-replace-template': 'תמונה להחלפה', | |||
'checkty-set-label': 'הזנת תווית עברית ל-$1', | |||
'checkty-translate-saved': 'התרגום נשמר!', | |||
// 'checkty-empty-parameters': 'פרמטרים ריקים - דף זה מכיל תבניות עם פרמטרים ריקים. אם הם אינם רלוונטיים ניתן להסירם. ', | |||
// 'checkty-empty-parameters-summary': 'ניקוי קוד', | |||
'checkty-main-articles-merge-summary': 'מיזוג הפניות לערכים מורחבים', | |||
'checkty-missing-ref-section-add': 'נמצאו הערות שוליים בערך אך לא פרק מתאים. מומלץ להוסיף את הפרק לערך, וניתן להוסיף אותו אוטומטית במקום המשוער לכך.', | |||
'checkty-missing-ref-section-added': 'פרק הערות שוליים הוסף.', | |||
'checkty-missing-ref-section-autofix': 'הוספה אוטומטית', | |||
'checkty-please-position-caret' : 'אנא מקמו את הסמן במקום בו יש להוסיף ', | |||
'checkty-ref-dir-fix' : 'תיקון כיווניות להערת שוליים', | |||
'checkty-ref-merge' : 'מיזוג הערות שוליים', | |||
'checkty-naked-link-found' : 'נמצא קישור חיצוני ערום: $1', | |||
'checkty-naked-link-fix' : 'תיקון', | |||
'checkty-broken-link': 'חשש לקישור שבור: ', | |||
'checkty-naked-link-fix-fail': 'שגיאה בתיקון קישור עירום', | |||
'checkty-naked-link-fixed': 'הקישור $1 הוחלף בתבנית. יש לבדוק את תקינות התוצאה והאם יש לעטוף בתבנית הערה<br> $2', | |||
'checkty-external-links-many': 'קישורים חיצוניים - פרק הקישורים החיצוניים מכיל $1 קישורים. מומלץ להעביר קישורים העוסקים בנושאים ספציפיים להערות שוליים, ולהסיר קישורים שלא מוסיפים. (ראו עוד ב<a href="' + mw.util.getUrl('ויקישיבה:קישורים חיצוניים') + '">ויקישיבה:קישורים חיצוניים</a>)', | |||
'checkty-external-links-diversity': 'קישורים חיצוניים - פרק הקישורים החיצוניים מכיל מספר קישורים למקורות זהים: $1. כאשר הקישור עוסק באספקט צר של הערך מומלץ להפכו להערת שוליים. (ראו עוד ב<a href="' + mw.util.getUrl('ויקישיבה:קישורים חיצוניים') + '">ויקישיבה:קישורים חיצוניים</a>)', | |||
'checkty-nonsense-edit': 'עריכה זו כוללת שינויים אוטומטיים בלבד. האם ברצונך לשמור למרות זאת?', | |||
'checkty-category-sort': 'ניתן למיין את הקטגוריות בדף על פי סדר אלפביתי ', | |||
'checkty-defaultsort-suggest': 'בערך לא מוגדר מיון רגיל. בערכים העוסקים באישים נהוג למיין לפי שם משפחה. ', | |||
'checkty-language-check': 'בערך זה מופיע הביטוי "$1". ', | |||
'checkty-wrong-use-discussion': 'דף זה עשוי להיות דף דיונים. אין לערוך תגובות של עורכים אחרים והשימוש בבדיקה של דף כזה אינו מומלץ. האם ברצונך להפעיל את הכלי למרות זאת?', | |||
'checkty-vav-warning': 'כאשר ו עיצורית מופיעה באמצע מילה נהוג להכפילה. ייתכן שיש מופעים שדורשים תיקון.' | |||
}); | |||
function dotSpaceRegex() { | |||
try { | |||
// proper spacing around , and . using negative lookahead and lookbehind (ES2018) to acronyms (b.b.c) | |||
return new RegExp('(?<!\\.[א-ת]*)([א-ת]\\]?\\]?) ?([,\\.]) ?(?=[א-ת]?\\[?\\[?[א-ת]{3})(?![א-ת]*\\.[א-ת])','g'); | |||
} catch(e) { | |||
console.log('Checkty - browser doesnt support negative lookhead or lookbehind (ES2018)'); | |||
//fallback to different regex with slightly less protections | |||
return /([א-ת]\]?\]?) ?([,\.]) ?(?=[א-ת]?\[?\[?[א-ת]{3})/g; | |||
} | |||
} | |||
var chectTyTool = { | |||
origText: null, | |||
textbox: null, | |||
skipCheckty: false, | |||
isSection: mw.util.getParamValue('section'), | |||
skip_dict: {}, | |||
named_comment: '{{' + 'הער' + 'ה|שם=', | |||
formatReplacesConfigSafe: [ | |||
{ | |||
from: /\[\[(File|Image|תמונה):/ig, | |||
to: '[[קובץ:' | |||
}, { | |||
from: /\|thumb(nail)?(?=[\|\]])/ig, | |||
to: '|ממוזער' | |||
}, { //remove unseen character | |||
from: new RegExp('\u200e|\u200f|\u202d|\u202e|\u202c|\u202a|\u202b', 'g'), // lrm/rlm/lro/rlo/popdf invisible characters/lre/rle | |||
to: "", | |||
comment: 'הסרת תווים בלתי נראים' | |||
}, { | |||
from: / +$/mg, | |||
to: '' | |||
}, { // remove accidental nowiki and simplify code | |||
from: /\[\[([^|\[\]]+)([^|\[\]]+?)\|\1\]\](?:<nowiki\/>)?\2/g, | |||
to: '[[$1$2]]', | |||
comment: 'פישוט קישור' | |||
}, { // trim beginning from redundant spaces | |||
from: /^\s+/g, | |||
to: '', | |||
comment: 'הסרת רווחים מיותרים' | |||
}, { // trim redundant spaces from category links | |||
from: /\[\[קטגוריה: */g, | |||
to: '[[קטגוריה:' | |||
}], | |||
formatReplacesConfig: [{ // piped link has identical text before and after the pipe | |||
from: /\[\[(.*?)\|\1([.,a-zא-ת]*)\]\]/g, | |||
to: '[[$1]]$2' | |||
}, { | |||
from: /\[\[(.+?)\|([במל])\1([א-ת]*)\]\]/g, | |||
to: "$2[[$1]]$3" | |||
}, { // proper spacing around , and . using negative lookahead and lookbehind (ES2018) to acronyms (b.b.c) | |||
from: dotSpaceRegex(), | |||
to: "$1$2 ", | |||
skippable: true | |||
}, { | |||
from: /([א-ת])\( ?([א-ת])/g, | |||
to: "$1 ($2" | |||
}, { | |||
from: /(\n\n)\n+/g, | |||
to: "$1" | |||
}, { | |||
from: /== ? ?\n\n==/g, | |||
to: "==\n==" | |||
}, { | |||
from: /^ ? ? \n/gm, | |||
to: "\n" | |||
}, { | |||
from: /(?!.{2}\|)[ \t\xA0]{2,}/g, | |||
to: ' ' | |||
}, { | |||
from: /\n\n\*/g, | |||
to: '\n*' | |||
}, | |||
{ | |||
from: /([בלמכ])- ((?:\[\[)?[0-9])/g, | |||
to: '$1־$2' | |||
}, | |||
{ // move , suffix outside links | |||
from: /,\]\]/g, | |||
to: ']],' | |||
}, | |||
{ // section between two headings without content | |||
from: /\n==[^=\n]+?==\n*(\n==[^=\n]+?==\n)/g, | |||
to: '$1', | |||
comment: 'הסרת כותרות ריקות' | |||
}], | |||
regexes: [], | |||
ignoreRegexes: [], | |||
run: function () { | |||
if (this != chectTyTool) { | |||
chectTyTool.run(); | |||
return; | |||
} | |||
var t = $('#wpTextbox1'); | |||
this.textbox = t.length ? t[0] : null; | |||
if (!this.textbox || this.textbox.value.length === 0) return; | |||
// this tool is not indented to be used in discussion pages | |||
if ( mw.config.get('wgNamespaceNumber') % 2 == 1 || ( mw.config.get('wgNamespaceNumber') != 0 && /\(IST\)/.exec(this.textbox.value ) )) { | |||
if (!prompt(mw.msg('checkty-wrong-use-discussion'))) return; | |||
} | |||
// indication that skippable replacements should be ignored in the page | |||
this.skipCheckty = this.textbox.value.match(/{{ללא[_ ]בוט\|\s*צ'קטי\s*}}/g); | |||
if (!($('#checktyResults').length)) $('.editButtons').after('<div id="checktyResults"><div class="checktyResultsTitle">'+mw.msg( 'checkty-warnings-title' )+'</div></div>'); | |||
//first call to remote functions than to local | |||
if ( ( chectTyTool.textbox.value === chectTyTool.origText ) && ( mw.config.get('wgAction') === 'edit') ) | |||
this.onSaveProtection(); | |||
this.formatChecks(); | |||
this.linkChecks(); | |||
if(!this.isSection) { | |||
this.articleOrgChecks(); | |||
this.checkImages(); | |||
} | |||
this.languageCheck(); | |||
this.vavCheck(); | |||
this.expandWikidata(); | |||
}, | |||
formatChecks: function() { | |||
if (!this.isSection) | |||
this.build_regexes(); | |||
this.formatReplace(); | |||
this.decodeExternalLinks(); | |||
// format - semi manual | |||
this.mainArticlesMerge(); | |||
this.numberRangeDash(false); | |||
}, | |||
decodeExternalLinks: function() { | |||
var t = this.textbox.value, newTxt = t, | |||
externalLinkRgx = /http[^ \]|]+/g, | |||
decodeRgx = /(?:%[0-9a-z]{2}){2,}/ig, | |||
m, mm, newLink; | |||
while ( m = externalLinkRgx.exec(t) ) { | |||
newLink = m[0]; | |||
while (mm = decodeRgx.exec(m[0])) { | |||
try { | |||
// decode URL except special chars | |||
var niceLink = decodeURI(mm[0]).replace(/[ |"\n\[\]{}<>]/g, encodeURI); | |||
if (niceLink == niceLink.normalize()) newLink = newLink.replace(mm[0], niceLink); | |||
} catch(ex) { | |||
} | |||
} | |||
newTxt = newTxt.replace(m[0], newLink); | |||
} | |||
if (newTxt === t) return; | |||
this.updateText(newTxt); | |||
chectTyTool.addSummary('קידוד קישורים'); | |||
}, | |||
articleOrgChecks: function() { | |||
this.titleOrderCheck(); | |||
this.checkRefs(); | |||
this.checkGallery(); | |||
this.defaultSortSuggest(); | |||
this.categoryOrder(); | |||
}, | |||
defaultSortSuggest: function(execute) { | |||
var txt = this.textbox.value, | |||
isPerson = false, | |||
firstFamilyRgx = /^([^ ]+) ([^ ]+)$/, | |||
name, defaultSortMsg; | |||
if (/\{\{מיון רגיל:/.exec(txt)) return; // skip if already exists | |||
if (!firstFamilyRgx.exec(mw.config.get('wgTitle'))) return; // skip if can't suggest | |||
isPerson = /== ?(ביוגרפיה|קורות חיים|חיי[וה]) ?==/.exec(txt); // heuristic to identify biographic articles | |||
if (!isPerson) return; // skip for non biographic pages | |||
if (!execute) { | |||
defaultSortMsg = $('<div>', { text: mw.msg('checkty-defaultsort-suggest') }).append($('<a>', { | |||
href: '#', | |||
text: 'הוספת מיון רגיל', | |||
}).click(function(){ chectTyTool.defaultSortSuggest(true); return false;})); | |||
this.writeMsg(defaultSortMsg, 'info'); | |||
} else { | |||
name = firstFamilyRgx.exec(mw.config.get('wgTitle')); | |||
txt = txt.replace('[[קטגוריה', '{{מיון רגיל:' + name[2] + ', ' + name[1] + '}}\n[[קטגוריה'); | |||
this.addSummary('מיון רגיל'); | |||
this.updateText(txt); | |||
} | |||
}, | |||
categoryOrder: function(execute) { | |||
var txt = this.textbox.value, | |||
newTxt = txt, | |||
self = this; | |||
categoryRE = /(\[\[קטגוריה:.+\]\])(?:$|\n)/g, | |||
cats = [], m = null, oldCats = null, categorySortMsg = null; | |||
while(m=categoryRE.exec(txt)) { | |||
cats.push(m[1]); | |||
newTxt = newTxt.replace(m[0], ''); | |||
} | |||
oldCats = JSON.stringify(cats); | |||
cats.sort(function(a,b){ | |||
if (/\|\*\]\]/.exec(a)) return -1; if (/\|\*\]\]/.exec(b)) return 1; | |||
if (/נפטרים ב-|נפטרות|שנפטרו/.exec(a)) return 1; if(/נפטרים ב-|נפטרות|שנפטרו/.exec(b)) return -1; | |||
if (/(ילידי|ילידות|שנולדו).*[0-9]/.exec(a)) return 1; if(/(ילידי|ילידות|שנולדו).*[0-9]/.exec(b)) return -1; | |||
if (/אישים במאה ה-|אישים שחיו במאה ה-|רבנים במאה ה-/.exec(a)) return 1; if(/אישים במאה ה-|אישים שחיו במאה ה-|רבנים במאה ה-/.exec(b)) return -1; | |||
return a>=b; | |||
}); | |||
if (oldCats == JSON.stringify(cats)) return; // same order - nothing to do | |||
if (!execute) { | |||
categorySortMsg = $('<div>', { text: mw.msg('checkty-category-sort') }).append($('<a>', { | |||
href: '#', | |||
text: 'סידור', | |||
}).click(function(){ chectTyTool.categoryOrder(true); return false;})); | |||
this.writeMsg(categorySortMsg, 'info'); | |||
} else { | |||
newTxt = newTxt.trimEnd(); | |||
newTxt += '\n' + cats.join('\n'); | |||
if (newTxt != txt) | |||
OO.ui.confirm( 'יש לאשר סדר הקטגוריות המוצע:' + cats.join(', ').replace(/\[\[קטגוריה:([^\[\]]+)\]\]/g, '$1') ).done( function ( confirmed ) { | |||
if ( !confirmed ) return; | |||
self.addSummary('סידור קטגוריות'); | |||
self.updateText(newTxt); | |||
} ); | |||
} | |||
}, | |||
linkChecks: function() { | |||
this.overlinkify(); | |||
this.nakedLinks(); | |||
this.checkExternalLinks(); | |||
this.disambigCheck(); | |||
}, | |||
expandWikidata: function() { | |||
this.wikidataLabelsMissing(); | |||
this.extractWikidataLabelsSuggestions(); | |||
if (!this.isSection) { | |||
this.addAuthorityControl(); | |||
} | |||
}, | |||
createSearchLink: function(v) { | |||
var highlightStr = this.highlightString, | |||
searchLink = $('<a href="#">'+mw.msg( 'checkty-search' )+'</a>').data({'search': v}).click(function(e){ | |||
highlightStr($(this).data('search')); e.preventDefault(); | |||
}); | |||
return searchLink; | |||
}, | |||
onSaveProtection: function() { | |||
$('#wpSave').click(function(e){ | |||
if (chectTyTool.textbox.value === chectTyTool.origText) { | |||
OO.ui.confirm( mw.msg('checkty-nonsense-edit') ).done( function ( confirmed ) { | |||
if ( confirmed ) { | |||
chectTyTool.origText = ''; | |||
$('#editform').submit(); | |||
} else { | |||
e.preventDefault(); | |||
} | |||
} ); | |||
e.preventDefault(); | |||
} | |||
}); | |||
}, | |||
updateText: function( val ) { | |||
if (this.textbox.value === chectTyTool.origText) { | |||
chectTyTool.origText = val; | |||
} | |||
this.textbox.value = val; | |||
}, | |||
nakedLinks: function() { | |||
var t = this.textbox.value, | |||
nakeRegex=/\[(http[^ ]+?)\]|\{\{הערה *\| *(?:1 *= *)?(https?:\/\/[^ }|]+)\}\}/g, | |||
nakeErrors=[], m, self= this; | |||
while (m = nakeRegex.exec(t)) { | |||
var nakedUrl = m[1] || m[2], | |||
fixNakedLink = $( '<a href="#">' + mw.msg( 'checkty-naked-link-fix' )+'</a>' ).data( { 'search': nakedUrl, 'inRef': m[2] }).click( function ( e ) { | |||
var searchUrl = $(this).data( 'search' ), | |||
inRef = $(this).data('inRef'), | |||
citoidTemplatesPromise = new mw.Api().loadMessagesIfMissing( [ 'citoid-template-type-map.json' ] ); | |||
// Common case: pasting a URI into this field. Citoid expects | |||
// minimally encoded input, so do some speculative decoding here to | |||
// avoid 404 fetches. T146539 | |||
searchUrl = decodeURIComponent(searchUrl); | |||
citoidPromise = $.ajax('/api/rest_v1/data/citation/mediawiki/'+encodeURIComponent(searchUrl), { | |||
headers: { 'accept-language': mw.config.get( 'wgContentLanguage' ) }, | |||
timeout: 20 * 1000, // 20 seconds | |||
type: 'GET' | |||
}); | |||
citoidPromise.done(function(d){ | |||
citoidTemplatesPromise.done(function(){ | |||
var CiteTemplates = JSON.parse( mw.message( 'citoid-template-type-map.json' ).plain() ); | |||
if ( d.length === 0 || !CiteTemplates[ d[0].itemType ] ) { | |||
self.writeMsg( mw.msg( 'checkty-naked-link-fix-fail' ) ); | |||
return; | |||
} | |||
var api = new mw.Api().get( { | |||
action: 'templatedata', | |||
titles: 'Template:' + CiteTemplates[ d[0].itemType ] | |||
}).done(function(tdq){ | |||
for(var pid in tdq.pages){ | |||
var td = tdq.pages[pid], | |||
params = []; | |||
for(var k in d[0]) { | |||
if (td.maps['citoid'][k]) { | |||
if ($.isArray(td.maps['citoid'][k]) && $.isArray(d[0][k])) { | |||
for(var i = 0;i < d[0][k].length; i++) { | |||
if ($.isArray(d[0][k][i]) && $.isArray(td.maps['citoid'][k][i])) { | |||
for(var j = 0;j < d[0][k][i].length; j++) { | |||
params.push(td.maps['citoid'][k][i][j] + '=' + d[0][k][i][j].replace(/\|/g, '{{!}}')); | |||
} | |||
} | |||
} | |||
} | |||
else { | |||
if ($.isArray(d[0][k])) { | |||
for(var i = 0;i < d[0][k].length; i++) if ($.isArray(d[0][k][i])) d[0][k][i] = d[0][k][i].join(' '); | |||
d[0][k] = d[0][k].join(', '); | |||
} | |||
params.push(td.maps['citoid'][k] + '=' + d[0][k].replace(/\|/g, '{{!}}')); | |||
} | |||
} | |||
} | |||
var template = '{{'+CiteTemplates[d[0].itemType]+'|' + params.join('|')+'}}', | |||
wikitext = self.textbox.value; | |||
if ( inRef ) { | |||
wikitext = wikitext.replace(new RegExp('\\{\\{הערה *\\| *(?:1 *= *)?'+mw.util.escapeRegExp(searchUrl)+'\\}\\}', 'g'), '{{הערה|' + template + '}}'); | |||
} else { | |||
// this is more context sensitive - sometimes we may or may not want to wrap with ref | |||
wikitext = wikitext.replace(new RegExp('\\['+mw.util.escapeRegExp(searchUrl)+'\\]', 'g'), template); | |||
} | |||
self.textbox.value = wikitext; | |||
self.writeMsg($('<div>').append(mw.msg('checkty-naked-link-fixed', searchUrl, template)).append(self.createSearchLink(template))); | |||
chectTyTool.addSummary('הלבשת קישורים עירומים'); | |||
} | |||
}) | |||
}); | |||
}).fail(function(){ | |||
self.writeMsg($('<div>').append(mw.msg('checkty-broken-link') + searchUrl)); | |||
}); | |||
e.preventDefault(); | |||
}); | |||
this.writeMsg($('<div>').append([mw.msg('checkty-naked-link-found', nakedUrl)+'" [',fixNakedLink, ' - ', this.createSearchLink(m[0]), ']']), 'alert'); | |||
} | |||
}, | |||
checkExternalLinks: function() { | |||
var t = this.textbox.value, | |||
externalLinksRgx = /== *קישורים חיצוניים *==(?:\n\{\{.+)*((?:\n\*.+|\n\{\{.+\}\})+)/mg, | |||
tplCounterRgx = /\n\* *\{\{([^|]+)/g, | |||
externalLinks = externalLinksRgx.exec(t), tplCounter={}, sameSources=[], m; | |||
if (!externalLinks) return; | |||
externalLinks = externalLinks[1]; | |||
if (externalLinks.split('\n').length > 8) { | |||
this.writeMsg('<div>' + mw.msg('checkty-external-links-many', externalLinks.split('\n').length) + '</div>', 'alert'); | |||
} | |||
else { | |||
while(m = tplCounterRgx.exec(externalLinks)) tplCounter[m[1]] = (tplCounter[m[1]]? tplCounter[m[1]]+1 : 1); | |||
delete tplCounter['קישור כללי']; | |||
for(m in tplCounter) { | |||
if (tplCounter[m] > 1) sameSources.push(m + ' ‏(' + tplCounter[m] + ')'); | |||
} | |||
if (sameSources.length) { | |||
this.writeMsg('<div>' + mw.msg('checkty-external-links-diversity', sameSources.join(', ')) + '</div>', 'alert'); | |||
} | |||
} | |||
}, | |||
checkRefs: function() { | |||
this.refsConsistencyCheck(); | |||
this.ibidWarning(); | |||
this.mergeRefs(); | |||
this.refDirSuggest(); | |||
if (!this.isSection) { | |||
this.refSection(); | |||
} | |||
}, | |||
writeMsg: function (msg, icon) { | |||
var x; | |||
if (msg instanceof Array) { | |||
if (msg.length === 0) return; | |||
msg = '<div>' + msg.join('<br/>') + '</div>'; | |||
} | |||
x = $(msg).css('display', 'none').addClass('checktyMsg'); | |||
if ( icon ) { | |||
var iconWidget = new OO.ui.IconWidget( { | |||
icon: icon, | |||
} ); | |||
x.prepend(iconWidget.$element); | |||
} | |||
$('#checktyResults').append(x); | |||
x.show('slow'); | |||
return x; | |||
}, | |||
build_regexes: function (data, protect) { | |||
if (/\{\{\s*ללא[_ ]בוט\s*\}\}/.test(this.textbox.value)) { | |||
this.writeMsg('<div>'+mw.msg( 'checkty-no-replacements' )+'</div>', 'info'); | |||
return; | |||
} | |||
if (data) { | |||
var lines = data.split(/\n/), | |||
clear_nowiki = /\|<nowiki>(.*)<\/nowiki>/, | |||
protect_title_regex = /\[\[(.+?)\]\]/g, | |||
matches, regex, pTitle; | |||
while (lines.length) { | |||
if (!(matches = lines.shift().match(/^\|(\d+)/))) continue; | |||
var num = parseInt(matches[1], 10); | |||
if (!(matches = lines.shift().match(clear_nowiki))) continue; | |||
try { | |||
regex = new RegExp(matches[1], 'g'); | |||
} catch (e) { | |||
//ignore | |||
continue; | |||
} | |||
if (!(matches = lines.shift().match(clear_nowiki))) continue; | |||
this.regexes[num] = [regex, matches[1]]; | |||
var ignore = lines.shift(), | |||
ignoreRegex = /^\|(?:<nowiki>)?(.+?)(?:<\/nowiki>)?$/.exec(ignore); | |||
if (ignoreRegex) { | |||
this.ignoreRegexes.push('(?:' + ignoreRegex[1] + ')'); | |||
} | |||
} | |||
if (protect) { | |||
// add titles of pages explicitly marked as special | |||
while (pTitle = protect_title_regex.exec(protect)) { | |||
this.ignoreRegexes.push(mw.util.escapeRegExp(pTitle[1])); | |||
this.ignoreRegexes.push('(?:\\[\\[' + mw.util.escapeRegExp(pTitle[1]) + '\\|.+?\\]\\])'); //protect links [[A (x)|A]] or other variations | |||
} | |||
} | |||
this.process_page(); | |||
} else { | |||
var replceQuery = $.ajax({ | |||
url: mw.util.getUrl( (window.replaceListPage || 'ויקישיבה:בוט/רשימת החלפות'), { action: 'raw', ctype: 'text/x-wiki' } ), | |||
dataType: 'html' | |||
}), protectQuery = $.ajax({ | |||
url: mw.util.getUrl( 'ויקישיבה:בוט/דפים חריגים', { action: 'raw', ctype: 'text/x-wiki' } ), | |||
dataType: 'html' | |||
}); | |||
$.when(replceQuery, protectQuery).done(function(dataRepalce, dataProtect){ | |||
chectTyTool.build_regexes(dataRepalce[0], dataProtect[0]); | |||
}); | |||
} | |||
}, | |||
process_page: function () { | |||
var t = this.textbox.value, | |||
skip_ar = [], | |||
actual_replaced = [], // list of actual replcements for summary | |||
actual_replaced_details = [], // list of actual replcements with details for editor | |||
skipmatch = t.match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/g), i, match; | |||
if (skipmatch) | |||
for (i = 0; i < skipmatch.length; i++) { | |||
var matches = skipmatch[i].match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/), detailedSkip = ''; | |||
chectTyTool.skip_dict[parseInt(matches[1], 10)] = true; | |||
if (this.regexes[matches[1]]) { | |||
detailedSkip = ': ' + $.trim(this.regexes[matches[1]][0].toString()); | |||
skip_ar.push($('<li></li>').append(matches[1] + detailedSkip + ' ').append(this.createSearchLink(this.regexes[matches[1]][0]))); | |||
} | |||
} | |||
var specials = [], | |||
ignoreRegex = new RegExp('(' + this.ignoreRegexes.join('|') + ')'); | |||
for (i in this.regexes) // assume regexs on templates are safe if contain template wikicode | |||
if (/\\{\\{/.test(this.regexes[i][0]) && this.regexes[i][0].test(t)) { | |||
t = t.replace(this.regexes[i][0], this.regexes[i][1]); | |||
actual_replaced.push($.trim(this.regexes[i][1].replace(/\$\d*/g, ''))); | |||
actual_replaced_details.push(i + ': ' + $.trim(this.regexes[i][1].replace(/\$\d*/g, ''))); | |||
} | |||
while (true) { //extract inner links, inner templates and inner params - we don't want to sptit those. | |||
match = t.match(/(\{\{[^\{\}]*\}\}|(\n|\[\[)(?:File|קובץ|תמונה|Image):.*?[\|\n]|[^\[\0]\[[^\{\}\[]*\])/); | |||
if ((!match || !match.length) && this.ignoreRegexes.length ) match = t.match(ignoreRegex); | |||
if (!match || !match.length) break; | |||
specials.push(match[0]); | |||
t = t.replace(match[0], "\0" + specials.length + "\0"); | |||
} | |||
for (i in this.regexes) | |||
if (!chectTyTool.skip_dict[i] && !isNaN(i)) | |||
if (this.regexes[i][0].test(t)) { | |||
var before = t, tries=0, befText, afterText; | |||
/* repeat replacement for 3 times as sometimes need to converge for more than a single run | |||
Such as boundary [^a-z][a-z][^a-z] replacing [a|a]. Use of negative char match is useful | |||
for different regex engines with different support of look ahead and lookbehind */ | |||
do { | |||
before = t; | |||
t = t.replace(this.regexes[i][0], this.regexes[i][1]); | |||
if ((tries==0) && (t != before)) { | |||
// 1st match as representative example for summary | |||
befText = this.regexes[i][0].exec(before)[0]; | |||
afterText = befText.replace(this.regexes[i][0], this.regexes[i][1]); | |||
actual_replaced.push(afterText); | |||
actual_replaced_details.push(i + ': ' + befText + ' ← ' + afterText); | |||
} | |||
tries++; | |||
} while ((tries < 3) && (t != before)) | |||
} | |||
while (true) { | |||
match = t.match(/\0(\d+)\0/); | |||
if (!match || !match.length) break; | |||
t = t.replace(match[0], specials[parseInt(match[1], 10) - 1]); | |||
} | |||
this.updateText(t); | |||
var msg = ['החלפות - בוצעו ' + actual_replaced.length + ' החלפות' + (actual_replaced_details.length==0? '.' : ': ' + actual_replaced_details.join(', '))]; | |||
if (skip_ar.length) { | |||
msg.push('<br />החלפות שלא התבצעו בגלל תבנית "ללא בוט": '); | |||
msg.push($('<ul></ul>').append(skip_ar)); | |||
} | |||
if(actual_replaced.length) { | |||
msg.push(' אנא בצעו "הצגת שינויים" לפני שמירה, כדי לוודא שהסקריפט לא גרם נזק.'); | |||
} | |||
this.writeMsg($('<div></div>').append(msg), 'info'); | |||
if (actual_replaced.length) chectTyTool.addSummary(mw.msg('checkty-replace-summary', actual_replaced.join(', '))); | |||
}, | |||
fetchDisambigLinks: function (next) { | |||
var dfd = new jQuery.Deferred(); | |||
var api = new mw.Api(); | |||
var params = { | |||
action: 'query', | |||
generator: 'links', | |||
titles: mw.config.get('wgPageName'), | |||
prop: 'pageprops', | |||
ppprop: 'disambiguation', | |||
gpllimit: '500', | |||
redirects: 1 | |||
}; | |||
if (next !== undefined) { | |||
params.gplcontinue = next; | |||
} | |||
api.get(params).done(function (data) { | |||
//extract disambig pages | |||
if (!data.hasOwnProperty('query')) { | |||
dfd.reject(); | |||
return; | |||
} | |||
var redirects = {}; | |||
if (data.query.redirects) { | |||
$.each(data.query.redirects, function(i,r) { redirects[r.to] = r.from; }); | |||
} | |||
var disambigs = []; | |||
for (var pid in data.query.pages) { | |||
var p = data.query.pages[pid], | |||
isDisambigPage = p.pageprops && p.title != mw.config.get('wgTitle') + mw.msg( 'checkty-disambig-suffix' ); | |||
//list only real disambig links | |||
if (isDisambigPage && (chectTyTool.getLinkRegex(p.title).exec(chectTyTool.textbox.value) || (redirects[p.title] && chectTyTool.getLinkRegex(redirects[p.title]).exec(chectTyTool.textbox.value)))) { | |||
disambigs.push(redirects[p.title] || p.title); | |||
} | |||
} | |||
if (data['query-continue'] !== undefined) { | |||
var nextReq = chectTyTool.fetchDisambigLinks(data['query-continue'].links.gplcontinue); | |||
nextReq.done(function (more) { | |||
dfd.resolve($.merge(disambigs, more)); | |||
}); | |||
} else { | |||
dfd.resolve(disambigs); | |||
} | |||
}); | |||
return dfd.promise(); | |||
}, | |||
mainArticlesMerge: function() { | |||
var origTxt = this.textbox.value, txt = this.textbox.value, m; | |||
while (m = /(\{\{(?:הפניה לערך מורחב|ערך מורחב)\|[^=\n]+?\}\}\n){2,}/g.exec(txt)) { | |||
var articles = [], expArticleRE = /\{\{(?:הפניה לערך מורחב|ערך מורחב)\|([^=\n]+?)\}\}/g; | |||
while (ma = expArticleRE.exec(m[0])) articles.push(ma[1]); | |||
txt = txt.replace(m[0], '{{הפניה לערך מורחב|ערכים=[['+articles.join(']], [[')+']]}}\n'); | |||
} | |||
this.textbox.value = txt; | |||
if (origTxt != txt) chectTyTool.addSummary( mw.msg( 'checkty-main-articles-merge-summary' ) ); | |||
}, | |||
formatReplace: function () { | |||
var txt = this.textbox.value, newTxt; | |||
// Format autofix | |||
var specials=[], match; | |||
// var emptyParametersRgx = /\n *\| *[^|=]+?= *(?=\n(?: *\||\}\}))/mg, emptyParametersMsg, emptyParametersBtn; | |||
$(this.formatReplacesConfigSafe).each(function (i, o) { | |||
newTxt = txt.replace(o.from, o.to); | |||
if ( newTxt!=txt && o.comment) { | |||
chectTyTool.addSummary( o.comment ); | |||
} | |||
txt = newTxt; | |||
}); | |||
//extract inner links, inner templates and inner params - we don't want to sptit those. | |||
while (true) { //extract inner links, inner templates and inner params - we don't want to sptit those. | |||
match = txt.match(/(\{\{[^\{\}]*\}\}|(\n|\[\[)(?:File|קובץ|תמונה|Image):.*?[\|\n]|[^\[\0]\[[^\{\}\[]*\])/); | |||
if (!match || !match.length) break; | |||
specials.push(match[0]); | |||
txt = txt.replace(match[0], "\0" + specials.length + "\0"); | |||
} | |||
$(this.formatReplacesConfig).each(function (i, o) { | |||
newTxt = txt.replace(o.from, o.to); | |||
if (o.skippable && (chectTyTool.skipCheckty || this.isSection)) return; // skip this one respecting {ללא בוט} | |||
if ( newTxt!=txt && o.comment) { | |||
chectTyTool.addSummary( o.comment ); | |||
} | |||
txt = newTxt; | |||
}); | |||
while (true) { | |||
match = txt.match(/\0(\d+)\0/); | |||
if (!match || !match.length) break; | |||
txt = txt.replace(match[0], specials[parseInt(match[1], 10) - 1]); | |||
} | |||
this.updateText(txt); | |||
// if (!emptyParametersRgx.test(txt)) return; | |||
// emptyParametersMsg = $('<div>', { text: mw.msg('checkty-empty-parameters') }).addClass('checkty-empty-params-warning').append($('<a>', { | |||
// href: '#', | |||
// text: 'ניקוי' | |||
// }).click(function(){ $('#wpTextbox1').val($('#wpTextbox1').val().replace(emptyParametersRgx, '')); chectTyTool.addSummary( mw.msg( 'checkty-empty-parameters-summary' ) ); return false; })); | |||
// this.writeMsg(emptyParametersMsg, 'info'); | |||
}, | |||
vavCheck: function () { | |||
var text = this.textbox.value, | |||
vavRegex = /[כלבמשה]\[\[ו[^ו][א-ת ]+\]\]/g, self = this; | |||
if (!vavRegex.exec( text )) return; | |||
var vavWarning = $('<div>').text( mw.msg( 'checkty-vav-warning' ) ).append(this.createSearchLink(vavRegex)); | |||
// TODO: add semi-automatic fix option? | |||
chectTyTool.writeMsg(vavWarning); | |||
}, | |||
disambigCheck: function () { | |||
var disambigMsg = this.writeMsg($('<div>', { | |||
id: 'waitForDisambigs' | |||
}).text( mw.msg( 'checkty-waiting-disambig-query' ) ), 'info'), self = this; | |||
this.fetchDisambigLinks().fail(function () { | |||
disambigMsg.remove(); | |||
self.writeMsg($('<div>', { text: mw.msg('checkty-disambig-no-links') }), 'check'); | |||
}).done(function (res) { | |||
var disambigs; | |||
disambigMsg.remove(); | |||
if (res.length === 0) { | |||
self.writeMsg($('<div>', { text: mw.msg('checkty-disambig-success') }), 'check'); | |||
return; | |||
} | |||
disambigs = $('<div id="disambigWarnning">'+mw.msg( 'checkty-disambig-links-title' )+'</div>').prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Disambig_RTL.svg/15px-Disambig_RTL.svg.png">').css('padding', '0 5px'); | |||
$.each(res, function (i, disTitle) { | |||
if (i > 0) { | |||
disambigs.append(', '); | |||
} | |||
disambigs.append($('<a href="' + mw.util.getUrl(disTitle) + '">' + disTitle + '</a>').click(function () { | |||
var disambigName = $(this).text(); | |||
new mw.Api().get({ | |||
action: 'parse', | |||
page: disambigName, | |||
prop: 'text', | |||
redirects: 1 | |||
}).done(function (data) { | |||
if (data && data.parse && data.parse.text) { | |||
var disambig = data.parse.text['*']; | |||
mw.loader.using( ['jquery.ui'] ).done ( function() { chectTyTool.resolveDisambig( disambigName, disambig ); } ); | |||
} else { | |||
console.error(data); | |||
} | |||
}); | |||
return false; | |||
})); | |||
}); | |||
chectTyTool.writeMsg(disambigs); | |||
}); | |||
}, | |||
getLinkRegex: function(name) { | |||
return new RegExp('(?:\\.|^)([^\\.\n]*(\\[\\[' + mw.util.escapeRegExp(name) + '[\\|\\]]).*?)[\\.\\n]', 'm'); | |||
}, | |||
resolveDisambig: function (name, data) { | |||
var offset = 0, | |||
textbox = this.textbox, | |||
linkRgx = this.getLinkRegex(name), | |||
orgPos = $(textbox).textSelection('getCaretPosition'), | |||
cSentence = $('<div>'); | |||
var options = $('<div>').append($('li', data).filter(function() { return $(this).closest( '.checkty-ignore' ).length === 0; } ).map(function () { | |||
var a = $(this).children('a').get(0); | |||
if (a) { | |||
var storeTitle = $(this).text(); | |||
var anchor = '', | |||
h = a.href; | |||
if (h.indexOf('#') + 1) anchor = decodeURI(h.substr(h.indexOf('#')).replace(/\./g, '%').replace(/_/g, ' ')); | |||
$(a).text( a.title.replace(mw.msg( 'checkty-page-doesnt-exist' ), "") + anchor); | |||
a.title = storeTitle; | |||
} | |||
return a || null; | |||
}).click(resolve)) | |||
.append($('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').click(removeLink)); | |||
var disambigDialog = $('<div>').append( mw.msg('checkty-disambig-meaning', name) + '<hr/>').append(cSentence).append(options.buttonset()).dialog({ | |||
title: mw.msg( 'checkty-dismabig-dialog-title' ), | |||
close: function () { | |||
$(textbox).textSelection('setSelection', { | |||
start: orgPos | |||
}); | |||
} | |||
}); | |||
findSentence(); | |||
function findSentence() { | |||
var text = textbox.value.substr(offset), | |||
m = text.match(linkRgx); | |||
if (!m) { | |||
disambigDialog.dialog('close'); | |||
return; | |||
} | |||
offset += text.indexOf(m[1]) + m[1].indexOf(m[2]); | |||
var linkIndex = m[1].indexOf(m[2]) + 2, | |||
html = m[1].substr(0, linkIndex) + '<big>' + name + '</big>' + m[1].substr(linkIndex + name.length); | |||
cSentence.html(html); | |||
} | |||
function resolve() { | |||
var answer = $(this).text(), | |||
text = textbox.value, | |||
startLink = text.indexOf('[[' + name, offset); | |||
if (text.charAt(startLink + 2 + name.length) != '|') answer += '|' + name; | |||
offset += answer.length + 2; | |||
text = text.substr(0, startLink + 2) + answer + text.substr(startLink + 2 + name.length); | |||
textbox.value = text; | |||
findSentence(); | |||
chectTyTool.addSummary( mw.msg( 'checkty-dismabig-fix-summary' ) ); | |||
return false; | |||
} | |||
function removeLink() { | |||
var text = textbox.value, | |||
startLink = text.indexOf('[[' + name, offset), | |||
endLink = text.indexOf(']]', startLink), | |||
pipeChar = startLink + 2 + name.length, | |||
linkText = (text.charAt(pipeChar) != '|') ? name : text.substr(pipeChar + 1, endLink - pipeChar - 1); | |||
offset += (endLink - startLink) + linkText.length; | |||
text = text.substr(0, startLink) + linkText + text.substr(endLink + 2); | |||
textbox.value = text; | |||
findSentence(); | |||
return false; | |||
} | |||
}, | |||
addSummary: function (msg) { | |||
var editSummary = $('#wpSummary').val(); | |||
if (editSummary.indexOf(msg) === -1) $('#wpSummary').val(editSummary + (editSummary.length === 0 ? '' : ', ') + msg); | |||
// tag the edit | |||
if ( $('#checktyTag').length === 0 ) { | |||
$('#editform').append('<input type="hidden" name="wpChangeTags" id="checktyTag" value="צ\'קטי">'); | |||
} | |||
}, | |||
checkGallery: function() { | |||
var text = this.textbox.value, | |||
longGalleryThreshold = 16, | |||
galleryRgx = /<gallery.+?\n(\n|.)+?<\/gallery>/mg, | |||
m, longGallery=false; | |||
while ( m=galleryRgx.exec( text ) ) { var galLength = m[0].split('\n').length-2; longGallery |= (galLength > longGalleryThreshold); } | |||
if (longGallery) { | |||
this.writeMsg('<div>' + mw.msg('checkty-long-gallery') + '</div>', 'alert'); | |||
} | |||
}, | |||
checkImages: function (data) { | |||
var fairUsageTemplates = ['תבנית:שימוש הוגן', 'תבנית:תמונת חבר כנסת']; | |||
if (!data) { | |||
if (!(/\{\{(ויקישיתוף בשורה|מיזמים)/.test(this.textbox.value))) { | |||
$.getJSON('//www.wikidata.org/w/api.php?callback=?', { | |||
languages: 'he', | |||
action: 'wbgetentities', | |||
sites: mw.config.get('wgDBname'), | |||
titles: mw.config.get('wgTitle'), | |||
format: 'json', | |||
props: 'claims' | |||
}).done(function (data) { | |||
if (data.success === undefined || !data.success) return; | |||
for (var entityId in data.entities) { | |||
var claims = data.entities[entityId].claims; | |||
if (claims && claims.hasOwnProperty('P373')) { | |||
chectTyTool.writeMsg($('<div>', { | |||
text: mw.msg('checkty-missing-commons-link') | |||
}).prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Commons-logo.svg/15px-Commons-logo.svg.png">').css('padding', '0 5px').append($('<a>', { | |||
href: '#', | |||
text: 'להוספה' | |||
}).click(function (e) { | |||
e.preventDefault(); | |||
var ta = $('#wpTextbox1'); | |||
if (ta.textSelection('getCaretPosition') >= ta.val().length) { | |||
chectTyTool.writeMsg($('<div>').text(mw.msg('checkty-please-position-caret'))); | |||
} else { | |||
ta.textSelection('encapsulateSelection',{pre:'{{ויקישיתוף בשורה}}'}); | |||
chectTyTool.addSummary('ויקישיתוף בשורה'); | |||
} | |||
}))); | |||
} | |||
} | |||
}); | |||
} | |||
//in case there are no images in page | |||
if (!(/\[\[(תמונה|קובץ|File|Image):/i.test(this.textbox.value))) { | |||
var articleName = mw.config.get('wgPageName'); | |||
var that = this; // we want to use "this" in the done method of the api call. | |||
new mw.Api().post( { | |||
action: 'parse', | |||
title: articleName, | |||
text: this.textbox.value | |||
} ).done( function( data ) { | |||
if ( data && data.parse && data.parse.text && $( data.parse.text['*'] ).find( 'a.image img' ).filter(function() { | |||
var width = $(this).attr('width'); | |||
if (isNaN(width)) width = $(this).width(); | |||
else width = parseInt(width); | |||
return (width >= 100) && $(this).parents('.navbox').length==0; | |||
} ).length === 0 ) { | |||
var fistURL = that.fistURL({ | |||
datatype: 'articles', | |||
data: articleName | |||
}); | |||
var msg = $('<div>', { | |||
text: 'בדף זה אין תמונות. ניתן לחפש תמונות חופשיות ממקורות שונים. ' | |||
}).append($('<a>', { | |||
href: decodeURI(fistURL), | |||
text: 'חיפוש תמונות', | |||
target: '_blank' | |||
})); | |||
that.writeMsg(msg, 'info'); | |||
} | |||
}); | |||
return; | |||
} | |||
new mw.Api().get({ | |||
action: 'query', | |||
generator: 'images', | |||
titles: mw.config.get('wgPageName'), | |||
prop: 'templates', | |||
tltemplates: fairUsageTemplates.join('|') | |||
}).done(function (data) { | |||
if (data && data.query && data.query.pages) chectTyTool.checkImages(data.query.pages); | |||
}); | |||
} else { | |||
var fairUseImgs = $.map(data, function (o) { | |||
if (!o.templates) return; | |||
var isFairUsage; | |||
$.each(o.templates, function (k, license) { | |||
if ($.inArray(license.title, fairUsageTemplates) != -1) { | |||
isFairUsage = true; | |||
return false; | |||
} | |||
}); | |||
if (isFairUsage) return o.title; | |||
}); | |||
if (fairUseImgs.length === 0) return; | |||
//add message with fair usage images | |||
var fistURL = this.fistURL({ | |||
data: fairUseImgs.join('\r\n'), | |||
datatype: 'replaceimages' | |||
}); | |||
var msg = $('<div>', { | |||
text: 'הדף מכיל תמונות בשימוש הוגן, שמומלץ להחליפן בחלופות חופשיות במידת האפשר. ' | |||
}).append($('<a>', { | |||
href: fistURL, | |||
text: 'חיפוש חלופות חופשיות', | |||
target: '_blank' | |||
})); | |||
if (!(new RegExp('\{\{'+mw.msg('checkty-fairuse-img-replace-template')+'\}\}').test($('#wpTextbox1').val()))) | |||
msg.append(' - ').append($('<a>', { | |||
text: 'סימון להחלפה', | |||
href: '#' | |||
}).click(function () { | |||
var t = $('#wpTextbox1'); | |||
$.each(fairUseImgs, function (i, fiImg) { | |||
var imgName = mw.util.escapeRegExp(/.:(.+)$/.exec(fiImg)[1]), | |||
imgDescRE = new RegExp(imgName.replace(' ', '[ _]') + '((?:[^\\[\\]]|\\[\\[[^\\[\\]]*?\\]\\])*?)\]\]', 'i'), | |||
matches; | |||
if (!(matches = imgDescRE.exec(t.val()))) { | |||
return; | |||
} | |||
//this is thumb img | |||
var imgDesc = matches[1].split('|'), isThumb = false, imgCaption = ''; | |||
for (var i in imgDesc) { | |||
if (/thumb|ממוזער/i.test(imgDesc[i])) { | |||
isThumb = true; | |||
} else if (!(/^(ימין|שמאל|מרכז|right|left|center|[0-9]+px)?$/i.test(imgDesc[i]))) { | |||
imgCaption = imgDesc[i]; //unknown parameter assumed to be description | |||
} | |||
} | |||
if (isThumb) { | |||
if (imgCaption) { | |||
imgDesc = matches[1].replace(imgCaption, imgCaption + '{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); | |||
} else { | |||
imgDesc = matches[1] + '|{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'; | |||
} | |||
t.val(t.val().replace(matches[1], imgDesc)); | |||
chectTyTool.addSummary('{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); | |||
} else { | |||
//is in infobox heuristic: = before image tag | |||
var isInInfobox = new RegExp('=\s*\\[\\[(?:file|image|קובץ|תמונה):' + imgName.replace(' ', '[ _]') + '[^\\]]*?\]\]', 'i'); | |||
if (isInInfobox.test(t.val())) { | |||
t.val(t.val().replace(matches[0], matches[0] + '{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}')); | |||
chectTyTool.addSummary('{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); | |||
} | |||
} | |||
}); | |||
})); | |||
this.writeMsg(msg, 'alert'); | |||
} | |||
}, | |||
fistURL: function (p) { | |||
return 'https://www.inn.co.il/search' + $.param(p); | |||
}, | |||
languageCheck: function (checks) { //style and language check | |||
var txt = this.textbox.value; | |||
if (checks) { | |||
var checkWarnings = $('<div></div>'); | |||
for (var x in checks) { | |||
if (checks[x]['test'].test(txt)) { | |||
var m = checks[x]['test'].exec(txt), langCheckPrefix = ''; | |||
if (m[0].split(' ').length < 4 && checks[x]['test'].source.indexOf('\\]')==-1) langCheckPrefix = mw.msg('checkty-language-check', m[0]); | |||
checkWarnings.append(this.createSearchLink(checks[x]['test'])); | |||
checkWarnings.append(' - ' + langCheckPrefix + checks[x]['remark'] + '<br/>'); | |||
} | |||
} | |||
// Lint checker: wikilink-in-extlink. see [[mw:Help:Lint_errors/wikilink-in-extlink]] | |||
var wikilinkInExtLink = /[^\[]\[[^\[\]]+\[\[.+?\]\]/; | |||
if (wikilinkInExtLink.test(txt)) { | |||
checkWarnings.append(this.createSearchLink(wikilinkInExtLink)); | |||
checkWarnings.append(' - ' + mw.msg( 'checkty-wikilink-in-extlink' )); | |||
} | |||
// Design checks: Elements width | |||
var largeElement = /[6789][0-9][0-9]px/; | |||
if (largeElement.test(txt)) checkWarnings.append(mw.msg( 'checkty-large-element' ) + '<br/>'); | |||
// Design checks: Long lists can have hint to use columns | |||
var manyLi = new RegExp('(?:\n\\*.*){20}'); | |||
if (manyLi.test(txt)) { | |||
checkWarnings.append(this.createSearchLink(manyLi)); | |||
checkWarnings.append(' - ' + mw.msg( 'checkty-long-list' )); | |||
} | |||
if (checkWarnings.html().length) this.writeMsg(checkWarnings, 'alert'); | |||
} else { | |||
var api = new mw.Api(); | |||
api.get({ | |||
action:'parse', | |||
page: 'ויקישיבה:בדיקה אוטומטית', | |||
prop: 'wikitext' | |||
}).done(function (data) { | |||
if (!(data && data.parse && data.parse.wikitext)) return; | |||
var DictionaryText = data.parse.wikitext['*'].split('-----')[1] | |||
var genrealWarningWords = DictionaryText.split('\n*'); | |||
var checks = []; | |||
for (var i=0;i<genrealWarningWords.length;i++) | |||
{ | |||
var splittedWarn = genrealWarningWords[i].split("//"); | |||
if ( splittedWarn.length !== 2 ) continue; | |||
checks.push({ | |||
'test': new RegExp( splittedWarn[0], 'i' ), | |||
'remark': splittedWarn[1] | |||
}); | |||
} | |||
chectTyTool.languageCheck(checks); | |||
}); | |||
var countRgx = /(?:[^ ]+ ){2}.?(?:ש[נת]י|שלוש[תה]?|ארבע[הת]?|חמשת?|חמישה|ששת?|שבע[הת]?|תשע[הת]?|עשר[הת]?) [א-ת]+/g, | |||
gramCheck = [], m, self = this; | |||
while (m = countRgx.exec(txt)) { gramCheck.push(m) } | |||
if (gramCheck.length) $.post('//tools.wmflabs.org/eranbot/shtei_shekel/heb_check.py', { wikitext: gramCheck.join('\n') } ).done(function(d){ | |||
if (!d.errs || d.errs.length === 0) return; | |||
var zaharNekevaWarns = $('<div></div>'); | |||
for ( var i = 0; i < d.errs.length; i++ ) { | |||
var origRgx = new RegExp(d.errs[i].orig); | |||
zaharNekevaWarns.append(self.createSearchLink(origRgx)); | |||
zaharNekevaWarns.append(' - ייתכן שיש אי התאמה במין ב"' + d.errs[i].orig + '" (תיקון: '+d.errs[i].suggested+') <br/>'); | |||
} | |||
chectTyTool.writeMsg(zaharNekevaWarns, 'alert'); | |||
}); | |||
} | |||
}, | |||
refsConsistencyCheck: function () { | |||
function safeRegexFix(fixRegex, fixReplace, check){ | |||
var txt = $('#wpTextbox1').val(), | |||
m; | |||
//remove templates within templates | |||
var specials = []; | |||
while (m = /\{\{(?!הערה\|)[^\{]*?\}\}/g.exec(txt)) { | |||
txt = txt.replace(m[0], '\0' + specials.length + '\0') | |||
specials.push(m[0]) | |||
} | |||
if (check) { | |||
var counterA = 0, counterB = 0; | |||
while (fixRegex.exec(txt)) counterA++; | |||
while (fixReplace.exec(txt)) counterB++; | |||
return [counterA, counterB]; | |||
} else { | |||
txt = txt.replace(/(\{\{הערה\|.*?\}\}|<ref>.*?<\/ref>)\s+(?=(\{\{הערה\||<ref>))/g, '$1'); // remove spaces between refs | |||
txt = txt.replace(fixRegex, fixReplace); | |||
while (m = specials.pop()) txt = txt.replace('\0' + specials.length + '\0', m); | |||
$('#wpTextbox1').val(txt); | |||
} | |||
} | |||
var refAfter = /([\.\,])\s*((\{\{הערה.*?\}\}|<ref>.*?<\/ref>)+)\.?/g, | |||
refBefore = /\.?((\{\{הערה\|([^\{]|\{(?!\{הערה\|))*\}\}|<ref>([^<]|<(?!\/ref>))*<\/ref>)+)\s*([\.\,])/g, | |||
refCounter = safeRegexFix(refAfter, refBefore, true); | |||
if (refCounter[0] > 0 && refCounter[1] > 0) { | |||
this.writeMsg($('<div>', { | |||
text: 'חוסר תאימות בהערות שוליים: ' + refCounter[0] + ' הערות אחרי סימן פיסוק, ' + refCounter[1] + ' הערות לפני סימן פיסוק [תיקון: ' | |||
}).append($('<a>', { | |||
text: 'אחרי', | |||
href: '#' | |||
}).click(function () { | |||
safeRegexFix(refBefore, '$5$1', false); | |||
chectTyTool.addSummary('אחידות במיקום הערות שוליים'); | |||
})).append(' | ').append($('<a>', { | |||
text: 'לפני', | |||
href: '#' | |||
}).click(function () { | |||
safeRegexFix(refAfter, '$2$1', false); | |||
chectTyTool.addSummary('אחידות במיקום הערות שוליים'); | |||
})).append(']'), 'alert'); | |||
} | |||
}, | |||
ibidWarning: function() { | |||
var refRE = /\{\{הערה\|(?! *שם *=)(?:1=)?((?:[^\{\}]|\{\{.*?\}\})+)(?!\|=שם=)\}\}/g, | |||
wikitext = this.textbox.value, self=this, m; | |||
var checkWarnings = $('<div></div>'); | |||
while ( m = refRE.exec( wikitext ) ){ | |||
if(/^שם[ .,]|^שם$/.test(m[1]) || /\bibid(?![^a-z])/i.test(m[1])){ | |||
checkWarnings.append(this.createSearchLink(m[0])); | |||
checkWarnings.append(' - נמצאה הערת שוליים המפנה להערה הקודמת בצורה של שם. יש להחליף לציון מקור מדויק. ראו עוד: <a href="'+mw.util.getUrl('ויקישיבה:הערות שוליים')+'">ויקישיבה:הערות שוליים</a><br/>'); | |||
} | |||
} | |||
if (checkWarnings.html().length) this.writeMsg(checkWarnings, 'alert'); | |||
}, | |||
mergeRefs: function() { // merge refs with same content | |||
this.autoMergeRefs(); // merge refs with name and content that are equal | |||
this.mergeRefsWithoutName(); // merge refs with same content and assign name. requires some manual work | |||
}, | |||
autoMergeRefs: function() { // merge refs with same content and same name | |||
var references = {},// a dictionary where key is the ref name and value is count | |||
refsContent = {},// a dictionary where key is the ref name and value is the content | |||
refReEn = /<ref name="([^"]+?)">(.+?)<\/ref>/g, | |||
wikitext = this.textbox.value, mergedRefs = [], mergedRefsNames = [], m, k, i; | |||
while ( m = refReEn.exec( wikitext ) ){ | |||
references[m[1]] = (references[m[1]] || 0) + 1; | |||
refsContent[m[1]] = { content: m[2], text: m[0] }; | |||
} | |||
for (k in references) { | |||
if (references[k] > 1) { | |||
var content = new RegExp('<ref name="'+mw.util.escapeRegExp(k)+'">(.+?)</ref>', 'g').exec(wikitext), | |||
shortRef = chectTyTool.named_comment + k + '}}'; | |||
i = 0; | |||
if (content) | |||
wikitext = wikitext.replace(new RegExp(mw.util.escapeRegExp(content[0]), 'g'), function(m) { return (i++? shortRef : m); }); | |||
mergedRefs.push(k); | |||
} | |||
} | |||
// merge referecnes with same content but different name | |||
for (k in refsContent) { | |||
if ($.inArray(k,mergedRefsNames) != -1) continue; // skip name already merged | |||
for (i in refsContent) { | |||
if (k === i) continue; // skip same name | |||
if (refsContent[k].content == refsContent[i].content) { // two refs with same content but different name | |||
var oldRefName = new RegExp('<ref name="'+mw.util.escapeRegExp(i)+'" */>', 'g'); | |||
mergedRefsNames.push(i); | |||
shortRef = chectTyTool.named_comment + k + '}}'; | |||
wikitext = wikitext.replace(refsContent[i].text, shortRef); | |||
wikitext = wikitext.replace(oldRefName, shortRef); | |||
} | |||
} | |||
} | |||
if (mergedRefs.length + mergedRefsNames.length) { | |||
if (mergedRefs.length) this.writeMsg( $('<div>').text('מיזוג הערות: ' + mergedRefs.join(', ')) ); | |||
if (mergedRefsNames.length) this.writeMsg( $('<div>').text('מיזוג הערות עם שמות שונים: ' + mergedRefsNames.join(', ')) ); | |||
this.addSummary( 'מיזוג הערות אוטומטי' ); | |||
this.textbox.value = wikitext; | |||
} | |||
}, | |||
mergeRefsWithoutName: function() { // merge refs with same content | |||
var references = {},// a dictionary where key is the ref content and the value is list of uses | |||
refTemplateRE = /\{\{הערה\|(?! *שם *=)(?:1=)?((?:[^\{\}]|\{\{.*?\}\})+)(?!\|=שם=)\}\}/g, | |||
refTagRE = /<ref>(.+?)<\/ref>/g, | |||
wikitext = this.textbox.value, m; | |||
while ( m = refTemplateRE.exec( wikitext ) ){ | |||
references[m[1]] = references[m[1]] || []; | |||
references[m[1]].push(m[0]); | |||
} | |||
while ( m = refTagRE.exec( wikitext ) ){ | |||
references[m[1]] = references[m[1]] || []; | |||
references[m[1]].push(m[0]); | |||
} | |||
for (var refContet in references) { | |||
var refUses = references[refContet]; | |||
if ( refUses.length === 1 ) continue; | |||
if(/^שם[ .,]|^שם$/.test(refContet) || /\bibid(?![^a-z])/i.test(refContet)) continue; // skip ibid refs | |||
var commonRefStructure = /(.+?) .+ (?:עמ|p).? ?([0-9]+)/.exec(refContet); | |||
var defaultRefName = ''; | |||
if (commonRefStructure) { | |||
defaultRefName = commonRefStructure[1]+commonRefStructure[2]; | |||
} | |||
var refName = prompt('ישנה הערת שוליים החוזרת מספר פעמים. ניתן להפנות את כל המופעים אל הערה אחת באמצעות מתן שם קצר ובעל משמעות להערה.\nאנא הזינו שם להערה הבאה:\n'+refContet, defaultRefName); | |||
if ( !refName ) continue; | |||
wikitext = wikitext.replace( refUses[0], chectTyTool.named_comment+refName+'|'+refContet+'}}'); | |||
for (var refI = 1; refI<refUses.length; refI++) { | |||
wikitext = wikitext.replace( refUses[refI], chectTyTool.named_comment+refName+'}}'); | |||
this.addSummary( mw.msg( 'checkty-ref-merge' ) ); // addSummary will only add it once | |||
} | |||
this.textbox.value = wikitext; | |||
} | |||
}, | |||
titleOrderCheck: function(){ | |||
// validates the titles order is consistent | |||
var orderedTitles = ['ראו גם', 'לקריאה נוספת', 'קישורים חיצוניים', 'הערות שוליים'], | |||
isSorted = 1, | |||
titles = $('#wpTextbox1').val().match('==\\s*'+orderedTitles.join('|')+'\\s*==','g'), i, indexes; | |||
if (!titles) return; // no such titles | |||
indexes = $.map( titles, function(e){return orderedTitles.indexOf(e.replace(/\s*==\s*/g,''))}); | |||
for ( i=0; ( i < indexes.length-1 ) && isSorted; i++) { isSorted &= (indexes[i] < indexes[i+1]) }; | |||
if ( !isSorted ) { | |||
this.writeMsg($('<div>מומלץ לתקן את סדר הכותרות ל: ' + orderedTitles.join(', ') + '</div>'), 'alert' ); | |||
} | |||
}, | |||
refDirSuggest: function() { // suggest adding direction for refs | |||
var wikitext = this.textbox.value, | |||
defaultDir = (/{{הערות שוליים *\| *יישור *= *שמאל}}/.exec( wikitext ))? 'ltr' : 'rtl', | |||
ltrRefRegex = /\{\{הערה\|(?:שם ?=[^|]*\|)?(?:1= *)?(?! *\[?https?:\/\/[^ ]+\]? *\}\})( *\[?[a-z][^א-ת{|]+?)\}\}/ig, | |||
rtlRefRegex = /\{\{הערה\|(?!שם ?=)(?:1= *)?([א-ת][^a-z{|]+?|\[http:[^ ]+ [א-ת][^a-z{|]+?)\}\}/ig, | |||
nonDefaultRefRegex = (defaultDir == 'rtl') ? ltrRefRegex : rtlRefRegex, | |||
uncommonDir = (defaultDir == 'rtl') ? 'שמאל' : 'ימין', | |||
m; | |||
while( m = nonDefaultRefRegex.exec(wikitext)) | |||
{ | |||
var fixRefDirLink = $('<a href="#">'+mw.msg( 'checkty-ref-dir-fix' )+'</a>').data({'search': m[0]}).click(function(e){ | |||
var specificRefRgx = new RegExp('\\[\\[('+mw.util.escapeRegExp($(this).data('search'))+')(\\]\\]|\\|).+\\[\\[\\1(\\]\\]|\\|.+?\\]\\])', 'g'), | |||
wikitext = $('#wpTextbox1').val(); | |||
wikitext = wikitext.replace($(this).data('search'), $(this).data('search').replace('}}', '|כיוון='+uncommonDir+'}}')); | |||
$('#wpTextbox1').val(wikitext); | |||
chectTyTool.addSummary(mw.msg('checkty-ref-dir-fix')); | |||
e.preventDefault(); | |||
}); | |||
var searchRef = this.createSearchLink(m[1]); | |||
this.writeMsg($('<div>').append(['נמצאה הערת שוליים ללא הגדרת כיווניות "'+m[1]+'" [',searchRef, ' - ', fixRefDirLink, ']']), 'alert'); | |||
} | |||
}, | |||
refSection: function(fix) { | |||
var wikitext = this.textbox.value, newText = wikitext, self = this, | |||
refRE = /<ref>|\{\{הערה\|/g, | |||
refSectionRE = /\{\{הערות[ _]שוליים|<references[ >]/g, | |||
hasRef = refRE.exec(wikitext) != null, | |||
hasRefSection = refSectionRE.exec(wikitext) != null; | |||
if (hasRefSection) return; //not missing | |||
if (!hasRef) return; // not needed | |||
if (fix) { // fix only when when explictly asked | |||
var autoRefSection = '\n{{הערות שוליים}}\n' | |||
var lastItems = [/(\n{{בקרת זהויות)/, /(\n{{קצרמר)/,/\n({{הבהרה (?:רפואית|הלכתית|משפטית))/,/(\n{{מיון רגיל:)/, /(\n\[\[קטגוריה:)/]; | |||
for (var i=0; (i < lastItems.length) && (wikitext == newText); i++ ) newText = wikitext.replace(lastItems[i], autoRefSection + '$1'); | |||
if ( wikitext === newText ) newText = wikitext + autoRefSection; | |||
this.textbox.value = newText; | |||
this.addSummary('פרק הערות שוליים'); | |||
this.writeMsg($('<div>'+mw.msg( 'checkty-missing-ref-section-added' )+'</div>'), 'info'); | |||
} else { | |||
this.writeMsg($('<div>'+mw.msg( 'checkty-missing-ref-section-add' )+'</div>').append($('<a href="#">'+mw.msg( 'checkty-missing-ref-section-autofix' )+'</a>').click(function(){ self.refSection(true); return false; })), 'info'); | |||
} | |||
}, | |||
overlinkify: function() { | |||
function removeOverlink(context, link){ | |||
var newcontext = context; | |||
do { | |||
context = newcontext; | |||
newcontext = newcontext.replace(new RegExp('\\[\\[(' + mw.util.escapeRegExp(link)+')([^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?)\\[\\[\\1\\|(.+?)\\]\\]', 'g'), '[[$1$2$3'); | |||
newcontext = newcontext.replace(new RegExp('\\[\\[(' + mw.util.escapeRegExp(link)+')([^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?)\\[\\[(\\1)\\]\\]', 'g'), '[[$1$2$3'); | |||
} while( newcontext != context ) | |||
return newcontext; | |||
} | |||
// using [^{}\n] as a quick hack to counting links within templates | |||
var wikitext = this.textbox.value, | |||
overlinkingRgx = /\[\[([^\[\]\|]+?)(?:\]\]|\|)[^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?\[\[\1(?:\]\]|\|.+?\]\])/g, | |||
m; | |||
while ( m = overlinkingRgx.exec(wikitext) ) { | |||
if (/(?:File|קובץ|תמונה|מדיה):.+/.test(m[1])) { | |||
continue; // skip files | |||
} | |||
var removeLink = $('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').data({'search': m[1]}).click(function(e){ | |||
var specificOverlinkingRgx = new RegExp('\\[\\[('+$(this).data('search')+')(\\]\\]|\\|)[^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?\\[\\[\\1(\\]\\]|\\|.+?\\]\\])', 'g'); | |||
var wikitext = $('#wpTextbox1').val(), m; | |||
while( m = specificOverlinkingRgx.exec( wikitext ) ) | |||
{ | |||
wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1])); | |||
} | |||
$('#wpTextbox1').val(wikitext); | |||
chectTyTool.addSummary('הסרת קישורים עודפים'); | |||
e.preventDefault(); | |||
}); | |||
var searchLink = this.createSearchLink( new RegExp( '\\[\\[' + mw.util.escapeRegExp(m[1]) + '(?:\\||\\]\\])' ) ); | |||
this.writeMsg($('<div>').append(['נמצאו קישורים עודפים ל"'+m[1]+'" [',searchLink, ' - ', removeLink, ']']), 'alert'); | |||
wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1])); | |||
} | |||
}, | |||
getWikidataApi: function(callback) { | |||
return new mw.ForeignApi( 'https://www.wikidata.org/w/api.php' ); | |||
}, | |||
extractWikidataLabelsSuggestions: function() { | |||
var suggestLabelsREen = /(?:\[\[|\|)([א-ת' \-]+?)\]\](?: \('*([a-z][^א-ת]+?)'*\)| *\{\{אנג?\|(.+?)\}\})/gi, | |||
langLink = /\{\{קישור שפה\|([א-ת]+)\| *([^|]+?)\| *([א-ת '\-]+?)\}\}/gi, | |||
langTable = { 'ספרדית': 'es', 'אנגלית': 'en' }, | |||
self = this, | |||
text = this.textbox.value, | |||
suggestionsByLang={}, // lang -> { foreign title -> hebrew label } | |||
foreignTitles= [], | |||
m, lang, suggestions; | |||
suggestionsByLang['en'] = {}; | |||
// populate suggestions | |||
while(m = suggestLabelsREen.exec(text)) suggestionsByLang['en'][m[2] || m[3]] = m[1]; | |||
while(m = langLink.exec(text)) { | |||
if (!langTable.hasOwnProperty(m[1])) | |||
{ | |||
console.log('wikidataLabelsSuggested: Missing lang: ' + m[1]); | |||
continue; | |||
} | |||
lang = langTable[m[1]]; // lang code | |||
if (!suggestionsByLang[lang]) suggestionsByLang[lang] = {}; | |||
suggestionsByLang[lang][m[2]] = m[3]; | |||
} | |||
// query wikidata by lang | |||
for (lang in suggestionsByLang) { | |||
self.suggestWikidataLabelTranslate(suggestionsByLang[lang], lang); | |||
} | |||
}, | |||
suggestWikidataLabelTranslate: function(suggestions, lang) { | |||
var foreignTitles = [], batchsize = 50, self = this; | |||
for(var v in suggestions) foreignTitles.push(v); | |||
if (foreignTitles.length === 0) return; | |||
var extWikiApi = new mw.ForeignApi('//'+lang+'.wikipedia.org/w/api.php'), | |||
queries = Math.ceil(foreignTitles.length/batchsize), | |||
def = $.Deferred(), dataEntities = {}, redirects = {}; | |||
for(var i=0; i<queries; i++) | |||
{ | |||
// go through enwiki API, to resolve redirects instead of direct approach with wikidataApi.getEntitiesByPage | |||
extWikiApi.get({ | |||
action: 'query', | |||
titles: foreignTitles.slice(i*batchsize,(i+1)*batchsize).join('|'), | |||
redirects: 1, | |||
prop: 'pageprops', | |||
ppprop: 'wikibase_item' | |||
}).done(function(d) { | |||
for(var i in d.query.redirects) redirects[d.query.redirects[i].to]=d.query.redirects[i].from; | |||
var wikidataIds = []; | |||
for(var i in d.query.pages) if(d.query.pages[i].pageprops && d.query.pages[i].pageprops.wikibase_item) wikidataIds.push(d.query.pages[i].pageprops.wikibase_item); | |||
if (wikidataIds.length === 0) return; | |||
self.getWikidataApi().get({ | |||
action: 'wbgetentities', | |||
ids: '\x1f' + wikidataIds.join( '\x1f' ), | |||
props: '\x1f' + ['labels', 'sitelinks'].join( '\x1f' ), | |||
languages: '\x1f' + ['he', lang].join( '\x1f' ) | |||
}).done(function(d){ | |||
queries--; | |||
$.extend(dataEntities, d.entities); | |||
if (queries === 0) def.resolve(dataEntities); | |||
}); | |||
}); | |||
} | |||
def.done(function(data) { | |||
var found = false, | |||
translations = $('<div>ייתכן שהערך מכיל תוויות חסרות לישויות בוויקינתונים:</div>'), | |||
langSite = lang + 'wiki'; // example: enwiki | |||
for(var en in data) | |||
{ | |||
if (/^-/.exec(en)) continue; | |||
if (data[en].labels && data[en].labels.hasOwnProperty('he')) continue; // already have hebrew label | |||
found = true; | |||
var suggestion = suggestions[data[en].sitelinks[langSite].title] || suggestions[redirects[data[en].sitelinks[langSite].title]]; | |||
translations.append($('<li>').append($('<a>', { | |||
href: 'https://www.wikidata.org/wiki/'+en, | |||
text: en + ' ('+data[en].labels[lang].value+')', | |||
target:'_blank' | |||
}).data({ | |||
'he': suggestion, | |||
'foreignLabel': data[en].sitelinks[langSite].title, | |||
'q': en | |||
}).click(function(){ | |||
self.wikidataLabelTranslate($(this).data('q'), $(this).data('foreignLabel'), $(this).data('he')); | |||
return false; | |||
}))); | |||
} | |||
if (found) self.writeMsg(translations, 'articleSearch'); | |||
}); | |||
}, | |||
wikidataLabelTranslate: function(entityId, foreignLabel, suggestion) { | |||
var localLabel = prompt(mw.msg('checkty-set-label', foreignLabel), suggestion); | |||
if (localLabel) { | |||
this.getWikidataApi().postWithToken( 'csrf', { | |||
action: 'wbsetlabel', | |||
id: entityId, | |||
value: localLabel, | |||
language: mw.config.get('wgContentLanguage') | |||
} ).done(function(d) { if (d.success) mw.notify(mw.msg('checkty-translate-saved')); }) | |||
} | |||
}, | |||
wikidataLabelsMissing: function() { | |||
var self = this, | |||
batchSize = 50, | |||
needTranslateQ = $( 'a.wb-entity-link[href*="Q"]' ) | |||
.filter( function( i, e ) { return /[a-z]/i.test( e.text ) } ) | |||
.map( function() { | |||
var m=/(Q[0-9]+)/.exec(this.href); | |||
if (m) return m[0]; | |||
}) | |||
.toArray(); | |||
if (needTranslateQ.length === 0 ) return; | |||
if (needTranslateQ.length>batchSize) needTranslateQ = needTranslateQ.slice(0, batchSize); // limit to batch size | |||
this.getWikidataApi().get({ | |||
action: 'wbgetentities', | |||
ids: '\x1f' + needTranslateQ.join( '\x1f' ), | |||
props: '\x1f' + ['labels'].join( '\x1f' ), | |||
languages: mw.config.get('wgContentLanguage') | |||
}).done( function(d) { | |||
var needTranslateIndeedQ = []; | |||
if (!d.entities) return; | |||
for(var q in d.entities) { | |||
if (d.entities[q].labels && !d.entities[q].labels.hasOwnProperty(mw.config.get('wgContentLanguage'))) needTranslateIndeedQ.push(q); | |||
} | |||
if(needTranslateIndeedQ.length === 0) return; | |||
var msg = $('<div>הישויות הבאות חסרות תווית בעברית בוויקינתונים: </div>').append($( 'a.wb-entity-link[href*="Q"]' ).filter( function( i, e ) { return $.inArray(/Q[0-9]+/.exec(e.href)[0], needTranslateIndeedQ)+1 } ).clone().css('margin', '0 3px')); | |||
self.writeMsg(msg, 'alert'); | |||
$('a', msg).click(function(){ | |||
self.wikidataLabelTranslate(/Q[0-9]+/.exec($(this).prop('href'))[0], $(this).text()); | |||
return false; | |||
}); | |||
}); | |||
}, | |||
numberRangeDash: function(fix) { | |||
// replace minus => dash foreach x-y s.t x<y and swap y and x otherwise. prefix/suffix requirement in regex to make sure it is within sentence | |||
var numberRangeReNoMinus = /([א-ת]+'?[-\ ]*)((?:\[\[|\()?[0-9]+(?:\]\])?)-((?:\[\[)?[0-9]+)((?:\]\]|\))?[.:,]?(?:[ \|]|\n)(?!לפנה"ס))/g, | |||
wikitext = this.textbox.value, replacements = [], m, i; | |||
while(m = numberRangeReNoMinus.exec(wikitext)) | |||
{ | |||
if (m[1]=='בואינג ') continue; // skip wrong suggestion | |||
// note - in replacement we dont include the prefix/suffix to make consistent replacement | |||
if ( parseInt(m[2].replace(/[^0-9]/g, '')) < parseInt(m[3].replace(/[^0-9]/g, '')) ) { | |||
replacements.push([m[1]+m[2] + '-' + m[3], m[1]+m[2] + '–' + m[3]]); | |||
} | |||
else if ( (parseInt(m[2].replace(/[^0-9]/g, '')) > parseInt(m[3].replace(/[^0-9]/g, ''))) && (/^[0-9]+$/.test(m[2])) && (/^[0-9]+$/.test(m[3])) ) { | |||
replacements.push([m[1]+m[2] + '-' + m[3], m[1]+m[3] + '–' + m[2]]); | |||
} | |||
} | |||
if (replacements.length === 0) return; | |||
if (fix) { | |||
for(i = 0; i < replacements.length; i++) { | |||
wikitext = wikitext.replace(new RegExp(mw.util.escapeRegExp(replacements[i][0]), 'g'), replacements[i][1]); | |||
} | |||
this.textbox.value = wikitext; | |||
this.addSummary( 'קו מפריד בטווח מספרים' ); | |||
} else { | |||
this.writeMsg($('<div><a href="#">'+mw.msg( 'checkty-fix-numberRangeDash' )+'</a></div>').click(function(e){ chectTyTool.numberRangeDash(true); e.preventDefault(); })); | |||
} | |||
}, | |||
//original version from http://code.google.com/p/proveit-js/source/browse/ProveIt_Wikipedia.js#384 | |||
//thanks to Georgia Tech Research Corporation. Atlanta, GA 30332-0415 | |||
highlightString: function (toFind) { | |||
var txtArea = $('#wpTextbox1'); | |||
// cast to string if this is regex | |||
if ( RegExp.prototype.isPrototypeOf(toFind) ) { | |||
toFind = toFind.exec(txtArea.val()); | |||
if (!toFind) return; | |||
toFind = toFind[0]; | |||
} | |||
var nextPlace = txtArea.val().indexOf(toFind, txtArea.textSelection('getCaretPosition') + 1); | |||
if (nextPlace === -1) nextPlace = txtArea.val().indexOf(toFind); //start from begining | |||
if (nextPlace === -1) return; //not found... nothing to do | |||
var origText = txtArea.val(); | |||
txtArea.val(origText.substring(0, nextPlace)); | |||
txtArea.focus(); | |||
txtArea.scrollTop(1000000); //Larger than any real textarea (hopefully) | |||
var curScrollTop = txtArea.scrollTop(); | |||
txtArea.val(origText); | |||
if (curScrollTop > 0) { | |||
var HALF_EDIT_BOX_HEIGHT = 200; | |||
txtArea.scrollTop(curScrollTop + HALF_EDIT_BOX_HEIGHT); | |||
} | |||
txtArea.focus().textSelection('setSelection', { | |||
start: nextPlace, | |||
end: nextPlace + toFind.length | |||
}); | |||
}, | |||
addAuthorityControl: function(v) { | |||
var t = this.textbox.value, | |||
acTemplate = /\{\{בקרת זהויות[\}|]/g, | |||
artsTitle = /== *(?:מ?ספרי[הו]|מ?יצירותי[הו]|מ?חיבורי[הו]|מאמרים נבחרים|פילמוגרפיה.*|(?:מבחר)? (?:כתבי)|דיסקוגרפיה|יצירות) *==/, //cheap hint for art occuption in text | |||
artsCategory = /\[\[קטגוריה:(מוזיקאיות|מוזיקאים|זמרות|זמרים|סופרים|סופרות|מתרגמים|ציירות|ציירים|במאי|רבנים|רבניות)(?!.+\*)/; //cheap hint for art occuption in category | |||
if ( acTemplate.exec( t ) ) return; // AC already exist | |||
if ( !artsTitle.exec( t ) && !artsCategory.exec( t ) ) return; // no hint will have authority control | |||
this.getWikidataApi().get({ | |||
action: 'wbgetentities', | |||
languages: 'he', | |||
sites: mw.config.get('wgDBname'), | |||
titles: mw.config.get('wgPageName'), | |||
props: '\x1f' + ['claims', 'info'].join( '\x1f' ) | |||
}).done(function(d){ | |||
var hasAC = false, | |||
authControlProps = [ 'P3372', 'P864', 'P1907', 'P6804', 'P2558', 'P4186', 'P3293', 'P1015', 'P2092', 'P1890', 'P950', 'P268', 'P428', 'P651', 'P1273', 'P271', 'P1908', 'P1707', 'P2456', 'P2349', 'P6792', 'P227', 'P902', 'P1146', 'P396', 'P1736', 'P213', 'P347', 'P1248', 'P244', 'P886', 'P1368', 'P640', 'P434', 'P982', 'P1330', 'P966', 'P1004', 'P436', 'P1407', 'P435', 'P549', 'P1225', 'P1048', 'P349', 'P2041', 'P691', 'P409', 'P3348', 'P949', 'P5034', 'P1695', 'P1003', 'P1375', 'P1006', 'P496', 'P2750', 'P1053', 'P3065', 'P650', 'P350', 'P947', 'P906', 'P781', 'P3430', 'P269', 'P4012', 'P1323', 'P7314', 'P1693', 'P3544', 'P1694', 'P1362', 'P1315', 'P6213', 'P245', 'P1157', 'P8034', 'P214', 'P7859' ], | |||
entityId, entity; | |||
for (entityId in d.entities) { entity = d.entities[entityId]; }; | |||
if (!entityId) return; // no entity | |||
if (entity.hasOwnProperty('claims')) authControlProps.forEach(function(p) { hasAC |= entity.claims.hasOwnProperty(p); }) | |||
if (hasAC) { | |||
chectTyTool.addAuthorityControlTemplate(); | |||
} | |||
chectTyTool.viafSearch(entity, entityId, hasAC); | |||
}); | |||
}, | |||
viafSearch: function(entity, entityId, hasAC){ | |||
if (entity.hasOwnProperty('claims') && entity.claims.hasOwnProperty('P214')) { | |||
return; | |||
} | |||
$.getJSON('ttps://www.viaf.org/viaf/AutoSuggest?callback=?&' + $.param({query: mw.config.get('wgTitle').replace(/ \([^()]+\)$/, '')})).done( function(d) { | |||
if (!d.result || d.result.length === 0) return; | |||
var viafOptions = $('<div>לערך זה אין בקרת זהויות ומזהה VIAF. ייתכן שיש דף מקביל ב-VIAF</div>'); | |||
for (var i = 0; i < d.result.length; i++) { | |||
var viafLink = $('<a>', { | |||
href: 'ttps://viaf.org/viaf/' + d.result[i].viafid, | |||
target: '_blank' | |||
}).text(d.result[i].term), | |||
viafAddLink = $('<a>', { | |||
href: '#' | |||
}).text('הוספה').data({ 'viaf': d.result[i].viafid }).click(function(e){ | |||
var viafId = $(this).data('viaf'); | |||
chectTyTool.createClaim(entityId, entity.lastrevid, 'value', 'P214', viafId); | |||
if (!hasAC) chectTyTool.addAuthorityControlTemplate(); | |||
e.preventDefault(); | |||
}); | |||
viafOptions.append($('<li>').append(viafLink, ' - ', viafAddLink)); | |||
} | |||
chectTyTool.writeMsg(viafOptions); | |||
} ); | |||
}, | |||
createClaim: function ( entityId, baseRevId, snakType, propertyId, value ) { | |||
if ( | |||
typeof entityId !== 'string' | |||
|| typeof baseRevId !== 'number' | |||
|| typeof snakType !== 'string' | |||
|| typeof propertyId !== 'string' | |||
|| value && typeof value !== 'string' && typeof value !== 'object' | |||
) { | |||
throw new Error( 'Parameter not specified properly' ); | |||
} | |||
var params = { | |||
action: 'wbcreateclaim', | |||
entity: entityId, | |||
baserevid: baseRevId, | |||
snaktype: snakType, | |||
property: propertyId | |||
}; | |||
if ( value ) { | |||
params.value = JSON.stringify( value ); | |||
} | |||
return this.getWikidataApi().postWithToken( 'csrf', params ); | |||
}, | |||
addAuthorityControlTemplate: function(){ | |||
var t = this.textbox.value, | |||
viafTemplate = /\{\{בקרת זהויות[\}|]/g, | |||
newText; | |||
if ( viafTemplate.exec( t ) ) return; // viaf already exist | |||
newText = t.replace(/{{קצרמר/, '{{בקרת זהויות}}\n{{קצרמר'); // before stub | |||
if (newText == t) newText = t.replace(/{{מיון רגיל:/, '{{בקרת זהויות}}\n{{מיון רגיל:'); //before default sort | |||
if (newText == t) newText = t.replace(/\[\[קטגוריה:/, '{{בקרת זהויות}}\n[[קטגוריה:'); // before categories | |||
if (newText == t) newText = t += '\n{{בקרת זהויות}}'; | |||
this.textbox.value = newText; | |||
this.addSummary('בקרת זהויות'); | |||
} | |||
}; | |||
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) + 1) $(document).ready(function () { | |||
chectTyTool.origText = $('#wpTextbox1').val(); | |||
var checktyBtn = new OO.ui.ButtonInputWidget({ label: 'בדיקה', title: 'צ\'קטי - כלי לבדיקת בעיות נפוצות ועוד', accessKey: (window.checkToolKey || 'e'), id:'btnCheckTool' }); | |||
checktyBtn.$button.updateTooltipAccessKeys(); | |||
$('#wpPreviewWidget').after(checktyBtn.$element.click(chectTyTool.run)); | |||
}); |
גרסה אחרונה מ־11:24, 7 בדצמבר 2021
|
// הוספת כפתור "בדיקה" שמבצע החלפות נפוצות של בוט ההחלפות וכן מתריע על בעיות סגנון ועיצוב שונות // נכתב על ידי [[משתמש:ערן]], [[משתמש:קיפודנחש]] מויקיפדיה והותאם לאתר ע"י [[משתמש:חלוקת קונטרסים]] מחב"דפדיה // לעזרה ראו [[mediawiki:Gadget-Checkty.js/הוראות]] mw.messages.set({ 'checkty-large-element': 'בערך קיים אלמנט גדול, רצוי להקטין כדי שיתאים לרזולוציות נמוכות', 'checkty-wikilink-in-extlink': 'בערך קיים קישור פנימי בתוך קישור חיצוני. רצוי לבטל את הקישור הפנימי או לצמצם את המסגרת של הקישור החיצוני', 'checkty-long-list': 'רשימה ארוכה - נראה כי בערך רשימה של מעל 20 פריטים. כדאי לשקול לפצלה לשני טורים באמצעות <a href="' + (new mw.Title('תבנית:טורים')).getUrl() + '">תבנית:טורים</a>', 'checkty-long-gallery': 'גלריה ארוכה - הערך מכיל גלריה ארוכה. מומלץ לשקול צמצום שלה או העברה לתבנית נפרדת.', 'checkty-waiting-disambig-query': 'ממתין לרשימת פירושונים מהשרת...', 'checkty-warnings-title': 'הערות לבדיקה:', 'checkty-no-replacements': 'הדף מכיל תבנית "ללא בוט" ולכן לא יבוצעו החלפות', 'checkty-replace-summary': '[[וק:רה|החלפות]] ($1)', 'checkty-disambig-suffix': ' (פירושונים)', 'checkty-disambig-no-links': 'קישורים לפירושונים - לא נמצאו', 'checkty-disambig-success': 'קישורים לפירושונים - הבדיקה הסתיימה בהצלחה.', 'checkty-disambig-links-title': 'הגרסה השמורה האחרונה של הדף מקשרת לדפי פירושונים. אנא תקנו את הקישורים לדפים הבאים: ', 'checkty-page-doesnt-exist': ' (הדף אינו קיים)', 'checkty-dismabig-dialog-title': 'תיקון פירושונים', 'checkty-remove-link': 'הסרת קישור', 'checkty-disambig-meaning': 'מה הכוונה ב "$1" במשפט: ', 'checkty-dismabig-fix-summary': 'תיקון קישור לפירושונים', 'checkty-search': 'חיפוש', 'checkty-fix-numberRangeDash': 'תיקון קווים מפרידים', 'checkty-missing-commons-link': 'בערך זה חסר קישור לוויקישיתוף. ניתן להוסיף קישור באמצעות בחירת המקום הנכון להוספתו בערך ולחיצה על הקישור הבא. ', 'checkty-fairuse-img-replace-template': 'תמונה להחלפה', 'checkty-set-label': 'הזנת תווית עברית ל-$1', 'checkty-translate-saved': 'התרגום נשמר!', // 'checkty-empty-parameters': 'פרמטרים ריקים - דף זה מכיל תבניות עם פרמטרים ריקים. אם הם אינם רלוונטיים ניתן להסירם. ', // 'checkty-empty-parameters-summary': 'ניקוי קוד', 'checkty-main-articles-merge-summary': 'מיזוג הפניות לערכים מורחבים', 'checkty-missing-ref-section-add': 'נמצאו הערות שוליים בערך אך לא פרק מתאים. מומלץ להוסיף את הפרק לערך, וניתן להוסיף אותו אוטומטית במקום המשוער לכך.', 'checkty-missing-ref-section-added': 'פרק הערות שוליים הוסף.', 'checkty-missing-ref-section-autofix': 'הוספה אוטומטית', 'checkty-please-position-caret' : 'אנא מקמו את הסמן במקום בו יש להוסיף ', 'checkty-ref-dir-fix' : 'תיקון כיווניות להערת שוליים', 'checkty-ref-merge' : 'מיזוג הערות שוליים', 'checkty-naked-link-found' : 'נמצא קישור חיצוני ערום: $1', 'checkty-naked-link-fix' : 'תיקון', 'checkty-broken-link': 'חשש לקישור שבור: ', 'checkty-naked-link-fix-fail': 'שגיאה בתיקון קישור עירום', 'checkty-naked-link-fixed': 'הקישור $1 הוחלף בתבנית. יש לבדוק את תקינות התוצאה והאם יש לעטוף בתבנית הערה<br> $2', 'checkty-external-links-many': 'קישורים חיצוניים - פרק הקישורים החיצוניים מכיל $1 קישורים. מומלץ להעביר קישורים העוסקים בנושאים ספציפיים להערות שוליים, ולהסיר קישורים שלא מוסיפים. (ראו עוד ב<a href="' + mw.util.getUrl('ויקישיבה:קישורים חיצוניים') + '">ויקישיבה:קישורים חיצוניים</a>)', 'checkty-external-links-diversity': 'קישורים חיצוניים - פרק הקישורים החיצוניים מכיל מספר קישורים למקורות זהים: $1. כאשר הקישור עוסק באספקט צר של הערך מומלץ להפכו להערת שוליים. (ראו עוד ב<a href="' + mw.util.getUrl('ויקישיבה:קישורים חיצוניים') + '">ויקישיבה:קישורים חיצוניים</a>)', 'checkty-nonsense-edit': 'עריכה זו כוללת שינויים אוטומטיים בלבד. האם ברצונך לשמור למרות זאת?', 'checkty-category-sort': 'ניתן למיין את הקטגוריות בדף על פי סדר אלפביתי ', 'checkty-defaultsort-suggest': 'בערך לא מוגדר מיון רגיל. בערכים העוסקים באישים נהוג למיין לפי שם משפחה. ', 'checkty-language-check': 'בערך זה מופיע הביטוי "$1". ', 'checkty-wrong-use-discussion': 'דף זה עשוי להיות דף דיונים. אין לערוך תגובות של עורכים אחרים והשימוש בבדיקה של דף כזה אינו מומלץ. האם ברצונך להפעיל את הכלי למרות זאת?', 'checkty-vav-warning': 'כאשר ו עיצורית מופיעה באמצע מילה נהוג להכפילה. ייתכן שיש מופעים שדורשים תיקון.' }); function dotSpaceRegex() { try { // proper spacing around , and . using negative lookahead and lookbehind (ES2018) to acronyms (b.b.c) return new RegExp('(?<!\\.[א-ת]*)([א-ת]\\]?\\]?) ?([,\\.]) ?(?=[א-ת]?\\[?\\[?[א-ת]{3})(?![א-ת]*\\.[א-ת])','g'); } catch(e) { console.log('Checkty - browser doesnt support negative lookhead or lookbehind (ES2018)'); //fallback to different regex with slightly less protections return /([א-ת]\]?\]?) ?([,\.]) ?(?=[א-ת]?\[?\[?[א-ת]{3})/g; } } var chectTyTool = { origText: null, textbox: null, skipCheckty: false, isSection: mw.util.getParamValue('section'), skip_dict: {}, named_comment: '{{' + 'הער' + 'ה|שם=', formatReplacesConfigSafe: [ { from: /\[\[(File|Image|תמונה):/ig, to: '[[קובץ:' }, { from: /\|thumb(nail)?(?=[\|\]])/ig, to: '|ממוזער' }, { //remove unseen character from: new RegExp('\u200e|\u200f|\u202d|\u202e|\u202c|\u202a|\u202b', 'g'), // lrm/rlm/lro/rlo/popdf invisible characters/lre/rle to: "", comment: 'הסרת תווים בלתי נראים' }, { from: / +$/mg, to: '' }, { // remove accidental nowiki and simplify code from: /\[\[([^|\[\]]+)([^|\[\]]+?)\|\1\]\](?:<nowiki\/>)?\2/g, to: '[[$1$2]]', comment: 'פישוט קישור' }, { // trim beginning from redundant spaces from: /^\s+/g, to: '', comment: 'הסרת רווחים מיותרים' }, { // trim redundant spaces from category links from: /\[\[קטגוריה: */g, to: '[[קטגוריה:' }], formatReplacesConfig: [{ // piped link has identical text before and after the pipe from: /\[\[(.*?)\|\1([.,a-zא-ת]*)\]\]/g, to: '[[$1]]$2' }, { from: /\[\[(.+?)\|([במל])\1([א-ת]*)\]\]/g, to: "$2[[$1]]$3" }, { // proper spacing around , and . using negative lookahead and lookbehind (ES2018) to acronyms (b.b.c) from: dotSpaceRegex(), to: "$1$2 ", skippable: true }, { from: /([א-ת])\( ?([א-ת])/g, to: "$1 ($2" }, { from: /(\n\n)\n+/g, to: "$1" }, { from: /== ? ?\n\n==/g, to: "==\n==" }, { from: /^ ? ? \n/gm, to: "\n" }, { from: /(?!.{2}\|)[ \t\xA0]{2,}/g, to: ' ' }, { from: /\n\n\*/g, to: '\n*' }, { from: /([בלמכ])- ((?:\[\[)?[0-9])/g, to: '$1־$2' }, { // move , suffix outside links from: /,\]\]/g, to: ']],' }, { // section between two headings without content from: /\n==[^=\n]+?==\n*(\n==[^=\n]+?==\n)/g, to: '$1', comment: 'הסרת כותרות ריקות' }], regexes: [], ignoreRegexes: [], run: function () { if (this != chectTyTool) { chectTyTool.run(); return; } var t = $('#wpTextbox1'); this.textbox = t.length ? t[0] : null; if (!this.textbox || this.textbox.value.length === 0) return; // this tool is not indented to be used in discussion pages if ( mw.config.get('wgNamespaceNumber') % 2 == 1 || ( mw.config.get('wgNamespaceNumber') != 0 && /\(IST\)/.exec(this.textbox.value ) )) { if (!prompt(mw.msg('checkty-wrong-use-discussion'))) return; } // indication that skippable replacements should be ignored in the page this.skipCheckty = this.textbox.value.match(/{{ללא[_ ]בוט\|\s*צ'קטי\s*}}/g); if (!($('#checktyResults').length)) $('.editButtons').after('<div id="checktyResults"><div class="checktyResultsTitle">'+mw.msg( 'checkty-warnings-title' )+'</div></div>'); //first call to remote functions than to local if ( ( chectTyTool.textbox.value === chectTyTool.origText ) && ( mw.config.get('wgAction') === 'edit') ) this.onSaveProtection(); this.formatChecks(); this.linkChecks(); if(!this.isSection) { this.articleOrgChecks(); this.checkImages(); } this.languageCheck(); this.vavCheck(); this.expandWikidata(); }, formatChecks: function() { if (!this.isSection) this.build_regexes(); this.formatReplace(); this.decodeExternalLinks(); // format - semi manual this.mainArticlesMerge(); this.numberRangeDash(false); }, decodeExternalLinks: function() { var t = this.textbox.value, newTxt = t, externalLinkRgx = /http[^ \]|]+/g, decodeRgx = /(?:%[0-9a-z]{2}){2,}/ig, m, mm, newLink; while ( m = externalLinkRgx.exec(t) ) { newLink = m[0]; while (mm = decodeRgx.exec(m[0])) { try { // decode URL except special chars var niceLink = decodeURI(mm[0]).replace(/[ |"\n\[\]{}<>]/g, encodeURI); if (niceLink == niceLink.normalize()) newLink = newLink.replace(mm[0], niceLink); } catch(ex) { } } newTxt = newTxt.replace(m[0], newLink); } if (newTxt === t) return; this.updateText(newTxt); chectTyTool.addSummary('קידוד קישורים'); }, articleOrgChecks: function() { this.titleOrderCheck(); this.checkRefs(); this.checkGallery(); this.defaultSortSuggest(); this.categoryOrder(); }, defaultSortSuggest: function(execute) { var txt = this.textbox.value, isPerson = false, firstFamilyRgx = /^([^ ]+) ([^ ]+)$/, name, defaultSortMsg; if (/\{\{מיון רגיל:/.exec(txt)) return; // skip if already exists if (!firstFamilyRgx.exec(mw.config.get('wgTitle'))) return; // skip if can't suggest isPerson = /== ?(ביוגרפיה|קורות חיים|חיי[וה]) ?==/.exec(txt); // heuristic to identify biographic articles if (!isPerson) return; // skip for non biographic pages if (!execute) { defaultSortMsg = $('<div>', { text: mw.msg('checkty-defaultsort-suggest') }).append($('<a>', { href: '#', text: 'הוספת מיון רגיל', }).click(function(){ chectTyTool.defaultSortSuggest(true); return false;})); this.writeMsg(defaultSortMsg, 'info'); } else { name = firstFamilyRgx.exec(mw.config.get('wgTitle')); txt = txt.replace('[[קטגוריה', '{{מיון רגיל:' + name[2] + ', ' + name[1] + '}}\n[[קטגוריה'); this.addSummary('מיון רגיל'); this.updateText(txt); } }, categoryOrder: function(execute) { var txt = this.textbox.value, newTxt = txt, self = this; categoryRE = /(\[\[קטגוריה:.+\]\])(?:$|\n)/g, cats = [], m = null, oldCats = null, categorySortMsg = null; while(m=categoryRE.exec(txt)) { cats.push(m[1]); newTxt = newTxt.replace(m[0], ''); } oldCats = JSON.stringify(cats); cats.sort(function(a,b){ if (/\|\*\]\]/.exec(a)) return -1; if (/\|\*\]\]/.exec(b)) return 1; if (/נפטרים ב-|נפטרות|שנפטרו/.exec(a)) return 1; if(/נפטרים ב-|נפטרות|שנפטרו/.exec(b)) return -1; if (/(ילידי|ילידות|שנולדו).*[0-9]/.exec(a)) return 1; if(/(ילידי|ילידות|שנולדו).*[0-9]/.exec(b)) return -1; if (/אישים במאה ה-|אישים שחיו במאה ה-|רבנים במאה ה-/.exec(a)) return 1; if(/אישים במאה ה-|אישים שחיו במאה ה-|רבנים במאה ה-/.exec(b)) return -1; return a>=b; }); if (oldCats == JSON.stringify(cats)) return; // same order - nothing to do if (!execute) { categorySortMsg = $('<div>', { text: mw.msg('checkty-category-sort') }).append($('<a>', { href: '#', text: 'סידור', }).click(function(){ chectTyTool.categoryOrder(true); return false;})); this.writeMsg(categorySortMsg, 'info'); } else { newTxt = newTxt.trimEnd(); newTxt += '\n' + cats.join('\n'); if (newTxt != txt) OO.ui.confirm( 'יש לאשר סדר הקטגוריות המוצע:' + cats.join(', ').replace(/\[\[קטגוריה:([^\[\]]+)\]\]/g, '$1') ).done( function ( confirmed ) { if ( !confirmed ) return; self.addSummary('סידור קטגוריות'); self.updateText(newTxt); } ); } }, linkChecks: function() { this.overlinkify(); this.nakedLinks(); this.checkExternalLinks(); this.disambigCheck(); }, expandWikidata: function() { this.wikidataLabelsMissing(); this.extractWikidataLabelsSuggestions(); if (!this.isSection) { this.addAuthorityControl(); } }, createSearchLink: function(v) { var highlightStr = this.highlightString, searchLink = $('<a href="#">'+mw.msg( 'checkty-search' )+'</a>').data({'search': v}).click(function(e){ highlightStr($(this).data('search')); e.preventDefault(); }); return searchLink; }, onSaveProtection: function() { $('#wpSave').click(function(e){ if (chectTyTool.textbox.value === chectTyTool.origText) { OO.ui.confirm( mw.msg('checkty-nonsense-edit') ).done( function ( confirmed ) { if ( confirmed ) { chectTyTool.origText = ''; $('#editform').submit(); } else { e.preventDefault(); } } ); e.preventDefault(); } }); }, updateText: function( val ) { if (this.textbox.value === chectTyTool.origText) { chectTyTool.origText = val; } this.textbox.value = val; }, nakedLinks: function() { var t = this.textbox.value, nakeRegex=/\[(http[^ ]+?)\]|\{\{הערה *\| *(?:1 *= *)?(https?:\/\/[^ }|]+)\}\}/g, nakeErrors=[], m, self= this; while (m = nakeRegex.exec(t)) { var nakedUrl = m[1] || m[2], fixNakedLink = $( '<a href="#">' + mw.msg( 'checkty-naked-link-fix' )+'</a>' ).data( { 'search': nakedUrl, 'inRef': m[2] }).click( function ( e ) { var searchUrl = $(this).data( 'search' ), inRef = $(this).data('inRef'), citoidTemplatesPromise = new mw.Api().loadMessagesIfMissing( [ 'citoid-template-type-map.json' ] ); // Common case: pasting a URI into this field. Citoid expects // minimally encoded input, so do some speculative decoding here to // avoid 404 fetches. T146539 searchUrl = decodeURIComponent(searchUrl); citoidPromise = $.ajax('/api/rest_v1/data/citation/mediawiki/'+encodeURIComponent(searchUrl), { headers: { 'accept-language': mw.config.get( 'wgContentLanguage' ) }, timeout: 20 * 1000, // 20 seconds type: 'GET' }); citoidPromise.done(function(d){ citoidTemplatesPromise.done(function(){ var CiteTemplates = JSON.parse( mw.message( 'citoid-template-type-map.json' ).plain() ); if ( d.length === 0 || !CiteTemplates[ d[0].itemType ] ) { self.writeMsg( mw.msg( 'checkty-naked-link-fix-fail' ) ); return; } var api = new mw.Api().get( { action: 'templatedata', titles: 'Template:' + CiteTemplates[ d[0].itemType ] }).done(function(tdq){ for(var pid in tdq.pages){ var td = tdq.pages[pid], params = []; for(var k in d[0]) { if (td.maps['citoid'][k]) { if ($.isArray(td.maps['citoid'][k]) && $.isArray(d[0][k])) { for(var i = 0;i < d[0][k].length; i++) { if ($.isArray(d[0][k][i]) && $.isArray(td.maps['citoid'][k][i])) { for(var j = 0;j < d[0][k][i].length; j++) { params.push(td.maps['citoid'][k][i][j] + '=' + d[0][k][i][j].replace(/\|/g, '{{!}}')); } } } } else { if ($.isArray(d[0][k])) { for(var i = 0;i < d[0][k].length; i++) if ($.isArray(d[0][k][i])) d[0][k][i] = d[0][k][i].join(' '); d[0][k] = d[0][k].join(', '); } params.push(td.maps['citoid'][k] + '=' + d[0][k].replace(/\|/g, '{{!}}')); } } } var template = '{{'+CiteTemplates[d[0].itemType]+'|' + params.join('|')+'}}', wikitext = self.textbox.value; if ( inRef ) { wikitext = wikitext.replace(new RegExp('\\{\\{הערה *\\| *(?:1 *= *)?'+mw.util.escapeRegExp(searchUrl)+'\\}\\}', 'g'), '{{הערה|' + template + '}}'); } else { // this is more context sensitive - sometimes we may or may not want to wrap with ref wikitext = wikitext.replace(new RegExp('\\['+mw.util.escapeRegExp(searchUrl)+'\\]', 'g'), template); } self.textbox.value = wikitext; self.writeMsg($('<div>').append(mw.msg('checkty-naked-link-fixed', searchUrl, template)).append(self.createSearchLink(template))); chectTyTool.addSummary('הלבשת קישורים עירומים'); } }) }); }).fail(function(){ self.writeMsg($('<div>').append(mw.msg('checkty-broken-link') + searchUrl)); }); e.preventDefault(); }); this.writeMsg($('<div>').append([mw.msg('checkty-naked-link-found', nakedUrl)+'" [',fixNakedLink, ' - ', this.createSearchLink(m[0]), ']']), 'alert'); } }, checkExternalLinks: function() { var t = this.textbox.value, externalLinksRgx = /== *קישורים חיצוניים *==(?:\n\{\{.+)*((?:\n\*.+|\n\{\{.+\}\})+)/mg, tplCounterRgx = /\n\* *\{\{([^|]+)/g, externalLinks = externalLinksRgx.exec(t), tplCounter={}, sameSources=[], m; if (!externalLinks) return; externalLinks = externalLinks[1]; if (externalLinks.split('\n').length > 8) { this.writeMsg('<div>' + mw.msg('checkty-external-links-many', externalLinks.split('\n').length) + '</div>', 'alert'); } else { while(m = tplCounterRgx.exec(externalLinks)) tplCounter[m[1]] = (tplCounter[m[1]]? tplCounter[m[1]]+1 : 1); delete tplCounter['קישור כללי']; for(m in tplCounter) { if (tplCounter[m] > 1) sameSources.push(m + ' ‏(' + tplCounter[m] + ')'); } if (sameSources.length) { this.writeMsg('<div>' + mw.msg('checkty-external-links-diversity', sameSources.join(', ')) + '</div>', 'alert'); } } }, checkRefs: function() { this.refsConsistencyCheck(); this.ibidWarning(); this.mergeRefs(); this.refDirSuggest(); if (!this.isSection) { this.refSection(); } }, writeMsg: function (msg, icon) { var x; if (msg instanceof Array) { if (msg.length === 0) return; msg = '<div>' + msg.join('<br/>') + '</div>'; } x = $(msg).css('display', 'none').addClass('checktyMsg'); if ( icon ) { var iconWidget = new OO.ui.IconWidget( { icon: icon, } ); x.prepend(iconWidget.$element); } $('#checktyResults').append(x); x.show('slow'); return x; }, build_regexes: function (data, protect) { if (/\{\{\s*ללא[_ ]בוט\s*\}\}/.test(this.textbox.value)) { this.writeMsg('<div>'+mw.msg( 'checkty-no-replacements' )+'</div>', 'info'); return; } if (data) { var lines = data.split(/\n/), clear_nowiki = /\|<nowiki>(.*)<\/nowiki>/, protect_title_regex = /\[\[(.+?)\]\]/g, matches, regex, pTitle; while (lines.length) { if (!(matches = lines.shift().match(/^\|(\d+)/))) continue; var num = parseInt(matches[1], 10); if (!(matches = lines.shift().match(clear_nowiki))) continue; try { regex = new RegExp(matches[1], 'g'); } catch (e) { //ignore continue; } if (!(matches = lines.shift().match(clear_nowiki))) continue; this.regexes[num] = [regex, matches[1]]; var ignore = lines.shift(), ignoreRegex = /^\|(?:<nowiki>)?(.+?)(?:<\/nowiki>)?$/.exec(ignore); if (ignoreRegex) { this.ignoreRegexes.push('(?:' + ignoreRegex[1] + ')'); } } if (protect) { // add titles of pages explicitly marked as special while (pTitle = protect_title_regex.exec(protect)) { this.ignoreRegexes.push(mw.util.escapeRegExp(pTitle[1])); this.ignoreRegexes.push('(?:\\[\\[' + mw.util.escapeRegExp(pTitle[1]) + '\\|.+?\\]\\])'); //protect links [[A (x)|A]] or other variations } } this.process_page(); } else { var replceQuery = $.ajax({ url: mw.util.getUrl( (window.replaceListPage || 'ויקישיבה:בוט/רשימת החלפות'), { action: 'raw', ctype: 'text/x-wiki' } ), dataType: 'html' }), protectQuery = $.ajax({ url: mw.util.getUrl( 'ויקישיבה:בוט/דפים חריגים', { action: 'raw', ctype: 'text/x-wiki' } ), dataType: 'html' }); $.when(replceQuery, protectQuery).done(function(dataRepalce, dataProtect){ chectTyTool.build_regexes(dataRepalce[0], dataProtect[0]); }); } }, process_page: function () { var t = this.textbox.value, skip_ar = [], actual_replaced = [], // list of actual replcements for summary actual_replaced_details = [], // list of actual replcements with details for editor skipmatch = t.match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/g), i, match; if (skipmatch) for (i = 0; i < skipmatch.length; i++) { var matches = skipmatch[i].match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/), detailedSkip = ''; chectTyTool.skip_dict[parseInt(matches[1], 10)] = true; if (this.regexes[matches[1]]) { detailedSkip = ': ' + $.trim(this.regexes[matches[1]][0].toString()); skip_ar.push($('<li></li>').append(matches[1] + detailedSkip + ' ').append(this.createSearchLink(this.regexes[matches[1]][0]))); } } var specials = [], ignoreRegex = new RegExp('(' + this.ignoreRegexes.join('|') + ')'); for (i in this.regexes) // assume regexs on templates are safe if contain template wikicode if (/\\{\\{/.test(this.regexes[i][0]) && this.regexes[i][0].test(t)) { t = t.replace(this.regexes[i][0], this.regexes[i][1]); actual_replaced.push($.trim(this.regexes[i][1].replace(/\$\d*/g, ''))); actual_replaced_details.push(i + ': ' + $.trim(this.regexes[i][1].replace(/\$\d*/g, ''))); } while (true) { //extract inner links, inner templates and inner params - we don't want to sptit those. match = t.match(/(\{\{[^\{\}]*\}\}|(\n|\[\[)(?:File|קובץ|תמונה|Image):.*?[\|\n]|[^\[\0]\[[^\{\}\[]*\])/); if ((!match || !match.length) && this.ignoreRegexes.length ) match = t.match(ignoreRegex); if (!match || !match.length) break; specials.push(match[0]); t = t.replace(match[0], "\0" + specials.length + "\0"); } for (i in this.regexes) if (!chectTyTool.skip_dict[i] && !isNaN(i)) if (this.regexes[i][0].test(t)) { var before = t, tries=0, befText, afterText; /* repeat replacement for 3 times as sometimes need to converge for more than a single run Such as boundary [^a-z][a-z][^a-z] replacing [a|a]. Use of negative char match is useful for different regex engines with different support of look ahead and lookbehind */ do { before = t; t = t.replace(this.regexes[i][0], this.regexes[i][1]); if ((tries==0) && (t != before)) { // 1st match as representative example for summary befText = this.regexes[i][0].exec(before)[0]; afterText = befText.replace(this.regexes[i][0], this.regexes[i][1]); actual_replaced.push(afterText); actual_replaced_details.push(i + ': ' + befText + ' ← ' + afterText); } tries++; } while ((tries < 3) && (t != before)) } while (true) { match = t.match(/\0(\d+)\0/); if (!match || !match.length) break; t = t.replace(match[0], specials[parseInt(match[1], 10) - 1]); } this.updateText(t); var msg = ['החלפות - בוצעו ' + actual_replaced.length + ' החלפות' + (actual_replaced_details.length==0? '.' : ': ' + actual_replaced_details.join(', '))]; if (skip_ar.length) { msg.push('<br />החלפות שלא התבצעו בגלל תבנית "ללא בוט": '); msg.push($('<ul></ul>').append(skip_ar)); } if(actual_replaced.length) { msg.push(' אנא בצעו "הצגת שינויים" לפני שמירה, כדי לוודא שהסקריפט לא גרם נזק.'); } this.writeMsg($('<div></div>').append(msg), 'info'); if (actual_replaced.length) chectTyTool.addSummary(mw.msg('checkty-replace-summary', actual_replaced.join(', '))); }, fetchDisambigLinks: function (next) { var dfd = new jQuery.Deferred(); var api = new mw.Api(); var params = { action: 'query', generator: 'links', titles: mw.config.get('wgPageName'), prop: 'pageprops', ppprop: 'disambiguation', gpllimit: '500', redirects: 1 }; if (next !== undefined) { params.gplcontinue = next; } api.get(params).done(function (data) { //extract disambig pages if (!data.hasOwnProperty('query')) { dfd.reject(); return; } var redirects = {}; if (data.query.redirects) { $.each(data.query.redirects, function(i,r) { redirects[r.to] = r.from; }); } var disambigs = []; for (var pid in data.query.pages) { var p = data.query.pages[pid], isDisambigPage = p.pageprops && p.title != mw.config.get('wgTitle') + mw.msg( 'checkty-disambig-suffix' ); //list only real disambig links if (isDisambigPage && (chectTyTool.getLinkRegex(p.title).exec(chectTyTool.textbox.value) || (redirects[p.title] && chectTyTool.getLinkRegex(redirects[p.title]).exec(chectTyTool.textbox.value)))) { disambigs.push(redirects[p.title] || p.title); } } if (data['query-continue'] !== undefined) { var nextReq = chectTyTool.fetchDisambigLinks(data['query-continue'].links.gplcontinue); nextReq.done(function (more) { dfd.resolve($.merge(disambigs, more)); }); } else { dfd.resolve(disambigs); } }); return dfd.promise(); }, mainArticlesMerge: function() { var origTxt = this.textbox.value, txt = this.textbox.value, m; while (m = /(\{\{(?:הפניה לערך מורחב|ערך מורחב)\|[^=\n]+?\}\}\n){2,}/g.exec(txt)) { var articles = [], expArticleRE = /\{\{(?:הפניה לערך מורחב|ערך מורחב)\|([^=\n]+?)\}\}/g; while (ma = expArticleRE.exec(m[0])) articles.push(ma[1]); txt = txt.replace(m[0], '{{הפניה לערך מורחב|ערכים=[['+articles.join(']], [[')+']]}}\n'); } this.textbox.value = txt; if (origTxt != txt) chectTyTool.addSummary( mw.msg( 'checkty-main-articles-merge-summary' ) ); }, formatReplace: function () { var txt = this.textbox.value, newTxt; // Format autofix var specials=[], match; // var emptyParametersRgx = /\n *\| *[^|=]+?= *(?=\n(?: *\||\}\}))/mg, emptyParametersMsg, emptyParametersBtn; $(this.formatReplacesConfigSafe).each(function (i, o) { newTxt = txt.replace(o.from, o.to); if ( newTxt!=txt && o.comment) { chectTyTool.addSummary( o.comment ); } txt = newTxt; }); //extract inner links, inner templates and inner params - we don't want to sptit those. while (true) { //extract inner links, inner templates and inner params - we don't want to sptit those. match = txt.match(/(\{\{[^\{\}]*\}\}|(\n|\[\[)(?:File|קובץ|תמונה|Image):.*?[\|\n]|[^\[\0]\[[^\{\}\[]*\])/); if (!match || !match.length) break; specials.push(match[0]); txt = txt.replace(match[0], "\0" + specials.length + "\0"); } $(this.formatReplacesConfig).each(function (i, o) { newTxt = txt.replace(o.from, o.to); if (o.skippable && (chectTyTool.skipCheckty || this.isSection)) return; // skip this one respecting {ללא בוט} if ( newTxt!=txt && o.comment) { chectTyTool.addSummary( o.comment ); } txt = newTxt; }); while (true) { match = txt.match(/\0(\d+)\0/); if (!match || !match.length) break; txt = txt.replace(match[0], specials[parseInt(match[1], 10) - 1]); } this.updateText(txt); // if (!emptyParametersRgx.test(txt)) return; // emptyParametersMsg = $('<div>', { text: mw.msg('checkty-empty-parameters') }).addClass('checkty-empty-params-warning').append($('<a>', { // href: '#', // text: 'ניקוי' // }).click(function(){ $('#wpTextbox1').val($('#wpTextbox1').val().replace(emptyParametersRgx, '')); chectTyTool.addSummary( mw.msg( 'checkty-empty-parameters-summary' ) ); return false; })); // this.writeMsg(emptyParametersMsg, 'info'); }, vavCheck: function () { var text = this.textbox.value, vavRegex = /[כלבמשה]\[\[ו[^ו][א-ת ]+\]\]/g, self = this; if (!vavRegex.exec( text )) return; var vavWarning = $('<div>').text( mw.msg( 'checkty-vav-warning' ) ).append(this.createSearchLink(vavRegex)); // TODO: add semi-automatic fix option? chectTyTool.writeMsg(vavWarning); }, disambigCheck: function () { var disambigMsg = this.writeMsg($('<div>', { id: 'waitForDisambigs' }).text( mw.msg( 'checkty-waiting-disambig-query' ) ), 'info'), self = this; this.fetchDisambigLinks().fail(function () { disambigMsg.remove(); self.writeMsg($('<div>', { text: mw.msg('checkty-disambig-no-links') }), 'check'); }).done(function (res) { var disambigs; disambigMsg.remove(); if (res.length === 0) { self.writeMsg($('<div>', { text: mw.msg('checkty-disambig-success') }), 'check'); return; } disambigs = $('<div id="disambigWarnning">'+mw.msg( 'checkty-disambig-links-title' )+'</div>').prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Disambig_RTL.svg/15px-Disambig_RTL.svg.png">').css('padding', '0 5px'); $.each(res, function (i, disTitle) { if (i > 0) { disambigs.append(', '); } disambigs.append($('<a href="' + mw.util.getUrl(disTitle) + '">' + disTitle + '</a>').click(function () { var disambigName = $(this).text(); new mw.Api().get({ action: 'parse', page: disambigName, prop: 'text', redirects: 1 }).done(function (data) { if (data && data.parse && data.parse.text) { var disambig = data.parse.text['*']; mw.loader.using( ['jquery.ui'] ).done ( function() { chectTyTool.resolveDisambig( disambigName, disambig ); } ); } else { console.error(data); } }); return false; })); }); chectTyTool.writeMsg(disambigs); }); }, getLinkRegex: function(name) { return new RegExp('(?:\\.|^)([^\\.\n]*(\\[\\[' + mw.util.escapeRegExp(name) + '[\\|\\]]).*?)[\\.\\n]', 'm'); }, resolveDisambig: function (name, data) { var offset = 0, textbox = this.textbox, linkRgx = this.getLinkRegex(name), orgPos = $(textbox).textSelection('getCaretPosition'), cSentence = $('<div>'); var options = $('<div>').append($('li', data).filter(function() { return $(this).closest( '.checkty-ignore' ).length === 0; } ).map(function () { var a = $(this).children('a').get(0); if (a) { var storeTitle = $(this).text(); var anchor = '', h = a.href; if (h.indexOf('#') + 1) anchor = decodeURI(h.substr(h.indexOf('#')).replace(/\./g, '%').replace(/_/g, ' ')); $(a).text( a.title.replace(mw.msg( 'checkty-page-doesnt-exist' ), "") + anchor); a.title = storeTitle; } return a || null; }).click(resolve)) .append($('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').click(removeLink)); var disambigDialog = $('<div>').append( mw.msg('checkty-disambig-meaning', name) + '<hr/>').append(cSentence).append(options.buttonset()).dialog({ title: mw.msg( 'checkty-dismabig-dialog-title' ), close: function () { $(textbox).textSelection('setSelection', { start: orgPos }); } }); findSentence(); function findSentence() { var text = textbox.value.substr(offset), m = text.match(linkRgx); if (!m) { disambigDialog.dialog('close'); return; } offset += text.indexOf(m[1]) + m[1].indexOf(m[2]); var linkIndex = m[1].indexOf(m[2]) + 2, html = m[1].substr(0, linkIndex) + '<big>' + name + '</big>' + m[1].substr(linkIndex + name.length); cSentence.html(html); } function resolve() { var answer = $(this).text(), text = textbox.value, startLink = text.indexOf('[[' + name, offset); if (text.charAt(startLink + 2 + name.length) != '|') answer += '|' + name; offset += answer.length + 2; text = text.substr(0, startLink + 2) + answer + text.substr(startLink + 2 + name.length); textbox.value = text; findSentence(); chectTyTool.addSummary( mw.msg( 'checkty-dismabig-fix-summary' ) ); return false; } function removeLink() { var text = textbox.value, startLink = text.indexOf('[[' + name, offset), endLink = text.indexOf(']]', startLink), pipeChar = startLink + 2 + name.length, linkText = (text.charAt(pipeChar) != '|') ? name : text.substr(pipeChar + 1, endLink - pipeChar - 1); offset += (endLink - startLink) + linkText.length; text = text.substr(0, startLink) + linkText + text.substr(endLink + 2); textbox.value = text; findSentence(); return false; } }, addSummary: function (msg) { var editSummary = $('#wpSummary').val(); if (editSummary.indexOf(msg) === -1) $('#wpSummary').val(editSummary + (editSummary.length === 0 ? '' : ', ') + msg); // tag the edit if ( $('#checktyTag').length === 0 ) { $('#editform').append('<input type="hidden" name="wpChangeTags" id="checktyTag" value="צ\'קטי">'); } }, checkGallery: function() { var text = this.textbox.value, longGalleryThreshold = 16, galleryRgx = /<gallery.+?\n(\n|.)+?<\/gallery>/mg, m, longGallery=false; while ( m=galleryRgx.exec( text ) ) { var galLength = m[0].split('\n').length-2; longGallery |= (galLength > longGalleryThreshold); } if (longGallery) { this.writeMsg('<div>' + mw.msg('checkty-long-gallery') + '</div>', 'alert'); } }, checkImages: function (data) { var fairUsageTemplates = ['תבנית:שימוש הוגן', 'תבנית:תמונת חבר כנסת']; if (!data) { if (!(/\{\{(ויקישיתוף בשורה|מיזמים)/.test(this.textbox.value))) { $.getJSON('//www.wikidata.org/w/api.php?callback=?', { languages: 'he', action: 'wbgetentities', sites: mw.config.get('wgDBname'), titles: mw.config.get('wgTitle'), format: 'json', props: 'claims' }).done(function (data) { if (data.success === undefined || !data.success) return; for (var entityId in data.entities) { var claims = data.entities[entityId].claims; if (claims && claims.hasOwnProperty('P373')) { chectTyTool.writeMsg($('<div>', { text: mw.msg('checkty-missing-commons-link') }).prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Commons-logo.svg/15px-Commons-logo.svg.png">').css('padding', '0 5px').append($('<a>', { href: '#', text: 'להוספה' }).click(function (e) { e.preventDefault(); var ta = $('#wpTextbox1'); if (ta.textSelection('getCaretPosition') >= ta.val().length) { chectTyTool.writeMsg($('<div>').text(mw.msg('checkty-please-position-caret'))); } else { ta.textSelection('encapsulateSelection',{pre:'{{ויקישיתוף בשורה}}'}); chectTyTool.addSummary('ויקישיתוף בשורה'); } }))); } } }); } //in case there are no images in page if (!(/\[\[(תמונה|קובץ|File|Image):/i.test(this.textbox.value))) { var articleName = mw.config.get('wgPageName'); var that = this; // we want to use "this" in the done method of the api call. new mw.Api().post( { action: 'parse', title: articleName, text: this.textbox.value } ).done( function( data ) { if ( data && data.parse && data.parse.text && $( data.parse.text['*'] ).find( 'a.image img' ).filter(function() { var width = $(this).attr('width'); if (isNaN(width)) width = $(this).width(); else width = parseInt(width); return (width >= 100) && $(this).parents('.navbox').length==0; } ).length === 0 ) { var fistURL = that.fistURL({ datatype: 'articles', data: articleName }); var msg = $('<div>', { text: 'בדף זה אין תמונות. ניתן לחפש תמונות חופשיות ממקורות שונים. ' }).append($('<a>', { href: decodeURI(fistURL), text: 'חיפוש תמונות', target: '_blank' })); that.writeMsg(msg, 'info'); } }); return; } new mw.Api().get({ action: 'query', generator: 'images', titles: mw.config.get('wgPageName'), prop: 'templates', tltemplates: fairUsageTemplates.join('|') }).done(function (data) { if (data && data.query && data.query.pages) chectTyTool.checkImages(data.query.pages); }); } else { var fairUseImgs = $.map(data, function (o) { if (!o.templates) return; var isFairUsage; $.each(o.templates, function (k, license) { if ($.inArray(license.title, fairUsageTemplates) != -1) { isFairUsage = true; return false; } }); if (isFairUsage) return o.title; }); if (fairUseImgs.length === 0) return; //add message with fair usage images var fistURL = this.fistURL({ data: fairUseImgs.join('\r\n'), datatype: 'replaceimages' }); var msg = $('<div>', { text: 'הדף מכיל תמונות בשימוש הוגן, שמומלץ להחליפן בחלופות חופשיות במידת האפשר. ' }).append($('<a>', { href: fistURL, text: 'חיפוש חלופות חופשיות', target: '_blank' })); if (!(new RegExp('\{\{'+mw.msg('checkty-fairuse-img-replace-template')+'\}\}').test($('#wpTextbox1').val()))) msg.append(' - ').append($('<a>', { text: 'סימון להחלפה', href: '#' }).click(function () { var t = $('#wpTextbox1'); $.each(fairUseImgs, function (i, fiImg) { var imgName = mw.util.escapeRegExp(/.:(.+)$/.exec(fiImg)[1]), imgDescRE = new RegExp(imgName.replace(' ', '[ _]') + '((?:[^\\[\\]]|\\[\\[[^\\[\\]]*?\\]\\])*?)\]\]', 'i'), matches; if (!(matches = imgDescRE.exec(t.val()))) { return; } //this is thumb img var imgDesc = matches[1].split('|'), isThumb = false, imgCaption = ''; for (var i in imgDesc) { if (/thumb|ממוזער/i.test(imgDesc[i])) { isThumb = true; } else if (!(/^(ימין|שמאל|מרכז|right|left|center|[0-9]+px)?$/i.test(imgDesc[i]))) { imgCaption = imgDesc[i]; //unknown parameter assumed to be description } } if (isThumb) { if (imgCaption) { imgDesc = matches[1].replace(imgCaption, imgCaption + '{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); } else { imgDesc = matches[1] + '|{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'; } t.val(t.val().replace(matches[1], imgDesc)); chectTyTool.addSummary('{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); } else { //is in infobox heuristic: = before image tag var isInInfobox = new RegExp('=\s*\\[\\[(?:file|image|קובץ|תמונה):' + imgName.replace(' ', '[ _]') + '[^\\]]*?\]\]', 'i'); if (isInInfobox.test(t.val())) { t.val(t.val().replace(matches[0], matches[0] + '{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}')); chectTyTool.addSummary('{{'+mw.msg('checkty-fairuse-img-replace-template')+'}}'); } } }); })); this.writeMsg(msg, 'alert'); } }, fistURL: function (p) { return 'https://www.inn.co.il/search' + $.param(p); }, languageCheck: function (checks) { //style and language check var txt = this.textbox.value; if (checks) { var checkWarnings = $('<div></div>'); for (var x in checks) { if (checks[x]['test'].test(txt)) { var m = checks[x]['test'].exec(txt), langCheckPrefix = ''; if (m[0].split(' ').length < 4 && checks[x]['test'].source.indexOf('\\]')==-1) langCheckPrefix = mw.msg('checkty-language-check', m[0]); checkWarnings.append(this.createSearchLink(checks[x]['test'])); checkWarnings.append(' - ' + langCheckPrefix + checks[x]['remark'] + '<br/>'); } } // Lint checker: wikilink-in-extlink. see [[mw:Help:Lint_errors/wikilink-in-extlink]] var wikilinkInExtLink = /[^\[]\[[^\[\]]+\[\[.+?\]\]/; if (wikilinkInExtLink.test(txt)) { checkWarnings.append(this.createSearchLink(wikilinkInExtLink)); checkWarnings.append(' - ' + mw.msg( 'checkty-wikilink-in-extlink' )); } // Design checks: Elements width var largeElement = /[6789][0-9][0-9]px/; if (largeElement.test(txt)) checkWarnings.append(mw.msg( 'checkty-large-element' ) + '<br/>'); // Design checks: Long lists can have hint to use columns var manyLi = new RegExp('(?:\n\\*.*){20}'); if (manyLi.test(txt)) { checkWarnings.append(this.createSearchLink(manyLi)); checkWarnings.append(' - ' + mw.msg( 'checkty-long-list' )); } if (checkWarnings.html().length) this.writeMsg(checkWarnings, 'alert'); } else { var api = new mw.Api(); api.get({ action:'parse', page: 'ויקישיבה:בדיקה אוטומטית', prop: 'wikitext' }).done(function (data) { if (!(data && data.parse && data.parse.wikitext)) return; var DictionaryText = data.parse.wikitext['*'].split('-----')[1] var genrealWarningWords = DictionaryText.split('\n*'); var checks = []; for (var i=0;i<genrealWarningWords.length;i++) { var splittedWarn = genrealWarningWords[i].split("//"); if ( splittedWarn.length !== 2 ) continue; checks.push({ 'test': new RegExp( splittedWarn[0], 'i' ), 'remark': splittedWarn[1] }); } chectTyTool.languageCheck(checks); }); var countRgx = /(?:[^ ]+ ){2}.?(?:ש[נת]י|שלוש[תה]?|ארבע[הת]?|חמשת?|חמישה|ששת?|שבע[הת]?|תשע[הת]?|עשר[הת]?) [א-ת]+/g, gramCheck = [], m, self = this; while (m = countRgx.exec(txt)) { gramCheck.push(m) } if (gramCheck.length) $.post('//tools.wmflabs.org/eranbot/shtei_shekel/heb_check.py', { wikitext: gramCheck.join('\n') } ).done(function(d){ if (!d.errs || d.errs.length === 0) return; var zaharNekevaWarns = $('<div></div>'); for ( var i = 0; i < d.errs.length; i++ ) { var origRgx = new RegExp(d.errs[i].orig); zaharNekevaWarns.append(self.createSearchLink(origRgx)); zaharNekevaWarns.append(' - ייתכן שיש אי התאמה במין ב"' + d.errs[i].orig + '" (תיקון: '+d.errs[i].suggested+') <br/>'); } chectTyTool.writeMsg(zaharNekevaWarns, 'alert'); }); } }, refsConsistencyCheck: function () { function safeRegexFix(fixRegex, fixReplace, check){ var txt = $('#wpTextbox1').val(), m; //remove templates within templates var specials = []; while (m = /\{\{(?!הערה\|)[^\{]*?\}\}/g.exec(txt)) { txt = txt.replace(m[0], '\0' + specials.length + '\0') specials.push(m[0]) } if (check) { var counterA = 0, counterB = 0; while (fixRegex.exec(txt)) counterA++; while (fixReplace.exec(txt)) counterB++; return [counterA, counterB]; } else { txt = txt.replace(/(\{\{הערה\|.*?\}\}|<ref>.*?<\/ref>)\s+(?=(\{\{הערה\||<ref>))/g, '$1'); // remove spaces between refs txt = txt.replace(fixRegex, fixReplace); while (m = specials.pop()) txt = txt.replace('\0' + specials.length + '\0', m); $('#wpTextbox1').val(txt); } } var refAfter = /([\.\,])\s*((\{\{הערה.*?\}\}|<ref>.*?<\/ref>)+)\.?/g, refBefore = /\.?((\{\{הערה\|([^\{]|\{(?!\{הערה\|))*\}\}|<ref>([^<]|<(?!\/ref>))*<\/ref>)+)\s*([\.\,])/g, refCounter = safeRegexFix(refAfter, refBefore, true); if (refCounter[0] > 0 && refCounter[1] > 0) { this.writeMsg($('<div>', { text: 'חוסר תאימות בהערות שוליים: ' + refCounter[0] + ' הערות אחרי סימן פיסוק, ' + refCounter[1] + ' הערות לפני סימן פיסוק [תיקון: ' }).append($('<a>', { text: 'אחרי', href: '#' }).click(function () { safeRegexFix(refBefore, '$5$1', false); chectTyTool.addSummary('אחידות במיקום הערות שוליים'); })).append(' | ').append($('<a>', { text: 'לפני', href: '#' }).click(function () { safeRegexFix(refAfter, '$2$1', false); chectTyTool.addSummary('אחידות במיקום הערות שוליים'); })).append(']'), 'alert'); } }, ibidWarning: function() { var refRE = /\{\{הערה\|(?! *שם *=)(?:1=)?((?:[^\{\}]|\{\{.*?\}\})+)(?!\|=שם=)\}\}/g, wikitext = this.textbox.value, self=this, m; var checkWarnings = $('<div></div>'); while ( m = refRE.exec( wikitext ) ){ if(/^שם[ .,]|^שם$/.test(m[1]) || /\bibid(?![^a-z])/i.test(m[1])){ checkWarnings.append(this.createSearchLink(m[0])); checkWarnings.append(' - נמצאה הערת שוליים המפנה להערה הקודמת בצורה של שם. יש להחליף לציון מקור מדויק. ראו עוד: <a href="'+mw.util.getUrl('ויקישיבה:הערות שוליים')+'">ויקישיבה:הערות שוליים</a><br/>'); } } if (checkWarnings.html().length) this.writeMsg(checkWarnings, 'alert'); }, mergeRefs: function() { // merge refs with same content this.autoMergeRefs(); // merge refs with name and content that are equal this.mergeRefsWithoutName(); // merge refs with same content and assign name. requires some manual work }, autoMergeRefs: function() { // merge refs with same content and same name var references = {},// a dictionary where key is the ref name and value is count refsContent = {},// a dictionary where key is the ref name and value is the content refReEn = /<ref name="([^"]+?)">(.+?)<\/ref>/g, wikitext = this.textbox.value, mergedRefs = [], mergedRefsNames = [], m, k, i; while ( m = refReEn.exec( wikitext ) ){ references[m[1]] = (references[m[1]] || 0) + 1; refsContent[m[1]] = { content: m[2], text: m[0] }; } for (k in references) { if (references[k] > 1) { var content = new RegExp('<ref name="'+mw.util.escapeRegExp(k)+'">(.+?)</ref>', 'g').exec(wikitext), shortRef = chectTyTool.named_comment + k + '}}'; i = 0; if (content) wikitext = wikitext.replace(new RegExp(mw.util.escapeRegExp(content[0]), 'g'), function(m) { return (i++? shortRef : m); }); mergedRefs.push(k); } } // merge referecnes with same content but different name for (k in refsContent) { if ($.inArray(k,mergedRefsNames) != -1) continue; // skip name already merged for (i in refsContent) { if (k === i) continue; // skip same name if (refsContent[k].content == refsContent[i].content) { // two refs with same content but different name var oldRefName = new RegExp('<ref name="'+mw.util.escapeRegExp(i)+'" */>', 'g'); mergedRefsNames.push(i); shortRef = chectTyTool.named_comment + k + '}}'; wikitext = wikitext.replace(refsContent[i].text, shortRef); wikitext = wikitext.replace(oldRefName, shortRef); } } } if (mergedRefs.length + mergedRefsNames.length) { if (mergedRefs.length) this.writeMsg( $('<div>').text('מיזוג הערות: ' + mergedRefs.join(', ')) ); if (mergedRefsNames.length) this.writeMsg( $('<div>').text('מיזוג הערות עם שמות שונים: ' + mergedRefsNames.join(', ')) ); this.addSummary( 'מיזוג הערות אוטומטי' ); this.textbox.value = wikitext; } }, mergeRefsWithoutName: function() { // merge refs with same content var references = {},// a dictionary where key is the ref content and the value is list of uses refTemplateRE = /\{\{הערה\|(?! *שם *=)(?:1=)?((?:[^\{\}]|\{\{.*?\}\})+)(?!\|=שם=)\}\}/g, refTagRE = /<ref>(.+?)<\/ref>/g, wikitext = this.textbox.value, m; while ( m = refTemplateRE.exec( wikitext ) ){ references[m[1]] = references[m[1]] || []; references[m[1]].push(m[0]); } while ( m = refTagRE.exec( wikitext ) ){ references[m[1]] = references[m[1]] || []; references[m[1]].push(m[0]); } for (var refContet in references) { var refUses = references[refContet]; if ( refUses.length === 1 ) continue; if(/^שם[ .,]|^שם$/.test(refContet) || /\bibid(?![^a-z])/i.test(refContet)) continue; // skip ibid refs var commonRefStructure = /(.+?) .+ (?:עמ|p).? ?([0-9]+)/.exec(refContet); var defaultRefName = ''; if (commonRefStructure) { defaultRefName = commonRefStructure[1]+commonRefStructure[2]; } var refName = prompt('ישנה הערת שוליים החוזרת מספר פעמים. ניתן להפנות את כל המופעים אל הערה אחת באמצעות מתן שם קצר ובעל משמעות להערה.\nאנא הזינו שם להערה הבאה:\n'+refContet, defaultRefName); if ( !refName ) continue; wikitext = wikitext.replace( refUses[0], chectTyTool.named_comment+refName+'|'+refContet+'}}'); for (var refI = 1; refI<refUses.length; refI++) { wikitext = wikitext.replace( refUses[refI], chectTyTool.named_comment+refName+'}}'); this.addSummary( mw.msg( 'checkty-ref-merge' ) ); // addSummary will only add it once } this.textbox.value = wikitext; } }, titleOrderCheck: function(){ // validates the titles order is consistent var orderedTitles = ['ראו גם', 'לקריאה נוספת', 'קישורים חיצוניים', 'הערות שוליים'], isSorted = 1, titles = $('#wpTextbox1').val().match('==\\s*'+orderedTitles.join('|')+'\\s*==','g'), i, indexes; if (!titles) return; // no such titles indexes = $.map( titles, function(e){return orderedTitles.indexOf(e.replace(/\s*==\s*/g,''))}); for ( i=0; ( i < indexes.length-1 ) && isSorted; i++) { isSorted &= (indexes[i] < indexes[i+1]) }; if ( !isSorted ) { this.writeMsg($('<div>מומלץ לתקן את סדר הכותרות ל: ' + orderedTitles.join(', ') + '</div>'), 'alert' ); } }, refDirSuggest: function() { // suggest adding direction for refs var wikitext = this.textbox.value, defaultDir = (/{{הערות שוליים *\| *יישור *= *שמאל}}/.exec( wikitext ))? 'ltr' : 'rtl', ltrRefRegex = /\{\{הערה\|(?:שם ?=[^|]*\|)?(?:1= *)?(?! *\[?https?:\/\/[^ ]+\]? *\}\})( *\[?[a-z][^א-ת{|]+?)\}\}/ig, rtlRefRegex = /\{\{הערה\|(?!שם ?=)(?:1= *)?([א-ת][^a-z{|]+?|\[http:[^ ]+ [א-ת][^a-z{|]+?)\}\}/ig, nonDefaultRefRegex = (defaultDir == 'rtl') ? ltrRefRegex : rtlRefRegex, uncommonDir = (defaultDir == 'rtl') ? 'שמאל' : 'ימין', m; while( m = nonDefaultRefRegex.exec(wikitext)) { var fixRefDirLink = $('<a href="#">'+mw.msg( 'checkty-ref-dir-fix' )+'</a>').data({'search': m[0]}).click(function(e){ var specificRefRgx = new RegExp('\\[\\[('+mw.util.escapeRegExp($(this).data('search'))+')(\\]\\]|\\|).+\\[\\[\\1(\\]\\]|\\|.+?\\]\\])', 'g'), wikitext = $('#wpTextbox1').val(); wikitext = wikitext.replace($(this).data('search'), $(this).data('search').replace('}}', '|כיוון='+uncommonDir+'}}')); $('#wpTextbox1').val(wikitext); chectTyTool.addSummary(mw.msg('checkty-ref-dir-fix')); e.preventDefault(); }); var searchRef = this.createSearchLink(m[1]); this.writeMsg($('<div>').append(['נמצאה הערת שוליים ללא הגדרת כיווניות "'+m[1]+'" [',searchRef, ' - ', fixRefDirLink, ']']), 'alert'); } }, refSection: function(fix) { var wikitext = this.textbox.value, newText = wikitext, self = this, refRE = /<ref>|\{\{הערה\|/g, refSectionRE = /\{\{הערות[ _]שוליים|<references[ >]/g, hasRef = refRE.exec(wikitext) != null, hasRefSection = refSectionRE.exec(wikitext) != null; if (hasRefSection) return; //not missing if (!hasRef) return; // not needed if (fix) { // fix only when when explictly asked var autoRefSection = '\n{{הערות שוליים}}\n' var lastItems = [/(\n{{בקרת זהויות)/, /(\n{{קצרמר)/,/\n({{הבהרה (?:רפואית|הלכתית|משפטית))/,/(\n{{מיון רגיל:)/, /(\n\[\[קטגוריה:)/]; for (var i=0; (i < lastItems.length) && (wikitext == newText); i++ ) newText = wikitext.replace(lastItems[i], autoRefSection + '$1'); if ( wikitext === newText ) newText = wikitext + autoRefSection; this.textbox.value = newText; this.addSummary('פרק הערות שוליים'); this.writeMsg($('<div>'+mw.msg( 'checkty-missing-ref-section-added' )+'</div>'), 'info'); } else { this.writeMsg($('<div>'+mw.msg( 'checkty-missing-ref-section-add' )+'</div>').append($('<a href="#">'+mw.msg( 'checkty-missing-ref-section-autofix' )+'</a>').click(function(){ self.refSection(true); return false; })), 'info'); } }, overlinkify: function() { function removeOverlink(context, link){ var newcontext = context; do { context = newcontext; newcontext = newcontext.replace(new RegExp('\\[\\[(' + mw.util.escapeRegExp(link)+')([^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?)\\[\\[\\1\\|(.+?)\\]\\]', 'g'), '[[$1$2$3'); newcontext = newcontext.replace(new RegExp('\\[\\[(' + mw.util.escapeRegExp(link)+')([^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?)\\[\\[(\\1)\\]\\]', 'g'), '[[$1$2$3'); } while( newcontext != context ) return newcontext; } // using [^{}\n] as a quick hack to counting links within templates var wikitext = this.textbox.value, overlinkingRgx = /\[\[([^\[\]\|]+?)(?:\]\]|\|)[^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?\[\[\1(?:\]\]|\|.+?\]\])/g, m; while ( m = overlinkingRgx.exec(wikitext) ) { if (/(?:File|קובץ|תמונה|מדיה):.+/.test(m[1])) { continue; // skip files } var removeLink = $('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').data({'search': m[1]}).click(function(e){ var specificOverlinkingRgx = new RegExp('\\[\\[('+$(this).data('search')+')(\\]\\]|\\|)[^{}\n]*(?:\{\{[^{]+\}\})?(?:[^{}\n]*)(?:\n\*[^{}\n]*)?\\[\\[\\1(\\]\\]|\\|.+?\\]\\])', 'g'); var wikitext = $('#wpTextbox1').val(), m; while( m = specificOverlinkingRgx.exec( wikitext ) ) { wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1])); } $('#wpTextbox1').val(wikitext); chectTyTool.addSummary('הסרת קישורים עודפים'); e.preventDefault(); }); var searchLink = this.createSearchLink( new RegExp( '\\[\\[' + mw.util.escapeRegExp(m[1]) + '(?:\\||\\]\\])' ) ); this.writeMsg($('<div>').append(['נמצאו קישורים עודפים ל"'+m[1]+'" [',searchLink, ' - ', removeLink, ']']), 'alert'); wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1])); } }, getWikidataApi: function(callback) { return new mw.ForeignApi( 'https://www.wikidata.org/w/api.php' ); }, extractWikidataLabelsSuggestions: function() { var suggestLabelsREen = /(?:\[\[|\|)([א-ת' \-]+?)\]\](?: \('*([a-z][^א-ת]+?)'*\)| *\{\{אנג?\|(.+?)\}\})/gi, langLink = /\{\{קישור שפה\|([א-ת]+)\| *([^|]+?)\| *([א-ת '\-]+?)\}\}/gi, langTable = { 'ספרדית': 'es', 'אנגלית': 'en' }, self = this, text = this.textbox.value, suggestionsByLang={}, // lang -> { foreign title -> hebrew label } foreignTitles= [], m, lang, suggestions; suggestionsByLang['en'] = {}; // populate suggestions while(m = suggestLabelsREen.exec(text)) suggestionsByLang['en'][m[2] || m[3]] = m[1]; while(m = langLink.exec(text)) { if (!langTable.hasOwnProperty(m[1])) { console.log('wikidataLabelsSuggested: Missing lang: ' + m[1]); continue; } lang = langTable[m[1]]; // lang code if (!suggestionsByLang[lang]) suggestionsByLang[lang] = {}; suggestionsByLang[lang][m[2]] = m[3]; } // query wikidata by lang for (lang in suggestionsByLang) { self.suggestWikidataLabelTranslate(suggestionsByLang[lang], lang); } }, suggestWikidataLabelTranslate: function(suggestions, lang) { var foreignTitles = [], batchsize = 50, self = this; for(var v in suggestions) foreignTitles.push(v); if (foreignTitles.length === 0) return; var extWikiApi = new mw.ForeignApi('//'+lang+'.wikipedia.org/w/api.php'), queries = Math.ceil(foreignTitles.length/batchsize), def = $.Deferred(), dataEntities = {}, redirects = {}; for(var i=0; i<queries; i++) { // go through enwiki API, to resolve redirects instead of direct approach with wikidataApi.getEntitiesByPage extWikiApi.get({ action: 'query', titles: foreignTitles.slice(i*batchsize,(i+1)*batchsize).join('|'), redirects: 1, prop: 'pageprops', ppprop: 'wikibase_item' }).done(function(d) { for(var i in d.query.redirects) redirects[d.query.redirects[i].to]=d.query.redirects[i].from; var wikidataIds = []; for(var i in d.query.pages) if(d.query.pages[i].pageprops && d.query.pages[i].pageprops.wikibase_item) wikidataIds.push(d.query.pages[i].pageprops.wikibase_item); if (wikidataIds.length === 0) return; self.getWikidataApi().get({ action: 'wbgetentities', ids: '\x1f' + wikidataIds.join( '\x1f' ), props: '\x1f' + ['labels', 'sitelinks'].join( '\x1f' ), languages: '\x1f' + ['he', lang].join( '\x1f' ) }).done(function(d){ queries--; $.extend(dataEntities, d.entities); if (queries === 0) def.resolve(dataEntities); }); }); } def.done(function(data) { var found = false, translations = $('<div>ייתכן שהערך מכיל תוויות חסרות לישויות בוויקינתונים:</div>'), langSite = lang + 'wiki'; // example: enwiki for(var en in data) { if (/^-/.exec(en)) continue; if (data[en].labels && data[en].labels.hasOwnProperty('he')) continue; // already have hebrew label found = true; var suggestion = suggestions[data[en].sitelinks[langSite].title] || suggestions[redirects[data[en].sitelinks[langSite].title]]; translations.append($('<li>').append($('<a>', { href: 'https://www.wikidata.org/wiki/'+en, text: en + ' ('+data[en].labels[lang].value+')', target:'_blank' }).data({ 'he': suggestion, 'foreignLabel': data[en].sitelinks[langSite].title, 'q': en }).click(function(){ self.wikidataLabelTranslate($(this).data('q'), $(this).data('foreignLabel'), $(this).data('he')); return false; }))); } if (found) self.writeMsg(translations, 'articleSearch'); }); }, wikidataLabelTranslate: function(entityId, foreignLabel, suggestion) { var localLabel = prompt(mw.msg('checkty-set-label', foreignLabel), suggestion); if (localLabel) { this.getWikidataApi().postWithToken( 'csrf', { action: 'wbsetlabel', id: entityId, value: localLabel, language: mw.config.get('wgContentLanguage') } ).done(function(d) { if (d.success) mw.notify(mw.msg('checkty-translate-saved')); }) } }, wikidataLabelsMissing: function() { var self = this, batchSize = 50, needTranslateQ = $( 'a.wb-entity-link[href*="Q"]' ) .filter( function( i, e ) { return /[a-z]/i.test( e.text ) } ) .map( function() { var m=/(Q[0-9]+)/.exec(this.href); if (m) return m[0]; }) .toArray(); if (needTranslateQ.length === 0 ) return; if (needTranslateQ.length>batchSize) needTranslateQ = needTranslateQ.slice(0, batchSize); // limit to batch size this.getWikidataApi().get({ action: 'wbgetentities', ids: '\x1f' + needTranslateQ.join( '\x1f' ), props: '\x1f' + ['labels'].join( '\x1f' ), languages: mw.config.get('wgContentLanguage') }).done( function(d) { var needTranslateIndeedQ = []; if (!d.entities) return; for(var q in d.entities) { if (d.entities[q].labels && !d.entities[q].labels.hasOwnProperty(mw.config.get('wgContentLanguage'))) needTranslateIndeedQ.push(q); } if(needTranslateIndeedQ.length === 0) return; var msg = $('<div>הישויות הבאות חסרות תווית בעברית בוויקינתונים: </div>').append($( 'a.wb-entity-link[href*="Q"]' ).filter( function( i, e ) { return $.inArray(/Q[0-9]+/.exec(e.href)[0], needTranslateIndeedQ)+1 } ).clone().css('margin', '0 3px')); self.writeMsg(msg, 'alert'); $('a', msg).click(function(){ self.wikidataLabelTranslate(/Q[0-9]+/.exec($(this).prop('href'))[0], $(this).text()); return false; }); }); }, numberRangeDash: function(fix) { // replace minus => dash foreach x-y s.t x<y and swap y and x otherwise. prefix/suffix requirement in regex to make sure it is within sentence var numberRangeReNoMinus = /([א-ת]+'?[-\ ]*)((?:\[\[|\()?[0-9]+(?:\]\])?)-((?:\[\[)?[0-9]+)((?:\]\]|\))?[.:,]?(?:[ \|]|\n)(?!לפנה"ס))/g, wikitext = this.textbox.value, replacements = [], m, i; while(m = numberRangeReNoMinus.exec(wikitext)) { if (m[1]=='בואינג ') continue; // skip wrong suggestion // note - in replacement we dont include the prefix/suffix to make consistent replacement if ( parseInt(m[2].replace(/[^0-9]/g, '')) < parseInt(m[3].replace(/[^0-9]/g, '')) ) { replacements.push([m[1]+m[2] + '-' + m[3], m[1]+m[2] + '–' + m[3]]); } else if ( (parseInt(m[2].replace(/[^0-9]/g, '')) > parseInt(m[3].replace(/[^0-9]/g, ''))) && (/^[0-9]+$/.test(m[2])) && (/^[0-9]+$/.test(m[3])) ) { replacements.push([m[1]+m[2] + '-' + m[3], m[1]+m[3] + '–' + m[2]]); } } if (replacements.length === 0) return; if (fix) { for(i = 0; i < replacements.length; i++) { wikitext = wikitext.replace(new RegExp(mw.util.escapeRegExp(replacements[i][0]), 'g'), replacements[i][1]); } this.textbox.value = wikitext; this.addSummary( 'קו מפריד בטווח מספרים' ); } else { this.writeMsg($('<div><a href="#">'+mw.msg( 'checkty-fix-numberRangeDash' )+'</a></div>').click(function(e){ chectTyTool.numberRangeDash(true); e.preventDefault(); })); } }, //original version from http://code.google.com/p/proveit-js/source/browse/ProveIt_Wikipedia.js#384 //thanks to Georgia Tech Research Corporation. Atlanta, GA 30332-0415 highlightString: function (toFind) { var txtArea = $('#wpTextbox1'); // cast to string if this is regex if ( RegExp.prototype.isPrototypeOf(toFind) ) { toFind = toFind.exec(txtArea.val()); if (!toFind) return; toFind = toFind[0]; } var nextPlace = txtArea.val().indexOf(toFind, txtArea.textSelection('getCaretPosition') + 1); if (nextPlace === -1) nextPlace = txtArea.val().indexOf(toFind); //start from begining if (nextPlace === -1) return; //not found... nothing to do var origText = txtArea.val(); txtArea.val(origText.substring(0, nextPlace)); txtArea.focus(); txtArea.scrollTop(1000000); //Larger than any real textarea (hopefully) var curScrollTop = txtArea.scrollTop(); txtArea.val(origText); if (curScrollTop > 0) { var HALF_EDIT_BOX_HEIGHT = 200; txtArea.scrollTop(curScrollTop + HALF_EDIT_BOX_HEIGHT); } txtArea.focus().textSelection('setSelection', { start: nextPlace, end: nextPlace + toFind.length }); }, addAuthorityControl: function(v) { var t = this.textbox.value, acTemplate = /\{\{בקרת זהויות[\}|]/g, artsTitle = /== *(?:מ?ספרי[הו]|מ?יצירותי[הו]|מ?חיבורי[הו]|מאמרים נבחרים|פילמוגרפיה.*|(?:מבחר)? (?:כתבי)|דיסקוגרפיה|יצירות) *==/, //cheap hint for art occuption in text artsCategory = /\[\[קטגוריה:(מוזיקאיות|מוזיקאים|זמרות|זמרים|סופרים|סופרות|מתרגמים|ציירות|ציירים|במאי|רבנים|רבניות)(?!.+\*)/; //cheap hint for art occuption in category if ( acTemplate.exec( t ) ) return; // AC already exist if ( !artsTitle.exec( t ) && !artsCategory.exec( t ) ) return; // no hint will have authority control this.getWikidataApi().get({ action: 'wbgetentities', languages: 'he', sites: mw.config.get('wgDBname'), titles: mw.config.get('wgPageName'), props: '\x1f' + ['claims', 'info'].join( '\x1f' ) }).done(function(d){ var hasAC = false, authControlProps = [ 'P3372', 'P864', 'P1907', 'P6804', 'P2558', 'P4186', 'P3293', 'P1015', 'P2092', 'P1890', 'P950', 'P268', 'P428', 'P651', 'P1273', 'P271', 'P1908', 'P1707', 'P2456', 'P2349', 'P6792', 'P227', 'P902', 'P1146', 'P396', 'P1736', 'P213', 'P347', 'P1248', 'P244', 'P886', 'P1368', 'P640', 'P434', 'P982', 'P1330', 'P966', 'P1004', 'P436', 'P1407', 'P435', 'P549', 'P1225', 'P1048', 'P349', 'P2041', 'P691', 'P409', 'P3348', 'P949', 'P5034', 'P1695', 'P1003', 'P1375', 'P1006', 'P496', 'P2750', 'P1053', 'P3065', 'P650', 'P350', 'P947', 'P906', 'P781', 'P3430', 'P269', 'P4012', 'P1323', 'P7314', 'P1693', 'P3544', 'P1694', 'P1362', 'P1315', 'P6213', 'P245', 'P1157', 'P8034', 'P214', 'P7859' ], entityId, entity; for (entityId in d.entities) { entity = d.entities[entityId]; }; if (!entityId) return; // no entity if (entity.hasOwnProperty('claims')) authControlProps.forEach(function(p) { hasAC |= entity.claims.hasOwnProperty(p); }) if (hasAC) { chectTyTool.addAuthorityControlTemplate(); } chectTyTool.viafSearch(entity, entityId, hasAC); }); }, viafSearch: function(entity, entityId, hasAC){ if (entity.hasOwnProperty('claims') && entity.claims.hasOwnProperty('P214')) { return; } $.getJSON('ttps://www.viaf.org/viaf/AutoSuggest?callback=?&' + $.param({query: mw.config.get('wgTitle').replace(/ \([^()]+\)$/, '')})).done( function(d) { if (!d.result || d.result.length === 0) return; var viafOptions = $('<div>לערך זה אין בקרת זהויות ומזהה VIAF. ייתכן שיש דף מקביל ב-VIAF</div>'); for (var i = 0; i < d.result.length; i++) { var viafLink = $('<a>', { href: 'ttps://viaf.org/viaf/' + d.result[i].viafid, target: '_blank' }).text(d.result[i].term), viafAddLink = $('<a>', { href: '#' }).text('הוספה').data({ 'viaf': d.result[i].viafid }).click(function(e){ var viafId = $(this).data('viaf'); chectTyTool.createClaim(entityId, entity.lastrevid, 'value', 'P214', viafId); if (!hasAC) chectTyTool.addAuthorityControlTemplate(); e.preventDefault(); }); viafOptions.append($('<li>').append(viafLink, ' - ', viafAddLink)); } chectTyTool.writeMsg(viafOptions); } ); }, createClaim: function ( entityId, baseRevId, snakType, propertyId, value ) { if ( typeof entityId !== 'string' || typeof baseRevId !== 'number' || typeof snakType !== 'string' || typeof propertyId !== 'string' || value && typeof value !== 'string' && typeof value !== 'object' ) { throw new Error( 'Parameter not specified properly' ); } var params = { action: 'wbcreateclaim', entity: entityId, baserevid: baseRevId, snaktype: snakType, property: propertyId }; if ( value ) { params.value = JSON.stringify( value ); } return this.getWikidataApi().postWithToken( 'csrf', params ); }, addAuthorityControlTemplate: function(){ var t = this.textbox.value, viafTemplate = /\{\{בקרת זהויות[\}|]/g, newText; if ( viafTemplate.exec( t ) ) return; // viaf already exist newText = t.replace(/{{קצרמר/, '{{בקרת זהויות}}\n{{קצרמר'); // before stub if (newText == t) newText = t.replace(/{{מיון רגיל:/, '{{בקרת זהויות}}\n{{מיון רגיל:'); //before default sort if (newText == t) newText = t.replace(/\[\[קטגוריה:/, '{{בקרת זהויות}}\n[[קטגוריה:'); // before categories if (newText == t) newText = t += '\n{{בקרת זהויות}}'; this.textbox.value = newText; this.addSummary('בקרת זהויות'); } }; if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) + 1) $(document).ready(function () { chectTyTool.origText = $('#wpTextbox1').val(); var checktyBtn = new OO.ui.ButtonInputWidget({ label: 'בדיקה', title: 'צ\'קטי - כלי לבדיקת בעיות נפוצות ועוד', accessKey: (window.checkToolKey || 'e'), id:'btnCheckTool' }); checktyBtn.$button.updateTooltipAccessKeys(); $('#wpPreviewWidget').after(checktyBtn.$element.click(chectTyTool.run)); });