From 53b144104604d8b00c474dcd1a3ce6d0d922f61c Mon Sep 17 00:00:00 2001 From: Andrew Rieder Date: Tue, 31 Jul 2018 09:27:02 -0400 Subject: [PATCH 1/3] Added support for MongoDB (second commit: fixed missing files). --- LICENSE | 0 Procfile | 0 README.md | 32 ++ hello.py | 97 +++- manifest.yml | 0 requirements.txt | 1 + setup.py | 0 static/antixss.js | 0 static/index.html | 0 .../jquery.i18n/jquery.i18n.emitter.bidi.js | 94 ---- .../js/lib/jquery.i18n/jquery.i18n.emitter.js | 168 ------ .../lib/jquery.i18n/jquery.i18n.fallbacks.js | 186 ------- static/js/lib/jquery.i18n/jquery.i18n.js | 301 ----------- .../lib/jquery.i18n/jquery.i18n.language.js | 498 ------------------ .../jquery.i18n/jquery.i18n.messagestore.js | 126 ----- .../js/lib/jquery.i18n/jquery.i18n.parser.js | 309 ----------- static/styles.css | 0 vcap-local.template.json | 10 +- 18 files changed, 128 insertions(+), 1694 deletions(-) mode change 100644 => 100755 LICENSE mode change 100644 => 100755 Procfile mode change 100644 => 100755 README.md mode change 100644 => 100755 hello.py mode change 100644 => 100755 manifest.yml mode change 100644 => 100755 requirements.txt mode change 100644 => 100755 setup.py mode change 100644 => 100755 static/antixss.js mode change 100644 => 100755 static/index.html delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.emitter.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.language.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.messagestore.js delete mode 100644 static/js/lib/jquery.i18n/jquery.i18n.parser.js mode change 100644 => 100755 static/styles.css mode change 100644 => 100755 vcap-local.template.json diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/Procfile b/Procfile old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index dfe3e50..4098ded --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ To get started, we'll take you through a sample Python Flask app, help you set up a development environment, deploy to IBM Cloud and add a Cloudant database. +

+ Gif of the sample app contains a title that says, Welcome, a prompt asking the user to enter their name, and a list of the database contents which are the names Joe, Jane, and Bob. The user enters the name, Mary and the screen refreshes to display, Hello, Mary, I've added you to the database. The database contents listed are now Mary, Joe, Jane, and Bob. +

