Merge branch 'master' into srpski

This commit is contained in:
Niklas Laxström
2018-02-06 16:31:46 +02:00
committed by GitHub
46 changed files with 644 additions and 631 deletions

1
.eslintignore Normal file
View File

@@ -0,0 +1 @@
src/jquery.uls.data.js

14
.eslintrc.json Normal file
View File

@@ -0,0 +1,14 @@
{
"extends": "wikimedia",
"env": {
"browser": true,
"jquery": true,
"qunit": true
},
"globals": {
"require": false
},
"rules": {
"dot-notation": [ "error", { "allowKeywords": true } ]
}
}

View File

@@ -1,17 +0,0 @@
{
"preserve_newlines": true,
"jslint_happy": true,
"keep_array_indentation": true,
"space_before_conditional": true,
"max_preserve_newlines": 10,
"brace_style": "collapse",
"keep_function_indentation": false,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0,
"space_in_paren": true,
"space_in_square_bracket": true,
"git_happy": true,
"indent_with_tabs": true
}

View File

@@ -1 +0,0 @@
src/jquery.uls.data.js

View File

@@ -1,35 +0,0 @@
{
// Enforcing
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"onevar": true,
"quotmark": "single",
"regexp": false,
"strict": true,
"trailing": true,
"undef": true,
"unused": true,
// Relax
"laxbreak": true,
"multistr": true,
"smarttabs": true,
// Environment
"browser": true,
"predef": [
"_",
"jQuery",
"QUnit"
]
}

3
.stylelintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "stylelint-config-wikimedia"
}

View File

@@ -1,4 +1,4 @@
language: node_js language: node_js
node_js: node_js:
- "0.10" - 8
sudo: false sudo: false

View File

@@ -1,22 +1,35 @@
/*jshint node:true */
'use strict'; 'use strict';
module.exports = function ( grunt ) { module.exports = function ( grunt ) {
grunt.loadNpmTasks( 'grunt-contrib-jshint' ); grunt.loadNpmTasks( 'grunt-eslint' );
grunt.loadNpmTasks( 'grunt-stylelint' );
grunt.loadNpmTasks( 'grunt-contrib-qunit' ); grunt.loadNpmTasks( 'grunt-contrib-qunit' );
grunt.initConfig( { grunt.initConfig( {
jshint: { eslint: {
all: [
'**/*.js',
'src/jquery.uls.data.js',
'!examples/**',
'!node_modules/**',
'!vendor/**'
]
},
stylelint: {
options: { options: {
jshintrc: true syntax: 'css'
}, },
all: ['*.js', 'src/*.js'] src: [
'**/*.css',
'!node_modules/**'
]
}, },
qunit: { qunit: {
all: 'test/index.html' all: 'test/index.html'
} }
} ); } );
grunt.registerTask( 'lint', 'jshint' ); grunt.registerTask( 'lint', [ 'eslint' ] );
grunt.registerTask( 'test', ['lint', 'qunit'] ); grunt.registerTask( 'test', [ 'lint', 'qunit' ] );
grunt.registerTask( 'default', 'test' ); grunt.registerTask( 'default', 'test' );
}; };

View File

