/*! * jQuery UI Core 1.10.2 * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/category/ui-core/ */ (function( $, undefined ) { var uuid = 0, runiqueId = /^ui-id-\d+$/; // $.ui might exist from components with no dependencies, e.g., $.ui.position $.ui = $.ui || {}; $.extend( $.ui, { version: "1.10.2", keyCode: { BACKSPACE: 8, COMMA: 188, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, LEFT: 37, NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108, NUMPAD_MULTIPLY: 106, NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SPACE: 32, TAB: 9, UP: 38 } }); // plugins $.fn.extend({ focus: (function( orig ) { return function( delay, fn ) { return typeof delay === "number" ? this.each(function() { var elem = this; setTimeout(function() { $( elem ).focus(); if ( fn ) { fn.call( elem ); } }, delay ); }) : orig.apply( this, arguments ); }; })( $.fn.focus ), scrollParent: function() { var scrollParent; if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { scrollParent = this.parents().filter(function() { return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); }).eq(0); } else { scrollParent = this.parents().filter(function() { return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); }).eq(0); } return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; }, zIndex: function( zIndex ) { if ( zIndex !== undefined ) { return this.css( "zIndex", zIndex ); } if ( this.length ) { var elem = $( this[ 0 ] ), position, value; while ( elem.length && elem[ 0 ] !== document ) { // Ignore z-index if position is set to a value where z-index is ignored by the browser // This makes behavior of this function consistent across browsers // WebKit always returns auto if the element is positioned position = elem.css( "position" ); if ( position === "absolute" || position === "relative" || position === "fixed" ) { // IE returns 0 when zIndex is not specified // other browsers return a string // we ignore the case of nested elements with an explicit value of 0 //
load('path/to/all_localizations.json');
*
* This can also load a localization file for a locale
* load('path/to/de-messages.json', 'de' );
*
* A data object containing message key- message translation mappings
* can also be passed Eg:
*
* load( { 'hello' : 'Hello' }, optionalLocale );
*
If the data argument is
* null/undefined/false,
* all cached messages for the i18n instance will get reset.
*
* @param {String|Object|null} data
* @param {String} locale Language tag
*/
load: function ( data, locale ) {
this.messageStore.load( data, locale );
},
log: function ( /* arguments */ ) {
if ( window.console && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
},
/**
* Does parameter and magic word substitution.
*
* @param {string} key Message key
* @param {Array} parameters Message parameters
* @return {string}
*/
parse: function ( key, parameters ) {
var message = key.toLocaleString();
// FIXME: This changes the state of the I18N object,
// should probably not change the 'this.parser' but just
// pass it to the parser.
this.parser.language = $.i18n.languages[$.i18n().locale] || $.i18n.languages['default'];
if( message === '' ) {
message = key;
}
return this.parser.parse( message, parameters );
}
};
/**
* Process a message from the $.I18N instance
* for the current document, stored in jQuery.data(document).
*
* @param {string} key Key of the message.
* @param {string} param1 [param...] Variadic list of parameters for {key}.
* @return {string|$.I18N} Parsed message, or if no key was given
* the instance of $.I18N is returned.
*/
$.i18n = function ( key, param1 ) {
var parameters,
i18n = $.data( document, 'i18n' ),
options = typeof key === 'object' && key;
// If the locale option for this call is different then the setup so far,
// update it automatically. This doesn't just change the context for this
// call but for all future call as well.
// If there is no i18n setup yet, don't do this. It will be taken care of
// by the `new I18N` construction below.
// NOTE: It should only change language for this one call.
// Then cache instances of I18N somewhere.
if ( options && options.locale && i18n && i18n.locale !== options.locale ) {
String.locale = i18n.locale = options.locale;
}
if ( !i18n ) {
i18n = new I18N( options );
$.data( document, 'i18n', i18n );
}
if ( typeof key === 'string' ) {
if ( param1 !== undefined ) {
parameters = slice.call( arguments, 1 );
} else {
parameters = [];
}
return i18n.parse( key, parameters );
} else {
// FIXME: remove this feature/bug.
return i18n;
}
};
$.fn.i18n = function () {
var i18n = $.data( document, 'i18n' );
String.locale = i18n.locale;
if ( !i18n ) {
i18n = new I18N( );
$.data( document, 'i18n', i18n );
}
return this.each( function () {
var $this = $( this );
if ( $this.data( 'i18n' ) ) {
var messageKey = $this.data( 'i18n' ),
message = messageKey.toLocaleString();
if ( message !== '' ) {
$this.text( message );
}
} else {
$this.find( '[data-i18n]' ).i18n();
}
} );
};
String.locale = String.locale || $( 'html' ).attr( 'lang' );
if ( !String.locale ) {
if ( typeof window.navigator !== undefined ) {
nav = window.navigator;
String.locale = nav.language || nav.userLanguage || '';
} else {
String.locale = '';
}
}
$.i18n.languages = {};
$.i18n.messageStore = $.i18n.messageStore || {};
$.i18n.parser = {
// The default parser only handles variable substitution
parse: function ( message, parameters ) {
return message.replace( /\$(\d+)/g, function ( str, match ) {
var index = parseInt( match, 10 ) - 1;
return parameters[index] !== undefined ? parameters[index] : '$' + match;
} );
},
emitter: {}
};
$.i18n.debug = false;
/* Static members */
I18N.defaults = {
locale: String.locale,
fallbackLocale: 'en',
parser: $.i18n.parser,
messageStore: $.i18n.messageStore,
/* messageLocationResolver - should be a function taking language code as argument and
* returning absolute or relative path to the localization file
*/
messageLocationResolver: null
};
// Expose constructor
$.I18N = I18N;
}( jQuery ) );
/**
* jQuery Internationalization library Message loading , parsing, retrieving utilities
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n 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.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $, window, undefined ) {
'use strict';
var MessageStore = function () {
this.messages = {};
this.sources = {};
this.locale = String.locale;
};
MessageStore.prototype = {
/**
* See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading
*
* @param locale
*/
init: function ( locale ) {
var messageStore = this;
messageStore.locale = locale;
messageStore.log( 'initializing for ' + locale );
$( 'link' ).each( function ( index, element ) {
var $link = $( element ),
rel = ( $link.attr( 'rel' ) || '' ).toLowerCase().split( /\s+/ );
if ( $.inArray( 'localizations', rel ) !== -1 ) {
// multiple localizations
messageStore.load( $link.attr( 'href' ) );
} else if ( $.inArray( 'localization', rel ) !== -1 ) {
// single localization
messageStore.queue( ( $link.attr( 'hreflang' ) || '' ).toLowerCase(),
$link.attr( 'href' ) );
}
} );
},
/**
* General message loading API This can take a URL string for
* the json formatted messages.
* load('path/to/all_localizations.json');
*
* This can also load a localization file for a locale
* load('path/to/de-messages.json', 'de' );
*
* A data object containing message key- message translation mappings
* can also be passed Eg:
*
* load( { 'hello' : 'Hello' }, optionalLocale );
*
If the data argument is
* null/undefined/false,
* all cached messages for the i18n instance will get reset.
*
* @param {String|Object|null} data
* @param {String} locale Language tag
*/
load: function ( data, locale ) {
var key = null,
messageStore = this,
hasOwn = Object.prototype.hasOwnProperty;
if ( !data ) {
// reset all localizations
messageStore.log( 'Resetting for locale ' + locale );
messageStore.messages = {};
return;
}
if ( typeof data === 'string' ) {
// This is a URL to the messages file.
messageStore.log( 'Loading messages from: ' + data );
messageStore.jsonMessageLoader( data ).done( function ( localization, textStatus ) {
messageStore.load( localization, locale );
messageStore.queue( locale, data );
messageStore.markLoaded( locale, data );
} );
} else {
// Data is either a group of messages for {locale},
// or a group of languages with groups of messages inside.
for ( key in data ) {
if ( hasOwn.call( data, key ) ) {
if ( locale ) {
// Lazy-init the object
if ( !messageStore.messages[locale] ) {
messageStore.messages[locale] = {};
}
// Update message object keys,
// don't overwrite the entire object.
messageStore.messages[locale][key] = data[key];
messageStore.log(
'[' + locale + '][' + key + '] : ' + data[key]
);
// No {locale} given, assume data is a group of languages,
// call this function again for each langauge.
} else {
messageStore.load( data[key], key );
}
}
}
}
},
log: function ( /* arguments */ ) {
if ( window.console && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
},
/**
* Mark a message Location for a locale loaded
*
* @param locale
* @param messageLocation
*/
markLoaded: function ( locale, messageLocation ) {
var i,
queue = this.sources[locale];
if ( !queue ) {
this.queue( locale, messageLocation );
queue = this.sources[locale];
}
this.sources[locale] = this.sources[locale] || [];
for ( i = 0; i < queue.length; i++ ) {
if ( queue[i].source.url === messageLocation ) {
queue[i].source.loaded = true;
return;
}
}
},
/**
* Register the message location for a locale, will be loaded when required
*
* @param locale
* @param messageLocation
*/
queue: function ( locale, messageLocation ) {
var i,
queue = this.sources[locale];
this.sources[locale] = this.sources[locale] || [];
if ( queue ) {
for ( i = 0; i < queue.length; i++ ) {
if ( queue[i].source.url === messageLocation ) {
return;
}
}
}
this.log( 'Source for: ' + locale + ' is ' + messageLocation + ' registered' );
this.sources[locale].push( {
source: {
url: messageLocation,
loaded: false
}
} );
},
/**
* Load the messages from the source queue for the locale
*
* @param {String} locale
*/
loadFromQueue: function ( locale ) {
var i,
queue = this.sources[locale];
if ( queue ) {
for ( i = 0; i < queue.length; i++ ) {
if ( !queue[i].source.loaded ) {
this.load( queue[i].source.url, locale );
this.sources[locale][i].source.loaded = true;
}
}
}
},
isLoaded: function ( locale, messageLocation ) {
var i,
sources = this.sources[locale],
result = false;
if ( sources ) {
for ( i = 0; i < sources.length; i++ ) {
if ( sources[i].source.url === messageLocation ) {
result = true;
}
}
}
return result;
},
jsonMessageLoader: function ( url ) {
var messageStore = this;
return $.ajax( {
url: url,
dataType: 'json',
async: false
// This is unfortunate.
} ).fail( function ( jqxhr, settings, exception ) {
messageStore.log( 'Error in loading messages from ' + url + ' Exception: ' + exception );
} );
},
/**
*
* @param locale
* @param messageKey
* @returns {Boolean}
*/
get: function ( locale, messageKey ) {
// load locale if not loaded
if ( !this.messages[locale] ) {
this.loadFromQueue( locale );
}
return this.messages[locale] && this.messages[locale][messageKey];
}
};
$.extend( $.i18n.messageStore, new MessageStore() );
}( jQuery, window ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n 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.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $, undefined ) {
'use strict';
$.i18n = $.i18n || {};
$.i18n.fallbacks = {
'ab': ['ru'],
'ace': ['id'],
'aln': ['sq'],
// Not so standard - als is supposed to be Tosk Albanian,
// but in Wikipedia it's used for a Germanic language.
'als': ['gsw', 'de'],
'an': ['es'],
'anp': ['hi'],
'arn': ['es'],
'arz': ['ar'],
'av': ['ru'],
'ay': ['es'],
'ba': ['ru'],
'bar': ['de'],
'bat-smg': ['sgs', 'lt'],
'bcc': ['fa'],
'be-x-old': ['be-tarask'],
'bh': ['bho'],
'bjn': ['id'],
'bm': ['fr'],
'bpy': ['bn'],
'bqi': ['fa'],
'bug': ['id'],
'cbk-zam': ['es'],
'ce': ['ru'],
'crh': ['crh-latn'],
'crh-cyrl': ['ru'],
'csb': ['pl'],
'cv': ['ru'],
'de-at': ['de'],
'de-ch': ['de'],
'de-formal': ['de'],
'dsb': ['de'],
'dtp': ['ms'],
'egl': ['it'],
'eml': ['it'],
'ff': ['fr'],
'fit': ['fi'],
'fiu-vro': ['vro', 'et'],
'frc': ['fr'],
'frp': ['fr'],
'frr': ['de'],
'fur': ['it'],
'gag': ['tr'],
'gan': ['gan-hant', 'zh-hant', 'zh-hans'],
'gan-hans': ['zh-hans'],
'gan-hant': ['zh-hant', 'zh-hans'],
'gl': ['pt'],
'glk': ['fa'],
'gn': ['es'],
'gsw': ['de'],
'hif': ['hif-latn'],
'hsb': ['de'],
'ht': ['fr'],
'ii': ['zh-cn', 'zh-hans'],
'inh': ['ru'],
'iu': ['ike-cans'],
'jut': ['da'],
'jv': ['id'],
'kaa': ['kk-latn', 'kk-cyrl'],
'kbd': ['kbd-cyrl'],
'khw': ['ur'],
'kiu': ['tr'],
'kk': ['kk-cyrl'],
'kk-arab': ['kk-cyrl'],
'kk-latn': ['kk-cyrl'],
'kk-cn': ['kk-arab', 'kk-cyrl'],
'kk-kz': ['kk-cyrl'],
'kk-tr': ['kk-latn', 'kk-cyrl'],
'kl': ['da'],
'ko-kp': ['ko'],
'koi': ['ru'],
'krc': ['ru'],
'ks': ['ks-arab'],
'ksh': ['de'],
'ku': ['ku-latn'],
'ku-arab': ['ckb'],
'kv': ['ru'],
'lad': ['es'],
'lb': ['de'],
'lbe': ['ru'],
'lez': ['ru'],
'li': ['nl'],
'lij': ['it'],
'liv': ['et'],
'lmo': ['it'],
'ln': ['fr'],
'ltg': ['lv'],
'lzz': ['tr'],
'mai': ['hi'],
'map-bms': ['jv', 'id'],
'mg': ['fr'],
'mhr': ['ru'],
'min': ['id'],
'mo': ['ro'],
'mrj': ['ru'],
'mwl': ['pt'],
'myv': ['ru'],
'mzn': ['fa'],
'nah': ['es'],
'nap': ['it'],
'nds': ['de'],
'nds-nl': ['nl'],
'nl-informal': ['nl'],
'no': ['nb'],
'os': ['ru'],
'pcd': ['fr'],
'pdc': ['de'],
'pdt': ['de'],
'pfl': ['de'],
'pms': ['it'],
'pt': ['pt-br'],
'pt-br': ['pt'],
'qu': ['es'],
'qug': ['qu', 'es'],
'rgn': ['it'],
'rmy': ['ro'],
'roa-rup': ['rup'],
'rue': ['uk', 'ru'],
'ruq': ['ruq-latn', 'ro'],
'ruq-cyrl': ['mk'],
'ruq-latn': ['ro'],
'sa': ['hi'],
'sah': ['ru'],
'scn': ['it'],
'sg': ['fr'],
'sgs': ['lt'],
'sli': ['de'],
'sr': ['sr-ec'],
'srn': ['nl'],
'stq': ['de'],
'su': ['id'],
'szl': ['pl'],
'tcy': ['kn'],
'tg': ['tg-cyrl'],
'tt': ['tt-cyrl', 'ru'],
'tt-cyrl': ['ru'],
'ty': ['fr'],
'udm': ['ru'],
'ug': ['ug-arab'],
'uk': ['ru'],
'vec': ['it'],
'vep': ['et'],
'vls': ['nl'],
'vmf': ['de'],
'vot': ['fi'],
'vro': ['et'],
'wa': ['fr'],
'wo': ['fr'],
'wuu': ['zh-hans'],
'xal': ['ru'],
'xmf': ['ka'],
'yi': ['he'],
'za': ['zh-hans'],
'zea': ['nl'],
'zh': ['zh-hans'],
'zh-classical': ['lzh'],
'zh-cn': ['zh-hans'],
'zh-hant': ['zh-hans'],
'zh-hk': ['zh-hant', 'zh-hans'],
'zh-min-nan': ['nan'],
'zh-mo': ['zh-hk', 'zh-hant', 'zh-hans'],
'zh-my': ['zh-sg', 'zh-hans'],
'zh-sg': ['zh-hans'],
'zh-tw': ['zh-hant', 'zh-hans'],
'zh-yue': ['yue']
};
}( jQuery ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n 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.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $ ) {
'use strict';
var MessageParser = function ( options ) {
this.options = $.extend( {}, $.i18n.parser.defaults, options );
this.language = $.i18n.languages[String.locale] || $.i18n.languages['default'];
this.emitter = $.i18n.parser.emitter;
};
MessageParser.prototype = {
constructor: MessageParser,
simpleParse: function ( message, parameters ) {
return message.replace( /\$(\d+)/g, function ( str, match ) {
var index = parseInt( match, 10 ) - 1;
return parameters[index] !== undefined ? parameters[index] : '$' + match;
} );
},
parse: function ( message, replacements ) {
if ( message.indexOf( '{{' ) < 0 ) {
return this.simpleParse( message, replacements );
}
this.emitter.language = $.i18n.languages[$.i18n().locale] ||
$.i18n.languages['default'];
return this.emitter.emit( this.ast( message ), replacements );
},
ast: function ( message ) {
var pos = 0;
// Try parsers until one works, if none work return null
function choice ( parserSyntax ) {
return function () {
var i, result;
for ( i = 0; i < parserSyntax.length; i++ ) {
result = parserSyntax[i]();
if ( result !== null ) {
return result;
}
}
return null;
};
}
// Try several parserSyntax-es in a row.
// All must succeed; otherwise, return null.
// This is the only eager one.
function sequence ( parserSyntax ) {
var i, res,
originalPos = pos,
result = [];
for ( i = 0; i < parserSyntax.length; i++ ) {
res = parserSyntax[i]();
if ( res === null ) {
pos = originalPos;
return null;
}
result.push( res );
}
return result;
}
// Run the same parser over and over until it fails.
// Must succeed a minimum of n times; otherwise, return null.
function nOrMore ( n, p ) {
return function () {
var originalPos = pos,
result = [],
parsed = p();
while ( parsed !== null ) {
result.push( parsed );
parsed = p();
}
if ( result.length < n ) {
pos = originalPos;
return null;
}
return result;
};
}
// Helpers -- just make parserSyntax out of simpler JS builtin types
function makeStringParser ( s ) {
var len = s.length;
return function () {
var result = null;
if ( message.substr( pos, len ) === s ) {
result = s;
pos += len;
}
return result;
};
}
function makeRegexParser ( regex ) {
return function () {
var matches = message.substr( pos ).match( regex );
if ( matches === null ) {
return null;
}
pos += matches[0].length;
return matches[0];
};
}
var pipe = makeStringParser( '|' );
var colon = makeStringParser( ':' );
var backslash = makeStringParser( '\\' );
var anyCharacter = makeRegexParser( /^./ );
var dollar = makeStringParser( '$' );
var digits = makeRegexParser( /^\d+/ );
var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
var regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
var regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
// There is a general pattern:
// parse a thing;
// if it worked, apply transform,
// otherwise return null.
// But using this as a combinator seems to cause problems
// when combined with nOrMore().
// May be some scoping issue.
function transform ( p, fn ) {
return function () {
var result = p();
return result === null ? null : fn( result );
};
}
// Used to define "literals" within template parameters. The pipe
// character is the parameter delimeter, so by default
// it is not a literal in the parameter
function literalWithoutBar () {
var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
return result === null ? null : result.join( '' );
}
function literal () {
var result = nOrMore( 1, escapedOrRegularLiteral )();
return result === null ? null : result.join( '' );
}
function escapedLiteral () {
var result = sequence( [ backslash, anyCharacter ] );
return result === null ? null : result[1];
}
choice( [ escapedLiteral, regularLiteralWithoutSpace ] );
var escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] );
var escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] );
function replacement () {
var result = sequence( [ dollar, digits ] );
if ( result === null ) {
return null;
}
return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
}
var templateName = transform(
// see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1"
makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
function ( result ) {
return result.toString();
}
);
function templateParam () {
var result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] );
if ( result === null ) {
return null;
}
var expr = result[1];
// use a "CONCAT" operator if there are multiple nodes,
// otherwise return the first node, raw.
return expr.length > 1 ? [ 'CONCAT' ].concat( expr ) : expr[0];
}
function templateWithReplacement () {
var result = sequence( [ templateName, colon, replacement ] );
return result === null ? null : [ result[0], result[2] ];
}
function templateWithOutReplacement () {
var result = sequence( [ templateName, colon, paramExpression ] );
return result === null ? null : [ result[0], result[2] ];
}
var templateContents = choice( [
function () {
var res = sequence( [
// templates can have placeholders for dynamic
// replacement eg: {{PLURAL:$1|one car|$1 cars}}
// or no placeholders eg:
// {{GRAMMAR:genitive|{{SITENAME}}}
choice( [ templateWithReplacement, templateWithOutReplacement ] ),
nOrMore( 0, templateParam )
] );
return res === null ? null : res[0].concat( res[1] );
},
function () {
var res = sequence( [ templateName, nOrMore( 0, templateParam ) ] );
if ( res === null ) {
return null;
}
return [ res[0] ].concat( res[1] );
}
] );
var openTemplate = makeStringParser( '{{' );
var closeTemplate = makeStringParser( '}}' );
function template () {
var result = sequence( [ openTemplate, templateContents, closeTemplate ] );
return result === null ? null : result[1];
}
var expression = choice( [ template, replacement, literal ] );
var paramExpression = choice( [ template, replacement, literalWithoutBar ] );
function start () {
var result = nOrMore( 0, expression )();
if ( result === null ) {
return null;
}
return [ 'CONCAT' ].concat( result );
}
var result = start();
/*
* For success, the pos must have gotten to the end of the input
* and returned a non-null.
* n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
*/
if ( result === null || pos !== message.length ) {
throw new Error( 'Parse error at position ' + pos.toString() + ' in input: ' + message );
}
return result;
}
};
$.extend( $.i18n.parser, new MessageParser() );
}( jQuery ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n 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.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $ ) {
'use strict';
var MessageParserEmitter = function () {
this.language = $.i18n.languages[String.locale] || $.i18n.languages['default'];
};
MessageParserEmitter.prototype = {
constructor: MessageParserEmitter,
/**
* (We put this method definition here, and not in prototype, to make
* sure it's not overwritten by any magic.) Walk entire node structure,
* applying replacements and template functions when appropriate
*
* @param {Mixed} node abstract syntax tree (top node or subnode)
* @param {Array} replacements for $1, $2, ... $n
* @return {Mixed} single-string node or array of nodes suitable for
* jQuery appending.
*/
emit: function ( node, replacements ) {
var ret, subnodes, operation,
messageParserEmitter = this;
switch ( typeof node ) {
case 'string':
case 'number':
ret = node;
break;
case 'object':
// node is an array of nodes
subnodes = $.map( node.slice( 1 ), function ( n ) {
return messageParserEmitter.emit( n, replacements );
} );
operation = node[0].toLowerCase();
if ( typeof messageParserEmitter[operation] === 'function' ) {
ret = messageParserEmitter[operation]( subnodes, replacements );
} else {
throw new Error( 'unknown operation "' + operation + '"' );
}
break;
case 'undefined':
// Parsing the empty string (as an entire expression, or as a
// paramExpression in a template) results in undefined
// Perhaps a more clever parser can detect this, and return the
// empty string? Or is that useful information?
// The logical thing is probably to return the empty string here
// when we encounter undefined.
ret = '';
break;
default:
throw new Error( 'unexpected type in AST: ' + typeof node );
}
return ret;
},
/**
* Parsing has been applied depth-first we can assume that all nodes
* here are single nodes Must return a single node to parents -- a
* jQuery with synthetic span However, unwrap any other synthetic spans
* in our children and pass them upwards
*
* @param {Array} nodes Mixed, some single nodes, some arrays of nodes.
* @return String
*/
concat: function ( nodes ) {
var result = '';
$.each( nodes, function ( i, node ) {
// strings, integers, anything else
result += node;
} );
return result;
},
/**
* Return escaped replacement of correct index, or string if
* unavailable. Note that we expect the parsed parameter to be
* zero-based. i.e. $1 should have become [ 0 ]. if the specified
* parameter is not found return the same string (e.g. "$99" ->
* parameter 98 -> not found -> return "$99" ) TODO throw error if
* nodes.length > 1 ?
*
* @param {Array} nodes One element, integer, n >= 0
* @param {Array} replacements for $1, $2, ... $n
* @return {string} replacement
*/
replace: function ( nodes, replacements ) {
var index = parseInt( nodes[0], 10 );
if ( index < replacements.length ) {
// replacement is not a string, don't touch!
return replacements[index];
} else {
// index not found, fallback to displaying variable
return '$' + ( index + 1 );
}
},
/**
* Transform parsed structure into pluralization n.b. The first node may
* be a non-integer (for instance, a string representing an Arabic
* number). So convert it back with the current language's
* convertNumber.
*
* @param {Array} nodes List [ {String|Number}, {String}, {String} ... ]
* @return {String} selected pluralized form according to current
* language.
*/
plural: function ( nodes ) {
var count = parseFloat( this.language.convertNumber( nodes[0], 10 ) ),
forms = nodes.slice( 1 );
return forms.length ? this.language.convertPlural( count, forms ) : '';
},
/**
* Transform parsed structure into gender Usage
* {{gender:gender|masculine|feminine|neutral}}.
*
* @param {Array} nodes List [ {String}, {String}, {String} , {String} ]
* @return {String} selected gender form according to current language
*/
gender: function ( nodes ) {
var gender = nodes[0],
forms = nodes.slice( 1 );
return this.language.gender( gender, forms );
},
/**
* Transform parsed structure into grammar conversion. Invoked by
* putting {{grammar:form|word}} in a message
*
* @param {Array} nodes List [{Grammar case eg: genitive}, {String word}]
* @return {String} selected grammatical form according to current
* language.
*/
grammar: function ( nodes ) {
var form = nodes[0],
word = nodes[1];
return word && form && this.language.convertGrammar( word, form );
}
};
$.extend( $.i18n.parser.emitter, new MessageParserEmitter() );
}( jQuery ) );
/*global pluralRuleParser */
( function ( $ ) {
'use strict';
var language = {
// CLDR plural rules generated using
// http://i18ndata.appspot.com/cldr/tags/unconfirmed/supplemental/plurals?action=browse&depth=-1
// and compressed
pluralRules: {
gv: {
one: 'n mod 10 in 1..2 or n mod 20 is 0'
},
gu: {
one: 'n is 1'
},
rof: {
one: 'n is 1'
},
ga: {
few: 'n in 3..6',
many: 'n in 7..10',
two: 'n is 2',
one: 'n is 1'
},
gl: {
one: 'n is 1'
},
lg: {
one: 'n is 1'
},
lb: {
one: 'n is 1'
},
xog: {
one: 'n is 1'
},
ln: {
one: 'n in 0..1'
},
lo: '',
brx: {
one: 'n is 1'
},
tr: '',
ts: {
one: 'n is 1'
},
tn: {
one: 'n is 1'
},
to: '',
lt: {
few: 'n mod 10 in 2..9 and n mod 100 not in 11..19',
one: 'n mod 10 is 1 and n mod 100 not in 11..19'
},
tk: {
one: 'n is 1'
},
th: '',
ksb: {
one: 'n is 1'
},
te: {
one: 'n is 1'
},
ksh: {
zero: 'n is 0',
one: 'n is 1'
},
fil: {
one: 'n in 0..1'
},
haw: {
one: 'n is 1'
},
kcg: {
one: 'n is 1'
},
ssy: {
one: 'n is 1'
},
yo: '',
de: {
one: 'n is 1'
},
ko: '',
da: {
one: 'n is 1'
},
dz: '',
dv: {
one: 'n is 1'
},
guw: {
one: 'n in 0..1'
},
shi: {
few: 'n in 2..10',
one: 'n within 0..1'
},
el: {
one: 'n is 1'
},
eo: {
one: 'n is 1'
},
en: {
one: 'n is 1'
},
ses: '',
teo: {
one: 'n is 1'
},
ee: {
one: 'n is 1'
},
kde: '',
fr: {
one: 'n within 0..2 and n is not 2'
},
eu: {
one: 'n is 1'
},
et: {
one: 'n is 1'
},
es: {
one: 'n is 1'
},
seh: {
one: 'n is 1'
},
ru: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
kl: {
one: 'n is 1'
},
sms: {
two: 'n is 2',
one: 'n is 1'
},
smn: {
two: 'n is 2',
one: 'n is 1'
},
smj: {
two: 'n is 2',
one: 'n is 1'
},
smi: {
two: 'n is 2',
one: 'n is 1'
},
fy: {
one: 'n is 1'
},
rm: {
one: 'n is 1'
},
ro: {
few: 'n is 0 OR n is not 1 AND n mod 100 in 1..19',
one: 'n is 1'
},
bn: {
one: 'n is 1'
},
sma: {
two: 'n is 2',
one: 'n is 1'
},
be: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
bg: {
one: 'n is 1'
},
ms: '',
wa: {
one: 'n in 0..1'
},
ps: {
one: 'n is 1'
},
wo: '',
bm: '',
jv: '',
bo: '',
bh: {
one: 'n in 0..1'
},
kea: '',
asa: {
one: 'n is 1'
},
cgg: {
one: 'n is 1'
},
br: {
few: 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99',
many: 'n mod 1000000 is 0 and n is not 0',
two: 'n mod 10 is 2 and n mod 100 not in 12,72,92',
one: 'n mod 10 is 1 and n mod 100 not in 11,71,91'
},
bs: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
ja: '',
om: {
one: 'n is 1'
},
fa: '',
vun: {
one: 'n is 1'
},
or: {
one: 'n is 1'
},
xh: {
one: 'n is 1'
},
nso: {
one: 'n in 0..1'
},
ca: {
one: 'n is 1'
},
cy: {
few: 'n is 3',
zero: 'n is 0',
many: 'n is 6',
two: 'n is 2',
one: 'n is 1'
},
cs: {
few: 'n in 2..4',
one: 'n is 1'
},
zh: '',
lv: {
zero: 'n is 0',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
pt: {
one: 'n is 1'
},
wae: {
one: 'n is 1'
},
tl: {
one: 'n in 0..1'
},
chr: {
one: 'n is 1'
},
pa: {
one: 'n is 1'
},
ak: {
one: 'n in 0..1'
},
pl: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14',
one: 'n is 1'
},
hr: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
am: {
one: 'n in 0..1'
},
ti: {
one: 'n in 0..1'
},
hu: '',
hi: {
one: 'n in 0..1'
},
jmc: {
one: 'n is 1'
},
ha: {
one: 'n is 1'
},
he: {
one: 'n is 1'
},
mg: {
one: 'n in 0..1'
},
fur: {
one: 'n is 1'
},
bem: {
one: 'n is 1'
},
ml: {
one: 'n is 1'
},
mo: {
few: 'n is 0 OR n is not 1 AND n mod 100 in 1..19',
one: 'n is 1'
},
mn: {
one: 'n is 1'
},
mk: {
one: 'n mod 10 is 1 and n is not 11'
},
ur: {
one: 'n is 1'
},
bez: {
one: 'n is 1'
},
mt: {
few: 'n is 0 or n mod 100 in 2..10',
many: 'n mod 100 in 11..19',
one: 'n is 1'
},
uk: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
mr: {
one: 'n is 1'
},
ta: {
one: 'n is 1'
},
my: '',
sah: '',
ve: {
one: 'n is 1'
},
af: {
one: 'n is 1'
},
vi: '',
is: {
one: 'n is 1'
},
iu: {
two: 'n is 2',
one: 'n is 1'
},
it: {
one: 'n is 1'
},
kn: '',
ii: '',
ar: {
few: 'n mod 100 in 3..10',
zero: 'n is 0',
many: 'n mod 100 in 11..99',
two: 'n is 2',
one: 'n is 1'
},
zu: {
one: 'n is 1'
},
saq: {
one: 'n is 1'
},
az: '',
tzm: {
one: 'n in 0..1 or n in 11..99'
},
id: '',
ig: '',
pap: {
one: 'n is 1'
},
nl: {
one: 'n is 1'
},
nn: {
one: 'n is 1'
},
no: {
one: 'n is 1'
},
nah: {
one: 'n is 1'
},
nd: {
one: 'n is 1'
},
ne: {
one: 'n is 1'
},
ny: {
one: 'n is 1'
},
naq: {
two: 'n is 2',
one: 'n is 1'
},
nyn: {
one: 'n is 1'
},
kw: {
two: 'n is 2',
one: 'n is 1'
},
nr: {
one: 'n is 1'
},
tig: {
one: 'n is 1'
},
kab: {
one: 'n within 0..2 and n is not 2'
},
mas: {
one: 'n is 1'
},
rwk: {
one: 'n is 1'
},
kaj: {
one: 'n is 1'
},
lag: {
zero: 'n is 0',
one: 'n within 0..2 and n is not 0 and n is not 2'
},
syr: {
one: 'n is 1'
},
kk: {
one: 'n is 1'
},
ff: {
one: 'n within 0..2 and n is not 2'
},
fi: {
one: 'n is 1'
},
fo: {
one: 'n is 1'
},
ka: '',
gsw: {
one: 'n is 1'
},
ckb: {
one: 'n is 1'
},
ss: {
one: 'n is 1'
},
sr: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
sq: {
one: 'n is 1'
},
sw: {
one: 'n is 1'
},
sv: {
one: 'n is 1'
},
km: '',
st: {
one: 'n is 1'
},
sk: {
few: 'n in 2..4',
one: 'n is 1'
},
sh: {
few: 'n mod 10 in 2..4 and n mod 100 not in 12..14',
many: 'n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14',
one: 'n mod 10 is 1 and n mod 100 is not 11'
},
so: {
one: 'n is 1'
},
sn: {
one: 'n is 1'
},
ku: {
one: 'n is 1'
},
sl: {
few: 'n mod 100 in 3..4',
two: 'n mod 100 is 2',
one: 'n mod 100 is 1'
},
sg: '',
nb: {
one: 'n is 1'
},
se: {
two: 'n is 2',
one: 'n is 1'
}
},
/**
* Plural form transformations, needed for some languages.
*
* @param count
* integer Non-localized quantifier
* @param forms
* array List of plural forms
* @return string Correct form for quantifier in this language
*/
convertPlural: function ( count, forms ) {
var pluralRules,
pluralFormIndex = 0;
if ( !forms || forms.length === 0 ) {
return '';
}
pluralRules = this.pluralRules[$.i18n().locale];
if ( !pluralRules ) {
// default fallback.
return ( count === 1 ) ? forms[0] : forms[1];
}
pluralFormIndex = this.getPluralForm( count, pluralRules );
pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 );
return forms[pluralFormIndex];
},
/**
* For the number, get the plural for index
*
* @param number
* @param pluralRules
* @return plural form index
*/
getPluralForm: function ( number, pluralRules ) {
var i,
pluralForms = [ 'zero', 'one', 'two', 'few', 'many', 'other' ],
pluralFormIndex = 0;
for ( i = 0; i < pluralForms.length; i++ ) {
if ( pluralRules[pluralForms[i]] ) {
if ( pluralRuleParser( pluralRules[pluralForms[i]], number ) ) {
return pluralFormIndex;
}
pluralFormIndex++;
}
}
return pluralFormIndex;
},
/**
* Converts a number using digitTransformTable.
*
* @param {number} num Value to be converted
* @param {boolean} integer Convert the return value to an integer
*/
'convertNumber': function ( num, integer ) {
var tmp, item, i,
transformTable, numberString, convertedNumber;
// Set the target Transform table:
transformTable = this.digitTransformTable( $.i18n().locale );
numberString = '' + num;
convertedNumber = '';
if ( !transformTable ) {
return num;
}
// Check if the restore to Latin number flag is set:
if ( integer ) {
if ( parseFloat( num, 10 ) === num ) {
return num;
}
tmp = [];
for ( item in transformTable ) {
tmp[transformTable[item]] = item;
}
transformTable = tmp;
}
for ( i = 0; i < numberString.length; i++ ) {
if ( transformTable[numberString[i]] ) {
convertedNumber += transformTable[numberString[i]];
} else {
convertedNumber += numberString[i];
}
}
return integer ? parseFloat( convertedNumber, 10 ) : convertedNumber;
},
/**
* Grammatical transformations, needed for inflected languages.
* Invoked by putting {{grammar:form|word}} in a message.
* Override this method for languages that need special grammar rules
* applied dynamically.
*
* @param word {String}
* @param form {String}
* @return {String}
*/
convertGrammar: function ( word, form ) {
return word;
},
/**
* Provides an alternative text depending on specified gender. Usage
* {{gender:[gender|user object]|masculine|feminine|neutral}}. If second
* or third parameter are not specified, masculine is used.
*
* These details may be overriden per language.
*
* @param gender
* string male, female, or anything else for neutral.
* @param forms
* array List of gender forms
*
* @return string
*/
'gender': function ( gender, forms ) {
if ( !forms || forms.length === 0 ) {
return '';
}
while ( forms.length < 2 ) {
forms.push( forms[forms.length - 1] );
}
if ( gender === 'male' ) {
return forms[0];
}
if ( gender === 'female' ) {
return forms[1];
}
return ( forms.length === 3 ) ? forms[2] : forms[0];
},
/**
* Get the digit transform table for the given language
* See http://cldr.unicode.org/translation/numbering-systems
* @param language
* @returns {Array|boolean} List of digits in the passed language or false
* representation, or boolean false if there is no information.
*/
digitTransformTable: function ( language ) {
var tables = {
ar: '٠١٢٣٤٥٦٧٨٩',
fa: '۰۱۲۳۴۵۶۷۸۹',
ml: '൦൧൨൩൪൫൬൭൮൯',
kn: '೦೧೨೩೪೫೬೭೮೯',
lo: '໐໑໒໓໔໕໖໗໘໙',
or: '୦୧୨୩୪୫୬୭୮୯',
kh: '០១២៣៤៥៦៧៨៩',
pa: '੦੧੨੩੪੫੬੭੮੯',
gu: '૦૧૨૩૪૫૬૭૮૯',
hi: '०१२३४५६७८९',
my: '၀၁၂၃၄၅၆၇၈၉',
ta: '௦௧௨௩௪௫௬௭௮௯',
te: '౦౧౨౩౪౫౬౭౮౯',
th: '๐๑๒๓๔๕๖๗๘๙', //FIXME use iso 639 codes
bo: '༠༡༢༣༤༥༦༧༨༩' //FIXME use iso 639 codes
};
if ( !tables[language] ) {
return false;
}
return tables[language].split( '' );
}
};
$.extend( $.i18n.languages, {
'default': language
} );
}( jQuery ) );
/**
* Bosnian (bosanski) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.bs = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'instrumental': // instrumental
word = 's ' + word;
break;
case 'lokativ': // locative
word = 'o ' + word;
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Lower Sorbian (Dolnoserbski) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.dsb = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'instrumental': // instrumental
word = 'z ' + word;
break;
case 'lokatiw': // lokatiw
word = 'wo ' + word;
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Finnish (Suomi) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
'use strict';
$.i18n.languages.fi = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
// vowel harmony flag
var aou = word.match( /[aou][^äöy]*$/i ),
origWord = word;
if ( word.match( /wiki$/i ) ) {
aou = false;
}
// append i after final consonant
if ( word.match( /[bcdfghjklmnpqrstvwxz]$/i ) ) {
word += 'i';
}
switch ( form ) {
case 'genitive':
word += 'n';
break;
case 'elative':
word += ( aou ? 'sta' : 'stä' );
break;
case 'partitive':
word += ( aou ? 'a' : 'ä' );
break;
case 'illative':
// Double the last letter and add 'n'
word += word.substr( word.length - 1 ) + 'n';
break;
case 'inessive':
word += ( aou ? 'ssa' : 'ssä' );
break;
default:
word = origWord;
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Irish (Gaeilge) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.ga = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'ainmlae' ) {
switch ( word ) {
case 'an Domhnach':
word = 'Dé Domhnaigh';
break;
case 'an Luan':
word = 'Dé Luain';
break;
case 'an Mháirt':
word = 'Dé Mháirt';
break;
case 'an Chéadaoin':
word = 'Dé Chéadaoin';
break;
case 'an Déardaoin':
word = 'Déardaoin';
break;
case 'an Aoine':
word = 'Dé hAoine';
break;
case 'an Satharn':
word = 'Dé Sathairn';
break;
}
}
return word;
}
} );
}( jQuery ) );
/**
* Hebrew (עברית) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.he = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'prefixed':
case 'תחילית': // the same word in Hebrew
// Duplicate prefixed "Waw", but only if it's not already double
if ( word.substr( 0, 1 ) === 'ו' && word.substr( 0, 2 ) !== 'וו' ) {
word = 'ו' + word;
}
// Remove the "He" if prefixed
if ( word.substr( 0, 1 ) === 'ה' ) {
word = word.substr( 1, word.length );
}
// Add a hyphen (maqaf) before numbers and non-Hebrew letters
if ( word.substr( 0, 1 ) < 'א' || word.substr( 0, 1 ) > 'ת' ) {
word = '־' + word;
}
}
return word;
}
} );
}( jQuery ) );
/**
* Upper Sorbian (Hornjoserbsce) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.hsb = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'instrumental': // instrumental
word = 'z ' + word;
break;
case 'lokatiw': // lokatiw
word = 'wo ' + word;
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Hungarian language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
'use strict';
$.i18n.languages.hu = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'rol':
word += 'ról';
break;
case 'ba':
word += 'ba';
break;
case 'k':
word += 'k';
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Armenian (Հայերեն) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.hy = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'genitive' ) { // սեռական հոլով
if ( word.substr( -1 ) === 'ա' ) {
word = word.substr( 0, word.length - 1 ) + 'այի';
} else if ( word.substr( -1 ) === 'ո' ) {
word = word.substr( 0, word.length - 1 ) + 'ոյի';
} else if ( word.substr( -4 ) === 'գիրք' ) {
word = word.substr( 0, word.length - 4 ) + 'գրքի';
} else {
word = word + 'ի';
}
}
return word;
}
} );
}( jQuery ) );
/**
* Latin (lingua Latina) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
'use strict';
$.i18n.languages.la = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'genitive':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'i' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'ommunium' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'ae' ); // 1st declension singular
word = word.replace( /libri$/i, 'librorum' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntiorum' ); // 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tionis' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'ntis' );
word = word.replace( /as$/i, 'atis' );
word = word.replace( /es$/i, 'ei' ); // 5th declension singular
break;
case 'accusative':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'um' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'am' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'ommunia' ); // 1st declension singular
word = word.replace( /libri$/i, 'libros' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntios' );// 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tionem' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'ntem' );
word = word.replace( /as$/i, 'atem' );
word = word.replace( /es$/i, 'em' ); // 5th declension singular
break;
case 'ablative':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'o' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'ommunibus' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'a' ); // 1st declension singular
word = word.replace( /libri$/i, 'libris' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntiis' ); // 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tione' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'nte' );
word = word.replace( /as$/i, 'ate' );
word = word.replace( /es$/i, 'e' ); // 5th declension singular
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Ossetian (Ирон) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
'use strict';
$.i18n.languages.os = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
// Ending for allative case
var endAllative = 'мæ';
// Variable for 'j' beetwen vowels
var jot = '';
// Variable for "-" for not Ossetic words
var hyphen = '';
// Variable for ending
var ending = '';
// Checking if the $word is in plural form
if ( word.match( /тæ$/i ) ) {
word = word.substring( 0, word.length - 1 );
endAllative = 'æм';
}
// Works if word is in singular form.
// Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю,
// я.
else if ( word.match( /[аæеёиоыэюя]$/i ) ) {
jot = 'й';
}
// Checking if word ends on 'у'. 'У' can be either consonant 'W' or
// vowel 'U' in cyrillic Ossetic.
// Examples: {{grammar:genitive|аунеу}} = аунеуы,
// {{grammar:genitive|лæппу}} = лæппуйы.
else if ( word.match( /у$/i ) ) {
if ( !word.substring( word.length - 2, word.length - 1 )
.match( /[аæеёиоыэюя]$/i ) ) {
jot = 'й';
}
} else if ( !word.match( /[бвгджзйклмнопрстфхцчшщьъ]$/i ) ) {
hyphen = '-';
}
switch ( form ) {
case 'genitive':
ending = hyphen + jot + 'ы';
break;
case 'dative':
ending = hyphen + jot + 'æн';
break;
case 'allative':
ending = hyphen + endAllative;
break;
case 'ablative':
if ( jot === 'й' ) {
ending = hyphen + jot + 'æ';
} else {
ending = hyphen + jot + 'æй';
}
break;
case 'superessive':
ending = hyphen + jot + 'ыл';
break;
case 'equative':
ending = hyphen + jot + 'ау';
break;
case 'comitative':
ending = hyphen + 'имæ';
break;
}
return word + ending;
}
} );
}( jQuery ) );
/**
* Russian (Русский) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.ru = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'genitive' ) { // родительный падеж
if ( ( word.substr( word.length - 4 ) === 'вики' ) ||
( word.substr( word.length - 4 ) === 'Вики' )
) {
// ...
} else if ( word.substr( word.length - 1 ) === 'ь' ) {
word = word.substr( 0, word.length - 1 ) + 'я';
} else if ( word.substr( word.length - 2 ) === 'ия' ) {
word = word.substr( 0, word.length - 2 ) + 'ии';
} else if ( word.substr( word.length - 2 ) === 'ка' ) {
word = word.substr( 0, word.length - 2 ) + 'ки';
} else if ( word.substr( word.length - 2 ) === 'ти' ) {
word = word.substr( 0, word.length - 2 ) + 'тей';
} else if ( word.substr( word.length - 2 ) === 'ды' ) {
word = word.substr( 0, word.length - 2 ) + 'дов';
} else if ( word.substr( word.length - 3 ) === 'ник' ) {
word = word.substr( 0, word.length - 3 ) + 'ника';
}
}
return word;
}
} );
}( jQuery ) );
/**
* Slovenian (Slovenščina) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.sl = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
// locative
case 'mestnik':
word = 'o ' + word;
break;
// instrumental
case 'orodnik':
word = 'z ' + word;
break;
}
return word;
}
} );
}( jQuery ) );
/**
* Ukrainian (Українська) language functions
*/
( function ( $ ) {
'use strict';
$.i18n.languages.uk = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch ( form ) {
case 'genitive': // родовий відмінок
if ( ( word.substr( word.length - 4 ) === 'вікі' ) ||
( word.substr( word.length - 4 ) === 'Вікі' )
) {
// ...
} else if ( word.substr( word.length - 1 ) === 'ь' ) {
word = word.substr( 0, word.length - 1 ) + 'я';
} else if ( word.substr( word.length - 2 ) === 'ія' ) {
word = word.substr( 0, word.length - 2 ) + 'ії';
} else if ( word.substr( word.length - 2 ) === 'ка' ) {
word = word.substr( 0, word.length - 2 ) + 'ки';
} else if ( word.substr( word.length - 2 ) === 'ти' ) {
word = word.substr( 0, word.length - 2 ) + 'тей';
} else if ( word.substr( word.length - 2 ) === 'ды' ) {
word = word.substr( 0, word.length - 2 ) + 'дов';
} else if ( word.substr( word.length - 3 ) === 'ник' ) {
word = word.substr( 0, word.length - 3 ) + 'ника';
}
break;
case 'accusative': // знахідний відмінок
if ( ( word.substr( word.length - 4 ) === 'вікі' ) ||
( word.substr( word.length - 4 ) === 'Вікі' )
) {
// ...
} else if ( word.substr( word.length - 2 ) === 'ія' ) {
word = word.substr( 0, word.length - 2 ) + 'ію';
}
break;
}
return word;
}
} );
}( jQuery ) );
/**
* cldrpluralparser.js
* A parser engine for CLDR plural rules.
*
* Copyright 2012 GPLV3+, Santhosh Thottingal
*
* @version 0.1.0-alpha
* @source https://github.com/santhoshtr/CLDRPluralRuleParser
* @author Santhosh Thottingal
', invisibleSpace: '', rBlockTest: /^(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)$/i, alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'TD', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], ownLine: ['area', 'body', 'head', 'hr', 'i?frame', 'link', 'meta', 'noscript', 'style', 'script', 'table', 'tbody', 'thead', 'tfoot'], contOwnLine: ['li', 'dt', 'dt', 'h[1-6]', 'option', 'script'], newLevel: ['blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset', 'map', 'ol', 'p', 'pre', 'select', 'td', 'th', 'tr', 'ul'], blockLevelElements: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'LI', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'PRE', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], // lang langs: { en: { html: 'HTML', video: 'Insert Video', image: 'Insert Image', table: 'Table', link: 'Link', link_insert: 'Insert link', link_edit: 'Edit link', unlink: 'Unlink', formatting: 'Formatting', paragraph: 'Normal text', quote: 'Quote', code: 'Code', header1: 'Header 1', header2: 'Header 2', header3: 'Header 3', header4: 'Header 4', bold: 'Bold', italic: 'Italic', fontcolor: 'Font Color', backcolor: 'Back Color', unorderedlist: 'Unordered List', orderedlist: 'Ordered List', outdent: 'Outdent', indent: 'Indent', cancel: 'Cancel', insert: 'Insert', save: 'Save', _delete: 'Delete', insert_table: 'Insert Table', insert_row_above: 'Add Row Above', insert_row_below: 'Add Row Below', insert_column_left: 'Add Column Left', insert_column_right: 'Add Column Right', delete_column: 'Delete Column', delete_row: 'Delete Row', delete_table: 'Delete Table', rows: 'Rows', columns: 'Columns', add_head: 'Add Head', delete_head: 'Delete Head', title: 'Title', image_position: 'Position', none: 'None', left: 'Left', right: 'Right', image_web_link: 'Image Web Link', text: 'Text', mailto: 'Email', web: 'URL', video_html_code: 'Video Embed Code', file: 'Insert File', upload: 'Upload', download: 'Download', choose: 'Choose', or_choose: 'Or choose', drop_file_here: 'Drop file here', align_left: 'Align text to the left', align_center: 'Center text', align_right: 'Align text to the right', align_justify: 'Justify text', horizontalrule: 'Insert Horizontal Rule', deleted: 'Deleted', anchor: 'Anchor', link_new_tab: 'Open link in new tab', underline: 'Underline', alignment: 'Alignment', filename: 'Name (optional)' } } }; // Functionality Redactor.fn = $.Redactor.prototype = { keyCode: { BACKSPACE: 8, DELETE: 46, DOWN: 40, ENTER: 13, ESC: 27, TAB: 9, CTRL: 17, META: 91, LEFT: 37, LEFT_WIN: 91 }, // Initialization init: function(el, options) { this.$element = this.$source = $(el); this.uuid = uuid++; // current settings this.opts = $.extend( {}, $.Redactor.opts, this.$element.data(), options ); this.start = true; this.dropdowns = []; // get sizes this.sourceHeight = this.$source.css('height'); this.sourceWidth = this.$source.css('width'); // dependency of the editor modes if (this.opts.fullpage) this.opts.iframe = true; if (this.opts.linebreaks) this.opts.paragraphy = false; if (this.opts.paragraphy) this.opts.linebreaks = false; if (this.opts.toolbarFixedBox) this.opts.toolbarFixed = true; // the alias for iframe mode this.document = document; this.window = window; // selection saved this.savedSel = false; // clean setup this.cleanlineBefore = new RegExp('^<(/?' + this.opts.ownLine.join('|/?' ) + '|' + this.opts.contOwnLine.join('|') + ')[ >]'); this.cleanlineAfter = new RegExp('^<(br|/?' + this.opts.ownLine.join('|/?' ) + '|/' + this.opts.contOwnLine.join('|/') + ')[ >]'); this.cleannewLevel = new RegExp('^?(' + this.opts.newLevel.join('|' ) + ')[ >]'); // block level this.rTestBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i'); // setup formatting permissions if (this.opts.linebreaks === false) { if (this.opts.allowedTags !== false && $.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p'); if (this.opts.deniedTags !== false) { var pos = $.inArray('p', this.opts.deniedTags); if (pos !== '-1') this.opts.deniedTags.splice(pos, pos); } } // ie & opera if (this.browser('msie') || this.browser('opera')) { this.opts.buttons = this.removeFromArrayByValue(this.opts.buttons, 'horizontalrule'); } // load lang this.opts.curLang = this.opts.langs[this.opts.lang]; // Build this.buildStart(); }, initToolbar: function(lang) { return { html: { title: lang.html, func: 'toggle' }, formatting: { title: lang.formatting, func: 'show', dropdown: { p: { title: lang.paragraph, func: 'formatBlocks' }, blockquote: { title: lang.quote, func: 'formatQuote', className: 'redactor_format_blockquote' }, pre: { title: lang.code, func: 'formatBlocks', className: 'redactor_format_pre' }, h1: { title: lang.header1, func: 'formatBlocks', className: 'redactor_format_h1' }, h2: { title: lang.header2, func: 'formatBlocks', className: 'redactor_format_h2' }, h3: { title: lang.header3, func: 'formatBlocks', className: 'redactor_format_h3' }, h4: { title: lang.header4, func: 'formatBlocks', className: 'redactor_format_h4' } } }, bold: { title: lang.bold, exec: 'bold' }, italic: { title: lang.italic, exec: 'italic' }, deleted: { title: lang.deleted, exec: 'strikethrough' }, underline: { title: lang.underline, exec: 'underline' }, unorderedlist: { title: '• ' + lang.unorderedlist, exec: 'insertunorderedlist' }, orderedlist: { title: '1. ' + lang.orderedlist, exec: 'insertorderedlist' }, outdent: { title: '< ' + lang.outdent, func: 'indentingOutdent' }, indent: { title: '> ' + lang.indent, func: 'indentingIndent' }, image: { title: lang.image, func: 'imageShow' }, video: { title: lang.video, func: 'videoShow' }, file: { title: lang.file, func: 'fileShow' }, table: { title: lang.table, func: 'show', dropdown: { insert_table: { title: lang.insert_table, func: 'tableShow' }, separator_drop1: { name: 'separator' }, insert_row_above: { title: lang.insert_row_above, func: 'tableAddRowAbove' }, insert_row_below: { title: lang.insert_row_below, func: 'tableAddRowBelow' }, insert_column_left: { title: lang.insert_column_left, func: 'tableAddColumnLeft' }, insert_column_right: { title: lang.insert_column_right, func: 'tableAddColumnRight' }, separator_drop2: { name: 'separator' }, add_head: { title: lang.add_head, func: 'tableAddHead' }, delete_head: { title: lang.delete_head, func: 'tableDeleteHead' }, separator_drop3: { name: 'separator' }, delete_column: { title: lang.delete_column, func: 'tableDeleteColumn' }, delete_row: { title: lang.delete_row, func: 'tableDeleteRow' }, delete_table: { title: lang.delete_table, func: 'tableDeleteTable' } } }, link: { title: lang.link, func: 'show', dropdown: { link: { title: lang.link_insert, func: 'linkShow' }, unlink: { title: lang.unlink, exec: 'unlink' } } }, fontcolor: { title: lang.fontcolor, func: 'show' }, backcolor: { title: lang.backcolor, func: 'show' }, alignment: { title: lang.alignment, func: 'show', dropdown: { alignleft: { title: lang.align_left, func: 'alignmentLeft' }, aligncenter: { title: lang.align_center, func: 'alignmentCenter' }, alignright: { title: lang.align_right, func: 'alignmentRight' }, justify: { title: lang.align_justify, func: 'alignmentJustify' } } }, alignleft: { title: lang.align_left, func: 'alignmentLeft' }, aligncenter: { title: lang.align_center, func: 'alignmentCenter' }, alignright: { title: lang.align_right, func: 'alignmentRight' }, justify: { title: lang.align_justify, func: 'alignmentJustify' }, horizontalrule: { exec: 'inserthorizontalrule', title: lang.horizontalrule } } }, // CALLBACKS callback: function(type, event, data) { var callback = this.opts[ type + 'Callback' ]; if ($.isFunction(callback)) { if (event === false) return callback.call(this, data); else return callback.call(this, event, data); } else return data; }, // DESTROY destroy: function() { clearInterval(this.autosaveInterval); $(window).off('.redactor'); this.$element.off('.redactor').removeData('redactor'); var html = this.get(); if (this.opts.textareamode) { this.$box.after(this.$source); this.$box.remove(); this.$source.val(html).show(); } else { var $elem = this.$editor; if (this.opts.iframe) $elem = this.$element; this.$box.after($elem); this.$box.remove(); $elem.removeClass('redactor_editor').removeClass('redactor_editor_wym').removeAttr('contenteditable').html(html).show(); } }, // API GET getObject: function() { return $.extend({}, this); }, getEditor: function() { return this.$editor; }, getBox: function() { return this.$box; }, getIframe: function() { return (this.opts.iframe) ? this.$frame : false; }, getToolbar: function() { return this.$toolbar; }, // CODE GET & SET get: function() { return this.$source.val(); }, getCodeIframe: function() { this.$editor.removeAttr('contenteditable').removeAttr('dir'); var html = this.outerHtml(this.$frame.contents().children()); this.$editor.attr({ 'contenteditable': true, 'dir': this.opts.direction }); return html; }, set: function(html, strip) { html = html.toString(); if (this.opts.fullpage) this.setCodeIframe(html); else this.setEditor(html, strip); }, setEditor: function(html, strip) { if (strip !== false) { html = this.cleanSavePreCode(html); html = this.cleanStripTags(html); html = this.cleanConvertProtected(html); html = this.cleanConvertInlineTags(html); if (this.opts.linebreaks === false) html = this.cleanConverters(html); else html = html.replace(/([\w\W]*?)<\/p>/gi, '$2 ' + this.opts.invisibleSpace + ' ' + this.opts.invisibleSpace + ' ').append($(current).clone());
$(current).replaceWith(node);
var next = $(node).next();
if (typeof(next[0]) !== 'undefined' && next[0].tagName == 'BR')
{
next.remove();
}
this.selectionEnd(node);
}
// convert links
if (this.opts.convertLinks && key === this.keyCode.ENTER)
{
this.formatLinkify(this.opts.linkProtocol);
}
// if empty
if (this.opts.linebreaks === false && (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE))
{
return this.formatEmpty(e);
}
this.callback('keyup', e);
this.sync();
}, this));
// focus callback
if ($.isFunction(this.opts.focusCallback))
{
this.$editor.on('focus.redactor', $.proxy(this.opts.focusCallback, this));
}
// blur callback
if ($.isFunction(this.opts.blurCallback))
{
this.$editor.on('blur.redactor', $.proxy(this.opts.blurCallback, this));
}
},
buildPlugins: function()
{
if (!this.opts.plugins ) return;
$.each(this.opts.plugins, $.proxy(function(i, s)
{
if (RedactorPlugins[s])
{
$.extend(this, RedactorPlugins[s]);
if ($.isFunction( RedactorPlugins[ s ].init)) this.init();
}
}, this ));
},
// IFRAME
iframeStart: function()
{
this.iframeCreate();
if (this.opts.textareamode) this.iframeAppend(this.$source);
else
{
this.$sourceOld = this.$source.hide();
this.$source = this.buildCodearea(this.$sourceOld);
this.iframeAppend(this.$sourceOld);
}
},
iframeAppend: function(el)
{
this.$source.attr('dir', this.opts.direction).hide();
this.$box.insertAfter(el).append(this.$frame).append(this.$source);
},
iframeCreate: function()
{
this.$frame = $('').one('load', $.proxy(function()
{
if (this.opts.fullpage)
{
this.iframePage();
if (this.content === '') this.content = this.opts.invisibleSpace;
this.$frame.contents()[0].write(this.content);
this.$frame.contents()[0].close();
var timer = setInterval($.proxy(function()
{
if (this.$frame.contents().find('body').html())
{
clearInterval(timer);
this.iframeLoad();
}
}, this), 0);
}
else this.iframeLoad();
}, this));
},
iframeDoc: function()
{
return this.$frame[0].contentWindow.document;
},
iframePage: function()
{
var doc = this.iframeDoc();
if (doc.documentElement) doc.removeChild(doc.documentElement);
return doc;
},
iframeAddCss: function(css)
{
css = css || this.opts.css;
if (this.isString(css))
{
this.$frame.contents().find('head').append('');
}
if ($.isArray(css))
{
$.each(css, $.proxy(function(i, url)
{
this.iframeAddCss(url);
}, this));
}
},
iframeLoad: function()
{
this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction });
// set document & window
if (this.$editor[0])
{
this.document = this.$editor[0].ownerDocument;
this.window = this.document.defaultView || window;
}
// iframe css
this.iframeAddCss();
if (this.opts.fullpage) this.setFullpageOnInit(this.$editor.html());
else this.set(this.content);
this.buildOptions();
this.buildAfter();
},
// PLACEHOLDER
placeholderStart: function(html)
{
if (this.isEmpty(html))
{
if (this.$element.attr('placeholder')) this.opts.placeholder = this.$element.attr('placeholder');
if (this.opts.placeholder === '') this.opts.placeholder = false;
if (this.opts.placeholder !== false)
{
this.opts.focus = false;
this.$editor.one('focus.redactor_placeholder', $.proxy(this.placeholderFocus, this));
return $('').attr('contenteditable', false).text(this.opts.placeholder);
}
}
return false;
},
placeholderFocus: function()
{
this.$editor.find('span.redactor_placeholder').remove();
var html = '';
if (this.opts.linebreaks === false) html = this.opts.emptyHtml;
this.$editor.off('focus.redactor_placeholder');
this.$editor.html(html);
if (this.opts.linebreaks === false)
{
// place the cursor inside emptyHtml
this.selectionStart(this.$editor.children()[0]);
}
this.sync();
},
placeholderRemove: function()
{
this.opts.placeholder = false;
this.$editor.find('span.redactor_placeholder').remove();
this.$editor.off('focus.redactor_placeholder');
},
placeholderRemoveFromCode: function(html)
{
return html.replace(/(.*?)<\/span>/i, '');
},
// SHORTCUTS
shortcuts: function(e, key)
{
if (!this.opts.shortcuts) return;
if (!e.altKey)
{
if (key === 77) this.shortcutsLoad(e, 'removeFormat'); // Ctrl + m
else if (key === 66) this.shortcutsLoad(e, 'bold'); // Ctrl + b
else if (key === 73) this.shortcutsLoad(e, 'italic'); // Ctrl + i
else if (key === 74) this.shortcutsLoad(e, 'insertunorderedlist'); // Ctrl + j
else if (key === 75) this.shortcutsLoad(e, 'insertorderedlist'); // Ctrl + k
else if (key === 72) this.shortcutsLoad(e, 'superscript'); // Ctrl + h
else if (key === 76) this.shortcutsLoad(e, 'subscript'); // Ctrl + l
}
else
{
if (key === 48) this.shortcutsLoadFormat(e, 'p'); // ctrl + alt + 0
else if (key === 49) this.shortcutsLoadFormat(e, 'h1'); // ctrl + alt + 1
else if (key === 50) this.shortcutsLoadFormat(e, 'h2'); // ctrl + alt + 2
else if (key === 51) this.shortcutsLoadFormat(e, 'h3'); // ctrl + alt + 3
else if (key === 52) this.shortcutsLoadFormat(e, 'h4'); // ctrl + alt + 4
else if (key === 53) this.shortcutsLoadFormat(e, 'h5'); // ctrl + alt + 5
else if (key === 54) this.shortcutsLoadFormat(e, 'h6'); // ctrl + alt + 6
}
},
shortcutsLoad: function(e, cmd)
{
e.preventDefault();
this.execCommand(cmd, false);
},
shortcutsLoadFormat: function(e, cmd)
{
e.preventDefault();
this.formatBlocks(cmd);
},
// FOCUS
focus: function()
{
if (!this.browser('opera')) this.window.setTimeout($.proxy(this.focusSet, this, true), 1);
else this.$editor.focus();
},
focusEnd: function()
{
this.focusSet();
},
focusSet: function(collapse)
{
this.$editor.focus();
var range = this.getRange();
range.selectNodeContents(this.$editor[0]);
// collapse - controls the position of focus: the beginning (true), at the end (false).
range.collapse(collapse || false);
var sel = this.getSelection();
sel.removeAllRanges();
sel.addRange(range);
},
// TOGGLE
toggle: function(direct)
{
var html;
if (this.opts.visual)
{
if (direct !== false) this.selectionSave();
var height = null;
if (this.opts.iframe)
{
height = this.$frame.height();
if (this.opts.fullpage) this.$editor.removeAttr('contenteditable');
this.$frame.hide();
}
else
{
height = this.$editor.innerHeight();
this.$editor.hide();
}
html = this.$source.val();
this.modified = html;
this.$source.height(height).show().focus();
// textarea indenting
this.$source.on('keydown.redactor-textarea', function (e)
{
if (e.keyCode === 9)
{
var $el = $(this);
var start = $el.get(0).selectionStart;
$el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd));
$el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1;
return false;
}
});
this.buttonInactiveVisual();
this.buttonActive('html');
this.opts.visual = false;
}
else
{
html = this.$source.hide().val();
if (typeof this.modified !== 'undefined')
{
this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false);
}
if (this.modified)
{
// don't remove the iframe even if cleared all.
if (this.opts.fullpage && html === '') this.setFullpageOnInit(html);
else
{
this.set(html);
if (this.opts.fullpage) this.buildBindKeyboard();
}
}
if (this.opts.iframe) this.$frame.show();
else this.$editor.show();
if (this.opts.fullpage ) this.$editor.attr('contenteditable', true );
this.$source.off('keydown.redactor-textarea');
this.$editor.focus();
this.selectionRestore();
this.observeStart();
this.buttonActiveVisual();
this.buttonInactive('html');
this.opts.visual = true;
}
},
// AUTOSAVE
autosave: function()
{
var savedHtml = false;
this.autosaveInterval = setInterval($.proxy(function()
{
var html = this.get();
if (savedHtml !== html)
{
$.ajax({
url: this.opts.autosave,
type: 'post',
data: this.$source.attr('name') + '=' + escape(encodeURIComponent(html)),
success: $.proxy(function(data)
{
this.callback('autosave', false, data);
savedHtml = html;
}, this)
});
}
}, this), this.opts.autosaveInterval*1000);
},
// TOOLBAR
toolbarBuild: function()
{
// extend buttons
if (this.opts.air)
{
this.opts.buttons = this.opts.airButtons;
}
else
{
if (!this.opts.buttonSource)
{
var index = this.opts.buttons.indexOf('html'), next = this.opts.buttons[index + 1];
this.opts.buttons.splice(index, 1);
if (next === '|') this.opts.buttons.splice(index, 1);
}
}
$.extend(this.opts.toolbar, this.opts.buttonsCustom);
$.each(this.opts.buttonsAdd, $.proxy(function(i, s)
{
this.opts.buttons.push(s);
}, this));
// formatting tags
if (this.opts.toolbar)
{
$.each(this.opts.toolbar.formatting.dropdown, $.proxy(function (i, s)
{
if ($.inArray(i, this.opts.formattingTags ) == '-1') delete this.opts.toolbar.formatting.dropdown[i];
}, this));
}
// if no buttons don't create a toolbar
if (this.opts.buttons.length === 0) return false;
// air enable
this.airEnable();
// toolbar build
this.$toolbar = $('
');
}
html = this.cleanEmpty(html);
this.$editor.html(html);
this.sync();
},
setCodeIframe: function(html)
{
var doc = this.iframePage();
this.$frame[0].src = "about:blank";
html = this.cleanConvertProtected(html);
html = this.cleanConvertInlineTags(html);
html = this.cleanRemoveSpaces(html);
doc.open();
doc.write(html);
doc.close();
// redefine editor for fullpage mode
if (this.opts.fullpage)
{
this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction });
}
this.sync();
},
setFullpageOnInit: function(html)
{
html = this.cleanSavePreCode(html, true);
html = this.cleanConverters(html);
html = this.cleanEmpty(html);
// set code
this.$editor.html(html);
this.sync();
},
// SYNC
sync: function()
{
var html = '';
this.cleanUnverified();
if (this.opts.fullpage) html = this.getCodeIframe();
else html = this.$editor.html();
html = this.syncClean(html);
html = this.cleanRemoveSpaces(html);
html = this.cleanRemoveEmptyTags(html);
// fix second level up ul, ol
html = html.replace(/<\/li><(ul|ol)>([\w\W]*?)<\/(ul|ol)>/gi, '<$1>$2$1>');
if ($.trim(html) === '
') html = '';
if (html !== '') html = this.cleanHtml(html);
// before callback
html = this.callback('syncBefore', false, html);
this.$source.val(html);
// TMP:
if (typeof htmlEncode != 'undefined')
{
$('#' + this.$element[0].id + '_code').html(htmlEncode(html));
}
// onchange & after callback
this.callback('syncAfter', false, html);
if (this.start === false)
{
this.callback('change', false, html);
}
},
syncClean: function(html)
{
if (!this.opts.fullpage) html = this.cleanStripTags(html);
html = $.trim(html);
// removeplaceholder
html = this.placeholderRemoveFromCode(html);
// remove space
html = html.replace(//gi, '');
html = html.replace(//gi, '');
html = html.replace(/ /gi, ' ');
// php code fix
html = html.replace('', '?>');
// Remove verified attr
html = html.replace(/([\w\W]*?)<\/span>/gi, '$3');
html = html.replace(/([\w\W]*?)<\/span>/gi, '$2');
html = html.replace(/([\w\W]*?)<\/font>/gi, '$3');
html = html.replace(/ data-tagblock=""/gi, '');
html = html.replace(/
\n?<\/(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi, '$1>');
html = html.replace(/([\w\W]*?)<\/span>/gi, '$3' );
html = html.replace(/([\w\W]*?)<\/span>/gi, '$1');
html = html.replace(/([\w\W]*?)<\/span>/gi, '');
html = html.replace(/([\w\W]*?)<\/span>/gi, '$1');
html = html.replace(/([\w\W]*?)<\/span>/gi, '$1');
html = this.cleanReConvertProtected(html);
return html;
},
// BUILD
buildStart: function()
{
// content
this.content = '';
// container
this.$box = $('');
// textarea test
if (this.$source[0].tagName === 'TEXTAREA') this.opts.textareamode = true;
// mobile
if (this.opts.mobile === false && this.isMobile())
{
this.buildMobile();
}
else
{
// get the content at the start
this.buildContent();
if (this.opts.iframe)
{
// build as iframe
this.opts.autoresize = false;
this.iframeStart();
}
else if (this.opts.textareamode) this.buildFromTextarea();
else this.buildFromElement();
// options and final setup
if (!this.opts.iframe)
{
this.buildOptions();
this.buildAfter();
}
}
},
buildMobile: function()
{
if (!this.opts.textareamode)
{
this.$editor = this.$source;
this.$editor.hide();
this.$source = this.buildCodearea(this.$editor);
this.$source.val(this.content);
}
this.$box.insertAfter(this.$source).append(this.$source);
},
buildContent: function()
{
if (this.opts.textareamode) this.content = $.trim(this.$source.val());
else this.content = $.trim(this.$source.html());
},
buildFromTextarea: function()
{
this.$editor = $('');
this.$box.insertAfter(this.$source).append(this.$editor).append(this.$source);
// enable
this.buildAddClasses(this.$editor);
this.buildEnable();
},
buildFromElement: function()
{
this.$editor = this.$source;
this.$source = this.buildCodearea(this.$editor);
this.$box.insertAfter(this.$editor).append(this.$editor).append(this.$source);
// enable
this.buildEnable();
},
buildCodearea: function($source)
{
return $('').attr('name', $source.attr('id')).css('height', this.sourceHeight);
},
buildAddClasses: function(el)
{
// append textarea classes to editable layer
$.each(this.$source.get(0).className.split(/\s+/), function(i,s)
{
el.addClass('redactor_' + s);
});
},
buildEnable: function()
{
this.$editor.addClass('redactor_editor').attr({ 'contenteditable': true, 'dir': this.opts.direction });
this.$source.attr('dir', this.opts.direction).hide();
// set code
this.set(this.content);
},
buildOptions: function()
{
var $source = this.$editor;
if (this.opts.iframe) $source = this.$frame;
// options
if (this.opts.tabindex) $source.attr('tabindex', this.opts.tabindex);
if (this.opts.minHeight) $source.css('min-height', this.opts.minHeight + 'px');
if (this.opts.wym) this.$editor.addClass('redactor_editor_wym');
if (!this.opts.autoresize) $source.css('height', this.sourceHeight);
},
buildAfter: function()
{
this.start = false;
// load toolbar
if (this.opts.toolbar)
{
this.opts.toolbar = this.initToolbar(this.opts.curLang);
this.toolbarBuild();
}
// modal templates
this.modalTemplatesInit();
// plugins
this.buildPlugins();
// enter, tab, etc.
this.buildBindKeyboard();
// autosave
if (this.opts.autosave) this.autosave();
// observers
setTimeout($.proxy(this.observeStart, this), 4);
// FF fix
if (this.browser('mozilla'))
{
try {
this.document.execCommand('enableObjectResizing', false, false);
this.document.execCommand('enableInlineTableEditing', false, false);
} catch (e) {}
}
// focus
if (this.opts.focus) setTimeout($.proxy(this.focus, this), 100);
// code mode
if (!this.opts.visual)
{
setTimeout($.proxy(function()
{
this.opts.visual = true;
this.toggle(false);
}, this), 200);
}
// init callback
this.callback('init');
},
buildBindKeyboard: function()
{
var oldsafari = false;
if (this.browser('webkit') && navigator.userAgent.indexOf('Chrome') === -1)
{
var arr = this.browser('version').split('.');
if (arr[0] < 536) oldsafari = true;
}
this.$editor.on('paste.redactor', $.proxy(function(e)
{
if (oldsafari) return true;
// paste except opera
if (this.browser('opera')) return true;
if (this.isMobile()) return false;
if (!this.opts.cleanup) return false;
rtePaste = true;
this.selectionSave();
if (!this.selectall)
{
if (this.opts.autoresize === true )
{
this.$editor.height(this.$editor.height());
this.saveScroll = this.document.body.scrollTop;
}
else
{
this.saveScroll = this.$editor.scrollTop();
}
}
var frag = this.extractContent();
setTimeout($.proxy(function()
{
var pastedFrag = this.extractContent();
this.$editor.append(frag);
this.selectionRestore();
var html = this.getFragmentHtml(pastedFrag);
this.pasteClean(html);
if (this.opts.autoresize === true) this.$editor.css('height', 'auto');
}, this), 1);
}, this));
this.$editor.on('keydown.redactor', $.proxy(function(e)
{
if (rtePaste) return false;
var key = e.which;
var ctrl = e.ctrlKey || e.metaKey;
var parent = this.getParent();
var current = this.getCurrent();
var block = this.getBlock();
var pre = false;
this.callback('keydown', e);
// pre & down
if ((parent && $(parent).get(0).tagName === 'PRE') || (current && $(current).get(0).tagName === 'PRE'))
{
pre = true;
if (key === this.keyCode.DOWN) this.insertAfterLastElement(block);
}
// down
if (key === this.keyCode.DOWN)
{
if (parent && $(parent).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(parent);
if (current && $(current).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(current);
}
// shortcuts setup
if (ctrl && !e.shiftKey) this.shortcuts(e, key);
// buffer setup
if (ctrl && key === 90 && !e.shiftKey && !e.altKey) // z key
{
e.preventDefault();
if (this.opts.buffer.length) this.bufferUndo();
else this.document.execCommand('undo', false, false);
return;
}
// undo
else if (ctrl && key === 90 && e.shiftKey && !e.altKey)
{
e.preventDefault();
if (this.opts.rebuffer.length != 0) this.bufferRedo();
else this.document.execCommand('redo', false, false);
return;
}
// select all
if (ctrl && key === 65)
{
this.selectall = true;
}
else if (key != this.keyCode.LEFT_WIN && !ctrl)
{
this.selectall = false;
}
// enter
if (key == this.keyCode.ENTER && !e.shiftKey && !e.ctrlKey && !e.metaKey )
{
// In ie, opera in the tables are created paragraphs, fix it.
if (parent.nodeType == 1 && (parent.tagName == 'TD' || parent.tagName == 'TH'))
{
this.bufferSet();
this.insertNode(document.createElement('br'));
e.preventDefault();
return false;
}
// pre
if (pre === true)
{
this.bufferSet();
e.preventDefault();
var html = $(current).parent().text();
this.insertNode(document.createTextNode('\n'));
if (html.search(/\s$/) == -1)
{
this.insertNode(document.createTextNode('\n'));
}
this.sync();
return false;
}
else
{
if (!this.opts.linebreaks)
{
// replace div to p
if (block && this.opts.rBlockTest.test(block.tagName))
{
// hit enter
this.bufferSet();
setTimeout($.proxy(function()
{
var blockElem = this.getBlock();
if (blockElem.tagName === 'DIV' && !$(blockElem).hasClass('redactor_editor'))
{
var node = $('').addClass('redactor_toolbar').attr('id', 'redactor_toolbar_' + this.uuid);
if (this.opts.air)
{
// air box
this.$air = $('