diff options
Diffstat (limited to 'www/wiki/extensions/UniversalLanguageSelector/lib/jquery.webfonts/src/jquery.webfonts.js')
-rw-r--r-- | www/wiki/extensions/UniversalLanguageSelector/lib/jquery.webfonts/src/jquery.webfonts.js | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/www/wiki/extensions/UniversalLanguageSelector/lib/jquery.webfonts/src/jquery.webfonts.js b/www/wiki/extensions/UniversalLanguageSelector/lib/jquery.webfonts/src/jquery.webfonts.js new file mode 100644 index 00000000..a28ca39d --- /dev/null +++ b/www/wiki/extensions/UniversalLanguageSelector/lib/jquery.webfonts/src/jquery.webfonts.js @@ -0,0 +1,517 @@ +/** + * jQuery Webfonts. + * + * Copyright (C) 2012 Santhosh Thottingal + * + * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't + * have to do anything special to choose one license or the other and you don't + * have to notify anyone which license you are using. You are free to use + * UniversalLanguageSelector in commercial projects as long as the copyright + * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. + * + * @file + * @ingroup Extensions + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ + +( function( $, window, document, undefined ) { + 'use strict'; + + var WebFonts = function( element, options ) { + // Load defaults + this.options = $.extend( {}, $.fn.webfonts.defaults, options ); + this.$element = $( element ); + this.repository = $.extend( WebFonts.repository, this.options.repository ); + // List of loaded fonts + this.fonts = []; + this.originalFontFamily = this.$element.css( 'font-family' ); + this.language = this.$element.attr( 'lang' ) || $( 'html' ).attr( 'lang' ); + + this.init(); + }; + + WebFonts.repository = { + base: 'fonts', // Relative or absolute path to the font repository. + languages: {}, // languages to font mappings + fonts: {}, // Font name to font configuration mapping + + // Utility methods to work on the repository. + defaultFont: function( language ) { + var defaultFont = null; + + if ( this.languages[language] ) { + defaultFont = this.languages[language][0]; + } + + return defaultFont; + }, + + get: function( fontFamily ) { + return this.fonts[fontFamily]; + } + }; + + WebFonts.prototype = { + constructor: WebFonts, + + /** + * Get the default font family for given language. + * @param {String|undefined} language Language code. + * @param {array} classes + * @return {String} Font family name + */ + getFont: function( language, classes ) { + language = ( language || this.language || '' ).toLowerCase(); + + if ( this.options.fontSelector && language ) { + return this.options.fontSelector( this.repository, language, classes ); + } else { + return this.repository.defaultFont( language ); + } + }, + + /** + * Initialize. + */ + init: function() { + if ( this.language ) { + this.apply( this.getFont( this.language ) ); + } + + this.parse(); + }, + + /** + * TODO: document + */ + refresh: function() { + this.reset(); + this.init(); + }, + + /** + * Apply a font for given elements. + * + * @param {String} fontFamily Font family name + * @param {jQuery} $element One or more jQuery elements + */ + apply: function( fontFamily, $element ) { + var fontStack = this.options.fontStack.slice( 0 ); + + $element = $element || this.$element; + + // Loading an empty string is pointless. + // Putting an empty string into a font-family list doesn't work with + // jQuery.css(). + if ( fontFamily ) { + this.load( fontFamily ); + // Avoid duplicates + if ( $.inArray( fontFamily, fontStack ) < 0 ) { + fontStack.unshift( fontFamily ); + } + } + + if ( !fontFamily ) { + // We are resetting the font to original font. + fontStack = []; + // This will cause removing inline fontFamily style. + } + + // Set the font of this element if it's not excluded. + // Add class webfonts-changed when webfonts are applied. + $element.not( this.options.exclude ) + .css( 'font-family', fontStack.join() ) + .addClass( 'webfonts-changed' ); + + // Set the font of this element's children if they are not excluded. + // font-family of <input>, <textarea> and <button> must be changed explicitly. + // Add class webfonts-changed when webfonts are applied. + $element.find( 'textarea, input, button' ).not( this.options.exclude ) + .css( 'font-family', fontStack.join() ) + .addClass( 'webfonts-changed' ); + }, + + /** + * Load given font families if not loaded already. Creates the CSS rules + * and appends them to document. + * + * @param {Array|String} fontFamilies List of font families + */ + load: function( fontFamilies ) { + var css, fontFamily, i, + fontFaceRule = ''; + + // Convert to array if string given (old signature) + if ( typeof fontFamilies === 'string' ) { + fontFamilies = [fontFamilies]; + } + + for ( i = 0; i < fontFamilies.length; i++ ) { + fontFamily = fontFamilies[i]; + if ( $.inArray( fontFamily, this.fonts ) >= 0 ) { + continue; + } + + css = this.getCSS( fontFamily, 'normal' ); + if ( css !== false ) { + fontFaceRule += css; + this.fonts.push( fontFamily ); + } + } + + // In case the list contained only fonts that are already loaded + // or non-existing fonts. + if ( fontFaceRule !== '' ) { + injectCSS( fontFaceRule ); + } + + return true; + }, + + /** + * Parse the element for custom font-family styles and for nodes with + * different language than what the element itself has. + */ + parse: function() { + var webfonts = this, + // Fonts can be added indirectly via classes, but also with + // style attributes. For lang attributes we will use our font + // if they don't have explicit font already. + $elements = webfonts.$element.find( '*[lang], [style], [class]' ), + // List of fonts to load in a batch + fontQueue = [], + // List of elements to apply a certain font family in a batch. + // Object keys are the font family, values are list of plain elements. + elementQueue = {}; + + // Add to the font queue(no dupes) + function addToFontQueue( value ) { + if ( $.inArray( value, fontQueue ) < 0 ) { + fontQueue.push( value ); + } + } + + // Add to the font queue + function addToElementQueue( element, fontFamily ) { + elementQueue[fontFamily] = elementQueue[fontFamily] || []; + elementQueue[fontFamily].push( element ); + } + + $elements.each( function( i, element ) { + var fontFamilyStyle, fontFamily, + $element = $( element ); + + if ( $element.is( webfonts.options.exclude ) ) { + return; + } + + // Note: it depends on the browser whether this returns font names + // which don't exist. In Chrome it does, while in Opera it doesn't. + fontFamilyStyle = $element.css( 'fontFamily' ); + // Note: It is unclear whether this can ever be falsy. Maybe also + // browser specific. + if ( fontFamilyStyle ) { + // if it is overridable, override. always. + if ( webfonts.isOverridable( fontFamilyStyle ) ) { + fontFamily = webfonts.getFont( element.lang || webfonts.language ); + // We do not have fonts for all languages + if ( fontFamily ) { + addToFontQueue( fontFamily ); + addToElementQueue( element, fontFamily ); + } + return; + } else { + fontFamily = fontFamilyStyle.split( ',' )[0]; + // Remove the ' and " characters if any. + fontFamily = $.trim( fontFamily.replace( /["']/g, '' ) ); + addToFontQueue( fontFamily ); + } + } + + // Load and apply fonts for other language tagged elements (batched) + if ( element.lang && element.lang !== webfonts.language ) { + // language differs. We may want to apply a different font. + if ( webfonts.hasExplicitFontStyle ( $element ) && + !webfonts.isOverridable( fontFamilyStyle ) ) { + // respect the explicit font family style. Do not override. + // This style may be from css, inheritance, or even from + // browser settings. + return; + } else { + fontFamily = webfonts.getFont( element.lang, element.className.split(/\s+/) ); + } + + if ( !fontFamily ) { + // No font preference for the language. + // Check if we need to reset for this language. + // If the font of the parent element, to which webfonts were applied, + // remained the same, there is no need to reset. + if ( webfonts.$element.css( 'fontFamily' ) !== webfonts.originalFontFamily ) { + // The parent font changed. + // Is there an inheritance? + // Is the font for this element the same as parent's font? + if ( fontFamilyStyle === webfonts.$element.css( 'fontFamily' ) ) { + // Break inheritance of the font from the parent element + // by applying the original font to this element + fontFamily = webfonts.originalFontFamily; + } + } + } + + // We do not have fonts for all languages + if ( fontFamily ) { + addToFontQueue( fontFamily ); + addToElementQueue( element, fontFamily ); + } + } + } ); + + // Process in batch the accumulated fonts and elements + this.load( fontQueue ); + $.each( elementQueue, function( fontFamily, elements ) { + webfonts.apply( fontFamily, $( elements ) ); + } ); + }, + + /** + * Find out whether an element has explicit non generic font family style + * For the practical purpose we check whether font is same as top element + * or having any of generic font family + * http://www.w3.org/TR/CSS2/fonts.html#generic-font-families + * @param {jQuery} $element + * @return {boolean} + */ + hasExplicitFontStyle: function ( $element ) { + var elementFontFamily = $element.css( 'fontFamily' ); + + // whether the font is inherited from top element to which plugin applied + return this.$element.css( 'fontFamily' ) !== elementFontFamily + // whether the element has generic font family + && ( $.inArray( elementFontFamily, + [ 'monospace', 'serif', 'cursive', 'fantasy', 'sans-serif' ] ) < 0 ); + }, + + /** + * Check whether the given font family is overridable or not. jquery.webfonts + * by default does not override any font-family styles other than generic + * font family styles (see hasExplicitFontStyle method). + * @param {string} fontFamily + * @return {boolean} Whether the given fontFamily is overridable or not. + */ + isOverridable: function( fontFamily ) { + var overridableFontFamilies = [ 'monospace', 'serif', 'cursive', 'fantasy', 'sans-serif' ]; + $.merge( overridableFontFamilies, this.options.overridableFontFamilies ); + // Browsers like FF put space after comma in font stack. Chrome does not. + // Normalise it by removing the spaces and quotes + overridableFontFamilies = $.map( overridableFontFamilies, function( item ) { + return item.replace( /[\s'"]/g, '' ); + } ); + fontFamily = fontFamily.replace( /[\s'"]/g, '' ); + + return $.inArray( fontFamily, overridableFontFamilies ) >= 0; + }, + + /** + * List all fonts for the given language + * + * @param {String} [language] Language code. If undefined all fonts will be listed. + * @return {Array} List of font family names. + */ + list: function( language ) { + var fontName, + fontNames = []; + + if ( language ) { + fontNames = this.repository.languages[language] || []; + } else { + for ( fontName in this.repository.fonts ) { + if ( this.repository.fonts.hasOwnProperty( fontName ) ) { + fontNames.push( fontName ); + } + } + } + + return fontNames; + }, + + /** + * List all languages supported by the repository + * + * @return {Array} List of language codes + */ + languages: function() { + var language, + languages = []; + + for ( language in this.repository.languages ) { + if ( this.repository.languages.hasOwnProperty( language ) ) { + languages.push( language ); + } + } + + return languages; + }, + + /** + * Set the font repository + * + * @param {Object} repository The font repository. + */ + setRepository: function( repository ) { + this.repository = $.extend( WebFonts.repository, repository ); + }, + + /** + * Reset the font-family style. + */ + reset: function() { + this.$element.find( '.webfonts-changed' ) + .removeClass( '.webfonts-changed' ) + .css( 'font-family', '' ); + this.apply( this.originalFontFamily ); + }, + + /** + * Unbind the plugin + */ + unbind: function() { + this.$element.data( 'webfonts', null ); + }, + + /** + * Construct the CSS required for the font-family. + * + * @param {String} fontFamily The font-family name + * @param {String} [variant] The font variant, eg: bold, italic etc. Default is normal. + * @return {String} CSS + */ + getCSS: function( fontFamily, variant ) { + var webfonts, base, + fontFaceRule, userAgent, fontStyle, fontFormats, fullFontName, + fontconfig = this.repository.get( fontFamily ); + + variant = variant || 'normal'; + fullFontName = fontFamily; + + if ( variant !== 'normal' ) { + if ( fontconfig.variants !== undefined && fontconfig.variants[variant] ) { + fullFontName = fontconfig.variants[variant]; + fontconfig = this.repository.get( fontconfig.variants[variant] ); + } + } + + if ( !fontconfig ) { + return false; + } + + base = this.repository.base; + fontFaceRule = '@font-face { font-family: \'' + fontFamily + '\';\n'; + userAgent = window.navigator.userAgent; + fontStyle = fontconfig.fontstyle || 'normal'; + fontFormats = []; + + if ( fontconfig.eot ) { + fontFaceRule += '\tsrc: url(\'' + base + fontconfig.eot + '\');\n'; + } + fontFaceRule += '\tsrc: '; + + // If the font is present locally, use it. + if ( userAgent.match( /Android 2\.3/ ) === null ) { + // Android 2.3.x does not respect local() syntax. + // http://code.google.com/p/android/issues/detail?id=10609 + fontFaceRule += 'local(\'' + fullFontName + '\'),'; + } + + if ( fontconfig.woff2 ) { + fontFormats.push( '\t\turl(\'' + base + fontconfig.woff2 + + '\') format(\'woff2\')' ); + } + + if ( fontconfig.woff ) { + fontFormats.push( '\t\turl(\'' + base + fontconfig.woff + + '\') format(\'woff\')' ); + } + + if ( fontconfig.svg ) { + fontFormats.push( '\t\turl(\'' + base + fontconfig.svg + '#' + + fontFamily + '\') format(\'svg\')' ); + } + + if ( fontconfig.ttf ) { + fontFormats.push( '\t\turl(\'' + base + fontconfig.ttf + + '\') format(\'truetype\')' ); + } + + fontFaceRule += fontFormats.join() + ';\n'; + + if ( fontconfig.fontweight ) { + fontFaceRule += '\tfont-weight:' + fontconfig.fontweight + ';'; + } + + if ( fontconfig.fontstyle !== undefined ) { + fontFaceRule += '\tfont-style:' + fontconfig.fontstyle + ';'; + } else { + fontFaceRule += '\tfont-style: normal;'; + } + + fontFaceRule += '}\n'; + + webfonts = this; + if ( fontconfig.variants !== undefined ) { + $.each( fontconfig.variants, function ( variant ) { + fontFaceRule += webfonts.getCSS( fontFamily, variant ); + } ); + } + + return fontFaceRule; + } + }; + + $.fn.webfonts = function( option ) { + return this.each( function() { + var $this = $( this ), + data = $this.data( 'webfonts' ), + options = typeof option === 'object' && option; + + if ( !data ) { + $this.data( 'webfonts', ( data = new WebFonts( this, options ) ) ); + } + + if ( typeof option === 'string' ) { + data[option](); + } + } ); + }; + + $.fn.webfonts.defaults = { + repository: WebFonts.repository, // Default font repository + fontStack: [ 'Helvetica', 'Arial', 'sans-serif' ], // Default font fallback + exclude: '', // jQuery selectors to exclude + overridableFontFamilies: [] + }; + + $.fn.webfonts.Constructor = WebFonts; + + // Private methods for the WebFonts prototype + + /** + * Create a new style tag and add it to the DOM. + * + * @param {String} css + */ + function injectCSS( css ) { + var s = document.createElement( 'style' ); + + // Insert into document before setting cssText + document.getElementsByTagName( 'head' )[0].appendChild( s ); + + if ( s.styleSheet ) { + s.styleSheet.cssText = css; + // IE + } else { + // Safari sometimes borks on null + s.appendChild( document.createTextNode( String( css ) ) ); + } + } +} )( jQuery, window, document ); |