@@ -1,10 +1,8 @@
jQuery Universal Language Selector Universal Language Selector jQuery library
================================== ==================================
Universal Language Selector
[![Build Status](https://secure.travis-ci.org/wikimedia/jquery.uls.png)](http://travis-ci.org/wikimedia/jquery.uls) [![Build Status](https://secure.travis-ci.org/wikimedia/jquery.uls.png)](http://travis-ci.org/wikimedia/jquery.uls)
This is a [Wikimedia Foundation Language Engineering team project](https://www.mediawiki.org/wiki/Project_Milkshake). This is a [Wikimedia Foundation project](https://www.mediawiki.org/wiki/Project_Milkshake).
![Universal Language Selector](https://upload.wikimedia.org/wikipedia/commons/a/a1/UniversalLanguageSelector-Compact.png "Universal Language Selector") ![Universal Language Selector](https://upload.wikimedia.org/wikipedia/commons/a/a1/UniversalLanguageSelector-Compact.png "Universal Language Selector")
@@ -19,86 +17,70 @@ git clone https://github.com/wikimedia/jquery.uls.git
Documentation Documentation
------------- -------------
The quick and easy way to learn usage of jquery.uls is trying out the examples/index.html in some webserver. Try it from here: http://thottingal.in/projects/js/jquery.uls/examples/ The quick and easy way to learn usage of jquery.uls is trying out the examples/index.html in webserver. Try it online at http://thottingal.in/projects/js/jquery.uls/examples/
The jQuery.uls provides a jQuery extension ```$.fn.uls``` that can be attached to a trigger element. The expected behavior is, when you click on the trigger, the language selector opens up. The jquery.uls provides a jQuery extension ```$.fn.uls``` that can be attached to a trigger element. When you click on the trigger element, the language selector is shown.
The trigger can be a link, button or any valid jQuery element. The trigger can be a link, button or any valid jQuery element.
Example: Example:
```javascript ```javascript
$( '.uls-trigger' ).uls( ); $( '.uls-trigger' ).uls();
``` ```
To use the selected language, you need define a selection Handler as shown below To use the selected language, you need define a selection handler:
```javascript ```javascript
$( '.uls-trigger' ).uls( { $( '.uls-trigger' ).uls( {
onSelect : function( language ) { onSelect: function( language ) {
// language is a ISO 639 language code. eg: en, hi, fi, he etc // language is a IETF language tag in lowercase, for example: en, fi, ku-latn
// Your selection handler code goes here. // Your selection handler code goes here.
} }
} ); } );
``` ```
In some usecases, you may need to provide a quick list of languages to select before going through all languages. For eg, it can a list of recently selected languages, language suggestions based on Geo IP. You can provide a quick list of likely useful languages, for example based on Geo IP or recently selected languages:
That can be done as follows
```javascript ```javascript
$( '.uls-trigger' ).uls( { $( '.uls-trigger' ).uls( {
onSelect : function( language ) { onSelect: function( language ) { ... },
// language is a ISO 639 language code. eg: en, hi, fi, he etc quickList: [ 'en', 'ml', 'hi' ] // Can be a function returning an array too.
// Your selection handler code goes here.
},
quickList: [ 'en', 'ml', 'hi' ] // An array of language codes. Can be a function that returns this array too.
} ); } );
``` ```
If the search needs to be more complex(such as cross language search, spelling error tolerating etc), a search API option can be provided. jquery.uls knows about 500 languages. You can specify a subset of those languages:
```javascript ```javascript
$( '.uls-trigger' ).uls( { $( '.uls-trigger' ).uls( {
onSelect : function( language ) { onSelect: function( language ) { ... },
// language is a ISO 639 language code. eg: en, hi, fi, he etc languages: { languageCode1: languageName, languageCode2: languageName2, .... },
// Your selection handler code goes here.
},
searchAPI: apiURL,
quickList: [ 'en', 'ml', 'hi' ] // An array of language codes. Can be a function that returns this array too.
} ); } );
``` ```
Example for such an api is used in Wikipedia: http://en.wikipedia.org/w/api.php?action=languagesearch&search=Te *All options*
ULS knows about 500 languages. If you dont want to use that many languages for your usecase, use languages option.
```javascript
$( '.uls-trigger' ).uls( {
onSelect : function( language ) {
// language is a ISO 639 language code. eg: en, hi, fi, he etc
// Your selection handler code goes here.
},
languages: { languageCode1: languageName, languageCode2: languageName2 , .... },
searchAPI: apiURL,
quickList: [ 'en', 'ml', 'hi' ] // An array of language codes. Can be a function that returns this array too.
} );
```
*Other Options*
| Option | Description | | Option | Description |
|-------------|---------------------| |-------------|---------------------|
| left | left position of ULS. eg: 100px, 20%| | left | Left position of ULS dialog. E.g: 100px or 20% |
| top | top position of ULS. eg: 100px, 20%| | top | Top position of ULS dialog. E.g: 100px or 20% |
| onCancel | function to be handled when language selection is not done. ie. language selector is closed without selecting any | | onCancel | Callback function when the dialog is closed without selecting a language. |
| showRegions | Regions to be shown in the language selector. Default: ['WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA'] | | onReady | Callback function when ULS has initialized. |
| onVisible | Callback function when ULS dialog is shown. |
| onSelect | Callback function when user selects a language. |
| languages | List of selectable languages. Defaults to all known languages. |
| quicklist | List of suggested languages. Defaults to empty list. |
| searchAPI | API URL. Parameter query with the user query is appened to it. |
| menuWidth | Override the automatic choice of menu width. One of narrow, medium, wide (1, 2, 4 columns respectively). |
| showRegions | Regions to be shown in the language selector. Defaults to [ WW, AM, EU, ME, AF, AS, PA ] |
| groupByRegion | Whether to group languages by the regions: true of false. Default value depends on the menu width. |
| itemsPerColumn | Number of languages per column. Default is 8 | | itemsPerColumn | Number of languages per column. Default is 8 |
| languageDecorator | Callback function to be called when a language link is prepared - for custom decoration. Arguments: (a) the $language - the language link jQuery object (b) languageCode. The function can do any styling, changing properties etc on the passed link. See examples/decorator.html for example usage.| | languageDecorator | Callback function to be called when a language link is prepared - for custom decoration. Arguments: (a) the $language - the language link jQuery object (b) languageCode. The function can do any styling, changing properties etc on the passed link. See examples/decorator.html for example usage.|
| noResultsTemplate | A jQuery object or a function that returns a jQuery object. This will be used as the template to display when no results found from the search. The function will receive the current search query as argument|
Features Features
-------- --------
jQuery.uls has an elaborative language information collection and it is based on https://github.com/wikimedia/language-data.git. It knows about jquery.uls has an elaborative language information collection and it is based on https://github.com/wikimedia/language-data.git. It knows about
1. The script in which a language is written. 1. The script in which a language is written.
2. The script code 2. The script code
@@ -107,14 +89,7 @@ jQuery.uls has an elaborative language information collection and it is based on
5. The autonym - language name written in its own script 5. The autonym - language name written in its own script
6. The directionality of the text 6. The directionality of the text
With all these information the search becomes very effective. Based on the spoken regions, the UI organize the languages. In side regions With all these information the search becomes very effective. An advanced usage of jquery.uls can be tried out from Wikimedia sites. For example, see the language icon at the top of https://mediawiki.org or the cog icon near to the languages list in Wikipedia in any language.
the language is again organized based on scripts.
A user can search for a language based on script name.
ULS can autocomplete a language name search.
An advanced usage of jQuery.uls can be tried out from wikimedia sites. For eg, see the language icon at the top of http://mediawiki.org or the cog icon near to the languages list in wikipedia in any language
More details More details
------------ ------------
@@ -122,7 +97,7 @@ More details
* UX Design https://www.mediawiki.org/wiki/Universal_Language_Selector/Design * UX Design https://www.mediawiki.org/wiki/Universal_Language_Selector/Design
How to build and test jQuery ULS How to build and test jquery.uls
---------------------------------- ----------------------------------
First, get a copy of the git repo by running: First, get a copy of the git repo by running:
@@ -150,4 +125,4 @@ You can also run the tests in a browser by navigating to the `test/` directory,
Coding style Coding style
------------- -------------
Please follow [jQuery coding guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) Please follow [MediaWiki coding conventions](https://www.mediawiki.org/wiki/Manual:Coding_conventions/JavaScript)

View File

@@ -1,11 +1,8 @@
.uls-trigger { .uls-trigger {
background: url( ../images/language.png ) no-repeat left center;
/* @embed */ /* @embed */
background: url('../images/icon-language.png') no-repeat left center; background-image: linear-gradient( transparent, transparent ), url( ../images/language.svg );
/* @embed */ padding-left: 24px;
background-image: -webkit-linear-gradient(transparent, transparent), url('../images/icon-language.svg');
/* @embed */
background-image: linear-gradient(transparent, transparent), url('../images/icon-language.svg');
padding-left: 30px;
} }
.uls-menu { .uls-menu {
@@ -15,10 +12,10 @@
margin-top: 1px; margin-top: 1px;
background-color: #fff; background-color: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
border-color: rgba(0, 0, 0, 0.2); border-color: rgba( 0, 0, 0, 0.2 );
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 5px 10px rgba( 0, 0, 0, 0.2 );
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba( 0, 0, 0, 0.2 );
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba( 0, 0, 0, 0.2 );
-webkit-background-clip: padding-box; -webkit-background-clip: padding-box;
-moz-background-clip: padding; -moz-background-clip: padding;
background-clip: padding-box; background-clip: padding-box;
@@ -68,10 +65,9 @@
} }
.uls-search-label { .uls-search-label {
background: url('../images/search.png') no-repeat center center; background: url( ../images/search.png ) no-repeat center center;
background-image: -webkit-linear-gradient(transparent, transparent), url('../images/search.svg');
/* @embed */ /* @embed */
background-image: linear-gradient(transparent, transparent), url('../images/search.svg'); background-image: linear-gradient( transparent, transparent ), url( ../images/search.svg );
background-size: 20px; background-size: 20px;
height: 32px; height: 32px;
width: 44px; width: 44px;
@@ -92,7 +88,7 @@
width: 100%; width: 100%;
/* For the custom clear (X) icon */ /* For the custom clear (X) icon */
padding: 6px 25px 6px 0; padding: 6px 25px 6px 0;
outline: none; outline: 0;
border: 0; border: 0;
display: block; display: block;
position: absolute; position: absolute;
@@ -114,10 +110,9 @@
} }
.uls-languagefilter-clear { .uls-languagefilter-clear {
background: url('../images/clear.png') no-repeat left center; background: url( ../images/clear.png ) no-repeat left center;
background-image: -webkit-linear-gradient(transparent, transparent), url('../images/clear.svg');
/* @embed */ /* @embed */
background-image: linear-gradient(transparent, transparent), url('../images/clear.svg'); background-image: linear-gradient( transparent, transparent ), url( ../images/clear.svg );
background-size: 15px; background-size: 15px;
cursor: pointer; cursor: pointer;
height: 15px; height: 15px;

View File

@@ -32,15 +32,18 @@
} }
.grid .highlight { .grid .highlight {
background: #ffff99; background: #ff9;
} }
/* The Grid ---------------------- */ /* The Grid ---------------------- */
.grid .row { .grid .row {
width: 100%; width: 100%;
max-width: none; max-width: none;
min-width: 600px; min-width: 600px;
margin: 0 auto; margin: 0 auto;
/* Nicolas Gallagher's micro clearfix */
*zoom: 1;
} }
.grid .row .row { .grid .row .row {
@@ -50,30 +53,25 @@
margin: 0 -5px; margin: 0 -5px;
} }
.grid .column,
.grid .columns {
float: left;
min-height: 1px;
padding: 0 5px;
position: relative;
}
.grid .row.collapse .column, .grid .row.collapse .column,
.grid .row.collapse .columns { .grid .row.collapse .columns {
padding: 0; padding: 0;
} }
.grid .row .row {
width: auto;
max-width: none;
min-width: 0;
margin: 0 -5px;
}
.grid .row .row.collapse { .grid .row .row.collapse {
margin: 0; margin: 0;
} }
.grid .column, .grid .columns { .grid .column.centered,
float: left; .grid .columns.centered {
min-height: 1px;
padding: 0 5px;
position: relative;
}
.grid .column.centered, .grid .columns.centered {
float: none; float: none;
margin: 0 auto; margin: 0 auto;
} }
@@ -238,13 +236,9 @@
right: 83.333%; right: 83.333%;
} }
/* Nicolas Gallagher's micro clearfix */ .grid .row:before,
.grid .row { .grid .row:after {
*zoom: 1; content: '';
}
.grid .row:before, .grid .row:after {
content: "";
display: table; display: table;
} }

View File

@@ -17,6 +17,10 @@
padding: 0 16px; padding: 0 16px;
} }
.uls-lcd--no-quicklist [data-region="all"] .uls-lcd-region-title {
display: none;
}
.uls-lcd-region-section { .uls-lcd-region-section {
margin-top: 10px; margin-top: 10px;
} }
@@ -36,6 +40,16 @@
margin: 0; margin: 0;
} }
.uls-language-block > ul {
/*
* We don't want any visible bullets in this list. Not by default anyway.
* Using very unspecific selector here to allow other classes to override.
* Bug because overflow: hidden is incompatible with bullets, also render
* the bullets inside the list in case there should be any.
*/
list-style: none none;
}
/* /*
* Each block should have 16px padding on both sides. But because we already gave * Each block should have 16px padding on both sides. But because we already gave
* 16px for the whole menu, we need to remove it for first and last items the blocks. * 16px for the whole menu, we need to remove it for first and last items the blocks.
@@ -53,17 +67,6 @@
padding-right: 0; padding-right: 0;
} }
.uls-language-block > ul {
/*
* We don't want any visible bullets in this list. Not by default anyway.
* Using very unspecific selector here to allow other classes to override.
* Bug because overflow: hidden is incompatible with bullets, also render
* the bullets inside the list in case there should be any.
*/
list-style: none none;
}
.uls-language-block > ul > li { .uls-language-block > ul > li {
cursor: pointer; cursor: pointer;
margin-left: 20px; margin-left: 20px;
@@ -103,16 +106,30 @@
vertical-align: middle; vertical-align: middle;
} }
.uls-no-results-view {
display: none;
}
.uls-lcd.uls-no-results > .uls-lcd-region-section {
display: none;
}
.uls-lcd.uls-no-results > .uls-no-results-view {
display: block;
}
.uls-no-results-found-title { .uls-no-results-found-title {
font-size: 16px; font-size: 16px;
padding: 0 16px 0 28px; padding: 0 16px 0 28px;
margin: 20px 0;
border-bottom: 0; border-bottom: 0;
color: #555; color: #54595d;
} }
.uls-no-found-more { .uls-no-found-more {
background: #f8f8f8; border-top: 1px solid #eaecf0;
padding: 0 16px 0 44px; color: #54595d;
padding: 12px 16px 12px 44px;
font-size: 0.9em; font-size: 0.9em;
width: 100%; width: 100%;
margin-top: 1.6em; margin-top: 1.6em;

View File

@@ -1,4 +1,4 @@
@media only screen and (max-width: 767px) { @media only screen and ( max-width: 767px ) {
.uls-mobile.uls-menu { .uls-mobile.uls-menu {
width: 95%; width: 95%;
@@ -40,7 +40,7 @@
float: none; float: none;
} }
.uls-mobile [class*="column"] + [class*="column"]:last-child { .uls-mobile [ class*='column' ] + [ class*='column' ]:last-child {
float: none; float: none;
} }
@@ -48,7 +48,7 @@
.uls-mobile .uls-mobile .columns:before, .uls-mobile .uls-mobile .columns:before,
.uls-mobile .column:after, .uls-mobile .column:after,
.columns:after { .columns:after {
content: ""; content: '';
display: table; display: table;
} }
@@ -197,11 +197,12 @@
} }
/* Very large display targeting */ /* Very large display targeting */
@media only screen and (min-width: 1441px) { @media only screen and ( min-width: 1441px ) {
.uls-mobile .hide-for-small, .uls-mobile .hide-for-small,
.uls-mobile .hide-for-medium, .uls-mobile .hide-for-medium,
.uls-mobile .hide-for-medium-down, .uls-mobile .hide-for-medium-down,
.hide-for-large, .show-for-large-up, .hide-for-large,
.show-for-large-up,
.show-for-xlarge { .show-for-xlarge {
display: block !important; display: block !important;
} }
@@ -217,7 +218,7 @@
} }
/* Medium display targeting */ /* Medium display targeting */
@media only screen and (max-width: 1279px) and (min-width: 768px) { @media only screen and ( max-width: 1279px ) and ( min-width: 768px ) {
.uls-mobile .hide-for-small, .uls-mobile .hide-for-small,
.uls-mobile .show-for-medium, .uls-mobile .show-for-medium,
.uls-mobile .show-for-medium-down, .uls-mobile .show-for-medium-down,
@@ -238,7 +239,7 @@
} }
/* Small display targeting */ /* Small display targeting */
@media only screen and (max-width: 767px) { @media only screen and ( max-width: 767px ) {
.uls-mobile .show-for-small, .uls-mobile .show-for-small,
.uls-mobile .hide-for-medium, .uls-mobile .hide-for-medium,
.uls-mobile .show-for-medium-down, .uls-mobile .show-for-medium-down,
@@ -268,7 +269,7 @@
display: none !important; display: none !important;
} }
@media screen and (orientation: landscape) { @media screen and ( orientation: landscape ) {
.uls-mobile .show-for-landscape, .uls-mobile .show-for-landscape,
.uls-mobile .hide-for-portrait { .uls-mobile .hide-for-portrait {
display: block !important; display: block !important;
@@ -279,7 +280,7 @@
} }
} }
@media screen and (orientation: portrait) { @media screen and ( orientation: portrait ) {
.uls-mobile .show-for-portrait, .uls-mobile .show-for-portrait,
.uls-mobile .hide-for-landscape { .uls-mobile .hide-for-landscape {
display: block !important; display: block !important;

View File

@@ -10,7 +10,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>

View File

@@ -10,7 +10,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>

View File

@@ -21,7 +21,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>

View File

@@ -10,7 +10,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>

View File

@@ -20,7 +20,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>
@@ -30,7 +30,7 @@
<script> <script>
$( document ).ready( function() { $( document ).ready( function() {
$( '.uls-trigger' ).uls( { $( '.uls-trigger' ).uls( {
onSelect : function( language ) { onSelect: function( language ) {
var languageName = $.uls.data.getAutonym( language ); var languageName = $.uls.data.getAutonym( language );
$( '.uls-trigger' ).text( languageName ); $( '.uls-trigger' ).text( languageName );
}, },
@@ -42,12 +42,11 @@
<body> <body>
<div class="navbar navbar-fixed-top"> <div class="navbar navbar-fixed-top">
<span class="active uls-trigger">Select Language</span> <span class="active uls-trigger">Select language</span>
<h1>Universal Language Selector</h1> <h1>Universal Language Selector</h1>
<p> <p>
Demonstration of jQuery plugin Demonstration of jQuery.uls plugin
</p> </p>
</div> </div>
<div class="container"></div>
</body> </body>
</html> </html>

View File

@@ -13,7 +13,7 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>

View File

@@ -1,18 +1,18 @@
body { body {
padding-left: 10%; padding-left: 10%;
padding-right: 10%; padding-right: 10%;
font-family: Arial, Helvetica, sans-serif; font-family: 'Arial', 'Helvetica', sans-serif;
} }
div.navbar { div.navbar {
background-color: #333; background-color: #333;
color: #FFFFFF; color: #fff;
padding: 20px; padding: 20px;
} }
span.uls-trigger { span.uls-trigger {
float: right; float: right;
cursor: pointer; cursor: pointer;
color: #FFFFFF; color: #fff;
font-weight: bold; font-weight: bold;
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -20,34 +20,23 @@
<!-- demo --> <!-- demo -->
<link href="resources/demo.css" rel="stylesheet"> <link href="resources/demo.css" rel="stylesheet">
<!-- Libs --> <!-- Libs -->
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Source --> <!-- Source -->
<script src="../src/jquery.uls.data.js"></script> <script src="../src/jquery.uls.data.js"></script>
<script src="../src/jquery.uls.data.utils.js"></script> <script src="../src/jquery.uls.data.utils.js"></script>
<script src="../src/jquery.uls.lcd.js"></script> <script src="../src/jquery.uls.lcd.js"></script>
<script src="../src/jquery.uls.languagefilter.js"></script> <script src="../src/jquery.uls.languagefilter.js"></script>
<script src="../src/jquery.uls.core.js"></script> <script src="../src/jquery.uls.core.js"></script>
<style>
@font-face {
font-family: 'Autonym';
src: url('resources/font/Autonym.eot');
src: local('Autonym'),
url('resources/font/Autonym.woff') format('woff'),
url('resources/font/Autonym.ttf') format('truetype');
font-style: normal;
}
.autonym {
font-family: 'Autonym', sans-serif;
}
</style>
<script> <script>
$( document ).ready( function() { $( document ).ready( function() {
$( '.uls-trigger' ).uls( { $( '.uls-trigger' ).uls( {
onSelect : function( language ) { onSelect: function( language ) {
var languageName = $.uls.data.getAutonym( language ); var languageName = $.uls.data.getAutonym( language );
$( '.uls-trigger' ).text( languageName ); $( '.uls-trigger' ).text( languageName );
}, },
quickList: ['en', 'hi', 'he', 'ml', 'ta', 'fr'] //FIXME noResultsTemplate: function( query ) {
return $( '<div>No article exists in the language ' + query + '</div>' )
}
} ); } );
} ); } );
</script> </script>
@@ -55,12 +44,11 @@
<body> <body>
<div class="navbar navbar-fixed-top"> <div class="navbar navbar-fixed-top">
<span class="active uls-trigger">Select Language</span> <span class="active uls-trigger">Select language</span>
<h1>Universal Language Selector</h1> <h1>Universal Language Selector</h1>
<p> <p>
Demonstration of jQuery plugin Demonstration of jQuery.uls plugin
</p> </p>
</div> </div>
<div class="container"></div>
</body> </body>
</html> </html>

View File

@@ -18,8 +18,8 @@
"uls-region-PA": "Stille Oseaan", "uls-region-PA": "Stille Oseaan",
"uls-region-all": "Alle tale", "uls-region-all": "Alle tale",
"uls-no-results-found": "Geen resultate gevind nie", "uls-no-results-found": "Geen resultate gevind nie",
"uls-common-languages": "Voorgestelde Tale", "uls-common-languages": "Voorgestelde tale",
"uls-no-results-suggestion-title": "U mag geïnteresseerd wees in:", "uls-no-results-suggestion-title": "U mag geïnteresseerd wees in:",
"uls-search-help": "Soektogte kan volgende taalnaam, skripnaam, ISO-kode, of volgens streek geskied.", "uls-search-help": "Soek gerus volgens taalnaam, skrifnaam of ISO-kode, of blaai volgens streek.",
"uls-search-placeholder": "Soek na n taal" "uls-search-placeholder": "Soek na n taal"
} }

11
i18n/ais.json Normal file
View File

@@ -0,0 +1,11 @@
{
"@metadata": {
"authors": [
"Benel"
]
},
"uls-region-AM": "Amilikaco",
"uls-region-AF": "Afilika",
"uls-region-EU": "Ouco",
"uls-region-AS": "Yaco"
}

View File

@@ -3,7 +3,8 @@
"authors": [ "authors": [
"Calak", "Calak",
"Asoxor", "Asoxor",
"Lost Whispers" "Lost Whispers",
"Épine"
] ]
}, },
"uls-region-WW": "لە گشت جیھاندا", "uls-region-WW": "لە گشت جیھاندا",

View File

@@ -1,12 +1,20 @@
{ {
"@metadata": { "@metadata": {
"authors": [ "authors": [
"ⲁϩⲙⲉⲧ" "ⲁϩⲙⲉⲧ",
"Bloomaround"
] ]
}, },
"uls-region-WW": "ⲧⲏⲣⲙⲁⲓ",
"uls-region-AM": "ⲁⲙⲉⲣⲓⲕⲏ", "uls-region-AM": "ⲁⲙⲉⲣⲓⲕⲏ",
"uls-region-AF": "ⲁϥⲣⲓⲕⲏ", "uls-region-AF": "ⲁⲣⲓⲕⲏ",
"uls-region-EU": "ⲉⲩⲣⲱⲡⲏ", "uls-region-EU": "ⲉⲩⲣⲱⲡⲏ",
"uls-region-AS": "ⲁⲥⲓⲁ", "uls-region-AS": "ⲁⲥⲓⲁ",
"uls-region-all": "ⲁⲥⲡⲓ ⲛⲓⲃⲉⲛ" "uls-region-ME": "ⲡⲓⲉⲃⲧ `ⲛⲑⲙⲏϯ",
"uls-region-PA": "ⲱⲕⲉⲁⲛⲓⲁ",
"uls-region-all": "ⲁⲥⲡⲓ ⲛⲓⲃⲉⲛ",
"uls-no-results-found": "ⲁⲩϫⲓⲙⲓ ⲁⲛ ⲛⲓⲁⲡⲟⲧⲉⲗⲉⲥⲙⲁ",
"uls-common-languages": "ⲛⲓⲁⲥⲡⲓ ⲁⲩϯⲙⲉⲩⲓ",
"uls-search-help": "ⲧⲉⲧⲉⲛϫⲉⲙϫⲟⲙ ϫⲉⲙⲣⲁⲧ ϩⲉⲛ `ⲫⲣⲁⲛ ⲛⲧⲉ ⲁⲥⲡⲓ, `ⲫⲣⲁⲛ ⲛⲧⲉ ⲓⲟⲡⲓⲥϧⲁⲓ , ISO ⲕⲱⲇⲓⲕⲟⲥ ⲛⲧⲉ ⲁⲥⲡⲓ ⲓⲉ ⲧⲉⲧⲉⲛϫⲉⲙϫⲟⲙ ⲥⲓⲛⲓ ϧⲁⲧⲉⲛⲙⲁⲛⲧⲉϭⲁ.",
"uls-search-placeholder": "ϫⲉⲙⲣⲁⲧⲕ `ⲛⲟⲩⲁⲥⲡⲓ"
} }

View File

@@ -16,5 +16,6 @@
"uls-no-results-found": "𑢮𑣁𑣓𑣄 𑣌𑣁 𑣓𑣖 𑣎𑣓𑣁", "uls-no-results-found": "𑢮𑣁𑣓𑣄 𑣌𑣁 𑣓𑣖 𑣎𑣓𑣁",
"uls-common-languages": "𑢨𑣒𑣄 𑣎𑣈𑣋𑣁𑣜 𑣌𑣉", "uls-common-languages": "𑢨𑣒𑣄 𑣎𑣈𑣋𑣁𑣜 𑣌𑣉",
"uls-no-results-suggestion-title": "𑢳𑣈𑣓𑣁 𑣌𑣉 𑣏𑣂 𑣗𑣈𑣓 𑣓𑣖 𑣕𑣓𑣁:", "uls-no-results-suggestion-title": "𑢳𑣈𑣓𑣁 𑣌𑣉 𑣏𑣂 𑣗𑣈𑣓 𑣓𑣖 𑣕𑣓𑣁:",
"uls-search-help": "𑢮𑣈𑣋𑣁𑣜 𑣓𑣃𑣕𑣃𑣖 𑣕𑣈, 𑣏𑣂𑣕𑣂 𑣓𑣃𑣕𑣃𑣖 𑣕𑣈, 𑣎𑣈𑣋𑣁𑣜 𑣜𑣈𑣅𑣄 ISO 𑣌𑣉𑣉𑣑 𑣕𑣈 𑣌𑣁𑣜𑣈 𑣕𑣅𑣑 𑣓𑣃𑣕𑣃𑣖 𑣕𑣈 𑣉𑣉 𑣓𑣖-𑣓𑣈𑣚 𑣔𑣆𑣅𑣉𑣄𑣁.",
"uls-search-placeholder": "𑢶𑣂𑣈𑣑 𑣎𑣈𑣋𑣁𑣓 𑣓𑣈𑣚-𑣓𑣖𑣈" "uls-search-placeholder": "𑢶𑣂𑣈𑣑 𑣎𑣈𑣋𑣁𑣓 𑣓𑣈𑣚-𑣓𑣖𑣈"
} }

View File

@@ -1,7 +1,8 @@
{ {
"@metadata": { "@metadata": {
"authors": [ "authors": [
"MokaAkashiyaPT" "MokaAkashiyaPT",
"Athena in Wonderland"
] ]
}, },
"uls-region-WW": "Global", "uls-region-WW": "Global",
@@ -12,6 +13,10 @@
"uls-region-AS": "Ásia", "uls-region-AS": "Ásia",
"uls-region-ME": "Médio Ouriente", "uls-region-ME": "Médio Ouriente",
"uls-region-PA": "Pacífico", "uls-region-PA": "Pacífico",
"uls-region-all": "Todas las lhénguas",
"uls-no-results-found": "Nó fúrun ancontrados resultados",
"uls-common-languages": "Lhénguas sugeridas", "uls-common-languages": "Lhénguas sugeridas",
"uls-no-results-suggestion-title": "Puode star antressado an:",
"uls-search-help": "Puode percurar pul nome de la lhéngua, pul tipo de abc, código ISO de la lhéngua, ó puode nabegar por region.",
"uls-search-placeholder": "Percurar por lhéngua" "uls-search-placeholder": "Percurar por lhéngua"
} }

View File

@@ -2,7 +2,8 @@
"@metadata": { "@metadata": {
"authors": [ "authors": [
"Siebrand", "Siebrand",
"Sjoerddebruin" "Sjoerddebruin",
"Mar(c)"
] ]
}, },
"uls-region-WW": "Wereldwijd", "uls-region-WW": "Wereldwijd",
@@ -16,7 +17,7 @@
"uls-region-all": "Alle talen", "uls-region-all": "Alle talen",
"uls-no-results-found": "Geen resultaten gevonden", "uls-no-results-found": "Geen resultaten gevonden",
"uls-common-languages": "Taalsuggesties", "uls-common-languages": "Taalsuggesties",
"uls-no-results-suggestion-title": "Wellicht wilt u een van de volgende talen kiezen:", "uls-no-results-suggestion-title": "Mogelijk bent u geïnteresseerd in:",
"uls-search-help": "U kunt zoeken op taalnaam, schriftnaam, ISO-code van de taal of u kunt bladeren per regio.", "uls-search-help": "U kunt zoeken op taalnaam, schriftnaam of ISO-code van de taal, of u kunt bladeren per regio.",
"uls-search-placeholder": "Zoeken naar een taal" "uls-search-placeholder": "Zoeken naar een taal"
} }

21
i18n/roa-tara.json Normal file
View File

@@ -0,0 +1,21 @@
{
"@metadata": {
"authors": [
"Joetaras"
]
},
"uls-region-WW": "Tutte 'u munne",
"uls-region-SP": "Speciale",
"uls-region-AM": "Americhe",
"uls-region-AF": "Afriche",
"uls-region-EU": "Europe",
"uls-region-AS": "Asia",
"uls-region-ME": "Medie oriende",
"uls-region-PA": "Pacifeche",
"uls-region-all": "Tutte le lènghe",
"uls-no-results-found": "Nisciune resultate acchiate",
"uls-common-languages": "Lènghe suggerite",
"uls-no-results-suggestion-title": "Puè essere 'nderessate a:",
"uls-search-help": "Puè cercà pe nome d'a lènghe, nome d'u script, codece ISO d'a lènghe o puè navegà passanne pa reggione.",
"uls-search-placeholder": "Cirche 'na lènghe"
}

11
i18n/twd.json Normal file
View File

@@ -0,0 +1,11 @@
{
"@metadata": {
"authors": [
"Robin van der Vliet"
]
},
"uls-region-WW": "Wearldwied",
"uls-region-AM": "Amerika",
"uls-region-AF": "Afrika",
"uls-region-EU": "Europa"
}

View File

@@ -5,16 +5,17 @@
] ]
}, },
"uls-region-WW": "Tuto el mondo", "uls-region-WW": "Tuto el mondo",
"uls-region-SP": "Speciałi", "uls-region-SP": "Speciaƚi",
"uls-region-AM": "Mèrica", "uls-region-AM": "Mèrica",
"uls-region-AF": "Àfrica", "uls-region-AF": "Àfrica",
"uls-region-EU": "Eoropa", "uls-region-EU": "Eoropa",
"uls-region-AS": "Axia", "uls-region-AS": "Axia",
"uls-region-ME": "Levante mexan", "uls-region-ME": "Levante mexan",
"uls-region-PA": "Pasifego", "uls-region-PA": "Pasifego",
"uls-region-all": "Tute ƚe ƚengue",
"uls-no-results-found": "Nisun rixultado", "uls-no-results-found": "Nisun rixultado",
"uls-common-languages": "Lengue pì sielte", "uls-common-languages": "Lengue sugerìe",
"uls-no-results-suggestion-title": "Te pol èsar interesà a:", "uls-no-results-suggestion-title": "Te pol èsar interesà a:",
"uls-search-help": "Xe posibiłe sercar par nome, nome del script, còdexe ISO de ła lengua o se pol navegar par region:", "uls-search-help": "Xe posibiƚe sercar par nòme, nòme del script, còdexe ISO de ƚa lengua o se pol navegar par region.",
"uls-search-placeholder": "Serca lengua" "uls-search-placeholder": "Serca na lengua"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="27" height="16"><path fill="#D3E3F4" d="M0 0h11.876v16h-11.876v-16z"/><path fill="#E9E9E9" d="M12.819 0v15.969h10.755c1.085-2.578 1.837-4.991 3.062-8.591l-3.062-7.378h-10.755z"/><path fill="#434343" d="M14.828 4.146c.32.023.639.035.951.035.471 0 .922-.016 1.354-.046l.023-.258.034-.367.081-.711.033-.333.873.058c-.073.577-.133 1.091-.179 1.542 1.116-.119 2.233-.331 3.349-.637l.092.809c-1.078.271-2.251.472-3.521.602-.05.463-.082.968-.098 1.514.524-.187 1.14-.313 1.847-.378.064-.207.136-.461.213-.763l.865.201c-.027.123-.088.315-.184.579.814.104 1.461.34 1.938.711.681.542 1.021 1.252 1.021 2.128 0 .963-.406 1.746-1.221 2.351-.635.47-1.505.784-2.609.94l-.505-.779c.968-.104 1.739-.337 2.317-.7.733-.458 1.101-1.066 1.101-1.823 0-.772-.341-1.363-1.021-1.772-.345-.21-.764-.344-1.256-.401-.604 1.422-1.4 2.598-2.387 3.527.035.352.102.715.201 1.09l-.849.314-.126-.773c-.654.462-1.27.693-1.847.693-.696 0-1.044-.407-1.044-1.222 0-1.104.591-2.103 1.771-2.993.227-.165.532-.352.918-.562.012-.524.046-1.128.104-1.812-.585.046-1.143.069-1.675.069l-.493-.011-.071-.822m2.127 3.384c-.268.161-.545.392-.831.694-.612.619-.94 1.226-.986 1.817l-.017.121.017.138c0 .252.112.378.339.378.492 0 1.02-.258 1.582-.773-.057-.613-.092-1.404-.104-2.375m2.455-.884c-.57.08-1.111.226-1.623.436 0 .883.018 1.581.052 2.093.634-.707 1.158-1.55 1.571-2.529"/><path fill="#1A1A1A" d="M8.517 11.735l-.734-2.409h-3.69l-.734 2.409h-2.313l3.573-10.165h2.625l3.587 10.165h-2.314m-1.246-4.21c-.679-2.183-1.062-3.418-1.149-3.704-.083-.286-.143-.512-.18-.678-.153.59-.589 2.051-1.309 4.382h2.638"/><path fill="#BCBCBC" d="M12.817 13.5v2.5h10.757l.98-2.5h-11.737z"/><path fill="#3692D0" d="M.011 13.511h3.569l2.367-3.116 2.367 3.116h3.57v2.484h-11.873v-2.484z"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/language.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

4
images/language.svg Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path d="M20 18h-1.44a.61.61 0 0 1-.4-.12.81.81 0 0 1-.23-.31L17 15h-5l-1 2.54a.77.77 0 0 1-.22.3.59.59 0 0 1-.4.14H9l4.55-11.47h1.89zm-3.53-4.31L14.89 9.5a11.62 11.62 0 0 1-.39-1.24q-.09.37-.19.69l-.19.56-1.58 4.19zm-6.3-1.58a13.43 13.43 0 0 1-2.91-1.41 11.46 11.46 0 0 0 2.81-5.37H12V4H7.31a4 4 0 0 0-.2-.56C6.87 2.79 6.6 2 6.6 2l-1.47.5s.4.89.6 1.5H0v1.33h2.15A11.23 11.23 0 0 0 5 10.7a17.19 17.19 0 0 1-5 2.1q.56.82.87 1.38a23.28 23.28 0 0 0 5.22-2.51 15.64 15.64 0 0 0 3.56 1.77zM3.63 5.33h4.91a8.11 8.11 0 0 1-2.45 4.45 9.11 9.11 0 0 1-2.46-4.45z" fill="#54595d"/>
</svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@@ -25,20 +25,23 @@
"Siebrand Mazeland" "Siebrand Mazeland"
], ],
"devDependencies": { "devDependencies": {
"eslint-config-wikimedia": "0.5.0",
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-cli": "^1.2.0", "grunt-eslint": "20.0.0",
"grunt-contrib-jshint": "^1.0.0", "grunt-stylelint": "0.8.0",
"grunt-contrib-qunit": "^1.2.0" "stylelint": "8.2.0",
"grunt-contrib-qunit": "^1.2.0",
"stylelint-config-wikimedia": "0.4.2"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/wikimedia/jquery.uls.git" "url": "git://github.com/wikimedia/jquery.uls.git"
}, },
"bugs": { "bugs": {
"url" : "http://github.com/wikimedia/jquery.uls/issues" "url" : "https://github.com/wikimedia/jquery.uls/issues"
}, },
"engine": { "engine": {
"node": ">=0.10.x" "node": ">=6.11.1"
}, },
"license": "(MIT OR GPL-2.0+)", "license": "(MIT OR GPL-2.0+)",
"scripts": { "scripts": {

View File

@@ -1,10 +1,11 @@
var through = require( 'through' ); var through = require( 'through' ); // eslint-disable-line no-implicit-globals
module.exports = function ( file ) { module.exports = function () {
var data, end, write; var data, end, write;
data = '( function ( $ ) {\n\t$.uls = $.uls || {};\n\t$.uls.data = '; data = '( function ( $ ) {\n\t$.uls = $.uls || {};\n\t$.uls.data = ';
write = function ( buf ) { write = function ( buf ) {
return data += buf; data += buf;
return data;
}; };
end = function () { end = function () {
data += '\n} ( jQuery ) );'; data += '\n} ( jQuery ) );';

View File

@@ -24,17 +24,17 @@
var template, ULS; var template, ULS;
// Region numbers in id attributes also appear in the langdb. // Region numbers in id attributes also appear in the langdb.
/*jshint multistr:true */ // eslint-disable-next-line no-multi-str
template = '<div class="grid uls-menu"> \ template = '<div class="grid uls-menu"> \
<div id="search" class="row uls-search"> \ <div id="search" class="row uls-search"> \
<div class="uls-search-wrapper"> \ <div class="uls-search-wrapper"> \
<label class="uls-search-label" for="uls-languagefilter"></label>\ <label class="uls-search-label" for="uls-languagefilter"></label>\
<div class="uls-search-input-wrapper">\ <div class="uls-search-input-wrapper">\
<span id="uls-languagefilter-clear" class="uls-languagefilter-clear"></span>\ <span class="uls-languagefilter-clear"></span>\
<input type="text" class="uls-filterinput uls-filtersuggestion"\ <input type="text" class="uls-filterinput uls-filtersuggestion"\
id="uls-filtersuggestion" disabled="true" autocomplete="off">\ disabled="true" autocomplete="off">\
<input type="text" class="uls-filterinput uls-languagefilter"\ <input type="text" class="uls-filterinput uls-languagefilter"\
id="uls-languagefilter" data-clear="uls-languagefilter-clear"\ data-clear="uls-languagefilter-clear"\
data-suggestion="uls-filtersuggestion"\ data-suggestion="uls-filtersuggestion"\
placeholder="Search for a language" autocomplete="off">\ placeholder="Search for a language" autocomplete="off">\
</div>\ </div>\
@@ -43,42 +43,20 @@
<div class="row uls-language-list"></div>\ <div class="row uls-language-list"></div>\
<div class="row" id="uls-settings-block"></div>\ <div class="row" id="uls-settings-block"></div>\
</div>'; </div>';
/*jshint multistr:false */
/**
* Count the number of keys in an object.
* Works in a cross-browser way.
* @param {Object} The object.
*/
function objectLength ( obj ) {
var count, key;
// Some old browsers don't support Object.keys
if ( Object.keys ) {
return Object.keys( obj ).length;
}
count = 0;
for ( key in obj ) {
if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
count++;
}
}
return count;
}
/** /**
* ULS Public class definition * ULS Public class definition
* @param {Element} element
* @param {Object} options
*/ */
ULS = function ( element, options ) { ULS = function ( element, options ) {
var code;
this.$element = $( element ); this.$element = $( element );
this.options = $.extend( {}, $.fn.uls.defaults, options ); this.options = $.extend( {}, $.fn.uls.defaults, options );
this.$menu = $( template ); this.$menu = $( template );
this.languages = this.options.languages; this.languages = this.options.languages;
for ( var code in this.languages ) { for ( code in this.languages ) {
if ( $.uls.data.languages[ code ] === undefined ) { if ( $.uls.data.languages[ code ] === undefined ) {
// Language is unknown to ULS. // Language is unknown to ULS.
delete this.languages[ code ]; delete this.languages[ code ];
@@ -90,7 +68,7 @@
this.shown = false; this.shown = false;
this.initialized = false; this.initialized = false;
this.$languageFilter = this.$menu.find( '#uls-languagefilter' ); this.$languageFilter = this.$menu.find( '.uls-languagefilter' );
this.$resultsView = this.$menu.find( '.uls-language-list' ); this.$resultsView = this.$menu.find( '.uls-language-list' );
this.render(); this.render();
@@ -131,7 +109,7 @@
/** /**
* Calculate the position of ULS * Calculate the position of ULS
* Returns an object with top and left properties. * Returns an object with top and left properties.
* @returns {Object} * @return {Object}
*/ */
position: function () { position: function () {
var pos, var pos,
@@ -142,14 +120,13 @@
pos = $.extend( {}, this.$element.offset(), { pos = $.extend( {}, this.$element.offset(), {
height: this.$element[ 0 ].offsetHeight height: this.$element[ 0 ].offsetHeight
} ); } );
top = pos.top + pos.height; top = pos.top + pos.height;
} }
if ( left === undefined ) { if ( left === undefined ) {
left = $( window ).width() / 2 - this.$menu.outerWidth() / 2; left = $( window ).width() / 2 - this.$menu.outerWidth() / 2;
} }
return { return {
top: top, top: top,
left: left left: left
@@ -166,7 +143,7 @@
narrow: 'uls-narrow' narrow: 'uls-narrow'
}; };
this.$menu.addClass( widthClasses[this.getMenuWidth()] ); this.$menu.addClass( widthClasses[ this.getMenuWidth() ] );
if ( !this.initialized ) { if ( !this.initialized ) {
$( 'body' ).prepend( this.$menu ); $( 'body' ).prepend( this.$menu );
@@ -213,13 +190,6 @@
// Rendering stuff here // Rendering stuff here
}, },
/**
* Callback for no results found context.
*/
noresults: function () {
this.$resultsView.lcd( 'noResults' );
},
/** /**
* Callback for results found context. * Callback for results found context.
*/ */
@@ -248,33 +218,30 @@
} ); } );
// Handle key press events on the menu // Handle key press events on the menu
this.$menu.on( 'keypress', $.proxy( this.keypress, this ) ) this.$menu.on( 'keydown', $.proxy( this.keypress, this ) );
.on( 'keyup', $.proxy( this.keyup, this ) );
if ( this.eventSupported( 'keydown' ) ) { languagesCount = Object.keys( this.options.languages ).length;
this.$menu.on( 'keydown', $.proxy( this.keypress, this ) );
}
languagesCount = objectLength( this.options.languages );
lcd = this.$resultsView.lcd( { lcd = this.$resultsView.lcd( {
languages: this.languages, languages: this.languages,
columns: columnsOptions[ this.getMenuWidth() ], columns: columnsOptions[ this.getMenuWidth() ],
quickList: languagesCount > 12 ? this.options.quickList : [], quickList: languagesCount > 12 ? this.options.quickList : [],
clickhandler: $.proxy( this.select, this ), clickhandler: $.proxy( this.select, this ),
source: this.$languageFilter,
showRegions: this.options.showRegions, showRegions: this.options.showRegions,
languageDecorator: this.options.languageDecorator languageDecorator: this.options.languageDecorator,
noResultsTemplate: this.options.noResultsTemplate,
itemsPerColumn: this.options.itemsPerColumn,
groupByRegion: this.options.groupByRegion
} ).data( 'lcd' ); } ).data( 'lcd' );
this.$languageFilter.languagefilter( { this.$languageFilter.languagefilter( {
$target: lcd, lcd: lcd,
languages: this.languages, languages: this.languages,
searchAPI: this.options.searchAPI, searchAPI: this.options.searchAPI,
onSelect: $.proxy( this.select, this ) onSelect: $.proxy( this.select, this )
} ); } );
this.$languageFilter.on( 'noresults.uls', $.proxy( this.noresults, this ) ); this.$languageFilter.on( 'noresults.uls', $.proxy( lcd.noResults, lcd ) );
this.$languageFilter.on( 'resultsfound.uls', $.proxy( this.success, this ) ); this.$languageFilter.on( 'resultsfound.uls', $.proxy( this.success, this ) );
$( 'html' ).click( $.proxy( this.cancel, this ) ); $( 'html' ).click( $.proxy( this.cancel, this ) );
@@ -282,7 +249,7 @@
/** /**
* On select handler for search results * On select handler for search results
* @param langCode * @param {string} langCode
*/ */
select: function ( langCode ) { select: function ( langCode ) {
this.hide(); this.hide();
@@ -293,27 +260,16 @@
/** /**
* On cancel handler for the uls menu * On cancel handler for the uls menu
* @param {Event} e
*/ */
cancel: function ( e ) { cancel: function ( e ) {
if ( e && ( this.$element.is( e.target ) || $.contains( this.$element[0], e.target ) ) ) { if ( e && ( this.$element.is( e.target ) || $.contains( this.$element[ 0 ], e.target ) ) ) {
return; return;
} }
this.hide(); this.hide();
}, },
keyup: function ( e ) {
if ( !this.shown ) {
return;
}
if ( e.keyCode === 27 ) { // escape
this.cancel();
e.preventDefault();
e.stopPropagation();
}
},
keypress: function ( e ) { keypress: function ( e ) {
if ( !this.shown ) { if ( !this.shown ) {
return; return;
@@ -334,20 +290,9 @@
} }
}, },
eventSupported: function ( eventName ) {
var isSupported = eventName in this.$menu;
if ( !isSupported ) {
this.$element.setAttribute( eventName, 'return;' );
isSupported = typeof this.$element[ eventName ] === 'function';
}
return isSupported;
},
/** /**
* Get the panel menu width parameter * Get the panel menu width parameter
* @return string * @return {string}
*/ */
getMenuWidth: function () { getMenuWidth: function () {
var languagesCount; var languagesCount;
@@ -356,7 +301,7 @@
return this.options.menuWidth; return this.options.menuWidth;
} }
languagesCount = objectLength( this.options.languages ); languagesCount = Object.keys( this.options.languages ).length;
if ( languagesCount < 25 ) { if ( languagesCount < 25 ) {
return 'narrow'; return 'narrow';
@@ -394,15 +339,35 @@
}; };
$.fn.uls.defaults = { $.fn.uls.defaults = {
onSelect: null, // Callback function to be called when a language is selected // CSS top position for the dialog
searchAPI: null, // Language search API top: undefined,
languages: $.uls.data.getAutonyms(), // Languages to be used for ULS, default is all languages // CSS left position for the dialog
quickList: [], // Array of language codes or function that returns such left: undefined,
// Callback function when user selects a language
onSelect: undefined,
// Callback function when the dialog is closed without selecting a language
onCancel: undefined,
// Callback function when ULS has initialized
onReady: undefined,
// Callback function when ULS dialog is shown
onVisible: undefined,
// Languages to be used for ULS, default is all languages
languages: $.uls.data.getAutonyms(),
// The options are wide (4 columns), medium (2 columns), and narrow (1 column). // The options are wide (4 columns), medium (2 columns), and narrow (1 column).
// If not specified, it will be set automatically. // If not specified, it will be set automatically.
menuWidth: null, menuWidth: undefined,
showRegions: [ 'WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA' ], // Used by LCD
languageDecorator: null // Callback function to be called when a language link is prepared - for custom decoration. quickList: [],
// Used by LCD
showRegions: undefined,
// Used by LCD
languageDecorator: undefined,
// Used by LCD
itemsPerColumn: undefined,
// Used by LCD
groupByRegion: undefined,
// Used by LanguageFilter
searchAPI: undefined
}; };
// Define a dummy i18n function, if jquery.i18n not integrated. // Define a dummy i18n function, if jquery.i18n not integrated.

View File

@@ -897,7 +897,8 @@ module.exports=( function ( $ ) {
"EU", "EU",
"AM", "AM",
"AF", "AF",
"WW" "WW",
"PA"
], ],
"español" "español"
], ],
@@ -1360,6 +1361,14 @@ module.exports=( function ( $ ) {
], ],
"Հայերեն" "Հայերեն"
], ],
"hyw": [
"Armn",
[
"EU",
"ME"
],
"արեւմտահայերէն"
],
"hz": [ "hz": [
"Latn", "Latn",
[ [
@@ -1801,6 +1810,13 @@ module.exports=( function ( $ ) {
], ],
"Kurdî" "Kurdî"
], ],
"kum": [
"Cyrl",
[
"EU"
],
"къумукъ"
],
"kv": [ "kv": [
"Cyrl", "Cyrl",
[ [
@@ -5084,6 +5100,7 @@ module.exports=( function ( $ ) {
"kbd", "kbd",
"myv", "myv",
"mdf", "mdf",
"kum",
"kv", "kv",
"lez", "lez",
"krc", "krc",
@@ -5449,6 +5466,5 @@ module.exports=( function ( $ ) {
] ]
} }
} }
} ( jQuery ) ); } ( jQuery ) );
},{}]},{},[1]); },{}]},{},[1]);

