מדיה ויקי:סקריפטים/88.js
קפיצה לניווט
קפיצה לחיפוש
הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.
- פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload) או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
- גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
- אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh) או ללחוץ על צירוף המקשים Ctrl-F5.
mw.loader.using( ['mediawiki.api', 'mediawiki.util', 'mediawiki.user','mediawiki.storage'] , function() { const storageTimestampKey = 'watchlistwatcher-newestseen', storageSelectionKey = 'watchlistwatcher-userselection', nbsp = ' ', rlm = '‏'; var api = new mw.Api(), storage = mw.storage, items, newest, watchButton, watchwButtonAnchor, timer, counts, popup, contentDiv, selectedOptions = storage.get(storageSelectionKey), titleCheckboxes, selectAll, refreshButton, optionsLabels = { watchlisthideanons: 'anon', watchlisthidebots: 'bot', watchlisthideliu: 'registered', /* this strangely named option means "hide regitered users", IOW, show anons */ watchlisthideminor: 'minor', watchlisthidepatrolled: 'patrolled', watchlisthidecategorization: 'cats', hidelog: 'log', edits: 'edits', }, lastOption = 'hideedit', opts = { /* watchlisthideown: we do not care about this one, since "unread" doesn't have own edits anyway */ watchlisthideanons: '!anon', watchlisthidebots: '!bot', watchlisthideliu: 'anon', /* this strangely named option means "hide regitered users", IOW, show anons */ watchlisthideminor: '!minor', watchlisthidepatrolled: '!patrolled' }, langStrings = { he: { watchlist: 'רשימת המעקב (שינויים שטרם נצפו)', numpages: 'דפים ברשימת המעקב שטרם נצפו, מעודכן ל-', 'new': 'חדשות', prev: 'קודמות', hide: 'הסתרת שינויים', remove: 'הסרת הדפים המסומנים מרשימת המעקב', markread: 'סימון הדפים המסומנים כאילו נצפו', refresh: 'רענון', anon: 'אנונימים', bot: 'בוטים', registered: 'רשומים', minor: 'משניות', patrolled: 'בדוקות', cats: 'קטגוריות', log: 'פעולות יומן', edits: 'עריכות', dif: 'הבדל', hist: 'היסטוריה', added: 'נוספו', removed: 'הוסרו', bytes: 'בתים', lastedit: 'בעריכה האחרונה', multedits: 'מספר עריכות', talk: 'שיחה', contrib: 'תרומות', nounviewed: 'אין שינויים שטרם נצפו', selectall: 'בחירת הכול' }, 'en': { watchlist: 'watchlist', numpages: 'unviewed pages in your watchlist. updated to-', 'new': 'new', prev: 'previous', hide: 'hide changes', remove: 'remove selected from watchlist', markread: 'mark selected as viewed', refresh: 'refresh', anon: 'anonymous', bot: 'bots', registered: 'registered', minor: 'minor', patrolled: 'patrolled', cats: 'categories', log: 'log actions', edits: 'edits', dif: 'diff', hist: 'history', added: 'added', removed: 'removed', bytes: 'bytes', lastedit: 'last change', multedits: 'multiple changes', talk: 'talk', contrib: 'contrib', nounviewed: 'no unviewed changes', selectall: 'select all' } } ; function i18n(key) { switch (mw.config.get('wgUserLanguage')) { case 'he': return langStrings['he'][key] || key; default: return langStrings['en'][key] || key; } } function titleLine(item) { var titleLink = $('<a>').attr( { href: mw.util.getUrl(item.title), title: item.title }).text(item.title), diffLink = $('<a>').attr( { href: mw.util.getUrl('special:diff/' + item.revid), title: i18n('dif') }) .text(i18n('dif')), histLink = $('<a>').attr( { href: mw.util.getUrl(item.title, { action: 'history' }), title: i18n('hist') }).text(i18n('hist')); if (item.timestamp != item.notificationtimestamp) histLink = $('<strong>').append(histLink); return $('<span>') .append(titleLink) .append(nbsp) .append('(') .append(diffLink) .append('|') .append(histLink) .append(')'); } function userLink(item) { var user = item.user, contribl = mw.util.getUrl('Special:Contributions/' + user), pagel = mw.util.getUrl('User:' + user), talkl = mw.util.getUrl('User talk:' + user), pageLink = $('<a>', { href: pagel, title: user }).text(user), contribLink = $('<a>', { href: contribl, title: i18n('contrib') }).text(i18n('contrib')), talkLink = $('<a>', { href: talkl, title: i18n('talk') }).text(i18n('talk')); return $('<td>') .css('max-width', '36em') .append(pageLink) .append(' (') .append(talkLink) .append('|') .append(contribLink) .append(')') .append(nbsp) .append($('<span>').html(rlm + (item.parsedcomment || '') + rlm)) ; } function localize(date, short) { var options = short ? { month: 'numeric', day: 'numeric', hour:'numeric', minute:'numeric' } : { hour:'numeric', minute:'numeric', second: 'numeric', year: 'numeric', day: 'numeric', month: 'long' }; return date.toLocaleDateString(mw.config.get('wgUserLanguage'), options); } function timeOrMany(item) { var date = new Date(item.timestamp); var timestamp = item.timestamp == item.notificationtimestamp ? localize(date) : i18n('multedits') + ' (' + localize(date, true) + ')'; return $('<td>').html(timestamp); } function makeCheckbox(item) { var checkbox = new OO.ui.CheckboxInputWidget( { value: item.title } ); titleCheckboxes.push(checkbox); return $('<td>').append(checkbox.$element); } function buildLine(item) { var change = item.newlen - item.oldlen, tooltip = change > 0 ? i18n('added') + ' ' + change : i18n('removed') + ' ' + (-change); tooltip = i18n('lastedit') + ' ' + tooltip + ' ' + i18n('bytes'); return $('<tr>') .attr('title', tooltip) .css( 'vertical-align', 'top' ) .append(makeCheckbox(item)) .append( timeOrMany(item) ) .append( titleLine(item) ) .append( userLink(item) ) ; } function buildContent() { var div = $('<div>'), already = false, lastClicked = new Date(storage.get( storageTimestampKey ) || 0), optionsDiv = $('<div>'), table = $('<table>') .css( { 'border-spacing': '1em 0' } ); selectAll = new OO.ui.CheckboxInputWidget( ); table.append( $('<tr>') .append($('<td>').append(selectAll.$element)) .append($('<td>').text(i18n('selectall'))) ) .append(counts ? $('<th>', { colspan: 3 }).text(i18n('new')) : '') .append($('<tr>')) .appendTo(div); titleCheckboxes = [selectAll]; //reset every time selectAll.on( 'change', function(selected) { for (var i = 1; i < titleCheckboxes.length; i++) { titleCheckboxes[i].setSelected(selected, true); } }) ; if (! counts) return div.append($('<p>').css( { 'text-align': 'center', 'font-weight': 'bold' }).text(i18n('nounviewed'))); storage.set( storageTimestampKey, new Date(items[0].timestamp )); table; for (var i in items) { var item = items[i]; if (! already && new Date(item.timestamp) <= lastClicked) { table.append($('<th>', { colspan: 3 }).text(i18n('prev'))); already = true; } table.append(buildLine(item)); } return div; } function buildCheckboxWidget() { var checkboxes = []; function createChangeFunc(key) { return function(value) { selectedOptions[key] = value; triggerRefresh(); }; } for (var key in optionsLabels) { checkboxes.push( new OO.ui.FieldLayout( new OO.ui.CheckboxInputWidget( { value: key, selected: selectedOptions[key] } ).on( 'change', createChangeFunc(key) ), { label: i18n(optionsLabels[key]), align: 'inline' } ) ); } var layout = new OO.ui.FieldLayout( new OO.ui.Widget( { content: [ new OO.ui.HorizontalLayout( { items: checkboxes }) ] } ) ); return $('<div>') .css( { 'margin-right': '1em' } ) .append($('<p>') .css({ 'font-size': '150%', 'font-weight': 'bolder', margin: '1em' }) .text(i18n('hide') + ':') ) .append(layout.$element); } function triggerRefresh() { setTimeout(showPopup, 0); } function buttonsPanel() { var p = $('<p>'); refreshButton = new OO.ui.ButtonWidget( { label: i18n('refresh') }) .on( 'click', triggerRefresh ); refreshButton.$element.appendTo(p); var markseenButton = new OO.ui.ButtonWidget( { label: i18n('markread') }) .on( 'click', markAsSeen ), unwatchButton = new OO.ui.ButtonWidget( { label: i18n('remove') }) .on( 'click', unwatch ); return $('<p>') .css({ 'text-align': 'center' }) .append(refreshButton.$element) .append(markseenButton.$element) .append(unwatchButton.$element); } function updateStorageAndRefresh() { storage.set( storageTimestampKey, newest ); // remember the click: only items newer than latest click will trigger coloring the bubble. triggerQuery( 0 ); // initiate click, so when it returns we can color the icon accordingly } function showPopup( e ) { if (e) { e.stopImmediatePropagation(); e.preventDefault(); } storage.set( storageTimestampKey, newest ); // remember the click: only items newer than latest click will trigger coloring the bubble. mw.loader.using( 'oojs-ui' ).done(function() { queryAndUpdate().done(function() { if (! popup) { contentDiv = $('<div>') .addClass('wlw-content'); popup = new OO.ui.PopupWidget( { label: '', head: true, width: 1000, autoClose: true, $floatableContainer: watchButton, $content: $('<div>') .css( { overflow: 'auto' } ) .append(buttonsPanel()) .append(buildCheckboxWidget()) .append($('<p>')) .append(contentDiv) }); $('#mw-content-text').first('div, form').prepend(popup.$element); } popup.setLabel( i18n('numpages') + localize( new Date() ) ); contentDiv .empty() .append(buildContent()); if ( e ) popup.toggle(); } ); // queryAndUpdate,done } ); // using.done } function announce() { const new_id = 'pt-notifications-watchlist'; if ( ! watchButton ) { var url, notif = $( '#pt-notifications-notice, #pt-notifications-alert' ).eq(-1); if ( ! notif.length ) return; watchButton = notif.clone() .attr( { id: new_id } ) .insertAfter( notif ); watchwButtonAnchor = watchButton.find( 'a' ) .removeClass('oo-ui-icon-tray') .click( showPopup ) .attr( { href: '', title: i18n('watchlist') } ); watchwButtonAnchor.find('span').remove(); watchwButtonAnchor.append($('<span>').addClass('vector-icon watchlistwatcher vector-icon mw-ui-icon-tray mw-ui-icon-wikimedia-tray')); } var lastClicked = new Date(storage.get( storageTimestampKey ) || 0), newChanges = newest > lastClicked; if (refreshButton) { refreshButton.setDisabled(! newChanges); } watchwButtonAnchor .toggleClass( 'mw-echo-notifications-badge-unseen', newChanges ) .toggleClass( 'mw-echo-notifications-badge-all-read', counts === 0 ) .attr( { 'data-counter-text': counts || '', 'data-counter-num': counts } ); } function calcParams() { var wlshow; if (! selectedOptions || ! selectedOptions.hasOwnProperty(lastOption)) { selectedOptions = {}; for (var key in optionsLabels) { selectedOptions[key] = mw.user.options.get(key); } } wlshow = Object.keys( opts ) .filter( function( opt) { return selectedOptions[opt]; } ) .map( function( k ) { return opts[k]; } ) .concat( 'unread' ) .join( '|' ), wltype = ['new']; if (! selectedOptions.watchlisthidecategorization) wltype.push('categorize'); if (! selectedOptions.hidelog) wltype.push('log'); if (! selectedOptions.hideedit) wltype.push('edit'); return { list: 'watchlist', wlprop: 'ids|user|title|timestamp|notificationtimestamp|parsedcomment|sizes', wlshow: wlshow, wltype: wltype.join('|'), wllimit: window.script88limit || 50 }; } function triggerQuery( timeout ) { clearTimeout( timer ); if ( typeof(timeout) != 'number') timeout = 1000; timer = setTimeout( queryAndUpdate, timeout ); } function markAsSeen() { var titles = titleCheckboxes && titleCheckboxes .filter(cb => cb.selected && cb.value) .map(cb => cb.value) .slice(0, 50); if (titles && titles.length) { api.postWithToken('csrf', { action: 'setnotificationtimestamp', titles: titles.join('|') } ) .then( () => setTimeout(triggerRefresh, 1500) ); } } function unwatch() { var titles = titleCheckboxes && titleCheckboxes .filter(cb => cb.selected && cb.value) .map(cb => cb.value) .slice(0, 50); if (titles && titles.length) { api.postWithToken('watch', { action: 'watch', unwatch: true, titles: titles.join('|') } ) .then(triggerRefresh); } } function queryAndUpdate() { return api.get( calcParams() ) .done( function(data) { counts = {}; if ( data && data.query && data.query.watchlist ) { items = data.query.watchlist; counts = items.length; newest = counts ? new Date(items[0].timestamp) : null; announce(); triggerQuery( 60000 ); } } ); // promise? } // queryandupdate //load css page. would do it locally, but javascript does not support multiline strings, and putting all the CSS locally is just too much. mw.loader.load( '//he.wikipedia.org/w/index.php?title=מדיה_ויקי:סקריפטים/88.css&action=raw&ctype=text/css', 'text/css' ); queryAndUpdate(); $('#mw-watchlist-resetbutton').submit( triggerQuery ); $( 'body ').on( 'script-88-refresh', triggerQuery ); mw.hook('wikipage.content').add( triggerQuery ); $('.oo-ui-buttonElement-button').click( triggerQuery ); $( 'body ').on( 'script-88-pretend-clicked', updateStorageAndRefresh ); } );