r70169 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70168‎ | r70169 | r70170 >
Date:20:08, 29 July 2010
Author:tparscal
Status:deferred
Tags:
Comment:
Moved client and url utilities to optional modules
Modified paths:
  • /branches/resourceloader/phase3/resources/mw/mw.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.client.js (added) (history)
  • /branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.url.js (added) (history)

Diff [purge]

Index: branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.client.js
@@ -0,0 +1,185 @@
 2+/*
 3+ * User-agent detection
 4+ */
 5+
 6+( function( $, mw ) {
 7+
 8+/* Extension */
 9+
 10+$.extend( mw.utilities, {
 11+ 'client': {
 12+ /**
 13+ * Returns an object containing information about the browser
 14+ *
 15+ * The resulting client object will be in the following format:
 16+ * {
 17+ * 'name': 'firefox',
 18+ * 'layout': 'gecko',
 19+ * 'os': 'linux'
 20+ * 'version': '3.5.1',
 21+ * 'versionBase': '3',
 22+ * 'versionNumber': 3.5,
 23+ * }
 24+ */
 25+ this.profile = function() {
 26+ // Use the cached version if possible
 27+ if ( typeof this.profile === 'undefined' ) {
 28+
 29+ /* Configuration */
 30+
 31+ // Name of browsers or layout engines we don't recognize
 32+ var uk = 'unknown';
 33+ // Generic version digit
 34+ var x = 'x';
 35+ // Strings found in user agent strings that need to be conformed
 36+ var wildUserAgents = [ 'Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'];
 37+ // Translations for conforming user agent strings
 38+ var userAgentTranslations = [
 39+ // Tons of browsers lie about being something they are not
 40+ [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
 41+ // Chrome lives in the shadow of Safari still
 42+ ['Chrome Safari', 'Chrome'],
 43+ // KHTML is the layout engine not the browser - LIES!
 44+ ['KHTML', 'Konqueror'],
 45+ // Firefox nightly builds
 46+ ['Minefield', 'Firefox'],
 47+ // This helps keep differnt versions consistent
 48+ ['Navigator', 'Netscape'],
 49+ // This prevents version extraction issues, otherwise translation would happen later
 50+ ['PLAYSTATION 3', 'PS3'],
 51+ ];
 52+ // Strings which precede a version number in a user agent string - combined and used as match 1 in
 53+ // version detectection
 54+ var versionPrefixes = [
 55+ 'camino', 'chrome', 'firefox', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', 'lynx',
 56+ 'msie', 'safari', 'ps3'
 57+ ];
 58+ // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
 59+ var versionSuffix = '(\/|\;?\s|)([a-z0-9\.\+]*?)(\;|dev|rel|\\)|\s|$)';
 60+ // Names of known browsers
 61+ var browserNames = [
 62+ 'camino', 'chrome', 'firefox', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', 'safari', 'ipod',
 63+ 'iphone', 'blackberry', 'ps3'
 64+ ];
 65+ // Tanslations for conforming browser names
 66+ var browserTranslations = [];
 67+ // Names of known layout engines
 68+ var layoutNames = ['gecko', 'konqueror', 'msie', 'opera', 'webkit'];
 69+ // Translations for conforming layout names
 70+ var layoutTranslations = [['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto']];
 71+ // Names of known operating systems
 72+ var osNames = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'];
 73+ // Translations for conforming operating system names
 74+ var osTranslations = [['sunos', 'solaris']];
 75+
 76+ /* Methods */
 77+
 78+ // Performs multiple replacements on a string
 79+ function translate( source, translations ) {
 80+ for ( var i = 0; i < translations.length; i++ ) {
 81+ source = source.replace( translations[i][0], translations[i][1] );
 82+ }
 83+ return source;
 84+ };
 85+
 86+ /* Pre-processing */
 87+
 88+ var userAgent = navigator.userAgent, match, browser = uk, layout = uk, os = uk, version = x;
 89+ if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( userAgent ) ) {
 90+ // Takes a userAgent string and translates given text into something we can more easily work with
 91+ userAgent = translate( userAgent, userAgentTranslations );
 92+ }
 93+ // Everything will be in lowercase from now on
 94+ userAgent = userAgent.toLowerCase();
 95+
 96+ /* Extraction */
 97+
 98+ if ( match = new RegExp( '(' + browserNames.join( '|' ) + ')' ).exec( userAgent ) ) {
 99+ browser = translate( match[1], browserTranslations );
 100+ }
 101+ if ( match = new RegExp( '(' + layoutNames.join( '|' ) + ')' ).exec( userAgent ) ) {
 102+ layout = translate( match[1], layoutTranslations );
 103+ }
 104+ if ( match = new RegExp( '(' + osNames.join( '|' ) + ')' ).exec( navigator.platform.toLowerCase() ) ) {
 105+ var os = translate( match[1], osTranslations );
 106+ }
 107+ if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( userAgent ) ) {
 108+ version = match[3];
 109+ }
 110+
 111+ /* Edge Cases -- did I mention about how user agent string lie? */
 112+
 113+ // Decode Safari's crazy 400+ version numbers
 114+ if ( name.match( /safari/ ) && version > 400 ) {
 115+ version = '2.0';
 116+ }
 117+ // Expose Opera 10's lies about being Opera 9.8
 118+ if ( name === 'opera' && version >= 9.8) {
 119+ version = userAgent.match( /version\/([0-9\.]*)/i )[1] || 10;
 120+ }
 121+
 122+ /* Caching */
 123+
 124+ this.profile = {
 125+ 'browser': browser,
 126+ 'layout': layout,
 127+ 'os': os,
 128+ 'version': version,
 129+ 'versionBase': ( version !== x ? new String( version ).substr( 0, 1 ) : x ),
 130+ 'versionNumber': ( parseFloat( version, 10 ) || 0.0 )
 131+ };
 132+ }
 133+ return this.profile;
 134+ };
 135+ /**
 136+ * Checks the current browser against a support map object to determine if the browser has been black-listed or
 137+ * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
 138+ * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
 139+ *
 140+ * A browser map is in the following format:
 141+ * {
 142+ * 'ltr': {
 143+ * // Multiple rules with configurable operators
 144+ * 'msie': [['>=', 7], ['!=', 9]],
 145+ * // Blocked entirely
 146+ * 'iphone': false
 147+ * },
 148+ * 'rtl': {
 149+ * // Test against a string
 150+ * 'msie': [['!==', '8.1.2.3']],
 151+ * // RTL rules do not fall through to LTR rules, you must explicity set each of them
 152+ * 'iphone': false
 153+ * }
 154+ * }
 155+ *
 156+ * @param map Object of browser support map
 157+ *
 158+ * @return Boolean true if browser known or assumed to be supported, false if blacklisted
 159+ */
 160+ this.test = function( map ) {
 161+ var client = this.client();
 162+ // Check over each browser condition to determine if we are running in a compatible client
 163+ var browser = map[$( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'][client.browser];
 164+ if ( typeof browser !== 'object' ) {
 165+ // Unknown, so we assume it's working
 166+ return true;
 167+ }
 168+ for ( var condition in browser ) {
 169+ var op = browser[condition][0];
 170+ var val = browser[condition][1];
 171+ if ( val === false ) {
 172+ return false;
 173+ } else if ( typeof val == 'string' ) {
 174+ if ( !( eval( 'client.version' + op + '"' + val + '"' ) ) ) {
 175+ return false;
 176+ }
 177+ } else if ( typeof val == 'number' ) {
 178+ if ( !( eval( 'client.versionNumber' + op + val ) ) ) {
 179+ return false;
 180+ }
 181+ }
 182+ }
 183+ return true;
 184+ };
 185+ }
 186+} );
\ No newline at end of file
Property changes on: branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.client.js
___________________________________________________________________
Added: svn:eol-style
1187 + native
Index: branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.url.js
@@ -0,0 +1,76 @@
 2+/*
 3+ *
 4+ */
 5+
 6+( function( $, mw ) {
 7+
 8+/* Extension */
 9+
 10+$.extend( mw.utilities, {
 11+ 'url': {
 12+ /**
 13+ * Builds a url string from an object containing any of the following components:
 14+ *
 15+ * Component Example
 16+ * scheme "http"
 17+ * server "www.domain.com"
 18+ * path "path/to/my/file.html"
 19+ * query "this=thåt" or { 'this': 'thåt' }
 20+ * fragment "place_on_the_page"
 21+ *
 22+ * Results in: "http://www.domain.com/path/to/my/file.html?this=th%C3%A5t#place_on_the_page"
 23+ *
 24+ * All arguments to this function are assumed to be URL-encoded already, except for the
 25+ * query parameter if provided in object form.
 26+ */
 27+ 'buildUrlString': function( components ) {
 28+ var url = '';
 29+ if ( typeof components.scheme === 'string' ) {
 30+ url += components.scheme + '://';
 31+ }
 32+ if ( typeof components.server === 'string' ) {
 33+ url += components.server + '/';
 34+ }
 35+ if ( typeof components.path === 'string' ) {
 36+ url += components.path;
 37+ }
 38+ if ( typeof components.query === 'string' ) {
 39+ url += '?' + components.query;
 40+ } else if ( typeof components.query === 'object' ) {
 41+ url += '?' + that.buildQueryString( components.query );
 42+ }
 43+ if ( typeof components.fragment === 'string' ) {
 44+ url += '#' + components.fragment;
 45+ }
 46+ return url;
 47+ },
 48+ /**
 49+ * RFC 3986 compliant URI component encoder - with identical behavior as PHP's urlencode function. Note: PHP's
 50+ * urlencode function prior to version 5.3 also escapes tildes, this does not. The naming here is not the same
 51+ * as PHP because PHP can't decide out to name things (underscores sometimes?), much less set a reasonable
 52+ * precedence for how things should be named in other environments. We use camelCase and action-subject here.
 53+ */
 54+ 'encodeUrlComponent': function( string ) {
 55+ return encodeURIComponent( new String( string ) )
 56+ .replace(/!/g, '%21')
 57+ .replace(/'/g, '%27')
 58+ .replace(/\(/g, '%28')
 59+ .replace(/\)/g, '%29')
 60+ .replace(/\*/g, '%2A')
 61+ .replace(/%20/g, '+');
 62+ },
 63+ /**
 64+ * Builds a query string from an object with key and values
 65+ */
 66+ 'buildQueryString': function( parameters ) {
 67+ if ( typeof parameters === 'object' ) {
 68+ var parts = [];
 69+ for ( var p in parameters ) {
 70+ parts[parts.length] = that.encodeUrlComponent( p ) + '=' + that.encodeUrlComponent( parameters[p] );
 71+ }
 72+ return parts.join( '&' );
 73+ }
 74+ return '';
 75+ }
 76+ }
 77+} );
\ No newline at end of file
Property changes on: branches/resourceloader/phase3/resources/mw/utilities/mw.utilities.url.js
___________________________________________________________________
Added: svn:eol-style
178 + native
Index: branches/resourceloader/phase3/resources/mw/mw.js
@@ -387,10 +387,7 @@
388388 var html = '';
389389 for ( r in requests ) {
390390 // Build out the HTML
391 - var src = mw.util.buildUrlString( {
392 - 'path': mw.config.get( 'wgScriptPath' ) + '/load.php',
393 - 'query': requests[r]
394 - } );
 391+ var src = mw.config.get( 'wgScriptPath' ) + '/load.php?' + jQuery.param( requests[r] );
395392 html += '<script type="text/javascript" src="' + src + '"></script>';
396393 }
397394 // Append script to head
@@ -495,257 +492,11 @@
496493 that.work();
497494 } );
498495 } )();
499 - /**
500 - * General purpose utilities
501 - */
502 - this.util = new ( function() {
503 -
504 - /* Private Members */
505 -
506 - var that = this;
507 - // Decoded user agent string cache
508 - var client = null;
509 -
510 - /* Public Methods */
511 -
512 - /**
513 - * Builds a url string from an object containing any of the following components:
514 - *
515 - * Component Example
516 - * scheme "http"
517 - * server "www.domain.com"
518 - * path "path/to/my/file.html"
519 - * query "this=thåt" or { 'this': 'thåt' }
520 - * fragment "place_on_the_page"
521 - *
522 - * Results in: "http://www.domain.com/path/to/my/file.html?this=th%C3%A5t#place_on_the_page"
523 - *
524 - * All arguments to this function are assumed to be URL-encoded already, except for the
525 - * query parameter if provided in object form.
526 - */
527 - this.buildUrlString = function( components ) {
528 - var url = '';
529 - if ( typeof components.scheme === 'string' ) {
530 - url += components.scheme + '://';
531 - }
532 - if ( typeof components.server === 'string' ) {
533 - url += components.server + '/';
534 - }
535 - if ( typeof components.path === 'string' ) {
536 - url += components.path;
537 - }
538 - if ( typeof components.query === 'string' ) {
539 - url += '?' + components.query;
540 - } else if ( typeof components.query === 'object' ) {
541 - url += '?' + that.buildQueryString( components.query );
542 - }
543 - if ( typeof components.fragment === 'string' ) {
544 - url += '#' + components.fragment;
545 - }
546 - return url;
547 - };
548 - /**
549 - * RFC 3986 compliant URI component encoder - with identical behavior as PHP's urlencode function. Note: PHP's
550 - * urlencode function prior to version 5.3 also escapes tildes, this does not. The naming here is not the same
551 - * as PHP because PHP can't decide out to name things (underscores sometimes?), much less set a reasonable
552 - * precedence for how things should be named in other environments. We use camelCase and action-subject here.
553 - */
554 - this.encodeUrlComponent = function( string ) {
555 - return encodeURIComponent( new String( string ) )
556 - .replace(/!/g, '%21')
557 - .replace(/'/g, '%27')
558 - .replace(/\(/g, '%28')
559 - .replace(/\)/g, '%29')
560 - .replace(/\*/g, '%2A')
561 - .replace(/%20/g, '+');
562 - };
563 - /**
564 - * Builds a query string from an object with key and values
565 - */
566 - this.buildQueryString = function( parameters ) {
567 - if ( typeof parameters === 'object' ) {
568 - var parts = [];
569 - for ( var p in parameters ) {
570 - parts[parts.length] = that.encodeUrlComponent( p ) + '=' + that.encodeUrlComponent( parameters[p] );
571 - }
572 - return parts.join( '&' );
573 - }
574 - return '';
575 - };
576 - /**
577 - * Returns an object containing information about the browser
578 - *
579 - * The resulting client object will be in the following format:
580 - * {
581 - * 'name': 'firefox',
582 - * 'layout': 'gecko',
583 - * 'os': 'linux'
584 - * 'version': '3.5.1',
585 - * 'versionBase': '3',
586 - * 'versionNumber': 3.5,
587 - * }
588 - */
589 - this.client = function() {
590 - // Use the cached version if possible
591 - if ( client === null ) {
592 -
593 - /* Configuration */
594 -
595 - // Name of browsers or layout engines we don't recognize
596 - var uk = 'unknown';
597 - // Generic version digit
598 - var x = 'x';
599 - // Strings found in user agent strings that need to be conformed
600 - var wildUserAgents = [ 'Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'];
601 - // Translations for conforming user agent strings
602 - var userAgentTranslations = [
603 - // Tons of browsers lie about being something they are not
604 - [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
605 - // Chrome lives in the shadow of Safari still
606 - ['Chrome Safari', 'Chrome'],
607 - // KHTML is the layout engine not the browser - LIES!
608 - ['KHTML', 'Konqueror'],
609 - // Firefox nightly builds
610 - ['Minefield', 'Firefox'],
611 - // This helps keep differnt versions consistent
612 - ['Navigator', 'Netscape'],
613 - // This prevents version extraction issues, otherwise translation would happen later
614 - ['PLAYSTATION 3', 'PS3'],
615 - ];
616 - // Strings which precede a version number in a user agent string - combined and used as match 1 in
617 - // version detectection
618 - var versionPrefixes = [
619 - 'camino', 'chrome', 'firefox', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', 'lynx',
620 - 'msie', 'safari', 'ps3'
621 - ];
622 - // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
623 - var versionSuffix = '(\/|\;?\s|)([a-z0-9\.\+]*?)(\;|dev|rel|\\)|\s|$)';
624 - // Names of known browsers
625 - var browserNames = [
626 - 'camino', 'chrome', 'firefox', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', 'safari', 'ipod',
627 - 'iphone', 'blackberry', 'ps3'
628 - ];
629 - // Tanslations for conforming browser names
630 - var browserTranslations = [];
631 - // Names of known layout engines
632 - var layoutNames = ['gecko', 'konqueror', 'msie', 'opera', 'webkit'];
633 - // Translations for conforming layout names
634 - var layoutTranslations = [['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto']];
635 - // Names of known operating systems
636 - var osNames = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'];
637 - // Translations for conforming operating system names
638 - var osTranslations = [['sunos', 'solaris']];
639 -
640 - /* Methods */
641 -
642 - // Performs multiple replacements on a string
643 - function translate( source, translations ) {
644 - for ( var i = 0; i < translations.length; i++ ) {
645 - source = source.replace( translations[i][0], translations[i][1] );
646 - }
647 - return source;
648 - };
649 -
650 - /* Pre-processing */
651 -
652 - var userAgent = navigator.userAgent, match, browser = uk, layout = uk, os = uk, version = x;
653 - if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( userAgent ) ) {
654 - // Takes a userAgent string and translates given text into something we can more easily work with
655 - userAgent = translate( userAgent, userAgentTranslations );
656 - }
657 - // Everything will be in lowercase from now on
658 - userAgent = userAgent.toLowerCase();
659 -
660 - /* Extraction */
661 -
662 - if ( match = new RegExp( '(' + browserNames.join( '|' ) + ')' ).exec( userAgent ) ) {
663 - browser = translate( match[1], browserTranslations );
664 - }
665 - if ( match = new RegExp( '(' + layoutNames.join( '|' ) + ')' ).exec( userAgent ) ) {
666 - layout = translate( match[1], layoutTranslations );
667 - }
668 - if ( match = new RegExp( '(' + osNames.join( '|' ) + ')' ).exec( navigator.platform.toLowerCase() ) ) {
669 - var os = translate( match[1], osTranslations );
670 - }
671 - if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( userAgent ) ) {
672 - version = match[3];
673 - }
674 -
675 - /* Edge Cases -- did I mention about how user agent string lie? */
676 -
677 - // Decode Safari's crazy 400+ version numbers
678 - if ( name.match( /safari/ ) && version > 400 ) {
679 - version = '2.0';
680 - }
681 - // Expose Opera 10's lies about being Opera 9.8
682 - if ( name === 'opera' && version >= 9.8) {
683 - version = userAgent.match( /version\/([0-9\.]*)/i )[1] || 10;
684 - }
685 -
686 - /* Caching */
687 -
688 - client = {
689 - 'browser': browser,
690 - 'layout': layout,
691 - 'os': os,
692 - 'version': version,
693 - 'versionBase': ( version !== x ? new String( version ).substr( 0, 1 ) : x ),
694 - 'versionNumber': ( parseFloat( version, 10 ) || 0.0 )
695 - };
696 - }
697 - return client;
698 - };
699 - /**
700 - * Checks the current browser against a support map object to determine if the browser has been black-listed or
701 - * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
702 - * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
703 - *
704 - * A browser map is in the following format:
705 - * {
706 - * 'ltr': {
707 - * // Multiple rules with configurable operators
708 - * 'msie': [['>=', 7], ['!=', 9]],
709 - * // Blocked entirely
710 - * 'iphone': false
711 - * },
712 - * 'rtl': {
713 - * // Test against a string
714 - * 'msie': [['!==', '8.1.2.3']],
715 - * // RTL rules do not fall through to LTR rules, you must explicity set each of them
716 - * 'iphone': false
717 - * }
718 - * }
719 - *
720 - * @param map Object of browser support map
721 - *
722 - * @return Boolean true if browser known or assumed to be supported, false if blacklisted
723 - */
724 - this.testClient = function( map ) {
725 - var client = this.client();
726 - // Check over each browser condition to determine if we are running in a compatible client
727 - var browser = map[$( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'][client.browser];
728 - if ( typeof browser !== 'object' ) {
729 - // Unknown, so we assume it's working
730 - return true;
731 - }
732 - for ( var condition in browser ) {
733 - var op = browser[condition][0];
734 - var val = browser[condition][1];
735 - if ( val === false ) {
736 - return false;
737 - } else if ( typeof val == 'string' ) {
738 - if ( !( eval( 'client.version' + op + '"' + val + '"' ) ) ) {
739 - return false;
740 - }
741 - } else if ( typeof val == 'number' ) {
742 - if ( !( eval( 'client.versionNumber' + op + val ) ) ) {
743 - return false;
744 - }
745 - }
746 - }
747 - return true;
748 - };
749 - } )();
 496+
 497+ /* Extension points */
 498+
 499+ this.utilities = {};
 500+
750501 // Attach to window
751502 window.MediaWiki = window.mw = $.extend( 'mw' in window ? window.mw : {}, this );
752503 } )();
\ No newline at end of file

Status & tagging log