મીડિયાવિકિ:Gadget-VisualFileChange.js/ui.js
દિશાશોધન પર જાઓ
શોધ પર જાઓ
નોંધ: પાનું પ્રકાશિત કર્યા પછી, તમારે તમારા બ્રાઉઝરની કૅશ બાયપાસ કરવાની આવશ્યકતા પડી શકે છે.
- ફાયરફોક્સ / સફારી: શીફ્ટ દબાવેલી રાખીને રિલોડ પર ક્લિક કરો, અથવા તો Ctrl-F5 કે Ctrl-R દબાવો (મેક પર ⌘-R)
- ગુગલ ક્રોમ: Ctrl-Shift-R દબાવો (મેક પર ⌘-Shift-R)
- ઈન્ટરનેટ એક્સપ્લોરર/એજ: Ctrl દબાવેલી રાખીને રિફ્રેશ પર ક્લિક કરો, અથવા Ctrl-F5 દબાવો
- Opera: Ctrl-F5 દબાવો
// Main script is at [[MediaWiki:VisualFileChange.js]] // These are the delayed loaded display components (UI) in order to speed up loading of the first // <nowiki> /* global jQuery:false, mediaWiki:false*/ /* eslint one-var:0, vars-on-top:0, no-underscore-dangle:0, no-multi-str:0, indent:0, valid-jsdoc:0, no-bitwise:0, camelcase:0, curly:0, space-in-parens:0, computed-property-spacing:0,array-bracket-spacing:0 */ /* jshint curly:false, multistr:true*/ (function ($, mw) { 'use strict'; // Return if Main Script is not loaded if (!window.VisualFileChange) return; // Return if this script is already loaded if (window.VisualFileChange.displayComponents) return; var vfc = window.VisualFileChange, i18n = vfc.i18n, $doc = $(document), $win = $(window), css = '.rgallery{background-color:#FFF !important;}\n\ #AjaxMdContainer{position:relative;}\n\ .md-toggle-pane{position:absolute;}\n\ .md-nav-button{display:inline-block;background-color:white;border:2px solid #f3f3f3;border-radius:10px}\ .md-nav-button:hover,.md-nav-button:focus{background-color:#EEF;}\ .md-nav-button:active{border-color:#ccc }\ .md-nav-button-container{position:absolute;top:3em;right:1.5em;max-width:18px }\n\ .md-nav-button-wrap{display:inline-block;background:none!important;border:none!important;}\n\ .numbersOnly{text-align:right;}\n\ .md-deleted-uploads{width:99%;display:none;background:#FCC url(//upload.wikimedia.org/wikipedia/commons/d/d6/User-trash.png) no-repeat scroll center;}\n\ a:link.md-loglink{color:#BB6!important;display:inline-block;}\n\ a.md-talklink{color:#888!important;border-bottom:1px dotted #555;white-space:nowrap;font-style:italic;font-weight:bold;margin-right:4px;display:inline-block;text-decoration:none!important;}\n\ .tipsycategory{border-bottom:1px dotted #888;font-style:italic;margin-right:4px;display:inline-block;cursor:help }\n\ .tipsymetadata{color:#68B;border-bottom:1px dotted #57B;white-space:nowrap;font-style:italic;font-weight:bold;margin-right:4px;display:inline-block;cursor:help }\n\ .jFileTitle{display:block;text-align:center;padding-right:0 !important;background:#EEE !important;margin-top:3px;border-radius:5px;}\n\ .jFileTime{color:gray;font-size:0.9em;line-height:1.1em;}\n\ .jFileSize{color:green;font-size:0.9em;}\n\ li.rgallerybox{width:155px;background:#f8f8f8;border:#FFF 2px solid;}\n\ li.md-selected{background-color:#BBE;}\n\ li.md-selected div.thumb{background-color:#DDE;border:#FFF 1px solid;}\n\ div.jImage{position:relative }\n\ div.jProgress{position:absolute;top:0;left:0;height:16px;width:16px }\n\ div.jImage.progress-doing div.jProgress{background:url(\'' + vfc.icons.current + '\') no-repeat center;}\n\ div.jImage.progress-done div.jProgress{background:url(\'' + vfc.icons.done + '\') no-repeat center;}\n\ div.jImage.progress-failed div.jProgress{background:url(\'' + vfc.icons.failed + '\') no-repeat center;}\n\ div.jImage.progress-nochange div.jProgress{background:url(\'' + vfc.icons.nochange + '\') no-repeat center;}\n\ div.jGU{position:absolute;right:0;bottom:0;height:0;width:0;overflow:visible;cursor:pointer }\n\ li.rgallerybox div.jImage.progress-done{border:2px solid #393;}\n\ li.rgallerybox div.jImage.progress-failed{background-color:#ebb;border:2px solid #933;}\n\ li.rgallerybox div.jImage.progress-nochange{background-color:#eeb;border:2px solid #993;}\n\ .md-re-escape-helper{position:absolute;z-index:1002;color:#FFF;cursor:pointer;padding:3px;\ border-top-left-radius:8px;border-bottom-left-radius:8px;background:rgb(97,196,25);background:\ -webkit-gradient(radial,center center,0,center center,100%,color-stop(0%,rgb(97,196,25)),color-stop(100%,rgb(180,227,145)));\ background:radial-gradient(center,ellipse cover,rgb(97,196,25),rgb(180,227,145));}\n\ .md-examine-contents{display:inline-block;width:49%;white-space:pre-wrap;font-family:monospace;border:1px solid #333;text-align:left;word-wrap:break-word;}\n\ li#mdLastSelected{border:#447 2px solid;}\n'; // Fix https://bugzilla.wikimedia.org/show_bug.cgi?id=32687 and some vector readability improvements if (mw.config.get('skin') === 'vector') { css += '.ui-buttonset .ui-button{margin-left:0!important;margin-right:-.3em!important;}\n\ .ui-buttonset .ui-button-text-only > .ui-button-text{padding-left:0.5em !important;padding-right:0.5em !important;}\n\ .ui-buttonset > label.ui-state-active{background:#EEE !important;}\n\ .md-examine-contents{font-size:1.2em;}'; } mw.util.addCSS(css); $.extend(window.VisualFileChange, { displayComponents: true, /** ** Called by nextTask after the fill-in-user-dialog and by mdQueryFileDone (follows later) ** Calls the appropriate method (uploads of cats) **/ mdCreateList: function () { var si = vfc.startInput; if (vfc.mdNumberOfExecs) { $doc.triggerHandler('vFC', ['filelist query aborted due to exection of edit-tasks', vfc]); return true; } if (vfc.mdListUploadsPending > 0) { $doc.triggerHandler('vFC', ['filelist will queried later due to a pending query', vfc]); setTimeout(function () { vfc.mdCreateList(); }, 200); // See me later ... return true; } $doc.triggerHandler('vFC', ['preparing querying the filelist', vfc]); vfc.pb.setCurrentTaskDone(); vfc.pb.setTaskState('list', 'md-doing'); if (vfc.mdFirstRun) { // First time only vfc.pb.setHelp(i18n.mdPleaseWait); } if (vfc.internalState !== 'md') return; vfc.mdListUploadsPending++; var method; if (si.modeCat) method = 'mdFindCatMembers'; if (si.modeUser) method = 'mdFindUploads'; if (si.modePage) method = 'mdFindFiles'; if (si.modeSearch) method = 'mdFindViaSearch'; vfc.secureCall(method); }, mdSaveContinueKey: function (key) { vfc.lastContinues.vals.push(key); if (vfc.lastContinues.vals.length > 2) vfc.lastContinues.vals.shift(); }, mdFindUploads: function () { var qp = vfc.queryParams; var query = { action: 'query', list: 'logevents', rawcontinue: 1, leprop: 'title|timestamp|type', leaction: 'upload/upload', leuser: qp.target, lelimit: vfc.mdSettings.loadBatchSize, ledir: qp.ledir }; if (qp.lecontinue) query.lecontinue = qp.lecontinue; if (qp.lestart) query.lestart = qp.lestart; $doc.triggerHandler('vFC', ['useruploads', vfc, query]); vfc.queryAPI(query, 'mdFindUploadsCB'); }, mdFindUploadsCB: function (result) { vfc.mdListUploadsPending--; $doc.triggerHandler('vFC', ['got useruploads', vfc, result]); vfc.pb.setCurrentTaskDone(); var allFileChanges = result.query.logevents, qp = vfc.queryParams; $.each(allFileChanges, function (id, fc) { // loop through all filechanges (upload-events) (array) // This file was initially uploaded by the user. if (fc.action === 'upload') { // Do not mess-up numbers when a file was deleted and re-uploaded if (fc.title in vfc.iUploads) return; vfc.iUploads[fc.title] = { uploader: [], iId: vfc.iiUploads, time: fc.timestamp }; vfc.iiUploads++; // User overwrote file. } else if (fc.action === 'overwrite') { vfc.oUploads[fc.title] = { iId: vfc.ioUploads, time: fc.timestamp }; vfc.ioUploads++; } }); if (!result['query-continue']) vfc.mdNoMoreFiles = true; if (!vfc.mdNoMoreFiles) { qp.lecontinue = result['query-continue'].logevents.lecontinue; qp.lestart = result['query-continue'].logevents.lestart; vfc.mdSaveContinueKey(qp.lecontinue); } if (vfc.mdFirstRun) { // Call nextTask the first time only vfc.mdFirstRun = false; vfc.nextTask(); } else { if (!vfc.mdActualResultCount) vfc.secureCall('mdQueryMore'); } }, mdFindCatMembers: function () { var qp = vfc.queryParams; var query = { action: 'query', list: 'categorymembers', rawcontinue: 1, cmtitle: qp.target, cmtype: 'file', cmprop: 'title|type|timestamp', cmlimit: vfc.mdSettings.loadBatchSize, cmdir: qp.cmdir, cmsort: qp.cmsort }; // "[cmtype is] Ignored when cmsort=timestamp is set" if (qp.cmsort === 'timestamp') query.cmnamespace = 6; // Prevent passing empty params if (qp.cmcontinue) query.cmcontinue = qp.cmcontinue; if (qp.cmstart) query.cmstart = qp.cmstart; if (qp.cmstartsortkey) query.cmstartsortkey = qp.cmstartsortkey; $doc.triggerHandler('vFC', ['catmembers', vfc, query]); vfc.queryAPI(query, 'mdFindCatMembersCB'); }, mdFindCatMembersCB: function (result) { vfc.mdListUploadsPending--; $doc.triggerHandler('vFC', ['got catmembers', vfc, result]); vfc.pb.setCurrentTaskDone(); var qp = vfc.queryParams; // loop through all category-members (object) $.each(result.query.categorymembers, function (id, fc) { vfc.iUploads[fc.title] = { uploader: [], comments: [], iId: vfc.iiUploads, time: fc.timestamp }; vfc.iiUploads++; }); if (!result['query-continue']) vfc.mdNoMoreFiles = true; if (!vfc.mdNoMoreFiles) { qp.cmcontinue = result['query-continue'].categorymembers.cmcontinue || result['query-continue'].categorymembers.cmstart; if (result['query-continue'].categorymembers.cmstart) qp.cmstart = result['query-continue'].categorymembers.cmstart; vfc.mdSaveContinueKey(qp.cmcontinue || qp.cmstart); } if (vfc.mdFirstRun) { // Call nextTask the first time only vfc.mdFirstRun = false; vfc.nextTask(); } }, mdFindFiles: function () { var qp = vfc.queryParams; var query = { action: 'query', prop: 'images', rawcontinue: 1, titles: qp.target, imlimit: vfc.mdSettings.loadBatchSize, imdir: qp.imdir }; // Prevent passing empty params if (qp.imcontinue) query.imcontinue = qp.imcontinue; $doc.triggerHandler('vFC', ['imagesonpage', vfc, query]); vfc.queryAPI(query, 'mdFindFilesCB'); }, mdFindFilesCB: function (result) { vfc.mdListUploadsPending--; $doc.triggerHandler('vFC', ['got imagesonpage', vfc, result]); vfc.pb.setCurrentTaskDone(); var qp = vfc.queryParams; // loop through all pages-members (there should be only one) $.each(result.query.pages, function (pgId, pg) { if (!pg.images) { if (pg.missing !== undefined) throw new Error('Page does not exist. Enter full page name!'); if (pg.invalid) throw new Error('Invalid page name specified.'); pg.images = {}; } $.each(pg.images, function (imNr, im) { vfc.iUploads[im.title] = { uploader: [], comments: [], iId: vfc.iiUploads }; vfc.iiUploads++; }); }); if (!result['query-continue']) vfc.mdNoMoreFiles = true; if (!vfc.mdNoMoreFiles) { qp.imcontinue = result['query-continue'].images.imcontinue; vfc.mdSaveContinueKey(qp.imcontinue); } if (vfc.mdFirstRun) { // Call nextTask the first time only vfc.mdFirstRun = false; vfc.nextTask(); } }, mdFindViaSearch: function () { var qp = vfc.queryParams; var query = { action: 'query', list: 'search', rawcontinue: 1, srsearch: qp.target, srlimit: vfc.mdSettings.loadBatchSize, srnamespace: 6 }; // Prevent passing empty params if (qp.sroffset) query.sroffset = qp.sroffset; $doc.triggerHandler('vFC', ['imagesonpage', vfc, query]); vfc.queryAPI(query, 'mdFindViaSearchCB'); }, mdFindViaSearchCB: function (result) { vfc.mdListUploadsPending--; $doc.triggerHandler('vFC', ['got imagesonpage', vfc, result]); vfc.pb.setCurrentTaskDone(); var qp = vfc.queryParams; // loop through all search results (array) $.each(result.query.search, function (id, sr) { vfc.iUploads[sr.title] = { uploader: [], comments: [], iId: vfc.iiUploads, time: sr.timestamp }; vfc.iiUploads++; }); if (!result['query-continue']) vfc.mdNoMoreFiles = true; if (!vfc.mdNoMoreFiles) { qp.sroffset = result['query-continue'].search.sroffset; vfc.mdSaveContinueKey(qp.sroffset); } if (vfc.mdFirstRun) { // Call nextTask the first time only vfc.mdFirstRun = false; vfc.nextTask(); } }, mdRegExpFromString: function (st) { var match, m = st.match(vfc.mdRegExpPattern); if (m && m[0] && m[1]) match = new RegExp(m[1], m[2]); else match = ''; return match; }, mdVarReplacements: function (replacetext, uploadTitle, uploadObject) { if (!/%.+%/.test(replacetext)) return replacetext; replacetext = replacetext.replace('%PAGENAME%', uploadTitle.replace(/^File:/, '')) .replace('%FULLPAGENAME%', uploadTitle) .replace('%FULLPAGENAMEE%', encodeURIComponent(uploadTitle)); $.each(uploadObject.metadata, function (i, el) { if ($.inArray(typeof el.value, ['string', 'number']) !== -1) replacetext = replacetext.replace('%' + el.name + '%', el.value); }); return replacetext; }, mdCreateExamineNode: function () { var $examineInput, $examineButton, $loadDivButton, $examineFirstDiv, $examineSecondDiv, $examineDiffDiv, $examineOuterDiv; var _getRenderLink = function (text, title, $node) { var rl, blockCSS = { width: '99%' }, __doUnblock = function () { $node.unblock(); return false; }, $blockNode = $('<div>', { style: 'text-align:left;', title: 'Click to hide preview', click: __doUnblock }), $blockContent = $('<div>').appendTo($blockNode); /* $closeButton = */ $.createIcon('ui-icon-close').css({ cursor: 'pointer', 'float': 'right' }).click(__doUnblock).appendTo($blockNode); var _gotParsedText = function (t) { $blockContent.html(t); // Block again in order to vertically center text $node.block({ css: blockCSS, message: $blockNode }); }; rl = $('<a>', { href: '#render', text: 'Render preview', style: 'display:inline-block; float:right; font-family:sans-serif;' }).button().click(function (e) { e.preventDefault(); $blockContent.html('').append($('<div>', { style: 'text-align:center; font-size:2em; line-height:1.5em', text: 'Server is parsing wiki-markup' })); $node.block({ css: blockCSS, message: $blockNode }); mw.libs.commons.api.parse(text, mw.config.get('wgUserLanguage'), title, _gotParsedText); }); return rl; }; $examineInput = $('<input>').attr({ type: 'text', 'class': 'numbersOnly', maxlength: 4, size: 11, placeholder: 'file number' }); $examineButton = $('<button>', { text: 'Compare!' }).click(function () { $examineFirstDiv.text(''); $examineSecondDiv.text(''); if (!$examineInput.val()) return; var examineInputVal = Number($examineInput.val()) - 1; var replaceSecurityLevel = vfc.$selPreserve.val(); $.each(vfc.iUploads, function (upload, curUpld) { if (!curUpld.content || (examineInputVal !== curUpld.iId)) return; var newText, oldText = curUpld.content, replaceContainer = mw.libs.wikiDOM.nowikiEscaper(oldText); replaceContainer.alsoPreserve(vfc.$alsoPreserve.val()); $.each(vfc.mdReplacementMatrix, function (i, rObj) { var match, replace; match = rObj.match.val(); replace = rObj.replace.val(); if (rObj.regex[0].checked && match) match = vfc.mdRegExpFromString(match); if (!match) return; if (rObj.vars[0].checked) replace = vfc.mdVarReplacements(replace, upload, curUpld); switch (replaceSecurityLevel) { case 'secure': replaceContainer.secureReplace(match, replace); break; case 'placeholder': replaceContainer.replace(match, replace); break; case 'none': replaceContainer.ordinaryReplace(match, replace); break; } }); newText = replaceContainer.doCleanUp(); // Side by side $examineFirstDiv.text(oldText); $examineSecondDiv.text(newText); $examineSecondDiv.prepend(_getRenderLink(newText, upload, $examineOuterDiv)); // Diff if (mw.libs.schnarkDiff && mw.libs.schnarkDiff.htmlDiff) $examineDiffDiv.html('<a target="_blank" href="//de.wikipedia.org/wiki/Benutzer:Schnark/js/diff/core" style="display:inline-block; float:right; font-family:sans-serif;">Powered by SchnarkDiff</a>' + mw.libs.schnarkDiff.htmlDiff(oldText, newText, true)); }); }).button({ icons: { primary: 'ui-icon-search' } }); $loadDivButton = $('<button>', { text: 'Diff' }).click(function () { var $btn = $(this); $btn.button({ disabled: true, label: 'Loading from [[:de:Benutzer:Schnark/js/diff.js/core.js]]' }); vfc.loadModule('diff', function () { $btn.button({ label: 'Diff loaded' }); $examineButton.click(); }); }).button({ icons: { primary: 'ui-icon-shuffle' } }); $examineFirstDiv = $('<div>', { 'class': 'md-examine-contents' }); $examineSecondDiv = $('<div>', { 'class': 'md-examine-contents' }); $examineDiffDiv = $('<div>', { 'class': 'md-examine-contents', style: 'width:98%' }); $examineOuterDiv = $('<div>', { style: 'text-align:center;' }).append($examineInput, ' ', $examineButton, ' ', $loadDivButton, ' ', $('<div>').append($examineDiffDiv, $examineFirstDiv, $examineSecondDiv)); $('.numbersOnly').keyup(function () { this.value = this.value.replace(/[^0-9]/g, ''); }); return vfc.$createToggler('Examine scheduled changes', $examineOuterDiv.hide()); }, mdEscapeSpecial: function (string) { var specials = ['t', 'n', 'v', '0', 'f']; $.each(specials, function (i, s) { var rx = new RegExp('\\' + s, 'g'); string = string.replace(rx, '\\' + s); }); return string; }, mdCreateReplaceNode: function () { var rowCount = 0; var $lastBigArea = 0; var $desc = $('<p>', { text: 'This is a very powerful tool, and it is strongly suggested that you test your edits before running a batch task. You are fully responsible for your edits. Replacement is done row-by-row from top. Rows with empty or invalid pattern fields are skipped. When not using RegExp-replace only the first occurrence of the pattern in each file will be replaced.' }); var $regExpEscapeHelper = $('<div>', { style: 'display:none;', 'class': 'md-re-escape-helper', html: 'R. → /R\\./g', title: 'Convert pattern to a \'match-all-occurrences-RegExp\'' }).click(function () { var $rREH = $(this), $serviceFor = $rREH.data('mdServiceFor'), $serviceForEnable = $rREH.data('mdServiceForEnable'); $serviceFor.val('/' + vfc.mdEscapeSpecial(mw.RegExp.escape($serviceFor.val())) + '/g').keyup(); if (!$serviceForEnable[0].checked) $serviceForEnable.click(); $(this).hide(); }); var $table = $('<table>').attr({ style: 'border:1px solid #BBB', width: '100%', cellspacing: 0, cellpadding: 0 }); /* var $thead = */ $('<thead>').append( $('<th>').append($('<abbr>', { title: 'Replacing is done for each selected file from top to bottom', text: 'Nr ' })), $('<th>').append($('<a>', { title: 'Flags (=options) control how to treat your input.', text: 'Flags', target: '_blank', href: mw.util.getUrl('Help:VisualFileChange.js') + '#Custom_replace:_Flags' })), $('<th>').append($('<abbr>', { title: 'Simple string or a /Regular Expression/ \nSimple strings are only replaced once per file', text: 'Pattern to match' })), $('<th>').append($('<abbr>', { title: 'Text, %variables% and/or submatches of a RegExp ($1-$9) like /abc(.+?)ghi/', text: 'Text to insert instead' }))).appendTo($table); var $selPreserve = vfc.$selPreserve = $('<select>').attr({ id: 'selPreserve', size: 1 }).append( $('<option>', { text: 'Preserve nowikis, comments. Do this as secure as possible (internal split into array). (recommended)', value: 'secure' }), $('<option>', { text: 'Preserve nowikis, comments. Allow usage of substring and $1 (internal usage of placeholders (%v%f%c%\\d+)).', value: 'placeholder' }), $('<option>', { text: 'Do not preserve nowikis and comments.', value: 'none' })); var $alsoPreserveL = vfc.$alsoPreserve = $('<label>', { 'for': 'alsoPreserve', text: 'Also preserve the following areas from being replaced.' }); var $alsoPreserve = vfc.$alsoPreserve = $('<input>').attr({ type: 'text', id: 'alsoPreserve', placeholder: 'RegExp without flags enclosed in (): /(toPreserve)/ Example: /(<gallery>(?:.|\\n)*?<\\/gallery>)/', style: 'width:99%' }); var $tbody = $('<tbody>', { id: 'mdRTableBody' }).appendTo($table); var $rn = $('<tr>', { id: 'mdReplaceTextNode' }).append($('<td>').append($desc, $regExpEscapeHelper, $table, $selPreserve, $('<br>'), $alsoPreserveL, $alsoPreserve, $('<br>'), vfc.mdCreateExamineNode())); var $newRow; var regExTester = function ($text, $cb, $replace) { var testConditions = function () { var val = $text.val(), isRE = vfc.mdRegExpPattern.test(val); $cb.parent().css('background', ''); $text.css('background', ''); if (isRE && !$cb[0].checked) $cb.parent().css('background', '#FCC'); else if (!isRE && $cb[0].checked) $text.css('background', '#FCC'); if (!val || isRE) $regExpEscapeHelper.hide(); else $regExpEscapeHelper.show(); }; var testEmptyReplace = function () { $replace.css('background', ''); if ($text.val() && !$replace.val()) $replace.css('background', '#FEB'); }; $text.on('input keyup change', testConditions); $text.on('input keyup change', testEmptyReplace); $cb.change(testConditions); $replace.on('input keyup change', testEmptyReplace); }; var resizeHandler = function ($el) { $el.focus(function () { if ($lastBigArea) $lastBigArea.css('height', ''); $el.css('height', '5em'); $lastBigArea = $el; }); return $el; }; var $newMatchArea = function () { return resizeHandler($('<textarea>').attr({ id: 'mdMatchText' + rowCount, rows: 1, cols: 50, style: 'width:95%', placeholder: 'text or pattern to be replaced' }).on('input.resize keyup.resize change.resize', function (/* e */) { var $el = $(this); if (!$el.val()) return; $el.off('input.resize keyup.resize change.resize'); $newRow(); })).focus(function () { var $el = $(this), val = $el.val(), pos = $el.offset(); $regExpEscapeHelper.show().offset({top: pos.top + 5, left: pos.left - $regExpEscapeHelper.width() - 6}); $regExpEscapeHelper.data('mdServiceFor', $el); $regExpEscapeHelper.data('mdServiceForEnable', $el.data('mdServiceForEnable')); if (!val || vfc.mdRegExpPattern.test(val)) $regExpEscapeHelper.hide(); }); }; var $newReplaceArea = function () { return resizeHandler($('<textarea>').attr({ id: 'mdReplaceText' + rowCount, rows: 1, cols: 50, style: 'width:98%', placeholder: 'text or variables to be inserted instead' })); }; var $newRegexCheckBox = function () { return $('<input>').attr({ type: 'checkbox', id: 'mdRRegEx' + rowCount }); }; var $newRegexLabel = function () { return $('<label>', { 'for': 'mdRRegEx' + rowCount, text: '/R/', title: 'Select if Regular Expression. Use full JavaScript RegExp-Syntax like /find.+/g' }); }; var $newVarCheckBox = function () { return $('<input type="checkbox" checked="checked">').attr({ id: 'mdRVar' + rowCount }); }; var $newVarLabel = function () { return $('<label>', { 'for': 'mdRVar' + rowCount, text: '%V%', title: 'Select to allow inserting variables like %FULLPAGENAME%' }); }; $newRow = function () { rowCount++; var $mA = $newMatchArea(), $rA = $newReplaceArea(), $rE = $newRegexCheckBox(), $rEL = $newRegexLabel(), $v = $newVarCheckBox(), $vL = $newVarLabel(); vfc.mdReplacementMatrix.push({ match: $mA, replace: $rA, regex: $rE, vars: $v }); $mA.data('mdServiceForEnable', $rE); $rE.click(function (e) { e.stopPropagation(); }); $rEL.click(function (e) { e.stopPropagation(); }); regExTester($mA, $rE, $rA); $('<tr>', { align: 'center' }).append( $('<td>').append(rowCount + '.'), $('<td>', { style: 'max-width:15%' }).append($rE, $rEL, $v, $vL).buttonset(), $('<td>').append($mA), $('<td>').append($rA)).appendTo($tbody); }; $newRow(); return $rn; }, mdMwRTACache: {}, mdMwReasonToAutocomplete: function (title, $el, saveKey) { var mwRTAtoken = vfc.mdMwRTAtoken = new Date(); if (!title) { // Disable autocomplete $el.autocomplete({ disabled: true }); // Discard pending query vfc.mdMwRTAtoken = 0; return; } var __gotResult = function (r) { var $r = $(r), suggestions = [], lastSummaries = mw.storage.get(vfc.summaryChageKey); // Add last used summaries if (lastSummaries) { lastSummaries = JSON.parse(lastSummaries); if (lastSummaries[saveKey]) { suggestions = $.map(lastSummaries[saveKey], function (txt /* , i */) { return { value: txt }; }); } } // There is http://jqueryui.com/demos/autocomplete/#categories // But since there are only two groups, is seems to be unnecessary // to group the result $r.find('li > ul > li').each(function (i, li) { var $li = $(li), wikitext = ''; $li.contents().each(function (x, n) { var $n = $(n), href = $n.attr('href'); if (href) { href = href.match(vfc.mdWikipageRegExp); if (href && href[1]) { wikitext += '[[' + decodeURI(href[1]).replace(/^Commons:/, 'COM:').replace(/%27/g, '\'').replace(/%22/g, '"') + '|' + $n.text() + ']]'; return; } } wikitext += $n.text(); }); suggestions.push({ label: $li.text(), value: wikitext }); }); // Add to cache vfc.mdMwRTACache[title] = suggestions; // Upadate if the token matches if (mwRTAtoken === vfc.mdMwRTAtoken) { $el.autocomplete({ disabled: false, source: suggestions }); } }; if (title in vfc.mdMwRTACache) { // If a query for this page is pending, return var rtaItem = vfc.mdMwRTACache[title]; if (rtaItem instanceof Date) { // Allows updating on callback (__gotResult) vfc.mdMwRTAtoken = rtaItem; return; } // Use the cached value $el.autocomplete({ disabled: false, source: rtaItem }); } else { vfc.mdMwRTACache[title] = mwRTAtoken; mw.libs.commons.api.parse('{{' + title + '}}', mw.config.get('wgUserLanguage'), 'File:A.png', __gotResult, true); } }, /** ** Mammoth method to set up and manage the UserInterface (UI) **/ mdGenIGallery: function () { $doc.triggerHandler('vFC', ['preparing initial uploads dialog', vfc]); vfc.mdUploadCt = 0; // Number of pending API queries vfc.mdActualResultCount = 0; // Doesn't include reverts and deleted media vfc.mdPendingBatchQueries = 0; // Number of batches currently on query; should be max. 1 vfc.mdThumbsOnDlg = 0; // How many thumbs on the dialog? vfc.mdReplacementMatrix = []; // Contains pointers to jQuery-Array-Inputfields for the custom replace vfc.mdDateOldestDataFetched = new Date(); vfc.$ctrs = {}; // Tipsy-tooltips for metadata and categories $('.tipsymetadata').off(); $('.tipsycategory').off(); /* The UI */ var _callExec = function (action) { // Save suggestions: if (i18n.reasonAutoSuggest[action]) { // FIXME: JSON.stringify/parse seems sometimes not working here var s = mw.storage.get(vfc.summaryChageKey); if (s) { try { s = JSON.parse(s); } catch (e) { mw.log.warn(e); s = {}; } } else { s = {}; } var ss = s[action] || [], nv = vfc.$ctrs.editSummary.val(); while (ss.length > vfc.mdSettings.summaryChacheLen) ss.pop(); if (nv && $.inArray(nv, ss) === -1) { if (ss.length === vfc.mdSettings.summaryChacheLen && ss.length) ss.pop(); ss.unshift(nv); } s[action] = ss; mw.storage.set(vfc.summaryChageKey, JSON.stringify(s)); } vfc.secureCall('mdExecute'); }; var confirmDlg = function (title, text, cancel, ignore, icon, action) { var dlg2Btns = {}; var dlgWidth = Math.min(600, $win.width() - 250); dlg2Btns[cancel] = function () { $(this).dialog('close'); }; dlg2Btns[ignore] = function () { $(this).dialog('close'); _callExec(action); }; $('<div>').append($('<img>', { src: icon, height: 128, width: 128, style: 'float:left;' }), $('<div>', { style: 'margin-top: 30px' }).text(text)) .dialog({ title: title, buttons: dlg2Btns, modal: true, position: [($win.width() - dlgWidth - 250) / 2 + 250, ($win.height() - 200) / 2], close: function () { $(this).dialog('destroy'); $(this).remove(); } }); }; var dlgButtons = {}; dlgButtons[i18n.submitButtonLabel] = function () { var action = vfc.$ctrs.ajaxMdType.val(), pp = i18n.mdPotentialProblems, cf = vfc.mdCommandsExec[action].confirm; if ($('input[name="mdCheckDelete"]:checked').length === 0) { confirmDlg(pp.titleNf, pp.textNf, pp.back, pp.proceed, vfc.icons.info, action); } else if (cf) { var cfl = i18n.mdConfirm; confirmDlg(cfl[cf + 'Title'], cfl[cf], cfl[cf + 'Cancel'], cfl[cf + 'Ignore'], vfc.icons.question, action); } else { _callExec(action); } }; dlgButtons[i18n.cancelButtonLabel] = function () { $(this).dialog('close'); }; var getSelect = function () { var sel = '<select size="1" id="AjaxMdType">'; $.each(vfc.mdOpt, function (id /* , opt*/) { var ugs = vfc.mdCommandsExec[id].userGroups, disabled = ''; if ($.isArray(ugs)) { var ugsIntersect = $.map(mw.config.get('wgUserGroups'), function (a) { return $.inArray(a, ugs) < 0 ? null : a; }); if (!ugsIntersect.length) disabled = 'disabled="disabled"'; } sel += '<option value="' + id + '" ' + disabled + '>' + mw.html.escape(i18n.mdOptions[id]) + '</option>'; }); sel += '</select>'; return sel; }; var dlgResizeTimeout = 0, $AjaxMdContainer = vfc.$AjaxMdContainer.text(''), si = vfc.startInput; vfc.dlg.dialog('widget').stop(true); if ($AjaxMdContainer.$banner) $AjaxMdContainer.$banner.fadeOut(); $AjaxMdContainer.fadeIn(); vfc.dlg.dialog({ modal: false, title: vfc.mdHelpNode + ' ' + i18n.action + ': ' + getSelect() + '<label for="AjaxMdIa">' + i18n.mdDisselectAll + '</label><input type="checkbox" id="AjaxMdIa" value="1"> ', width: $win.width() - 250, height: $win.height(), buttons: dlgButtons, resizeStop: function (/* evt, ui*/) { clearTimeout(dlgResizeTimeout); dlgResizeTimeout = setTimeout(function () { $('.ui-dialog > .ui-dialog-content').eq(0).scroll(); }, 500); } // Fire scroll evt on resize for (continue query if btn gets visible) }); vfc.dlg.dialog('option', 'position', 'right').dialog('option', 'modal', 'false').css('padding', '3px 6px'); var windowRezizeTimeout = 0; $win.resize(function () { clearTimeout(windowRezizeTimeout); windowRezizeTimeout = setTimeout(function () { if (vfc && vfc.dlg) vfc.dlg.dialog('option', 'width', $win.width() - (vfc.pb.isHidden() ? 0 : 250)); }, 1000); }); var subHeading, target = vfc.queryParams.target, targetHref = target, defaultHeading; if (si.modeCat) { subHeading = i18n.filesIn + ' -'; defaultHeading = 'Files in [[:' + target + ']] '; } if (si.modeUser) { subHeading = i18n.filesBy + ' -'; targetHref = vfc.mdUserTalkPrefix + targetHref; defaultHeading = 'Files uploaded by [[' + vfc.mdUserPrefix + target + '|' + target + ']] ([[' + vfc.mdUserTalkPrefix + target + '|talk]] · [[' + vfc.mdContribPrefix + target + '|contribs]])'; } if (si.modePage) { subHeading = i18n.filesOn + ' -'; defaultHeading = 'Files on [[' + (/^(?:File|Category)/.test(target) ? ':' : '') + target + ']] '; } targetHref = mw.util.getUrl(targetHref); if (si.modeSearch) { subHeading = i18n.filesWith + ' -'; targetHref = document.location.href; defaultHeading = 'Files found with [[Special:Search/' + target + ']] '; } // Build the input-ui // Save the controls into a var that's reference we need later $.extend(vfc.$ctrs, { ajaxMdType: $('#AjaxMdType'), ajaxMdIa: $('#AjaxMdIa'), taskForUser: $('<label>', { text: i18n.mdInsertDeleteReasen }).attr({ id: 'mdTaskForUser', 'for': 'mdDeleteReason' }), deleteReason: $('<textarea>').attr({ id: 'mdDeleteReason', style: 'width: 99%; height: 40px;' }), editSummaryL: $('<label>', { text: i18n.mdInsertEditSummary }).attr({ 'for': 'mdEditSummary' }), editSummary: $('<input>').attr({ type: 'text', id: 'mdEditSummary', style: 'width: 70%;', maxlength: 255, placeholder: '+Edit summary or reason' }), replacePermission: $('<input>').attr({ type: 'checkbox', id: 'mdReplacePermission' }), deleteHeading: $('<input>', { value: defaultHeading }).attr({ type: 'text', id: 'mdDeleteHeading', style: 'width: 99%;', placeholder: 'Heading for deletion request' }), talkNote: $('<input>', { value: vfc.mdSettings.userNote }).attr({ type: 'text', id: 'mdTalkNote', style: 'width: 99%;', placeholder: 'Comments for the uploader' }), ajaxDeletedUploads: $('<ul>').attr({ id: 'AjaxDeletedUploads', 'class': 'md-deleted-uploads' }), ajaxMdNotReady: $('<div>', { text: 'Please wait while enumerating uploads…' }).attr({ id: 'AjaxMdNotReady' }), ajaxMdUlContainer: $('<ul>').attr({ id: 'AjaxMdUlContainer', 'class': 'gallery rgallery' }), ajaxMdActionConfirm: $('<span>').attr({ id: 'AjaxMdActionConfirm' }) }); vfc.$ctrs.container = $('<div>', { id: 'mdControlContainer', 'class': 'ui-widget-content' }).css('padding', '6px 10px'); var $toTop = $('<div>', { 'class': 'md-nav-button-wrap ui-helper-reset' }).append($('<a>', { 'class': 'md-nav-button ui-icon ui-icon-arrowstop-1-n', href: '#goToTop', text: '↑', title: 'To top' })), $toBottom = $('<div>', { 'class': 'md-nav-button-wrap ui-helper-reset' }).append($('<a>', { 'class': 'md-nav-button ui-icon ui-icon-arrowstop-1-s', href: '#goToBottom', text: '↓', title: 'To bottom' })), __onHover = function () { $(this).addClass('ui-state-hover'); }, __onOut = function () { $(this).removeClass('ui-state-hover'); }, __onDown = function () { var $i = $(this); $(this).addClass('ui-state-active'); $doc.one('mouseup', function () { $i.removeClass('ui-state-active'); }); }; /* $navButtonContainer = */ $('<div>', { 'class': 'md-nav-button-container' }).append($toTop, ' ', $toBottom).appendTo(vfc.dlg.dialog('widget')); $toTop.on('mouseenter', __onHover).on('mouseleave', __onOut) .focus(__onHover).blur(__onOut).mousedown(__onDown).click(function (e) { e.preventDefault(); vfc.dlg.scrollTop(0); }); $toBottom.on('mouseenter', __onHover).on('mouseleave', __onOut) .focus(__onHover).blur(__onOut).mousedown(__onDown).click(function (e) { e.preventDefault(); vfc.dlg.scrollTop($AjaxMdContainer.height()); vfc.dlg.triggerHandler('scroll'); }); if (!vfc.dlg) return 0; var updateSelectedCount = function () { var $inputs = $('input[name="mdCheckDelete"]'); var $checkedInputs = $inputs.filter(':checked'); vfc.pb.setHelp3(vfc._msg('selected-count', $checkedInputs.length)); $inputs.closest('li.gallerybox').removeClass('md-selected'); $checkedInputs.closest('li.gallerybox').addClass('md-selected'); }; $('<a>', { href: '#', text: i18n.mdInvertSelection }).button().css('margin', '0').click(function (e) { e.preventDefault(); var $inputs = $('input[name="mdCheckDelete"]'), $checkedInputs = $inputs.filter(':checked').prop('checked', false); $inputs.not($checkedInputs).prop('checked', true); // $uncheckedInputs updateSelectedCount(); }).appendTo(vfc.$ctrs.ajaxMdIa.parent()); $('<a>', { href: '#', text: i18n.mdCuteSelectLabel }).button().css('margin', '0').click(function (e) { e.preventDefault(); vfc.mdCuteSelectDlg.dialog('open'); }).appendTo(vfc.$ctrs.ajaxMdIa.parent()); // Change the title of the browser tab/window document.title = 'VisualFileChange: ' + subHeading + vfc.queryParams.target + '- '; // Append controls to the dialog vfc.$ctrs.container .append( subHeading, $('<a>', { href: targetHref, target: '_blank', text: vfc.queryParams.target }), '-. ', vfc.$ctrs.taskForUser, ': ', vfc.$ctrs.deleteReason, $('<br>'), vfc.$ctrs.editSummaryL, ': ', vfc.$ctrs.editSummary, $('<span>', { id: 'mdReplacePermissionWrapper' }).append( vfc.$ctrs.replacePermission, $('<label>', { 'for': 'mdReplacePermission', text: ' ' + i18n.mdReplacePermissionText }))) .append( $('<table>', { cellspacing: 0, cellpadding: 0, style: 'position:relative;', width: '100%' }) .append($('<tr>', { id: 'mdRequestPageTitle' }).append( $('<td>').append($('<label>', { 'for': 'mdDeleteHeading', text: i18n.mdInsertDeleteHeading })), $('<td>', { width: '70%' }).append(vfc.$ctrs.deleteHeading))).append($('<tr>', { id: 'mdTalkNoteNode' }).append( $('<td>').append($('<label>', { 'for': 'mdTalkNote', text: i18n.mdInsertTalkNote })), $('<td>', { width: '70%' }).append(vfc.$ctrs.talkNote))).append(vfc.mdCreateReplaceNode())) .appendTo($AjaxMdContainer); $AjaxMdContainer .append(vfc.$createToggler(i18n.mdDelContribsButtonLabel, vfc.$ctrs.ajaxDeletedUploads)) .append(vfc.$ctrs.ajaxMdNotReady, vfc.$ctrs.ajaxMdUlContainer); // Add label to the button var $buttons = vfc.dlg.parent().find('.ui-dialog-buttonpane button'), $submitButton = $buttons.eq(0).specialButton('proceed').button({ label: $('<span>', { text: i18n.submitButtonLabel }).append(vfc.$ctrs.ajaxMdActionConfirm) }); $buttons.eq(1).specialButton('cancel'); // $cancelButton = var keyTimout = 0; var __handleCriticalKey = function (/* event*/) { var reason = vfc.$ctrs.deleteReason.val(), summary = vfc.$ctrs.editSummary.val(); if ((reason.length < vfc.minLenReq) || (summary.length < vfc.summaryMinLen)) { $submitButton.button('option', 'disabled', true); if (vfc.minLenReq) vfc.pb.setHelp2(vfc._msg('enter-reason', vfc.minLenReq), true); } else { $submitButton.button('option', 'disabled', false); if (vfc.minLenReq) { vfc.pb.setHelp2(reason, true); mw.libs.commons.api.parse(reason, mw.config.get('wgUserLanguage'), '', $.proxy(vfc.pb.setHelp2, vfc.pb)); } } }; var _handleCriticalKey = function () { clearTimeout(keyTimout); keyTimout = setTimeout(__handleCriticalKey, 85); }; vfc.$ctrs.deleteReason.on('input keyup change autocompleteselect', _handleCriticalKey); vfc.$ctrs.editSummary.on('input keyup change autocompleteselect', _handleCriticalKey); // Binding enter-key event. var __submitOnEnter = function (e) { if (e.which === 13 && (vfc.$ctrs.deleteReason.val().length >= vfc.minLenReq) && vfc.$ctrs.editSummary.val().length >= vfc.summaryMinLen) $submitButton.click(); }; vfc.$ctrs.deleteHeading.keyup(__submitOnEnter); vfc.$ctrs.talkNote.keyup(__submitOnEnter); vfc.$ctrs.editSummary.keyup(__submitOnEnter); var timoutID = 0; updateSelectedCount(); vfc.$ctrs.ajaxMdIa.change(function (/* event*/) { var tmpChecked = this.checked; $('input[name="mdCheckDelete"]').each(function (/* index*/) { this.checked = tmpChecked; }); clearTimeout(timoutID); timoutID = setTimeout(updateSelectedCount, 200); }); $(document).off('change', 'input[name="mdCheckDelete"]'); $(document).on('change', 'input[name="mdCheckDelete"]', function (/* e*/) { clearTimeout(timoutID); timoutID = setTimeout(updateSelectedCount, 200); }); $(document).off('mouseup', 'input[name="mdCheckDelete"]'); $(document).on('mouseup', 'input[name="mdCheckDelete"]', function (e) { var $ls, $this, newVal, $lsBox; $ls = $('#mdLastSelected'); $lsBox = $ls.find('input[name="mdCheckDelete"]'); $this = $(this).closest('li.gallerybox'); if ($ls.length && e.shiftKey && this !== $lsBox[0]) { newVal = !this.checked; if ($this.nextAll('#mdLastSelected').length) { $this.nextUntil('#mdLastSelected').find('input[name="mdCheckDelete"]').each(function () { this.checked = newVal; }); } else { $this.prevUntil('#mdLastSelected').find('input[name="mdCheckDelete"]').each(function () { this.checked = newVal; }); } $lsBox[0].checked = newVal; clearTimeout(timoutID); timoutID = setTimeout(updateSelectedCount, 200); } $ls.removeAttr('id'); $this.attr('id', 'mdLastSelected'); }); // // Event handler for the selection box (task to perform) // vfc.$ctrs.ajaxMdType.click(function () { if (mw.user.isAnon()) { var $anonInfo = $('<div>'); $anonInfo.html(vfc._msg_parsed('anon-info')); $anonInfo.dialog({ modal: true, resize: false, title: vfc._msg('anon-info-heading'), close: function () { $anonInfo.remove(); } }); return false; } }); vfc.$ctrs.ajaxMdType.on('change', function (/* event*/) { var action = $(this).val(), opt = vfc.mdOpt[action], uTags = vfc.mdUserTags[action]; if (!opt) return; if (action === 'del') $('#mdRequestPageTitle').show(); else $('#mdRequestPageTitle').hide(); // minLenReq vfc.minLenReq = opt.minLenReq || 0; vfc.summaryMinLen = opt.summaryMinLen || 0; vfc.$ctrs.deleteReason.keyup(); // byte limit // PlugIn has a bug: It attaches multiple handlers // So start unbinding them. vfc.$ctrs.editSummary.off('keypress'); vfc.$ctrs.editSummary.byteLimit(opt.bLimit); var summary = vfc.$ctrs.editSummary.val(); var mwString = require('mediawiki.String'); while (mwString.byteLength(summary) > opt.bLimit) summary = $.trim(summary.slice(0, summary.length - 1)); vfc.$ctrs.editSummary.val(summary); // acceptReason if (opt.reasonText) { vfc.$ctrs.taskForUser.text(i18n[opt.reasonText]); vfc.$ctrs.taskForUser.show(); vfc.$ctrs.deleteReason.show(); } else { vfc.$ctrs.taskForUser.hide(); vfc.$ctrs.deleteReason.hide(); } // prefill if (opt.prefill) { if (!vfc.$ctrs.deleteReason.val()) vfc.$ctrs.deleteReason.val(vfc[opt.prefill]); // For some reason RegExp.$1 will not work in the following line if (/(\d{16})/g.test(vfc.$ctrs.deleteReason.val())) vfc.$ctrs.deleteReason.val(vfc[opt.prefill].replace(/%ID/g, /(\d{16})/g.exec(vfc.$ctrs.deleteReason.val())[0])); if (vfc.mdURLPattern.test(vfc.$ctrs.deleteReason.val())) vfc.$ctrs.deleteReason.val(vfc.mdOTRSTicketPrefill.replace(/%URL/g, vfc.$ctrs.deleteReason.val())); } // confirmation if (opt.reasonParse) { vfc.pb.setHelp(i18n[opt.reasonParse]); } else { vfc.pb.setHelp2(''); vfc.pb.setHelp(''); } // talk note if (uTags.summary) $('#mdTalkNoteNode').show(); else $('#mdTalkNoteNode').hide(); // replace node if (opt.replaceNode) $('#mdReplaceTextNode').show(); else $('#mdReplaceTextNode').hide(); // OTRS replace node if (opt.permissionWrapper) $('#mdReplacePermissionWrapper').show(); else $('#mdReplacePermissionWrapper').hide(); vfc.$ctrs.ajaxMdActionConfirm.text(' (' + i18n.mdOptions[action] + ')'); // Summary / Reason input vfc.$ctrs.editSummaryL.text(i18n[(opt.addSummary || 'mdInsertEditSummary')]); // Reason autocomplete vfc.mdMwReasonToAutocomplete(i18n.reasonAutoSuggest[action], vfc.$ctrs.editSummary, action); }); vfc.$ctrs.ajaxMdType.val(vfc.mdSettings.defaultAction); vfc.$ctrs.ajaxMdType.change(); vfc.$ctrs.ajaxMdType.on('click mousedown mouseup', function (e) { e.stopPropagation(); }); vfc.$ctrs.ajaxMdIa.on('click mousedown mouseup', function (e) { e.stopPropagation(); }); vfc.$ctrs.deleteReason.keyup(); // cute-selection dialog var $metaSelect = $('<select>').attr({ style: 'width:10em;', size: 1 }), $uploaderSelect = $('<select>').attr({ type: 'text', id: 'txtAjaxMdSelectUploader', size: 1, placeholder: i18n.cuteSelect.uploader }); var dlgSelectButtons = {}; dlgSelectButtons[i18n.cuteSelect.button] = function () { var $curThmb = ''; var newVal = $('#chkAjaxMdSelect')[0].checked; var v_txtAjaxMdSelectCat = $.trim($('#txtAjaxMdSelectCat').val().replace(/^Category:/, '').replace(/_/g, '')); var v_txtAjaxMdSelectTitle = $.trim($('#txtAjaxMdSelectTitle').val().replace(/^File:/, '').replace(/_/g, '')); if (v_txtAjaxMdSelectTitle) v_txtAjaxMdSelectTitle = new RegExp(v_txtAjaxMdSelectTitle, ''); var v_txtAjaxMdSelectWikitext = $('#txtAjaxMdSelectWikitext').val(); if (v_txtAjaxMdSelectWikitext) v_txtAjaxMdSelectWikitext = new RegExp(v_txtAjaxMdSelectWikitext, ''); var v_txtAjaxMdSelectSize = $('#txtAjaxMdSelectSize').val(); var vl_selAjaxMdSelectSize = ((String($('#selAjaxMdSelectSize').val())) === 'l'); var vl_selAjaxMdSelectUploader = $uploaderSelect.val(); var v_txtAjaxMdSelectMeta = new RegExp($.trim($('#txtAjaxMdSelectMeta').val()), ''); var vl_selAjaxMdSelectMeta = $metaSelect.val(); var v_txtAjaxMdSelectDate0 = $('#txtAjaxMdSelectDate0').val(); var dateFromString = function (st) { try { /(\d{4})-(\d\d?)-(\d\d?)(?: (\d\d?):(\d\d?):(\d\d?))?/.exec(st); return RegExp.$4 ? (new Date(RegExp.$1, (RegExp.$2 - 1), RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6)) : (new Date(RegExp.$1, (RegExp.$2 - 1), RegExp.$3)); } catch (ex) { return 0; } }; if (v_txtAjaxMdSelectDate0) { v_txtAjaxMdSelectDate0 = dateFromString(v_txtAjaxMdSelectDate0); if (!v_txtAjaxMdSelectDate0) { alert('Invalid start-date'); return; } } var v_txtAjaxMdSelectDate1 = $('#txtAjaxMdSelectDate1').val(); if (v_txtAjaxMdSelectDate1 !== '') { v_txtAjaxMdSelectDate1 = dateFromString(v_txtAjaxMdSelectDate1); if (!v_txtAjaxMdSelectDate1) { alert('Invalid end-date'); return; } } $.each(vfc.iUploads, function (upload, curUpld) { try { var isMatch = {}; // yyyy mm dd /(\d{4})-(\d\d)-(\d\d)T(?:(\d\d):(\d\d):(\d\d))Z/.exec(curUpld.time); var curUpldDate = new Date(RegExp.$1, (RegExp.$2 - 1), RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6); $curThmb = $('input[value="' + upload.replace('"', '\\"') + '"]'); if (v_txtAjaxMdSelectCat) { for (var ct in curUpld.categories) { if (curUpld.categories.hasOwnProperty(ct) && (curUpld.categories[ct].title.replace('Category:', '') === v_txtAjaxMdSelectCat)) isMatch.cat = true; } } else { isMatch.cat = true; } if (typeof (v_txtAjaxMdSelectTitle) === 'object') { if (v_txtAjaxMdSelectTitle.test(upload.replace('File:', ''))) isMatch.title = true; } else { isMatch.title = true; } if (typeof (v_txtAjaxMdSelectWikitext) === 'object' && curUpld.content) { if (v_txtAjaxMdSelectWikitext.test(curUpld.content)) isMatch.content = true; } else { isMatch.content = true; } if (v_txtAjaxMdSelectSize !== '') { if ((vl_selAjaxMdSelectSize && (curUpld.size < v_txtAjaxMdSelectSize)) || (!vl_selAjaxMdSelectSize && (curUpld.size > v_txtAjaxMdSelectSize))) isMatch.size = true; } else { isMatch.size = true; } if (vl_selAjaxMdSelectUploader) { if ($.inArray(vl_selAjaxMdSelectUploader, curUpld.uploader) !== -1) isMatch.uploder = true; } else { isMatch.uploder = true; } if (typeof (v_txtAjaxMdSelectDate0) === 'object') { // between -> start date -> must be in the past if (curUpldDate >= v_txtAjaxMdSelectDate0) isMatch.date0 = true; } else { isMatch.date0 = true; } if (typeof (v_txtAjaxMdSelectDate1) === 'object') { // and -> end date -> must be in the future if (curUpldDate <= v_txtAjaxMdSelectDate1) isMatch.date1 = true; } else { isMatch.date1 = true; } if (vl_selAjaxMdSelectMeta) { for (var md in curUpld.metadata) { if (curUpld.metadata.hasOwnProperty(md) && curUpld.metadata[md].name === vl_selAjaxMdSelectMeta && v_txtAjaxMdSelectMeta.test(curUpld.metadata[md].value)) isMatch.meta = true; } } else { isMatch.meta = true; } if (isMatch.cat && isMatch.title && isMatch.content && isMatch.size && isMatch.meta && isMatch.uploder && isMatch.date0 && isMatch.date1 && ($curThmb.length === 1)) $curThmb[0].checked = newVal; } catch (ex) { vfc.log('Error: ' + ex); } }); updateSelectedCount(); }; var cuteI18n = i18n.cuteSelect, $AjaxMdCuteSelect = $('<div>', { id: 'AjaxMdCuteSelect' }); $AjaxMdCuteSelect .append(cuteI18n.intro) .append($('<table>').append( $('<tr>').append( $('<td>').append($('<label>', { 'for': 'chkAjaxMdSelect', text: cuteI18n.select })), $('<td>').append($('<input>', { type: 'checkbox', id: 'chkAjaxMdSelect', value: 1, checked: '' }))), $('<tr>').append( $('<td>').append($('<label>', { 'for': 'txtAjaxMdSelectCat', text: cuteI18n.inCat })), $('<td>').append($('<input>', { type: 'text', id: 'txtAjaxMdSelectCat', size: 50 }), cuteI18n.and)), $('<tr>').append( $('<td>').append($('<label>', { 'for': 'txtAjaxMdSelectTitle', html: cuteI18n.title + ' <abbr title="Regular Expression. Example: Tower.+ will select Towers or TowersInSpain but not Tower">(RegExpr)</abbr>' })), $('<td>').append($('<input>').attr({ type: 'text', id: 'txtAjaxMdSelectTitle', size: 50, placeholder: cuteI18n.titleplaceholder }), cuteI18n.and)), $('<tr>').append( $('<td>').append($('<label>', { 'for': 'txtAjaxMdSelectWikitext', html: cuteI18n.wikitext + ' <abbr title="Regular Expression. Example: \\{\\{[Tt]emplate\\}\\} would match {{template}} and {{Template}}">(RegExpr)</abbr>' })), $('<td>').append($('<input>').attr({ type: 'text', id: 'txtAjaxMdSelectWikitext', size: 50, placeholder: cuteI18n.wikitextplaceholder }), cuteI18n.and)), $('<tr>').append( $('<td>').append(cuteI18n.size + ' <select size="1" id="selAjaxMdSelectSize"><option value="l"><</option><option value="g">></option></select>'), $('<td>').append($('<input>', { type: 'text', 'class': 'numbersOnly', id: 'txtAjaxMdSelectSize', size: 15 }), $('<label>', { 'for': 'txtAjaxMdSelectSize', text: cuteI18n.kibibyte }), ' ', cuteI18n.and)), !vfc.startInput.modeUser ? $('<tr>').append( $('<td>').append($('<label>', { 'for': 'txtAjaxMdSelectUploader', text: cuteI18n.uploader })), $('<td>').append($uploaderSelect, cuteI18n.and)) : '', $('<tr>').append( $('<td>').append($metaSelect), $('<td>').append($('<label>', { 'for': 'txtAjaxMdSelectMeta', text: cuteI18n.matches }), $('<input>').attr({ type: 'text', placeholder: cuteI18n.titleplaceholder, id: 'txtAjaxMdSelectMeta', size: 41 }), cuteI18n.and)), $('<tr>').append( $('<td>').append(cuteI18n.date), $('<td>').append( $('<label>', { 'for': 'txtAjaxMdSelectDate0' }).append($('<abbr>', { text: cuteI18n.between, title: cuteI18n.after })), $('<input>') .attr({ type: 'text', id: 'txtAjaxMdSelectDate0', size: 20, 'class': 'dateOnly', maxlength: 19, placeholder: cuteI18n.dateplaceholder }) .datepicker({ changeYear: true, changeMonth: true, dateFormat: 'yy-mm-dd 00:00:00', showWeek: true, firstDay: 1 }) .tipsy({ trigger: 'focus', gravity: 's', html: true, title: function () { return i18n.optStartAtHowTo; } }), $('<label>', { 'for': 'txtAjaxMdSelectDate1' }).append($('<abbr>', { text: cuteI18n.and, title: cuteI18n.before })), $('<input>') .attr({ type: 'text', id: 'txtAjaxMdSelectDate1', size: 20, 'class': 'dateOnly', maxlength: 19, placeholder: cuteI18n.dateplaceholder }) .datepicker({ changeYear: true, changeMonth: true, dateFormat: 'yy-mm-dd 23:59:59', showWeek: true, firstDay: 1 }) .tipsy({ trigger: 'focus', gravity: 's', html: true, title: function () { return i18n.optStartAtHowTo; } }))))); var pbWidth = (vfc.pb.isHidden() ? 0 : 250); var dlgWidth = Math.min(600, $win.width() - pbWidth); vfc.mdCuteSelectDlg = $('<div>').append($AjaxMdCuteSelect).dialog({ modal: true, resizable: false, closeOnEscape: true, position: [($win.width() - dlgWidth - pbWidth) / 2 + pbWidth, 0], title: cuteI18n.heading, height: 'auto', width: dlgWidth, buttons: dlgSelectButtons, open: function (/* evt, ui*/) { $metaSelect.children().remove(); $uploaderSelect.children().remove(); $('<option>', { text: ' ', value: '' }).appendTo($metaSelect).clone().appendTo($uploaderSelect); vfc.metaKeys.sort(); vfc.allUploaders.sort(); $.each(vfc.metaKeys, function (k, val) { $('<option>', { text: val }).appendTo($metaSelect); }); var isRtl = $uploaderSelect.css('direction') === 'rtl', bidiMark = isRtl ? '\u200f' : '\u200e'; // rlm : lrm $.each(vfc.allUploaders, function (k, val) { $('<option>', { value: val, text: val + ' ' + bidiMark + '[' + vfc.uploadsByUser[val] + ']' }).appendTo($uploaderSelect); }); }, autoOpen: false }); this.$dialogsToClose.push(this.mdCuteSelectDlg); $('.numbersOnly').keyup(function () { this.value = this.value.replace(/[^0-9]/g, ''); }); $('.dateOnly').keyup(function () { this.value = this.value.replace(/[^0-9\-: ]/g, ''); }); // End of cute-selection dialog var $queryMore = $('<div>', { id: 'mdQueryMore', align: 'center' }); vfc.$mdQueryMoreBtn = $('<button>', { id: 'mdQueryMoreBtn', text: i18n.mdMore }) .button({ disabled: true, icons: { primary: 'ui-icon-arrowthick-1-s', secondary: 'ui-icon-arrowthick-1-s' } }) .click(function (/* event*/) { vfc.mdQueryMore(); }) .appendTo($queryMore); $AjaxMdContainer.append($queryMore).append('<div style="height:100px"> </div>'); /* End of UI */ $doc.triggerHandler('vFC', ['initial uploads dialog', vfc, vfc.dlg]); // Temporary fix $('.vFCConfigRequired').removeClass('ui-state-disabled'); vfc.iCurrentIId = -1; // Last queried file to continue obtaining detail-info vfc.secureCall('mdSendNextQueries'); }, /** ** Send the next batch of queries. Called by mdGenIGallery (mammoth method above) and mdQueryMore (Query on demand) **/ mdSendNextQueries: function () { $doc.triggerHandler('vFC', ['querying detail-info', this]); this.pb.setTaskState('datails', 'md-doing'); if (!this.mdPendingBatchQueries) { this.mdPendingBatchQueries++; var collectedFiles = [], collectedCount = 0, si = vfc.startInput; var sendReq = function (files) { if (!files.length) return; var query = { action: 'query', prop: 'imageinfo|info|revisions|categories', rvprop: 'timestamp', inprop: 'talkid', iiprop: 'url|size|metadata', titles: files.join('|'), clprop: 'hidden', cllimit: 500, redirects: 1 }; if (si.loadWikitext) query.rvprop += '|content'; if (!si.modeUser) { query.iiprop += '|user|sha1|comment'; query.iilimit = 500; } if (si.loadThumbs) query.iiurlwidth = query.iiurlheight = 120; $doc.triggerHandler('vFC', ['detail-info', vfc, query]); vfc.mdUploadCt++; vfc.failQueriedAgain = ''; vfc.queryAPI(query, 'mdQueriedFile', 'failQueriedFile'); // Dont use task queue because this is a loop }; $.each(this.iUploads, function (upload, upli) { if ((vfc.iCurrentIId + 1) > upli.iId) return; collectedFiles.push(upload); collectedCount++; if (collectedCount > 9) { // only 10 at once collectedCount = 0; sendReq(collectedFiles); collectedFiles = []; } vfc.iCurrentIId = upli.iId; if ((vfc.mdUploadCt * 10 + collectedCount) >= vfc.mdSettings.loadBatchSize) return false; }); sendReq(collectedFiles); } if (!this.mdUploadCt) this.nextTask(); this.$mdQueryMoreBtn.button('option', 'disabled', true); }, /* Helper function: Creating a gallery box for gallery view */ mdCreateGalleryBox: function (img, txt, height, style, imgTitle, imgH, addGU) { var $ih = $('<div>', { style: 'margin:' + (imgH ? (Math.round((height - imgH) / 2) + 'px auto;') : '15px auto;') }), $th = $('<div>', { 'class': 'gallerytext', 'tipsy-title': imgTitle }), $gbProgress = $('<div>', { 'class': 'jProgress' }), $gbGU = $('<div>', { 'class': 'jGU', title: 'GlobalUsage' }), $thumb = $('<div>', { style: 'width: 150px;', 'class': 'thumb jImage' }).css('height', height).append($ih, $gbProgress, $gbGU), $galleryBox = $('<li>', { 'class': 'gallerybox rgallerybox' }).append($('<div>', { style: 'width: 155px' }).append($thumb).append($th)); if (typeof style === 'string') $th.attr('style', style); $.each(img, function (id, imgi) { $ih.append(imgi); }); $.each(txt, function (id, txti) { $th.append(txti); }); $galleryBox.$thumb = $thumb; if (addGU) { $gbGU.badge('?'); $galleryBox.$gbGU = $gbGU; } return $galleryBox; }, fillGlobalUsage: function () { mw.loader.using('ext.gadget.GlobalUsage', function () { vfc.secureCall('_fillGlobalUsage'); }); }, _fillGlobalUsage: function () { var gu = window.mw.libs.GlobalUsage(5, 15, 3, true); gu.tipsyGravity = 'se'; gu.query(vfc.gbu).done(function () { $.each(vfc.gbu, function (i, $el) { $el.off('click'); }); vfc.gbu = {}; }); }, mdLargerGallery: function () { if ( !mw.libs.largerGallery) { mw.loader.using('ext.gadget.LargerGallery', function () { vfc.secureCall('_mdLargerGallery'); }); } else vfc.secureCall('_mdLargerGallery'); }, _mdLargerGallery: function () { if (!$('#largerGallery2')[0]) mw.libs.largerGallery.init('AjaxDeletedUploads', vfc.$ctrs.ajaxMdUlContainer); vfc.nextTask(); }, /** Helper function: Creating a link to the log of a deleted item **/ mdCreateDelUploadItem: function (img) { return $('<li>').append($('<a>', { href: mw.config.get('wgServer') + mw.config.get('wgScript') + '?title=Special:Log&page=' + img, target: '_blank' }).append(mw.html.escape(img))) .append($('<a>', { href: mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace('$1', 'Special:Undelete/' + img), target: '_blank' }).append(' (undel)')); }, /** Called by Tipsy on hovering. Get a list of cats for a specific file. @param {string} revision-identifier @param {object} reference to JSONListUploads @param {boolean} render as flickr-like tags? **/ mdGetCatTable: function (rv, o, asTags) { var cats = o.iUploads[rv].categories, $catWrap = $('<div>'), $catsList = $('<ul>'); if (asTags) $catsList.addClass('j-cat-wrap'); else $catWrap.html('<b><i>Categories:</i></b><br><br>'); $catWrap.append($catsList); $.each(cats, function (id, tCat) { if (undefined === cats[id].hidden) { $catsList.append( $('<li>', { 'class': (asTags ? ' j-cat-label' : '') }).append($('<a>', { 'class': (asTags ? ' j-cat-label' : ''), href: mw.util.getUrl(tCat.title), target: '_blank' }).text(tCat.title.replace('Category:', '')), ' ')); } }); return $catWrap; }, /* Helper function: Called by QueryIICB. Get model info from Image-Metadata */ mdGetCamModel: function (uItem) { if (!uItem || !uItem.metadata) return ''; var model = ''; var mdata = uItem.metadata; if (typeof mdata !== 'object') return ''; $.each(mdata, function (id, mdatai) { if (mdatai.name === 'Model') { model = $.trim(mdatai.value) + ' '; return false; } }); return model; }, /* Called by Tipsy on hovering. Get a list of cats for a specific file */ mdGetMetaTable: function (rv, o) { var val, mdata = o.iUploads[rv].metadata, fRestr = (mdata && mdata.length > 6), stMdat = '<b><i>File-Metadata:</i></b><br><br>', restrArr = ['ImageDescription', 'Make', 'Model', 'Copyright', 'DateTime', 'Artist', 'Title', 'Author', 'Creator', 'CreationDate']; $.each(mdata, function (id, mdatai) { if (typeof mdatai.value !== 'string') return; try { val = mw.html.escape(mdatai.value).slice(0, 200); } catch (ex) { val = mdatai.value; } stMdat += ((fRestr && ($.inArray(mdatai.name, restrArr) === -1)) ? '' : '<i>' + mdatai.name + '</i><br>' + val + '<br><br>'); }); return stMdat; }, /* Helper function: Extract categories from the list of those. */ mdExtractTags: function (mdCats) { var thiscat, mdTags = '', _this = this, skip = false; if (typeof mdCats !== 'object') return mdTags; mdTags += '<i>'; // First the licenses (hidden): $.each(mdCats, function (i, cati) { if (typeof cati.hidden === 'string') { // it is an empty string if true thiscat = cati.title; skip = false; $.each(_this.mdLicenseRecognization, function (id, recogi) { if (recogi[0].test(thiscat)) { if (recogi[1] !== '') mdTags += '<abbr title="' + thiscat.replace('Category:', '') + '">' + recogi[1] + '</abbr> '; skip = true; return false; } }); if (skip) return; // This will be only executed if there was no match mdTags += '<abbr title="' + thiscat.replace('Category:', '') + '">U</abbr> '; } }); mdTags += '</i> <span style="background-color:#FC9;">'; // Then, deletion tags: $.each(mdCats, function (i, cati) { thiscat = cati.title; skip = false; $.each(_this.mdTagRecognization, function (id, thistag) { if (thistag[0].test(thiscat)) { if (thistag[1] !== '') mdTags += (thistag[2] ? '<span style="background-color:#' + thistag[2] + ';">' : '') + '<abbr title="' + thiscat.replace('Category:', '') + '">' + thistag[1] + '</abbr>' + (thistag[2] ? '</span>' : ''); skip = true; return false; } }); if (skip) return; }); mdTags += '</span>'; return mdTags; }, /* Format a number > 1 (1000 --> 1 000) */ mdFormattNumber: function (iNr) { iNr = String(iNr); var rx = /(\d+)(\d{3})/; while (rx.test(iNr)) iNr = iNr.replace(rx, '$1<span style="font-size:40%;font-weight:100"> </span>$2'); return iNr; }, /** * API-Call-back method to eval the queried file and add a thumb */ mdQueriedFile: function (result) { vfc.mdUploadCt--; // If some failed, try again if (vfc.failQueriedAgain) { vfc.queryAPI( vfc.failQueriedAgain, 'mdQueriedFile' ); vfc.failQueriedAgain = ''; } $doc.triggerHandler('vFC', ['got detail-info', vfc, result]); if (({ err: 1, done: 1 })[vfc.internalState]) return; // don't try to add something if there was an error. result = result.query; var pages = result.pages, redirects = result.redirects, loadThumb = vfc.startInput.loadThumbs; vfc.mdBusy = true; $.each(pages, function (id, pg) { var dirtyTitle = pg.title, uItem = vfc.iUploads[dirtyTitle], i, ii, seenHash = {}; if (!uItem && redirects) { // Resolve redirect for (var r = 0; r < redirects.length; r++) { var redirect = redirects[r]; if (redirect && dirtyTitle === redirect.to) { uItem = vfc.iUploads[dirtyTitle] = vfc.iUploads[redirect.from]; delete vfc.iUploads[redirect.from]; redirects.splice(r, 1); break; } } // uItem = uItem || {}; // should never happen } vfc.$ctrs.ajaxMdNotReady.text(vfc._msg('query-progress', vfc.mdUploadCt, vfc.iiUploads, dirtyTitle)); if (!pg.revisions) { // The file has been deleted try { vfc.$ctrs.ajaxDeletedUploads.append(vfc.secureCall('mdCreateDelUploadItem', dirtyTitle)); } catch (e) {} return; } // No thumb-image, create dummy if ( loadThumb && (!pg.imageinfo || $.isEmptyObject( pg.imageinfo[0] ) || !pg.imageinfo[0].thumbwidth )) { var fileext = dirtyTitle.slice(-4).toLowerCase(); fileext = $.inArray(fileext, ['djvu', '.mov', 'mpga', '.svg', '.ogg', '.pdf', '.psd', '.xcf']) !== -1 ? '-' + fileext.replace(/^\./, '') : ''; pg.imageinfo = [{ thumburl: '/w/resources/assets/file-type-icons/fileicon' + fileext + '.png', size: 0, width: 0, height: 0, thumbwidth: 120, thumbheight: 120, descriptionurl: '/wiki/' + dirtyTitle }]; } /* Adding a thumbnail to the dialog */ vfc.mdActualResultCount++; if (!vfc.startInput.modeUser) { for (i = (pg.imageinfo || []).length - 1; i >= 0; i--) { ii = pg.imageinfo[i]; if (ii.sha1 && !seenHash[ii.sha1]) { seenHash[ii.sha1] = true; // Admins can hide usernames. Make sure not pushing undefined. if (ii.user) { uItem.uploader.push(ii.user); uItem.comments.push(ii.comment || ''); vfc.uploadsByUser[ii.user] = vfc.uploadsByUser[ii.user] || 0; vfc.uploadsByUser[ii.user]++; if ($.inArray(ii.user, vfc.allUploaders) === -1) vfc.allUploaders.push(ii.user); } } } } ii = (pg.imageinfo || [])[0] || {}; ii.metadata = ii.metadata || []; // not all images have metadata pg.categories = pg.categories || []; // not all images have categories if (pg.revisions[0]) { uItem.content = pg.revisions[0]['*'] || ''; // save the content for OTRS usage, replace and find-the-real-uploader uItem.time = uItem.time || pg.revisions[0].timestamp; uItem.basetimestamp = pg.revisions[0].timestamp; // timestamp to detect edit conflicts } else { mw.log.warn('API response for ' + id + ' has empty revisions list (w.wiki/Njw)'); // TODO properly handle this case (there should be some rvcontinue in the response, follow that) } uItem.categories = pg.categories; // save cats for cute-selection uItem.metadata = ii.metadata; // save Exif and other for cute-selection uItem.size = (ii.size >> 10); // for cute-selection uItem.pageId = id; uItem.talkId = pg.talkid; // List all Meta-Keys if (ii.metadata.length) { $.each(ii.metadata, function (i, m) { var tp = typeof m.value; if (tp === 'string' || tp === 'number' || tp === 'boolean') { if ($.inArray(m.name, vfc.metaKeys) === -1) vfc.metaKeys.push(m.name); } }); } var camModel = vfc.secureCall('mdGetCamModel', uItem), loglink = mw.config.get('wgServer') + mw.config.get('wgScript') + '?title=Special:Log&page=' + mw.util.wikiUrlencode(dirtyTitle); var $galleryBox = vfc.mdCreateGalleryBox( [$('<a>', { href: ii.descriptionurl, target: '_blank', 'class': 'image' }) .append(loadThumb ? $('<img>').attr({ // attr is needed to not convert to CSS src: ii.thumburl, width: ii.thumbwidth, height: ii.thumbheight, alt: dirtyTitle }) : '')], [('<i>' + (uItem.iId + 1) + ': </i>'), $('<input>', { type: 'checkbox', name: 'mdCheckDelete', value: dirtyTitle }), $('<span>', { 'class': 'tipsycategory' }).text('c').tipsy({ title: function () { $('.tipsy').remove(); return vfc.secureCall('mdGetCatTable', $(this).parent().attr('tipsy-title'), vfc, false).html(); }, html: true, delayOut: 1000, gravity: $.fn.tipsy.autoNS }), ' ', (($.isArray(ii.metadata) && !!ii.metadata.length) ? $('<span>', { 'class': 'tipsymetadata' }).text(camModel || 'm').tipsy({ title: function () { $('.tipsy').remove(); return vfc.secureCall('mdGetMetaTable', $(this).parent().attr('tipsy-title'), vfc); }, html: true, gravity: $.fn.tipsy.autoNS }) : ''), (uItem.talkId ? $('<a>', { 'class': 'md-talklink', href: mw.util.getUrl(dirtyTitle.replace(/^File/, 'File_talk')), target: '_blank', title: i18n.hasTalk }).text('t') : ''), $('<a>', { href: loglink, target: '_blank', text: 'log', 'class': 'md-loglink' }), '<br>', (vfc.secureCall('mdExtractTags', pg.categories)), ' ', $('<a>', { href: ii.descriptionurl, target: '_blank', 'class': 'image jFileTitle' }) .text(dirtyTitle), $('<div>', { 'class': 'jFileTime' }).text(uItem.time.replace(/[T|Z]/g, ' ')), $('<div>', { 'class': 'jFileSize' }).html( vfc.secureCall('mdFormattNumber', uItem.size) + ' ' + i18n.kibibyte + ' ' + (vfc.secureCall('mdFormattNumber', ii.width) + ' × ' + vfc.secureCall('mdFormattNumber', ii.height)) + 'px') ], 150, '', dirtyTitle, ii.thumbheight, true); vfc.gbs[dirtyTitle] = $galleryBox; vfc.gbu[dirtyTitle] = $galleryBox.$gbGU.click(vfc.fillGlobalUsage); }); vfc.mdBusy = false; if (!vfc.mdUploadCt) vfc.nextTask(); }, /** ** Called when a batch of files has been queried **/ mdQueryFileDone: function () { // Append all queried files to the screen-container var largerGallery = $('#largerGallery2')[0] ? mw.libs.largerGallery.run : function () {}; $.each(vfc.iUploads, function (id, uItem) { if (!uItem.gbInUse) { var $galleryBox = vfc.gbs[id]; if ($galleryBox) { largerGallery($galleryBox, 1); uItem.gbInUse = true; vfc.$ctrs.ajaxMdUlContainer.append($galleryBox); vfc.mdThumbsOnDlg++; } } }); vfc.mdBusy = false; vfc.mdPendingBatchQueries--; vfc.$ctrs.ajaxMdNotReady.hide(); vfc.pb.setCurrentTaskDone(); $doc.triggerHandler('vFC', ['detail-info processed', vfc]); if ((vfc.iiUploads > (vfc.iCurrentIId + 1)) || !vfc.mdNoMoreFiles) { vfc.$mdQueryMoreBtn.button('option', 'disabled', false); var __onScroll = function () { if (vfc.$mdQueryMoreBtn.inView().length > 0) vfc.secureCall('mdQueryMore'); }; vfc.dlg.off('scroll', __onScroll) .on('scroll', __onScroll); } if (!vfc.mdNoMoreFiles) { // silently run the next upload-list-query in background vfc.secureCall('mdCreateList'); } document.body.style.cursor = 'auto'; var si = vfc.startInput; if (!vfc.mdActualResultCount) { // If there are no thumbs on the dialog, continue if (vfc.internalState !== 'revert') { // Don't shedule further tasks if called from revert part if (vfc.internalState === 'md') { if (si.modeUser && !vfc.mdNoMoreFiles) { // When no files in cat/page-mode are found, it's clear there are no more files return; } else { vfc.infoTextToEndDlg.push(i18n.mdNoResult.replace('%TARGET%', vfc.queryParams.target)); } } if (si.modeUser) { $.each(vfc.mdCommandsPostExec[vfc.mdSettings.defaultAction], function (id, task) { vfc.addTask(task); if (task in i18n.task) vfc.pb.addTask(task); }); } vfc.addTask('mdExecuteReady'); } vfc.nextTask(); } }, /** ** Called by scroll-event when the button "more" is scrolled to view (mdQueryFileDone binds the evt) or onClick on the button **/ mdQueryMore: function () { if (!this.mdPendingBatchQueries && !this.mdNumberOfExecs) { this.mdUploadCt = 0; this.addTask('mdQueryFileDone'); this.secureCall('mdSendNextQueries'); } }, /** ** II. REVERT PART ** **/ mdRvGenOGallery: function () { this.pb.setCurrentTaskDone(); if ('mdRvGenOGallery' in this.pb.t) this.pb.setTaskState('mdRvGenOGallery', 'md-doing'); this.pb.setHelp('We are working to re-add the possibility to rollback overwritten files to combat vandalism'); // ///////////////////////////////////////// // SKIP THIS - TEMPORARY NOT WORKING // // ///////////////////////////////////////// this.nextTask(); return; // ///////////////////////////////////////// }, mdExecuteReady: function () { this.internalState = 'ready'; this.pb.setCurrentTaskDone(); var i18nED = i18n.endDlg, $dlgNode = $('<div>', { id: 'mdWhereToGoDlg', title: i18nED.heading, text: i18nED.intro }) .append($('<br>'), $('<a>', { href: '#', text: i18nED.saveInProfile }).button().click(function (e) { e.preventDefault(); vfc.mdProfile(); })) .append($('<br>'), mw.html.escape(i18nED.aboutLastExec)); var $ulNode = $('<ul>'); var addLink = function (href, text, target) { $('<li>').append($('<a>', { style: 'font-size:2em; line-height:1.8em', href: mw.util.getUrl(href), target: target, text: text })).appendTo($ulNode); }; if (this.api.wasError) { $dlgNode.append( $.createNotifyArea(i18nED.errorDuringTask, 'ui-icon-alert', 'ui-state-error')); } $.each(this.infoTextToEndDlg, function (i, text) { $dlgNode.append($.createNotifyArea(text, 'ui-icon-info', 'ui-state-highlight')); }); if (this.mdTaskToPerform === 'aDelete') addLink('Special:Log/delete/' + this.username.replace(/ /g, '_'), i18nED.checkDeletions); else addLink('Special:MyContributions', i18nED.checkContribs, 'contribswindow'); if (this.mdInsertedTag) { // var encTitle; if (this.mdTaskToPerform === 'del') { // allow navigating to the new created deletion request addLink(this.requestPage, i18nED.goToRequestPage); } if (this.startInput.modeUser && ($.inArray(this.mdTaskToPerform, ['c_replace', 'OTRS', 'other']) === -1)) { addLink(this.mdUserTalkPrefix + vfc.queryParams.target, i18nED.goUserTalk); addLink(this.mdContribPrefix + vfc.queryParams.target, i18nED.goUserContribs); // Revert files overwritten by this user ... } addLink(mw.config.get('wgPageName'), i18nED.reload, '_self'); } $dlgNode.append($ulNode).dialog({ show: { effect: 'highlight', duration: 1800 }, width: Math.min(450, $win.width()) }); document.title = i18nED.title; vfc.$dialogsToClose.push($dlgNode); } }); mw.loader.using([// TODO 'jquery.ui', // deprecated 'jquery.ui', // deprecated 'jquery.tipsy', // deprecated 'jquery.lengthLimit', 'mediawiki.String', 'mediawiki.storage', // former jquery.jStorage 'mediawiki.util', 'mediawiki.page.gallery.styles', 'ext.gadget.libAPI', 'ext.gadget.libJQuery', 'ext.gadget.jquery.badge', 'ext.gadget.jquery.blockUI', 'ext.gadget.libWikiDOM' ], function () { $doc.triggerHandler('scriptLoaded', ['VisualFileChange', 'displayComponents']); }); }(jQuery, mediaWiki)); // </nowiki>