User:Js/urldecoder.js: Difference between revisions

Content deleted Content added
Js (talk | contribs)
corrections
Maintenance: mw:RL/MGU - Updated deprecated module name
 
(17 intermediate revisions by 6 users not shown)
Line 1:
/*jshint strict: false, asi: true, eqeqeq: false, curly: false, laxbreak:true, forin:false, boss:true, funcscope:true, noempty: false */
//[[user:js/urldecoder]]
/*global mw, $, urlDecoderCustom, urlDecoderPrefixes, urlDecoderEngNames */
 
var btn = newToolbarBtn;
function urlDecoderButton(){
function addUrlDecoderButton(){
var tlb = document.getElementById('toolbar')
btn(
if (!tlb) return
'urlDecoder',
var bt = document.createElement('input')
urlDecoderRun,
bt.type = 'button'; bt.onclick = urlDecoderRun
'//upload.wikimedia.org/wikipedia/commons/9/91/Link_go.png',
bt.value = '→[\[]]'; bt.id = 'urlDecoder'
bt.title = 'Decode URL before cursor or all URLs in selected text',
window.urlDecoderKey
bt.style.cssText = 'background:#adbede; height:22px; vertical-align:top; padding:0'
);
tlb.appendChild(bt)
if (!window.urlDecoderKey) return
bt.accessKey = urlDecoderKey
bt.title += ' ['+urlDecoderKey+']'
updateTooltipAccessKeys([bt])
}
if (wgAction=='edit' || wgAction=='submit') addOnloadHook(urlDecoderButton)
 
mw.loader.using( [ 'user.options', 'jquery.textSelection', 'mediawiki.util' ], function () {
/* Check if view is in edit mode and that the required modules are available. Then, customize the toolbar . . . */
if ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 ) {
$( function () {
if ( mw.user.options.get('usebetatoolbar') ) {
mw.loader.using( 'ext.wikiEditor', addUrlDecoderButton );
} else {
btn = oldToolbarBtn;
addUrlDecoderButton();
}
} );
}
// Add the customizations to LiquidThreads' edit toolbar, if available
mw.hook( 'ext.lqt.textareaCreated' ).add( addUrlDecoderButton );
} );
function newToolbarBtn(bId, bFunc, bIcon, bTitle){
var msg = {}; msg[bId] = bTitle; mw.messages.set(msg) // mw.usability.addMessages(msg) doesn't work
$('#wpTextbox1').wikiEditor('addToToolbar', {
section:'main', group:'insert', tools: {
bId:{
type: 'button',
action: {type:'callback', execute: bFunc},
labelMsg: bId,
icon: bIcon
}}})
 
}
function oldToolbarBtn(bId, bFunc, bIcon, bTitle, bKey){
 
var btn = $('<img class=mw-toolbar-custombutton id="' + bId + '">')
.attr({ src: bIcon, title: bTitle, alt: bTitle.substr(0,3) })
.css({ height:'20px', 'background-color':'#bce', border:'1px outset #bce', margin:'0 1px', cursor:'pointer'})
.click(bFunc)
.appendTo('#toolbar')
if( bKey ){
btn.attr({ accesskey: bKey, title: bTitle + ' ['+bKey+']' })
.updateTooltipAccessKeys();
}
}
/**/
 
 
function urlDecoderRun(){ //main function
 
var httpRegExp = '(https?:\\/\\/[^\\]\\[\\n\\r<>" ]+)' // except []<>"
var beforeCursor = new RegExp('(\\[{0,2})'+httpRegExp+'( +[^\\]\n]+)?\\]{0,2}$', 'i')
var localPrefix = getPrefixes(wgServer.substring(7))
if (wgServer == 'https://secure.wikimedia.org')
localPrefix = getPrefixes(wgScript.split('/')[2]+'.'+wgScript.split('/')[1]+'.org')
var newText, linkSize, txtarea = document.editform.wpTextbox1
var isBeforeCursor = false
 
//WMF domains mess
if (document.selection) { //IE/Opera
//2nd-lvl domains; secure link: .../wikipedia/mediawiki, .../wikipedia/foundation
var scrollTop = document.documentElement.scrollTop
var wmDomain = {
txtarea.focus()
mediawiki: 'mw',
range = document.selection.createRange()
wikimediafoundation: 'foundation' }
if (!range.moveStart) return
//2nd-lvl domains with multiple languages; secure link: wikinews/en
if (range.text){
var wmDomainM = {
newText = processSelText(range.text)
wikipedia:'w',
}else { //no selection
wikibooks:'b',
if (!(rr=range.duplicate())) return
wikinews:'n',
rr.moveStart('character', - 1000)
wikiquote:'q',
linkSize = processBeforeCursor(rr.text)
wikisource:'s',
if (!linkSize) return
wikiversity:'v',
range.moveStart('character', - linkSize) //select matched
wiktionary:'wikt'}
}
//3rd-lvl domains on .wikimedia.org; however secure link is wikipedia/*
//replace text
var wmSubDomains = /^(meta|commons|incubator|species|strategy)$/
if (newText != range.text){
//Exceptions:
range.text = newText
// https:/.../wikipedia/sources/wiki/Main_Page - not recognized by script
if (navigator.userAgent.indexOf('MSIE') != -1) newText = newText.replace(/\r/g,'') //for IE: do not count \r
range.moveStart('character', - newText.length)
range.select()
}
document.documentElement.scrollTop = scrollTop //restore window scroll position
 
}else if (txtarea.selectionStart || txtarea.selectionStart == '0') { // Mozilla
var scrollTop = txtarea.scrollTop, txt = txtarea.value
txtarea.focus()
var startPos = txtarea.selectionStart, endPos = txtarea.selectionEnd
if (startPos != endPos){
newText = processSelText(txt.substring(startPos, endPos))
}else{ //no selection
linkSize = processBeforeCursor(txt.substring((endPos-1000>0?endPos-1000:0), endPos))
if (!linkSize) return
startPos = endPos - linkSize //select matched
}
//replace text
if (newText != txt.substring(startPos, endPos)){
txtarea.value = txt.substring(0, startPos) + newText + txt.substring(endPos, txtarea.value.length)
txtarea.selectionEnd = startPos + newText.length
txtarea.selectionStart = startPos
}
txtarea.scrollTop = scrollTop
}//end of main function
 
var httpRegExp = '((?:https?:)?\\/\\/[^\\]\\[\\n\\r<>" ]+)' // any chars except []<>" and \n and spaces
var localPrefix = WMPrefixes( mw.config.get( 'wgServer' ).replace(/^\/\//,'http://') + mw.config.get( 'wgScript' ) )
var oldText, newText, isBeforeCursor, colonNS
 
var tbox = $('#wpTextbox1').focus()
oldText = tbox.textSelection( 'getSelection' )
var rx
 
if( oldText ){ //there was selection
 
rx = RegExp('(\\[{0,2})' + httpRegExp + '([^\\]\\[\\n\\r]*?\\]\\]?)?', 'ig')
newText = oldText.replace(rx, simplifyMatched)
 
if( window.urlDecoderIntLinks ){
var ut = '(' + mw.config.get( 'wgFormattedNamespaces' )[3].replace(/ /g,'_') + '|user_talk)' //both localized and canonical 'user_talk'
ut = RegExp ('\\[\\[' + ut.toLowerCase() + ':[^#]+$', 'i')
newText = newText.replace(/\[\[[^\]\|\n]+/g, function(lnk){
return ut.test(lnk) ? lnk : decodeAnchor(lnk) // skip user_talk, usually found in signatures
})
}
 
if( newText == oldText) return
 
 
}else{ //process text before cursor
 
function processBeforeCursor(str){ //looks for http: inside string, returns it's distance to cursor, also sets newText var
isBeforeCursor = true
var pos = str.lastIndexOf('http://') //to cut out possible preceding http links
if (pos == -1) pos = str.lastIndexOf('https://')
if (pos == -1) return 0
else if (pos > 10) str = str.substring(pos-2) //cut chars before http, leaving some room for possible [
var ma = str.match(beforeCursor) // result: (whole string)' '[', 'http:...', ' name]'
if (!ma) return 0
if (ma[3]) //link with name: add brackets just in case
newText = decodeMatchedLink(ma[0], '[', ma[2], ma[3]+']')
else //just url: add closing bracket if there is leading bracket
newText = decodeMatchedLink(ma[0], ma[1], ma[2], ma[1]?']':'')
return ma[0].length
}
 
//move back enough characters
function processSelText(txt){
var caretPos = tbox.textSelection('getCaretPosition')
txt = txt.replace(RegExp('(\\[{0,2})' + httpRegExp + '([^\\]\\[\\n\\r]*?\\]\\]?)?', 'ig'),
var beginPos = caretPos - 2000
decodeMatchedLink)
if( beginPos < 0 ) beginPos = 0
if (window.urlDecoderIntLinks) txt = txt.replace(/\[\[[^\]\|\n]+/g, decodeWikiLink)
tbox.textSelection( 'setSelection', {start:beginPos, end:caretPos} )
return txt
oldText = tbox.textSelection( 'getSelection' )
tbox.textSelection( 'setSelection', {start:caretPos, end:caretPos} )
 
//try to find http in oldText
rx = new RegExp('(\\[{0,2})'+httpRegExp+'( +[^\\]\n]+)?\\]{0,2}$', 'i')
var ma = rx.exec( oldText ) // result: (whole string)' '[', 'http:...', ' name]'
if( !ma ) return
oldText = ma[0]
if( ma[3] ) //link with name: automatically add brackets
newText = simplifyMatched(ma[0], '[', ma[2], ma[3]+']')
else //just url: add closing bracket only if there is leading bracket
newText = simplifyMatched(ma[0], ma[1], ma[2], ma[1]?']':'')
 
if( oldText == newText ) return
tbox.textSelection( 'setSelection', {start: caretPos - oldText.length, end: caretPos} )
 
}
 
//replace text
tbox.textSelection( 'encapsulateSelection', {replace:true, peri:newText} )
 
//end of main code
return
 
 
 
 
 
//---FUNCTIONS
 
 
function simplifyMatched(str, bracket, url, rest){//arguments: (whole string), '[', url, ' name]'
 
if( !bracket ){//no brackets, just url
var trail = RegExp(
'['
+ ',;\\\\.:!\\?' //trailing punctuation, per Parser.php
+ ( /\(/.test(url) ? '' : '\\)' ) //also closing bracket without opening bracket
+ ']+$'
+ "|''+$" //or possible bold/italic at the end of url
)
.exec( url )
if( trail ){
url = url.substring( 0, url.length - trail[0].length ) //move these out of url
}
return decodeUrl(url) + str.substring(url.length)
 
}else if( rest ){ //both brackets and possibly name
return decodeUrl(url, rest.replace(/\]+$|^ +| +$/g,'')) //trim ending brackets and spaces in 'name]'
 
}else{
return str //probably broken wikicode in selected text
}
function decodeMatchedLink(str, bracket, url, rest){//gets: (whole string), '[', url, ' name]'
if (!bracket) return decodeLink(url) + str.substring(url.length) //just url
if (!rest) return str //no matching closing bracket
var name = rest.replace(/^ +|\]+$/g,'') //trim leading spaces and ending brackets
str = decodeLink(url, name)
if (str.charAt(0) != '[' ) str = '['+str+']' //not wikilink - restore single brackets
return str
}
 
 
function decodeLink(url, name){ //name is optional
var parts, endingDots, result
if (name) name = name.replace(/^ +| +$/g,'') //trim spaces
//secure Wikimedia link into non-secure
url = url.replace(/https:\/\/secure.wikimedia.org\/(\w+)\/(\w+)\/([^\]\|\n\r ]+)/, 'http://$2.$1.org/$3')
//bugzilla
if (parts = url.match(/^https:\/\/bugzilla\.wikimedia.org\/show_bug\.cgi\?id=(\d+)/))
return '[\[mediazilla:'+parts[1]+ (name?'|'+name:'') + ']]'
 
function decodeUrl(url, name){ //url -> %-decoded -> [[link|name]] (if possible); name is optional
//decode %
 
if (url.indexOf('%') != -1) try { url = decodeURI(url) } catch(e){}
var decodingFailed //need to skip some strange percent-encoded URIs
url = url.replace(/%(3B|2F|2C|3A)/g, function(s){return decodeURIComponent(s)}) // ;/,:
 
//encode back some chars not allowed by MediaWiki
//percent-decoding
url = url.replace(/[ <>"\[\]]/g, function(s){return encodeURIComponent(s)}) //"
if( url.indexOf('%') != -1 )
try {
if (isBeforeCursor)
url = decodeURI(url)
for (var n in window.urlDecoderEngNames) //to eng keywords
url = url.replace(/%(3B|2F|2C|3A)/g, decodeURIComponent) //decode ;/,:
url = url.replace(/[ <>"\[\]|]/g, encodeURIComponent) //" some disallowed chars, and pipe can screw template params
} catch(e){
decodingFailed = true
}
 
if( isBeforeCursor ) //user-defined conversion to eng keywords
for( var n in window.urlDecoderEngNames )
url = url.replace(RegExp('(title=|wiki\/)('+urlDecoderEngNames[n]+':)'), '$1' + n + ':')
 
//analyze
//try converting to internal link
parts = url.substring(7).split('/')
if( !decodingFailed && !/(\}\}|\|)$/.test(url) ) //trailing | or }} could mean a part of a template, skip to be safe
var ___domain = parts[0]
var linkPrefixlink = getPrefixestoWikilink(___domainurl)
 
//return unchanged if cannot convert into wiki link
//user-defined function
if (!linkPrefix || parts[1] != 'wiki' || url.indexOf('?')!=-1)
if( window.urlDecoderCustom ){
return name ? '[' + url + ' ' + name + ']' : url
url = urlDecoderCustom(url)
//convert to wiki link
if( ! /^((?:https?:)?\/\/|\{\{)/.test(url) ) link = url //was converted to internal link
var title = url.substring(parts[0].length + parts[1].length + 9) //get part after /wiki/
}
title = decodeWikiLink(title)
 
//return internal link
if( link ){
link = link.replace(/%(3f|26|22)/ig, decodeURIComponent) //decode ?&"
if( ( mw.config.get( 'wgNamespaceNumber' ) === 0 || mw.config.get( 'wgNamespaceNumber' ) === 14 ) && isBeforeCursor )
link = link.replace(/^:/,'') //probably user adding interwiki
return '[' + '[' + link + (name?'|'+name:'') + ']]'
}
 
//or return external link
if( typeof name == 'string' ){
if( isBeforeCursor ) url = url.replace(/''/g,'%27%27') //techically '' should stop URL, but more likely it's part of it
return '[' + url + (name?' '+name:'') + ']' //empty name
}else{
return url
}
 
}
 
 
 
function toWikilink(url){ // 'http://xx.wikipedia.org/wiki/YY' -> xx:YY
 
//add bugzilla to user-defined prefixes
window.urlDecoderPrefixes = $.extend( window.urlDecoderPrefixes,
{ 'https://bugzilla.wikimedia.org/show_bug.cgi?id=' : 'mediazilla' } )
 
//apply user-defined prefixes
for( var key in urlDecoderPrefixes )
if( url.toLowerCase().indexOf(key) != -1 )
return urlDecoderPrefixes[key] + ':' + url.substring( url.indexOf(key) + key.length )
 
//check if we can convert to internal link with WM prefixes
var ma = /^((?:https?:)?\/\/[^\/]+)\/wiki\/([^?]+)$/.exec( url )// 1:'http://___domain.org' 2:part after /wiki/
if( !ma ) return null
var linkPrefix = WMPrefixes( ma[1] )
if( !linkPrefix) return null
 
//convert to internal
var title = decodeAnchor( ma[2] )
var prefixes = ''
if ( linkPrefix[0] && (linkPrefix[0] != localPrefix[0]) ) prefixes += linkPrefix[0]
if ( linkPrefix[1] && (linkPrefix[1] != localPrefix[1]) ) prefixes += ':' + linkPrefix[1]
if ( prefixes || isColonNeeded(title) ) prefixes += ':' //colon after prefix or leading colon on cat/file link
return prefixes + title
if (!prefixes && isColonNeeded(title)) prefixes = ':' //leading colon for cat/file
 
//return result
result = '[\[' + prefixes + title
if (name) result += '|' + name + ']]'
else result = (result + ']]').replace(/(\.+)\]\]/,']]$1') //url. -> [[w]]. not [[w.]]
return result
}
 
 
function decodeWikiLink(link){
 
link = link.replace(/_/g, ' ').replace(/^ +| +$/g, '')
 
function decodeAnchor(link){//simplify internal link: replace %20 and _ then decode anchor
link = link.replace(/(_|%20)/g, ' ').replace(/^ +| +$/g, '')
var parts = link.split('#')
if ( parts.length != 2 ) return link //no anchor
var anchor = parts[1], hidIdx = -1, hidden = []
link = parts[0]
anchor = anchor.replace( //hide IPs
/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/,
function(s){ hidden[++hidIdx] = s; return '\x01' + hidIdx + '\x02' }
)
//decode 4, 3 and 2-byte: http://en.wikipedia.org/wiki/UTF-8
anchor = anchor.replace(/\.F[0-4]\.[89AB][\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, deChar)
anchor = anchor.replace(/\.E[\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, deChar)
anchor = anchor.replace(/\.[CD][\dA-F]\.[89AB][\dA-F]/g, deChar)
anchor = anchor.replace( //hide IPs
//decode reserved 1-byte chars: http://meta.wikimedia.org/wiki/Url , http://en.wikipedia.org/wiki/Percent_encoding
/(?:^|[^0-9A-F\.])(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/,
anchor = anchor.replace(/\.[2-5][0-9A-F]/g, function(str){
function(s){ hidden[++hidIdx] = s; return '\x01' + hidIdx + '\x02' }
var ch = deChar(str) // ;:@&=+$,/?%#[]<>
)
if ((ch != ':') &&
//decode 1-byte chars: all symbols except -.:_ and []{} prohibited in links
((encodeURIComponent(ch).replace(/%/,'.') == str) || ('!*\'()'.indexOf(ch) != -1)))
anchor = anchor.replace(/\.[2-7][0-9A-F]/g, function(hhh){
return ch
var ch = deChar(hhh)
else
if( '!"#$%&\'()*+,/;<=>?@\\^`~'.indexOf(ch) >= 0 ) return ch
return str //do not decode e.g. %35 which is '5' and not supposed to be encoded
else return hhh
})
//unhide IPs and return
function deChar(str){ return tryDecode(str.replace(/\.([0-9A-F][0-9A-F])/g, '%$1')) }
for( var i=hidIdx; i>=0; i-- ) anchor = anchor.replace('\x01'+i+'\x02', hidden[i])
function tryDecode(s){ try {s = decodeURIComponent(s)} catch(e){}; return s }
if( anchor.indexOf("''") != -1 ) return link //cannot have double '' in link
//unhide IPs
else return parts[0] + '#' + anchor
for (var i=hidIdx; i>=0; i--) anchor = anchor.replace('\x01'+i+'\x02', hidden[i])
//return
return link + '#' + anchor
}
 
function deChar(ss){
function getPrefixes(___domain){ //en.wikipedia.org -> [ 'w', 'en'] , using projPrefix{}
try{ss = decodeURIComponent(ss.replace(/\.([0-9A-F][0-9A-F])/g, '%$1'))} catch(e){}
var projPrefix = {
return ss
'test.wikipedia.org':'testwiki',
}
'wikipedia.org':'w',
'wikibooks.org':'b',
'wikinews.org':'n',
'wikiquote.org':'q',
'wikisource.org':'s',
'wikiversity.org':'v',
'wiktionary.org':'wikt',
'mediawiki.org':'mw',
'www.wikimedia.org':'foundation',
'wikimedia.org':'*',
'wikimediafoundation.org':'foundation'
}
___domain = ___domain.toLowerCase()
var proj, isFound
for (proj in projPrefix) if (___domain.indexOf(proj) != -1) {isFound=true; break}
if (!isFound) return null
var prefix = projPrefix[proj], lang = ''
if (prefix == '*') prefix = ___domain.split('.')[0] // .wikimedia.org project
else if ((___domain=___domain.replace(proj, '')) && (___domain != 'www.')) lang = ___domain.split('.')[0] //multi-lang project
//else if (prefix.charAt(0) == ':'){ //multi-lang project if (pp.length == 3) lang = pp[0]
return [prefix, lang]
}
 
function isColonNeeded(pg){
if (pg.indexOf(':')==-1) return false
urlDecoderNS = window.urlDecoderNS || {}
urlDecoderNS.en = 'image'
var ns = urlDecoderNS[wgContentLanguage]
if (typeof ns != 'string') ns = requestColonNS()
return RegExp('^('+ns+'|file|category) *:','i').test(pg)
}
 
function requestColonNS(){
var whatsthis = ' &nbsp; &nbsp; <a href="http://en.wikipedia.org/wiki/user:js/urldecoder#Localization" target=_blank>(?)</a>'
showMsg('Requesting namespaces...'+whatsthis)
var aj = sajax_init_object(), q = null, ns = []
aj.open('GET', '/w/api.php?format=json&action=query&meta=siteinfo&siprop=namespaces|namespacealiases', false)
aj.send(null)
try { eval('q='+aj.responseText); q = q.query
} catch(e){return null}
ns.push(q.namespaces[6]['*']); ns.push(q.namespaces[14]['*'])
for (var k in q.namespacealiases)
if (q.namespacealiases[k].id==6 || q.namespacealiases[k].id==14)
ns.push(q.namespacealiases[k]['*'])
ns = ns.join('|').toLowerCase()
urlDecoderNS[wgContentLanguage] = ns
showMsg("<code>urlDecoderNS = {'"+wgContentLanguage+"':'"+ns+"'}<code>"+whatsthis)
return ns
}
 
 
function showMsg(htm){
var dv = document.getElementById('edit-msg')
if (!dv){
dv = document.createElement('div')
dv.id = 'edit-msg'; dv.style.cssText = 'margin:5px; border:1px solid gray'
var tbox = document.getElementById('wpTextbox1')
tbox.parentNode.insertBefore(dv, tbox)
}
dv.innerHTML = htm; dv.style.display = ''
setTimeout(hideMsg, 15000)
}
 
function WMPrefixes(url){ // http: //en.wikipedia.org/wiki/... -> [ 'w', 'en']
function hideMsg(){
 
var dv = document.getElementById('edit-msg')
var dd = /^(?:https?:)?\/\/([a-z\.]+)\.org/.exec( url.toLowerCase() )
dv.style.display = 'none'
if( !dd ) return null
dd = dd[1].split('.') //domains, e.g. ['en','wikipedia']
if( dd.length > 2 ) return null //too many subdomains, possibly mobile site XX.m.wikipedia.org/
 
var lang = '', proj = '', ___domain = dd.pop(), subdomain = dd.pop()
if( subdomain == 'www' ) subdomain = ''
 
if( ___domain == 'wikimedia' ){ // *.wikimedia.org
if( !subdomain )
proj = 'foundation'
else if( wmSubDomains.test(subdomain) )
proj = subdomain
else
return null
 
}else if( (proj = wmDomain[___domain]) && !subdomain ){ // mediawiki.org & wikimediafoundation.org
//done: proj is set
 
}else if( proj = wmDomainM[___domain] ){ //multi-lang domains
if( !subdomain );
//done: e.g. 'wikisource.org'
else if( proj == 'w' && subdomain == 'test' )
proj = 'testwiki'
else if( subdomain.length >= 2 )
lang = subdomain
else
return null
 
}else return null //unrecognized ___domain
 
return [proj, lang]
 
}
 
function isColonNeeded(pg){
if( ! /:/.test(pg) ) return false
if( ! colonNS ){ //define list of all possible category and file namespaces
var list = ['file', 'category'] //canonical aliases
var namespaces = mw.config.get( 'wgNamespaceIds' );
for( var name in namespaces )
if( (namespaces[name]==6 || namespaces[name]==14) && $.inArray(name, list) == -1 )
list.push(name)
colonNS = RegExp( '^(' + list.join('|') + ') *:', 'i')
}
return colonNS.test( $.trim(pg) )
}
}