r90063 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r90062‎ | r90063 | r90064 >
Date:17:44, 14 June 2011
Author:krinkle
Status:ok
Tags:
Comment:
Remove redundant mediawiki.util directory
Modified paths:
  • /trunk/phase3/resources/Resources.php (modified) (history)
  • /trunk/phase3/resources/mediawiki.util (deleted) (history)
  • /trunk/phase3/resources/mediawiki/mediawiki.util.js (added) (history)
  • /trunk/phase3/tests/qunit/index.html (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/mediawiki.util (deleted) (history)
  • /trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.util.js (added) (history)

Diff [purge]

Index: trunk/phase3/resources/Resources.php
@@ -446,7 +446,7 @@
447447 ),
448448 ),
449449 'mediawiki.util' => array(
450 - 'scripts' => 'resources/mediawiki.util/mediawiki.util.js',
 450+ 'scripts' => 'resources/mediawiki/mediawiki.util.js',
451451 'dependencies' => array(
452452 'jquery.checkboxShiftClick',
453453 'jquery.client',
Index: trunk/phase3/resources/mediawiki/mediawiki.util.js
@@ -0,0 +1,598 @@
 2+/**
 3+ * Utilities
 4+ */
 5+( function( $, mw ) {
 6+
 7+ mw.util = {
 8+
 9+ /* Initialisation */
 10+ /**
 11+ * @var boolean Wether or not already initialised
 12+ */
 13+ 'initialised' : false,
 14+ 'init' : function() {
 15+ if ( this.initialised === false ) {
 16+ this.initialised = true;
 17+
 18+ // Any initialisation after the DOM is ready
 19+ $( function() {
 20+
 21+ /* Set up $.messageBox */
 22+ $.messageBoxNew( {
 23+ 'id': 'mw-js-message',
 24+ 'parent': '#content'
 25+ } );
 26+
 27+ // Shortcut to client profile return
 28+ var profile = $.client.profile();
 29+
 30+ /* Set tooltipAccessKeyPrefix */
 31+
 32+ // Opera on any platform
 33+ if ( profile.name == 'opera' ) {
 34+ mw.util.tooltipAccessKeyPrefix = 'shift-esc-';
 35+
 36+ // Chrome on any platform
 37+ } else if ( profile.name == 'chrome' ) {
 38+ // Chrome on Mac or Chrome on other platform ?
 39+ mw.util.tooltipAccessKeyPrefix = ( profile.platform == 'mac'
 40+ ? 'ctrl-option-' : 'alt-' );
 41+
 42+ // Non-Windows Safari with webkit_version > 526
 43+ } else if ( profile.platform !== 'win'
 44+ && profile.name == 'safari'
 45+ && profile.layoutVersion > 526 ) {
 46+ mw.util.tooltipAccessKeyPrefix = 'ctrl-alt-';
 47+
 48+ // Safari/Konqueror on any platform, or any browser on Mac
 49+ // (but not Safari on Windows)
 50+ } else if ( !( profile.platform == 'win' && profile.name == 'safari' )
 51+ && ( profile.name == 'safari'
 52+ || profile.platform == 'mac'
 53+ || profile.name == 'konqueror' ) ) {
 54+ mw.util.tooltipAccessKeyPrefix = 'ctrl-';
 55+
 56+ // Firefox 2.x
 57+ } else if ( profile.name == 'firefox' && profile.versionBase == '2' ) {
 58+ mw.util.tooltipAccessKeyPrefix = 'alt-shift-';
 59+ }
 60+
 61+ /* Enable CheckboxShiftClick */
 62+ $( 'input[type=checkbox]:not(.noshiftselect)' ).checkboxShiftClick();
 63+
 64+ /* Emulate placeholder if not supported by browser */
 65+ if ( !( 'placeholder' in document.createElement( 'input' ) ) ) {
 66+ $( 'input[placeholder]' ).placeholder();
 67+ }
 68+
 69+ /* Fill $content var */
 70+ if ( $( '#bodyContent' ).length ) {
 71+ // Vector, Monobook, Chick etc.
 72+ mw.util.$content = $( '#bodyContent' );
 73+
 74+ } else if ( $( '#mw_contentholder' ).length ) {
 75+ // Modern
 76+ mw.util.$content = $( '#mw_contentholder' );
 77+
 78+ } else if ( $( '#article' ).length ) {
 79+ // Standard, CologneBlue
 80+ mw.util.$content = $( '#article' );
 81+
 82+ } else {
 83+ // #content is present on almost all if not all skins. Most skins (the above cases)
 84+ // have #content too, but as an outer wrapper instead of the article text container.
 85+ // The skins that don't have an outer wrapper do have #content for everything
 86+ // so it's a good fallback
 87+ mw.util.$content = $( '#content' );
 88+ }
 89+
 90+ /* Enable makeCollapse */
 91+ $( '.mw-collapsible' ).makeCollapsible();
 92+
 93+ /* Table of Contents toggle */
 94+ var $tocContainer = $( '#toc' ),
 95+ $tocTitle = $( '#toctitle' ),
 96+ $tocToggleLink = $( '#togglelink' );
 97+ // Only add it if there is a TOC and there is no toggle added already
 98+ if ( $tocContainer.size() && $tocTitle.size() && !$tocToggleLink.size() ) {
 99+ var hideTocCookie = $.cookie( 'mw_hidetoc' );
 100+ $tocToggleLink = $( '<a href="#" class="internal" id="togglelink">' )
 101+ .text( mw.msg( 'hidetoc' ) )
 102+ .click( function(e){
 103+ e.preventDefault();
 104+ mw.util.toggleToc( $(this) );
 105+ } );
 106+ $tocTitle.append( $tocToggleLink.wrap( '<span class="toctoggle">' ).parent().prepend( '&nbsp;[' ).append( ']&nbsp;' ) );
 107+
 108+ if ( hideTocCookie == '1' ) {
 109+ // Cookie says user want toc hidden
 110+ $tocToggleLink.click();
 111+ }
 112+ }
 113+ } );
 114+
 115+ return true;
 116+ }
 117+ return false;
 118+ },
 119+
 120+ /* Main body */
 121+
 122+ /**
 123+ * Encode the string like PHP's rawurlencode
 124+ *
 125+ * @param str string String to be encoded
 126+ */
 127+ 'rawurlencode' : function( str ) {
 128+ str = ( str + '' ).toString();
 129+ return encodeURIComponent( str )
 130+ .replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' )
 131+ .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' );
 132+ },
 133+
 134+ /**
 135+ * Encode page titles for use in a URL
 136+ * We want / and : to be included as literal characters in our title URLs
 137+ * as they otherwise fatally break the title
 138+ *
 139+ * @param str string String to be encoded
 140+ */
 141+ 'wikiUrlencode' : function( str ) {
 142+ return this.rawurlencode( str )
 143+ .replace( /%20/g, '_' ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' );
 144+ },
 145+
 146+ /**
 147+ * Get the link to a page name (relative to wgServer)
 148+ *
 149+ * @param str string Page name to get the link for.
 150+ * @return string Location for a page with name of 'str' or boolean false on error.
 151+ */
 152+ 'wikiGetlink' : function( str ) {
 153+
 154+ return mw.config.get( 'wgArticlePath' ).replace( '$1', this.wikiUrlencode( str ) );
 155+ },
 156+
 157+ /**
 158+ * Get address to a script in the wiki root.
 159+ * For index.php use mw.config.get( 'wgScript' )
 160+ *
 161+ * @param str string Name of script (eg. 'api'), defaults to 'index'
 162+ * @return string Address to script (eg. '/w/api.php' )
 163+ */
 164+ 'wikiScript' : function( str ) {
 165+ return mw.config.get( 'wgScriptPath' ) + '/' + ( str || 'index' ) + mw.config.get( 'wgScriptExtension' );
 166+ },
 167+
 168+ /**
 169+ * Append a new style block to the head
 170+ *
 171+ * @param text string CSS to be appended
 172+ * @return CSSStyleSheet
 173+ */
 174+ 'addCSS' : function( text ) {
 175+ var s = document.createElement( 'style' );
 176+ s.type = 'text/css';
 177+ s.rel = 'stylesheet';
 178+ if ( s.styleSheet ) {
 179+ s.styleSheet.cssText = text; // IE
 180+ } else {
 181+ s.appendChild( document.createTextNode( text + '' ) ); // Safari sometimes borks on null
 182+ }
 183+ document.getElementsByTagName('head')[0].appendChild( s );
 184+ return s.sheet || s;
 185+ },
 186+
 187+ /**
 188+ * Hide/show the table of contents element
 189+ *
 190+ * @param $toggleLink jQuery A jQuery object of the toggle link.
 191+ * @param callback function Function to be called after the toggle is
 192+ * completed (including the animation) (optional)
 193+ * @return mixed Boolean visibility of the toc (true if it's visible)
 194+ * or Null if there was no table of contents.
 195+ */
 196+ 'toggleToc' : function( $toggleLink, callback ) {
 197+ var $tocList = $( '#toc ul:first' );
 198+
 199+ // This function shouldn't be called if there's no TOC,
 200+ // but just in case...
 201+ if ( $tocList.size() ) {
 202+ if ( $tocList.is( ':hidden' ) ) {
 203+ $tocList.slideDown( 'fast', callback );
 204+ $toggleLink.text( mw.msg( 'hidetoc' ) );
 205+ $( '#toc' ).removeClass( 'tochidden' );
 206+ $.cookie( 'mw_hidetoc', null, {
 207+ expires: 30,
 208+ path: '/'
 209+ } );
 210+ return true;
 211+ } else {
 212+ $tocList.slideUp( 'fast', callback );
 213+ $toggleLink.text( mw.msg( 'showtoc' ) );
 214+ $( '#toc' ).addClass( 'tochidden' );
 215+ $.cookie( 'mw_hidetoc', '1', {
 216+ expires: 30,
 217+ path: '/'
 218+ } );
 219+ return false;
 220+ }
 221+ } else {
 222+ return null;
 223+ }
 224+ },
 225+
 226+ /**
 227+ * Grab the URL parameter value for the given parameter.
 228+ * Returns null if not found.
 229+ *
 230+ * @param param string The parameter name.
 231+ * @param url string URL to search through (optional)
 232+ * @return mixed Parameter value or null.
 233+ */
 234+ 'getParamValue' : function( param, url ) {
 235+ url = url ? url : document.location.href;
 236+ // Get last match, stop at hash
 237+ var re = new RegExp( '[^#]*[&?]' + $.escapeRE( param ) + '=([^&#]*)' );
 238+ var m = re.exec( url );
 239+ if ( m && m.length > 1 ) {
 240+ return decodeURIComponent( m[1] );
 241+ }
 242+ return null;
 243+ },
 244+
 245+ /**
 246+ * @var string
 247+ * Access key prefix. Will be re-defined based on browser/operating system
 248+ * detection in mw.util.init().
 249+ */
 250+ 'tooltipAccessKeyPrefix' : 'alt-',
 251+
 252+ /**
 253+ * @var RegExp
 254+ * Regex to match accesskey tooltips.
 255+ */
 256+ 'tooltipAccessKeyRegexp': /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
 257+
 258+ /**
 259+ * Add the appropriate prefix to the accesskey shown in the tooltip.
 260+ * If the nodeList parameter is given, only those nodes are updated;
 261+ * otherwise, all the nodes that will probably have accesskeys by
 262+ * default are updated.
 263+ *
 264+ * @param nodeList mixed A jQuery object, or array of elements to update.
 265+ */
 266+ 'updateTooltipAccessKeys' : function( nodeList ) {
 267+ var $nodes;
 268+ if ( nodeList instanceof jQuery ) {
 269+ $nodes = nodeList;
 270+ } else if ( nodeList ) {
 271+ $nodes = $( nodeList );
 272+ } else {
 273+ // Rather than scanning all links, just the elements that
 274+ // contain the relevant links
 275+ this.updateTooltipAccessKeys(
 276+ $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a' ) );
 277+
 278+ // these are rare enough that no such optimization is needed
 279+ this.updateTooltipAccessKeys( $( 'input' ) );
 280+ this.updateTooltipAccessKeys( $( 'label' ) );
 281+ return;
 282+ }
 283+
 284+ $nodes.each( function ( i ) {
 285+ var tip = $(this).attr( 'title' );
 286+ if ( !!tip && mw.util.tooltipAccessKeyRegexp.exec( tip ) ) {
 287+ tip = tip.replace( mw.util.tooltipAccessKeyRegexp,
 288+ '[' + mw.util.tooltipAccessKeyPrefix + "$5]" );
 289+ $(this).attr( 'title', tip );
 290+ }
 291+ } );
 292+ },
 293+
 294+ /*
 295+ * @var jQuery
 296+ * A jQuery object that refers to the page-content element
 297+ * Populated by init().
 298+ */
 299+ '$content' : null,
 300+
 301+ /**
 302+ * Add a link to a portlet menu on the page, such as:
 303+ *
 304+ * p-cactions (Content actions), p-personal (Personal tools),
 305+ * p-navigation (Navigation), p-tb (Toolbox)
 306+ *
 307+ * The first three paramters are required, the others are optional and
 308+ * may be null. Though providing an id and tooltip is recommended.
 309+ *
 310+ * By default the new link will be added to the end of the list. To
 311+ * add the link before a given existing item, pass the DOM node
 312+ * (document.getElementById( 'foobar' )) or the jQuery-selector
 313+ * ( '#foobar' ) of that item.
 314+ *
 315+ * @example mw.util.addPortletLink(
 316+ * 'p-tb', 'http://mediawiki.org/',
 317+ * 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', '#t-print'
 318+ * )
 319+ *
 320+ * @param portlet string ID of the target portlet ( 'p-cactions' or 'p-personal' etc.)
 321+ * @param href string Link URL
 322+ * @param text string Link text
 323+ * @param id string ID of the new item, should be unique and preferably have
 324+ * the appropriate prefix ( 'ca-', 'pt-', 'n-' or 't-' )
 325+ * @param tooltip string Text to show when hovering over the link, without accesskey suffix
 326+ * @param accesskey string Access key to activate this link (one character, try
 327+ * to avoid conflicts. Use $( '[accesskey=x]' ).get() in the console to
 328+ * see if 'x' is already used.
 329+ * @param nextnode mixed DOM Node or jQuery-selector string of the item that the new
 330+ * item should be added before, should be another item in the same
 331+ * list, it will be ignored otherwise
 332+ *
 333+ * @return mixed The DOM Node of the added item (a ListItem or Anchor element,
 334+ * depending on the skin) or null if no element was added to the document.
 335+ */
 336+ 'addPortletLink' : function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
 337+
 338+ // Check if there's atleast 3 arguments to prevent a TypeError
 339+ if ( arguments.length < 3 ) {
 340+ return null;
 341+ }
 342+ // Setup the anchor tag
 343+ var $link = $( '<a></a>' ).attr( 'href', href ).text( text );
 344+ if ( tooltip ) {
 345+ $link.attr( 'title', tooltip );
 346+ }
 347+
 348+ // Some skins don't have any portlets
 349+ // just add it to the bottom of their 'sidebar' element as a fallback
 350+ switch ( mw.config.get( 'skin' ) ) {
 351+ case 'standard' :
 352+ case 'cologneblue' :
 353+ $( '#quickbar' ).append( $link.after( '<br />' ) );
 354+ return $link[0];
 355+ case 'nostalgia' :
 356+ $( '#searchform' ).before( $link).before( ' &#124; ' );
 357+ return $link[0];
 358+ default : // Skins like chick, modern, monobook, myskin, simple, vector...
 359+
 360+ // Select the specified portlet
 361+ var $portlet = $( '#' + portlet );
 362+ if ( $portlet.length === 0 ) {
 363+ return null;
 364+ }
 365+ // Select the first (most likely only) unordered list inside the portlet
 366+ var $ul = $portlet.find( 'ul' );
 367+
 368+ // If it didn't have an unordered list yet, create it
 369+ if ( $ul.length === 0 ) {
 370+ // If there's no <div> inside, append it to the portlet directly
 371+ if ( $portlet.find( 'div:first' ).length === 0 ) {
 372+ $portlet.append( '<ul></ul>' );
 373+ } else {
 374+ // otherwise if there's a div (such as div.body or div.pBody)
 375+ // append the <ul> to last (most likely only) div
 376+ $portlet.find( 'div' ).eq( -1 ).append( '<ul></ul>' );
 377+ }
 378+ // Select the created element
 379+ $ul = $portlet.find( 'ul' ).eq( 0 );
 380+ }
 381+ // Just in case..
 382+ if ( $ul.length === 0 ) {
 383+ return null;
 384+ }
 385+
 386+ // Unhide portlet if it was hidden before
 387+ $portlet.removeClass( 'emptyPortlet' );
 388+
 389+ // Wrap the anchor tag in a <span> and create a list item for it
 390+ // and back up the selector to the list item
 391+ var $item = $link.wrap( '<li><span></span></li>' ).parent().parent();
 392+
 393+ // Implement the properties passed to the function
 394+ if ( id ) {
 395+ $item.attr( 'id', id );
 396+ }
 397+ if ( accesskey ) {
 398+ $link.attr( 'accesskey', accesskey );
 399+ tooltip += ' [' + accesskey + ']';
 400+ $link.attr( 'title', tooltip );
 401+ }
 402+ if ( accesskey && tooltip ) {
 403+ this.updateTooltipAccessKeys( $link );
 404+ }
 405+
 406+ // Where to put our node ?
 407+ // - nextnode is a DOM element (before MW 1.17, in wikibits.js, this was the only option)
 408+ if ( nextnode && nextnode.parentNode == $ul[0] ) {
 409+ $(nextnode).before( $item );
 410+
 411+ // - nextnode is a CSS selector for jQuery
 412+ } else if ( typeof nextnode == 'string' && $ul.find( nextnode ).length !== 0 ) {
 413+ $ul.find( nextnode ).eq( 0 ).before( $item );
 414+
 415+
 416+ // If the jQuery selector isn't found within the <ul>,
 417+ // or if nextnode was invalid or not passed at all,
 418+ // then just append it at the end of the <ul> (this is the default behaviour)
 419+ } else {
 420+ $ul.append( $item );
 421+ }
 422+
 423+
 424+ return $item[0];
 425+ }
 426+ },
 427+
 428+ /**
 429+ * Add a little box at the top of the screen to inform the user of
 430+ * something, replacing any previous message.
 431+ * Calling with no arguments, with an empty string or null will hide the message
 432+ *
 433+ * @param message mixed The DOM-element or HTML-string to be put inside the message box.
 434+ * @param className string Used in adding a class; should be different for each call
 435+ * to allow CSS/JS to hide different boxes. null = no class used.
 436+ * @return boolean True on success, false on failure.
 437+ */
 438+ 'jsMessage' : function( message, className ) {
 439+
 440+ if ( !arguments.length || message === '' || message === null ) {
 441+
 442+ $( '#mw-js-message' ).empty().hide();
 443+ return true; // Emptying and hiding message is intended behaviour, return true
 444+
 445+ } else {
 446+ // We special-case skin structures provided by the software. Skins that
 447+ // choose to abandon or significantly modify our formatting can just define
 448+ // an mw-js-message div to start with.
 449+ var $messageDiv = $( '#mw-js-message' );
 450+ if ( !$messageDiv.length ) {
 451+ $messageDiv = $( '<div id="mw-js-message">' );
 452+ if ( mw.util.$content.parent().length ) {
 453+ mw.util.$content.parent().prepend( $messageDiv );
 454+ } else {
 455+ return false;
 456+ }
 457+ }
 458+
 459+ if ( className ) {
 460+ $messageDiv.attr( 'class', 'mw-js-message-' + className );
 461+ }
 462+
 463+ if ( typeof message === 'object' ) {
 464+ $messageDiv.empty();
 465+ $messageDiv.append( message ); // Append new content
 466+ } else {
 467+ $messageDiv.html( message );
 468+ }
 469+
 470+ $messageDiv.slideDown();
 471+ return true;
 472+ }
 473+ },
 474+
 475+ /**
 476+ * Validate a string as representing a valid e-mail address
 477+ * according to HTML5 specification. Please note the specification
 478+ * does not validate a domain with one character.
 479+ *
 480+ * @todo FIXME: should be moved to or replaced by a JavaScript validation module.
 481+ *
 482+ * @param mailtxt string E-mail address to be validated.
 483+ * @return mixed Null if mailtxt was an empty string, otherwise true/false
 484+ * is determined by validation.
 485+ */
 486+ 'validateEmail' : function( mailtxt ) {
 487+ if( mailtxt === '' ) {
 488+ return null;
 489+ }
 490+
 491+ /**
 492+ * HTML5 defines a string as valid e-mail address if it matches
 493+ * the ABNF:
 494+ * 1 * ( atext / "." ) "@" ldh-str 1*( "." ldh-str )
 495+ * With:
 496+ * - atext : defined in RFC 5322 section 3.2.3
 497+ * - ldh-str : defined in RFC 1034 section 3.5
 498+ *
 499+ * (see STD 68 / RFC 5234 http://tools.ietf.org/html/std68):
 500+ */
 501+
 502+ /**
 503+ * First, define the RFC 5322 'atext' which is pretty easy :
 504+ * atext = ALPHA / DIGIT / ; Printable US-ASCII
 505+ "!" / "#" / ; characters not including
 506+ "$" / "%" / ; specials. Used for atoms.
 507+ "&" / "'" /
 508+ "*" / "+" /
 509+ "-" / "/" /
 510+ "=" / "?" /
 511+ "^" / "_" /
 512+ "`" / "{" /
 513+ "|" / "}" /
 514+ "~"
 515+ */
 516+ var rfc5322_atext = "a-z0-9!#$%&'*+\\-/=?^_`{|}~",
 517+
 518+ /**
 519+ * Next define the RFC 1034 'ldh-str'
 520+ * <domain> ::= <subdomain> | " "
 521+ * <subdomain> ::= <label> | <subdomain> "." <label>
 522+ * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
 523+ * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
 524+ * <let-dig-hyp> ::= <let-dig> | "-"
 525+ * <let-dig> ::= <letter> | <digit>
 526+ */
 527+ rfc1034_ldh_str = "a-z0-9\\-",
 528+
 529+ HTML5_email_regexp = new RegExp(
 530+ // start of string
 531+ '^'
 532+ +
 533+ // User part which is liberal :p
 534+ '[' + rfc5322_atext + '\\.]+'
 535+ +
 536+ // 'at'
 537+ '@'
 538+ +
 539+ // Domain first part
 540+ '[' + rfc1034_ldh_str + ']+'
 541+ +
 542+ // Optional second part and following are separated by a dot
 543+ '(?:\\.[' + rfc1034_ldh_str + ']+)*'
 544+ +
 545+ // End of string
 546+ '$',
 547+ // RegExp is case insensitive
 548+ 'i'
 549+ );
 550+ return (null !== mailtxt.match( HTML5_email_regexp ) );
 551+ },
 552+
 553+ /**
 554+ * Note: borrows from IP::isIPv4
 555+ *
 556+ * @param address string
 557+ * @param allowBlock boolean
 558+ * @return boolean
 559+ */
 560+ 'isIPv4Address' : function( address, allowBlock ) {
 561+ var block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
 562+ var RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
 563+ var RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
 564+ return typeof address === 'string' && address.search( new RegExp( '^' + RE_IP_ADD + block + '$' ) ) != -1;
 565+ },
 566+ /**
 567+ * Note: borrows from IP::isIPv6
 568+ *
 569+ * @param address string
 570+ * @param allowBlock boolean
 571+ * @return boolean
 572+ */
 573+ 'isIPv6Address' : function( address, allowBlock ) {
 574+ if ( typeof address !== 'string' ) {
 575+ return false;
 576+ }
 577+ var block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '';
 578+ var RE_IPV6_ADD =
 579+ '(?:' + // starts with "::" (including "::")
 580+ ':(?::|(?::' + '[0-9A-Fa-f]{1,4}' + '){1,7})' +
 581+ '|' + // ends with "::" (except "::")
 582+ '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){0,6}::' +
 583+ '|' + // contains no "::"
 584+ '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){7}' +
 585+ ')';
 586+ if ( address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) != -1 ) {
 587+ return true;
 588+ }
 589+ RE_IPV6_ADD = // contains one "::" in the middle (single '::' check below)
 590+ '[0-9A-Fa-f]{1,4}' + '(?:::?' + '[0-9A-Fa-f]{1,4}' + '){1,6}';
 591+ return address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) != -1
 592+ && address.search( /::/ ) != -1 && address.search( /::.*::/ ) == -1;
 593+ }
 594+
 595+ };
 596+
 597+ mw.util.init();
 598+
 599+} )( jQuery, mediaWiki );
\ No newline at end of file
Property changes on: trunk/phase3/resources/mediawiki/mediawiki.util.js
___________________________________________________________________
Added: svn:eol-style
1600 + native
Index: trunk/phase3/tests/qunit/index.html
@@ -25,7 +25,7 @@
2626 <script src="../../resources/jquery/jquery.makeCollapsible.js"></script>
2727 <script src="../../resources/jquery/jquery.mwPrototypes.js"></script>
2828 <script src="../../resources/jquery/jquery.placeholder.js"></script>
29 - <script src="../../resources/mediawiki.util/mediawiki.util.js"></script>
 29+ <script src="../../resources/mediawiki/mediawiki.util.js"></script>
3030
3131 <!-- MW: user.options -->
3232 <script>
@@ -47,7 +47,7 @@
4848
4949 <script src="suites/resources/jquery/jquery.client.js"></script>
5050 <script src="suites/resources/jquery/jquery.mwPrototypes.js"></script>
51 - <script src="suites/resources/mediawiki.util/mediawiki.util.js"></script>
 51+ <script src="suites/resources/mediawiki/mediawiki.util.js"></script>
5252
5353 <script src="suites/resources/jquery/jquery.autoEllipsis.js"></script>
5454 <script src="suites/resources/jquery/jquery.colorUtil.js"></script>
Index: trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.util.js
@@ -0,0 +1,265 @@
 2+module( 'mediawiki.util.js' );
 3+
 4+test( '-- Initial check', function() {
 5+ expect(1);
 6+
 7+ ok( mw.util, 'mw.util defined' );
 8+});
 9+
 10+test( 'rawurlencode', function() {
 11+ expect(1);
 12+
 13+ equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
 14+});
 15+
 16+test( 'wikiUrlencode', function() {
 17+ expect(1);
 18+
 19+ equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
 20+});
 21+
 22+test( 'wikiGetlink', function() {
 23+ expect(2);
 24+
 25+ // Not part of startUp module
 26+ mw.config.set( 'wgArticlePath', '/wiki/$1' );
 27+
 28+ var hrefA = mw.util.wikiGetlink( 'Sandbox' );
 29+ equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
 30+
 31+ var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
 32+ equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
 33+ 'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
 34+});
 35+
 36+test( 'wikiScript', function() {
 37+ expect(2);
 38+
 39+ mw.config.set({
 40+ 'wgScript': '/w/index.php',
 41+ 'wgScriptPath': '/w',
 42+ 'wgScriptExtension': '.php'
 43+ });
 44+
 45+ equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
 46+ equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
 47+
 48+});
 49+
 50+test( 'addCSS', function() {
 51+ expect(3);
 52+
 53+ var a = mw.util.addCSS( '#bodyContent { visibility: hidden; }' );
 54+ equal( typeof a, 'object', 'addCSS returned an object' );
 55+ strictEqual( a.disabled, false, 'property "disabled" is available and set to false' );
 56+
 57+ var $b = $('#bodyContent');
 58+ equal( $b.css('visibility'), 'hidden', 'Added style properties are in effect' );
 59+
 60+ // Clean up
 61+ $( a.ownerNode ).remove();
 62+});
 63+
 64+test( 'toggleToc', function() {
 65+ expect(3);
 66+
 67+ strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
 68+
 69+ var tocHtml =
 70+ '<table id="toc" class="toc"><tr><td>' +
 71+ '<div id="toctitle">' +
 72+ '<h2>Contents</h2>' +
 73+ '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
 74+ '</div>' +
 75+ '<ul><li></li></ul>' +
 76+ '</td></tr></table>';
 77+ var $toc = $(tocHtml).appendTo( 'body' );
 78+ var $toggleLink = $( '#togglelink' );
 79+
 80+ // Toggle animation is asynchronous
 81+ // QUnit should not finish this test() untill they are all done
 82+ stop();
 83+
 84+ var actionC = function() {
 85+ start();
 86+
 87+ // Clean up
 88+ $toc.remove();
 89+ };
 90+ var actionB = function() {
 91+ strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
 92+ };
 93+ var actionA = function() {
 94+ strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
 95+ };
 96+
 97+ actionA();
 98+});
 99+
 100+test( 'getParamValue', function() {
 101+ expect(2);
 102+
 103+ var url = 'http://mediawiki.org/?foo=wrong&foo=right#&foo=bad';
 104+
 105+ equal( mw.util.getParamValue( 'foo', url ), 'right', 'Use latest one, ignore hash' );
 106+ strictEqual( mw.util.getParamValue( 'bar', url ), null, 'Return null when not found' );
 107+});
 108+
 109+test( 'tooltipAccessKey', function() {
 110+ expect(3);
 111+
 112+ equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
 113+ ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
 114+ ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
 115+});
 116+
 117+test( '$content', function() {
 118+ expect(2);
 119+
 120+ ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
 121+ strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
 122+});
 123+
 124+test( 'addPortletLink', function() {
 125+ expect(5);
 126+
 127+ var A = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/ResourceLoader',
 128+ 'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l', '#t-specialpages' );
 129+
 130+ ok( $.isDomElement( A ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
 131+
 132+ var B = mw.util.addPortletLink( "p-tb", "http://mediawiki.org/",
 133+ "MediaWiki.org", "t-mworg", "Go to MediaWiki.org ", "m", A );
 134+
 135+ equal( $( B ).attr( 'id' ), 't-mworg', 'Link has correct ID set' );
 136+ equal( $( B ).closest( '.portal' ).attr( 'id' ), 'p-tb', 'Link was inserted within correct portlet' );
 137+ equal( $( B ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
 138+
 139+ var C = mw.util.addPortletLink( "p-tb", "http://mediawiki.org/wiki/RL/DM",
 140+ "Default modules", "t-rldm", "List of all default modules ", "d", "#t-rl" );
 141+
 142+ equal( $( C ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
 143+
 144+ // Clean up
 145+ $( [A, B, C] ).remove();
 146+});
 147+
 148+test( 'jsMessage', function() {
 149+ expect(1);
 150+
 151+ var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
 152+ ok( a, 'Basic checking of return value' );
 153+
 154+ // Clean up
 155+ $( '#mw-js-message' ).remove();
 156+});
 157+
 158+test( 'validateEmail', function() {
 159+ expect(6);
 160+
 161+ strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
 162+ strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
 163+
 164+ // testEmailWithCommasAreInvalids
 165+ strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
 166+ strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
 167+
 168+ // testEmailWithHyphens
 169+ strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
 170+ strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
 171+});
 172+
 173+test( 'isIPv6Address', function() {
 174+ expect(40);
 175+
 176+ // Shortcuts
 177+ var assertFalseIPv6 = function( addy, summary ) {
 178+ return strictEqual( mw.util.isIPv6Address( addy ), false, summary );
 179+ },
 180+ assertTrueIPv6 = function( addy, summary ) {
 181+ return strictEqual( mw.util.isIPv6Address( addy ), true, summary );
 182+ };
 183+
 184+ // Based on IPTest.php > testisIPv6
 185+ assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
 186+ assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
 187+ assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
 188+ assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
 189+
 190+ $.each(
 191+ ['fc:100::',
 192+ 'fc:100:a::',
 193+ 'fc:100:a:d::',
 194+ 'fc:100:a:d:1::',
 195+ 'fc:100:a:d:1:e::',
 196+ 'fc:100:a:d:1:e:ac::'], function( i, addy ){
 197+ assertTrueIPv6( addy, addy + ' is a valid IP' );
 198+ });
 199+
 200+ assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
 201+ assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
 202+
 203+ assertFalseIPv6( ':::' );
 204+ assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
 205+
 206+ assertTrueIPv6( '::', 'IPv6 zero address' );
 207+ $.each(
 208+ ['::0',
 209+ '::fc',
 210+ '::fc:100',
 211+ '::fc:100:a',
 212+ '::fc:100:a:d',
 213+ '::fc:100:a:d:1',
 214+ '::fc:100:a:d:1:e',
 215+ '::fc:100:a:d:1:e:ac',
 216+
 217+ 'fc:100:a:d:1:e:ac:0'], function( i, addy ){
 218+ assertTrueIPv6( addy, addy + ' is a valid IP' );
 219+ });
 220+
 221+ assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
 222+ assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
 223+
 224+ assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
 225+ assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
 226+ assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
 227+
 228+ assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
 229+ assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
 230+ assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
 231+ assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
 232+ assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
 233+ assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
 234+ assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
 235+ assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
 236+ assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
 237+
 238+ assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
 239+ assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
 240+});
 241+
 242+test( 'isIPv4Address', function() {
 243+ expect(11);
 244+
 245+ // Shortcuts
 246+ var assertFalseIPv4 = function( addy, summary ) {
 247+ return strictEqual( mw.util.isIPv4Address( addy ), false, summary );
 248+ },
 249+ assertTrueIPv4 = function( addy, summary ) {
 250+ return strictEqual( mw.util.isIPv4Address( addy ), true, summary );
 251+ };
 252+
 253+ // Based on IPTest.php > testisIPv4
 254+ assertFalseIPv4( false, 'Boolean false is not an IP' );
 255+ assertFalseIPv4( true, 'Boolean true is not an IP' );
 256+ assertFalseIPv4( '', 'Empty string is not an IP' );
 257+ assertFalseIPv4( 'abc', '"abc" is not an IP' );
 258+ assertFalseIPv4( ':', 'Colon is not an IP' );
 259+ assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
 260+ assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
 261+ assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
 262+
 263+ assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
 264+ assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
 265+ assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
 266+});
Property changes on: trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.util.js
___________________________________________________________________
Added: svn:eol-style
1267 + native

Status & tagging log