+ ## Prerequisites You'll need the following: @@ -135,3 +139,31 @@ cf push ``` View your app at the URL listed in the output of the push command, for example, *myUrl.mybluemix.net*. + +## Connecting a MongoDB Database + +You'll need access to a MongoDB instance. The below steps outline how to do this: + +1. Sign in to your IBM Cloud Console, and select the 'Create Resource' button in the top right of the page. +2. Type 'MongoDB' in the search field and select 'Compose for MongoDB' under the 'Data & Analytics' section. +3. Check that the name/region/organization/pricing fields are accurate. +4. Select the 'Create' button in the botton left of the page. +5. Return to the IBM Cloud dashboard and select the newly created service. +6. In the sidebar on the left side of the page, select 'Service Credentials' +7. Select the 'New Credential' button, and then the 'Add' button in the page that appears. + +You will also need to run `pip install -r requirements.txt` again to install the required [MongoDB driver](https://github.com/mongodb/mongo-python-driver). + +To prepare the application for use in a local environment, use the below steps. Otherwise, skip the steps to move on to deploying the application to IBM Cloud: + +1. Make a copy of the 'vcap-local.template.json' file (in the application root directory) as 'vcap-local.json' in the same directory. +2. Copy the contents of the 'uri' field in the credentials generated from earlier into the field (replacing "MONGO_DATABASE_URI"). +3. Run the application using `python hello.py`. + +Before deploying the application, make sure that it builds successfully by running `python hello.py` from the root directory of the project (where this README document is located). If no errors are shown, run `cf push` to deploy the application. Once the deployment process completes, run `cf bind-service GetStartedPython [SERVICE_NAME]`, where [SERVICE_NAME] is the name of your MongoDB service. Finally, run `cf retage GetStartedPytohon`. Once this finishes, you can access the application using the URL provided in the output from the 'cf push' command from earlier. + +## Additional Notes on Changes + +The application may work with either Cloudant or MongoDB. However, if both services are available (and at most one is user-provided), **the application will default to MongoDB**. If both services are available as user-provided services, this behavior is **not** guaranteed, and the application may default to either service. + +When deployed on IBM Cloud, this application **does** require bound MongoDB services to have some permutation of 'mongodb' in the name. User-provided services (as created with the cf utility) are also acceptable. \ No newline at end of file diff --git a/hello.py b/hello.py old mode 100644 new mode 100755 index 737e0f9..0644284 --- a/hello.py +++ b/hello.py @@ -1,35 +1,94 @@ +############################################################################### +# Copyright IBM Corporation 2018 # +# # +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################### from cloudant import Cloudant from flask import Flask, render_template, request, jsonify import atexit import os import json +import ssl +import re +from pymongo import MongoClient app = Flask(__name__, static_url_path='') db_name = 'mydb' +collection_name = 'mycollection' # MongoDB requires a collection name. client = None db = None +vendor_name = None # Record DB vendor to determine which methods are used. + +# Allow for any service with some permutation of a vendor name to be used +# (this is mostly to provide a general solution for user-provided services). +mongo_re = re.compile(r'.*[Mm][Oo][Nn][Gg][Oo].*') +cloudant_re = re.compile(r'.*[Cc][Ll][Oo][Uu][Dd][Aa][Nn][Tt].*') if 'VCAP_SERVICES' in os.environ: vcap = json.loads(os.getenv('VCAP_SERVICES')) print('Found VCAP_SERVICES') - if 'cloudantNoSQLDB' in vcap: - creds = vcap['cloudantNoSQLDB'][0]['credentials'] + + mongo_result = next(iter(filter(lambda key: mongo_re.search(key) != None, vcap)), None) + cloudant_result = next(iter(filter(lambda key: cloudant_re.search(key) != None, vcap)), None) + user_provided_result = next(iter(filter(lambda key: key == 'user-provided', vcap)), None) + + # Allow for user-provided services to be used. + if user_provided_result: + if mongo_re.search(vcap[user_provided_result][0]['name']): + mongo_result = user_provided_result + elif cloudant_re.search(vcap[user_provided_result][0]['name']): + cloudant_result = user_provided_result + + if mongo_result: + creds = vcap[mongo_result][0]['credentials'] + uri = creds['uri'] + client = MongoClient(uri, ssl_cert_reqs=ssl.CERT_NONE) + db = client[db_name][collection_name] + vendor_name = 'mongo' + elif cloudant_result: + creds = vcap[cloudant_result][0]['credentials'] user = creds['username'] password = creds['password'] url = 'https://' + creds['host'] client = Cloudant(user, password, url=url, connect=True) db = client.create_database(db_name, throw_on_exists=False) + vendor_name = 'cloudant' + elif os.path.isfile('vcap-local.json'): with open('vcap-local.json') as f: vcap = json.load(f) print('Found local VCAP_SERVICES') - creds = vcap['services']['cloudantNoSQLDB'][0]['credentials'] - user = creds['username'] - password = creds['password'] - url = 'https://' + creds['host'] - client = Cloudant(user, password, url=url, connect=True) - db = client.create_database(db_name, throw_on_exists=False) + + mongo_result = next(iter(filter(lambda key: mongo_re.search(key) != None, vcap['services'])), None) + cloudant_result = next(iter(filter(lambda key: cloudant_re.search(key) != None, vcap['services'])), None) + + if mongo_result: + #creds = vcap['services']['compose-for-mongodb'][0]['credentials'] + creds = vcap['services'][mongo_result][0]['credentials'] + uri = creds['uri'] + client = MongoClient(uri, ssl_cert_reqs=ssl.CERT_NONE) + db = client[db_name][collection_name] + vendor_name = 'mongo' + elif cloudant_result: + #creds = vcap['services']['cloudantNoSQLDB'][0]['credentials'] + creds = vcap['services'][cloudant_result][0]['credentials'] + user = creds['username'] + password = creds['password'] + url = 'https://' + creds['host'] + client = Cloudant(user, password, url=url, connect=True) + db = client.create_database(db_name, throw_on_exists=False) + vendor_name = 'cloudant' # On IBM Cloud Cloud Foundry, get the port number from the environment variable PORT # When running this app on the local machine, default the port to 8000 @@ -39,6 +98,12 @@ def root(): return app.send_static_file('index.html') +def get_visitor_cloudant(): + return list(map(lambda doc: doc['name'], db)) + +def get_visitor_mongo(): + return list(map(lambda doc: doc['name'], db.find(projection={"_id": False, "count": False}))) + # /* Endpoint to greet and add a new visitor to database. # * Send a POST request to localhost:8000/api/visitors with body # * { @@ -48,11 +113,21 @@ def root(): @app.route('/api/visitors', methods=['GET']) def get_visitor(): if client: - return jsonify(list(map(lambda doc: doc['name'], db))) + # Call vendor-specific handler. + return jsonify(globals()['get_visitor_' + vendor_name]()) else: print('No database') return jsonify([]) +def add_visitor_cloudant(data): + my_document = db.create_document(data) + data['_id'] = my_document['_id'] + return data + +def add_visitor_mongo(data): + data['_id'] = str(db.insert_one(data).inserted_id) + return data + # /** # * Endpoint to get a JSON array of all the visitors in the database # * REST API example: @@ -69,8 +144,8 @@ def put_visitor(): user = request.json['name'] data = {'name':user} if client: - my_document = db.create_document(data) - data['_id'] = my_document['_id'] + # Call vendor-specific handler. + data = globals()['add_visitor_' + vendor_name](data) return jsonify(data) else: print('No database') diff --git a/manifest.yml b/manifest.yml old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 735f649..ca3c284 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ Flask==0.12.2 cloudant==2.4.0 +pymongo[tls]==3.7.0 diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 diff --git a/static/antixss.js b/static/antixss.js old mode 100644 new mode 100755 diff --git a/static/index.html b/static/index.html old mode 100644 new mode 100755 diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js deleted file mode 100644 index e245557..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js +++ /dev/null @@ -1,94 +0,0 @@ -/*! - * BIDI embedding support for jQuery.i18n - * - * Copyright (C) 2015, David Chan - * - * This code 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 this code - * 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 strongDirRegExp; - - /** - * Matches the first strong directionality codepoint: - * - in group 1 if it is LTR - * - in group 2 if it is RTL - * Does not match if there is no strong directionality codepoint. - * - * Generated by UnicodeJS (see tools/strongDir) from the UCD; see - * https://phabricator.wikimedia.org/diffusion/GUJS/ . - */ - strongDirRegExp = new RegExp( - '(?:' + - '(' + - '[\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02b8\u02bb-\u02c1\u02d0\u02d1\u02e0-\u02e4\u02ee\u0370-\u0373\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0482\u048a-\u052f\u0531-\u0556\u0559-\u055f\u0561-\u0587\u0589\u0903-\u0939\u093b\u093d-\u0940\u0949-\u094c\u094e-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd-\u09c0\u09c7\u09c8\u09cb\u09cc\u09ce\u09d7\u09dc\u09dd\u09df-\u09e1\u09e6-\u09f1\u09f4-\u09fa\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3e-\u0a40\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd-\u0ac0\u0ac9\u0acb\u0acc\u0ad0\u0ae0\u0ae1\u0ae6-\u0af0\u0af9\u0b02\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0b5c\u0b5d\u0b5f-\u0b61\u0b66-\u0b77\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd0\u0bd7\u0be6-\u0bf2\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c41-\u0c44\u0c58-\u0c5a\u0c60\u0c61\u0c66-\u0c6f\u0c7f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd-\u0cc4\u0cc6-\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0cde\u0ce0\u0ce1\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d4e\u0d57\u0d5f-\u0d61\u0d66-\u0d75\u0d79-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dcf-\u0dd1\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df4\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e4f-\u0e5b\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edf\u0f00-\u0f17\u0f1a-\u0f34\u0f36\u0f38\u0f3e-\u0f47\u0f49-\u0f6c\u0f7f\u0f85\u0f88-\u0f8c\u0fbe-\u0fc5\u0fc7-\u0fcc\u0fce-\u0fda\u1000-\u102c\u1031\u1038\u103b\u103c\u103f-\u1057\u105a-\u105d\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108c\u108e-\u109c\u109e-\u10c5\u10c7\u10cd\u10d0-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1360-\u137c\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u167f\u1681-\u169a\u16a0-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17b6\u17be-\u17c5\u17c7\u17c8\u17d4-\u17da\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1923-\u1926\u1929-\u192b\u1930\u1931\u1933-\u1938\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a16\u1a19\u1a1a\u1a1e-\u1a55\u1a57\u1a61\u1a63\u1a64\u1a6d-\u1a72\u1a80-\u1a89\u1a90-\u1a99\u1aa0-\u1aad\u1b04-\u1b33\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b4b\u1b50-\u1b6a\u1b74-\u1b7c\u1b82-\u1ba1\u1ba6\u1ba7\u1baa\u1bae-\u1be5\u1be7\u1bea-\u1bec\u1bee\u1bf2\u1bf3\u1bfc-\u1c2b\u1c34\u1c35\u1c3b-\u1c49\u1c4d-\u1c7f\u1cc0-\u1cc7\u1cd3\u1ce1\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200e\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u214f\u2160-\u2188\u2336-\u237a\u2395\u249c-\u24e9\u26ac\u2800-\u28ff\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d70\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u302e\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u3190-\u31ba\u31f0-\u321c\u3220-\u324f\u3260-\u327b\u327f-\u32b0\u32c0-\u32cb\u32d0-\u32fe\u3300-\u3376\u337b-\u33dd\u33e0-\u33fe\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua60c\ua610-\ua62b\ua640-\ua66e\ua680-\ua69d\ua6a0-\ua6ef\ua6f2-\ua6f7\ua722-\ua787\ua789-\ua7ad\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua824\ua827\ua830-\ua837\ua840-\ua873\ua880-\ua8c3\ua8ce-\ua8d9\ua8f2-\ua8fd\ua900-\ua925\ua92e-\ua946\ua952\ua953\ua95f-\ua97c\ua983-\ua9b2\ua9b4\ua9b5\ua9ba\ua9bb\ua9bd-\ua9cd\ua9cf-\ua9d9\ua9de-\ua9e4\ua9e6-\ua9fe\uaa00-\uaa28\uaa2f\uaa30\uaa33\uaa34\uaa40-\uaa42\uaa44-\uaa4b\uaa4d\uaa50-\uaa59\uaa5c-\uaa7b\uaa7d-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaaeb\uaaee-\uaaf5\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab65\uab70-\uabe4\uabe6\uabe7\uabe9-\uabec\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ue000-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b]|\ud800[\udc0d-\udc26]|\ud800[\udc28-\udc3a]|\ud800\udc3c|\ud800\udc3d|\ud800[\udc3f-\udc4d]|\ud800[\udc50-\udc5d]|\ud800[\udc80-\udcfa]|\ud800\udd00|\ud800\udd02|\ud800[\udd07-\udd33]|\ud800[\udd37-\udd3f]|\ud800[\uddd0-\uddfc]|\ud800[\ude80-\ude9c]|\ud800[\udea0-\uded0]|\ud800[\udf00-\udf23]|\ud800[\udf30-\udf4a]|\ud800[\udf50-\udf75]|\ud800[\udf80-\udf9d]|\ud800[\udf9f-\udfc3]|\ud800[\udfc8-\udfd5]|\ud801[\udc00-\udc9d]|\ud801[\udca0-\udca9]|\ud801[\udd00-\udd27]|\ud801[\udd30-\udd63]|\ud801\udd6f|\ud801[\ude00-\udf36]|\ud801[\udf40-\udf55]|\ud801[\udf60-\udf67]|\ud804\udc00|\ud804[\udc02-\udc37]|\ud804[\udc47-\udc4d]|\ud804[\udc66-\udc6f]|\ud804[\udc82-\udcb2]|\ud804\udcb7|\ud804\udcb8|\ud804[\udcbb-\udcc1]|\ud804[\udcd0-\udce8]|\ud804[\udcf0-\udcf9]|\ud804[\udd03-\udd26]|\ud804\udd2c|\ud804[\udd36-\udd43]|\ud804[\udd50-\udd72]|\ud804[\udd74-\udd76]|\ud804[\udd82-\uddb5]|\ud804[\uddbf-\uddc9]|\ud804\uddcd|\ud804[\uddd0-\udddf]|\ud804[\udde1-\uddf4]|\ud804[\ude00-\ude11]|\ud804[\ude13-\ude2e]|\ud804\ude32|\ud804\ude33|\ud804\ude35|\ud804[\ude38-\ude3d]|\ud804[\ude80-\ude86]|\ud804\ude88|\ud804[\ude8a-\ude8d]|\ud804[\ude8f-\ude9d]|\ud804[\ude9f-\udea9]|\ud804[\udeb0-\udede]|\ud804[\udee0-\udee2]|\ud804[\udef0-\udef9]|\ud804\udf02|\ud804\udf03|\ud804[\udf05-\udf0c]|\ud804\udf0f|\ud804\udf10|\ud804[\udf13-\udf28]|\ud804[\udf2a-\udf30]|\ud804\udf32|\ud804\udf33|\ud804[\udf35-\udf39]|\ud804[\udf3d-\udf3f]|\ud804[\udf41-\udf44]|\ud804\udf47|\ud804\udf48|\ud804[\udf4b-\udf4d]|\ud804\udf50|\ud804\udf57|\ud804[\udf5d-\udf63]|\ud805[\udc80-\udcb2]|\ud805\udcb9|\ud805[\udcbb-\udcbe]|\ud805\udcc1|\ud805[\udcc4-\udcc7]|\ud805[\udcd0-\udcd9]|\ud805[\udd80-\uddb1]|\ud805[\uddb8-\uddbb]|\ud805\uddbe|\ud805[\uddc1-\udddb]|\ud805[\ude00-\ude32]|\ud805\ude3b|\ud805\ude3c|\ud805\ude3e|\ud805[\ude41-\ude44]|\ud805[\ude50-\ude59]|\ud805[\ude80-\udeaa]|\ud805\udeac|\ud805\udeae|\ud805\udeaf|\ud805\udeb6|\ud805[\udec0-\udec9]|\ud805[\udf00-\udf19]|\ud805\udf20|\ud805\udf21|\ud805\udf26|\ud805[\udf30-\udf3f]|\ud806[\udca0-\udcf2]|\ud806\udcff|\ud806[\udec0-\udef8]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e]|\ud809[\udc70-\udc74]|\ud809[\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38]|\ud81a[\ude40-\ude5e]|\ud81a[\ude60-\ude69]|\ud81a\ude6e|\ud81a\ude6f|\ud81a[\uded0-\udeed]|\ud81a\udef5|\ud81a[\udf00-\udf2f]|\ud81a[\udf37-\udf45]|\ud81a[\udf50-\udf59]|\ud81a[\udf5b-\udf61]|\ud81a[\udf63-\udf77]|\ud81a[\udf7d-\udf8f]|\ud81b[\udf00-\udf44]|\ud81b[\udf50-\udf7e]|\ud81b[\udf93-\udf9f]|\ud82c\udc00|\ud82c\udc01|\ud82f[\udc00-\udc6a]|\ud82f[\udc70-\udc7c]|\ud82f[\udc80-\udc88]|\ud82f[\udc90-\udc99]|\ud82f\udc9c|\ud82f\udc9f|\ud834[\udc00-\udcf5]|\ud834[\udd00-\udd26]|\ud834[\udd29-\udd66]|\ud834[\udd6a-\udd72]|\ud834\udd83|\ud834\udd84|\ud834[\udd8c-\udda9]|\ud834[\uddae-\udde8]|\ud834[\udf60-\udf71]|\ud835[\udc00-\udc54]|\ud835[\udc56-\udc9c]|\ud835\udc9e|\ud835\udc9f|\ud835\udca2|\ud835\udca5|\ud835\udca6|\ud835[\udca9-\udcac]|\ud835[\udcae-\udcb9]|\ud835\udcbb|\ud835[\udcbd-\udcc3]|\ud835[\udcc5-\udd05]|\ud835[\udd07-\udd0a]|\ud835[\udd0d-\udd14]|\ud835[\udd16-\udd1c]|\ud835[\udd1e-\udd39]|\ud835[\udd3b-\udd3e]|\ud835[\udd40-\udd44]|\ud835\udd46|\ud835[\udd4a-\udd50]|\ud835[\udd52-\udea5]|\ud835[\udea8-\udeda]|\ud835[\udedc-\udf14]|\ud835[\udf16-\udf4e]|\ud835[\udf50-\udf88]|\ud835[\udf8a-\udfc2]|\ud835[\udfc4-\udfcb]|\ud836[\udc00-\uddff]|\ud836[\ude37-\ude3a]|\ud836[\ude6d-\ude74]|\ud836[\ude76-\ude83]|\ud836[\ude85-\ude8b]|\ud83c[\udd10-\udd2e]|\ud83c[\udd30-\udd69]|\ud83c[\udd70-\udd9a]|\ud83c[\udde6-\ude02]|\ud83c[\ude10-\ude3a]|\ud83c[\ude40-\ude48]|\ud83c\ude50|\ud83c\ude51|[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6]|\ud869[\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34]|\ud86d[\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud86e[\udc20-\udfff]|[\ud86f-\ud872][\udc00-\udfff]|\ud873[\udc00-\udea1]|\ud87e[\udc00-\ude1d]|[\udb80-\udbbe][\udc00-\udfff]|\udbbf[\udc00-\udffd]|[\udbc0-\udbfe][\udc00-\udfff]|\udbff[\udc00-\udffd]' + - ')|(' + - '[\u0590\u05be\u05c0\u05c3\u05c6\u05c8-\u05ff\u07c0-\u07ea\u07f4\u07f5\u07fa-\u0815\u081a\u0824\u0828\u082e-\u0858\u085c-\u089f\u200f\ufb1d\ufb1f-\ufb28\ufb2a-\ufb4f\u0608\u060b\u060d\u061b-\u064a\u066d-\u066f\u0671-\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u0710\u0712-\u072f\u074b-\u07a5\u07b1-\u07bf\u08a0-\u08e2\ufb50-\ufd3d\ufd40-\ufdcf\ufdf0-\ufdfc\ufdfe\ufdff\ufe70-\ufefe]|\ud802[\udc00-\udd1e]|\ud802[\udd20-\ude00]|\ud802\ude04|\ud802[\ude07-\ude0b]|\ud802[\ude10-\ude37]|\ud802[\ude3b-\ude3e]|\ud802[\ude40-\udee4]|\ud802[\udee7-\udf38]|\ud802[\udf40-\udfff]|\ud803[\udc00-\ude5f]|\ud803[\ude7f-\udfff]|\ud83a[\udc00-\udccf]|\ud83a[\udcd7-\udfff]|\ud83b[\udc00-\uddff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\ude00-\udeef]|\ud83b[\udef2-\udeff]' + - ')' + - ')' - ); - - /** - * Gets directionality of the first strongly directional codepoint - * - * This is the rule the BIDI algorithm uses to determine the directionality of - * paragraphs ( http://unicode.org/reports/tr9/#The_Paragraph_Level ) and - * FSI isolates ( http://unicode.org/reports/tr9/#Explicit_Directional_Isolates ). - * - * TODO: Does not handle BIDI control characters inside the text. - * TODO: Does not handle unallocated characters. - * - * @param {string} text The text from which to extract initial directionality. - * @return {string} Directionality (either 'ltr' or 'rtl') - */ - function strongDirFromContent( text ) { - var m = text.match( strongDirRegExp ); - if ( !m ) { - return null; - } - if ( m[ 2 ] === undefined ) { - return 'ltr'; - } - return 'rtl'; - } - - $.extend( $.i18n.parser.emitter, { - /** - * Wraps argument with unicode control characters for directionality safety - * - * This solves the problem where directionality-neutral characters at the edge of - * the argument string get interpreted with the wrong directionality from the - * enclosing context, giving renderings that look corrupted like "(Ben_(WMF". - * - * The wrapping is LRE...PDF or RLE...PDF, depending on the detected - * directionality of the argument string, using the BIDI algorithm's own "First - * strong directional codepoint" rule. Essentially, this works round the fact that - * there is no embedding equivalent of U+2068 FSI (isolation with heuristic - * direction inference). The latter is cleaner but still not widely supported. - * - * @param {string[]} nodes The text nodes from which to take the first item. - * @return {string} Wrapped String of content as needed. - */ - bidi: function ( nodes ) { - var dir = strongDirFromContent( nodes[ 0 ] ); - if ( dir === 'ltr' ) { - // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING - return '\u202A' + nodes[ 0 ] + '\u202C'; - } - if ( dir === 'rtl' ) { - // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING - return '\u202B' + nodes[ 0 ] + '\u202C'; - } - // No strong directionality: do not wrap - return nodes[ 0 ]; - } - } ); -}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.js deleted file mode 100644 index 330e50c..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.emitter.js +++ /dev/null @@ -1,168 +0,0 @@ -/*! - * jQuery Internationalization library - * - * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar - * - * 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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js b/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js deleted file mode 100644 index 74b16f2..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js +++ /dev/null @@ -1,186 +0,0 @@ -/*! - * 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'; - - $.i18n = $.i18n || {}; - $.extend( $.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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.js b/static/js/lib/jquery.i18n/jquery.i18n.js deleted file mode 100644 index 4365dfb..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.js +++ /dev/null @@ -1,301 +0,0 @@ -/*! - * 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 nav, I18N, - slice = Array.prototype.slice; - /** - * @constructor - * @param {Object} options - */ - I18N = function ( options ) { - // Load defaults - this.options = $.extend( {}, I18N.defaults, options ); - - this.parser = this.options.parser; - this.locale = this.options.locale; - this.messageStore = this.options.messageStore; - this.languages = {}; - - this.init(); - }; - - I18N.prototype = { - /** - * Initialize by loading locales and setting up - * String.prototype.toLocaleString and String.locale. - */ - init: function () { - var i18n = this; - - // Set locale of String environment - String.locale = i18n.locale; - - // Override String.localeString method - String.prototype.toLocaleString = function () { - var localeParts, localePartIndex, value, locale, fallbackIndex, - tryingLocale, message; - - value = this.valueOf(); - locale = i18n.locale; - fallbackIndex = 0; - - while ( locale ) { - // Iterate through locales starting at most-specific until - // localization is found. As in fi-Latn-FI, fi-Latn and fi. - localeParts = locale.split( '-' ); - localePartIndex = localeParts.length; - - do { - tryingLocale = localeParts.slice( 0, localePartIndex ).join( '-' ); - message = i18n.messageStore.get( tryingLocale, value ); - - if ( message ) { - return message; - } - - localePartIndex--; - } while ( localePartIndex ); - - if ( locale === 'en' ) { - break; - } - - locale = ( $.i18n.fallbacks[ i18n.locale ] && $.i18n.fallbacks[ i18n.locale ][ fallbackIndex ] ) || - i18n.options.fallbackLocale; - $.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale + ' (' + value + ')' ); - - fallbackIndex++; - } - - // key not found - return ''; - }; - }, - - /* - * Destroy the i18n instance. - */ - destroy: function () { - $.removeData( document, 'i18n' ); - }, - - /** - * General message loading API This can take a URL string for - * the json formatted messages. Example: - * load('path/to/all_localizations.json'); - * - * To load a localization file for a locale: - * - * load('path/to/de-messages.json', 'de' ); - * - * - * To load a localization file from a directory: - * - * load('path/to/i18n/directory', 'de' ); - * - * The above method has the advantage of fallback resolution. - * ie, it will automatically load the fallback locales for de. - * For most usecases, this is the recommended method. - * It is optional to have trailing slash at end. - * - * A data object containing message key- message translation mappings - * can also be passed. Example: - * - * load( { 'hello' : 'Hello' }, optionalLocale ); - * - * - * A source map containing key-value pair of languagename and locations - * can also be passed. Example: - * - * load( { - * bn: 'i18n/bn.json', - * he: 'i18n/he.json', - * en: 'i18n/en.json' - * } ) - * - * - * If the data argument is null/undefined/false, - * all cached messages for the i18n instance will get reset. - * - * @param {string|Object} source - * @param {string} locale Language tag - * @return {jQuery.Promise} - */ - load: function ( source, locale ) { - var fallbackLocales, locIndex, fallbackLocale, sourceMap = {}; - if ( !source && !locale ) { - source = 'i18n/' + $.i18n().locale + '.json'; - locale = $.i18n().locale; - } - if ( typeof source === 'string' && - // source extension should be json, but can have query params after that. - source.split( '?' )[ 0 ].split( '.' ).pop() !== 'json' - ) { - // Load specified locale then check for fallbacks when directory is specified in load() - sourceMap[ locale ] = source + '/' + locale + '.json'; - fallbackLocales = ( $.i18n.fallbacks[ locale ] || [] ) - .concat( this.options.fallbackLocale ); - for ( locIndex = 0; locIndex < fallbackLocales.length; locIndex++ ) { - fallbackLocale = fallbackLocales[ locIndex ]; - sourceMap[ fallbackLocale ] = source + '/' + fallbackLocale + '.json'; - } - return this.load( sourceMap ); - } else { - return this.messageStore.load( source, locale ); - } - - }, - - /** - * 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' ); - - if ( !i18n ) { - i18n = new I18N(); - $.data( document, 'i18n', i18n ); - } - String.locale = i18n.locale; - return this.each( function () { - var $this = $( this ), - messageKey = $this.data( 'i18n' ), - lBracket, rBracket, type, key; - - if ( messageKey ) { - lBracket = messageKey.indexOf( '[' ); - rBracket = messageKey.indexOf( ']' ); - if ( lBracket !== -1 && rBracket !== -1 && lBracket < rBracket ) { - type = messageKey.slice( lBracket + 1, rBracket ); - key = messageKey.slice( rBracket + 1 ); - if ( type === 'html' ) { - $this.html( i18n.parse( key ) ); - } else { - $this.attr( type, i18n.parse( key ) ); - } - } else { - $this.text( i18n.parse( messageKey ) ); - } - } 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.fallbacks = {}; - $.i18n.debug = false; - $.i18n.log = function ( /* arguments */ ) { - if ( window.console && $.i18n.debug ) { - window.console.log.apply( window.console, arguments ); - } - }; - /* Static members */ - I18N.defaults = { - locale: String.locale, - fallbackLocale: 'en', - parser: $.i18n.parser, - messageStore: $.i18n.messageStore - }; - - // Expose constructor - $.i18n.constructor = I18N; -}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.language.js b/static/js/lib/jquery.i18n/jquery.i18n.language.js deleted file mode 100644 index 4e34576..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.language.js +++ /dev/null @@ -1,498 +0,0 @@ -/* global pluralRuleParser */ -( function ( $ ) { - 'use strict'; - - // jscs:disable - var language = { - // CLDR plural rules generated using - // libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html - pluralRules: { - ak: { - one: 'n = 0..1' - }, - am: { - one: 'i = 0 or n = 1' - }, - ar: { - zero: 'n = 0', - one: 'n = 1', - two: 'n = 2', - few: 'n % 100 = 3..10', - many: 'n % 100 = 11..99' - }, - ars: { - zero: 'n = 0', - one: 'n = 1', - two: 'n = 2', - few: 'n % 100 = 3..10', - many: 'n % 100 = 11..99' - }, - as: { - one: 'i = 0 or n = 1' - }, - be: { - one: 'n % 10 = 1 and n % 100 != 11', - few: 'n % 10 = 2..4 and n % 100 != 12..14', - many: 'n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14' - }, - bh: { - one: 'n = 0..1' - }, - bn: { - one: 'i = 0 or n = 1' - }, - br: { - one: 'n % 10 = 1 and n % 100 != 11,71,91', - two: 'n % 10 = 2 and n % 100 != 12,72,92', - few: 'n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99', - many: 'n != 0 and n % 1000000 = 0' - }, - bs: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' - }, - cs: { - one: 'i = 1 and v = 0', - few: 'i = 2..4 and v = 0', - many: 'v != 0' - }, - cy: { - zero: 'n = 0', - one: 'n = 1', - two: 'n = 2', - few: 'n = 3', - many: 'n = 6' - }, - da: { - one: 'n = 1 or t != 0 and i = 0,1' - }, - dsb: { - one: 'v = 0 and i % 100 = 1 or f % 100 = 1', - two: 'v = 0 and i % 100 = 2 or f % 100 = 2', - few: 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4' - }, - fa: { - one: 'i = 0 or n = 1' - }, - ff: { - one: 'i = 0,1' - }, - fil: { - one: 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9' - }, - fr: { - one: 'i = 0,1' - }, - ga: { - one: 'n = 1', - two: 'n = 2', - few: 'n = 3..6', - many: 'n = 7..10' - }, - gd: { - one: 'n = 1,11', - two: 'n = 2,12', - few: 'n = 3..10,13..19' - }, - gu: { - one: 'i = 0 or n = 1' - }, - guw: { - one: 'n = 0..1' - }, - gv: { - one: 'v = 0 and i % 10 = 1', - two: 'v = 0 and i % 10 = 2', - few: 'v = 0 and i % 100 = 0,20,40,60,80', - many: 'v != 0' - }, - he: { - one: 'i = 1 and v = 0', - two: 'i = 2 and v = 0', - many: 'v = 0 and n != 0..10 and n % 10 = 0' - }, - hi: { - one: 'i = 0 or n = 1' - }, - hr: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' - }, - hsb: { - one: 'v = 0 and i % 100 = 1 or f % 100 = 1', - two: 'v = 0 and i % 100 = 2 or f % 100 = 2', - few: 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4' - }, - hy: { - one: 'i = 0,1' - }, - is: { - one: 't = 0 and i % 10 = 1 and i % 100 != 11 or t != 0' - }, - iu: { - one: 'n = 1', - two: 'n = 2' - }, - iw: { - one: 'i = 1 and v = 0', - two: 'i = 2 and v = 0', - many: 'v = 0 and n != 0..10 and n % 10 = 0' - }, - kab: { - one: 'i = 0,1' - }, - kn: { - one: 'i = 0 or n = 1' - }, - kw: { - one: 'n = 1', - two: 'n = 2' - }, - lag: { - zero: 'n = 0', - one: 'i = 0,1 and n != 0' - }, - ln: { - one: 'n = 0..1' - }, - lt: { - one: 'n % 10 = 1 and n % 100 != 11..19', - few: 'n % 10 = 2..9 and n % 100 != 11..19', - many: 'f != 0' - }, - lv: { - zero: 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19', - one: 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1' - }, - mg: { - one: 'n = 0..1' - }, - mk: { - one: 'v = 0 and i % 10 = 1 or f % 10 = 1' - }, - mo: { - one: 'i = 1 and v = 0', - few: 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19' - }, - mr: { - one: 'i = 0 or n = 1' - }, - mt: { - one: 'n = 1', - few: 'n = 0 or n % 100 = 2..10', - many: 'n % 100 = 11..19' - }, - naq: { - one: 'n = 1', - two: 'n = 2' - }, - nso: { - one: 'n = 0..1' - }, - pa: { - one: 'n = 0..1' - }, - pl: { - one: 'i = 1 and v = 0', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', - many: 'v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14' - }, - prg: { - zero: 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19', - one: 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1' - }, - pt: { - one: 'i = 0..1' - }, - ro: { - one: 'i = 1 and v = 0', - few: 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19' - }, - ru: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', - many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14' - }, - se: { - one: 'n = 1', - two: 'n = 2' - }, - sh: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' - }, - shi: { - one: 'i = 0 or n = 1', - few: 'n = 2..10' - }, - si: { - one: 'n = 0,1 or i = 0 and f = 1' - }, - sk: { - one: 'i = 1 and v = 0', - few: 'i = 2..4 and v = 0', - many: 'v != 0' - }, - sl: { - one: 'v = 0 and i % 100 = 1', - two: 'v = 0 and i % 100 = 2', - few: 'v = 0 and i % 100 = 3..4 or v != 0' - }, - sma: { - one: 'n = 1', - two: 'n = 2' - }, - smi: { - one: 'n = 1', - two: 'n = 2' - }, - smj: { - one: 'n = 1', - two: 'n = 2' - }, - smn: { - one: 'n = 1', - two: 'n = 2' - }, - sms: { - one: 'n = 1', - two: 'n = 2' - }, - sr: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' - }, - ti: { - one: 'n = 0..1' - }, - tl: { - one: 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9' - }, - tzm: { - one: 'n = 0..1 or n = 11..99' - }, - uk: { - one: 'v = 0 and i % 10 = 1 and i % 100 != 11', - few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', - many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14' - }, - wa: { - one: 'n = 0..1' - }, - zu: { - one: 'i = 0 or n = 1' - } - }, - // jscs:enable - - /** - * Plural form transformations, needed for some languages. - * - * @param {integer} count - * Non-localized quantifier - * @param {Array} forms - * List of plural forms - * @return {string} Correct form for quantifier in this language - */ - convertPlural: function ( count, forms ) { - var pluralRules, - pluralFormIndex, - index, - explicitPluralPattern = new RegExp( '\\d+=', 'i' ), - formCount, - form; - - if ( !forms || forms.length === 0 ) { - return ''; - } - - // Handle for Explicit 0= & 1= values - for ( index = 0; index < forms.length; index++ ) { - form = forms[ index ]; - if ( explicitPluralPattern.test( form ) ) { - formCount = parseInt( form.slice( 0, form.indexOf( '=' ) ), 10 ); - if ( formCount === count ) { - return ( form.slice( form.indexOf( '=' ) + 1 ) ); - } - forms[ index ] = undefined; - } - } - - forms = $.map( forms, function ( form ) { - if ( form !== undefined ) { - return form; - } - } ); - - 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 {integer} number - * @param {Object} pluralRules - * @return {integer} 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 - * @return {string} The number converted into a String. - */ - convertNumber: function ( num, integer ) { - var tmp, item, i, - transformTable, numberString, convertedNumber; - - // Set the target Transform table: - transformTable = this.digitTransformTable( $.i18n().locale ); - numberString = String( 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 {string} word - * @param {string} form - * @return {string} - */ - // eslint-disable-next-line no-unused-vars - 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 {string} gender - * male, female, or anything else for neutral. - * @param {Array} forms - * 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 {string} language - * @return {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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js b/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js deleted file mode 100644 index 350be10..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js +++ /dev/null @@ -1,126 +0,0 @@ -/*! - * jQuery Internationalization library - Message Store - * - * 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 MessageStore = function () { - this.messages = {}; - this.sources = {}; - }; - - function jsonMessageLoader( url ) { - var deferred = $.Deferred(); - - $.getJSON( url ) - .done( deferred.resolve ) - .fail( function ( jqxhr, settings, exception ) { - $.i18n.log( 'Error in loading messages from ' + url + ' Exception: ' + exception ); - // Ignore 404 exception, because we are handling fallabacks explicitly - deferred.resolve(); - } ); - - return deferred.promise(); - } - - /** - * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading - */ - MessageStore.prototype = { - - /** - * 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} source - * @param {string} locale Language tag - * @return {jQuery.Promise} - */ - load: function ( source, locale ) { - var key = null, - deferred = null, - deferreds = [], - messageStore = this; - - if ( typeof source === 'string' ) { - // This is a URL to the messages file. - $.i18n.log( 'Loading messages from: ' + source ); - deferred = jsonMessageLoader( source ) - .done( function ( localization ) { - messageStore.set( locale, localization ); - } ); - - return deferred.promise(); - } - - if ( locale ) { - // source is an key-value pair of messages for given locale - messageStore.set( locale, source ); - - return $.Deferred().resolve(); - } else { - // source is a key-value pair of locales and their source - for ( key in source ) { - if ( Object.prototype.hasOwnProperty.call( source, key ) ) { - locale = key; - // No {locale} given, assume data is a group of languages, - // call this function again for each language. - deferreds.push( messageStore.load( source[ key ], locale ) ); - } - } - return $.when.apply( $, deferreds ); - } - - }, - - /** - * Set messages to the given locale. - * If locale exists, add messages to the locale. - * - * @param {string} locale - * @param {Object} messages - */ - set: function ( locale, messages ) { - if ( !this.messages[ locale ] ) { - this.messages[ locale ] = messages; - } else { - this.messages[ locale ] = $.extend( this.messages[ locale ], messages ); - } - }, - - /** - * - * @param {string} locale - * @param {string} messageKey - * @return {boolean} - */ - get: function ( locale, messageKey ) { - return this.messages[ locale ] && this.messages[ locale ][ messageKey ]; - } - }; - - $.extend( $.i18n.messageStore, new MessageStore() ); -}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.parser.js b/static/js/lib/jquery.i18n/jquery.i18n.parser.js deleted file mode 100644 index 27d3566..0000000 --- a/static/js/lib/jquery.i18n/jquery.i18n.parser.js +++ /dev/null @@ -1,309 +0,0 @@ -/*! - * jQuery Internationalization library - * - * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar - * - * 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 pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral, - regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar, - escapedOrRegularLiteral, templateContents, templateName, openTemplate, - closeTemplate, expression, paramExpression, result, - 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.slice( pos, pos + len ) === s ) { - result = s; - pos += len; - } - - return result; - }; - } - - function makeRegexParser( regex ) { - return function () { - var matches = message.slice( pos ).match( regex ); - - if ( matches === null ) { - return null; - } - - pos += matches[ 0 ].length; - - return matches[ 0 ]; - }; - } - - pipe = makeStringParser( '|' ); - colon = makeStringParser( ':' ); - backslash = makeStringParser( '\\' ); - anyCharacter = makeRegexParser( /^./ ); - dollar = makeStringParser( '$' ); - digits = makeRegexParser( /^\d+/ ); - regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); - regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ ); - 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 ] ); - escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); - escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); - - function replacement() { - var result = sequence( [ dollar, digits ] ); - - if ( result === null ) { - return null; - } - - return [ 'REPLACE', parseInt( result[ 1 ], 10 ) - 1 ]; - } - - 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 expr, - result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); - - if ( result === null ) { - return null; - } - - 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 ] ]; - } - - 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 ] ); - } - ] ); - - openTemplate = makeStringParser( '{{' ); - closeTemplate = makeStringParser( '}}' ); - - function template() { - var result = sequence( [ openTemplate, templateContents, closeTemplate ] ); - - return result === null ? null : result[ 1 ]; - } - - expression = choice( [ template, replacement, literal ] ); - paramExpression = choice( [ template, replacement, literalWithoutBar ] ); - - function start() { - var result = nOrMore( 0, expression )(); - - if ( result === null ) { - return null; - } - - return [ 'CONCAT' ].concat( result ); - } - - 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 ) ); diff --git a/static/styles.css b/static/styles.css old mode 100644 new mode 100755 diff --git a/vcap-local.template.json b/vcap-local.template.json old mode 100644 new mode 100755 index d947a20..8b7e74a --- a/vcap-local.template.json +++ b/vcap-local.template.json @@ -9,6 +9,14 @@ }, "label": "cloudantNoSQLDB" } + ], + "compose-for-mongodb": [ + { + "credentials": { + "uri": "MONGO_DATABASE_URI" + }, + "label": "compose-for-mongodb" + } ] } - } \ No newline at end of file +} \ No newline at end of file From b26a6ccda6e2ba660b654d95af10c49fabe71a52 Mon Sep 17 00:00:00 2001 From: Andrew Rieder Date: Mon, 6 Aug 2018 08:27:38 -0400 Subject: [PATCH 2/3] Separated MongoDB documentation. --- README.md | 28 ---------------------------- README_MONGO.md | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 28 deletions(-) create mode 100755 README_MONGO.md diff --git a/README.md b/README.md index 4098ded..9f6199b 100755 --- a/README.md +++ b/README.md @@ -139,31 +139,3 @@ cf push ``` View your app at the URL listed in the output of the push command, for example, *myUrl.mybluemix.net*. - -## Connecting a MongoDB Database - -You'll need access to a MongoDB instance. The below steps outline how to do this: - -1. Sign in to your IBM Cloud Console, and select the 'Create Resource' button in the top right of the page. -2. Type 'MongoDB' in the search field and select 'Compose for MongoDB' under the 'Data & Analytics' section. -3. Check that the name/region/organization/pricing fields are accurate. -4. Select the 'Create' button in the botton left of the page. -5. Return to the IBM Cloud dashboard and select the newly created service. -6. In the sidebar on the left side of the page, select 'Service Credentials' -7. Select the 'New Credential' button, and then the 'Add' button in the page that appears. - -You will also need to run `pip install -r requirements.txt` again to install the required [MongoDB driver](https://github.com/mongodb/mongo-python-driver). - -To prepare the application for use in a local environment, use the below steps. Otherwise, skip the steps to move on to deploying the application to IBM Cloud: - -1. Make a copy of the 'vcap-local.template.json' file (in the application root directory) as 'vcap-local.json' in the same directory. -2. Copy the contents of the 'uri' field in the credentials generated from earlier into the field (replacing "MONGO_DATABASE_URI"). -3. Run the application using `python hello.py`. - -Before deploying the application, make sure that it builds successfully by running `python hello.py` from the root directory of the project (where this README document is located). If no errors are shown, run `cf push` to deploy the application. Once the deployment process completes, run `cf bind-service GetStartedPython [SERVICE_NAME]`, where [SERVICE_NAME] is the name of your MongoDB service. Finally, run `cf retage GetStartedPytohon`. Once this finishes, you can access the application using the URL provided in the output from the 'cf push' command from earlier. - -## Additional Notes on Changes - -The application may work with either Cloudant or MongoDB. However, if both services are available (and at most one is user-provided), **the application will default to MongoDB**. If both services are available as user-provided services, this behavior is **not** guaranteed, and the application may default to either service. - -When deployed on IBM Cloud, this application **does** require bound MongoDB services to have some permutation of 'mongodb' in the name. User-provided services (as created with the cf utility) are also acceptable. \ No newline at end of file diff --git a/README_MONGO.md b/README_MONGO.md new file mode 100755 index 0000000..1dbe4be --- /dev/null +++ b/README_MONGO.md @@ -0,0 +1,27 @@ +## Connecting a MongoDB Database + +You'll need access to a MongoDB instance. The below steps outline how to do this: + +1. Sign in to your IBM Cloud Console, and select the 'Create Resource' button in the top right of the page. +2. Type 'MongoDB' in the search field and select 'Compose for MongoDB' under the 'Data & Analytics' section. +3. Check that the name/region/organization/pricing fields are accurate. +4. Select the 'Create' button in the botton left of the page. +5. Return to the IBM Cloud dashboard and select the newly created service. +6. In the sidebar on the left side of the page, select 'Service Credentials' +7. Select the 'New Credential' button, and then the 'Add' button in the page that appears. + +You will also need to run `pip install -r requirements.txt` again to install the required [MongoDB driver](https://github.com/mongodb/mongo-python-driver). + +To prepare the application for use in a local environment, use the below steps. Otherwise, skip the steps to move on to deploying the application to IBM Cloud: + +1. Make a copy of the 'vcap-local.template.json' file (in the application root directory) as 'vcap-local.json' in the same directory. +2. Copy the contents of the 'uri' field in the credentials generated from earlier into the field (replacing "MONGO_DATABASE_URI"). +3. Run the application using `python hello.py`. + +Before deploying the application, make sure that it builds successfully by running `python hello.py` from the root directory of the project (where this README document is located). If no errors are shown, run `cf push` to deploy the application. Once the deployment process completes, run `cf bind-service GetStartedPython [SERVICE_NAME]`, where [SERVICE_NAME] is the name of your MongoDB service. Finally, run `cf retage GetStartedPytohon`. Once this finishes, you can access the application using the URL provided in the output from the 'cf push' command from earlier. + +## Additional Notes on Changes + +The application may work with either Cloudant or MongoDB. However, if both services are available (and at most one is user-provided), **the application will default to MongoDB**. If both services are available as user-provided services, this behavior is **not** guaranteed, and the application may default to either service. + +When deployed on IBM Cloud, this application **does** require bound MongoDB services to have some permutation of 'mongodb' in the name. User-provided services (as created with the cf utility) are also acceptable. From b92786a09487ec215816da02a85170de20549d76 Mon Sep 17 00:00:00 2001 From: Andrew Rieder Date: Wed, 14 Nov 2018 09:58:56 -0600 Subject: [PATCH 3/3] Re-added js folder. --- .../jquery.i18n/jquery.i18n.emitter.bidi.js | 94 ++++ .../js/lib/jquery.i18n/jquery.i18n.emitter.js | 168 ++++++ .../lib/jquery.i18n/jquery.i18n.fallbacks.js | 186 +++++++ static/js/lib/jquery.i18n/jquery.i18n.js | 301 +++++++++++ .../lib/jquery.i18n/jquery.i18n.language.js | 498 ++++++++++++++++++ .../jquery.i18n/jquery.i18n.messagestore.js | 126 +++++ .../js/lib/jquery.i18n/jquery.i18n.parser.js | 309 +++++++++++ 7 files changed, 1682 insertions(+) create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.emitter.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.language.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.messagestore.js create mode 100755 static/js/lib/jquery.i18n/jquery.i18n.parser.js diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js new file mode 100755 index 0000000..e245557 --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.emitter.bidi.js @@ -0,0 +1,94 @@ +/*! + * BIDI embedding support for jQuery.i18n + * + * Copyright (C) 2015, David Chan + * + * This code 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 this code + * 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 strongDirRegExp; + + /** + * Matches the first strong directionality codepoint: + * - in group 1 if it is LTR + * - in group 2 if it is RTL + * Does not match if there is no strong directionality codepoint. + * + * Generated by UnicodeJS (see tools/strongDir) from the UCD; see + * https://phabricator.wikimedia.org/diffusion/GUJS/ . + */ + strongDirRegExp = new RegExp( + '(?:' + + '(' + + '[\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02b8\u02bb-\u02c1\u02d0\u02d1\u02e0-\u02e4\u02ee\u0370-\u0373\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0482\u048a-\u052f\u0531-\u0556\u0559-\u055f\u0561-\u0587\u0589\u0903-\u0939\u093b\u093d-\u0940\u0949-\u094c\u094e-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd-\u09c0\u09c7\u09c8\u09cb\u09cc\u09ce\u09d7\u09dc\u09dd\u09df-\u09e1\u09e6-\u09f1\u09f4-\u09fa\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3e-\u0a40\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd-\u0ac0\u0ac9\u0acb\u0acc\u0ad0\u0ae0\u0ae1\u0ae6-\u0af0\u0af9\u0b02\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0b5c\u0b5d\u0b5f-\u0b61\u0b66-\u0b77\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd0\u0bd7\u0be6-\u0bf2\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c41-\u0c44\u0c58-\u0c5a\u0c60\u0c61\u0c66-\u0c6f\u0c7f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd-\u0cc4\u0cc6-\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0cde\u0ce0\u0ce1\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d4e\u0d57\u0d5f-\u0d61\u0d66-\u0d75\u0d79-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dcf-\u0dd1\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df4\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e4f-\u0e5b\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edf\u0f00-\u0f17\u0f1a-\u0f34\u0f36\u0f38\u0f3e-\u0f47\u0f49-\u0f6c\u0f7f\u0f85\u0f88-\u0f8c\u0fbe-\u0fc5\u0fc7-\u0fcc\u0fce-\u0fda\u1000-\u102c\u1031\u1038\u103b\u103c\u103f-\u1057\u105a-\u105d\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108c\u108e-\u109c\u109e-\u10c5\u10c7\u10cd\u10d0-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1360-\u137c\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u167f\u1681-\u169a\u16a0-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17b6\u17be-\u17c5\u17c7\u17c8\u17d4-\u17da\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1923-\u1926\u1929-\u192b\u1930\u1931\u1933-\u1938\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a16\u1a19\u1a1a\u1a1e-\u1a55\u1a57\u1a61\u1a63\u1a64\u1a6d-\u1a72\u1a80-\u1a89\u1a90-\u1a99\u1aa0-\u1aad\u1b04-\u1b33\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b4b\u1b50-\u1b6a\u1b74-\u1b7c\u1b82-\u1ba1\u1ba6\u1ba7\u1baa\u1bae-\u1be5\u1be7\u1bea-\u1bec\u1bee\u1bf2\u1bf3\u1bfc-\u1c2b\u1c34\u1c35\u1c3b-\u1c49\u1c4d-\u1c7f\u1cc0-\u1cc7\u1cd3\u1ce1\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200e\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u214f\u2160-\u2188\u2336-\u237a\u2395\u249c-\u24e9\u26ac\u2800-\u28ff\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d70\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u302e\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u3190-\u31ba\u31f0-\u321c\u3220-\u324f\u3260-\u327b\u327f-\u32b0\u32c0-\u32cb\u32d0-\u32fe\u3300-\u3376\u337b-\u33dd\u33e0-\u33fe\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua60c\ua610-\ua62b\ua640-\ua66e\ua680-\ua69d\ua6a0-\ua6ef\ua6f2-\ua6f7\ua722-\ua787\ua789-\ua7ad\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua824\ua827\ua830-\ua837\ua840-\ua873\ua880-\ua8c3\ua8ce-\ua8d9\ua8f2-\ua8fd\ua900-\ua925\ua92e-\ua946\ua952\ua953\ua95f-\ua97c\ua983-\ua9b2\ua9b4\ua9b5\ua9ba\ua9bb\ua9bd-\ua9cd\ua9cf-\ua9d9\ua9de-\ua9e4\ua9e6-\ua9fe\uaa00-\uaa28\uaa2f\uaa30\uaa33\uaa34\uaa40-\uaa42\uaa44-\uaa4b\uaa4d\uaa50-\uaa59\uaa5c-\uaa7b\uaa7d-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaaeb\uaaee-\uaaf5\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab65\uab70-\uabe4\uabe6\uabe7\uabe9-\uabec\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ue000-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b]|\ud800[\udc0d-\udc26]|\ud800[\udc28-\udc3a]|\ud800\udc3c|\ud800\udc3d|\ud800[\udc3f-\udc4d]|\ud800[\udc50-\udc5d]|\ud800[\udc80-\udcfa]|\ud800\udd00|\ud800\udd02|\ud800[\udd07-\udd33]|\ud800[\udd37-\udd3f]|\ud800[\uddd0-\uddfc]|\ud800[\ude80-\ude9c]|\ud800[\udea0-\uded0]|\ud800[\udf00-\udf23]|\ud800[\udf30-\udf4a]|\ud800[\udf50-\udf75]|\ud800[\udf80-\udf9d]|\ud800[\udf9f-\udfc3]|\ud800[\udfc8-\udfd5]|\ud801[\udc00-\udc9d]|\ud801[\udca0-\udca9]|\ud801[\udd00-\udd27]|\ud801[\udd30-\udd63]|\ud801\udd6f|\ud801[\ude00-\udf36]|\ud801[\udf40-\udf55]|\ud801[\udf60-\udf67]|\ud804\udc00|\ud804[\udc02-\udc37]|\ud804[\udc47-\udc4d]|\ud804[\udc66-\udc6f]|\ud804[\udc82-\udcb2]|\ud804\udcb7|\ud804\udcb8|\ud804[\udcbb-\udcc1]|\ud804[\udcd0-\udce8]|\ud804[\udcf0-\udcf9]|\ud804[\udd03-\udd26]|\ud804\udd2c|\ud804[\udd36-\udd43]|\ud804[\udd50-\udd72]|\ud804[\udd74-\udd76]|\ud804[\udd82-\uddb5]|\ud804[\uddbf-\uddc9]|\ud804\uddcd|\ud804[\uddd0-\udddf]|\ud804[\udde1-\uddf4]|\ud804[\ude00-\ude11]|\ud804[\ude13-\ude2e]|\ud804\ude32|\ud804\ude33|\ud804\ude35|\ud804[\ude38-\ude3d]|\ud804[\ude80-\ude86]|\ud804\ude88|\ud804[\ude8a-\ude8d]|\ud804[\ude8f-\ude9d]|\ud804[\ude9f-\udea9]|\ud804[\udeb0-\udede]|\ud804[\udee0-\udee2]|\ud804[\udef0-\udef9]|\ud804\udf02|\ud804\udf03|\ud804[\udf05-\udf0c]|\ud804\udf0f|\ud804\udf10|\ud804[\udf13-\udf28]|\ud804[\udf2a-\udf30]|\ud804\udf32|\ud804\udf33|\ud804[\udf35-\udf39]|\ud804[\udf3d-\udf3f]|\ud804[\udf41-\udf44]|\ud804\udf47|\ud804\udf48|\ud804[\udf4b-\udf4d]|\ud804\udf50|\ud804\udf57|\ud804[\udf5d-\udf63]|\ud805[\udc80-\udcb2]|\ud805\udcb9|\ud805[\udcbb-\udcbe]|\ud805\udcc1|\ud805[\udcc4-\udcc7]|\ud805[\udcd0-\udcd9]|\ud805[\udd80-\uddb1]|\ud805[\uddb8-\uddbb]|\ud805\uddbe|\ud805[\uddc1-\udddb]|\ud805[\ude00-\ude32]|\ud805\ude3b|\ud805\ude3c|\ud805\ude3e|\ud805[\ude41-\ude44]|\ud805[\ude50-\ude59]|\ud805[\ude80-\udeaa]|\ud805\udeac|\ud805\udeae|\ud805\udeaf|\ud805\udeb6|\ud805[\udec0-\udec9]|\ud805[\udf00-\udf19]|\ud805\udf20|\ud805\udf21|\ud805\udf26|\ud805[\udf30-\udf3f]|\ud806[\udca0-\udcf2]|\ud806\udcff|\ud806[\udec0-\udef8]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e]|\ud809[\udc70-\udc74]|\ud809[\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38]|\ud81a[\ude40-\ude5e]|\ud81a[\ude60-\ude69]|\ud81a\ude6e|\ud81a\ude6f|\ud81a[\uded0-\udeed]|\ud81a\udef5|\ud81a[\udf00-\udf2f]|\ud81a[\udf37-\udf45]|\ud81a[\udf50-\udf59]|\ud81a[\udf5b-\udf61]|\ud81a[\udf63-\udf77]|\ud81a[\udf7d-\udf8f]|\ud81b[\udf00-\udf44]|\ud81b[\udf50-\udf7e]|\ud81b[\udf93-\udf9f]|\ud82c\udc00|\ud82c\udc01|\ud82f[\udc00-\udc6a]|\ud82f[\udc70-\udc7c]|\ud82f[\udc80-\udc88]|\ud82f[\udc90-\udc99]|\ud82f\udc9c|\ud82f\udc9f|\ud834[\udc00-\udcf5]|\ud834[\udd00-\udd26]|\ud834[\udd29-\udd66]|\ud834[\udd6a-\udd72]|\ud834\udd83|\ud834\udd84|\ud834[\udd8c-\udda9]|\ud834[\uddae-\udde8]|\ud834[\udf60-\udf71]|\ud835[\udc00-\udc54]|\ud835[\udc56-\udc9c]|\ud835\udc9e|\ud835\udc9f|\ud835\udca2|\ud835\udca5|\ud835\udca6|\ud835[\udca9-\udcac]|\ud835[\udcae-\udcb9]|\ud835\udcbb|\ud835[\udcbd-\udcc3]|\ud835[\udcc5-\udd05]|\ud835[\udd07-\udd0a]|\ud835[\udd0d-\udd14]|\ud835[\udd16-\udd1c]|\ud835[\udd1e-\udd39]|\ud835[\udd3b-\udd3e]|\ud835[\udd40-\udd44]|\ud835\udd46|\ud835[\udd4a-\udd50]|\ud835[\udd52-\udea5]|\ud835[\udea8-\udeda]|\ud835[\udedc-\udf14]|\ud835[\udf16-\udf4e]|\ud835[\udf50-\udf88]|\ud835[\udf8a-\udfc2]|\ud835[\udfc4-\udfcb]|\ud836[\udc00-\uddff]|\ud836[\ude37-\ude3a]|\ud836[\ude6d-\ude74]|\ud836[\ude76-\ude83]|\ud836[\ude85-\ude8b]|\ud83c[\udd10-\udd2e]|\ud83c[\udd30-\udd69]|\ud83c[\udd70-\udd9a]|\ud83c[\udde6-\ude02]|\ud83c[\ude10-\ude3a]|\ud83c[\ude40-\ude48]|\ud83c\ude50|\ud83c\ude51|[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6]|\ud869[\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34]|\ud86d[\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud86e[\udc20-\udfff]|[\ud86f-\ud872][\udc00-\udfff]|\ud873[\udc00-\udea1]|\ud87e[\udc00-\ude1d]|[\udb80-\udbbe][\udc00-\udfff]|\udbbf[\udc00-\udffd]|[\udbc0-\udbfe][\udc00-\udfff]|\udbff[\udc00-\udffd]' + + ')|(' + + '[\u0590\u05be\u05c0\u05c3\u05c6\u05c8-\u05ff\u07c0-\u07ea\u07f4\u07f5\u07fa-\u0815\u081a\u0824\u0828\u082e-\u0858\u085c-\u089f\u200f\ufb1d\ufb1f-\ufb28\ufb2a-\ufb4f\u0608\u060b\u060d\u061b-\u064a\u066d-\u066f\u0671-\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u0710\u0712-\u072f\u074b-\u07a5\u07b1-\u07bf\u08a0-\u08e2\ufb50-\ufd3d\ufd40-\ufdcf\ufdf0-\ufdfc\ufdfe\ufdff\ufe70-\ufefe]|\ud802[\udc00-\udd1e]|\ud802[\udd20-\ude00]|\ud802\ude04|\ud802[\ude07-\ude0b]|\ud802[\ude10-\ude37]|\ud802[\ude3b-\ude3e]|\ud802[\ude40-\udee4]|\ud802[\udee7-\udf38]|\ud802[\udf40-\udfff]|\ud803[\udc00-\ude5f]|\ud803[\ude7f-\udfff]|\ud83a[\udc00-\udccf]|\ud83a[\udcd7-\udfff]|\ud83b[\udc00-\uddff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\ude00-\udeef]|\ud83b[\udef2-\udeff]' + + ')' + + ')' + ); + + /** + * Gets directionality of the first strongly directional codepoint + * + * This is the rule the BIDI algorithm uses to determine the directionality of + * paragraphs ( http://unicode.org/reports/tr9/#The_Paragraph_Level ) and + * FSI isolates ( http://unicode.org/reports/tr9/#Explicit_Directional_Isolates ). + * + * TODO: Does not handle BIDI control characters inside the text. + * TODO: Does not handle unallocated characters. + * + * @param {string} text The text from which to extract initial directionality. + * @return {string} Directionality (either 'ltr' or 'rtl') + */ + function strongDirFromContent( text ) { + var m = text.match( strongDirRegExp ); + if ( !m ) { + return null; + } + if ( m[ 2 ] === undefined ) { + return 'ltr'; + } + return 'rtl'; + } + + $.extend( $.i18n.parser.emitter, { + /** + * Wraps argument with unicode control characters for directionality safety + * + * This solves the problem where directionality-neutral characters at the edge of + * the argument string get interpreted with the wrong directionality from the + * enclosing context, giving renderings that look corrupted like "(Ben_(WMF". + * + * The wrapping is LRE...PDF or RLE...PDF, depending on the detected + * directionality of the argument string, using the BIDI algorithm's own "First + * strong directional codepoint" rule. Essentially, this works round the fact that + * there is no embedding equivalent of U+2068 FSI (isolation with heuristic + * direction inference). The latter is cleaner but still not widely supported. + * + * @param {string[]} nodes The text nodes from which to take the first item. + * @return {string} Wrapped String of content as needed. + */ + bidi: function ( nodes ) { + var dir = strongDirFromContent( nodes[ 0 ] ); + if ( dir === 'ltr' ) { + // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING + return '\u202A' + nodes[ 0 ] + '\u202C'; + } + if ( dir === 'rtl' ) { + // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING + return '\u202B' + nodes[ 0 ] + '\u202C'; + } + // No strong directionality: do not wrap + return nodes[ 0 ]; + } + } ); +}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.emitter.js b/static/js/lib/jquery.i18n/jquery.i18n.emitter.js new file mode 100755 index 0000000..330e50c --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.emitter.js @@ -0,0 +1,168 @@ +/*! + * jQuery Internationalization library + * + * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar + * + * 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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js b/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js new file mode 100755 index 0000000..74b16f2 --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.fallbacks.js @@ -0,0 +1,186 @@ +/*! + * 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'; + + $.i18n = $.i18n || {}; + $.extend( $.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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.js b/static/js/lib/jquery.i18n/jquery.i18n.js new file mode 100755 index 0000000..4365dfb --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.js @@ -0,0 +1,301 @@ +/*! + * 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 nav, I18N, + slice = Array.prototype.slice; + /** + * @constructor + * @param {Object} options + */ + I18N = function ( options ) { + // Load defaults + this.options = $.extend( {}, I18N.defaults, options ); + + this.parser = this.options.parser; + this.locale = this.options.locale; + this.messageStore = this.options.messageStore; + this.languages = {}; + + this.init(); + }; + + I18N.prototype = { + /** + * Initialize by loading locales and setting up + * String.prototype.toLocaleString and String.locale. + */ + init: function () { + var i18n = this; + + // Set locale of String environment + String.locale = i18n.locale; + + // Override String.localeString method + String.prototype.toLocaleString = function () { + var localeParts, localePartIndex, value, locale, fallbackIndex, + tryingLocale, message; + + value = this.valueOf(); + locale = i18n.locale; + fallbackIndex = 0; + + while ( locale ) { + // Iterate through locales starting at most-specific until + // localization is found. As in fi-Latn-FI, fi-Latn and fi. + localeParts = locale.split( '-' ); + localePartIndex = localeParts.length; + + do { + tryingLocale = localeParts.slice( 0, localePartIndex ).join( '-' ); + message = i18n.messageStore.get( tryingLocale, value ); + + if ( message ) { + return message; + } + + localePartIndex--; + } while ( localePartIndex ); + + if ( locale === 'en' ) { + break; + } + + locale = ( $.i18n.fallbacks[ i18n.locale ] && $.i18n.fallbacks[ i18n.locale ][ fallbackIndex ] ) || + i18n.options.fallbackLocale; + $.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale + ' (' + value + ')' ); + + fallbackIndex++; + } + + // key not found + return ''; + }; + }, + + /* + * Destroy the i18n instance. + */ + destroy: function () { + $.removeData( document, 'i18n' ); + }, + + /** + * General message loading API This can take a URL string for + * the json formatted messages. Example: + * load('path/to/all_localizations.json'); + * + * To load a localization file for a locale: + * + * load('path/to/de-messages.json', 'de' ); + * + * + * To load a localization file from a directory: + * + * load('path/to/i18n/directory', 'de' ); + * + * The above method has the advantage of fallback resolution. + * ie, it will automatically load the fallback locales for de. + * For most usecases, this is the recommended method. + * It is optional to have trailing slash at end. + * + * A data object containing message key- message translation mappings + * can also be passed. Example: + * + * load( { 'hello' : 'Hello' }, optionalLocale ); + * + * + * A source map containing key-value pair of languagename and locations + * can also be passed. Example: + * + * load( { + * bn: 'i18n/bn.json', + * he: 'i18n/he.json', + * en: 'i18n/en.json' + * } ) + * + * + * If the data argument is null/undefined/false, + * all cached messages for the i18n instance will get reset. + * + * @param {string|Object} source + * @param {string} locale Language tag + * @return {jQuery.Promise} + */ + load: function ( source, locale ) { + var fallbackLocales, locIndex, fallbackLocale, sourceMap = {}; + if ( !source && !locale ) { + source = 'i18n/' + $.i18n().locale + '.json'; + locale = $.i18n().locale; + } + if ( typeof source === 'string' && + // source extension should be json, but can have query params after that. + source.split( '?' )[ 0 ].split( '.' ).pop() !== 'json' + ) { + // Load specified locale then check for fallbacks when directory is specified in load() + sourceMap[ locale ] = source + '/' + locale + '.json'; + fallbackLocales = ( $.i18n.fallbacks[ locale ] || [] ) + .concat( this.options.fallbackLocale ); + for ( locIndex = 0; locIndex < fallbackLocales.length; locIndex++ ) { + fallbackLocale = fallbackLocales[ locIndex ]; + sourceMap[ fallbackLocale ] = source + '/' + fallbackLocale + '.json'; + } + return this.load( sourceMap ); + } else { + return this.messageStore.load( source, locale ); + } + + }, + + /** + * 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' ); + + if ( !i18n ) { + i18n = new I18N(); + $.data( document, 'i18n', i18n ); + } + String.locale = i18n.locale; + return this.each( function () { + var $this = $( this ), + messageKey = $this.data( 'i18n' ), + lBracket, rBracket, type, key; + + if ( messageKey ) { + lBracket = messageKey.indexOf( '[' ); + rBracket = messageKey.indexOf( ']' ); + if ( lBracket !== -1 && rBracket !== -1 && lBracket < rBracket ) { + type = messageKey.slice( lBracket + 1, rBracket ); + key = messageKey.slice( rBracket + 1 ); + if ( type === 'html' ) { + $this.html( i18n.parse( key ) ); + } else { + $this.attr( type, i18n.parse( key ) ); + } + } else { + $this.text( i18n.parse( messageKey ) ); + } + } 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.fallbacks = {}; + $.i18n.debug = false; + $.i18n.log = function ( /* arguments */ ) { + if ( window.console && $.i18n.debug ) { + window.console.log.apply( window.console, arguments ); + } + }; + /* Static members */ + I18N.defaults = { + locale: String.locale, + fallbackLocale: 'en', + parser: $.i18n.parser, + messageStore: $.i18n.messageStore + }; + + // Expose constructor + $.i18n.constructor = I18N; +}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.language.js b/static/js/lib/jquery.i18n/jquery.i18n.language.js new file mode 100755 index 0000000..4e34576 --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.language.js @@ -0,0 +1,498 @@ +/* global pluralRuleParser */ +( function ( $ ) { + 'use strict'; + + // jscs:disable + var language = { + // CLDR plural rules generated using + // libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html + pluralRules: { + ak: { + one: 'n = 0..1' + }, + am: { + one: 'i = 0 or n = 1' + }, + ar: { + zero: 'n = 0', + one: 'n = 1', + two: 'n = 2', + few: 'n % 100 = 3..10', + many: 'n % 100 = 11..99' + }, + ars: { + zero: 'n = 0', + one: 'n = 1', + two: 'n = 2', + few: 'n % 100 = 3..10', + many: 'n % 100 = 11..99' + }, + as: { + one: 'i = 0 or n = 1' + }, + be: { + one: 'n % 10 = 1 and n % 100 != 11', + few: 'n % 10 = 2..4 and n % 100 != 12..14', + many: 'n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14' + }, + bh: { + one: 'n = 0..1' + }, + bn: { + one: 'i = 0 or n = 1' + }, + br: { + one: 'n % 10 = 1 and n % 100 != 11,71,91', + two: 'n % 10 = 2 and n % 100 != 12,72,92', + few: 'n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99', + many: 'n != 0 and n % 1000000 = 0' + }, + bs: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' + }, + cs: { + one: 'i = 1 and v = 0', + few: 'i = 2..4 and v = 0', + many: 'v != 0' + }, + cy: { + zero: 'n = 0', + one: 'n = 1', + two: 'n = 2', + few: 'n = 3', + many: 'n = 6' + }, + da: { + one: 'n = 1 or t != 0 and i = 0,1' + }, + dsb: { + one: 'v = 0 and i % 100 = 1 or f % 100 = 1', + two: 'v = 0 and i % 100 = 2 or f % 100 = 2', + few: 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4' + }, + fa: { + one: 'i = 0 or n = 1' + }, + ff: { + one: 'i = 0,1' + }, + fil: { + one: 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9' + }, + fr: { + one: 'i = 0,1' + }, + ga: { + one: 'n = 1', + two: 'n = 2', + few: 'n = 3..6', + many: 'n = 7..10' + }, + gd: { + one: 'n = 1,11', + two: 'n = 2,12', + few: 'n = 3..10,13..19' + }, + gu: { + one: 'i = 0 or n = 1' + }, + guw: { + one: 'n = 0..1' + }, + gv: { + one: 'v = 0 and i % 10 = 1', + two: 'v = 0 and i % 10 = 2', + few: 'v = 0 and i % 100 = 0,20,40,60,80', + many: 'v != 0' + }, + he: { + one: 'i = 1 and v = 0', + two: 'i = 2 and v = 0', + many: 'v = 0 and n != 0..10 and n % 10 = 0' + }, + hi: { + one: 'i = 0 or n = 1' + }, + hr: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' + }, + hsb: { + one: 'v = 0 and i % 100 = 1 or f % 100 = 1', + two: 'v = 0 and i % 100 = 2 or f % 100 = 2', + few: 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4' + }, + hy: { + one: 'i = 0,1' + }, + is: { + one: 't = 0 and i % 10 = 1 and i % 100 != 11 or t != 0' + }, + iu: { + one: 'n = 1', + two: 'n = 2' + }, + iw: { + one: 'i = 1 and v = 0', + two: 'i = 2 and v = 0', + many: 'v = 0 and n != 0..10 and n % 10 = 0' + }, + kab: { + one: 'i = 0,1' + }, + kn: { + one: 'i = 0 or n = 1' + }, + kw: { + one: 'n = 1', + two: 'n = 2' + }, + lag: { + zero: 'n = 0', + one: 'i = 0,1 and n != 0' + }, + ln: { + one: 'n = 0..1' + }, + lt: { + one: 'n % 10 = 1 and n % 100 != 11..19', + few: 'n % 10 = 2..9 and n % 100 != 11..19', + many: 'f != 0' + }, + lv: { + zero: 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19', + one: 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1' + }, + mg: { + one: 'n = 0..1' + }, + mk: { + one: 'v = 0 and i % 10 = 1 or f % 10 = 1' + }, + mo: { + one: 'i = 1 and v = 0', + few: 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19' + }, + mr: { + one: 'i = 0 or n = 1' + }, + mt: { + one: 'n = 1', + few: 'n = 0 or n % 100 = 2..10', + many: 'n % 100 = 11..19' + }, + naq: { + one: 'n = 1', + two: 'n = 2' + }, + nso: { + one: 'n = 0..1' + }, + pa: { + one: 'n = 0..1' + }, + pl: { + one: 'i = 1 and v = 0', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', + many: 'v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14' + }, + prg: { + zero: 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19', + one: 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1' + }, + pt: { + one: 'i = 0..1' + }, + ro: { + one: 'i = 1 and v = 0', + few: 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19' + }, + ru: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', + many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14' + }, + se: { + one: 'n = 1', + two: 'n = 2' + }, + sh: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' + }, + shi: { + one: 'i = 0 or n = 1', + few: 'n = 2..10' + }, + si: { + one: 'n = 0,1 or i = 0 and f = 1' + }, + sk: { + one: 'i = 1 and v = 0', + few: 'i = 2..4 and v = 0', + many: 'v != 0' + }, + sl: { + one: 'v = 0 and i % 100 = 1', + two: 'v = 0 and i % 100 = 2', + few: 'v = 0 and i % 100 = 3..4 or v != 0' + }, + sma: { + one: 'n = 1', + two: 'n = 2' + }, + smi: { + one: 'n = 1', + two: 'n = 2' + }, + smj: { + one: 'n = 1', + two: 'n = 2' + }, + smn: { + one: 'n = 1', + two: 'n = 2' + }, + sms: { + one: 'n = 1', + two: 'n = 2' + }, + sr: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14' + }, + ti: { + one: 'n = 0..1' + }, + tl: { + one: 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9' + }, + tzm: { + one: 'n = 0..1 or n = 11..99' + }, + uk: { + one: 'v = 0 and i % 10 = 1 and i % 100 != 11', + few: 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14', + many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14' + }, + wa: { + one: 'n = 0..1' + }, + zu: { + one: 'i = 0 or n = 1' + } + }, + // jscs:enable + + /** + * Plural form transformations, needed for some languages. + * + * @param {integer} count + * Non-localized quantifier + * @param {Array} forms + * List of plural forms + * @return {string} Correct form for quantifier in this language + */ + convertPlural: function ( count, forms ) { + var pluralRules, + pluralFormIndex, + index, + explicitPluralPattern = new RegExp( '\\d+=', 'i' ), + formCount, + form; + + if ( !forms || forms.length === 0 ) { + return ''; + } + + // Handle for Explicit 0= & 1= values + for ( index = 0; index < forms.length; index++ ) { + form = forms[ index ]; + if ( explicitPluralPattern.test( form ) ) { + formCount = parseInt( form.slice( 0, form.indexOf( '=' ) ), 10 ); + if ( formCount === count ) { + return ( form.slice( form.indexOf( '=' ) + 1 ) ); + } + forms[ index ] = undefined; + } + } + + forms = $.map( forms, function ( form ) { + if ( form !== undefined ) { + return form; + } + } ); + + 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 {integer} number + * @param {Object} pluralRules + * @return {integer} 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 + * @return {string} The number converted into a String. + */ + convertNumber: function ( num, integer ) { + var tmp, item, i, + transformTable, numberString, convertedNumber; + + // Set the target Transform table: + transformTable = this.digitTransformTable( $.i18n().locale ); + numberString = String( 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 {string} word + * @param {string} form + * @return {string} + */ + // eslint-disable-next-line no-unused-vars + 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 {string} gender + * male, female, or anything else for neutral. + * @param {Array} forms + * 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 {string} language + * @return {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 ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js b/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js new file mode 100755 index 0000000..350be10 --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.messagestore.js @@ -0,0 +1,126 @@ +/*! + * jQuery Internationalization library - Message Store + * + * 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 MessageStore = function () { + this.messages = {}; + this.sources = {}; + }; + + function jsonMessageLoader( url ) { + var deferred = $.Deferred(); + + $.getJSON( url ) + .done( deferred.resolve ) + .fail( function ( jqxhr, settings, exception ) { + $.i18n.log( 'Error in loading messages from ' + url + ' Exception: ' + exception ); + // Ignore 404 exception, because we are handling fallabacks explicitly + deferred.resolve(); + } ); + + return deferred.promise(); + } + + /** + * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading + */ + MessageStore.prototype = { + + /** + * 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} source + * @param {string} locale Language tag + * @return {jQuery.Promise} + */ + load: function ( source, locale ) { + var key = null, + deferred = null, + deferreds = [], + messageStore = this; + + if ( typeof source === 'string' ) { + // This is a URL to the messages file. + $.i18n.log( 'Loading messages from: ' + source ); + deferred = jsonMessageLoader( source ) + .done( function ( localization ) { + messageStore.set( locale, localization ); + } ); + + return deferred.promise(); + } + + if ( locale ) { + // source is an key-value pair of messages for given locale + messageStore.set( locale, source ); + + return $.Deferred().resolve(); + } else { + // source is a key-value pair of locales and their source + for ( key in source ) { + if ( Object.prototype.hasOwnProperty.call( source, key ) ) { + locale = key; + // No {locale} given, assume data is a group of languages, + // call this function again for each language. + deferreds.push( messageStore.load( source[ key ], locale ) ); + } + } + return $.when.apply( $, deferreds ); + } + + }, + + /** + * Set messages to the given locale. + * If locale exists, add messages to the locale. + * + * @param {string} locale + * @param {Object} messages + */ + set: function ( locale, messages ) { + if ( !this.messages[ locale ] ) { + this.messages[ locale ] = messages; + } else { + this.messages[ locale ] = $.extend( this.messages[ locale ], messages ); + } + }, + + /** + * + * @param {string} locale + * @param {string} messageKey + * @return {boolean} + */ + get: function ( locale, messageKey ) { + return this.messages[ locale ] && this.messages[ locale ][ messageKey ]; + } + }; + + $.extend( $.i18n.messageStore, new MessageStore() ); +}( jQuery ) ); diff --git a/static/js/lib/jquery.i18n/jquery.i18n.parser.js b/static/js/lib/jquery.i18n/jquery.i18n.parser.js new file mode 100755 index 0000000..27d3566 --- /dev/null +++ b/static/js/lib/jquery.i18n/jquery.i18n.parser.js @@ -0,0 +1,309 @@ +/*! + * jQuery Internationalization library + * + * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar + * + * 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 pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral, + regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar, + escapedOrRegularLiteral, templateContents, templateName, openTemplate, + closeTemplate, expression, paramExpression, result, + 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.slice( pos, pos + len ) === s ) { + result = s; + pos += len; + } + + return result; + }; + } + + function makeRegexParser( regex ) { + return function () { + var matches = message.slice( pos ).match( regex ); + + if ( matches === null ) { + return null; + } + + pos += matches[ 0 ].length; + + return matches[ 0 ]; + }; + } + + pipe = makeStringParser( '|' ); + colon = makeStringParser( ':' ); + backslash = makeStringParser( '\\' ); + anyCharacter = makeRegexParser( /^./ ); + dollar = makeStringParser( '$' ); + digits = makeRegexParser( /^\d+/ ); + regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); + regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ ); + 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 ] ); + escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); + escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); + + function replacement() { + var result = sequence( [ dollar, digits ] ); + + if ( result === null ) { + return null; + } + + return [ 'REPLACE', parseInt( result[ 1 ], 10 ) - 1 ]; + } + + 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 expr, + result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); + + if ( result === null ) { + return null; + } + + 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 ] ]; + } + + 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 ] ); + } + ] ); + + openTemplate = makeStringParser( '{{' ); + closeTemplate = makeStringParser( '}}' ); + + function template() { + var result = sequence( [ openTemplate, templateContents, closeTemplate ] ); + + return result === null ? null : result[ 1 ]; + } + + expression = choice( [ template, replacement, literal ] ); + paramExpression = choice( [ template, replacement, literalWithoutBar ] ); + + function start() { + var result = nOrMore( 0, expression )(); + + if ( result === null ) { + return null; + } + + return [ 'CONCAT' ].concat( result ); + } + + 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 ) );