diff --git a/UniversalLanguageSelector.hooks.php b/UniversalLanguageSelector.hooks.php index 9af3b102..dbc71b9e 100644 --- a/UniversalLanguageSelector.hooks.php +++ b/UniversalLanguageSelector.hooks.php @@ -48,5 +48,50 @@ class UniversalLanguageSelectorHooks { ) ) + $personal_urls; return true; } - + /** + * Add the template for the ULS to the body. + * Hooks: SkinAfterContent + * TODO: move to JavaScript side + * TODO: hardcoded English + */ + public static function addTemplate( &$data, $skin ) { + global $wgContLang; + $languages = Language::fetchLanguageNames( $wgContLang->getCode() ); + $languageData = htmlspecialchars( FormatJSON::encode( $languages ) ); + $data .= " +
+ +
+
+

" . wfMsgHtml( 'uls-select-content-language' ) . "

+
+ +
+
+
+
+ + +
+
+
    +
+
+
+
"; + return true; + } } diff --git a/UniversalLanguageSelector.i18n.php b/UniversalLanguageSelector.i18n.php index 9dd57915..481ea134 100644 --- a/UniversalLanguageSelector.i18n.php +++ b/UniversalLanguageSelector.i18n.php @@ -8,11 +8,21 @@ $messages = array(); -/** English +/** + * English * @author santhosh -*/ + */ $messages['en'] = array( 'UniversalLanguageSelector' => 'Universal Language Selector', - 'uls-desc' => 'Universal Language Selector', - 'uls-select-content-language' => 'Select Content Language', + 'uls-desc' => 'Universal Language Selector', + 'uls-select-content-language' => 'Select language', ); +/** + * Message documentation + * @author santhosh + */ +$messages['qqq'] = array( + 'UniversalLanguageSelector' => 'Extension name', + 'uls-desc' => 'Extension description', + 'uls-select-content-language' => 'Main heading in the language selector popup.', +); \ No newline at end of file diff --git a/UniversalLanguageSelector.php b/UniversalLanguageSelector.php index 8636df22..ad9f6e16 100644 --- a/UniversalLanguageSelector.php +++ b/UniversalLanguageSelector.php @@ -1,7 +1,7 @@ 'uls-desc', ); -$dir = dirname( __FILE__ ); +$dir = __DIR__ ; // Internationalization $wgExtensionMessagesFiles['UniversalLanguageSelector'] = "$dir/UniversalLanguageSelector.i18n.php"; @@ -34,6 +34,7 @@ $wgAutoloadClasses['UniversalLanguageSelectorHooks'] = "$dir/UniversalLanguageSe $wgHooks['BeforePageDisplay'][] = 'UniversalLanguageSelectorHooks::addModules'; $wgHooks['PersonalUrls'][] = 'UniversalLanguageSelectorHooks::addTrigger'; +$wgHooks['SkinAfterContent'][] = 'UniversalLanguageSelectorHooks::addTemplate'; $wgResourceModules['ext.uls.init'] = array( 'scripts' => 'resources/ext.uls.init.js', @@ -44,15 +45,14 @@ $wgResourceModules['ext.uls.init'] = array( ); $wgResourceModules['ext.uls.core'] = array( - 'scripts' => array( 'resources/ext.uls.core.js' ), + 'scripts' => array( 'resources/ext.uls.core.js', 'resources/ext.uls.languagefilter.js' ), 'styles' => 'resources/css/ext.uls.css', 'localBasePath' => $dir, 'remoteExtPath' => 'UniversalLanguageSelector', 'dependencies' => array( 'mediawiki.util', - ), - 'messages' => array( - 'uls-select-content-language', + 'mediawiki.Uri', + 'jquery.ui.autocomplete' ), 'position' => 'top', ); diff --git a/resources/css/ext.uls.css b/resources/css/ext.uls.css index 485a1f1e..3f75eddf 100644 --- a/resources/css/ext.uls.css +++ b/resources/css/ext.uls.css @@ -5,17 +5,15 @@ } .uls-menu { - position: absolute; - top: 100%; - left: 0; + position: fixed; z-index: 1000; display: none; - float: left; - min-width: 400px; - min-height: 400px; - padding: 4px 0; - margin: 1px 0 0; - list-style: none; + min-width: 600px; + min-height: 500px; + margin-top: 1px; + width: 50%; + + /* Styling */ background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); @@ -30,20 +28,109 @@ -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; - width: 50% -} -.uls-menu h2 { - float:left; -} -a.close{ - float: right; - padding: 10px; } -div#worldmap { +.uls-menu-header { + margin: 2%; + margin-top: 20px; +} + +.uls-lang-selector { + margin: 2%; +} + +.uls-menu-header-left { + float: left; +} + +.uls-menu-header-left h1 { + font-weight: bold; + padding-top: 12px; + border:none; + padding-bottom: 3px; +} + +.uls-menu-header-right { + float: right; +} + +.uls-worldmap { /* @embed */ background: url('../images/world_map.png') no-repeat scroll left center transparent; - float: right; width: 400px; - height: 200px; + height: 197px; } + +.uls-region { + float: left; + cursor: pointer; + text-align: center; + width: 133px; + padding: 0; + margin: 0; + height: 197px; +} + +.uls-region:hover { + outline: 1px solid #0E90D2; + background: black; + opacity: 0.4; +} + +.uls-region.active { + background: black; + opacity: 0.4; +} + +.icon-close { + /* @embed */ + background: url('../images/close.png') no-repeat scroll left center transparent; + float: right; + padding: 10px; + cursor: pointer; +} + +/* Language list */ +.uls-language-list { + height: 300px; + overflow: auto; + margin-top: 2%; +} + +.uls-language-list ul { + list-style: none; + margin-left: 0; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; +} + +.uls-language-list ul li { + font-weight: bold; +} + +.uls-language-list strong { + text-decoration: underline; +} + +.uls-language-list a { + color: #0645AD; +} + +input#languagefilter { + height: 20px; + border: 1px solid #0E90D2; + border-right: none; + width: 95%; +} + +span.search-button { + /* @embed */ + background: url('../images/search.png') no-repeat scroll left center transparent; + cursor: pointer; + height: 22px; + border: 1px solid #0E90D2; + position: absolute; + width: 20px; + border-left: none; +} \ No newline at end of file diff --git a/resources/ext.uls.core.js b/resources/ext.uls.core.js index e4749cd7..85a50dbe 100644 --- a/resources/ext.uls.core.js +++ b/resources/ext.uls.core.js @@ -1,48 +1,59 @@ -(function($) { +(function ( $, mw ) { "use strict"; - var ULS = function(element, options) { + var ULS = function( element, options ) { this.$element = $( element ); this.options = $.extend( {}, $.fn.uls.defaults, options ); - this.$menu = $( this.options.menu ).appendTo( 'body' ); + this.$menu = $( this.options.menu ); this.shown = false; this.render(); this.listen(); - } + }; ULS.prototype = { - constructor : ULS, - show : function() { + constructor: ULS, + show: function() { var pos = $.extend( {}, this.$element.offset(), { - height : this.$element[0].offsetHeight + height: this.$element[0].offsetHeight } ); this.$menu.css( { - top : pos.top + pos.height, - left : '25%' //pos.left // FIXME + top: pos.top + pos.height, + left: '25%' //pos.left // FIXME } ); this.$menu.show(); this.shown = true; + return this; }, - hide : function() { + hide: function() { this.$menu.hide(); this.shown = false; return this; }, - render: function(){ - // TODO : All UI construction code to go here. - var $heading = $("