View File

@@ -22,18 +22,18 @@
/** /**
* Is this language a redirect to another language? * Is this language a redirect to another language?
* @param language string Language code * @param {string} language Language code
* @return Target language code if it's a redirect or false if it's not * @return {string|boolean} Target language code if it's a redirect or false if it's not
*/ */
$.uls.data.isRedirect = function ( language ) { $.uls.data.isRedirect = function ( language ) {
return ( $.uls.data.languages[language] !== undefined && return ( $.uls.data.languages[ language ] !== undefined &&
$.uls.data.languages[language].length === 1 ) ? $.uls.data.languages[language][0] : false; $.uls.data.languages[ language ].length === 1 ) ? $.uls.data.languages[ language ][ 0 ] : false;
}; };
/** /**
* Returns the script of the language. * Returns the script of the language.
* @param language string Language code * @param {string} language Language code
* @return string * @return {string}
*/ */
$.uls.data.getScript = function ( language ) { $.uls.data.getScript = function ( language ) {
var target = $.uls.data.isRedirect( language ); var target = $.uls.data.isRedirect( language );
@@ -42,18 +42,18 @@
return $.uls.data.getScript( target ); return $.uls.data.getScript( target );
} }
if ( !$.uls.data.languages[language] ) { if ( !$.uls.data.languages[ language ] ) {
// Undetermined // Undetermined
return 'Zyyy'; return 'Zyyy';
} }
return $.uls.data.languages[language][0]; return $.uls.data.languages[ language ][ 0 ];
}; };
/** /**
* Returns the regions in which a language is spoken. * Returns the regions in which a language is spoken.
* @param language string Language code * @param {string} language Language code
* @return array|string 'UNKNOWN' * @return {string|string[]}
*/ */
$.uls.data.getRegions = function ( language ) { $.uls.data.getRegions = function ( language ) {
var target = $.uls.data.isRedirect( language ); var target = $.uls.data.isRedirect( language );
@@ -62,13 +62,13 @@
return $.uls.data.getRegions( target ); return $.uls.data.getRegions( target );
} }
return ( $.uls.data.languages[language] && $.uls.data.languages[language][1] ) || 'UNKNOWN'; return ( $.uls.data.languages[ language ] && $.uls.data.languages[ language ][ 1 ] ) || 'UNKNOWN';
}; };
/** /**
* Returns the autonym of the language. * Returns the autonym of the language.
* @param language string Language code * @param {string} language Language code
* @return string * @return {string}
*/ */
$.uls.data.getAutonym = function ( language ) { $.uls.data.getAutonym = function ( language ) {
var target = $.uls.data.isRedirect( language ); var target = $.uls.data.isRedirect( language );
@@ -77,12 +77,12 @@
return $.uls.data.getAutonym( target ); return $.uls.data.getAutonym( target );
} }
return ( $.uls.data.languages[language] && $.uls.data.languages[language][2] ) || language; return ( $.uls.data.languages[ language ] && $.uls.data.languages[ language ][ 2 ] ) || language;
}; };
/** /**
* Returns all language codes and corresponding autonyms * Returns all language codes and corresponding autonyms
* @return array * @return {string[]}
*/ */
$.uls.data.getAutonyms = function () { $.uls.data.getAutonyms = function () {
var language, var language,
@@ -93,7 +93,7 @@
continue; continue;
} }
autonymsByCode[language] = $.uls.data.getAutonym( language ); autonymsByCode[ language ] = $.uls.data.getAutonym( language );
} }
return autonymsByCode; return autonymsByCode;
@@ -101,8 +101,8 @@
/** /**
* Returns all languages written in script. * Returns all languages written in script.
* @param script string * @param {string} script string
* @return array of strings (languages codes) * @return {string[]} languages codes
*/ */
$.uls.data.getLanguagesInScript = function ( script ) { $.uls.data.getLanguagesInScript = function ( script ) {
return $.uls.data.getLanguagesInScripts( [ script ] ); return $.uls.data.getLanguagesInScripts( [ script ] );
@@ -110,8 +110,8 @@
/** /**
* Returns all languages written in the given scripts. * Returns all languages written in the given scripts.
* @param scripts array of strings * @param {string[]} scripts
* @return array of strings (languages codes) * @return {string[]} languages codes
*/ */
$.uls.data.getLanguagesInScripts = function ( scripts ) { $.uls.data.getLanguagesInScripts = function ( scripts ) {
var language, i, var language, i,
@@ -123,7 +123,7 @@
} }
for ( i = 0; i < scripts.length; i++ ) { for ( i = 0; i < scripts.length; i++ ) {
if ( scripts[i] === $.uls.data.getScript( language ) ) { if ( scripts[ i ] === $.uls.data.getScript( language ) ) {
languagesInScripts.push( language ); languagesInScripts.push( language );
break; break;
} }
@@ -136,8 +136,8 @@
/** /**
* Returns an associative array of languages in a region, * Returns an associative array of languages in a region,
* grouped by script group. * grouped by script group.
* @param region string Region code * @param {string} region Region code
* @return associative array * @return {object}
*/ */
$.uls.data.getLanguagesByScriptGroupInRegion = function ( region ) { $.uls.data.getLanguagesByScriptGroupInRegion = function ( region ) {
return $.uls.data.getLanguagesByScriptGroupInRegions( [ region ] ); return $.uls.data.getLanguagesByScriptGroupInRegions( [ region ] );
@@ -145,7 +145,7 @@
/** /**
* Get the given list of languages grouped by script. * Get the given list of languages grouped by script.
* @param languages Array of language codes * @param {string} languages Array of language codes
* @return {Object} Array of languages indexed by script codes * @return {Object} Array of languages indexed by script codes
*/ */
$.uls.data.getLanguagesByScriptGroup = function ( languages ) { $.uls.data.getLanguagesByScriptGroup = function ( languages ) {
@@ -157,11 +157,11 @@
langScriptGroup = $.uls.data.getScriptGroupOfLanguage( resolvedRedirect ); langScriptGroup = $.uls.data.getScriptGroupOfLanguage( resolvedRedirect );
if ( !languagesByScriptGroup[langScriptGroup] ) { if ( !languagesByScriptGroup[ langScriptGroup ] ) {
languagesByScriptGroup[langScriptGroup] = []; languagesByScriptGroup[ langScriptGroup ] = [];
} }
languagesByScriptGroup[langScriptGroup].push( language ); languagesByScriptGroup[ langScriptGroup ].push( language );
} }
return languagesByScriptGroup; return languagesByScriptGroup;
@@ -170,8 +170,8 @@
/** /**
* Returns an associative array of languages in several regions, * Returns an associative array of languages in several regions,
* grouped by script group. * grouped by script group.
* @param regions array of strings - region codes * @param {string[]} regions region codes
* @return associative array * @return {Object}
*/ */
$.uls.data.getLanguagesByScriptGroupInRegions = function ( regions ) { $.uls.data.getLanguagesByScriptGroupInRegions = function ( regions ) {
var language, i, scriptGroup, var language, i, scriptGroup,
@@ -183,14 +183,14 @@
} }
for ( i = 0; i < regions.length; i++ ) { for ( i = 0; i < regions.length; i++ ) {
if ( $.inArray( regions[i], $.uls.data.getRegions( language ) ) !== -1 ) { if ( $.inArray( regions[ i ], $.uls.data.getRegions( language ) ) !== -1 ) {
scriptGroup = $.uls.data.getScriptGroupOfLanguage( language ); scriptGroup = $.uls.data.getScriptGroupOfLanguage( language );
if ( languagesByScriptGroupInRegions[scriptGroup] === undefined ) { if ( languagesByScriptGroupInRegions[ scriptGroup ] === undefined ) {
languagesByScriptGroupInRegions[scriptGroup] = []; languagesByScriptGroupInRegions[ scriptGroup ] = [];
} }
languagesByScriptGroupInRegions[scriptGroup].push( language ); languagesByScriptGroupInRegions[ scriptGroup ].push( language );
break; break;
} }
} }
@@ -202,14 +202,14 @@
/** /**
* Returns the script group of a script or 'Other' if it doesn't * Returns the script group of a script or 'Other' if it doesn't
* belong to any group. * belong to any group.
* @param script string Script code * @param {string} script Script code
* @return string script group name * @return {string} script group name
*/ */
$.uls.data.getGroupOfScript = function ( script ) { $.uls.data.getGroupOfScript = function ( script ) {
var scriptGroup; var scriptGroup;
for ( scriptGroup in $.uls.data.scriptgroups ) { for ( scriptGroup in $.uls.data.scriptgroups ) {
if ( $.inArray( script, $.uls.data.scriptgroups[scriptGroup] ) !== -1 ) { if ( $.inArray( script, $.uls.data.scriptgroups[ scriptGroup ] ) !== -1 ) {
return scriptGroup; return scriptGroup;
} }
} }
@@ -219,8 +219,8 @@
/** /**
* Returns the script group of a language. * Returns the script group of a language.
* @param language string Language code * @param {string} language Language code
* @return string script group name * @return {string} script group name
*/ */
$.uls.data.getScriptGroupOfLanguage = function ( language ) { $.uls.data.getScriptGroupOfLanguage = function ( language ) {
return $.uls.data.getGroupOfScript( $.uls.data.getScript( language ) ); return $.uls.data.getGroupOfScript( $.uls.data.getScript( language ) );
@@ -229,8 +229,9 @@
/** /**
* A callback for sorting languages by autonym. * A callback for sorting languages by autonym.
* Can be used as an argument to a sort function. * Can be used as an argument to a sort function.
* @param a string Language code * @param {string} a Language code
* @param b string Language code * @param {string} b Language code
* @return {number}
*/ */
$.uls.data.sortByAutonym = function ( a, b ) { $.uls.data.sortByAutonym = function ( a, b ) {
var autonymA = $.uls.data.getAutonym( a ) || a, var autonymA = $.uls.data.getAutonym( a ) || a,
@@ -241,8 +242,8 @@
/** /**
* Check if a language is right-to-left. * Check if a language is right-to-left.
* @param language string Language code * @param {string} language Language code
* @return boolean * @return {boolean}
*/ */
$.uls.data.isRtl = function ( language ) { $.uls.data.isRtl = function ( language ) {
return $.inArray( $.uls.data.getScript( language ), $.uls.data.rtlscripts ) !== -1; return $.inArray( $.uls.data.getScript( language ), $.uls.data.rtlscripts ) !== -1;
@@ -250,8 +251,8 @@
/** /**
* Return the direction of the language * Return the direction of the language
* @param language string Language code * @param {string} language Language code
* @return string * @return {string}
*/ */
$.uls.data.getDir = function ( language ) { $.uls.data.getDir = function ( language ) {
return $.uls.data.isRtl( language ) ? 'rtl' : 'ltr'; return $.uls.data.isRtl( language ) ? 'rtl' : 'ltr';
@@ -259,11 +260,11 @@
/** /**
* Returns the languages spoken in a territory. * Returns the languages spoken in a territory.
* @param territory string Territory code * @param {string} territory Territory code
* @return list of language codes * @return {string[]} list of language codes
*/ */
$.uls.data.getLanguagesInTerritory = function ( territory ) { $.uls.data.getLanguagesInTerritory = function ( territory ) {
return $.uls.data.territories[territory]; return $.uls.data.territories[ territory ];
}; };
/** /**
@@ -271,30 +272,30 @@
* If the target option is provided, the language is defined as a redirect. * If the target option is provided, the language is defined as a redirect.
* Other possible options are script, regions and autonym. * Other possible options are script, regions and autonym.
* *
* @param code string New language code. * @param {string} code New language code.
* @param options Object Language properties. * @param {Object} options Language properties.
*/ */
$.uls.data.addLanguage = function( code, options ) { $.uls.data.addLanguage = function ( code, options ) {
if ( options.target ) { if ( options.target ) {
$.uls.data.languages[code] = [options.target]; $.uls.data.languages[ code ] = [ options.target ];
} else { } else {
$.uls.data.languages[code] = [options.script, options.regions, options.autonym]; $.uls.data.languages[ code ] = [ options.script, options.regions, options.autonym ];
} }
}; };
/** /**
* Removes a language from the langdb in run time. * Removes a language from the langdb in run time.
* *
* @param code string Language code to delete. * @param {string} code Language code to delete.
* @return true if the language was removed, false otherwise. * @return {boolean} true if the language was removed, false otherwise.
*/ */
$.uls.data.deleteLanguage = function( code ) { $.uls.data.deleteLanguage = function ( code ) {
if ( $.uls.data.languages[code] ) { if ( $.uls.data.languages[ code ] ) {
delete $.uls.data.languages[code]; delete $.uls.data.languages[ code ];
return true; return true;
} }
return false; return false;
}; };
} ( jQuery ) ); }( jQuery ) );

View File

@@ -19,21 +19,34 @@
/** /**
* Usage: $( 'inputbox' ).languagefilter(); * Usage: $( 'inputbox' ).languagefilter();
* The values for autocompletion is from the options.languages. * The values for autocompletion is from the options.languages or options.searchAPI.
* The data is in the format of languagecode:languagename.
*/ */
( function ( $ ) { ( function ( $ ) {
'use strict'; 'use strict';
var LanguageFilter, delay; var LanguageFilter, delay;
/**
* Check if a prefix is visually prefix of a string
*
* @param {string} prefix
* @param {string} string
* @return {boolean}
*/
function isVisualPrefix( prefix, string ) {
// Pre-base vowel signs of Indic languages. A vowel sign is called pre-base if
// consonant + vowel becomes [vowel][consonant] when rendered. Eg: ക + െ => കെ
var prebases = 'െേൈൊോൌெேைொோௌେୈୋୌિਿिিেৈোৌෙේෛොෝෞ';
return prebases.indexOf( string[ prefix.length ] ) <= 0;
}
LanguageFilter = function ( element, options ) { LanguageFilter = function ( element, options ) {
this.$element = $( element ); this.$element = $( element );
this.options = $.extend( {}, $.fn.languagefilter.defaults, options ); this.options = $.extend( {}, $.fn.languagefilter.defaults, options );
this.$element.addClass( 'languagefilter' ); this.$element.addClass( 'languagefilter' );
this.resultCount = 0; this.resultCount = 0;
this.$suggestion = this.$element.parents().find( '#' + this.$element.data( 'suggestion' ) ); this.$suggestion = this.$element.siblings( '.' + this.$element.data( 'suggestion' ) );
this.$clear = this.$element.parents().find( '#' + this.$element.data( 'clear' ) ); this.$clear = this.$element.siblings( '.' + this.$element.data( 'clear' ) );
this.selectedLanguage = null; this.selectedLanguage = null;
this.init(); this.init();
this.listen(); this.listen();
@@ -54,12 +67,8 @@
}, },
listen: function () { listen: function () {
this.$element.on( 'keypress', $.proxy( this.keyup, this ) )
.on( 'keyup', $.proxy( this.keyup, this ) );
if ( this.eventSupported( 'keydown' ) ) { this.$element.on( 'keydown', $.proxy( this.keypress, this ) );
this.$element.on( 'keydown', $.proxy( this.keyup, this ) );
}
if ( this.$clear.length ) { if ( this.$clear.length ) {
this.$clear.on( 'click', $.proxy( this.clear, this ) ); this.$clear.on( 'click', $.proxy( this.clear, this ) );
@@ -68,62 +77,62 @@
this.toggleClear(); this.toggleClear();
}, },
keyup: function ( e ) { keypress: function ( e ) {
var suggestion, query, languageFilter; var suggestion, query, languageFilter;
switch ( e.keyCode ) { switch ( e.keyCode ) {
case 9: // Tab -> Autocomplete case 9: // Tab -> Autocomplete
suggestion = this.$suggestion.val(); suggestion = this.$suggestion.val();
if ( suggestion && suggestion !== this.$element.val() ) { if ( suggestion && suggestion !== this.$element.val() ) {
this.$element.val( suggestion ); this.$element.val( suggestion );
e.preventDefault();
e.stopPropagation();
}
break;
case 13: // Enter
if ( !this.options.onSelect ) {
break;
}
// Avoid bubbling this 'enter' to background page elements
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}
break;
case 13: // Enter
if ( !this.options.onSelect ) {
break;
}
// Avoid bubbling this 'enter' to background page elements query = $.trim( this.$element.val() ).toLowerCase();
e.preventDefault();
e.stopPropagation();
query = $.trim( this.$element.val() ).toLowerCase(); if ( this.selectedLanguage ) {
// this.selectLanguage will be populated from a matching search
if ( this.selectedLanguage ) { this.options.onSelect( this.selectedLanguage );
// this.selectLanguage will be populated from a matching search } else if ( this.options.languages[ query ] ) {
this.options.onSelect( this.selectedLanguage ); // Search is yet to happen (in timeout delay),
} else if ( this.options.languages[ query ] ) { // but we have a matching language code.
// Search is yet to happen (in timeout delay), this.options.onSelect( query );
// but we have a matching language code.
this.options.onSelect( query );
}
break;
default:
languageFilter = this;
if ( e.which < 32 &&
e.which !== 8 // Backspace
) {
// ignore any ASCII control characters
break;
}
this.selectedLanguage = null;
delay( function () {
if ( !languageFilter.$element.val() ) {
languageFilter.clear();
} else {
languageFilter.options.$target.empty();
languageFilter.search();
} }
}, 300 );
this.toggleClear(); break;
default:
languageFilter = this;
if ( e.which < 32 &&
e.which !== 8 // Backspace
) {
// ignore any ASCII control characters
break;
}
this.selectedLanguage = null;
delay( function () {
if ( !languageFilter.$element.val() ) {
languageFilter.clear();
} else {
languageFilter.options.lcd.empty();
languageFilter.search();
}
}, 300 );
this.toggleClear();
} }
}, },
@@ -167,86 +176,75 @@
}, },
search: function () { search: function () {
var langCode, scriptGroup, langNum, languagesInScript, var languages = Object.keys( this.options.languages ),
languages = $.uls.data.getLanguagesByScriptGroup( this.options.languages ), results = [],
query = $.trim( this.$element.val() ); query = $.trim( this.$element.val() ).toLowerCase();
this.resultCount = 0; if ( query === '' ) {
for ( scriptGroup in languages ) { this.options.lcd.setGroupByRegionOverride( null );
languagesInScript = languages[ scriptGroup ]; this.resultHandler( query, languages );
languagesInScript.sort( $.uls.data.sortByAutonym ); return;
for ( langNum = 0; langNum < languagesInScript.length; langNum++ ) {
langCode = languagesInScript[ langNum ];
if ( query === '' || this.filter( langCode, query ) ) {
if ( this.resultCount === 0 ) {
// Autofill the first result.
this.autofill( langCode );
}
if ( query.toLowerCase() === langCode ) {
this.selectedLanguage = langCode;
}
if ( this.render( langCode ) ) {
this.resultCount++;
}
}
}
} }
// Also do a search by search API this.options.lcd.setGroupByRegionOverride( false );
if ( !this.resultCount && this.options.searchAPI && query ) { // Local search results
this.searchAPI( query ); results = languages.filter( function ( langCode ) {
return this.filter( langCode, query );
}.bind( this ) );
// Use the searchAPI if available, assuming that it has superior search results.
if ( this.options.searchAPI ) {
this.searchAPI( query )
.done( this.resultHandler.bind( this ) )
.fail( this.resultHandler.bind( this, query, results, undefined ) );
} else { } else {
this.resultHandler( query ); this.resultHandler( query, results );
} }
}, },
searchAPI: function ( query ) { searchAPI: function ( query ) {
var languageFilter = this; return $.get( this.options.searchAPI, { search: query } ).then( function ( result ) {
var autofillLabel,
results = [];
$.get( languageFilter.options.searchAPI, {
search: query
}, function ( result ) {
$.each( result.languagesearch, function ( code, name ) { $.each( result.languagesearch, function ( code, name ) {
var target; if ( this.options.languages[ code ] ) {
autofillLabel = autofillLabel || name;
if ( languageFilter.resultCount === 0 ) { results.push( code );
// Autofill the first result. return;
languageFilter.autofill( code, name );
}
if ( languageFilter.options.languages[ code ] &&
languageFilter.render( code )
) {
languageFilter.resultCount++;
} }
// Try to hide issues caused by inconsistent language codes // Try to hide issues caused by inconsistent language codes
target = $.uls.data.isRedirect( code ); target = $.uls.data.isRedirect( code );
if ( languageFilter.options.languages[ target ] && if ( target && this.options.languages[ target ] ) {
languageFilter.render( target ) autofillLabel = autofillLabel || name;
) { results.push( target );
languageFilter.resultCount++;
} }
} ); return $.Deferred().resolve( query, results, autofillLabel );
}.bind( this ) );
languageFilter.resultHandler( query );
} );
}, },
/** /**
* Handler method to be called once search is over. * Handler method to be called once search is over.
* Based on search result triggers resultsfound or noresults events * Based on search result triggers resultsfound or noresults events
* @param query string * @param {string} query
* @param {string[]} results
* @param {string} [autofillLabel]
*/ */
resultHandler: function ( query ) { resultHandler: function ( query, results, autofillLabel ) {
if ( this.resultCount === 0 ) { if ( results.length === 0 ) {
this.$suggestion.val( '' ); this.$suggestion.val( '' );
this.$element.trigger( 'noresults.uls', query ); this.$element.trigger( 'noresults.uls', query );
} else { return;
this.$element.trigger( 'resultsfound.uls', [ query, this.resultCount ] );
} }
if ( query ) {
this.selectedLanguage = results[ 0 ];
this.autofill( results[ 0 ], autofillLabel );
}
results.map( this.render.bind( this ) );
this.$element.trigger( 'resultsfound.uls', [ query, results.length ] );
}, },
autofill: function ( langCode, languageName ) { autofill: function ( langCode, languageName ) {
@@ -261,7 +259,6 @@
return; return;
} }
this.selectedLanguage = langCode;
languageName = languageName || this.options.languages[ langCode ]; languageName = languageName || this.options.languages[ langCode ];
if ( !languageName ) { if ( !languageName ) {
@@ -291,17 +288,11 @@
}, },
render: function ( langCode ) { render: function ( langCode ) {
var $target = this.options.$target; return this.options.lcd.append( langCode );
if ( !$target ) {
return false;
}
return $target.append( langCode );
}, },
escapeRegex: function ( value ) { escapeRegex: function ( value ) {
return value.replace( /[\-\[\]{}()*+?.,\\\^$\|#\s]/g, '\\$&' ); return value.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
}, },
/** /**
@@ -311,6 +302,9 @@
* b) Language autonym 'starts with' search string. * b) Language autonym 'starts with' search string.
* c) ISO 639 code match with search string. * c) ISO 639 code match with search string.
* d) ISO 15924 code for the script match the search string. * d) ISO 15924 code for the script match the search string.
* @param {string} langCode
* @param {string} searchTerm
* @return {boolean}
*/ */
filter: function ( langCode, searchTerm ) { filter: function ( langCode, searchTerm ) {
// FIXME script is ISO 15924 code. We might need actual name of script. // FIXME script is ISO 15924 code. We might need actual name of script.
@@ -321,17 +315,6 @@
matcher.test( $.uls.data.getAutonym( langCode ) ) || matcher.test( $.uls.data.getAutonym( langCode ) ) ||
matcher.test( langCode ) || matcher.test( langCode ) ||
matcher.test( $.uls.data.getScript( langCode ) ); matcher.test( $.uls.data.getScript( langCode ) );
},
eventSupported: function ( eventName ) {
var isSupported = eventName in this.$element;
if ( !isSupported ) {
this.$element.setAttribute( eventName, 'return;' );
isSupported = typeof this.$element[ eventName ] === 'function';
}
return isSupported;
} }
}; };
@@ -352,24 +335,16 @@
}; };
$.fn.languagefilter.defaults = { $.fn.languagefilter.defaults = {
$target: null, // Where to append the results // LanguageCategoryDisplay
searchAPI: null, lcd: undefined,
languages: null, // Languages as code:name format. // URL to which we append query parameter with the query value
onSelect: null // Language select handler - like enter in filter textbox. searchAPI: undefined,
// Object of language tags to language names
languages: [],
// Callback function when language is selected
onSelect: undefined
}; };
$.fn.languagefilter.Constructor = LanguageFilter; $.fn.languagefilter.Constructor = LanguageFilter;
/**
* Check if a prefix is visually prefix of a string
*
* @param {string} prefix
* @param {string} string
*/
function isVisualPrefix( prefix, string ) {
// Pre-base vowel signs of Indic languages. A vowel sign is called pre-base if
// consonant + vowel becomes [vowel][consonant] when rendered. Eg: ക + െ => കെ
var prebases = 'െേൈൊോൌெேைொோௌେୈୋୌિਿिিেৈোৌෙේෛොෝෞ';
return prebases.indexOf( string[ prefix.length ] ) <= 0;
}
}( jQuery ) ); }( jQuery ) );

View File

@@ -22,43 +22,46 @@
( function ( $ ) { ( function ( $ ) {
'use strict'; 'use strict';
var noResultsTemplate, LanguageCategoryDisplay; // eslint-disable-next-line no-multi-str
var noResultsTemplate = '<div class="uls-no-results-view"> \
<h2 data-i18n="uls-no-results-found" class="uls-no-results-found-title">No results found</h2> \
<div class="uls-no-results-suggestions"></div> \
<div class="uls-no-found-more"> \
<div data-i18n="uls-search-help">You can search by language name, script name, ISO code of language or you can browse by region.</div> \
</div></div>';
noResultsTemplate = $( '<div>' ).addClass( 'uls-no-results-view hide' ); /**
noResultsTemplate.append( * Language category display
$( '<h2>' ) * @param {Element} element The container element to which the languages to be displayed
.attr( 'data-i18n', 'uls-no-results-found' ) * @param {Object} [options] Configuration object
.addClass( 'uls-no-results-found-title' ) * @cfg {Object} [languages] Selectable languages. Keyed by language code, values are autonyms.
.text( 'No results found' ), * @cfg {string[]} [showRegions] Array of region codes to show. Default is
$( '<div>' ) * [ 'WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA' ]
.addClass( 'uls-no-found-more' ) * @cfg {number} [itemsPerColumn] Number of languages per column.
.append( * @cfg {number} [columns] Number of columns for languages. Default is 4.
$( '<div>' ) * @cfg {Function} [languageDecorator] Callback function to be called when a language
.addClass( '' ) * link is prepared - for custom decoration.
.append( * @cfg {Function|string[]} [quickList] The languages to display as suggestions for quick selection.
$( '<p>' ).append( * @cfg {Function} [clickhandler] Callback when language is selected.
$( '<span>' ) * @cfg {jQuery|Function} [noResultsTemplate]
.attr( 'data-i18n', 'uls-search-help' ) */
.text( 'You can search by language name, script name, ISO code of language or you can browse by region.' ) function LanguageCategoryDisplay( element, options ) {
)
)
)
);
LanguageCategoryDisplay = function ( element, options ) {
this.$element = $( element ); this.$element = $( element );
this.options = $.extend( {}, $.fn.lcd.defaults, options ); this.options = $.extend( {}, $.fn.lcd.defaults, options );
// Ensure the internal region 'all' is always present
if ( this.options.showRegions.indexOf( 'all' ) === -1 ) {
this.options.showRegions.push( 'all' );
}
this.$element.addClass( 'uls-lcd' ); this.$element.addClass( 'uls-lcd' );
this.regionLanguages = {}; this.regionLanguages = {};
this.renderTimeout = null; this.renderTimeout = null;
this.cachedQuicklist = null; this.cachedQuicklist = null;
this.groupByRegionOverride = null;
this.$element.append( noResultsTemplate.clone() );
this.$noResults = this.$element.children( '.uls-no-results-view' );
this.render(); this.render();
this.listen(); this.listen();
}; }
LanguageCategoryDisplay.prototype = { LanguageCategoryDisplay.prototype = {
constructor: LanguageCategoryDisplay, constructor: LanguageCategoryDisplay,
@@ -67,24 +70,21 @@
* Adds language to the language list. * Adds language to the language list.
* @param {string} langCode * @param {string} langCode
* @param {string} [regionCode] * @param {string} [regionCode]
* @return {boolean} Whether the language was added. * @return {boolean} Whether the language was known and accepted
*/ */
append: function ( langCode, regionCode ) { append: function ( langCode, regionCode ) {
var lcd = this, var i, regions;
i, regions;
if ( !$.uls.data.languages[ langCode ] ) { if ( !$.uls.data.languages[ langCode ] ) {
// Language is unknown or not in the list of languages for this context. // Language is unknown or not in the list of languages for this context.
return false; return false;
} }
// Show everything in one region when there is only one column if ( !this.isGroupingByRegionEnabled() ) {
if ( lcd.options.columns === 1 ) { regions = [ 'all' ];
regions = [ 'WW' ];
// Languages are expected to be repeated in this case, // Make sure we do not get duplicates
// and we only want to show them once if ( this.regionLanguages.all.indexOf( langCode ) > -1 ) {
if ( $.inArray( langCode, this.regionLanguages.WW ) > -1 ) {
return true; return true;
} }
} else { } else {
@@ -101,21 +101,45 @@
// Work around the bad interface, delay rendering until we have got // Work around the bad interface, delay rendering until we have got
// all the languages to speed up performance. // all the languages to speed up performance.
window.clearTimeout( this.renderTimeout ); clearTimeout( this.renderTimeout );
this.renderTimeout = window.setTimeout( function () { this.renderTimeout = setTimeout( function () {
lcd.renderRegions(); this.renderRegions();
}, 50 ); }.bind( this ), 50 );
return true; return true;
}, },
/**
* Whether we should render languages grouped to geographic regions.
* @return {boolean}
*/
isGroupingByRegionEnabled: function () {
if ( this.groupByRegionOverride !== null ) {
return this.groupByRegionOverride;
} else if ( this.options.groupByRegion !== 'auto' ) {
return this.options.groupByRegion;
} else {
return this.options.columns > 1;
}
},
/**
* Override the default region grouping setting.
* This is to allow LanguageFilter to disable grouping when displaying search results.
*
* @param {boolean|null} val True to force grouping, false to disable, null to undo override.
*/
setGroupByRegionOverride: function ( val ) {
this.groupByRegionOverride = val;
},
render: function () { render: function () {
var $section, $quicklist, var $section,
lcd = this, $quicklist = this.buildQuicklist(),
narrowMode = this.options.columns === 1,
regions = [], regions = [],
regionNames = { regionNames = {
// These are fallback text when i18n library not present // These are fallback text when i18n library not present
all: 'All languages', // Used if there is quicklist and no region grouping
WW: 'Worldwide', WW: 'Worldwide',
SP: 'Special', SP: 'Special',
AM: 'America', AM: 'America',
@@ -126,42 +150,30 @@
PA: 'Pacific' PA: 'Pacific'
}; };
$quicklist = this.buildQuicklist(); if ( $quicklist.length ) {
regions.push( $quicklist ); regions.push( $quicklist );
} else {
if ( narrowMode && $quicklist.length ) { // We use CSS to hide the header for 'all' when quicklist is NOT present
regions.push( $( '<h3>' ) this.$element.addClass( 'uls-lcd--no-quicklist' );
.attr( 'data-i18n', 'uls-region-all' )
.addClass( 'uls-lcd-region-title' )
.text( 'All languages' )
);
} }
$.each( $.uls.data.regiongroups, function ( regionCode ) { this.options.showRegions.forEach( function ( regionCode ) {
lcd.regionLanguages[ regionCode ] = []; this.regionLanguages[ regionCode ] = [];
// Don't show the region unless it was enabled
if ( $.inArray( regionCode, lcd.options.showRegions ) === -1 ) {
return;
}
$section = $( '<div>' ) $section = $( '<div>' )
.addClass( 'uls-lcd-region-section hide' ) .addClass( 'uls-lcd-region-section hide' )
.attr( 'id', regionCode ); .attr( 'data-region', regionCode );
// Show a region heading, unless we are using a narrow ULS $( '<h3>' )
if ( !narrowMode ) { .attr( 'data-i18n', 'uls-region-' + regionCode )
$section.append( $( '<h3>' ) .addClass( 'uls-lcd-region-title' )
.attr( 'data-i18n', 'uls-region-' + regionCode ) .text( regionNames[ regionCode ] )
.addClass( 'uls-lcd-region-title' ) .appendTo( $section );
.text( regionNames[ regionCode ] )
);
}
regions.push( $section ); regions.push( $section );
} ); }.bind( this ) );
lcd.$element.append( regions ); this.$element.append( regions );
this.i18n(); this.i18n();
}, },
@@ -173,12 +185,12 @@
var languages, var languages,
lcd = this; lcd = this;
this.$noResults.addClass( 'hide' ); this.$element.removeClass( 'uls-no-results' );
this.$element.children( '.uls-lcd-region-section' ).each( function () { this.$element.children( '.uls-lcd-region-section' ).each( function () {
var $region = $( this ), var $region = $( this ),
regionCode = $region.attr( 'id' ); regionCode = $region.data( 'region' );
if ( $region.is( '#uls-lcd-quicklist' ) ) { if ( $region.is( '.uls-lcd-quicklist' ) ) {
return; return;
} }
@@ -241,8 +253,9 @@
nextScript = $.uls.data.getScriptGroupOfLanguage( languages[ i + 1 ] ); nextScript = $.uls.data.getScriptGroupOfLanguage( languages[ i + 1 ] );
lastItem = languagesCount - i === 1; lastItem = languagesCount - i === 1;
// Force column break if script changes and column has more than one row already // Force column break if script changes and column has more than one row already,
if ( i === 0 ) { // but only if grouping by region
if ( i === 0 || !this.isGroupingByRegionEnabled() ) {
currentScript = $.uls.data.getScriptGroupOfLanguage( languages[ i ] ); currentScript = $.uls.data.getScriptGroupOfLanguage( languages[ i ] );
} else if ( currentScript !== nextScript && items.length > 1 ) { } else if ( currentScript !== nextScript && items.length > 1 ) {
force = true; force = true;
@@ -287,7 +300,6 @@
a.lang = code; a.lang = code;
a.dir = $.uls.data.getDir( code ); a.dir = $.uls.data.getDir( code );
li.appendChild( a ); li.appendChild( a );
if ( this.options.languageDecorator ) { if ( this.options.languageDecorator ) {
this.options.languageDecorator( $( a ), code ); this.options.languageDecorator( $( a ), code );
@@ -303,7 +315,7 @@
* Adds quicklist as a region. * Adds quicklist as a region.
*/ */
quicklist: function () { quicklist: function () {
this.$element.find( '#uls-lcd-quicklist' ).removeClass( 'hide' ); this.$element.find( '.uls-lcd-quicklist' ).removeClass( 'hide' );
}, },
buildQuicklist: function () { buildQuicklist: function () {
@@ -328,8 +340,7 @@
quickList.sort( $.uls.data.sortByAutonym ); quickList.sort( $.uls.data.sortByAutonym );
$quickListSection = $( '<div>' ) $quickListSection = $( '<div>' )
.addClass( 'uls-lcd-region-section' ) .addClass( 'uls-lcd-region-section uls-lcd-quicklist' );
.attr( 'id', 'uls-lcd-quicklist' );
$quickListSectionTitle = $( '<h3>' ) $quickListSectionTitle = $( '<h3>' )
.attr( 'data-i18n', 'uls-common-languages' ) .attr( 'data-i18n', 'uls-common-languages' )
@@ -356,30 +367,41 @@
} }
}, },
/**
* Called when a fresh search is started
*/
empty: function () { empty: function () {
this.$element.find( '#uls-lcd-quicklist' ).addClass( 'hide' ); this.$element.addClass( 'uls-lcd--no-quicklist' );
this.$element.find( '.uls-lcd-quicklist' ).addClass( 'hide' );
}, },
focus: function () { focus: function () {
this.$element.focus(); this.$element.focus();
}, },
noResults: function () { /**
this.$noResults.removeClass( 'hide' ); * No-results event handler
this.$noResults.siblings( '.uls-lcd-region-section' ).addClass( 'hide' ); * @param {Event} event
* @param {string} [currentSearchQuery] Current search query that gave mp results
*/
noResults: function ( event, currentSearchQuery ) {
var $noResults;
// Only build the data once this.$element.addClass( 'uls-no-results' );
if ( this.$noResults.find( '.uls-lcd-region-title' ).length ) {
return; this.$element.find( '.uls-no-results-view' ).remove();
if ( typeof this.options.noResultsTemplate === 'function' ) {
$noResults =
this.options.noResultsTemplate.call( this, currentSearchQuery );
} else if ( this.options.noResultsTemplate instanceof jQuery ) {
$noResults = this.options.noResultsTemplate;
} else {
throw new Error( 'noResultsTemplate option must be ' +
'either jQuery or function returning jQuery' );
} }
var $suggestions = this.buildQuicklist().clone(); this.$element.append( $noResults.addClass( 'uls-no-results-view' ) );
$suggestions.removeClass( 'hide' ).removeAttr( 'id' );
$suggestions.find( 'h3' )
.data( 'i18n', 'uls-no-results-suggestion-title' )
.text( 'You may be interested in:' )
.i18n();
this.$noResults.find( 'h2' ).after( $suggestions );
}, },
listen: function () { listen: function () {
@@ -410,15 +432,37 @@
}; };
$.fn.lcd.defaults = { $.fn.lcd.defaults = {
languages: null, // List of languages to show
languages: [],
// List of regions to show
showRegions: [ 'WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA' ], showRegions: [ 'WW', 'AM', 'EU', 'ME', 'AF', 'AS', 'PA' ],
// Whether to group by region, defaults to true when columns > 1
groupByRegion: 'auto',
// How many items per column until new "row" starts
itemsPerColumn: 8, itemsPerColumn: 8,
// Other supported values are 1 and 2. // Number of columns, only 1, 2 and 4 are supported
// Other values will have rendering issues.
columns: 4, columns: 4,
languageDecorator: null, // Callback function for language item styling
quickList: [] languageDecorator: undefined,
// Likely candidates
quickList: [],
// Callback function for language selection
clickhandler: undefined,
// Callback function when no search results
noResultsTemplate: function () {
var $suggestionsContainer, $suggestions,
$noResultsTemplate = $( noResultsTemplate );
$suggestions = this.buildQuicklist().clone();
$suggestions.removeClass( 'hide' )
.find( 'h3' )
.data( 'i18n', 'uls-no-results-suggestion-title' )
.text( 'You may be interested in:' )
.i18n();
$suggestionsContainer = $noResultsTemplate.find( '.uls-no-results-suggestions' );
$suggestionsContainer.append( $suggestions );
return $noResultsTemplate;
}
}; };
$.fn.lcd.Constructor = LanguageCategoryDisplay;
}( jQuery ) ); }( jQuery ) );

View File

@@ -13,8 +13,6 @@
* *
*/ */
/*jshint sub:true */
( function ( $ ) { ( function ( $ ) {
'use strict'; 'use strict';