mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 12:15:51 +00:00
285e4c3839
patch by jerfa@yahoo.com r=smontagu sr=tor
632 lines
17 KiB
JavaScript
632 lines
17 KiB
JavaScript
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Adrian Havill <havill@redhat.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
//GLOBALS
|
|
|
|
|
|
//locale bundles
|
|
var regionsBundle;
|
|
var languagesBundle;
|
|
var acceptedBundle;
|
|
var prefLangBundle;
|
|
|
|
//dictionary of all supported locales
|
|
var availLanguageDict;
|
|
|
|
//XUL listbox handles
|
|
var available_languages;
|
|
var active_languages;
|
|
|
|
//XUL window pref window interface object
|
|
var pref_string = new String();
|
|
|
|
//Reg expression for splitting multiple pref values
|
|
var separatorRe = /\s*,\s*/;
|
|
|
|
function GetBundles()
|
|
{
|
|
if (!regionsBundle) regionsBundle = srGetStrBundle("chrome://global/locale/regionNames.properties");
|
|
if (!languagesBundle) languagesBundle = srGetStrBundle("chrome://global/locale/languageNames.properties");
|
|
if (!prefLangBundle) prefLangBundle = srGetStrBundle("chrome://communicator/locale/pref/pref-languages.properties");
|
|
if (!acceptedBundle) acceptedBundle = srGetStrBundle("resource://gre/res/language.properties");
|
|
}
|
|
|
|
|
|
function Init()
|
|
{
|
|
try {
|
|
GetBundles();
|
|
}
|
|
|
|
catch(ex) {
|
|
}
|
|
|
|
ReadAvailableLanguages();
|
|
|
|
if (!("arguments" in window)) {
|
|
|
|
//no caller arguments - load base window
|
|
|
|
try {
|
|
active_languages = document.getElementById('active_languages');
|
|
} //try
|
|
|
|
catch(ex) {
|
|
} //catch
|
|
|
|
try {
|
|
parent.initPanel('chrome://communicator/content/pref/pref-languages.xul');
|
|
}
|
|
|
|
catch(ex) {
|
|
//pref service backup
|
|
} //catch
|
|
|
|
pref_string = active_languages.getAttribute("prefvalue");
|
|
LoadActiveLanguages();
|
|
SelectLanguage();
|
|
|
|
} else {
|
|
|
|
//load available languages popup
|
|
try {
|
|
|
|
//add language popup
|
|
available_languages = document.getElementById('available_languages');
|
|
active_languages = window.opener.document.getElementById('active_languages');
|
|
pref_string = window.opener.document.getElementById('intlAcceptLanguages').label;
|
|
|
|
} //try
|
|
|
|
catch(ex) {
|
|
} //catch
|
|
|
|
LoadAvailableLanguages();
|
|
}
|
|
}
|
|
|
|
|
|
function AddLanguage()
|
|
{
|
|
//cludge: make pref string available from the popup
|
|
document.getElementById('intlAcceptLanguages').label = pref_string;
|
|
window.openDialog("chrome://communicator/content/pref/pref-languages-add.xul","_blank","modal,chrome,centerscreen,titlebar", "addlangwindow");
|
|
UpdateSavePrefString();
|
|
SelectLanguage();
|
|
}
|
|
|
|
|
|
function ReadAvailableLanguages()
|
|
{
|
|
availLanguageDict = new Array();
|
|
var visible = new String();
|
|
var str = new String();
|
|
var i =0;
|
|
|
|
var acceptedBundleEnum = acceptedBundle.getSimpleEnumeration();
|
|
|
|
var curItem;
|
|
var stringName;
|
|
var stringNameProperty;
|
|
|
|
while (acceptedBundleEnum.hasMoreElements()) {
|
|
|
|
//progress through the bundle
|
|
curItem = acceptedBundleEnum.getNext();
|
|
|
|
//"unpack" the item, nsIPropertyElement is now partially scriptable
|
|
curItem = curItem.QueryInterface(Components.interfaces.nsIPropertyElement);
|
|
|
|
//dump string name (key)
|
|
stringName = curItem.key;
|
|
stringNameProperty = stringName.split('.');
|
|
|
|
if (stringNameProperty[1] == 'accept') {
|
|
|
|
//dump the UI string (value)
|
|
visible = curItem.value;
|
|
|
|
//if (visible == 'true') {
|
|
|
|
str = stringNameProperty[0];
|
|
var stringLangRegion = stringNameProperty[0].split('-');
|
|
|
|
if (stringLangRegion[0]) {
|
|
var tit;
|
|
var language;
|
|
var region;
|
|
var use_region_format = false;
|
|
|
|
try {
|
|
language = languagesBundle.GetStringFromName(stringLangRegion[0]);
|
|
}
|
|
catch (ex) {
|
|
language = "";
|
|
}
|
|
|
|
if (stringLangRegion.length > 1) {
|
|
|
|
try {
|
|
region = regionsBundle.GetStringFromName(stringLangRegion[1]);
|
|
use_region_format = true;
|
|
}
|
|
catch (ex) {
|
|
}
|
|
}
|
|
|
|
if (use_region_format) {
|
|
tit = prefLangBundle.formatStringFromName("languageRegionCodeFormat",
|
|
[language, region, str], 3);
|
|
} else {
|
|
tit = prefLangBundle.formatStringFromName("languageCodeFormat",
|
|
[language, str], 2);
|
|
}
|
|
|
|
} //if language
|
|
|
|
if (str && tit) {
|
|
|
|
availLanguageDict[i] = new Array(3);
|
|
availLanguageDict[i][0] = tit;
|
|
availLanguageDict[i][1] = str;
|
|
availLanguageDict[i][2] = visible;
|
|
i++;
|
|
|
|
} // if str&tit
|
|
//} //if visible
|
|
} //if accepted
|
|
} //while
|
|
availLanguageDict.sort( // sort on first element
|
|
function compareFn(a, b) {
|
|
return a[0].localeCompare(b[0]);
|
|
});
|
|
}
|
|
|
|
|
|
function LoadAvailableLanguages()
|
|
{
|
|
if (availLanguageDict)
|
|
for (var i = 0; i < availLanguageDict.length; i++) {
|
|
|
|
if (availLanguageDict[i][2] == 'true') {
|
|
|
|
AddListItem(document, available_languages, availLanguageDict[i][1], availLanguageDict[i][0]);
|
|
|
|
} //if
|
|
|
|
} //for
|
|
}
|
|
|
|
|
|
function LoadActiveLanguages()
|
|
{
|
|
if (pref_string) {
|
|
var arrayOfPrefs = pref_string.split(separatorRe);
|
|
|
|
for (var i = 0; i < arrayOfPrefs.length; i++) {
|
|
var str = arrayOfPrefs[i];
|
|
var tit = GetLanguageTitle(str);
|
|
|
|
if (str) {
|
|
if (!tit)
|
|
tit = '[' + str + ']';
|
|
AddListItem(document, active_languages, str, tit);
|
|
|
|
} //if
|
|
} //for
|
|
}
|
|
}
|
|
|
|
|
|
function LangAlreadyActive(langId)
|
|
{
|
|
var found = false;
|
|
try {
|
|
var arrayOfPrefs = pref_string.split(separatorRe);
|
|
|
|
if (arrayOfPrefs)
|
|
for (var i = 0; i < arrayOfPrefs.length; i++) {
|
|
if (arrayOfPrefs[i] == langId) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
catch(ex){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function isAlpha(mixedCase) {
|
|
var allCaps = mixedCase.toUpperCase();
|
|
for (var i = allCaps.length - 1; i >= 0; i--) {
|
|
var c = allCaps.charAt(i);
|
|
if (c < 'A' || c > 'Z') return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function isAlphaNum(mixedCase) {
|
|
var allCaps = mixedCase.toUpperCase();
|
|
for (var i = allCaps.length - 1; i >= 0; i--) {
|
|
var c = allCaps.charAt(i);
|
|
if ((c < 'A' || c > 'Z') && (c < '0' || c > '9')) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function IsRFC1766LangTag(candidate) {
|
|
|
|
/* reject bogus lang strings, INCLUDING those with HTTP "q"
|
|
values kludged on the end of them
|
|
|
|
Valid language codes examples:
|
|
i.e. ja-JP-kansai (Kansai dialect of Japanese)
|
|
en-US-texas (Texas dialect)
|
|
i-klingon-tng (did TOS Klingons speak in non-English?)
|
|
sgn-US-MA (Martha Vineyard's Sign Language)
|
|
*/
|
|
var tags = candidate.split('-');
|
|
|
|
/* if not IANA "i" or a private "x" extension, the primary
|
|
tag should be a ISO 639 country code, two or three letters long.
|
|
we don't check if the country code is bogus or not.
|
|
*/
|
|
var checkedTags = 0;
|
|
if (tags[0].toLowerCase() != "x" && tags[0].toLowerCase() != "i") {
|
|
if (tags[0].length != 2 && tags[0].length != 3) return false;
|
|
if (!isAlpha(tags[0])) return false;
|
|
checkedTags++;
|
|
|
|
/* the first subtag can be either a 2 letter ISO 3166 country code,
|
|
or an IANA registered tag from 3 to 8 characters.
|
|
*/
|
|
if (tags.length > 1) {
|
|
if (tags[1].length < 2 || tags[1].length > 8) return false;
|
|
if (!isAlphaNum(tags[1])) return false;
|
|
|
|
/* do not allow user-assigned ISO 3166 country codes */
|
|
if (tags[1].length == 2 && isAlpha(tags[1])) {
|
|
var countryCode = tags[1].toUpperCase();
|
|
if (countryCode == "AA" || countryCode == "ZZ") return false;
|
|
if (countryCode[0] == 'X') return false;
|
|
if (countryCode[0] == 'Q' && countryCode[1] > 'L') return false;
|
|
}
|
|
checkedTags++;
|
|
}
|
|
}
|
|
else if (tags.length < 2) return false;
|
|
else {
|
|
if ((tags[1].length < 1) || (tags[1].length > 8)) return false;
|
|
if (!isAlphaNum(tags[1])) return false;
|
|
checkedTags++;
|
|
}
|
|
|
|
/* any remaining subtags must be one to eight alphabetic characters */
|
|
|
|
for (var i = checkedTags; i < tags.length; i++) {
|
|
if ((tags[1].length < 1) || (tags[i].length > 8)) return false;
|
|
if (!isAlphaNum(tags[i])) return false;
|
|
checkedTags++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function AddAvailableLanguage()
|
|
{
|
|
var Languagename = new String();
|
|
var Languageid = new String();
|
|
|
|
var addThese = new Array();
|
|
var addTheseNames = new Array();
|
|
var invalidLangs = new Array();
|
|
|
|
//selected languages
|
|
for (var nodeIndex=0; nodeIndex < available_languages.selectedItems.length; nodeIndex++) {
|
|
|
|
var selItem = available_languages.selectedItems[nodeIndex];
|
|
|
|
Languagename = selItem.getAttribute('label');
|
|
Languageid = selItem.getAttribute('id');
|
|
|
|
if (!LangAlreadyActive(Languageid)) {
|
|
addThese[nodeIndex] = Languageid;
|
|
addTheseNames[nodeIndex] = Languagename;
|
|
}
|
|
}
|
|
|
|
//user-defined languages
|
|
var otherField = document.getElementById( "languages.other" );
|
|
|
|
var selCount = addThese.length;
|
|
|
|
if (otherField.value) {
|
|
|
|
var LanguageidsString = otherField.value.replace(/\s/g,"").toLowerCase();
|
|
var Languageids = LanguageidsString.split(separatorRe);
|
|
for (var i = 0; i < Languageids.length; i++) {
|
|
Languageid = Languageids[i];
|
|
|
|
if (IsRFC1766LangTag(Languageid)) {
|
|
if (!LangAlreadySelected(Languageid)) {
|
|
if (!LangAlreadyActive(Languageid)) {
|
|
Languagename = GetLanguageTitle(Languageid);
|
|
if (!Languagename)
|
|
Languagename = '[' + Languageid + ']';
|
|
addThese[i+selCount] = Languageid;
|
|
addTheseNames[i+selCount] = Languagename;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
invalidLangs[invalidLangs.length] = Languageid;
|
|
}
|
|
}
|
|
if (invalidLangs.length > 0) {
|
|
var errorMsg = prefLangBundle.GetStringFromName("illegalOtherLanguage") + " " +
|
|
invalidLangs.join(", ");
|
|
var errorTitle = prefLangBundle.GetStringFromName("illegalOtherLanguageTitle");
|
|
|
|
var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
|
|
prompter.alert(this.window, errorTitle, errorMsg);
|
|
otherField.focus();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < addThese.length; i++) {
|
|
AddListItem(window.opener.document, active_languages, addThese[i], addTheseNames[i]);
|
|
}
|
|
|
|
available_languages.clearSelection();
|
|
return true;
|
|
}
|
|
|
|
function LangAlreadySelected(langid) {
|
|
var found = false;
|
|
|
|
for (var i = 0; i < available_languages.selectedItems.length; i++) {
|
|
var currentLang = available_languages.selectedItems[i];
|
|
var Languageid = currentLang.getAttribute('id');
|
|
if (Languageid == langid)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function HandleDoubleClick(node)
|
|
{
|
|
var languageId = node.id;
|
|
var languageName = node.getAttribute('label');
|
|
|
|
if (languageName && languageName.length > 0)
|
|
{
|
|
if (!LangAlreadyActive(languageId)) {
|
|
AddListItem(window.opener.document, active_languages, languageId, languageName);
|
|
}
|
|
window.close();
|
|
}//if
|
|
} //HandleDoubleClick
|
|
|
|
function RemoveActiveLanguage()
|
|
{
|
|
|
|
var nextNode = null;
|
|
var numSelected = active_languages.selectedItems.length;
|
|
var deleted_all = false;
|
|
|
|
while (active_languages.selectedItems.length > 0) {
|
|
|
|
var selectedNode = active_languages.selectedItems[0];
|
|
nextNode = selectedNode.nextSibling;
|
|
|
|
if (!nextNode)
|
|
|
|
if (selectedNode.previousSibling)
|
|
nextNode = selectedNode.previousSibling;
|
|
|
|
active_languages.removeChild(selectedNode);
|
|
} //while
|
|
|
|
if (nextNode) {
|
|
active_languages.selectItem(nextNode)
|
|
} else {
|
|
//active_languages.clearSelection();
|
|
}
|
|
|
|
UpdateSavePrefString();
|
|
|
|
} //RemoveActiveLanguage
|
|
|
|
|
|
function GetLanguageTitle(id)
|
|
{
|
|
|
|
if (availLanguageDict)
|
|
for (var j = 0; j < availLanguageDict.length; j++) {
|
|
|
|
if ( availLanguageDict[j][1] == id) {
|
|
//title =
|
|
return availLanguageDict[j][0];
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
|
|
function AddListItem(doc, listbox, langID, langTitle)
|
|
{
|
|
try { //let's beef up our error handling for languages without label / title
|
|
|
|
// Create a listitem for the new Language
|
|
var item = doc.createElement('listitem');
|
|
|
|
// Copy over the attributes
|
|
item.setAttribute('label', langTitle);
|
|
item.setAttribute('id', langID);
|
|
|
|
listbox.appendChild(item);
|
|
|
|
} //try
|
|
|
|
catch (ex) {
|
|
} //catch
|
|
|
|
}
|
|
|
|
|
|
function UpdateSavePrefString()
|
|
{
|
|
var num_languages = 0;
|
|
pref_string = "";
|
|
|
|
for (var item = active_languages.firstChild; item != null; item = item.nextSibling) {
|
|
|
|
var languageid = item.getAttribute('id');
|
|
|
|
if (languageid.length > 1) {
|
|
|
|
num_languages++;
|
|
|
|
//separate >1 languages by commas
|
|
if (num_languages > 1) {
|
|
pref_string = pref_string + "," + " " + languageid;
|
|
} else {
|
|
pref_string = languageid;
|
|
} //if
|
|
} //if
|
|
}//for
|
|
|
|
active_languages.setAttribute("prefvalue", pref_string);
|
|
}
|
|
|
|
|
|
function MoveUp() {
|
|
|
|
if (active_languages.selectedItems.length == 1) {
|
|
var selected = active_languages.selectedItems[0];
|
|
var before = selected.previousSibling
|
|
if (before) {
|
|
before.parentNode.insertBefore(selected, before);
|
|
active_languages.selectItem(selected);
|
|
active_languages.ensureElementIsVisible(selected);
|
|
}
|
|
}
|
|
|
|
if (active_languages.selectedIndex == 0)
|
|
{
|
|
// selected item is first
|
|
var moveUp = document.getElementById("up");
|
|
moveUp.disabled = true;
|
|
}
|
|
|
|
if (active_languages.childNodes.length > 1)
|
|
{
|
|
// more than one item so we can move selected item back down
|
|
var moveDown = document.getElementById("down");
|
|
moveDown.disabled = false;
|
|
}
|
|
|
|
UpdateSavePrefString();
|
|
|
|
} //MoveUp
|
|
|
|
|
|
function MoveDown() {
|
|
|
|
if (active_languages.selectedItems.length == 1) {
|
|
var selected = active_languages.selectedItems[0];
|
|
if (selected.nextSibling) {
|
|
if (selected.nextSibling.nextSibling) {
|
|
active_languages.insertBefore(selected, selected.nextSibling.nextSibling);
|
|
}
|
|
else {
|
|
active_languages.appendChild(selected);
|
|
}
|
|
active_languages.selectItem(selected);
|
|
}
|
|
}
|
|
|
|
if (active_languages.selectedIndex == active_languages.childNodes.length - 1)
|
|
{
|
|
// selected item is last
|
|
var moveDown = document.getElementById("down");
|
|
moveDown.disabled = true;
|
|
}
|
|
|
|
if (active_languages.childNodes.length > 1)
|
|
{
|
|
// more than one item so we can move selected item back up
|
|
var moveUp = document.getElementById("up");
|
|
moveUp.disabled = false;
|
|
}
|
|
|
|
UpdateSavePrefString();
|
|
|
|
} //MoveDown
|
|
|
|
|
|
function SelectLanguage() {
|
|
if (active_languages.selectedItems.length) {
|
|
document.getElementById("remove").disabled = false;
|
|
var selected = active_languages.selectedItems[0];
|
|
document.getElementById("down").disabled = !selected.nextSibling;
|
|
|
|
document.getElementById("up").disabled = !selected.previousSibling;
|
|
}
|
|
else {
|
|
document.getElementById("remove").disabled = true;
|
|
document.getElementById("down").disabled = true;
|
|
document.getElementById("up").disabled = true;
|
|
}
|
|
|
|
if (parent.hPrefWindow.getPrefIsLocked(document.getElementById("up").getAttribute("prefstring")))
|
|
document.getElementById("up").disabled = true;
|
|
if (parent.hPrefWindow.getPrefIsLocked(document.getElementById("down").getAttribute("prefstring")))
|
|
document.getElementById("down").disabled = true;
|
|
if (parent.hPrefWindow.getPrefIsLocked(document.getElementById("add").getAttribute("prefstring")))
|
|
document.getElementById("add").disabled = true;
|
|
if (parent.hPrefWindow.getPrefIsLocked(document.getElementById("remove").getAttribute("prefstring")))
|
|
document.getElementById("remove").disabled = true;
|
|
} // SelectLanguage
|