").text(mw.msg("uls-select-content-language")); - this.$menu.append($heading); - var $wordldMap = $("
"); - this.$menu.append($wordldMap); + render: function() { + // Rendering stuff here }, - listen : function() { + listen: function() { // Register all event listeners to the ULS here. this.$element.on( 'click', $.proxy( this.click, this ) ); - this.$menu.on( 'keyup', $.proxy( this.click, this ) ) - .on( 'keypress', $.proxy( this.click, this ) ); + $( ".icon-close" ).on( 'click', $.proxy( this.click, this ) ); + $( "#languagefilter" ).languagefilter( { + $target: $( 'ul.uls-language-filter-result' ), + clickhandler: function( item ) { + // TODO: dependency on MediaWiki + var uri = new mw.Uri( window.location.href ); + uri.extend( { setlang: item.value } ); + window.location.href = uri.toString(); + } + } ); + $( '.uls-region' ).live( 'click', function () { + $( this ).parent().find( '.uls-region' ).removeClass( 'active' ); + $( this ).addClass( 'active' ); + } ); + // trigger a search for all languages. + $( "#languagefilter" ).autocomplete( "search" ); }, keyup : function(e) { switch(e.keyCode) { @@ -53,15 +64,15 @@ } break; } - e.stopPropagation(); e.preventDefault(); }, - keypress : function(e) { - if (!this.shown ) + keypress: function( e ) { + if ( !this.shown ) { return; + } - switch(e.keyCode) { + switch( e.keyCode ) { case 27: // escape e.preventDefault(); @@ -69,7 +80,7 @@ } e.stopPropagation(); }, - click : function(e) { + click: function( e ) { e.stopPropagation(); e.preventDefault(); if ( !this.shown ) { @@ -78,26 +89,27 @@ this.hide(); } }, - } + }; /* ULS PLUGIN DEFINITION * =========================== */ - $.fn.uls = function(option) { + $.fn.uls = function( option ) { return this.each( function() { var $this = $( this ), data = $this.data( 'uls' ), options = typeof option == 'object' && option; - if (!data ) - $this.data( 'uls', ( data = new ULS(this, options)) ); - if ( typeof option == 'string' ) + if ( !data ) { + $this.data( 'uls', ( data = new ULS( this, options ) ) ); + } + if ( typeof option === 'string' ) { data[option](); - } ) + } + } ); }; $.fn.uls.defaults = { - // FIXME Menu template. Can it come from PHP? - menu : '', + menu : '.uls-menu', }; $.fn.uls.Constructor = ULS; -} )( jQuery ); +} )( jQuery, mediaWiki ); diff --git a/resources/ext.uls.init.js b/resources/ext.uls.init.js index 57fb3462..5c50cd6d 100644 --- a/resources/ext.uls.init.js +++ b/resources/ext.uls.init.js @@ -3,12 +3,6 @@ */ ( function( $ ) { $( document ).ready( function() { - /* Create a trigger somewhere in the page. - $trigger = $("") - .addClass( 'uls-trigger' ) - .text("English"); // FIXME proper trigger text to go here. - $('#mw-head').append( $trigger );*/ - // Bind ULS to the trigger. - $('.uls-trigger').uls(); + $( '.uls-trigger' ).uls(); } ); } )( jQuery ); diff --git a/resources/ext.uls.languagefilter.js b/resources/ext.uls.languagefilter.js new file mode 100644 index 00000000..4af9ec60 --- /dev/null +++ b/resources/ext.uls.languagefilter.js @@ -0,0 +1,83 @@ +/* + * @author Santhosh Thottingal + * jQuery autocomplete based language filter widget + * Usage: $('inputbox').languagefilter(); + * The values for autocompletion is from the data-languages of the element. + * the data is in the format of languagecode:languagename format. + * Credits: http://jqueryui.com/demos/autocomplete + */ +jQuery( function( $ ) { + "use strict"; + + $.widget( "ui.languagefilter", { + options: { + $target: null, // where to append the results + languages: null, // languages as code:name format. default values is from data-languages + clickhandler: null + }, + _create: function() { + var self = this.element, + options = this.options; + $( self ).autocomplete( { + delay: 0, + minLength: 0, + // Move the default dropdown for suggestions to somewhere + // where it is not visible, since we don't use it. + position: { offset: "-10000 -10000" }, + source: function( request, response ) { + var term = request.term; + var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), 'i' ); + var languages = options.languages; + + if ( languages === null ) { + // Apparently .data automatically parses valid looking JSON + languages = $( self ).data( 'languages' ); + } + + response( $.map( languages, function ( name, code ) { + if ( matcher.test( name ) ) { + return { + label: name.replace( + new RegExp( + "(?![^&;]+;)(?!<[^<>]*)(" + + $.ui.autocomplete.escapeRegex( term ) + + ")(?![^<>]*>)(?![^&;]+;)", "gi" + ), "$1" ), + value: code + }; + } + } ) ); + }, + search: function ( event, ui ) { + if ( options.$target ){ + options.$target.empty(); + } + } + } ); // /autocomplete + + $( self ).data( "autocomplete" )._renderItem = function ( ul, item ) { + var $target = ul; + if ( options.$target ) { + $target = options.$target; + } else { + return; + } + var $li = $( "
  • " ) + .data( "item.autocomplete", item ) + .append( $( "" ).prop( 'href', '#' ). html( item.label ) ) + .appendTo( $target ); + if ( options.clickhandler ) { + $li.click( function() { + options.clickhandler.call( this, item ); + } ); + } + return $li; + + }; + }, // End of _create + + destroy: function() { + $.Widget.prototype.destroy.call( this ); + } + } ); +} ); diff --git a/resources/images/close.png b/resources/images/close.png new file mode 100644 index 00000000..02e125a5 Binary files /dev/null and b/resources/images/close.png differ diff --git a/resources/images/search.png b/resources/images/search.png new file mode 100644 index 00000000..1db2eb24 Binary files /dev/null and b/resources/images/search.png differ