MediaWiki:Gadget-DelReqHandler.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
//<source lang="javascript">
/*
Support for quick deletions and closing of deletion requests at the Commons.
Authors: [[User:Lupo]], October 2007 - January 2008,
[[User:DieBuche]], February 2011
License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
Choose whichever license of these you like best :-)
IE not supported
*/
//Guard against double inclusions
//Enable the whole shebang only for sysops.
if (typeof(DelReqHandler) == 'undefined' && mw.config.get('wgUserGroups').join(' ').indexOf('sysop') >= 0) {
var DelReqHandler = {
/*------------------------------------------------------------------------------------------
Deletion request closing: add "[del]" and "[keep]" links to the left of the section edit
links of a deletion request. [del] and [keep] prompt for an (optional) reason, then
add "delh" and "delf" with "Deleted." or "Kept." plus the reason and signature (four tildes).
Links are added to every non-deleted image mentioned on a deletion request page. The "[del]" link
triggers deletion (auto-completed!) of the image, with a deletion summary linking to the
deletion request. If the image has a talk page, it is deleted as well. The "[keep]" link
automatically removes the "delete" template from the image page and adds the "kept" template
to the image talk page, both linking back to the deletion request.
------------------------------------------------------------------------------------------*/
running: [],
parse: function () {
var $content = $('#bodyContent, #mw_contentholder');
if (!$content.length) return;
mw.util.addCSS( '.reqHandlerLinks {font-size: 85%;}');
$h3s = $content.find('h3');
$h3s.each(function () {
var $t = $(this);
var unclosed = ($t.parents('.delh').length < 1);
var discussion = $t.nextUntil('h3, .printfooter, .delh');
var wholeDiscussion = discussion.add($t);
var editLink = $t.find('span.editsection a').eq(0);
var headLink = $t.find('span.mw-headline a').eq(0);
var title = (headLink.length && !headLink.is('.new')) ? DelReqHandler.titleFromHref(headLink.attr('href')) : "";
var archiveRegex = /Commons:Deletion_requests\/20\d\d\//;
var linkRegex = /Commons:Deletion_requests\/.*?§ion=(T-){0,1}1$/;
var requestHref = editLink.attr('href');
if (archiveRegex.test(requestHref) || !linkRegex.test(requestHref)) return true;
// It's really an edit lk to a deletion request subpage, and not a section
// edit for a daily subpage or something else
var requestPage = DelReqHandler.titleFromHref(requestHref);
if (unclosed) DelReqHandler.addLinks(requestPage, editLink, title, true, wholeDiscussion);
var links = discussion.find('a');
links = links.add(headLink).not('.new');
links.each(function () {
var href = this.href;
var title = DelReqHandler.titleFromHref(href);
if (title.indexOf('File:') == 0 && title.indexOf('/') < 0) {
//We have an image link
DelReqHandler.addLinks(requestPage, $(this), title, false, wholeDiscussion);
}
});
});
},
titleFromHref: function (href) {
if (!href) return "";
if (mw.util.getParamValue('title', href) != null) return mw.util.getParamValue('title', href).replace(/_/g, ' ');
var prefix = wgArticlePath.replace('$1', "");
// Fully expanded URL?
if (href.indexOf(prefix) != 0) prefix = mw.config.get('wgServer') + prefix;
if (href.indexOf(prefix) != 0 && prefix.indexOf('//') == 0) prefix = document.location.protocol + prefix; // protocol-relative wgServer?
if (href.indexOf(prefix) == 0) return decodeURIComponent(href.substring(prefix.length)).replace(/_/g, ' ');
return "";
},
addLinks: function (requestPage, location, imagePage, closeRequest, discussion) {
if (closeRequest) {
var link = $('<a href="#">Close: Kept</a>');
var link2 = $('<a href="#">Close: Deleted</a>');
var span = $('<span class="reqHandlerLinks2"></span>');
} else {
var link = $('<a href="#">keep</a>');
var link2 = $('<a href="#">del</a>');
var span = $('<span class="reqHandlerLinks"></span>');
}
span.append(' [').append(link).append('] [').append(link2);
location.after(span);
if (closeRequest) span.before(']');
else span.append(']');
link.click(function (event) {
event.preventDefault();
var n = new DelReqHandler.process(true, closeRequest, requestPage, imagePage, [location, span, discussion]);
DelReqHandler.running.push(n);
});
link2.click(function (event) {
event.preventDefault();
var n = new DelReqHandler.process(false, closeRequest, requestPage, imagePage, [location, span, discussion]);
DelReqHandler.running.push(n);
});
},
setup: function () {
if (mw.config.get('wgPageName').indexOf('Commons:Deletion_requests/') != -1 && mw.config.get('wgAction') == 'view' && document.URL.search(/[?&]oldid=/) == -1) {
// We're on COM:DEL or one of its daily subpages
// Don't do anything if we're not viewing the current version of the page
this.parse();
}
},
process: function (keep, closeRequestBool, requestPage, imagePage, domElements) {
//Merge the page processing functions into our new process
for (var j in DelReqHandler.processHelpers) {
this[j] = DelReqHandler.processHelpers[j];
}
this.tasks = [];
this.requestPage = requestPage.replace(/_/g, ' ');
this.keep = keep;
this.closeRequestBool = closeRequestBool;
this.imagePage = imagePage;
this.imageTalkPage = imagePage.replace(/^File:/, 'File talk:');
this.summary = 'Per [[' + requestPage + ']]';
this.domElements = domElements;
//getToken
this.addTask('getPages');
if (closeRequestBool) {
if (keep) {
this.reason = prompt('Why did you decide to keep this file?');
//User canceled
if (this.reason == null) return;
this.pagesToGet = [requestPage, imagePage];
//this.addTask('markAsKept');
//this.addTask('getDate');
} else {
this.reason = prompt('Why did you decide to delete this file?');
//User canceled
if (this.reason == null) return;
this.pagesToGet = [requestPage];
//if (imagePage != "") {
// this.addTask('deleteFile');
// this.addTask('deleteFileTalk');
//}
}
this.addTask('closeRequest');
} else {
this.pagesToGet = [imagePage];
if (keep) {
this.addTask('markAsKept');
this.addTask('getDate');
//FIXME first letter lowercase
this.summary = 'Kept ' + this.summary;
} else {
this.addTask('deleteFile');
this.addTask('nothing');
}
this.summary = prompt("Summary:", this.summary);
//User canceled
if (this.summary == null) return;
}
this.addTask('fakeReload');
this.nextTask();
this.showProgress();
}
};
DelReqHandler.processHelpers = {
getPages: function () {
var query = {
action: 'query',
prop: 'revisions|info',
rvprop: 'content|timestamp',
intoken: 'edit',
titles: this.pagesToGet.join('|')
};
this.doAPICall(query, 'getPagesCallback');
},
getPagesCallback: function (result) {
var pages = result.query.pages;
for (var id in pages) { // there should be only one, but we don't know it's ID
// The edittoken only changes between logins
this.edittoken = pages[id].edittoken;
var type;
switch (pages[id].title) {
case this.imagePage:
type = 'imagePage';
break;
case this.requestPage:
type = 'requestPage';
break;
default:
type = 'unknown';
break;
}
this[type + 'Result'] = {
pageContent: pages[id].revisions[0]['*'],
starttimestamp: pages[id].starttimestamp,
timestamp: pages[id].revisions[0]['timestamp']
};
}
this.nextTask();
},
closeRequest: function () {
var text = this.requestPageResult.pageContent;
this.decision = (this.keep) ? 'Kept' : 'Deleted';
this.decision += (this.reason) ? ':' : '.';
// text = '{{delh}}\n' + text + '\n{{delf|';
// text += d + '|' + this.reason + ' \~\~\~\~}}';
text = '{{delh}}\n' + $.trim(text) + '\n----\n';
text += "'''" + this.decision + "''' " + this.reason + ' \~\~\~\~ \n {{delf}}';
var page = {
title: this.requestPage,
text: text,
summary: this.decision + ' ' + this.reason,
editType: 'text'
};
this.savePage(page, 'nextTask');
},
markAsKept: function () {
var text = this.imagePageResult.pageContent;
text = this.removeTemplate(text);
var page = {
title: this.imagePage,
text: text,
summary: this.summary,
editType: 'text'
};
this.savePage(page, 'nextTask');
},
removeTemplate: function (text) {
var start = text.indexOf('{{delete');
if (start < 0) start = text.indexOf('{{Delete');
if (start < 0) start = text.indexOf('{{vfd');
if (start < 0) start = text.indexOf('{{Vfd');
if (start < 0) start = text.indexOf('{{ifd');
if (start < 0) start = text.indexOf('{{Ifd');
if (start >= 0) {
var level = 0;
var curr = start + 2;
var end = 0;
while (curr < text.length && end == 0) {
var opening = text.indexOf('{{', curr);
var closing = text.indexOf('}}', curr);
if (opening >= 0 && opening < closing) {
level = level + 1;
curr = opening + 2;
} else {
if (closing < 0) {
// No closing braces found
curr = text.length;
} else {
if (level > 0) level = level - 1;
else end = closing + 2;
curr = closing + 2;
}
}
}
if (end > start) {
// Also strip whitespace after the "delete" template
if (start > 0) {
text = text.substring(0, start) + text.substring(end).replace(/^\s*/, '');
} else {
text = text.substring(end).replace(/^\s*/, '');
}
return text;
}
}
alert('Couldn\'t remove the {{delete}} template, please check the file ' + this.imagePage + ' manually.');
return text;
},
getDate: function () {
var query = {
action: 'query',
prop: 'revisions',
rvlimit: 1,
rvprop: 'timestamp',
rvdir: 'newer',
titles: this.requestPage
};
this.doAPICall(query, 'addKeepToTalk');
},
addKeepToTalk: function (result) {
var pages = result.query.pages;
var date = "";
for (var id in pages) {
// there should be only one, but we don't know it's ID
var ts = pages[id].revisions[0].timestamp;
if (ts) {
// Extract year, month, and day from the timestamp.
// We don't care about the exact time.
var year = ts.substr(0, 4);
var month = ts.substr(5, 2);
var day = ts.substr(8, 2);
date = year + '-' + month + '-' + day;
}
}
var page = {
title: this.imageTalkPage,
text: '{{kept|' + date + '|' + this.requestPage + '}}\n',
summary: 'Adding {{kept}}',
editType: 'prependtext'
};
this.savePage(page, 'nextTask');
},
reload: function () {
window.location.reload();
},
fakeReload: function () {
var dE = this.domElements;
dE[3].unblock();
//Remove links
dE[1].remove();
if (this.closeRequestBool) {
dE[3].toggleClass('delh delreqworking');
dE[2].eq(0).before('<i>This deletion debate is now closed. Please do not make any edits to this archive.</i>');
dE[2].eq(-1).after('<br><span style="color:green">Saved sucessfully.<br>This is just an approximate rendering. Reload to see the actual request.</span>');
dE[2].eq(-1).after('<b>' + this.decision + '</b> ' + this.reason + ' --' + mw.config.get('wgUserName'));
dE[2].eq(-1).after('<hr>');
} else {
//Color link red
if (this.keep == false) dE[0].addClass('new');
}
},
apiURL: mw.config.get('wgServer') + wgScriptPath + "/api.php",
/**
** Simple task queue. addTask() adds a new task to the queue, nextTask() executes
** the next scheduled task. Tasks are specified as method names to call.
**/
// list of pending tasks
currentTask: '',
// current task, for error reporting
addTask: function (task) {
this.tasks.push(task);
},
nextTask: function () {
var task = this.currentTask = this.tasks.shift();
try {
this[task]();
} catch (e) {
this.fail(e);
}
},
deleteFile: function () {
var edit = {
action: 'delete',
reason: this.summary,
title: this.imagePage,
token: this.edittoken,
recreate: ''
};
this.doAPICall(edit, 'nextTask');
edit = {
action: 'delete',
reason: "Talk page of deleted image",
title: this.imageTalkPage,
token: this.edittoken,
recreate: ''
};
this.doAPICall(edit, 'nextTask', true);
},
savePage: function (page, callback) {
var edit = {
action: 'edit',
summary: page.summary,
title: page.title,
token: this.edittoken
};
edit[page.editType] = page.text;
this.doAPICall(edit, callback);
},
fail: function (e) {
alert(e);
},
doAPICall: function (params, callback, ignoreErrors) {
var k = this;
params.format = 'json';
$.ajax({
url: this.apiURL,
cache: false,
dataType: 'json',
data: params,
type: 'POST',
success: function (result, status, x) {
if (ignoreErrors) {
k[callback](result);
return;
}
if (!result) return k.fail("Receive empty API response:\n" + x.responseText);
// In case we get the mysterious 231 unknown error, just try again
if (result.error && result.error.info.indexOf('231') != -1) return setTimeout(function () {
k.doAPICall(params, callback);
}, 500);
if (result.error) return k.fail("API request failed (" + result.error.code + "): " + result.error.info);
k[callback](result);
},
error: function (x, status, error) {
return k.fail("API request returned code " + x.status + " " + status + "Error code is " + error);
}
});
},
showProgress: function () {
if (this.closeRequestBool){
this.domElements[2].wrapAll('<div class="delreqworking"></div>');
this.domElements[3] = this.domElements[2].parent('.delreqworking');
this.domElements[3].block({
message: 'Working...',
css: { border: '3px solid #A0C828', fontSize: '135%' }
});
} else {
this.domElements[3] = this.domElements[0].parent();
this.domElements[3].block({
message: 'Working...',
css: { color: '#A0C828', fontWeight: 'bold', background:'none', border:'none' }
});
}
},
nothing: function () {
//nothing
}
};
$(document).ready(function () {
DelReqHandler.setup();
});
} // End of sysop check
//</source>
/*!
* jQuery blockUI plugin
* Version 2.37 (29-JAN-2011)
* @requires jQuery v1.2.3 or later
*
* Examples at: http://malsup.com/jquery/block/
* Copyright (c) 2007-2010 M. Alsup
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Thanks to Amir-Hossein Sobhi for some excellent contributions!
*/
(function($) {
$.fn._fadeIn = $.fn.fadeIn;
var noOp = function() {};
// global $ methods for blocking/unblocking the entire page
$.blockUI = function(opts) { install(window, opts); };
$.unblockUI = function(opts) { remove(window, opts); };
// plugin method for blocking element content
$.fn.block = function(opts) {
return this.unblock({ fadeOut: 0 }).each(function() {
if ($.css(this,'position') == 'static')
this.style.position = 'relative';
if ($.browser.msie)
this.style.zoom = 1; // force 'hasLayout'
install(this, opts);
});
};
// plugin method for unblocking element content
$.fn.unblock = function(opts) {
return this.each(function() {
remove(this, opts);
});
};
$.blockUI.version = 2.37; // 2nd generation blocking at no extra cost!
// override these in your code to change the default behavior and style
$.blockUI.defaults = {
// message displayed when blocking (use null for no message)
message: '<h1>Please wait...</h1>',
title: null, // title string; only used when theme == true
draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded)
theme: false, // set to true to use with jQuery UI themes
// styles for the message when blocking; if you wish to disable
// these and use an external stylesheet then do this in your code:
// $.blockUI.defaults.css = {};
css: {
padding: 0,
margin: 0,
width: '30%',
top: '40%',
left: '35%',
textAlign: 'center',
color: '#000',
border: '3px solid #aaa',
backgroundColor:'#fff',
cursor: 'wait'
},
// minimal style set used when themes are used
themedCSS: {
width: '30%',
top: '40%',
left: '35%'
},
// styles for the overlay
overlayCSS: {
backgroundColor: '#000',
opacity: 0.6,
cursor: 'wait'
},
// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
// (hat tip to Jorge H. N. de Vasconcelos)
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
// force usage of iframe in non-IE browsers (handy for blocking applets)
forceIframe: false,
// z-index for the blocking overlay
baseZ: 1000,
// set these to true to have the message automatically centered
centerX: true, // <-- only effects element blocking (page block controlled via css above)
centerY: true,
// allow body element to be stetched in ie6; this makes blocking look better
// on "short" pages. disable if you wish to prevent changes to the body height
allowBodyStretch: true,
// enable if you want key and mouse events to be disabled for content that is blocked
bindEvents: true,
// be default blockUI will supress tab navigation from leaving blocking content
// (if bindEvents is true)
constrainTabKey: true,
// fadeIn time in millis; set to 0 to disable fadeIn on block
fadeIn: 200,
// fadeOut time in millis; set to 0 to disable fadeOut on unblock
fadeOut: 400,
// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
timeout: 0,
// disable if you don't want to show the overlay
showOverlay: true,
// if true, focus will be placed in the first available input field when
// page blocking
focusInput: true,
// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
applyPlatformOpacityRules: true,
// callback method invoked when fadeIn has completed and blocking message is visible
onBlock: null,
// callback method invoked when unblocking has completed; the callback is
// passed the element that has been unblocked (which is the window object for page
// blocks) and the options that were passed to the unblock call:
// onUnblock(element, options)
onUnblock: null,
// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
quirksmodeOffsetHack: 4,
// class name of the message block
blockMsgClass: 'blockMsg'
};
// private data and functions follow...
var pageBlock = null;
var pageBlockEls = [];
function install(el, opts) {
var full = (el == window);
var msg = opts && opts.message !== undefined ? opts.message : undefined;
opts = $.extend({}, $.blockUI.defaults, opts || {});
opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
msg = msg === undefined ? opts.message : msg;
// remove the current block (if there is one)
if (full && pageBlock)
remove(window, {fadeOut:0});
// if an existing element is being used as the blocking content then we capture
// its current place in the DOM (and current display style) so we can restore
// it when we unblock
if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
var node = msg.jquery ? msg[0] : msg;
var data = {};
$(el).data('blockUI.history', data);
data.el = node;
data.parent = node.parentNode;
data.display = node.style.display;
data.position = node.style.position;
if (data.parent)
data.parent.removeChild(node);
}
var z = opts.baseZ;
// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
// layer1 is the iframe layer which is used to supress bleed through of underlying content
// layer2 is the overlay layer which has opacity and a wait cursor (by default)
// layer3 is the message content that is displayed while blocking
var lyr1 = ($.browser.msie || opts.forceIframe)
? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
: $('<div class="blockUI" style="display:none"></div>');
var lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
var lyr3, s;
if (opts.theme && full) {
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:fixed">' +
'<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>' +
'<div class="ui-widget-content ui-dialog-content"></div>' +
'</div>';
}
else if (opts.theme) {
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:absolute">' +
'<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>' +
'<div class="ui-widget-content ui-dialog-content"></div>' +
'</div>';
}
else if (full) {
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+z+';display:none;position:fixed"></div>';
}
else {
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+z+';display:none;position:absolute"></div>';
}
lyr3 = $(s);
// if we have a message, style it
if (msg) {
if (opts.theme) {
lyr3.css(themedCSS);
lyr3.addClass('ui-widget-content');
}
else
lyr3.css(css);
}
// style the overlay
if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))
lyr2.css(opts.overlayCSS);
lyr2.css('position', full ? 'fixed' : 'absolute');
// make iframe layer transparent in IE
if ($.browser.msie || opts.forceIframe)
lyr1.css('opacity',0.0);
//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
$.each(layers, function() {
this.appendTo($par);
});
if (opts.theme && opts.draggable && $.fn.draggable) {
lyr3.draggable({
handle: '.ui-dialog-titlebar',
cancel: 'li'
});
}
// show the message
if (msg) {
if (opts.theme)
lyr3.find('.ui-widget-content').append(msg);
else
lyr3.append(msg);
if (msg.jquery || msg.nodeType)
$(msg).show();
}
if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
lyr1.show(); // opacity is zero
if (opts.fadeIn) {
var cb = opts.onBlock ? opts.onBlock : noOp;
var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
var cb2 = msg ? cb : noOp;
if (opts.showOverlay)
lyr2._fadeIn(opts.fadeIn, cb1);
if (msg)
lyr3._fadeIn(opts.fadeIn, cb2);
}
else {
if (opts.showOverlay)
lyr2.show();
if (msg)
lyr3.show();
if (opts.onBlock)
opts.onBlock();
}
// bind key and mouse events
bind(1, el, opts);
if (full) {
pageBlock = lyr3[0];
pageBlockEls = $(':input:enabled:visible',pageBlock);
if (opts.focusInput)
setTimeout(focus, 20);
}
else
center(lyr3[0], opts.centerX, opts.centerY);
if (opts.timeout) {
// auto-unblock
var to = setTimeout(function() {
full ? $.unblockUI(opts) : $(el).unblock(opts);
}, opts.timeout);
$(el).data('blockUI.timeout', to);
}
}
// remove the block
function remove(el, opts) {
var full = (el == window);
var $el = $(el);
var data = $el.data('blockUI.history');
var to = $el.data('blockUI.timeout');
if (to) {
clearTimeout(to);
$el.removeData('blockUI.timeout');
}
opts = $.extend({}, $.blockUI.defaults, opts || {});
bind(0, el, opts); // unbind events
var els;
if (full) // crazy selector to handle odd field errors in ie6/7
els = $('body').children().filter('.blockUI').add('body > .blockUI');
else
els = $('.blockUI', el);
if (full)
pageBlock = pageBlockEls = null;
if (opts.fadeOut) {
els.fadeOut(opts.fadeOut);
setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
}
else
reset(els, data, opts, el);
}
// move blocking element back into the DOM where it started
function reset(els,data,opts,el) {
els.each(function(i,o) {
// remove via DOM calls so we don't lose event handlers
if (this.parentNode)
this.parentNode.removeChild(this);
});
if (data && data.el) {
data.el.style.display = data.display;
data.el.style.position = data.position;
if (data.parent)
data.parent.appendChild(data.el);
$(el).removeData('blockUI.history');
}
if (typeof opts.onUnblock == 'function')
opts.onUnblock(el,opts);
}
// bind/unbind the handler
function bind(b, el, opts) {
var full = el == window, $el = $(el);
// don't bother unbinding if there is nothing to unbind
if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
return;
if (!full)
$el.data('blockUI.isBlocked', b);
// don't bind events when overlay is not in use or if bindEvents is false
if (!opts.bindEvents || (b && !opts.showOverlay))
return;
// bind anchors and inputs for mouse and key events
var events = 'mousedown mouseup keydown keypress';
b ? $(document).bind(events, opts, handler) : $(document).off(events, handler);
// former impl...
// var $e = $('a,:input');
// b ? $e.bind(events, opts, handler) : $e.off(events, handler);
}
// event handler to suppress keyboard/mouse events when blocking
function handler(e) {
// allow tab navigation (conditionally)
if (e.keyCode && e.keyCode == 9) {
if (pageBlock && e.data.constrainTabKey) {
var els = pageBlockEls;
var fwd = !e.shiftKey && e.target === els[els.length-1];
var back = e.shiftKey && e.target === els[0];
if (fwd || back) {
setTimeout(function(){focus(back);},10);
return false;
}
}
}
var opts = e.data;
// allow events within the message content
if ($(e.target).parents('div.' + opts.blockMsgClass).length > 0)
return true;
// allow events for content that is not being blocked
return $(e.target).parents().children().filter('div.blockUI').length == 0;
}
function focus(back) {
if (!pageBlockEls)
return;
var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
if (e)
e.focus();
}
function center(el, x, y) {
var p = el.parentNode, s = el.style;
var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
if (x) s.left = l > 0 ? (l+'px') : '0';
if (y) s.top = t > 0 ? (t+'px') : '0';
}
function sz(el, p) {
return parseInt($.css(el,p))||0;
}
})(jQuery);