Merge "Fix permissions check to show "hide" buttons."
[lhc/web/wiklou.git] / resources / mediawiki / mediawiki.Title.js
index 985c5c9..6b6e586 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink
  */
-( function( $ ) {
+( function ( mw, $ ) {
 
        /* Local space */
 
         * @param namespace {Number} (optional) Namespace id. If given, title will be taken as-is.
         * @return {Title} this
         */
-var    Title = function( title, namespace ) {
-                       this._ns = 0; // integer namespace id
-                       this._name = null; // name in canonical 'database' form
-                       this._ext = null; // extension
-
-                       if ( arguments.length === 2 ) {
-                               setNameAndExtension( this, title );
-                               this._ns = fixNsId( namespace );
-                       } else if ( arguments.length === 1 ) {
-                               setAll( this, title );
-                       }
-                       return this;
-       },
+       function Title( title, namespace ) {
+               this.ns = 0; // integer namespace id
+               this.name = null; // name in canonical 'database' form
+               this.ext = null; // extension
+
+               if ( arguments.length === 2 ) {
+                       setNameAndExtension( this, title );
+                       this.ns = fixNsId( namespace );
+               } else if ( arguments.length === 1 ) {
+                       setAll( this, title );
+               }
+               return this;
+       }
 
+var
        /**
         * Strip some illegal chars: control chars, colon, less than, greater than,
         * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
@@ -41,7 +42,7 @@ var   Title = function( title, namespace ) {
         * @param s {String}
         * @return {String}
         */
-       clean = function( s ) {
+       clean = function ( s ) {
                if ( s !== undefined ) {
                        return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
                }
@@ -61,25 +62,25 @@ var Title = function( title, namespace ) {
        },
 
        /**
-        * Sanatize name.
+        * Sanitize name.
         */
-       fixName = function( s ) {
+       fixName = function ( s ) {
                return clean( $.trim( s ) );
        },
 
        /**
-        * Sanatize name.
+        * Sanitize name.
         */
-       fixExt = function( s ) {
-               return clean( s.toLowerCase() );
+       fixExt = function ( s ) {
+               return clean( s );
        },
 
        /**
-        * Sanatize namespace id.
+        * Sanitize namespace id.
         * @param id {Number} Namespace id.
         * @return {Number|Boolean} The id as-is or boolean false if invalid.
         */
-       fixNsId = function( id ) {
+       fixNsId = function ( id ) {
                // wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] )
                var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()];
 
@@ -98,9 +99,13 @@ var  Title = function( title, namespace ) {
         * @param ns {String} Namespace name (case insensitive, leading/trailing space ignored).
         * @return {Number|Boolean} Namespace id or boolean false if unrecognized.
         */
-       getNsIdByName = function( ns ) {
-               // toLowerCase throws exception on null/undefined. Return early.
-               if ( ns == null ) {
+       getNsIdByName = function ( ns ) {
+               // Don't cast non-strings to strings, because null or undefined
+               // should not result in returning the id of a potential namespace
+               // called "Null:" (e.g. on nullwiki.example.org)
+               // Also, toLowerCase throws exception on null/undefined, because
+               // it is a String.prototype method.
+               if ( typeof ns !== 'string' ) {
                        return false;
                }
                ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize
@@ -119,16 +124,22 @@ var       Title = function( title, namespace ) {
         * @param raw {String}
         * @return {mw.Title}
         */
-       setAll = function( title, s ) {
+       setAll = function ( title, s ) {
+               // In normal browsers the match-array contains null/undefined if there's no match,
+               // IE returns an empty string.
                var     matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ),
                        ns_match = getNsIdByName( matches[1] );
-               if ( matches.length && ns_match ) {
-                       if ( matches[1] ) { title._ns = ns_match; }
-                       if ( matches[2] ) { title._name = fixName( matches[2] ); }
-                       if ( matches[3] ) { title._ext = fixExt( matches[3] ); }
+
+               // Namespace must be valid, and title must be a non-empty string.
+               if ( ns_match && typeof matches[2] === 'string' && matches[2] !== '' ) {
+                       title.ns = ns_match;
+                       title.name = fixName( matches[2] );
+                       if ( typeof matches[3] === 'string' && matches[3] !== '' ) {
+                               title.ext = fixExt( matches[3] );
+                       }
                } else {
-                       // Consistency with MediaWiki: Unknown namespace > fallback to main namespace.
-                       title._ns = 0;
+                       // Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace.
+                       title.ns = 0;
                        setNameAndExtension( title, s );
                }
                return title;
@@ -141,27 +152,33 @@ var       Title = function( title, namespace ) {
         * @param raw {String}
         * @return {mw.Title}
         */
-       setNameAndExtension = function( title, raw ) {
+       setNameAndExtension = function ( title, raw ) {
+               // In normal browsers the match-array contains null/undefined if there's no match,
+               // IE returns an empty string.
                var matches = raw.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ );
-               if ( matches.length ) {
-                       if ( matches[1] ) { title._name = fixName( matches[1] ); }
-                       if ( matches[2] ) { title._ext = fixExt( matches[2] ); }
+
+               // Title must be a non-empty string.
+               if ( typeof matches[1] === 'string' && matches[1] !== '' ) {
+                       title.name = fixName( matches[1] );
+                       if ( typeof matches[2] === 'string' && matches[2] !== '' ) {
+                               title.ext = fixExt( matches[2] );
+                       }
                } else {
                        throw new Error( 'mw.Title: Could not parse title "' + raw + '"' );
                }
                return title;
        };
-        
+
 
        /* Static space */
 
        /**
-        * Wether this title exists on the wiki.
+        * Whether this title exists on the wiki.
         * @param title {mixed} prefixed db-key name (string) or instance of Title
         * @return {mixed} Boolean true/false if the information is available. Otherwise null.
         */
-       Title.exists = function( title ) {
-               var     type = $.type( title ), obj = Title.exist.pages, match;
+       Title.exists = function ( title ) {
+               var type = $.type( title ), obj = Title.exist.pages, match;
                if ( type === 'string' ) {
                        match = obj[title];
                } else if ( type === 'object' && title instanceof Title ) {
@@ -186,15 +203,15 @@ var       Title = function( title, namespace ) {
                pages: {},
                /**
                 * @example Declare existing titles: Title.exist.set(['User:John_Doe', ...]);
-                * @example Declare titles inexisting: Title.exist.set(['File:Foo_bar.jpg', ...], false);
+                * @example Declare titles nonexistent: Title.exist.set(['File:Foo_bar.jpg', ...], false);
                 * @param titles {String|Array} Title(s) in strict prefixedDb title form.
                 * @param state {Boolean} (optional) State of the given titles. Defaults to true.
                 * @return {Boolean}
                 */
-               set: function( titles, state ) {
+               set: function ( titles, state ) {
                        titles = $.isArray( titles ) ? titles : [titles];
                        state = state === undefined ? true : !!state;
-                       var     pages = this.pages, i, len = titles.length;
+                       var pages = this.pages, i, len = titles.length;
                        for ( i = 0; i < len; i++ ) {
                                pages[ titles[i] ] = state;
                        }
@@ -211,8 +228,8 @@ var Title = function( title, namespace ) {
                 * Get the namespace number.
                 * @return {Number}
                 */
-               getNamespaceId: function(){
-                       return this._ns;
+               getNamespaceId: function (){
+                       return this.ns;
                },
 
                /**
@@ -220,19 +237,19 @@ var       Title = function( title, namespace ) {
                 * In NS_MAIN this is '', otherwise namespace name plus ':'
                 * @return {String}
                 */
-               getNamespacePrefix: function(){
-                       return mw.config.get( 'wgFormattedNamespaces' )[this._ns].replace( / /g, '_' ) + (this._ns === 0 ? '' : ':');
+               getNamespacePrefix: function (){
+                       return mw.config.get( 'wgFormattedNamespaces' )[this.ns].replace( / /g, '_' ) + (this.ns === 0 ? '' : ':');
                },
 
                /**
                 * The name, like "Foo_bar"
                 * @return {String}
                 */
-               getName: function() {
-                       if ( $.inArray( this._ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
-                               return this._name;
+               getName: function () {
+                       if ( $.inArray( this.ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
+                               return this.name;
                        } else {
-                               return $.ucFirst( this._name );
+                               return $.ucFirst( this.name );
                        }
                },
 
@@ -240,7 +257,7 @@ var Title = function( title, namespace ) {
                 * The name, like "Foo bar"
                 * @return {String}
                 */
-               getNameText: function() {
+               getNameText: function () {
                        return text( this.getName() );
                },
 
@@ -248,7 +265,7 @@ var Title = function( title, namespace ) {
                 * Get full name in prefixed DB form, like File:Foo_bar.jpg,
                 * most useful for API calls, anything that must identify the "title".
                 */
-               getPrefixedDb: function() {
+               getPrefixedDb: function () {
                        return this.getNamespacePrefix() + this.getMain();
                },
 
@@ -256,7 +273,7 @@ var Title = function( title, namespace ) {
                 * Get full name in text form, like "File:Foo bar.jpg".
                 * @return {String}
                 */
-               getPrefixedText: function() {
+               getPrefixedText: function () {
                        return text( this.getPrefixedDb() );
                },
 
@@ -264,7 +281,7 @@ var Title = function( title, namespace ) {
                 * The main title (without namespace), like "Foo_bar.jpg"
                 * @return {String}
                 */
-               getMain: function() {
+               getMain: function () {
                        return this.getName() + this.getDotExtension();
                },
 
@@ -272,7 +289,7 @@ var Title = function( title, namespace ) {
                 * The "text" form, like "Foo bar.jpg"
                 * @return {String}
                 */
-               getMainText: function() {
+               getMainText: function () {
                        return text( this.getMain() );
                },
 
@@ -280,31 +297,31 @@ var       Title = function( title, namespace ) {
                 * Get the extension (returns null if there was none)
                 * @return {String|null} extension
                 */
-               getExtension: function() {
-                       return this._ext;
+               getExtension: function () {
+                       return this.ext;
                },
 
                /**
                 * Convenience method: return string like ".jpg", or "" if no extension
                 * @return {String}
                 */
-               getDotExtension: function() {
-                       return this._ext === null ? '' : '.' + this._ext;
+               getDotExtension: function () {
+                       return this.ext === null ? '' : '.' + this.ext;
                },
 
                /**
                 * Return the URL to this title
                 * @return {String}
                 */
-               getUrl: function() {
+               getUrl: function () {
                        return mw.util.wikiGetlink( this.toString() );
                },
 
                /**
-                * Wether this title exists on the wiki.
+                * Whether this title exists on the wiki.
                 * @return {mixed} Boolean true/false if the information is available. Otherwise null.
                 */
-               exists: function() {
+               exists: function () {
                        return Title.exists( this );
                }
        };
@@ -319,4 +336,4 @@ var Title = function( title, namespace ) {
        // Expose
        mw.Title = Title;
 
-})(jQuery);
+}( mediaWiki, jQuery ) );