diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 69903015a0c..a1fc68f33bf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,15 @@ Changelog --------- +v2.6.0 TBA +================= +API changes and deprecations: + * Replace `c.__version__` with new helper `h.ckan_version()` (#3103) + +Major: + * Private datasets are now included in the default dataset search results (#3191) + * package_search API action now has an include_private parameter (#3191) + v2.5.2 2016-03-31 ================= diff --git a/bin/running_stats.py b/bin/running_stats.py index 006ce6cc029..a2612f35d0f 100644 --- a/bin/running_stats.py +++ b/bin/running_stats.py @@ -1,3 +1,5 @@ +# encoding: utf-8 + '''Tool for a script to keep track changes performed on a large number of objects. diff --git a/circle.yml b/circle.yml index d9c321c9b77..d9fff638dea 100644 --- a/circle.yml +++ b/circle.yml @@ -15,6 +15,7 @@ machine: version: 0.10.33 dependencies: + pre: - "[ -e ~/.local/bin/circleci-matrix ] || mkdir -p ~/.local/bin @@ -22,8 +23,8 @@ dependencies: && chmod +x ~/.local/bin/circleci-matrix" override: - - pip install -r requirements.txt --allow-all-external - - pip install -r dev-requirements.txt --allow-all-external + - pip install -r requirements.txt + - pip install -r dev-requirements.txt - python setup.py develop post: @@ -38,6 +39,7 @@ dependencies: - ~/nvm/v0.10.33/bin/phantomjs database: + post: - sudo -E -u postgres ./bin/postgres_init/1_create_ckan_db.sh - sudo -E -u postgres ./bin/postgres_init/2_create_ckan_datastore_db.sh @@ -53,6 +55,7 @@ database: - paster db init -c test-core.ini test: + override: - circleci-matrix: parallel: true diff --git a/ckan/__init__.py b/ckan/__init__.py index 28c91910ace..c31228b1774 100644 --- a/ckan/__init__.py +++ b/ckan/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 -__version__ = '2.6.0a' +__version__ = '2.7.0a' __description__ = 'CKAN Software' __long_description__ = \ diff --git a/ckan/authz.py b/ckan/authz.py index 4f169e0480a..d77f91b887d 100644 --- a/ckan/authz.py +++ b/ckan/authz.py @@ -4,7 +4,7 @@ import re from logging import getLogger -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool import ckan.plugins as p diff --git a/ckan/ckan_nose_plugin.py b/ckan/ckan_nose_plugin.py index ed5bb3015be..c0f922f479c 100644 --- a/ckan/ckan_nose_plugin.py +++ b/ckan/ckan_nose_plugin.py @@ -8,7 +8,7 @@ import re import pkg_resources from paste.deploy import loadapp -from pylons import config +from ckan.common import config import unittest import time diff --git a/ckan/common.py b/ckan/common.py index c711c1e48b5..ac55e52a6e5 100644 --- a/ckan/common.py +++ b/ckan/common.py @@ -8,6 +8,12 @@ # NOTE: This file is specificaly created for # from ckan.common import x, y, z to be allowed +from collections import MutableMapping + +import flask +import pylons + +from werkzeug.local import Local from pylons.i18n import _, ungettext from pylons import g, c, request, session, response @@ -17,3 +23,94 @@ from collections import OrderedDict # from python 2.7 except ImportError: from sqlalchemy.util import OrderedDict + + +def is_flask_request(): + u''' + A centralized way to determine whether we are in the context of a + request being served by Flask or Pylons + ''' + try: + pylons.request.environ + pylons_request_available = True + except TypeError: + pylons_request_available = False + + return (flask.request and + (flask.request.environ.get(u'ckan.app') == u'flask_app' or + not pylons_request_available)) + + +class CKANConfig(MutableMapping): + u'''Main CKAN configuration object + + This is a dict-like object that also proxies any changes to the + Flask and Pylons configuration objects. + + The actual `config` instance in this module is initialized in the + `load_environment` method with the values of the ini file or env vars. + + ''' + + def __init__(self, *args, **kwargs): + self.store = dict() + self.update(dict(*args, **kwargs)) + + def __getitem__(self, key): + return self.store[key] + + def __iter__(self): + return iter(self.store) + + def __len__(self): + return len(self.store) + + def __repr__(self): + return self.store.__repr__() + + def copy(self): + return self.store.copy() + + def clear(self): + self.store.clear() + + try: + flask.current_app.config.clear() + except RuntimeError: + pass + try: + pylons.config.clear() + # Pylons set this default itself + pylons.config[u'lang'] = None + except TypeError: + pass + + def __setitem__(self, key, value): + self.store[key] = value + try: + flask.current_app.config[key] = value + except RuntimeError: + pass + try: + pylons.config[key] = value + except TypeError: + pass + + def __delitem__(self, key): + del self.store[key] + try: + del flask.current_app.config[key] + except RuntimeError: + pass + try: + del pylons.config[key] + except TypeError: + pass + +local = Local() + +# This a proxy to the bounded config object +local(u'config') + +# Thread-local safe objects +config = local.config = CKANConfig() diff --git a/ckan/config/deployment.ini_tmpl b/ckan/config/deployment.ini_tmpl index d25c9773804..ee59c86e329 100644 --- a/ckan/config/deployment.ini_tmpl +++ b/ckan/config/deployment.ini_tmpl @@ -78,7 +78,11 @@ ckan.auth.roles_that_cascade_to_sub_groups = admin ckan.site_id = default #solr_url = http://127.0.0.1:8983/solr -#ckan.simple_search = 1 + +## Redis Settings + +# URL to your Redis instance, including the database to be used. +#ckan.redis.url = redis://localhost:6379/0 ## CORS Settings @@ -166,11 +170,11 @@ ckan.hide_activity_from_users = %(ckan.site_id)s ## Email settings -#email_to = you@yourdomain.com -#error_email_from = paste@localhost +#email_to = errors@example.com +#error_email_from = ckan-errors@example.com #smtp.server = localhost #smtp.starttls = False -#smtp.user = your_username@gmail.com +#smtp.user = username@example.com #smtp.password = your_password #smtp.mail_from = diff --git a/ckan/config/environment.py b/ckan/config/environment.py index bfc07556376..fb5f7d2cdc9 100644 --- a/ckan/config/environment.py +++ b/ckan/config/environment.py @@ -1,6 +1,6 @@ # encoding: utf-8 -"""Pylons environment configuration""" +'''CKAN environment configuration''' import os import logging import warnings @@ -8,7 +8,7 @@ import pytz import sqlalchemy -from pylons import config +from pylons import config as pylons_config import formencode import ckan.config.routing as routing @@ -16,13 +16,14 @@ import ckan.plugins as p import ckan.lib.helpers as helpers import ckan.lib.app_globals as app_globals +from ckan.lib.redis import is_redis_available import ckan.lib.render as render import ckan.lib.search as search import ckan.logic as logic import ckan.authz as authz import ckan.lib.jinja_extensions as jinja_extensions -from ckan.common import _, ungettext +from ckan.common import _, ungettext, config from ckan.exceptions import CkanConfigurationException log = logging.getLogger(__name__) @@ -72,8 +73,17 @@ def find_controller(self, controller): static_files=os.path.join(root, 'public'), templates=[]) - # Initialize config with the basic options - config.init_app(global_conf, app_conf, package='ckan', paths=paths) + # Initialize main CKAN config object + config.update(global_conf) + config.update(app_conf) + + # Initialize Pylons own config object + pylons_config.init_app(global_conf, app_conf, package='ckan', paths=paths) + + # Update the main CKAN config object with the Pylons specific stuff, as it + # quite hard to keep them separated. This should be removed once Pylons + # support is dropped + config.update(pylons_config) # Setup the SQLAlchemy database engine # Suppress a couple of sqlalchemy warnings @@ -84,9 +94,14 @@ def find_controller(self, controller): for msg in msgs: warnings.filterwarnings('ignore', msg, sqlalchemy.exc.SAWarning) + # Check Redis availability + if not is_redis_available(): + log.critical('Could not connect to Redis.') + # load all CKAN plugins - p.load_all(config) + p.load_all() + app_globals.reset() # A mapping of config settings that can be overridden by env vars. # Note: Do not remove the following lines, they are used in the docs @@ -95,6 +110,7 @@ def find_controller(self, controller): 'sqlalchemy.url': 'CKAN_SQLALCHEMY_URL', 'ckan.datastore.write_url': 'CKAN_DATASTORE_WRITE_URL', 'ckan.datastore.read_url': 'CKAN_DATASTORE_READ_URL', + 'ckan.redis.url': 'CKAN_REDIS_URL', 'solr_url': 'CKAN_SOLR_URL', 'ckan.site_id': 'CKAN_SITE_ID', 'ckan.site_url': 'CKAN_SITE_URL', @@ -184,10 +200,11 @@ def update_config(): # The RoutesMiddleware needs its mapper updating if it exists if 'routes.middleware' in config: config['routes.middleware'].mapper = routes_map + # routes.named_routes is a CKAN thing config['routes.named_routes'] = routing.named_routes config['pylons.app_globals'] = app_globals.app_globals # initialise the globals - config['pylons.app_globals']._init() + app_globals.app_globals._init() helpers.load_plugin_helpers() config['pylons.h'] = helpers.helper_functions diff --git a/ckan/config/middleware.py b/ckan/config/middleware.py deleted file mode 100644 index c9898fc11ae..00000000000 --- a/ckan/config/middleware.py +++ /dev/null @@ -1,595 +0,0 @@ -# encoding: utf-8 - -"""Pylons middleware initialization""" -import urllib -import urllib2 -import logging -import json -import hashlib -import os - -import sqlalchemy as sa -from beaker.middleware import CacheMiddleware, SessionMiddleware -from paste.cascade import Cascade -from paste.registry import RegistryManager -from paste.urlparser import StaticURLParser -from paste.deploy.converters import asbool -from pylons import config -from pylons.middleware import ErrorHandler, StatusCodeRedirect -from pylons.wsgiapp import PylonsApp -from routes.middleware import RoutesMiddleware -from repoze.who.config import WhoConfig -from repoze.who.middleware import PluggableAuthenticationMiddleware -from fanstatic import Fanstatic - -from wsgi_party import WSGIParty, HighAndDry -from flask import Flask -from flask import abort as flask_abort -from flask import request as flask_request -from flask import _request_ctx_stack -from werkzeug.exceptions import HTTPException -from werkzeug.test import create_environ, run_wsgi_app - -from ckan.plugins import PluginImplementations -from ckan.plugins.interfaces import IMiddleware -from ckan.lib.i18n import get_locales_from_config -import ckan.lib.uploader as uploader - -from ckan.config.environment import load_environment -import ckan.lib.app_globals as app_globals - -log = logging.getLogger(__name__) - - -def make_app(conf, full_stack=True, static_files=True, **app_conf): - - # :::TODO::: like the flask app, make the pylons app respond to invites at - # /__invite__/, and handle can_handle_request requests. - - pylons_app = make_pylons_stack(conf, full_stack, static_files, **app_conf) - flask_app = make_flask_stack(conf) - - app = AskAppDispatcherMiddleware({'pylons_app': pylons_app, 'flask_app': flask_app}) - - return app - - -def make_pylons_stack(conf, full_stack=True, static_files=True, **app_conf): - """Create a Pylons WSGI application and return it - - ``conf`` - The inherited configuration for this application. Normally from - the [DEFAULT] section of the Paste ini file. - - ``full_stack`` - Whether this application provides a full WSGI stack (by default, - meaning it handles its own exceptions and errors). Disable - full_stack when this application is "managed" by another WSGI - middleware. - - ``static_files`` - Whether this application serves its own static files; disable - when another web server is responsible for serving them. - - ``app_conf`` - The application's local configuration. Normally specified in - the [app:] section of the Paste ini file (where - defaults to main). - - """ - # Configure the Pylons environment - load_environment(conf, app_conf) - - # The Pylons WSGI app - app = PylonsApp() - # set pylons globals - app_globals.reset() - - for plugin in PluginImplementations(IMiddleware): - app = plugin.make_middleware(app, config) - - app = RootPathMiddleware(app, config) - - # Routing/Session/Cache Middleware - app = RoutesMiddleware(app, config['routes.map']) - # we want to be able to retrieve the routes middleware to be able to update - # the mapper. We store it in the pylons config to allow this. - config['routes.middleware'] = app - app = SessionMiddleware(app, config) - app = CacheMiddleware(app, config) - - # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) - # app = QueueLogMiddleware(app) - if asbool(config.get('ckan.use_pylons_response_cleanup_middleware', True)): - app = execute_on_completion(app, config, cleanup_pylons_response_string) - - # Fanstatic - if asbool(config.get('debug', False)): - fanstatic_config = { - 'versioning': True, - 'recompute_hashes': True, - 'minified': False, - 'bottom': True, - 'bundle': False, - } - else: - fanstatic_config = { - 'versioning': True, - 'recompute_hashes': False, - 'minified': True, - 'bottom': True, - 'bundle': True, - } - app = Fanstatic(app, **fanstatic_config) - - for plugin in PluginImplementations(IMiddleware): - try: - app = plugin.make_error_log_middleware(app, config) - except AttributeError: - log.critical('Middleware class {0} is missing the method' - 'make_error_log_middleware.'.format(plugin.__class__.__name__)) - - if asbool(full_stack): - # Handle Python exceptions - app = ErrorHandler(app, conf, **config['pylons.errorware']) - - # Display error documents for 400, 403, 404 status codes (and - # 500 when debug is disabled) - if asbool(config['debug']): - app = StatusCodeRedirect(app, [400, 403, 404]) - else: - app = StatusCodeRedirect(app, [400, 403, 404, 500]) - - # Initialize repoze.who - who_parser = WhoConfig(conf['here']) - who_parser.parse(open(app_conf['who.config_file'])) - - app = PluggableAuthenticationMiddleware( - app, - who_parser.identifiers, - who_parser.authenticators, - who_parser.challengers, - who_parser.mdproviders, - who_parser.request_classifier, - who_parser.challenge_decider, - logging.getLogger('repoze.who'), - logging.WARN, # ignored - who_parser.remote_user_key - ) - - # Establish the Registry for this application - app = RegistryManager(app) - - app = I18nMiddleware(app, config) - - if asbool(static_files): - # Serve static files - static_max_age = None if not asbool(config.get('ckan.cache_enabled')) \ - else int(config.get('ckan.static_max_age', 3600)) - - static_app = StaticURLParser(config['pylons.paths']['static_files'], - cache_max_age=static_max_age) - static_parsers = [static_app, app] - - storage_directory = uploader.get_storage_path() - if storage_directory: - path = os.path.join(storage_directory, 'storage') - try: - os.makedirs(path) - except OSError, e: - # errno 17 is file already exists - if e.errno != 17: - raise - - storage_app = StaticURLParser(path, cache_max_age=static_max_age) - static_parsers.insert(0, storage_app) - - # Configurable extra static file paths - extra_static_parsers = [] - for public_path in config.get('extra_public_paths', '').split(','): - if public_path.strip(): - extra_static_parsers.append( - StaticURLParser(public_path.strip(), - cache_max_age=static_max_age) - ) - app = Cascade(extra_static_parsers + static_parsers) - - # Page cache - if asbool(config.get('ckan.page_cache_enabled')): - app = PageCacheMiddleware(app, config) - - # Tracking - if asbool(config.get('ckan.tracking_enabled', 'false')): - app = TrackingMiddleware(app, config) - - return app - - -def make_flask_stack(conf): - """ This has to pass the flask app through all the same middleware that - Pylons used """ - - app = CKANFlask(__name__) - - @app.route('/hello', methods=['GET']) - def hello_world(): - return 'Hello World, this is served by Flask' - - @app.route('/hello', methods=['POST']) - def hello_world_post(): - return 'Hello World, this was posted to Flask' - - return app - - -class CKANFlask(Flask): - - '''Extend the Flask class with a special view to join the 'partyline' - established by AskAppDispatcherMiddleware. - - Also provide a 'can_handle_request' method. - ''' - - def __init__(self, import_name, *args, **kwargs): - super(CKANFlask, self).__init__(import_name, *args, **kwargs) - self.add_url_rule('/__invite__/', endpoint='partyline', - view_func=self.join_party) - self.partyline = None - self.partyline_connected = False - self.invitation_context = None - self.app_name = None # A label for the app handling this request - # (this app). - - def join_party(self, request=flask_request): - # Bootstrap, turn the view function into a 404 after registering. - if self.partyline_connected: - # This route does not exist at the HTTP level. - flask_abort(404) - self.invitation_context = _request_ctx_stack.top - self.partyline = request.environ.get(WSGIParty.partyline_key) - self.app_name = request.environ.get('partyline_handling_app') - self.partyline.connect('can_handle_request', self.can_handle_request) - self.partyline_connected = True - return 'ok' - - def can_handle_request(self, environ): - ''' - Decides whether it can handle a request with the Flask app by - matching the request environ against the route mapper - - Returns (True, 'flask_app') if this is the case. - ''' - - # TODO: identify matching urls as core or extension. This will depend - # on how we setup routing in Flask - - urls = self.url_map.bind_to_environ(environ) - try: - endpoint, args = urls.match() - log.debug('Flask route match, endpoint: {0}, args: {1}'.format( - endpoint, args)) - return (True, self.app_name) - except HTTPException: - raise HighAndDry() - - -class AskAppDispatcherMiddleware(WSGIParty): - - ''' - Establish a 'partyline' to each provided app. Select which app to call - by asking each if they can handle the requested path at PATH_INFO. - - Used to help transition from Pylons to Flask, and should be removed once - Pylons has been deprecated and all app requests are handled by Flask. - - Each app should handle a call to 'can_handle_request(environ)', responding - with a tuple: - (, , []) - where: - `bool` is True if the app can handle the payload url, - `app` is the wsgi app returning the answer - `origin` is an optional string to determine where in the app the url - will be handled, e.g. 'core' or 'extension'. - - Order of precedence if more than one app can handle a url: - Flask Extension > Pylons Extension > Flask Core > Pylons Core - ''' - - def __init__(self, apps=None, invites=(), ignore_missing_services=False): - # Dict of apps managed by this middleware {: , ...} - self.apps = apps or {} - - # A dict of service name => handler mappings. - self.handlers = {} - - # If True, suppress :class:`NoSuchServiceName` errors. Default: False. - self.ignore_missing_services = ignore_missing_services - - self.send_invitations(apps) - - def send_invitations(self, apps): - '''Call each app at the invite route to establish a partyline. Called - on init.''' - PATH = '/__invite__/' - for app_name, app in apps.items(): - environ = create_environ(path=PATH) - environ[self.partyline_key] = self.operator_class(self) - # A reference to the handling app. Used to id the app when - # responding to a handling request. - environ['partyline_handling_app'] = app_name - run_wsgi_app(app, environ) - - def __call__(self, environ, start_response): - '''Determine which app to call by asking each app if it can handle the - url and method defined on the eviron''' - # :::TODO::: Enforce order of precedence for dispatching to apps here. - - app_name = 'pylons_app' # currently defaulting to pylons app - answers = self.ask_around('can_handle_request', environ) - log.debug('Route support answers for {0} {1}: {2}'.format( - environ.get('REQUEST_METHOD'), environ.get('PATH_INFO'), - answers)) - available_handlers = [] - for answer in answers: - if len(answer) == 2: - can_handle, asked_app = answer - origin = 'core' - else: - can_handle, asked_app, origin = answer - if can_handle: - available_handlers.append('{0}_{1}'.format(asked_app, origin)) - - # Enforce order of precedence: - # Flask Extension > Pylons Extension > Flask Core > Pylons Core - if available_handlers: - if 'flask_app_extension' in available_handlers: - app_name = 'flask_app' - elif 'pylons_app_extension' in available_handlers: - app_name = 'pylons_app' - elif 'flask_app_core' in available_handlers: - app_name = 'flask_app' - - log.debug('Serving request via {0} app'.format(app_name)) - environ['ckan.app'] = app_name - return self.apps[app_name](environ, start_response) - - -class RootPathMiddleware(object): - ''' - Prevents the SCRIPT_NAME server variable conflicting with the ckan.root_url - config. The routes package uses the SCRIPT_NAME variable and appends to the - path and ckan addes the root url causing a duplication of the root path. - - This is a middleware to ensure that even redirects use this logic. - ''' - def __init__(self, app, config): - self.app = app - - def __call__(self, environ, start_response): - # Prevents the variable interfering with the root_path logic - if 'SCRIPT_NAME' in environ: - environ['SCRIPT_NAME'] = '' - - return self.app(environ, start_response) - - -class I18nMiddleware(object): - """I18n Middleware selects the language based on the url - eg /fr/home is French""" - def __init__(self, app, config): - self.app = app - self.default_locale = config.get('ckan.locale_default', 'en') - self.local_list = get_locales_from_config() - - def __call__(self, environ, start_response): - # strip the language selector from the requested url - # and set environ variables for the language selected - # CKAN_LANG is the language code eg en, fr - # CKAN_LANG_IS_DEFAULT is set to True or False - # CKAN_CURRENT_URL is set to the current application url - - # We only update once for a request so we can keep - # the language and original url which helps with 404 pages etc - if 'CKAN_LANG' not in environ: - path_parts = environ['PATH_INFO'].split('/') - if len(path_parts) > 1 and path_parts[1] in self.local_list: - environ['CKAN_LANG'] = path_parts[1] - environ['CKAN_LANG_IS_DEFAULT'] = False - # rewrite url - if len(path_parts) > 2: - environ['PATH_INFO'] = '/'.join([''] + path_parts[2:]) - else: - environ['PATH_INFO'] = '/' - else: - environ['CKAN_LANG'] = self.default_locale - environ['CKAN_LANG_IS_DEFAULT'] = True - - # Current application url - path_info = environ['PATH_INFO'] - # sort out weird encodings - path_info = '/'.join(urllib.quote(pce, '') for pce in path_info.split('/')) - - qs = environ.get('QUERY_STRING') - - if qs: - # sort out weird encodings - qs = urllib.quote(qs, '') - environ['CKAN_CURRENT_URL'] = '%s?%s' % (path_info, qs) - else: - environ['CKAN_CURRENT_URL'] = path_info - - return self.app(environ, start_response) - - -class PageCacheMiddleware(object): - ''' A simple page cache that can store and serve pages. It uses - Redis as storage. It caches pages that have a http status code of - 200, use the GET method. Only non-logged in users receive cached - pages. - Cachable pages are indicated by a environ CKAN_PAGE_CACHABLE - variable.''' - - def __init__(self, app, config): - self.app = app - import redis # only import if used - self.redis = redis # we need to reference this within the class - self.redis_exception = redis.exceptions.ConnectionError - self.redis_connection = None - - def __call__(self, environ, start_response): - - def _start_response(status, response_headers, exc_info=None): - # This wrapper allows us to get the status and headers. - environ['CKAN_PAGE_STATUS'] = status - environ['CKAN_PAGE_HEADERS'] = response_headers - return start_response(status, response_headers, exc_info) - - # Only use cache for GET requests - # REMOTE_USER is used by some tests. - if environ['REQUEST_METHOD'] != 'GET' or environ.get('REMOTE_USER'): - return self.app(environ, start_response) - - # If there is a ckan cookie (or auth_tkt) we avoid the cache. - # We want to allow other cookies like google analytics ones :( - cookie_string = environ.get('HTTP_COOKIE') - if cookie_string: - for cookie in cookie_string.split(';'): - if cookie.startswith('ckan') or cookie.startswith('auth_tkt'): - return self.app(environ, start_response) - - # Make our cache key - key = 'page:%s?%s' % (environ['PATH_INFO'], environ['QUERY_STRING']) - - # Try to connect if we don't have a connection. Doing this here - # allows the redis server to be unavailable at times. - if self.redis_connection is None: - try: - self.redis_connection = self.redis.StrictRedis() - self.redis_connection.flushdb() - except self.redis_exception: - # Connection may have failed at flush so clear it. - self.redis_connection = None - return self.app(environ, start_response) - - # If cached return cached result - try: - result = self.redis_connection.lrange(key, 0, 2) - except self.redis_exception: - # Connection failed so clear it and return the page as normal. - self.redis_connection = None - return self.app(environ, start_response) - - if result: - headers = json.loads(result[1]) - # Convert headers from list to tuples. - headers = [(str(key), str(value)) for key, value in headers] - start_response(str(result[0]), headers) - # Returning a huge string slows down the server. Therefore we - # cut it up into more usable chunks. - page = result[2] - out = [] - total = len(page) - position = 0 - size = 4096 - while position < total: - out.append(page[position:position + size]) - position += size - return out - - # Generate the response from our application. - page = self.app(environ, _start_response) - - # Only cache http status 200 pages - if not environ['CKAN_PAGE_STATUS'].startswith('200'): - return page - - cachable = False - if environ.get('CKAN_PAGE_CACHABLE'): - cachable = True - - # Cache things if cachable. - if cachable: - # Make sure we consume any file handles etc. - page_string = ''.join(list(page)) - # Use a pipe to add page in a transaction. - pipe = self.redis_connection.pipeline() - pipe.rpush(key, environ['CKAN_PAGE_STATUS']) - pipe.rpush(key, json.dumps(environ['CKAN_PAGE_HEADERS'])) - pipe.rpush(key, page_string) - pipe.execute() - return page - - -class TrackingMiddleware(object): - - def __init__(self, app, config): - self.app = app - self.engine = sa.create_engine(config.get('sqlalchemy.url')) - - def __call__(self, environ, start_response): - path = environ['PATH_INFO'] - method = environ.get('REQUEST_METHOD') - if path == '/_tracking' and method == 'POST': - # do the tracking - # get the post data - payload = environ['wsgi.input'].read() - parts = payload.split('&') - data = {} - for part in parts: - k, v = part.split('=') - data[k] = urllib2.unquote(v).decode("utf8") - start_response('200 OK', [('Content-Type', 'text/html')]) - # we want a unique anonomized key for each user so that we do - # not count multiple clicks from the same user. - key = ''.join([ - environ['HTTP_USER_AGENT'], - environ['REMOTE_ADDR'], - environ.get('HTTP_ACCEPT_LANGUAGE', ''), - environ.get('HTTP_ACCEPT_ENCODING', ''), - ]) - key = hashlib.md5(key).hexdigest() - # store key/data here - sql = '''INSERT INTO tracking_raw - (user_key, url, tracking_type) - VALUES (%s, %s, %s)''' - self.engine.execute(sql, key, data.get('url'), data.get('type')) - return [] - return self.app(environ, start_response) - - -def generate_close_and_callback(iterable, callback, environ): - """ - return a generator that passes through items from iterable - then calls callback(environ). - """ - try: - for item in iterable: - yield item - except GeneratorExit: - if hasattr(iterable, 'close'): - iterable.close() - raise - finally: - callback(environ) - - -def execute_on_completion(application, config, callback): - """ - Call callback(environ) once complete response is sent - """ - def inner(environ, start_response): - try: - result = application(environ, start_response) - except: - callback(environ) - raise - return generate_close_and_callback(result, callback, environ) - return inner - - -def cleanup_pylons_response_string(environ): - try: - msg = 'response cleared by pylons response cleanup middleware' - environ['pylons.controller']._py_object.response._body = msg - except (KeyError, AttributeError): - pass diff --git a/ckan/config/middleware/__init__.py b/ckan/config/middleware/__init__.py new file mode 100644 index 00000000000..41e58cc3840 --- /dev/null +++ b/ckan/config/middleware/__init__.py @@ -0,0 +1,135 @@ +# encoding: utf-8 + +"""WSGI app initialization""" + +import webob + +from werkzeug.test import create_environ, run_wsgi_app + +from ckan.config.environment import load_environment +from ckan.config.middleware.flask_app import make_flask_stack +from ckan.config.middleware.pylons_app import make_pylons_stack + +import logging +log = logging.getLogger(__name__) + +# This monkey-patches the webob request object because of the way it messes +# with the WSGI environ. + +# Start of webob.requests.BaseRequest monkey patch +original_charset__set = webob.request.BaseRequest._charset__set + + +def custom_charset__set(self, charset): + original_charset__set(self, charset) + if self.environ.get('CONTENT_TYPE', '').startswith(';'): + self.environ['CONTENT_TYPE'] = '' + +webob.request.BaseRequest._charset__set = custom_charset__set + +webob.request.BaseRequest.charset = property( + webob.request.BaseRequest._charset__get, + custom_charset__set, + webob.request.BaseRequest._charset__del, + webob.request.BaseRequest._charset__get.__doc__) + +# End of webob.requests.BaseRequest monkey patch + + +def make_app(conf, full_stack=True, static_files=True, **app_conf): + ''' + Initialise both the pylons and flask apps, and wrap them in dispatcher + middleware. + ''' + + load_environment(conf, app_conf) + + pylons_app = make_pylons_stack(conf, full_stack, static_files, + **app_conf) + flask_app = make_flask_stack(conf, **app_conf) + + app = AskAppDispatcherMiddleware({'pylons_app': pylons_app, + 'flask_app': flask_app}) + + return app + + +class AskAppDispatcherMiddleware(object): + + ''' + Dispatches incoming requests to either the Flask or Pylons apps depending + on the WSGI environ. + + Used to help transition from Pylons to Flask, and should be removed once + Pylons has been deprecated and all app requests are handled by Flask. + + Each app should handle a call to 'can_handle_request(environ)', responding + with a tuple: + (, , []) + where: + `bool` is True if the app can handle the payload url, + `app` is the wsgi app returning the answer + `origin` is an optional string to determine where in the app the url + will be handled, e.g. 'core' or 'extension'. + + Order of precedence if more than one app can handle a url: + Flask Extension > Pylons Extension > Flask Core > Pylons Core + ''' + + def __init__(self, apps=None): + # Dict of apps managed by this middleware {: , ...} + self.apps = apps or {} + + def ask_around(self, environ): + '''Checks with all apps whether they can handle the incoming request + ''' + answers = [ + app._wsgi_app.can_handle_request(environ) + for name, app in self.apps.iteritems() + ] + # Sort answers by app name + answers = sorted(answers, key=lambda x: x[1]) + log.debug('Route support answers for {0} {1}: {2}'.format( + environ.get('REQUEST_METHOD'), environ.get('PATH_INFO'), + answers)) + + return answers + + def __call__(self, environ, start_response): + '''Determine which app to call by asking each app if it can handle the + url and method defined on the eviron''' + + app_name = 'pylons_app' # currently defaulting to pylons app + answers = self.ask_around(environ) + available_handlers = [] + for answer in answers: + if len(answer) == 2: + can_handle, asked_app = answer + origin = 'core' + else: + can_handle, asked_app, origin = answer + if can_handle: + available_handlers.append('{0}_{1}'.format(asked_app, origin)) + + # Enforce order of precedence: + # Flask Extension > Pylons Extension > Flask Core > Pylons Core + if available_handlers: + if 'flask_app_extension' in available_handlers: + app_name = 'flask_app' + elif 'pylons_app_extension' in available_handlers: + app_name = 'pylons_app' + elif 'flask_app_core' in available_handlers: + app_name = 'flask_app' + + log.debug('Serving request via {0} app'.format(app_name)) + environ['ckan.app'] = app_name + if app_name == 'flask_app': + return self.apps[app_name](environ, start_response) + else: + # Although this request will be served by Pylons we still + # need an application context in order for the Flask URL + # builder to work and to be able to access the Flask config + flask_app = self.apps['flask_app']._wsgi_app + + with flask_app.app_context(): + return self.apps[app_name](environ, start_response) diff --git a/ckan/config/middleware/common_middleware.py b/ckan/config/middleware/common_middleware.py new file mode 100644 index 00000000000..b141b0f3621 --- /dev/null +++ b/ckan/config/middleware/common_middleware.py @@ -0,0 +1,216 @@ +# encoding: utf-8 + +"""Common middleware used by both Flask and Pylons app stacks.""" + +import urllib2 +import hashlib +import urllib +import json + +import sqlalchemy as sa + +from ckan.lib.i18n import get_locales_from_config + + +class I18nMiddleware(object): + """I18n Middleware selects the language based on the url + eg /fr/home is French""" + def __init__(self, app, config): + self.app = app + self.default_locale = config.get('ckan.locale_default', 'en') + self.local_list = get_locales_from_config() + + def __call__(self, environ, start_response): + # strip the language selector from the requested url + # and set environ variables for the language selected + # CKAN_LANG is the language code eg en, fr + # CKAN_LANG_IS_DEFAULT is set to True or False + # CKAN_CURRENT_URL is set to the current application url + + # We only update once for a request so we can keep + # the language and original url which helps with 404 pages etc + if 'CKAN_LANG' not in environ: + path_parts = environ['PATH_INFO'].split('/') + if len(path_parts) > 1 and path_parts[1] in self.local_list: + environ['CKAN_LANG'] = path_parts[1] + environ['CKAN_LANG_IS_DEFAULT'] = False + # rewrite url + if len(path_parts) > 2: + environ['PATH_INFO'] = '/'.join([''] + path_parts[2:]) + else: + environ['PATH_INFO'] = '/' + else: + environ['CKAN_LANG'] = self.default_locale + environ['CKAN_LANG_IS_DEFAULT'] = True + + # Current application url + path_info = environ['PATH_INFO'] + # sort out weird encodings + path_info = \ + '/'.join(urllib.quote(pce, '') for pce in path_info.split('/')) + + qs = environ.get('QUERY_STRING') + + if qs: + # sort out weird encodings + qs = urllib.quote(qs, '') + environ['CKAN_CURRENT_URL'] = '%s?%s' % (path_info, qs) + else: + environ['CKAN_CURRENT_URL'] = path_info + + return self.app(environ, start_response) + + +class RootPathMiddleware(object): + ''' + Prevents the SCRIPT_NAME server variable conflicting with the ckan.root_url + config. The routes package uses the SCRIPT_NAME variable and appends to the + path and ckan addes the root url causing a duplication of the root path. + + This is a middleware to ensure that even redirects use this logic. + ''' + def __init__(self, app, config): + self.app = app + + def __call__(self, environ, start_response): + # Prevents the variable interfering with the root_path logic + if 'SCRIPT_NAME' in environ: + environ['SCRIPT_NAME'] = '' + + return self.app(environ, start_response) + + +class PageCacheMiddleware(object): + ''' A simple page cache that can store and serve pages. It uses + Redis as storage. It caches pages that have a http status code of + 200, use the GET method. Only non-logged in users receive cached + pages. + Cachable pages are indicated by a environ CKAN_PAGE_CACHABLE + variable.''' + + def __init__(self, app, config): + self.app = app + import redis # only import if used + self.redis = redis # we need to reference this within the class + self.redis_exception = redis.exceptions.ConnectionError + self.redis_connection = None + + def __call__(self, environ, start_response): + + def _start_response(status, response_headers, exc_info=None): + # This wrapper allows us to get the status and headers. + environ['CKAN_PAGE_STATUS'] = status + environ['CKAN_PAGE_HEADERS'] = response_headers + return start_response(status, response_headers, exc_info) + + # Only use cache for GET requests + # REMOTE_USER is used by some tests. + if environ['REQUEST_METHOD'] != 'GET' or environ.get('REMOTE_USER'): + return self.app(environ, start_response) + + # If there is a ckan cookie (or auth_tkt) we avoid the cache. + # We want to allow other cookies like google analytics ones :( + cookie_string = environ.get('HTTP_COOKIE') + if cookie_string: + for cookie in cookie_string.split(';'): + if cookie.startswith('ckan') or cookie.startswith('auth_tkt'): + return self.app(environ, start_response) + + # Make our cache key + key = 'page:%s?%s' % (environ['PATH_INFO'], environ['QUERY_STRING']) + + # Try to connect if we don't have a connection. Doing this here + # allows the redis server to be unavailable at times. + if self.redis_connection is None: + try: + self.redis_connection = self.redis.StrictRedis() + self.redis_connection.flushdb() + except self.redis_exception: + # Connection may have failed at flush so clear it. + self.redis_connection = None + return self.app(environ, start_response) + + # If cached return cached result + try: + result = self.redis_connection.lrange(key, 0, 2) + except self.redis_exception: + # Connection failed so clear it and return the page as normal. + self.redis_connection = None + return self.app(environ, start_response) + + if result: + headers = json.loads(result[1]) + # Convert headers from list to tuples. + headers = [(str(key), str(value)) for key, value in headers] + start_response(str(result[0]), headers) + # Returning a huge string slows down the server. Therefore we + # cut it up into more usable chunks. + page = result[2] + out = [] + total = len(page) + position = 0 + size = 4096 + while position < total: + out.append(page[position:position + size]) + position += size + return out + + # Generate the response from our application. + page = self.app(environ, _start_response) + + # Only cache http status 200 pages + if not environ['CKAN_PAGE_STATUS'].startswith('200'): + return page + + cachable = False + if environ.get('CKAN_PAGE_CACHABLE'): + cachable = True + + # Cache things if cachable. + if cachable: + # Make sure we consume any file handles etc. + page_string = ''.join(list(page)) + # Use a pipe to add page in a transaction. + pipe = self.redis_connection.pipeline() + pipe.rpush(key, environ['CKAN_PAGE_STATUS']) + pipe.rpush(key, json.dumps(environ['CKAN_PAGE_HEADERS'])) + pipe.rpush(key, page_string) + pipe.execute() + return page + + +class TrackingMiddleware(object): + + def __init__(self, app, config): + self.app = app + self.engine = sa.create_engine(config.get('sqlalchemy.url')) + + def __call__(self, environ, start_response): + path = environ['PATH_INFO'] + method = environ.get('REQUEST_METHOD') + if path == '/_tracking' and method == 'POST': + # do the tracking + # get the post data + payload = environ['wsgi.input'].read() + parts = payload.split('&') + data = {} + for part in parts: + k, v = part.split('=') + data[k] = urllib2.unquote(v).decode("utf8") + start_response('200 OK', [('Content-Type', 'text/html')]) + # we want a unique anonomized key for each user so that we do + # not count multiple clicks from the same user. + key = ''.join([ + environ['HTTP_USER_AGENT'], + environ['REMOTE_ADDR'], + environ.get('HTTP_ACCEPT_LANGUAGE', ''), + environ.get('HTTP_ACCEPT_ENCODING', ''), + ]) + key = hashlib.md5(key).hexdigest() + # store key/data here + sql = '''INSERT INTO tracking_raw + (user_key, url, tracking_type) + VALUES (%s, %s, %s)''' + self.engine.execute(sql, key, data.get('url'), data.get('type')) + return [] + return self.app(environ, start_response) diff --git a/ckan/config/middleware/flask_app.py b/ckan/config/middleware/flask_app.py new file mode 100644 index 00000000000..2bbb4939354 --- /dev/null +++ b/ckan/config/middleware/flask_app.py @@ -0,0 +1,73 @@ +# encoding: utf-8 + +from flask import Flask +from werkzeug.exceptions import HTTPException + +from ckan.common import config + + +import logging +log = logging.getLogger(__name__) + + +def make_flask_stack(conf, **app_conf): + """ This has to pass the flask app through all the same middleware that + Pylons used """ + + app = flask_app = CKANFlask(__name__) + + # Update Flask config with the CKAN values. We use the common config + # object as values might have been modified on `load_environment` + if config: + app.config.update(config) + else: + app.config.update(conf) + app.config.update(app_conf) + + @app.route('/hello', methods=['GET']) + def hello_world(): + return 'Hello World, this is served by Flask' + + @app.route('/hello', methods=['POST']) + def hello_world_post(): + return 'Hello World, this was posted to Flask' + + # Update the main CKAN config object with the Flask specific keys + # that were set here or autogenerated + flask_config_keys = set(flask_app.config.keys()) - set(config.keys()) + for key in flask_config_keys: + config[key] = flask_app.config[key] + + # Add a reference to the actual Flask app so it's easier to access + app._wsgi_app = flask_app + + return app + + +class CKANFlask(Flask): + + '''Extend the Flask class with a special method called on incoming + requests by AskAppDispatcherMiddleware. + ''' + + app_name = 'flask_app' + + def can_handle_request(self, environ): + ''' + Decides whether it can handle a request with the Flask app by + matching the request environ against the route mapper + + Returns (True, 'flask_app') if this is the case. + ''' + + # TODO: identify matching urls as core or extension. This will depend + # on how we setup routing in Flask + + urls = self.url_map.bind_to_environ(environ) + try: + endpoint, args = urls.match() + log.debug('Flask route match, endpoint: {0}, args: {1}'.format( + endpoint, args)) + return (True, self.app_name) + except HTTPException: + return (False, self.app_name) diff --git a/ckan/config/middleware/pylons_app.py b/ckan/config/middleware/pylons_app.py new file mode 100644 index 00000000000..5a1218d4b4f --- /dev/null +++ b/ckan/config/middleware/pylons_app.py @@ -0,0 +1,258 @@ +# encoding: utf-8 + +import os + +from pylons.wsgiapp import PylonsApp + +from beaker.middleware import CacheMiddleware, SessionMiddleware +from paste.cascade import Cascade +from paste.registry import RegistryManager +from paste.urlparser import StaticURLParser +from paste.deploy.converters import asbool +from pylons.middleware import ErrorHandler, StatusCodeRedirect +from routes.middleware import RoutesMiddleware +from repoze.who.config import WhoConfig +from repoze.who.middleware import PluggableAuthenticationMiddleware +from fanstatic import Fanstatic + +from ckan.plugins import PluginImplementations +from ckan.plugins.interfaces import IMiddleware +import ckan.lib.uploader as uploader +from ckan.config.middleware import common_middleware +from ckan.common import config + +import logging +log = logging.getLogger(__name__) + + +def make_pylons_stack(conf, full_stack=True, static_files=True, + **app_conf): + """Create a Pylons WSGI application and return it + + ``conf`` + The inherited configuration for this application. Normally from + the [DEFAULT] section of the Paste ini file. + + ``full_stack`` + Whether this application provides a full WSGI stack (by default, + meaning it handles its own exceptions and errors). Disable + full_stack when this application is "managed" by another WSGI + middleware. + + ``static_files`` + Whether this application serves its own static files; disable + when another web server is responsible for serving them. + + ``app_conf`` + The application's local configuration. Normally specified in + the [app:] section of the Paste ini file (where + defaults to main). + + """ + # The Pylons WSGI app + app = pylons_app = CKANPylonsApp() + + for plugin in PluginImplementations(IMiddleware): + app = plugin.make_middleware(app, config) + + # Routing/Session/Cache Middleware + app = RoutesMiddleware(app, config['routes.map']) + # we want to be able to retrieve the routes middleware to be able to update + # the mapper. We store it in the pylons config to allow this. + config['routes.middleware'] = app + app = SessionMiddleware(app, config) + app = CacheMiddleware(app, config) + + # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) + # app = QueueLogMiddleware(app) + if asbool(config.get('ckan.use_pylons_response_cleanup_middleware', + True)): + app = execute_on_completion(app, config, + cleanup_pylons_response_string) + + # Fanstatic + if asbool(config.get('debug', False)): + fanstatic_config = { + 'versioning': True, + 'recompute_hashes': True, + 'minified': False, + 'bottom': True, + 'bundle': False, + } + else: + fanstatic_config = { + 'versioning': True, + 'recompute_hashes': False, + 'minified': True, + 'bottom': True, + 'bundle': True, + } + app = Fanstatic(app, **fanstatic_config) + + for plugin in PluginImplementations(IMiddleware): + try: + app = plugin.make_error_log_middleware(app, config) + except AttributeError: + log.critical('Middleware class {0} is missing the method' + 'make_error_log_middleware.' + .format(plugin.__class__.__name__)) + + if asbool(full_stack): + # Handle Python exceptions + app = ErrorHandler(app, conf, **config['pylons.errorware']) + + # Display error documents for 400, 403, 404 status codes (and + # 500 when debug is disabled) + if asbool(config['debug']): + app = StatusCodeRedirect(app, [400, 403, 404]) + else: + app = StatusCodeRedirect(app, [400, 403, 404, 500]) + + # Initialize repoze.who + who_parser = WhoConfig(conf['here']) + who_parser.parse(open(app_conf['who.config_file'])) + + app = PluggableAuthenticationMiddleware( + app, + who_parser.identifiers, + who_parser.authenticators, + who_parser.challengers, + who_parser.mdproviders, + who_parser.request_classifier, + who_parser.challenge_decider, + logging.getLogger('repoze.who'), + logging.WARN, # ignored + who_parser.remote_user_key + ) + + # Establish the Registry for this application + app = RegistryManager(app) + + app = common_middleware.I18nMiddleware(app, config) + + if asbool(static_files): + # Serve static files + static_max_age = None if not asbool( + config.get('ckan.cache_enabled')) \ + else int(config.get('ckan.static_max_age', 3600)) + + static_app = StaticURLParser( + config['pylons.paths']['static_files'], + cache_max_age=static_max_age) + static_parsers = [static_app, app] + + storage_directory = uploader.get_storage_path() + if storage_directory: + path = os.path.join(storage_directory, 'storage') + try: + os.makedirs(path) + except OSError, e: + # errno 17 is file already exists + if e.errno != 17: + raise + + storage_app = StaticURLParser(path, cache_max_age=static_max_age) + static_parsers.insert(0, storage_app) + + # Configurable extra static file paths + extra_static_parsers = [] + for public_path in config.get( + 'extra_public_paths', '').split(','): + if public_path.strip(): + extra_static_parsers.append( + StaticURLParser(public_path.strip(), + cache_max_age=static_max_age) + ) + app = Cascade(extra_static_parsers + static_parsers) + + # Page cache + if asbool(config.get('ckan.page_cache_enabled')): + app = common_middleware.PageCacheMiddleware(app, config) + + # Tracking + if asbool(config.get('ckan.tracking_enabled', 'false')): + app = common_middleware.TrackingMiddleware(app, config) + + app = common_middleware.RootPathMiddleware(app, config) + + # Add a reference to the actual Pylons app so it's easier to access + app._wsgi_app = pylons_app + + return app + + +class CKANPylonsApp(PylonsApp): + + app_name = 'pylons_app' + + def can_handle_request(self, environ): + ''' + Decides whether it can handle a request with the Pylons app by + matching the request environ against the route mapper + + Returns (True, 'pylons_app', origin) if this is the case. + + origin can be either 'core' or 'extension' depending on where + the route was defined. + + NOTE: There is currently a catch all route for GET requests to + point arbitrary urls to templates with the same name: + + map.connect('/*url', controller='template', action='view') + + This means that this function will match all GET requests. This + does not cause issues as the Pylons core routes are the last to + take precedence so the current behaviour is kept, but it's worth + keeping in mind. + ''' + + pylons_mapper = config['routes.map'] + match_route = pylons_mapper.routematch(environ=environ) + if match_route: + match, route = match_route + origin = 'core' + if hasattr(route, '_ckan_core') and not route._ckan_core: + origin = 'extension' + log.debug('Pylons route match: {0} Origin: {1}'.format( + match, origin)) + return (True, self.app_name, origin) + else: + return (False, self.app_name) + + +def generate_close_and_callback(iterable, callback, environ): + """ + return a generator that passes through items from iterable + then calls callback(environ). + """ + try: + for item in iterable: + yield item + except GeneratorExit: + if hasattr(iterable, 'close'): + iterable.close() + raise + finally: + callback(environ) + + +def execute_on_completion(application, config, callback): + """ + Call callback(environ) once complete response is sent + """ + def inner(environ, start_response): + try: + result = application(environ, start_response) + except: + callback(environ) + raise + return generate_close_and_callback(result, callback, environ) + return inner + + +def cleanup_pylons_response_string(environ): + try: + msg = 'response cleared by pylons response cleanup middleware' + environ['pylons.controller']._py_object.response._body = msg + except (KeyError, AttributeError): + pass diff --git a/ckan/config/resource_formats.json b/ckan/config/resource_formats.json index fad65202015..d4749a99b1f 100644 --- a/ckan/config/resource_formats.json +++ b/ckan/config/resource_formats.json @@ -69,7 +69,7 @@ ["TAR", "TAR Compressed File", "application/x-tar", []], ["PNG", "PNG Image File", "image/png", []], ["RSS", "RSS feed", "application/rss+xml", []], - ["GeoJSON", "Geographic JavaScript Object Notation", null, []], + ["GeoJSON", "Geographic JavaScript Object Notation", "application/geo+json", ["geojson"]], ["SHP", "Shapefile", null, ["esri shapefile"]], ["TORRENT", "Torrent", "application/x-bittorrent", ["bittorrent"]], ["ICS", "iCalendar", "text/calendar", ["ifb", "iCal"]], diff --git a/ckan/config/routing.py b/ckan/config/routing.py index c5f12df9f7c..03aa50f625b 100644 --- a/ckan/config/routing.py +++ b/ckan/config/routing.py @@ -9,10 +9,10 @@ """ import re -from pylons import config from routes.mapper import SubMapper, Mapper as _Mapper import ckan.plugins as p +from ckan.common import config named_routes = {} diff --git a/ckan/config/solr/schema-1.2.xml b/ckan/config/solr/schema-1.2.xml deleted file mode 100644 index 4f9b11a580b..00000000000 --- a/ckan/config/solr/schema-1.2.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -id -text - - - - - - - - - - - - - - - - - - - - diff --git a/ckan/config/solr/schema-1.3.xml b/ckan/config/solr/schema-1.3.xml deleted file mode 100644 index 89fea815356..00000000000 --- a/ckan/config/solr/schema-1.3.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -index_id -text - - - - - - - - - - - - - - - - - - - - diff --git a/ckan/config/solr/schema-1.4.xml b/ckan/config/solr/schema-1.4.xml deleted file mode 100644 index 98cbf378dbe..00000000000 --- a/ckan/config/solr/schema-1.4.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -index_id -text - - - - - - - - - - - - - - - - - - - - - diff --git a/ckan/config/solr/schema-2.0.xml b/ckan/config/solr/schema-2.0.xml deleted file mode 100644 index b306bb5d8c7..00000000000 --- a/ckan/config/solr/schema-2.0.xml +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -index_id -text - - - - - - - - - - - - - - - - - - - - - diff --git a/ckan/config/solr/schema.xml b/ckan/config/solr/schema.xml index e8893f70ff9..4578c164b10 100644 --- a/ckan/config/solr/schema.xml +++ b/ckan/config/solr/schema.xml @@ -24,7 +24,7 @@ - + @@ -112,6 +112,7 @@ schema. In this case the version should be set to the next CKAN version number. + diff --git a/ckan/config/supervisor-ckan-worker.conf b/ckan/config/supervisor-ckan-worker.conf new file mode 100644 index 00000000000..1cf2ffb460a --- /dev/null +++ b/ckan/config/supervisor-ckan-worker.conf @@ -0,0 +1,43 @@ +; ======================================================= +; Supervisor configuration for CKAN background job worker +; ======================================================= + +; 1. Copy this file to /etc/supervisr/conf.d +; 2. Make sure the paths below match your setup + + +[program:ckan-worker] + +; Use the full paths to the virtualenv and your configuration file here. +command=/usr/lib/ckan/default/bin/paster --plugin=ckan jobs worker --config=/etc/ckan/default/production.ini + + +; User the worker runs as. +user=www-data + + +; Start just a single worker. Increase this number if you have many or +; particularly long running background jobs. +numprocs=1 +process_name=%(program_name)s-%(process_num)02d + + +; Log files. +stdout_logfile=/var/log/ckan-worker.log +stderr_logfile=/var/log/ckan-worker.log + + +; Make sure that the worker is started on system start and automatically +; restarted if it crashes unexpectedly. +autostart=true +autorestart=true + + +; Number of seconds the process has to run before it is considered to have +; started successfully. +startsecs=10 + +; Need to wait for currently executing tasks to finish at shutdown. +; Increase this if you have very long running tasks. +stopwaitsecs = 600 + diff --git a/ckan/controllers/admin.py b/ckan/controllers/admin.py index d6768d964c9..18eccd16163 100644 --- a/ckan/controllers/admin.py +++ b/ckan/controllers/admin.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config +from ckan.common import config import ckan.lib.base as base import ckan.lib.helpers as h diff --git a/ckan/controllers/api.py b/ckan/controllers/api.py index ec82a4d0a82..dcda810d806 100644 --- a/ckan/controllers/api.py +++ b/ckan/controllers/api.py @@ -91,9 +91,9 @@ def _finish(self, status_int, response_data=None, else: response_msg = response_data # Support "JSONP" callback. - if status_int == 200 and 'callback' in request.params and \ - (request.method == 'GET' or - c.logic_function and request.method == 'POST'): + if (status_int == 200 and + 'callback' in request.params and + request.method == 'GET'): # escape callback to remove '<', '&', '>' chars callback = cgi.escape(request.params['callback']) response_msg = self._wrap_jsonp(callback, response_msg) diff --git a/ckan/controllers/feed.py b/ckan/controllers/feed.py index 0a772fddfaf..ff0efa50000 100644 --- a/ckan/controllers/feed.py +++ b/ckan/controllers/feed.py @@ -25,7 +25,7 @@ import urlparse import webhelpers.feedgenerator -from pylons import config +from ckan.common import config import ckan.model as model import ckan.lib.base as base @@ -171,12 +171,17 @@ def _alternate_url(self, params, **kwargs): def _group_or_organization(self, obj_dict, is_org): data_dict, params = self._parse_url_params() - key = 'owner_org' if is_org else 'groups' - data_dict['fq'] = '%s:"%s"' % (key, obj_dict['id'],) - group_type = 'organization' - if not is_org: + if is_org: + key = 'owner_org' + value = obj_dict['id'] + group_type = 'organization' + else: + key = 'groups' + value = obj_dict['name'] group_type = 'group' + data_dict['fq'] = '{0}:"{1}"'.format(key, value) + item_count, results = _package_search(data_dict) navigation_urls = self._navigation_urls(params, @@ -310,7 +315,7 @@ def custom(self): search_params[param] = value fq += ' %s:"%s"' % (param, value) - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) limit = ITEMS_LIMIT data_dict = { @@ -470,7 +475,7 @@ def _parse_url_params(self): Returns the constructed search-query dict, and the valid URL query parameters. """ - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) limit = ITEMS_LIMIT data_dict = { diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index bd353537aaa..087209f07c4 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -150,7 +150,7 @@ def add_group_type(cls, group_type): def index(self): group_type = self._guess_group_type() - page = self._get_page_number(request.params) or 1 + page = h.get_page_number(request.params) or 1 items_per_page = 21 context = {'model': model, 'session': model.Session, @@ -247,7 +247,7 @@ def _read(self, id, limit, group_type): context['return_query'] = True - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) # most search operations should reset the page counter: params_nopage = [(k, v) for k, v in request.params.items() @@ -294,13 +294,12 @@ def pager_url(q=None, page=None): else: search_extras[param] = value - fq = 'capacity:"public"' + include_private = False user_member_of_orgs = [org['id'] for org in h.organizations_available('read')] if (c.group and c.group.id in user_member_of_orgs): - fq = '' - context['ignore_capacity_check'] = True + include_private = True facets = OrderedDict() @@ -327,7 +326,8 @@ def pager_url(q=None, page=None): data_dict = { 'q': q, - 'fq': fq, + 'fq': '', + 'include_private': include_private, 'facet.field': facets.keys(), 'rows': limit, 'sort': sort_by, diff --git a/ckan/controllers/home.py b/ckan/controllers/home.py index 4a8cdbb26d4..e1bdfadce9f 100644 --- a/ckan/controllers/home.py +++ b/ckan/controllers/home.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config, cache +from pylons import cache import sqlalchemy.exc import ckan.logic as logic diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py index 5b41aa95e81..c89a999e9bc 100644 --- a/ckan/controllers/package.py +++ b/ckan/controllers/package.py @@ -6,7 +6,7 @@ import mimetypes import cgi -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool import paste.fileapp @@ -30,7 +30,6 @@ render = base.render abort = base.abort -redirect = base.redirect NotFound = logic.NotFound NotAuthorized = logic.NotAuthorized @@ -146,7 +145,7 @@ def search(self): # unicode format (decoded from utf8) q = c.q = request.params.get('q', u'') c.query_error = False - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) limit = g.datasets_per_page @@ -264,7 +263,9 @@ def pager_url(q=None, page=None): 'rows': limit, 'start': (page - 1) * limit, 'sort': sort_by, - 'extras': search_extras + 'extras': search_extras, + 'include_private': asbool(config.get( + 'ckan.search.default_include_private', True)), } query = get_action('package_search')(context, data_dict) @@ -573,8 +574,8 @@ def resource_edit(self, id, resource_id, data=None, errors=None, errors, error_summary) except NotAuthorized: abort(403, _('Unauthorized to edit this resource')) - redirect(h.url_for(controller='package', action='resource_read', - id=id, resource_id=resource_id)) + h.redirect_to(controller='package', action='resource_read', id=id, + resource_id=resource_id) context = {'model': model, 'session': model.Session, 'api_version': 3, 'for_edit': True, @@ -643,8 +644,7 @@ def new_resource(self, id, data=None, errors=None, error_summary=None): if not data_provided and save_action != "go-dataset-complete": if save_action == 'go-dataset': # go to final stage of adddataset - redirect(h.url_for(controller='package', - action='edit', id=id)) + h.redirect_to(controller='package', action='edit', id=id) # see if we have added any resources try: data_dict = get_action('package_show')(context, {'id': id}) @@ -659,8 +659,8 @@ def new_resource(self, id, data=None, errors=None, error_summary=None): # On new templates do not use flash message if g.legacy_templates: h.flash_error(msg) - redirect(h.url_for(controller='package', - action='new_resource', id=id)) + h.redirect_to(controller='package', + action='new_resource', id=id) else: errors = {} error_summary = {_('Error'): msg} @@ -671,8 +671,7 @@ def new_resource(self, id, data=None, errors=None, error_summary=None): get_action('package_update')( dict(context, allow_state_change=True), dict(data_dict, state='active')) - redirect(h.url_for(controller='package', - action='read', id=id)) + h.redirect_to(controller='package', action='read', id=id) data['package_id'] = id try: @@ -696,20 +695,17 @@ def new_resource(self, id, data=None, errors=None, error_summary=None): get_action('package_update')( dict(context, allow_state_change=True), dict(data_dict, state='active')) - redirect(h.url_for(controller='package', - action='read', id=id)) + h.redirect_to(controller='package', action='read', id=id) elif save_action == 'go-dataset': # go to first stage of add dataset - redirect(h.url_for(controller='package', - action='edit', id=id)) + h.redirect_to(controller='package', action='edit', id=id) elif save_action == 'go-dataset-complete': # go to first stage of add dataset - redirect(h.url_for(controller='package', - action='read', id=id)) + h.redirect_to(controller='package', action='read', id=id) else: # add more resources - redirect(h.url_for(controller='package', - action='new_resource', id=id)) + h.redirect_to(controller='package', action='new_resource', + id=id) # get resources for sidebar context = {'model': model, 'session': model.Session, @@ -904,7 +900,7 @@ def _save_new(self, context, package_type=None): url = h.url_for(controller='package', action='new_resource', id=pkg_dict['name']) - redirect(url) + h.redirect_to(url) # Make sure we don't index this dataset if request.params['save'] not in ['go-resource', 'go-metadata']: @@ -921,7 +917,7 @@ def _save_new(self, context, package_type=None): url = h.url_for(controller='package', action='new_resource', id=pkg_dict['name']) - redirect(url) + h.redirect_to(url) self._form_save_redirect(pkg_dict['name'], 'new', package_type=package_type) @@ -1008,7 +1004,7 @@ def _form_save_redirect(self, pkgname, action, package_type=None): id=pkgname) else: url = h.url_for('{0}_read'.format(package_type), id=pkgname) - redirect(url) + h.redirect_to(url) def delete(self, id): @@ -1163,7 +1159,7 @@ def resource_download(self, id, resource_id, filename=None): return app_iter elif not 'url' in rsc: abort(404, _('No download is available')) - redirect(rsc['url']) + h.redirect_to(rsc['url']) def follow(self, id): '''Start following this dataset.''' @@ -1262,8 +1258,7 @@ def groups(self, id): get_action('member_delete')(context, data_dict) except NotFound: abort(404, _('Group not found')) - redirect(h.url_for(controller='package', - action='groups', id=id)) + h.redirect_to(controller='package', action='groups', id=id) context['is_member'] = True users_groups = get_action('group_list_authz')(context, data_dict) @@ -1475,9 +1470,9 @@ def edit_view(self, id, resource_id, view_id=None): abort(403, _('Unauthorized to edit resource')) else: if not to_preview: - redirect(h.url_for(controller='package', - action='resource_views', - id=id, resource_id=resource_id)) + h.redirect_to(controller='package', + action='resource_views', + id=id, resource_id=resource_id) ## view_id exists only when updating if view_id: diff --git a/ckan/controllers/partyline.py b/ckan/controllers/partyline.py deleted file mode 100644 index 30dbf6edfba..00000000000 --- a/ckan/controllers/partyline.py +++ /dev/null @@ -1,66 +0,0 @@ -# encoding: utf-8 - -from pylons.controllers import WSGIController -from pylons import config - -import ckan.lib.base as base -from ckan.common import request - -from wsgi_party import WSGIParty, HighAndDry - -import logging -log = logging.getLogger(__name__) - - -class PartylineController(WSGIController): - - '''Handle requests from the WSGI stack 'partyline'. Most importantly, - answers the question, 'can you handle this url?'. ''' - - def __init__(self, *args, **kwargs): - super(PartylineController, self).__init__(*args, **kwargs) - self.app_name = None # A reference to the main pylons app. - self.partyline_connected = False - - def join_party(self): - if self.partyline_connected: - base.abort(404) - self.partyline = request.environ.get(WSGIParty.partyline_key) - self.app_name = request.environ.get('partyline_handling_app') - self.partyline.connect('can_handle_request', self.can_handle_request) - self.partyline_connected = True - return 'ok' - - def can_handle_request(self, environ): - ''' - Decides whether it can handle a request with the Pylons app by - matching the request environ against the route mapper - - Returns (True, 'pylons_app', origin) if this is the case. - - origin can be either 'core' or 'extension' depending on where - the route was defined. - - NOTE: There is currently a catch all route for GET requests to - point arbitrary urls to templates with the same name: - - map.connect('/*url', controller='template', action='view') - - This means that this function will match all GET requests. This - does not cause issues as the Pylons core routes are the last to - take precedence so the current behaviour is kept, but it's worth - keeping in mind. - ''' - - pylons_mapper = config['routes.map'] - match_route = pylons_mapper.routematch(environ=environ) - if match_route: - match, route = match_route - origin = 'core' - if hasattr(route, '_ckan_core') and not route._ckan_core: - origin = 'extension' - log.debug('Pylons route match: {0} Origin: {1}'.format( - match, origin)) - return (True, self.app_name, origin) - else: - raise HighAndDry() diff --git a/ckan/controllers/revision.py b/ckan/controllers/revision.py index 2c5962f943b..dd3527cc4ea 100644 --- a/ckan/controllers/revision.py +++ b/ckan/controllers/revision.py @@ -124,7 +124,7 @@ def list(self): query = model.Session.query(model.Revision) c.page = h.Page( collection=query, - page=self._get_page_number(request.params), + page=h.get_page_number(request.params), url=h.pager_url, items_per_page=20 ) diff --git a/ckan/controllers/tag.py b/ckan/controllers/tag.py index 67b761af444..e30b600fdb4 100644 --- a/ckan/controllers/tag.py +++ b/ckan/controllers/tag.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool import ckan.logic as logic @@ -36,7 +36,7 @@ def index(self): data_dict = {'all_fields': True} if c.q: - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) data_dict['q'] = c.q data_dict['limit'] = LIMIT data_dict['offset'] = (page - 1) * LIMIT diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index 110c27841be..ebc305d19f0 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -3,7 +3,7 @@ import logging from urllib import quote -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool import ckan.lib.base as base @@ -97,7 +97,7 @@ def _get_repoze_handler(self, handler_name): handler_name) def index(self): - page = self._get_page_number(request.params) + page = h.get_page_number(request.params) c.q = request.params.get('q', '') c.order_by = request.params.get('order_by', 'name') @@ -166,7 +166,8 @@ def new(self, data=None, errors=None, error_summary=None): '''GET to display a form for registering a new user. or POST the form data to actually do the user registration. ''' - context = {'model': model, 'session': model.Session, + context = {'model': model, + 'session': model.Session, 'user': c.user, 'auth_user_obj': c.userobj, 'schema': self._new_form_to_db_schema(), @@ -180,7 +181,7 @@ def new(self, data=None, errors=None, error_summary=None): if context['save'] and not data: return self._save_new(context) - if c.user and not data: + if c.user and not data and not authz.is_sysadmin(c.user): # #1799 Don't offer the registration form if already logged in return render('user/logout_first.html') @@ -264,7 +265,14 @@ def _save_new(self, context): h.flash_success(_('User "%s" is now registered but you are still ' 'logged in as "%s" from before') % (data_dict['name'], c.user)) - return render('user/logout_first.html') + if authz.is_sysadmin(c.user): + # the sysadmin created a new user. We redirect him to the + # activity page for the newly created user + h.redirect_to(controller='user', + action='activity', + id=data_dict['name']) + else: + return render('user/logout_first.html') def edit(self, id=None, data=None, errors=None, error_summary=None): context = {'save': 'save' in request.params, diff --git a/ckan/controllers/util.py b/ckan/controllers/util.py index 97c857b0ff5..4a41bfb0dc2 100644 --- a/ckan/controllers/util.py +++ b/ckan/controllers/util.py @@ -18,7 +18,7 @@ def redirect(self): base.abort(400, _('Missing Value') + ': url') if h.url_is_local(url): - return base.redirect(url) + return h.redirect_to(url) else: base.abort(403, _('Redirecting to external site is not allowed.')) diff --git a/ckan/i18n/ar/LC_MESSAGES/ckan.mo b/ckan/i18n/ar/LC_MESSAGES/ckan.mo index 83abb742833..dd336b5ffd1 100644 Binary files a/ckan/i18n/ar/LC_MESSAGES/ckan.mo and b/ckan/i18n/ar/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/bg/LC_MESSAGES/ckan.mo b/ckan/i18n/bg/LC_MESSAGES/ckan.mo index 01ecb5ed539..296527385c8 100644 Binary files a/ckan/i18n/bg/LC_MESSAGES/ckan.mo and b/ckan/i18n/bg/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/ca/LC_MESSAGES/ckan.mo b/ckan/i18n/ca/LC_MESSAGES/ckan.mo index 315a22cf7e7..ee2a169b460 100644 Binary files a/ckan/i18n/ca/LC_MESSAGES/ckan.mo and b/ckan/i18n/ca/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/cs_CZ/LC_MESSAGES/ckan.mo b/ckan/i18n/cs_CZ/LC_MESSAGES/ckan.mo index 7ce1afaeefe..3564a252526 100644 Binary files a/ckan/i18n/cs_CZ/LC_MESSAGES/ckan.mo and b/ckan/i18n/cs_CZ/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/da_DK/LC_MESSAGES/ckan.mo b/ckan/i18n/da_DK/LC_MESSAGES/ckan.mo index 38d073eda10..eb95bf39027 100644 Binary files a/ckan/i18n/da_DK/LC_MESSAGES/ckan.mo and b/ckan/i18n/da_DK/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/de/LC_MESSAGES/ckan.mo b/ckan/i18n/de/LC_MESSAGES/ckan.mo index c450867cafb..5b46df0e244 100644 Binary files a/ckan/i18n/de/LC_MESSAGES/ckan.mo and b/ckan/i18n/de/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/el/LC_MESSAGES/ckan.mo b/ckan/i18n/el/LC_MESSAGES/ckan.mo index dbb06368ffa..bd2c9a78769 100644 Binary files a/ckan/i18n/el/LC_MESSAGES/ckan.mo and b/ckan/i18n/el/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/en_AU/LC_MESSAGES/ckan.mo b/ckan/i18n/en_AU/LC_MESSAGES/ckan.mo index 0cfe2c33ab8..097d9ab0cab 100644 Binary files a/ckan/i18n/en_AU/LC_MESSAGES/ckan.mo and b/ckan/i18n/en_AU/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/en_GB/LC_MESSAGES/ckan.mo b/ckan/i18n/en_GB/LC_MESSAGES/ckan.mo index edc771f214f..ddaecccb0f7 100644 Binary files a/ckan/i18n/en_GB/LC_MESSAGES/ckan.mo and b/ckan/i18n/en_GB/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/es/LC_MESSAGES/ckan.mo b/ckan/i18n/es/LC_MESSAGES/ckan.mo index 9b4d9089069..597130a53c0 100644 Binary files a/ckan/i18n/es/LC_MESSAGES/ckan.mo and b/ckan/i18n/es/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/es_AR/LC_MESSAGES/ckan.mo b/ckan/i18n/es_AR/LC_MESSAGES/ckan.mo index d75c3b26101..1221df3b701 100644 Binary files a/ckan/i18n/es_AR/LC_MESSAGES/ckan.mo and b/ckan/i18n/es_AR/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/fa_IR/LC_MESSAGES/ckan.mo b/ckan/i18n/fa_IR/LC_MESSAGES/ckan.mo index 667902db4c8..6daffd11e29 100644 Binary files a/ckan/i18n/fa_IR/LC_MESSAGES/ckan.mo and b/ckan/i18n/fa_IR/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/fi/LC_MESSAGES/ckan.mo b/ckan/i18n/fi/LC_MESSAGES/ckan.mo index ff13378cb86..6cf741b9ef9 100644 Binary files a/ckan/i18n/fi/LC_MESSAGES/ckan.mo and b/ckan/i18n/fi/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/fi/LC_MESSAGES/ckan.po b/ckan/i18n/fi/LC_MESSAGES/ckan.po index a3310a73ec7..72ffb1f34f9 100644 --- a/ckan/i18n/fi/LC_MESSAGES/ckan.po +++ b/ckan/i18n/fi/LC_MESSAGES/ckan.po @@ -20,8 +20,8 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-11-26 14:20+0000\n" -"Last-Translator: dread \n" +"PO-Revision-Date: 2015-12-15 08:41+0000\n" +"Last-Translator: Mikko Koho \n" "Language-Team: Finnish (http://www.transifex.com/okfn/ckan/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -886,8 +886,8 @@ msgstr "Näytä" #: ckan/lib/email_notifications.py:103 msgid "{n} new activity from {site_title}" msgid_plural "{n} new activities from {site_title}" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{n} uusi tapahtuma sivulla {site_title}" +msgstr[1] "{n} uutta tapahtumaa sivulla {site_title}" #: ckan/lib/formatters.py:17 msgid "January" @@ -973,7 +973,7 @@ msgstr[1] "yli {years} vuotta sitten" #: ckan/lib/formatters.py:146 msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" -msgstr "" +msgstr "{day}. {month} {year}, {hour:02}:{min:02} ({timezone})" #: ckan/lib/formatters.py:151 msgid "{month} {day}, {year}" @@ -3248,7 +3248,7 @@ msgstr "Virhe:" #: ckan/templates/package/resource_data.html:36 msgid "Error traceback:" -msgstr "" +msgstr "Virhetulostus:" #: ckan/templates/package/resource_data.html:48 msgid "Status" @@ -4348,7 +4348,7 @@ msgstr "muokattu resurssi teksti" #: ckanext/example_itranslation/templates/home/index.html:4 msgid "This is an untranslated string" -msgstr "" +msgstr "Tämä on kääntämätön teksti" #: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 #: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 @@ -4385,7 +4385,7 @@ msgstr "Kartta" #: ckanext/reclineview/theme/public/recline_view.js:34 msgid "error loading view" -msgstr "" +msgstr "virhe ladattaessa näkymää" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 @@ -4529,7 +4529,7 @@ msgstr "Tietoaineistojen lukumäärä" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 msgid "Users Creating Most Datasets" -msgstr "" +msgstr "Eniten aineistoja luovat käyttäjät" #: ckanext/stats/templates/ckanext/stats/index.html:175 msgid "Statistics Menu" @@ -4546,7 +4546,7 @@ msgstr "Teksti" #: ckanext/textview/theme/public/text_view.js:5 #, python-format msgid "An error occurred: %(text)s %(error)s" -msgstr "" +msgstr "Virhe tapahtui: %(text)s %(error)s" #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" diff --git a/ckan/i18n/fr/LC_MESSAGES/ckan.mo b/ckan/i18n/fr/LC_MESSAGES/ckan.mo index 84084c9ca45..c1da1d1789a 100644 Binary files a/ckan/i18n/fr/LC_MESSAGES/ckan.mo and b/ckan/i18n/fr/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/he/LC_MESSAGES/ckan.mo b/ckan/i18n/he/LC_MESSAGES/ckan.mo index bbca46b1da3..e5af6f3687d 100644 Binary files a/ckan/i18n/he/LC_MESSAGES/ckan.mo and b/ckan/i18n/he/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/hr/LC_MESSAGES/ckan.mo b/ckan/i18n/hr/LC_MESSAGES/ckan.mo index 43d004df673..25b79ada173 100644 Binary files a/ckan/i18n/hr/LC_MESSAGES/ckan.mo and b/ckan/i18n/hr/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/hu/LC_MESSAGES/ckan.mo b/ckan/i18n/hu/LC_MESSAGES/ckan.mo index 62075c1f1ab..2d319c4b39c 100644 Binary files a/ckan/i18n/hu/LC_MESSAGES/ckan.mo and b/ckan/i18n/hu/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/id/LC_MESSAGES/ckan.mo b/ckan/i18n/id/LC_MESSAGES/ckan.mo index 0d9e17e297c..f5585d79a65 100644 Binary files a/ckan/i18n/id/LC_MESSAGES/ckan.mo and b/ckan/i18n/id/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/is/LC_MESSAGES/ckan.mo b/ckan/i18n/is/LC_MESSAGES/ckan.mo index ae7915cd3d2..9a8ef4979cc 100644 Binary files a/ckan/i18n/is/LC_MESSAGES/ckan.mo and b/ckan/i18n/is/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/is/LC_MESSAGES/ckan.po b/ckan/i18n/is/LC_MESSAGES/ckan.po index d1c8fac8b84..7b308137ae7 100644 --- a/ckan/i18n/is/LC_MESSAGES/ckan.po +++ b/ckan/i18n/is/LC_MESSAGES/ckan.po @@ -13,14 +13,14 @@ # Sean Hammond , 2013 # Sigurður Fjalar Sigurðarson , 2013 # Stefan Thor , 2013 -# Tryggvi Björgvinsson , 2014 +# Tryggvi Björgvinsson , 2014-2015 msgid "" msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-11-26 14:24+0000\n" -"Last-Translator: dread \n" +"PO-Revision-Date: 2015-12-28 21:17+0000\n" +"Last-Translator: Tryggvi Björgvinsson \n" "Language-Team: Icelandic (http://www.transifex.com/okfn/ckan/language/is/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -248,7 +248,7 @@ msgstr "Safn fannst ekki" #: ckan/controllers/feed.py:234 msgid "Organization not found" -msgstr "" +msgstr "Stofnun fannst ekki" #: ckan/controllers/group.py:138 ckan/controllers/group.py:609 msgid "Incorrect group type" @@ -352,7 +352,7 @@ msgstr "Safninu hefur verið eytt." #: ckan/controllers/group.py:653 #, python-format msgid "%s has been deleted." -msgstr "" +msgstr "%s hefur verið eytt." #: ckan/controllers/group.py:729 #, python-format @@ -507,7 +507,7 @@ msgstr "Þú hefur ekki heimild til að búa til tilfang" #: ckan/controllers/package.py:726 msgid "Unauthorized to create a resource for this package" -msgstr "" +msgstr "Þú hefur ekki heimild til að búa til tilfang fyrir þennan pakka" #: ckan/controllers/package.py:951 msgid "Unable to add package to search index." @@ -544,7 +544,7 @@ msgstr "Óheimilt að lesa gagnapakka %s" #: ckan/controllers/package.py:1133 ckan/controllers/package.py:1597 msgid "Resource view not found" -msgstr "" +msgstr "Tilfangssýn fannst ekki" #: ckan/controllers/package.py:1165 ckan/controllers/package.py:1355 #: ckan/controllers/package.py:1437 ckan/controllers/package.py:1470 @@ -563,33 +563,33 @@ msgstr "Engar skrár til niðurhals" #: ckan/controllers/package.py:1505 msgid "Unauthorized to edit resource" -msgstr "" +msgstr "Þú hefur ekki heimild til að breyta þessu tilfangi" #: ckan/controllers/package.py:1523 msgid "View not found" -msgstr "" +msgstr "Sýn fannst ekki" #: ckan/controllers/package.py:1525 #, python-format msgid "Unauthorized to view View %s" -msgstr "" +msgstr "Þú hefur ekki heimild til að skoða sýnina %s" #: ckan/controllers/package.py:1531 msgid "View Type Not found" -msgstr "" +msgstr "Sýnartegund fannst ekki" #: ckan/controllers/package.py:1591 msgid "Bad resource view data" -msgstr "" +msgstr "Villa í tilfangssýnargögnum" #: ckan/controllers/package.py:1600 #, python-format msgid "Unauthorized to read resource view %s" -msgstr "" +msgstr "Þú hefur ekki lesaðgang að tilfangssýn %s" #: ckan/controllers/package.py:1603 msgid "Resource view not supplied" -msgstr "" +msgstr "Tilfangssýn ekki uppgefin" #: ckan/controllers/package.py:1632 msgid "No preview has been defined." @@ -680,15 +680,15 @@ msgstr "Notandinn %s hefur ekki heimild til að breyta %s" #: ckan/controllers/user.py:354 msgid "Password entered was incorrect" -msgstr "" +msgstr "Innslegið aðgangsorð er rangt" #: ckan/controllers/user.py:355 ckan/templates/user/edit_user_form.html:27 msgid "Old Password" -msgstr "" +msgstr "Eldra aðgangsorð" #: ckan/controllers/user.py:355 msgid "incorrect password" -msgstr "" +msgstr "rangt aðgangsorð" #: ckan/controllers/user.py:396 msgid "Login failed. Bad username or password." @@ -763,7 +763,7 @@ msgstr "Gildi vantar" #: ckan/controllers/util.py:21 msgid "Redirecting to external site is not allowed." -msgstr "" +msgstr "Áframsending á ytri síðu er ekki heimil" #: ckan/lib/activity_streams.py:64 msgid "{actor} added the tag {tag} to the dataset {dataset}" @@ -885,8 +885,8 @@ msgstr "Skoða" #: ckan/lib/email_notifications.py:103 msgid "{n} new activity from {site_title}" msgid_plural "{n} new activities from {site_title}" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{n} ný uppfærsla á {site_title}" +msgstr[1] "{n} nýjar uppfærslur á {site_title}" #: ckan/lib/formatters.py:17 msgid "January" @@ -972,7 +972,7 @@ msgstr[1] "meira en {years} ár síðan" #: ckan/lib/formatters.py:146 msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" -msgstr "" +msgstr "{day}.{month}.{year}, {hour:02}:{min:02} ({timezone})" #: ckan/lib/formatters.py:151 msgid "{month} {day}, {year}" @@ -1091,7 +1091,7 @@ msgid "" "Please click the following link to confirm this request:\n" "\n" " {reset_link}\n" -msgstr "" +msgstr "Þú hefur óskað eftir að breyta aðgangsorðinu þínu á {site_title}.\n\nSmelltu á þennan hlekk til að breyta aðgangsorðinu:\n\n {reset_link}\n" #: ckan/lib/mailer.py:119 msgid "" @@ -1100,7 +1100,7 @@ msgid "" "To accept this invite, please reset your password at:\n" "\n" " {reset_link}\n" -msgstr "" +msgstr "Þér hefur verið boðið á {site_title}. Það er þegar búið að stofna notanda fyrir þig með notandanafninu {user_name}. Þú getur breytt því síðar.\n\n Til að samþykkja boðið skaltu endurstilla aðgangsorðið þitt á:\n\n {reset_link}\n \n" #: ckan/lib/mailer.py:145 ckan/templates/user/request_reset.html:3 #: ckan/templates/user/request_reset.html:13 @@ -1214,7 +1214,7 @@ msgstr "Engir tenglar eru leyfðir í log_message." #: ckan/logic/validators.py:156 msgid "Dataset id already exists" -msgstr "" +msgstr "Auðkenni gagnapakka er nú þegar til" #: ckan/logic/validators.py:197 ckan/logic/validators.py:288 msgid "Resource" @@ -1244,7 +1244,7 @@ msgstr "Það er ekki hægt að nota þetta heiti" #: ckan/logic/validators.py:361 #, python-format msgid "Must be at least %s characters long" -msgstr "" +msgstr "Þarf að vera að minnsta kosti %s stafir" #: ckan/logic/validators.py:363 ckan/logic/validators.py:646 #, python-format @@ -1255,7 +1255,7 @@ msgstr "Heiti má ekki vera lengra en %i bókstafir" msgid "" "Must be purely lowercase alphanumeric (ascii) characters and these symbols: " "-_" -msgstr "" +msgstr "Verður að vera skrifuð með enskum lágstöfum (ascii), tölustöfum eða þessum táknum: -_" #: ckan/logic/validators.py:384 msgid "That URL is already in use." @@ -1393,19 +1393,19 @@ msgstr "Þetta foreldri myndi skapa lykkju í stigveldinu" #: ckan/logic/validators.py:815 msgid "\"filter_fields\" and \"filter_values\" should have the same length" -msgstr "" +msgstr "\"filter_fields\" og \"filter_values\" ættu að vera af sömu lengd" #: ckan/logic/validators.py:826 msgid "\"filter_fields\" is required when \"filter_values\" is filled" -msgstr "" +msgstr "\"filter_fields\" er nauðsynlegt þegar \"filter_values\" er útfyllt" #: ckan/logic/validators.py:829 msgid "\"filter_values\" is required when \"filter_fields\" is filled" -msgstr "" +msgstr "\"filter_values\" er nauðsynlegt þegar \"filter_fields\" er útfyllt" #: ckan/logic/validators.py:843 msgid "There is a schema field with the same name" -msgstr "" +msgstr "Það er til skemareitur með sama nafni" #: ckan/logic/action/create.py:178 ckan/logic/action/create.py:723 #, python-format @@ -1575,7 +1575,7 @@ msgstr "Skráðu þig inn til að bæta við ítarefni" #: ckan/logic/auth/create.py:77 msgid "No dataset id provided, cannot check auth." -msgstr "" +msgstr "Auðkenni gagnapakka ekki gefið upp, get ekki athugað heimildir" #: ckan/logic/auth/create.py:84 ckan/logic/auth/delete.py:32 #: ckan/logic/auth/get.py:135 ckan/logic/auth/update.py:61 @@ -1585,7 +1585,7 @@ msgstr "Enginn pakki fannst fyrir þessa skrá. Ekki hægt að virkja auðkennin #: ckan/logic/auth/create.py:92 #, python-format msgid "User %s not authorized to create resources on dataset %s" -msgstr "" +msgstr "Notandinn %s hefur ekki heimild til að búa til tilfang fyrir gagnapakkann %s" #: ckan/logic/auth/create.py:124 #, python-format @@ -1639,7 +1639,7 @@ msgstr "Notandinn %s hefur ekki heimild til að eyða tilfanginu %s" #: ckan/logic/auth/delete.py:54 msgid "Resource view not found, cannot check auth." -msgstr "" +msgstr "Tilfangssýn fannst ekki, get ekki athugað heimildir" #: ckan/logic/auth/delete.py:69 ckan/logic/auth/delete.py:83 msgid "Only the owner can delete a related item" @@ -1697,7 +1697,7 @@ msgstr "Notandinn %s hefur ekki heimild til að lesa tilfangið %s" #: ckan/logic/auth/get.py:166 #, python-format msgid "User %s not authorized to read group %s" -msgstr "" +msgstr "Notandinn %s hefur ekki lesaðgang að safninu %s" #: ckan/logic/auth/get.py:237 msgid "You must be logged in to access your dashboard." @@ -1752,7 +1752,7 @@ msgstr "Notandinn %s hefur ekki heimild til að breyta notandanum %s" #: ckan/logic/auth/update.py:228 msgid "User {0} not authorized to update user {1}" -msgstr "" +msgstr "Notandinn {0} hefur ekki hemild til að uppfæra notandann {1}" #: ckan/logic/auth/update.py:236 #, python-format @@ -1779,7 +1779,7 @@ msgstr "Þú verður að hafa gildan API lykil til að breyta safni" #: ckan/model/license.py:220 msgid "License not specified" -msgstr "" +msgstr "Skilmálar ekki tilgreindir" #: ckan/model/license.py:230 msgid "Open Data Commons Public Domain Dedication and License (PDDL)" @@ -2036,7 +2036,7 @@ msgstr "Þú ert að sækja skrá. Ertu viss um að viljir fara af síðunni og #: ckan/public/base/javascript/modules/resource-view-reorder.js:8 msgid "Reorder resource view" -msgstr "" +msgstr "Endurraða tilfangssýnum" #: ckan/public/base/javascript/modules/slug-preview.js:35 #: ckan/templates/group/snippets/group_form.html:18 @@ -2115,7 +2115,7 @@ msgstr "Breyta stillingum" #: ckan/templates/header.html:37 msgid "Settings" -msgstr "" +msgstr "Stillingar" #: ckan/templates/header.html:43 ckan/templates/header.html:45 msgid "Log out" @@ -2159,7 +2159,7 @@ msgstr "Leit" #: ckan/templates/page.html:6 msgid "Skip to content" -msgstr "" +msgstr "Hoppa fram að innihaldi" #: ckan/templates/activity_streams/activity_stream_items.html:9 msgid "Load less" @@ -2233,7 +2233,7 @@ msgstr "Staðfesta núllstillingu" #: ckan/templates/admin/index.html:15 msgid "Administer CKAN" -msgstr "" +msgstr "Stjórna CKAN" #: ckan/templates/admin/index.html:20 #, python-format @@ -2241,15 +2241,15 @@ msgid "" "

As a sysadmin user you have full control over this CKAN instance. " "Proceed with care!

For guidance on using sysadmin features, see the " "CKAN sysadmin guide

" -msgstr "" +msgstr "

Sem kerfisstjóri hefur þú fulla stjórn á þessari CKAN-uppsetningu. Farðu mjög varlega!

Fyrir aðstoð við að nota kerfisstjórnarmöguleika, skoðaðu CKAN kerfisstjóraleiðbeiningarnar

" #: ckan/templates/admin/trash.html:20 msgid "Purge" -msgstr "" +msgstr "Eyða varanlega" #: ckan/templates/admin/trash.html:32 msgid "

Purge deleted datasets forever and irreversibly.

" -msgstr "" +msgstr "

Hreinsa varanlega burt eyddum gagnapökkum.

" #: ckan/templates/ajax_snippets/api_info.html:19 msgid "CKAN Data API" @@ -2264,7 +2264,7 @@ msgid "" " Further information in the main CKAN Data API and DataStore documentation.

" -msgstr "" +msgstr "Ítarlegri upplýsingar má finna í aðalleiðbeiningum CKAN gagnaforritaskila og gagnabankans.

" #: ckan/templates/ajax_snippets/api_info.html:33 msgid "Endpoints" @@ -2862,15 +2862,15 @@ msgstr "Þetta er í kastljósinu" #: ckan/templates/home/snippets/search.html:2 msgid "E.g. environment" -msgstr "" +msgstr "T.d. umhverfi" #: ckan/templates/home/snippets/search.html:6 msgid "Search data" -msgstr "" +msgstr "Leita að gögnum" #: ckan/templates/home/snippets/search.html:16 msgid "Popular tags" -msgstr "" +msgstr "Vinsæl efnisorð" #: ckan/templates/home/snippets/stats.html:5 msgid "{0} statistics" @@ -2906,7 +2906,7 @@ msgid "" "You can use Markdown formatting here" -msgstr "" +msgstr "Þú getur notað Markdown sniðmát hér" #: ckan/templates/macros/form.html:265 msgid "This field is required" @@ -3019,7 +3019,7 @@ msgstr "Notandanafn" #: ckan/templates/organization/member_new.html:50 msgid "Email address" -msgstr "" +msgstr "Tölvupóstfang" #: ckan/templates/organization/member_new.html:62 msgid "Update Member" @@ -3057,7 +3057,7 @@ msgstr "Bæta við gagnapakka" #: ckan/templates/organization/snippets/feeds.html:3 msgid "Datasets in organization: {group}" -msgstr "" +msgstr "Gagnapakkar hjá stofnun: {group}" #: ckan/templates/organization/snippets/help.html:4 #: ckan/templates/organization/snippets/helper.html:4 @@ -3072,7 +3072,7 @@ msgid "" "organizations, admins can assign roles and authorise its members, giving " "individual users the right to publish datasets from that particular " "organisation (e.g. Office of National Statistics).

" -msgstr "" +msgstr "

Stofnanir eru útgefendur gagnapakka (til dæmis Fjármálaráðuneytið). Gagnapakkar eru gefnir út undir stofnun frekar en í nafni einstaklinga.

Stjórnendur geta úthlutað hlutverkum og aðgangsheimildum innan stofnana og þannig gefið notendum réttindi til að gefa út gagnapakka fyrir viðkomandi stofnun (t.d. Hagstofa Íslands).

" #: ckan/templates/organization/snippets/helper.html:8 msgid "" @@ -3145,7 +3145,7 @@ msgstr "Breyta lýsigögnum" #: ckan/templates/package/edit_view.html:8 #: ckan/templates/package/edit_view.html:12 msgid "Edit view" -msgstr "" +msgstr "Breyta sýn" #: ckan/templates/package/edit_view.html:20 #: ckan/templates/package/new_view.html:28 @@ -3197,7 +3197,7 @@ msgstr "Nýtt tilfang" #: ckan/templates/package/new_view.html:8 #: ckan/templates/package/new_view.html:12 msgid "Add view" -msgstr "" +msgstr "Bæta við sýn" #: ckan/templates/package/new_view.html:19 msgid "" @@ -3206,7 +3206,7 @@ msgid "" "href='http://docs.ckan.org/en/latest/maintaining/data-viewer.html#viewing-" "structured-data-the-data-explorer' target='_blank'>Data Explorer " "documentation. " -msgstr "" +msgstr "Gagnaskoðandasýnir geta verið hægar og óáreiðanlegar nema DataStore viðbótin sé virk. Fyrir frekari upplýsingar skoðaðu leiðbeiningar fyrir gagnaskoðandann. " #: ckan/templates/package/new_view.html:29 #: ckan/templates/package/snippets/resource_form.html:82 @@ -3247,7 +3247,7 @@ msgstr "Villa:" #: ckan/templates/package/resource_data.html:36 msgid "Error traceback:" -msgstr "" +msgstr "Villumelding:" #: ckan/templates/package/resource_data.html:48 msgid "Status" @@ -3293,7 +3293,7 @@ msgstr "DataStore" #: ckan/templates/package/resource_edit_base.html:28 msgid "Views" -msgstr "" +msgstr "Sýnir" #: ckan/templates/package/resource_read.html:39 msgid "API Endpoint" @@ -3325,30 +3325,30 @@ msgstr "Heimild: %(dataset)s" #: ckan/templates/package/resource_read.html:112 msgid "There are no views created for this resource yet." -msgstr "" +msgstr "Það hafa engar sýnir verið búnar til fyrir þetta tilfang." #: ckan/templates/package/resource_read.html:116 msgid "Not seeing the views you were expecting?" -msgstr "" +msgstr "Sérðu ekki sýnirnar sem þú bjóst við?" #: ckan/templates/package/resource_read.html:121 msgid "Here are some reasons you may not be seeing expected views:" -msgstr "" +msgstr "Hér eru nokkrar ástæður fyrir því að þú sjáir ekki sýnirnar:" #: ckan/templates/package/resource_read.html:123 msgid "No view has been created that is suitable for this resource" -msgstr "" +msgstr "Engin sýn hefur verið búin til sem virkar fyrir þetta tilfang" #: ckan/templates/package/resource_read.html:124 msgid "The site administrators may not have enabled the relevant view plugins" -msgstr "" +msgstr "Kerfisstjórar hafa mögulega ekki bætt við viðeigandi sýnisviðbótum" #: ckan/templates/package/resource_read.html:125 msgid "" "If a view requires the DataStore, the DataStore plugin may not be enabled, " "or the data may not have been pushed to the DataStore, or the DataStore " "hasn't finished processing the data yet" -msgstr "" +msgstr "Ef sýn býst við DataStore viðbótinni, gæti DataStore viðbótin verið óvirk, gögnin ekki sett inn í gagnabankann eða gagnabankinn hefur ekki enn klárað að vinna gögnin" #: ckan/templates/package/resource_read.html:147 msgid "Additional Information" @@ -3392,11 +3392,11 @@ msgstr "Leyfisskilmálar" #: ckan/templates/package/resource_views.html:10 msgid "New view" -msgstr "" +msgstr "Ný sýn" #: ckan/templates/package/resource_views.html:28 msgid "This resource has no views" -msgstr "" +msgstr "Þetta tilfang hefur enga sýn" #: ckan/templates/package/resources.html:8 msgid "Add new resource" @@ -3438,15 +3438,15 @@ msgstr "Þú getur líka fengið aðgang að skránni með %(api_link)s (sjá %( #: ckan/templates/package/view_edit_base.html:9 msgid "All views" -msgstr "" +msgstr "Allar sýnir" #: ckan/templates/package/view_edit_base.html:12 msgid "View view" -msgstr "" +msgstr "Skoða sýn" #: ckan/templates/package/view_edit_base.html:37 msgid "View preview" -msgstr "" +msgstr "Forskoða sýn" #: ckan/templates/package/snippets/additional_info.html:2 #: ckan/templates/snippets/additional_info.html:7 @@ -3473,11 +3473,11 @@ msgstr "Útgáfa" #: ckan/templates/package/snippets/package_basic_fields.html:108 #: ckan/templates/user/read_base.html:91 msgid "State" -msgstr "Ríki" +msgstr "Staða" #: ckan/templates/package/snippets/additional_info.html:62 msgid "Last Updated" -msgstr "" +msgstr "Seinast uppfært" #: ckan/templates/package/snippets/data_api_button.html:10 msgid "Data API" @@ -3538,7 +3538,7 @@ msgid "" "agree to release the metadata values that you enter into the form " "under the Open " "Database License." -msgstr "" +msgstr "Notkunarskilmálarnir sem þú valdir hér fyrir ofan eiga aðeins við um innihald tilfangaskráa sem þú bætir við í gagnapakkann. Með því að senda inn þessar upplýsingar samþykkir þú að birta lýsigögnin sem þú hefur fyllt inn undir notkunarskilmálum Open Database License." #: ckan/templates/package/snippets/package_form.html:39 msgid "Are you sure you want to delete this dataset?" @@ -3598,7 +3598,7 @@ msgstr "t.d. CSV, XML eða JSON" #: ckan/templates/package/snippets/resource_form.html:40 msgid "This will be guessed automatically. Leave blank if you wish" -msgstr "" +msgstr "Kerfið giskar á þetta sjálfkrafa. Reiturinn má vera auður ef þú vilt" #: ckan/templates/package/snippets/resource_form.html:51 msgid "eg. 2012-06-05" @@ -3660,25 +3660,25 @@ msgstr "Fella inn í síðu" #: ckan/templates/package/snippets/resource_view.html:24 msgid "This resource view is not available at the moment." -msgstr "" +msgstr "Þessi tilfangssýn er ekki aðgengileg á þessum tímapunkti." #: ckan/templates/package/snippets/resource_view.html:63 msgid "Embed resource view" -msgstr "" +msgstr "Fella þess tilfangssýn inn í vefsíðu" #: ckan/templates/package/snippets/resource_view.html:66 msgid "" "You can copy and paste the embed code into a CMS or blog software that " "supports raw HTML" -msgstr "" +msgstr "Þú getur afritað og límt inn ívafskóðann í vefumsjónarkerfi eða blogghugbúnað sem styður hreint HTML" #: ckan/templates/package/snippets/resource_view.html:69 msgid "Width" -msgstr "" +msgstr "Breidd" #: ckan/templates/package/snippets/resource_view.html:72 msgid "Height" -msgstr "" +msgstr "Hæð" #: ckan/templates/package/snippets/resource_view.html:75 msgid "Code" @@ -3686,7 +3686,7 @@ msgstr "Kóði" #: ckan/templates/package/snippets/resource_views_list.html:8 msgid "Resource Preview" -msgstr "" +msgstr "Forskoðun tilfangs" #: ckan/templates/package/snippets/resources_list.html:13 msgid "Data and Resources" @@ -3694,7 +3694,7 @@ msgstr "Gögn og tilföng" #: ckan/templates/package/snippets/resources_list.html:29 msgid "This dataset has no data" -msgstr "" +msgstr "Þessi gagnapakki hefur engin gögn" #: ckan/templates/package/snippets/revisions_table.html:24 #, python-format @@ -3714,31 +3714,31 @@ msgstr "Bæta við gögnum" #: ckan/templates/package/snippets/view_form.html:8 msgid "eg. My View" -msgstr "" +msgstr "t.d. Mín sýn" #: ckan/templates/package/snippets/view_form.html:9 msgid "eg. Information about my view" -msgstr "" +msgstr "t.d. Upplýsingar um mína sýn" #: ckan/templates/package/snippets/view_form_filters.html:16 msgid "Add Filter" -msgstr "" +msgstr "Bæta við síu" #: ckan/templates/package/snippets/view_form_filters.html:28 msgid "Remove Filter" -msgstr "" +msgstr "Fjarlægja síu" #: ckan/templates/package/snippets/view_form_filters.html:46 msgid "Filters" -msgstr "" +msgstr "Síur" #: ckan/templates/package/snippets/view_help.html:2 msgid "What's a view?" -msgstr "" +msgstr "Hvað er sýn?" #: ckan/templates/package/snippets/view_help.html:4 msgid "A view is a representation of the data held against a resource" -msgstr "" +msgstr "Sýn er framsetning á gögnum út frá einu tilfangi" #: ckan/templates/revision/diff.html:6 msgid "Differences" @@ -4084,11 +4084,11 @@ msgstr "Ertu viss um að þú viljir eyða þessum notanda?" #: ckan/templates/user/edit_user_form.html:43 msgid "Are you sure you want to regenerate the API key?" -msgstr "" +msgstr "Ertu viss um að þú viljir endurstilla API-lykilinn þinn?" #: ckan/templates/user/edit_user_form.html:44 msgid "Regenerate API Key" -msgstr "" +msgstr "Endurstilla API-lykil" #: ckan/templates/user/edit_user_form.html:48 msgid "Update Profile" @@ -4180,7 +4180,7 @@ msgstr "Þú getur búið til gagnapakka, söfn og fleiri skemmtilega hluti" #: ckan/templates/user/new_user_form.html:5 msgid "username" -msgstr "" +msgstr "notandanafn" #: ckan/templates/user/new_user_form.html:6 msgid "Full Name" @@ -4242,11 +4242,11 @@ msgstr "API lykill" #: ckan/templates/user/request_reset.html:6 msgid "Password reset" -msgstr "" +msgstr "Breyta aðgangsorði" #: ckan/templates/user/request_reset.html:19 msgid "Request reset" -msgstr "" +msgstr "Óska eftir aðgangsorðsbreytingu" #: ckan/templates/user/request_reset.html:34 msgid "" @@ -4299,7 +4299,7 @@ msgstr "Tilfang DataStore fannst ekki" msgid "" "The data was invalid (for example: a numeric value is out of range or was " "inserted into a text field)." -msgstr "" +msgstr "Þessi gögn eru ógild (til dæmis: tölugildi er ekki innan marka eða var slegið inn í textareit)." #: ckanext/datastore/logic/action.py:215 ckanext/datastore/logic/action.py:255 #: ckanext/datastore/logic/action.py:332 ckanext/datastore/logic/action.py:422 @@ -4313,19 +4313,19 @@ msgstr "Notandinn {0} hefur ekki heimild til að uppfæra tilfangið {1}" #: ckanext/example_iconfigurer/templates/admin/config.html:11 msgid "Datasets per page" -msgstr "" +msgstr "Gagnapakkar á hverri síðu" #: ckanext/example_iconfigurer/templates/admin/config.html:13 msgid "Test conf" -msgstr "" +msgstr "Prófunarstillingar" #: ckanext/example_idatasetform/templates/package/search.html:16 msgid "Custom Field Ascending" -msgstr "" +msgstr "Viðbótarupplýsingar í vaxandi röð" #: ckanext/example_idatasetform/templates/package/search.html:17 msgid "Custom Field Descending" -msgstr "" +msgstr "Viðbótarupplýsingar í lækkandi röð" #: ckanext/example_idatasetform/templates/package/snippets/additional_info.html:6 #: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 @@ -4343,11 +4343,11 @@ msgstr "Landskóði" #: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 msgid "custom resource text" -msgstr "" +msgstr "valfrjáls tilfangstexti" #: ckanext/example_itranslation/templates/home/index.html:4 msgid "This is an untranslated string" -msgstr "" +msgstr "Þetta er óþýddur strengur" #: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 #: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 @@ -4356,91 +4356,91 @@ msgstr "Engin lýsing á safninu" #: ckanext/example_theme/v12_extra_public_dir/templates/home/snippets/promoted.html:4 msgid "CKAN's data previewing tool has many powerful features" -msgstr "" +msgstr "Gagnaforskoðunartól CKAN býður upp á mikla möguleika" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "Image url" -msgstr "" +msgstr "Slóð á mynd" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "eg. http://example.com/image.jpg (if blank uses resource url)" -msgstr "" +msgstr "t.d. http://example.com/image.jpg (ef tómt er vefslóð tilfangs notuð)" #: ckanext/reclineview/plugin.py:84 msgid "Data Explorer" -msgstr "" +msgstr "Gagnaskoðandi" #: ckanext/reclineview/plugin.py:111 msgid "Table" -msgstr "" +msgstr "Tafla" #: ckanext/reclineview/plugin.py:154 msgid "Graph" -msgstr "" +msgstr "Myndrit" #: ckanext/reclineview/plugin.py:214 msgid "Map" -msgstr "" +msgstr "Kort" #: ckanext/reclineview/theme/public/recline_view.js:34 msgid "error loading view" -msgstr "" +msgstr "villa við að hlaða sýn" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "Row offset" -msgstr "" +msgstr "Raðahliðrun" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "eg: 0" -msgstr "" +msgstr "t.d. 0" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "Number of rows" -msgstr "" +msgstr "Fjöldi raða" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "eg: 100" -msgstr "" +msgstr "t.d. 100" #: ckanext/reclineview/theme/templates/recline_graph_form.html:6 msgid "Graph type" -msgstr "" +msgstr "Gerð myndrits" #: ckanext/reclineview/theme/templates/recline_graph_form.html:7 msgid "Group (Axis 1)" -msgstr "" +msgstr "Hópur (ás 1)" #: ckanext/reclineview/theme/templates/recline_graph_form.html:8 msgid "Series (Axis 2)" -msgstr "" +msgstr "Röð (ás 2)" #: ckanext/reclineview/theme/templates/recline_map_form.html:6 msgid "Field type" -msgstr "" +msgstr "Gerð svæðis" #: ckanext/reclineview/theme/templates/recline_map_form.html:7 msgid "Latitude field" -msgstr "" +msgstr "Breiddargráðureitur" #: ckanext/reclineview/theme/templates/recline_map_form.html:8 msgid "Longitude field" -msgstr "" +msgstr "Lengdargráðureitur" #: ckanext/reclineview/theme/templates/recline_map_form.html:9 msgid "GeoJSON field" -msgstr "" +msgstr "GeoJSON-reitur" #: ckanext/reclineview/theme/templates/recline_map_form.html:10 msgid "Auto zoom to features" -msgstr "" +msgstr "Sjálfvirk þysjun að eiginleikum" #: ckanext/reclineview/theme/templates/recline_map_form.html:11 msgid "Cluster markers" -msgstr "" +msgstr "Klasamerkingar" #: ckanext/stats/templates/ckanext/stats/index.html:10 msgid "Total number of Datasets" @@ -4528,7 +4528,7 @@ msgstr "Fjöldi gagnapakka" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 msgid "Users Creating Most Datasets" -msgstr "" +msgstr "Notendur sem hafa búið til flesta gagnapakka" #: ckanext/stats/templates/ckanext/stats/index.html:175 msgid "Statistics Menu" @@ -4540,21 +4540,21 @@ msgstr "Heildarfjöldi gagnapakka" #: ckanext/textview/plugin.py:65 ckanext/textview/plugin.py:67 msgid "Text" -msgstr "" +msgstr "Texti" #: ckanext/textview/theme/public/text_view.js:5 #, python-format msgid "An error occurred: %(text)s %(error)s" -msgstr "" +msgstr "Villa kom upp: %(text)s %(error)s" #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" -msgstr "" +msgstr "Vefsíða" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "Web Page url" -msgstr "" +msgstr "Vefsíðuslóð" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "eg. http://example.com (if blank uses resource url)" -msgstr "" +msgstr "t.d. http://example.com (ef tómt er vefslóð tilfangs notuð)" diff --git a/ckan/i18n/it/LC_MESSAGES/ckan.mo b/ckan/i18n/it/LC_MESSAGES/ckan.mo index 0d75bab86c8..70ca7ee69ae 100644 Binary files a/ckan/i18n/it/LC_MESSAGES/ckan.mo and b/ckan/i18n/it/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/ja/LC_MESSAGES/ckan.mo b/ckan/i18n/ja/LC_MESSAGES/ckan.mo index 2bd990036b2..1a042eb1f79 100644 Binary files a/ckan/i18n/ja/LC_MESSAGES/ckan.mo and b/ckan/i18n/ja/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/km/LC_MESSAGES/ckan.mo b/ckan/i18n/km/LC_MESSAGES/ckan.mo index ee645f97cd4..a551d290013 100644 Binary files a/ckan/i18n/km/LC_MESSAGES/ckan.mo and b/ckan/i18n/km/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/ko_KR/LC_MESSAGES/ckan.mo b/ckan/i18n/ko_KR/LC_MESSAGES/ckan.mo index 47656f9353d..969296e7129 100644 Binary files a/ckan/i18n/ko_KR/LC_MESSAGES/ckan.mo and b/ckan/i18n/ko_KR/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/lt/LC_MESSAGES/ckan.mo b/ckan/i18n/lt/LC_MESSAGES/ckan.mo index c0c69fd0acd..fab413b2505 100644 Binary files a/ckan/i18n/lt/LC_MESSAGES/ckan.mo and b/ckan/i18n/lt/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/lv/LC_MESSAGES/ckan.mo b/ckan/i18n/lv/LC_MESSAGES/ckan.mo index 14038306bac..bafb4a96c93 100644 Binary files a/ckan/i18n/lv/LC_MESSAGES/ckan.mo and b/ckan/i18n/lv/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/mn_MN/LC_MESSAGES/ckan.mo b/ckan/i18n/mn_MN/LC_MESSAGES/ckan.mo index ba2e8cfe2ef..704ace6f7de 100644 Binary files a/ckan/i18n/mn_MN/LC_MESSAGES/ckan.mo and b/ckan/i18n/mn_MN/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/ne/LC_MESSAGES/ckan.po b/ckan/i18n/ne/LC_MESSAGES/ckan.po index 8c9895b2919..e0a5c947c05 100644 --- a/ckan/i18n/ne/LC_MESSAGES/ckan.po +++ b/ckan/i18n/ne/LC_MESSAGES/ckan.po @@ -9,22 +9,36 @@ msgid "" msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +<<<<<<< HEAD "POT-Creation-Date: 2015-06-23 20:40+0000\n" "PO-Revision-Date: 2015-06-25 10:42+0000\n" +======= +"POT-Creation-Date: 2015-11-26 13:42+0000\n" +"PO-Revision-Date: 2015-11-26 14:18+0000\n" +>>>>>>> 1596665... Update translations "Last-Translator: dread \n" "Language-Team: Nepali (http://www.transifex.com/okfn/ckan/language/ne/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +<<<<<<< HEAD "Generated-By: Babel 1.3\n" "Language: ne\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ckan/authz.py:178 +======= +"Generated-By: Babel 2.1.1\n" +"Language: ne\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ckan/authz.py:177 +>>>>>>> 1596665... Update translations #, python-format msgid "Authorization function not found: %s" msgstr "प्रमाणीकरण कार्य फेला परेन: %s" +<<<<<<< HEAD #: ckan/authz.py:190 ckan/templates/header.html:14 msgid "Admin" msgstr "व्यवस्थापक" @@ -34,6 +48,17 @@ msgid "Editor" msgstr "सम्पादक" #: ckan/authz.py:198 +======= +#: ckan/authz.py:189 ckan/templates/header.html:14 +msgid "Admin" +msgstr "व्यवस्थापक" + +#: ckan/authz.py:193 +msgid "Editor" +msgstr "सम्पादक" + +#: ckan/authz.py:197 +>>>>>>> 1596665... Update translations msgid "Member" msgstr "सदस्य" @@ -110,27 +135,44 @@ msgstr "" msgid "Action not implemented." msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:60 ckan/controllers/group.py:165 #: ckan/controllers/home.py:29 ckan/controllers/package.py:145 #: ckan/controllers/related.py:86 ckan/controllers/related.py:113 #: ckan/controllers/revision.py:31 ckan/controllers/tag.py:23 #: ckan/controllers/user.py:46 ckan/controllers/user.py:73 #: ckan/controllers/user.py:102 ckan/controllers/user.py:562 +======= +#: ckan/controllers/api.py:60 ckan/controllers/group.py:163 +#: ckan/controllers/home.py:26 ckan/controllers/package.py:142 +#: ckan/controllers/revision.py:31 ckan/controllers/tag.py:23 +#: ckan/controllers/user.py:46 ckan/controllers/user.py:73 +#: ckan/controllers/user.py:102 ckan/controllers/user.py:563 +>>>>>>> 1596665... Update translations #: ckanext/datapusher/plugin.py:68 msgid "Not authorized to see this page" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:118 ckan/controllers/api.py:209 msgid "Access denied" msgstr "" #: ckan/controllers/api.py:124 ckan/controllers/api.py:218 +======= +#: ckan/controllers/api.py:120 ckan/controllers/api.py:214 +msgid "Access denied" +msgstr "" + +#: ckan/controllers/api.py:126 ckan/controllers/api.py:223 +>>>>>>> 1596665... Update translations #: ckan/logic/converters.py:119 ckan/logic/converters.py:144 #: ckan/logic/converters.py:169 ckan/logic/validators.py:146 #: ckan/logic/validators.py:167 ckan/logic/validators.py:188 #: ckan/logic/validators.py:197 ckan/logic/validators.py:211 #: ckan/logic/validators.py:228 ckan/logic/validators.py:241 #: ckan/logic/validators.py:255 ckan/logic/validators.py:279 +<<<<<<< HEAD #: ckan/logic/validators.py:288 ckan/logic/validators.py:724 #: ckan/logic/action/create.py:969 msgid "Not found" @@ -141,87 +183,165 @@ msgid "Bad request" msgstr "" #: ckan/controllers/api.py:164 +======= +#: ckan/logic/validators.py:288 ckan/logic/validators.py:729 +#: ckan/logic/action/create.py:966 +msgid "Not found" +msgstr "" + +#: ckan/controllers/api.py:132 +msgid "Bad request" +msgstr "" + +#: ckan/controllers/api.py:166 +>>>>>>> 1596665... Update translations #, python-format msgid "Action name not known: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:185 ckan/controllers/api.py:352 #: ckan/controllers/api.py:414 +======= +#: ckan/controllers/api.py:188 ckan/controllers/api.py:358 +#: ckan/controllers/api.py:421 +>>>>>>> 1596665... Update translations #, python-format msgid "JSON Error: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:190 +======= +#: ckan/controllers/api.py:194 +>>>>>>> 1596665... Update translations #, python-format msgid "Bad request data: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:288 ckan/logic/action/get.py:2322 +======= +#: ckan/controllers/api.py:294 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot list entity of this type: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:318 +======= +#: ckan/controllers/api.py:324 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot read entity of this type: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:357 +======= +#: ckan/controllers/api.py:363 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot create new entity of this type: %s %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:389 msgid "Unable to add package to search index" msgstr "" #: ckan/controllers/api.py:419 +======= +#: ckan/controllers/api.py:396 +msgid "Unable to add package to search index" +msgstr "" + +#: ckan/controllers/api.py:426 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot update entity of this type: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:442 msgid "Unable to update search index" msgstr "" #: ckan/controllers/api.py:466 +======= +#: ckan/controllers/api.py:450 +msgid "Unable to update search index" +msgstr "" + +#: ckan/controllers/api.py:474 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot delete entity of this type: %s %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:489 msgid "No revision specified" msgstr "" #: ckan/controllers/api.py:493 +======= +#: ckan/controllers/api.py:497 +msgid "No revision specified" +msgstr "" + +#: ckan/controllers/api.py:501 +>>>>>>> 1596665... Update translations #, python-format msgid "There is no revision with id: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:503 msgid "Missing search term ('since_id=UUID' or 'since_time=TIMESTAMP')" msgstr "" #: ckan/controllers/api.py:513 +======= +#: ckan/controllers/api.py:511 +msgid "Missing search term ('since_id=UUID' or 'since_time=TIMESTAMP')" +msgstr "" + +#: ckan/controllers/api.py:523 +>>>>>>> 1596665... Update translations #, python-format msgid "Could not read parameters: %r" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:574 +======= +#: ckan/controllers/api.py:584 +>>>>>>> 1596665... Update translations #, python-format msgid "Bad search option: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:577 +======= +#: ckan/controllers/api.py:587 +>>>>>>> 1596665... Update translations #, python-format msgid "Unknown register: %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:586 +======= +#: ckan/controllers/api.py:596 +>>>>>>> 1596665... Update translations #, python-format msgid "Malformed qjson value: %r" msgstr "" +<<<<<<< HEAD #: ckan/controllers/api.py:596 msgid "Request params must be in form of a json encoded dictionary." msgstr "" @@ -235,6 +355,21 @@ msgstr "" #: ckan/controllers/group.py:772 ckan/controllers/group.py:831 #: ckan/controllers/group.py:925 ckan/controllers/package.py:1292 #: ckan/controllers/package.py:1307 +======= +#: ckan/controllers/api.py:606 +msgid "Request params must be in form of a json encoded dictionary." +msgstr "" + +#: ckan/controllers/feed.py:223 ckan/controllers/group.py:136 +#: ckan/controllers/group.py:222 ckan/controllers/group.py:408 +#: ckan/controllers/group.py:516 ckan/controllers/group.py:563 +#: ckan/controllers/group.py:595 ckan/controllers/group.py:606 +#: ckan/controllers/group.py:660 ckan/controllers/group.py:679 +#: ckan/controllers/group.py:731 ckan/controllers/group.py:763 +#: ckan/controllers/group.py:796 ckan/controllers/group.py:855 +#: ckan/controllers/group.py:951 ckan/controllers/package.py:1270 +#: ckan/controllers/package.py:1285 +>>>>>>> 1596665... Update translations msgid "Group not found" msgstr "" @@ -242,6 +377,7 @@ msgstr "" msgid "Organization not found" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:140 ckan/controllers/group.py:589 msgid "Incorrect group type" msgstr "" @@ -249,12 +385,26 @@ msgstr "" #: ckan/controllers/group.py:206 ckan/controllers/group.py:390 #: ckan/controllers/group.py:498 ckan/controllers/group.py:541 #: ckan/controllers/group.py:573 ckan/controllers/group.py:927 +======= +#: ckan/controllers/group.py:138 ckan/controllers/group.py:609 +msgid "Incorrect group type" +msgstr "" + +#: ckan/controllers/group.py:224 ckan/controllers/group.py:410 +#: ckan/controllers/group.py:518 ckan/controllers/group.py:561 +#: ckan/controllers/group.py:593 ckan/controllers/group.py:953 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to read group %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:291 ckan/controllers/home.py:70 #: ckan/controllers/package.py:241 ckan/lib/helpers.py:697 +======= +#: ckan/controllers/group.py:310 ckan/controllers/home.py:67 +#: ckan/controllers/package.py:239 ckan/lib/helpers.py:755 +>>>>>>> 1596665... Update translations #: ckan/templates/header.html:104 ckan/templates/organization/edit_base.html:5 #: ckan/templates/organization/edit_base.html:8 #: ckan/templates/organization/index.html:3 @@ -266,8 +416,13 @@ msgstr "" msgid "Organizations" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:292 ckan/controllers/home.py:71 #: ckan/controllers/package.py:242 ckan/lib/helpers.py:698 +======= +#: ckan/controllers/group.py:311 ckan/controllers/home.py:68 +#: ckan/controllers/package.py:240 ckan/lib/helpers.py:756 +>>>>>>> 1596665... Update translations #: ckan/templates/header.html:105 ckan/templates/group/base_form_page.html:6 #: ckan/templates/group/edit.html:4 ckan/templates/group/edit_base.html:3 #: ckan/templates/group/edit_base.html:8 ckan/templates/group/index.html:3 @@ -275,14 +430,24 @@ msgstr "" #: ckan/templates/group/members.html:3 ckan/templates/group/read_base.html:3 #: ckan/templates/group/read_base.html:6 #: ckan/templates/package/group_list.html:5 +<<<<<<< HEAD #: ckan/templates/package/read_base.html:25 +======= +#: ckan/templates/package/read_base.html:20 +>>>>>>> 1596665... Update translations #: ckan/templates/revision/diff.html:16 ckan/templates/revision/read.html:84 msgid "Groups" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:293 ckan/controllers/home.py:72 #: ckan/controllers/package.py:243 ckan/lib/helpers.py:699 #: ckan/logic/__init__.py:113 +======= +#: ckan/controllers/group.py:312 ckan/controllers/home.py:69 +#: ckan/controllers/package.py:241 ckan/lib/helpers.py:757 +#: ckan/logic/__init__.py:100 +>>>>>>> 1596665... Update translations #: ckan/templates/package/snippets/package_basic_fields.html:24 #: ckan/templates/snippets/context/dataset.html:17 #: ckan/templates/tag/index.html:3 ckan/templates/tag/index.html:6 @@ -290,6 +455,7 @@ msgstr "" msgid "Tags" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:294 ckan/controllers/home.py:73 #: ckan/controllers/package.py:244 ckan/lib/helpers.py:700 msgid "Formats" @@ -311,10 +477,34 @@ msgstr "" #: ckan/controllers/group.py:507 ckan/controllers/package.py:338 #: ckan/controllers/package.py:807 ckan/controllers/package.py:1440 #: ckan/controllers/package.py:1476 +======= +#: ckan/controllers/group.py:313 ckan/controllers/home.py:70 +#: ckan/controllers/package.py:242 ckan/lib/helpers.py:758 +msgid "Formats" +msgstr "" + +#: ckan/controllers/group.py:314 ckan/controllers/home.py:71 +#: ckan/controllers/package.py:243 ckan/lib/helpers.py:759 +msgid "Licenses" +msgstr "" + +#: ckan/controllers/group.py:453 +msgid "Not authorized to perform bulk update" +msgstr "" + +#: ckan/controllers/group.py:473 +msgid "Unauthorized to create a group" +msgstr "" + +#: ckan/controllers/group.py:527 ckan/controllers/package.py:319 +#: ckan/controllers/package.py:779 ckan/controllers/package.py:1418 +#: ckan/controllers/package.py:1454 +>>>>>>> 1596665... Update translations #, python-format msgid "User %r not authorized to edit %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:545 ckan/controllers/group.py:577 #: ckan/controllers/package.py:971 ckan/controllers/package.py:1018 #: ckan/controllers/related.py:190 ckan/controllers/user.py:236 @@ -323,16 +513,32 @@ msgid "Integrity Error" msgstr "" #: ckan/controllers/group.py:603 +======= +#: ckan/controllers/group.py:565 ckan/controllers/group.py:597 +#: ckan/controllers/package.py:945 ckan/controllers/package.py:993 +#: ckan/controllers/user.py:236 ckan/controllers/user.py:348 +#: ckan/controllers/user.py:517 +msgid "Integrity Error" +msgstr "" + +#: ckan/controllers/group.py:623 +>>>>>>> 1596665... Update translations #, python-format msgid "User %r not authorized to edit %s authorizations" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:623 ckan/controllers/group.py:638 #: ckan/controllers/group.py:657 ckan/controllers/group.py:738 +======= +#: ckan/controllers/group.py:643 ckan/controllers/group.py:658 +#: ckan/controllers/group.py:677 ckan/controllers/group.py:761 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to delete group %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:629 msgid "Organization has been deleted." msgstr "" @@ -342,20 +548,40 @@ msgid "Group has been deleted." msgstr "" #: ckan/controllers/group.py:633 +======= +#: ckan/controllers/group.py:649 +msgid "Organization has been deleted." +msgstr "" + +#: ckan/controllers/group.py:651 +msgid "Group has been deleted." +msgstr "" + +#: ckan/controllers/group.py:653 +>>>>>>> 1596665... Update translations #, python-format msgid "%s has been deleted." msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:707 +======= +#: ckan/controllers/group.py:729 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to add member to group %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:726 +======= +#: ckan/controllers/group.py:748 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to delete group %s members" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:732 msgid "Group member has been deleted." msgstr "" @@ -365,10 +591,22 @@ msgid "Select two revisions before doing the comparison." msgstr "" #: ckan/controllers/group.py:774 +======= +#: ckan/controllers/group.py:755 +msgid "Group member has been deleted." +msgstr "" + +#: ckan/controllers/group.py:779 ckan/controllers/package.py:412 +msgid "Select two revisions before doing the comparison." +msgstr "" + +#: ckan/controllers/group.py:798 +>>>>>>> 1596665... Update translations #, python-format msgid "User %r not authorized to edit %r" msgstr "" +<<<<<<< HEAD #: ckan/controllers/group.py:781 msgid "CKAN Group Revision History" msgstr "" @@ -396,10 +634,40 @@ msgid "You are no longer following {0}" msgstr "" #: ckan/controllers/group.py:894 ckan/controllers/user.py:548 +======= +#: ckan/controllers/group.py:805 +msgid "CKAN Group Revision History" +msgstr "" + +#: ckan/controllers/group.py:809 +msgid "Recent changes to CKAN Group: " +msgstr "" + +#: ckan/controllers/group.py:830 ckan/controllers/package.py:462 +msgid "Log message: " +msgstr "" + +#: ckan/controllers/group.py:858 +msgid "Unauthorized to read group {group_id}" +msgstr "" + +#: ckan/controllers/group.py:879 ckan/controllers/package.py:1195 +#: ckan/controllers/user.py:684 +msgid "You are now following {0}" +msgstr "" + +#: ckan/controllers/group.py:899 ckan/controllers/package.py:1214 +#: ckan/controllers/user.py:704 +msgid "You are no longer following {0}" +msgstr "" + +#: ckan/controllers/group.py:919 ckan/controllers/user.py:549 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to view followers %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/home.py:37 msgid "This site is currently off-line. Database is not initialised." msgstr "" @@ -412,15 +680,27 @@ msgid "" msgstr "" #: ckan/controllers/home.py:103 +======= +#: ckan/controllers/home.py:34 +msgid "This site is currently off-line. Database is not initialised." +msgstr "" + +#: ckan/controllers/home.py:79 +>>>>>>> 1596665... Update translations #, python-format msgid "Please update your profile and add your email address. " msgstr "" +<<<<<<< HEAD #: ckan/controllers/home.py:105 +======= +#: ckan/controllers/home.py:81 +>>>>>>> 1596665... Update translations #, python-format msgid "%s uses your email address if you need to reset your password." msgstr "" +<<<<<<< HEAD #: ckan/controllers/home.py:109 #, python-format msgid "Please update your profile and add your full name." @@ -448,22 +728,55 @@ msgstr "" #: ckan/controllers/package.py:850 ckan/controllers/package.py:868 #: ckan/controllers/package.py:967 ckan/controllers/package.py:1014 #: ckan/controllers/package.py:1264 ckan/controllers/related.py:124 +======= +#: ckan/controllers/package.py:293 +msgid "Parameter \"{parameter_name}\" is not an integer" +msgstr "" + +#: ckan/controllers/package.py:317 ckan/controllers/package.py:325 +#: ckan/controllers/package.py:365 ckan/controllers/package.py:431 +#: ckan/controllers/package.py:765 ckan/controllers/package.py:824 +#: ckan/controllers/package.py:842 ckan/controllers/package.py:943 +#: ckan/controllers/package.py:991 ckan/controllers/package.py:1043 +#: ckan/controllers/package.py:1085 ckan/controllers/package.py:1240 +#: ckan/controllers/package.py:1256 ckan/controllers/package.py:1323 +#: ckan/controllers/package.py:1424 ckan/controllers/package.py:1461 +#: ckan/controllers/package.py:1574 +msgid "Dataset not found" +msgstr "" + +#: ckan/controllers/package.py:327 ckan/controllers/package.py:367 +#: ckan/controllers/package.py:429 ckan/controllers/package.py:763 +#: ckan/controllers/package.py:822 ckan/controllers/package.py:840 +#: ckan/controllers/package.py:941 ckan/controllers/package.py:989 +#: ckan/controllers/package.py:1242 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to read package %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:385 ckan/controllers/package.py:387 #: ckan/controllers/package.py:389 +======= +#: ckan/controllers/package.py:353 ckan/controllers/package.py:355 +#: ckan/controllers/package.py:357 +>>>>>>> 1596665... Update translations #, python-format msgid "Invalid revision format: %r" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:427 +======= +#: ckan/controllers/package.py:393 +>>>>>>> 1596665... Update translations msgid "" "Viewing {package_type} datasets in {format} format is not supported " "(template file {file} not found)." msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:472 msgid "CKAN Dataset Revision History" msgstr "" @@ -537,17 +850,98 @@ msgid "Resource has been deleted." msgstr "" #: ckan/controllers/package.py:1097 +======= +#: ckan/controllers/package.py:438 +msgid "CKAN Dataset Revision History" +msgstr "" + +#: ckan/controllers/package.py:441 +msgid "Recent changes to CKAN Dataset: " +msgstr "" + +#: ckan/controllers/package.py:498 +msgid "Unauthorized to create a package" +msgstr "" + +#: ckan/controllers/package.py:576 ckanext/datapusher/plugin.py:59 +msgid "Unauthorized to edit this resource" +msgstr "" + +#: ckan/controllers/package.py:599 ckan/controllers/package.py:1072 +#: ckan/controllers/package.py:1094 ckan/controllers/package.py:1163 +#: ckan/controllers/package.py:1353 ckan/controllers/package.py:1435 +#: ckan/controllers/package.py:1468 ckan/controllers/package.py:1582 +#: ckan/controllers/package.py:1638 ckanext/datapusher/plugin.py:57 +#: ckanext/resourceproxy/controller.py:31 +msgid "Resource not found" +msgstr "" + +#: ckan/controllers/package.py:653 +msgid "Unauthorized to update dataset" +msgstr "" + +#: ckan/controllers/package.py:655 ckan/controllers/package.py:692 +#: ckan/controllers/package.py:721 +msgid "The dataset {id} could not be found." +msgstr "" + +#: ckan/controllers/package.py:659 +msgid "You must add at least one data resource" +msgstr "" + +#: ckan/controllers/package.py:667 ckanext/datapusher/helpers.py:22 +msgid "Error" +msgstr "" + +#: ckan/controllers/package.py:690 +msgid "Unauthorized to create a resource" +msgstr "" + +#: ckan/controllers/package.py:726 +msgid "Unauthorized to create a resource for this package" +msgstr "" + +#: ckan/controllers/package.py:951 +msgid "Unable to add package to search index." +msgstr "" + +#: ckan/controllers/package.py:999 +msgid "Unable to update search index." +msgstr "" + +#: ckan/controllers/package.py:1036 +msgid "Dataset has been deleted." +msgstr "" + +#: ckan/controllers/package.py:1041 ckan/controllers/package.py:1059 +#, python-format +msgid "Unauthorized to delete package %s" +msgstr "" + +#: ckan/controllers/package.py:1064 +msgid "Resource has been deleted." +msgstr "" + +#: ckan/controllers/package.py:1070 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to delete resource %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:1112 ckan/controllers/package.py:1280 #: ckan/controllers/package.py:1349 ckan/controllers/package.py:1448 #: ckan/controllers/package.py:1485 ckan/controllers/package.py:1598 +======= +#: ckan/controllers/package.py:1087 ckan/controllers/package.py:1258 +#: ckan/controllers/package.py:1325 ckan/controllers/package.py:1426 +#: ckan/controllers/package.py:1463 ckan/controllers/package.py:1576 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to read dataset %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:1157 ckan/controllers/package.py:1619 msgid "Resource view not found" msgstr "" @@ -555,10 +949,20 @@ msgstr "" #: ckan/controllers/package.py:1188 ckan/controllers/package.py:1379 #: ckan/controllers/package.py:1459 ckan/controllers/package.py:1492 #: ckan/controllers/package.py:1606 ckan/controllers/package.py:1662 +======= +#: ckan/controllers/package.py:1133 ckan/controllers/package.py:1597 +msgid "Resource view not found" +msgstr "" + +#: ckan/controllers/package.py:1165 ckan/controllers/package.py:1355 +#: ckan/controllers/package.py:1437 ckan/controllers/package.py:1470 +#: ckan/controllers/package.py:1584 ckan/controllers/package.py:1640 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to read resource %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:1197 msgid "Resource data not found" msgstr "" @@ -576,10 +980,30 @@ msgid "View not found" msgstr "" #: ckan/controllers/package.py:1547 +======= +#: ckan/controllers/package.py:1174 +msgid "Resource data not found" +msgstr "" + +#: ckan/controllers/package.py:1183 +msgid "No download is available" +msgstr "" + +#: ckan/controllers/package.py:1505 +msgid "Unauthorized to edit resource" +msgstr "" + +#: ckan/controllers/package.py:1523 +msgid "View not found" +msgstr "" + +#: ckan/controllers/package.py:1525 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to view View %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:1553 msgid "View Type Not found" msgstr "" @@ -589,10 +1013,22 @@ msgid "Bad resource view data" msgstr "" #: ckan/controllers/package.py:1622 +======= +#: ckan/controllers/package.py:1531 +msgid "View Type Not found" +msgstr "" + +#: ckan/controllers/package.py:1591 +msgid "Bad resource view data" +msgstr "" + +#: ckan/controllers/package.py:1600 +>>>>>>> 1596665... Update translations #, python-format msgid "Unauthorized to read resource view %s" msgstr "" +<<<<<<< HEAD #: ckan/controllers/package.py:1625 msgid "Resource view not supplied" msgstr "" @@ -683,6 +1119,16 @@ msgstr "" msgid "Visualization" msgstr "" +======= +#: ckan/controllers/package.py:1603 +msgid "Resource view not supplied" +msgstr "" + +#: ckan/controllers/package.py:1632 +msgid "No preview has been defined." +msgstr "" + +>>>>>>> 1596665... Update translations #: ckan/controllers/revision.py:42 msgid "CKAN Repository Revision History" msgstr "" @@ -829,6 +1275,7 @@ msgstr "" msgid "You must provide a password" msgstr "" +<<<<<<< HEAD #: ckan/controllers/user.py:601 msgid "Follow item not found" msgstr "" @@ -842,6 +1289,21 @@ msgid "Unauthorized to read {0} {1}" msgstr "" #: ckan/controllers/user.py:622 +======= +#: ckan/controllers/user.py:602 +msgid "Follow item not found" +msgstr "" + +#: ckan/controllers/user.py:606 +msgid "{0} not found" +msgstr "" + +#: ckan/controllers/user.py:608 +msgid "Unauthorized to read {0} {1}" +msgstr "" + +#: ckan/controllers/user.py:623 +>>>>>>> 1596665... Update translations msgid "Everything" msgstr "" @@ -971,7 +1433,11 @@ msgid "View" msgstr "" #: ckan/lib/email_notifications.py:103 +<<<<<<< HEAD msgid "1 new activity from {site_title}" +======= +msgid "{n} new activity from {site_title}" +>>>>>>> 1596665... Update translations msgid_plural "{n} new activities from {site_title}" msgstr[0] "" msgstr[1] "" @@ -1024,40 +1490,65 @@ msgstr "" msgid "December" msgstr "" +<<<<<<< HEAD #: ckan/lib/formatters.py:109 msgid "Just now" msgstr "" #: ckan/lib/formatters.py:111 +======= +#: ckan/lib/formatters.py:114 +msgid "Just now" +msgstr "" + +#: ckan/lib/formatters.py:116 +>>>>>>> 1596665... Update translations msgid "{mins} minute ago" msgid_plural "{mins} minutes ago" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/formatters.py:114 +======= +#: ckan/lib/formatters.py:119 +>>>>>>> 1596665... Update translations msgid "{hours} hour ago" msgid_plural "{hours} hours ago" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/formatters.py:120 +======= +#: ckan/lib/formatters.py:125 +>>>>>>> 1596665... Update translations msgid "{days} day ago" msgid_plural "{days} days ago" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/formatters.py:123 +======= +#: ckan/lib/formatters.py:128 +>>>>>>> 1596665... Update translations msgid "{months} month ago" msgid_plural "{months} months ago" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/formatters.py:125 +======= +#: ckan/lib/formatters.py:130 +>>>>>>> 1596665... Update translations msgid "over {years} year ago" msgid_plural "over {years} years ago" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/formatters.py:138 msgid "{month} {day}, {year}, {hour:02}:{min:02}" msgstr "" @@ -1147,12 +1638,107 @@ msgid "Edited settings." msgstr "" #: ckan/lib/helpers.py:1456 +======= +#: ckan/lib/formatters.py:146 +msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" +msgstr "" + +#: ckan/lib/formatters.py:151 +msgid "{month} {day}, {year}" +msgstr "" + +#: ckan/lib/formatters.py:167 +msgid "{bytes} bytes" +msgstr "" + +#: ckan/lib/formatters.py:169 +msgid "{kibibytes} KiB" +msgstr "" + +#: ckan/lib/formatters.py:171 +msgid "{mebibytes} MiB" +msgstr "" + +#: ckan/lib/formatters.py:173 +msgid "{gibibytes} GiB" +msgstr "" + +#: ckan/lib/formatters.py:175 +msgid "{tebibytes} TiB" +msgstr "" + +#: ckan/lib/formatters.py:187 +msgid "{n}" +msgstr "" + +#: ckan/lib/formatters.py:189 +msgid "{k}k" +msgstr "" + +#: ckan/lib/formatters.py:191 +msgid "{m}M" +msgstr "" + +#: ckan/lib/formatters.py:193 +msgid "{g}G" +msgstr "" + +#: ckan/lib/formatters.py:195 +msgid "{t}T" +msgstr "" + +#: ckan/lib/formatters.py:197 +msgid "{p}P" +msgstr "" + +#: ckan/lib/formatters.py:199 +msgid "{e}E" +msgstr "" + +#: ckan/lib/formatters.py:201 +msgid "{z}Z" +msgstr "" + +#: ckan/lib/formatters.py:203 +msgid "{y}Y" +msgstr "" + +#: ckan/lib/helpers.py:939 +msgid "Update your avatar at gravatar.com" +msgstr "" + +#: ckan/lib/helpers.py:1145 ckan/lib/helpers.py:1157 +msgid "Unknown" +msgstr "" + +#: ckan/lib/helpers.py:1202 +msgid "Unnamed resource" +msgstr "" + +#: ckan/lib/helpers.py:1250 +msgid "Created new dataset." +msgstr "" + +#: ckan/lib/helpers.py:1252 +msgid "Edited resources." +msgstr "" + +#: ckan/lib/helpers.py:1254 +msgid "Edited settings." +msgstr "" + +#: ckan/lib/helpers.py:1518 +>>>>>>> 1596665... Update translations msgid "{number} view" msgid_plural "{number} views" msgstr[0] "" msgstr[1] "" +<<<<<<< HEAD #: ckan/lib/helpers.py:1458 +======= +#: ckan/lib/helpers.py:1520 +>>>>>>> 1596665... Update translations msgid "{number} recent view" msgid_plural "{number} recent views" msgstr[0] "" @@ -1208,7 +1794,11 @@ msgstr "" #: ckan/lib/navl/dictization_functions.py:23 #: ckan/lib/navl/dictization_functions.py:25 ckan/lib/navl/validators.py:23 #: ckan/lib/navl/validators.py:30 ckan/lib/navl/validators.py:50 +<<<<<<< HEAD #: ckan/logic/validators.py:625 ckan/logic/action/get.py:1941 +======= +#: ckan/logic/validators.py:630 ckan/logic/action/get.py:2107 +>>>>>>> 1596665... Update translations msgid "Missing value" msgstr "" @@ -1221,7 +1811,11 @@ msgstr "" msgid "Please enter an integer value" msgstr "" +<<<<<<< HEAD #: ckan/logic/__init__.py:102 ckan/logic/action/__init__.py:58 +======= +#: ckan/logic/__init__.py:89 ckan/logic/action/__init__.py:58 +>>>>>>> 1596665... Update translations #: ckan/templates/package/edit_base.html:21 #: ckan/templates/package/resources.html:5 #: ckan/templates/package/snippets/package_context.html:12 @@ -1231,11 +1825,19 @@ msgstr "" msgid "Resources" msgstr "" +<<<<<<< HEAD #: ckan/logic/__init__.py:102 ckan/logic/action/__init__.py:58 msgid "Package resource(s) invalid" msgstr "" #: ckan/logic/__init__.py:109 ckan/logic/__init__.py:111 +======= +#: ckan/logic/__init__.py:89 ckan/logic/action/__init__.py:58 +msgid "Package resource(s) invalid" +msgstr "" + +#: ckan/logic/__init__.py:96 ckan/logic/__init__.py:98 +>>>>>>> 1596665... Update translations #: ckan/logic/action/__init__.py:60 ckan/logic/action/__init__.py:62 msgid "Extras" msgstr "" @@ -1246,7 +1848,11 @@ msgid "Tag vocabulary \"%s\" does not exist" msgstr "" #: ckan/logic/converters.py:119 ckan/logic/validators.py:211 +<<<<<<< HEAD #: ckan/logic/validators.py:228 ckan/logic/validators.py:724 +======= +#: ckan/logic/validators.py:228 ckan/logic/validators.py:729 +>>>>>>> 1596665... Update translations #: ckan/templates/group/members.html:17 #: ckan/templates/organization/members.html:17 #: ckanext/stats/templates/ckanext/stats/index.html:156 @@ -1254,16 +1860,24 @@ msgid "User" msgstr "" #: ckan/logic/converters.py:144 ckan/logic/validators.py:146 +<<<<<<< HEAD #: ckan/logic/validators.py:188 ckan/templates/package/read_base.html:24 #: ckanext/stats/templates/ckanext/stats/index.html:89 #: ckanext/stats/templates_legacy/ckanext/stats/index.html:65 #: ckanext/stats/templates_legacy/ckanext/stats/index.html:74 +======= +#: ckan/logic/validators.py:188 ckan/templates/package/read_base.html:19 +#: ckanext/stats/templates/ckanext/stats/index.html:89 +>>>>>>> 1596665... Update translations msgid "Dataset" msgstr "" #: ckan/logic/converters.py:169 ckan/logic/validators.py:241 #: ckanext/stats/templates/ckanext/stats/index.html:113 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:82 +======= +>>>>>>> 1596665... Update translations msgid "Group" msgstr "" @@ -1311,8 +1925,12 @@ msgstr "" msgid "Resource" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:255 ckan/templates/package/read_base.html:27 #: ckan/templates/package/related_list.html:4 +======= +#: ckan/logic/validators.py:255 ckan/templates/package/related_list.html:4 +>>>>>>> 1596665... Update translations #: ckan/templates/snippets/related.html:2 msgid "Related" msgstr "" @@ -1338,7 +1956,11 @@ msgstr "" msgid "Must be at least %s characters long" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:363 ckan/logic/validators.py:641 +======= +#: ckan/logic/validators.py:363 ckan/logic/validators.py:646 +>>>>>>> 1596665... Update translations #, python-format msgid "Name must be a maximum of %i characters long" msgstr "" @@ -1421,44 +2043,77 @@ msgstr "" msgid "The passwords you entered do not match" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:629 +======= +#: ckan/logic/validators.py:634 +>>>>>>> 1596665... Update translations msgid "" "Edit not allowed as it looks like spam. Please avoid links in your " "description." msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:638 +======= +#: ckan/logic/validators.py:643 +>>>>>>> 1596665... Update translations #, python-format msgid "Name must be at least %s characters long" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:646 msgid "That vocabulary name is already in use." msgstr "" #: ckan/logic/validators.py:652 +======= +#: ckan/logic/validators.py:651 +msgid "That vocabulary name is already in use." +msgstr "" + +#: ckan/logic/validators.py:657 +>>>>>>> 1596665... Update translations #, python-format msgid "Cannot change value of key from %s to %s. This key is read-only" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:661 msgid "Tag vocabulary was not found." msgstr "" #: ckan/logic/validators.py:674 +======= +#: ckan/logic/validators.py:666 +msgid "Tag vocabulary was not found." +msgstr "" + +#: ckan/logic/validators.py:679 +>>>>>>> 1596665... Update translations #, python-format msgid "Tag %s does not belong to vocabulary %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:680 msgid "No tag name" msgstr "" #: ckan/logic/validators.py:693 +======= +#: ckan/logic/validators.py:685 +msgid "No tag name" +msgstr "" + +#: ckan/logic/validators.py:698 +>>>>>>> 1596665... Update translations #, python-format msgid "Tag %s already belongs to vocabulary %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/validators.py:716 msgid "Please provide a valid URL" msgstr "" @@ -1500,20 +2155,72 @@ msgid "There is a schema field with the same name" msgstr "" #: ckan/logic/action/create.py:178 ckan/logic/action/create.py:722 +======= +#: ckan/logic/validators.py:721 +msgid "Please provide a valid URL" +msgstr "" + +#: ckan/logic/validators.py:735 +msgid "role does not exist." +msgstr "" + +#: ckan/logic/validators.py:764 +msgid "Datasets with no organization can't be private." +msgstr "" + +#: ckan/logic/validators.py:770 +msgid "Not a list" +msgstr "" + +#: ckan/logic/validators.py:773 +msgid "Not a string" +msgstr "" + +#: ckan/logic/validators.py:805 +msgid "This parent would create a loop in the hierarchy" +msgstr "" + +#: ckan/logic/validators.py:815 +msgid "\"filter_fields\" and \"filter_values\" should have the same length" +msgstr "" + +#: ckan/logic/validators.py:826 +msgid "\"filter_fields\" is required when \"filter_values\" is filled" +msgstr "" + +#: ckan/logic/validators.py:829 +msgid "\"filter_values\" is required when \"filter_fields\" is filled" +msgstr "" + +#: ckan/logic/validators.py:843 +msgid "There is a schema field with the same name" +msgstr "" + +#: ckan/logic/action/create.py:178 ckan/logic/action/create.py:723 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Create object %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/create.py:601 +======= +#: ckan/logic/action/create.py:602 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Create package relationship: %s %s %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/create.py:642 +======= +#: ckan/logic/action/create.py:643 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Create member object %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/create.py:865 msgid "Trying to create an organization as a group" msgstr "" @@ -1531,10 +2238,30 @@ msgid "Rating must be an integer value." msgstr "" #: ckan/logic/action/create.py:966 +======= +#: ckan/logic/action/create.py:862 +msgid "Trying to create an organization as a group" +msgstr "" + +#: ckan/logic/action/create.py:951 +msgid "You must supply a package id or name (parameter \"package\")." +msgstr "" + +#: ckan/logic/action/create.py:954 +msgid "You must supply a rating (parameter \"rating\")." +msgstr "" + +#: ckan/logic/action/create.py:959 +msgid "Rating must be an integer value." +msgstr "" + +#: ckan/logic/action/create.py:963 +>>>>>>> 1596665... Update translations #, python-format msgid "Rating must be between %i and %i." msgstr "" +<<<<<<< HEAD #: ckan/logic/action/create.py:1311 ckan/logic/action/create.py:1318 msgid "You must be logged in to follow users" msgstr "" @@ -1561,20 +2288,57 @@ msgid "You must be logged in to follow a group." msgstr "" #: ckan/logic/action/delete.py:66 +======= +#: ckan/logic/action/create.py:1312 ckan/logic/action/create.py:1319 +msgid "You must be logged in to follow users" +msgstr "" + +#: ckan/logic/action/create.py:1332 +msgid "You cannot follow yourself" +msgstr "" + +#: ckan/logic/action/create.py:1340 ckan/logic/action/create.py:1397 +#: ckan/logic/action/create.py:1530 +msgid "You are already following {0}" +msgstr "" + +#: ckan/logic/action/create.py:1371 ckan/logic/action/create.py:1379 +msgid "You must be logged in to follow a dataset." +msgstr "" + +#: ckan/logic/action/create.py:1431 +msgid "User {username} does not exist." +msgstr "" + +#: ckan/logic/action/create.py:1506 ckan/logic/action/create.py:1514 +msgid "You must be logged in to follow a group." +msgstr "" + +#: ckan/logic/action/delete.py:72 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Delete Package: %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/delete.py:195 ckan/logic/action/delete.py:324 +======= +#: ckan/logic/action/delete.py:241 ckan/logic/action/delete.py:370 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Delete %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/delete.py:284 +======= +#: ckan/logic/action/delete.py:330 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Delete Member: %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/delete.py:483 ckan/logic/action/delete.py:509 #: ckan/logic/action/get.py:2394 ckan/logic/action/update.py:984 msgid "id not in data" @@ -1582,15 +2346,29 @@ msgstr "" #: ckan/logic/action/delete.py:487 ckan/logic/action/get.py:2397 #: ckan/logic/action/update.py:988 +======= +#: ckan/logic/action/delete.py:556 ckan/logic/action/delete.py:582 +#: ckan/logic/action/get.py:2506 ckan/logic/action/update.py:993 +msgid "id not in data" +msgstr "" + +#: ckan/logic/action/delete.py:560 ckan/logic/action/get.py:2509 +#: ckan/logic/action/update.py:997 +>>>>>>> 1596665... Update translations #, python-format msgid "Could not find vocabulary \"%s\"" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/delete.py:517 +======= +#: ckan/logic/action/delete.py:590 +>>>>>>> 1596665... Update translations #, python-format msgid "Could not find tag \"%s\"" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/delete.py:543 ckan/logic/action/delete.py:547 msgid "You must be logged in to unfollow something." msgstr "" @@ -1620,29 +2398,76 @@ msgstr "" msgid "unknown user:" msgstr "" +======= +#: ckan/logic/action/delete.py:616 ckan/logic/action/delete.py:620 +msgid "You must be logged in to unfollow something." +msgstr "" + +#: ckan/logic/action/delete.py:631 +msgid "You are not following {0}." +msgstr "" + +#: ckan/logic/action/get.py:1147 ckan/logic/action/update.py:133 +#: ckan/logic/action/update.py:147 +msgid "Resource was not found." +msgstr "" + +#: ckan/logic/action/get.py:2111 +msgid "Do not specify if using \"query\" parameter" +msgstr "" + +#: ckan/logic/action/get.py:2120 +msgid "Must be : pair(s)" +msgstr "" + +#: ckan/logic/action/get.py:2152 +msgid "Field \"{field}\" not recognised in resource_search." +msgstr "" + +>>>>>>> 1596665... Update translations #: ckan/logic/action/update.py:68 msgid "Item was not found." msgstr "" +<<<<<<< HEAD #: ckan/logic/action/update.py:296 ckan/logic/action/update.py:1179 msgid "Package was not found." msgstr "" #: ckan/logic/action/update.py:339 ckan/logic/action/update.py:557 +======= +#: ckan/logic/action/update.py:297 ckan/logic/action/update.py:1094 +msgid "Package was not found." +msgstr "" + +#: ckan/logic/action/update.py:340 ckan/logic/action/update.py:561 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Update object %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/update.py:440 +======= +#: ckan/logic/action/update.py:443 +>>>>>>> 1596665... Update translations #, python-format msgid "REST API: Update package relationship: %s %s %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/action/update.py:792 msgid "TaskStatus was not found." msgstr "" #: ckan/logic/action/update.py:1183 +======= +#: ckan/logic/action/update.py:801 +msgid "TaskStatus was not found." +msgstr "" + +#: ckan/logic/action/update.py:1098 +>>>>>>> 1596665... Update translations msgid "Organization was not found." msgstr "" @@ -1673,7 +2498,11 @@ msgstr "" msgid "No dataset id provided, cannot check auth." msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/create.py:84 ckan/logic/auth/delete.py:28 +======= +#: ckan/logic/auth/create.py:84 ckan/logic/auth/delete.py:32 +>>>>>>> 1596665... Update translations #: ckan/logic/auth/get.py:135 ckan/logic/auth/update.py:61 msgid "No package found for this resource, cannot check auth." msgstr "" @@ -1728,11 +2557,16 @@ msgstr "" msgid "User %s not authorized to edit group %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:34 +======= +#: ckan/logic/auth/delete.py:38 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete resource %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:50 msgid "Resource view not found, cannot check auth." msgstr "" @@ -1742,35 +2576,73 @@ msgid "Only the owner can delete a related item" msgstr "" #: ckan/logic/auth/delete.py:91 +======= +#: ckan/logic/auth/delete.py:54 +msgid "Resource view not found, cannot check auth." +msgstr "" + +#: ckan/logic/auth/delete.py:69 ckan/logic/auth/delete.py:83 +msgid "Only the owner can delete a related item" +msgstr "" + +#: ckan/logic/auth/delete.py:95 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete relationship %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:100 +======= +#: ckan/logic/auth/delete.py:104 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete groups" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:104 +======= +#: ckan/logic/auth/delete.py:108 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete group %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:121 +======= +#: ckan/logic/auth/delete.py:125 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete organizations" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:125 +======= +#: ckan/logic/auth/delete.py:129 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete organization %s" msgstr "" +<<<<<<< HEAD #: ckan/logic/auth/delete.py:138 +======= +#: ckan/logic/auth/delete.py:142 +>>>>>>> 1596665... Update translations #, python-format msgid "User %s not authorized to delete task_status" msgstr "" +<<<<<<< HEAD +======= +#: ckan/logic/auth/get.py:10 ckan/logic/auth/get.py:270 +msgid "Not authorized" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckan/logic/auth/get.py:97 #, python-format msgid "User %s not authorized to read these packages" @@ -2032,8 +2904,11 @@ msgstr "" #: ckan/templates/organization/confirm_delete_member.html:15 #: ckan/templates/package/confirm_delete.html:14 #: ckan/templates/package/confirm_delete_resource.html:14 +<<<<<<< HEAD #: ckan/templates/related/confirm_delete.html:14 #: ckan/templates/related/snippets/related_form.html:32 +======= +>>>>>>> 1596665... Update translations msgid "Cancel" msgstr "" @@ -2137,7 +3012,10 @@ msgstr "" #: ckan/templates/organization/snippets/organization_form.html:18 #: ckan/templates/package/snippets/package_basic_fields.html:13 #: ckan/templates/package/snippets/resource_form.html:24 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:19 +======= +>>>>>>> 1596665... Update translations msgid "URL" msgstr "" @@ -2151,7 +3029,10 @@ msgstr "" #: ckan/templates/package/resource_edit.html:3 #: ckan/templates/package/resource_edit_base.html:12 #: ckan/templates/package/snippets/resource_item.html:57 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_item.html:36 +======= +>>>>>>> 1596665... Update translations msgid "Edit" msgstr "" @@ -2234,7 +3115,10 @@ msgstr "" #: ckan/templates/package/base.html:21 ckan/templates/package/search.html:4 #: ckan/templates/package/search.html:7 #: ckan/templates/package/snippets/new_package_breadcrumb.html:1 +<<<<<<< HEAD #: ckan/templates/related/base_form_page.html:4 +======= +>>>>>>> 1596665... Update translations #: ckan/templates/revision/diff.html:11 ckan/templates/revision/read.html:65 #: ckan/templates/snippets/organization.html:59 #: ckan/templates/snippets/context/group.html:17 @@ -2374,7 +3258,10 @@ msgid "" msgstr "" #: ckan/templates/ajax_snippets/api_info.html:42 +<<<<<<< HEAD #: ckan/templates/related/edit_form.html:7 +======= +>>>>>>> 1596665... Update translations msgid "Create" msgstr "" @@ -2526,7 +3413,11 @@ msgstr "" #: ckan/templates/organization/read_base.html:18 #: ckan/templates/package/activity.html:3 #: ckan/templates/package/activity.html:6 +<<<<<<< HEAD #: ckan/templates/package/read_base.html:26 +======= +#: ckan/templates/package/read_base.html:21 +>>>>>>> 1596665... Update translations #: ckan/templates/user/activity_stream.html:3 #: ckan/templates/user/activity_stream.html:6 #: ckan/templates/user/read_base.html:20 @@ -2559,8 +3450,11 @@ msgstr "" #: ckan/templates/package/confirm_delete.html:15 #: ckan/templates/package/confirm_delete_resource.html:3 #: ckan/templates/package/confirm_delete_resource.html:15 +<<<<<<< HEAD #: ckan/templates/related/confirm_delete.html:3 #: ckan/templates/related/confirm_delete.html:15 +======= +>>>>>>> 1596665... Update translations msgid "Confirm Delete" msgstr "" @@ -2578,7 +3472,11 @@ msgstr "" #: ckan/templates/group/read_base.html:12 #: ckan/templates/organization/edit_base.html:11 #: ckan/templates/organization/read_base.html:12 +<<<<<<< HEAD #: ckan/templates/package/read_base.html:19 +======= +#: ckan/templates/package/read_base.html:14 +>>>>>>> 1596665... Update translations #: ckan/templates/package/resource_read.html:31 #: ckan/templates/user/edit.html:8 ckan/templates/user/edit_base.html:3 #: ckan/templates/user/read_base.html:14 @@ -2723,14 +3621,20 @@ msgstr "" #: ckan/templates/package/edit_view.html:19 #: ckan/templates/package/snippets/package_form.html:40 #: ckan/templates/package/snippets/resource_form.html:66 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:29 +======= +>>>>>>> 1596665... Update translations #: ckan/templates/revision/read.html:24 #: ckan/templates/user/edit_user_form.html:38 msgid "Delete" msgstr "" #: ckan/templates/group/member_new.html:61 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:33 +======= +>>>>>>> 1596665... Update translations msgid "Save" msgstr "" @@ -2818,7 +3722,10 @@ msgstr "" #: ckan/templates/package/snippets/package_basic_fields.html:19 #: ckan/templates/package/snippets/resource_form.html:32 #: ckan/templates/package/snippets/view_form.html:9 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:21 +======= +>>>>>>> 1596665... Update translations msgid "Description" msgstr "" @@ -2879,7 +3786,11 @@ msgstr "" #: ckan/templates/group/snippets/info.html:16 #: ckan/templates/organization/bulk_process.html:72 #: ckan/templates/package/read.html:21 +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:111 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:112 +>>>>>>> 1596665... Update translations #: ckan/templates/snippets/organization.html:37 #: ckan/templates/snippets/package_item.html:42 msgid "Deleted" @@ -3003,6 +3914,7 @@ msgstr "" msgid "groups" msgstr "" +<<<<<<< HEAD #: ckan/templates/home/snippets/stats.html:29 msgid "related item" msgstr "" @@ -3011,6 +3923,8 @@ msgstr "" msgid "related items" msgstr "" +======= +>>>>>>> 1596665... Update translations #: ckan/templates/macros/form.html:126 #, python-format msgid "" @@ -3028,7 +3942,10 @@ msgid "Custom" msgstr "" #: ckan/templates/macros/form.html:290 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:7 +======= +>>>>>>> 1596665... Update translations msgid "The form contains invalid entries:" msgstr "" @@ -3041,7 +3958,10 @@ msgid "http://example.com/my-image.jpg" msgstr "" #: ckan/templates/macros/form.html:411 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:20 +======= +>>>>>>> 1596665... Update translations msgid "Image URL" msgstr "" @@ -3086,7 +4006,11 @@ msgstr "" #: ckan/templates/organization/bulk_process.html:75 #: ckan/templates/package/read.html:11 +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:91 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:92 +>>>>>>> 1596665... Update translations #: ckan/templates/snippets/package_item.html:31 #: ckan/templates/snippets/private.html:2 #: ckan/templates/user/read_base.html:82 ckan/templates/user/read_base.html:96 @@ -3268,7 +4192,10 @@ msgid "Preview" msgstr "" #: ckan/templates/package/edit_view.html:21 +<<<<<<< HEAD #: ckan/templates/related/edit_form.html:5 +======= +>>>>>>> 1596665... Update translations msgid "Update" msgstr "" @@ -3327,7 +4254,11 @@ msgstr "" msgid "Add" msgstr "" +<<<<<<< HEAD #: ckan/templates/package/read_base.html:38 +======= +#: ckan/templates/package/read_base.html:32 +>>>>>>> 1596665... Update translations #, python-format msgid "" "This is an old revision of this dataset, as edited at %(timestamp)s. It may " @@ -3359,15 +4290,28 @@ msgstr "" msgid "Error:" msgstr "" +<<<<<<< HEAD #: ckan/templates/package/resource_data.html:45 msgid "Status" msgstr "" #: ckan/templates/package/resource_data.html:49 +======= +#: ckan/templates/package/resource_data.html:36 +msgid "Error traceback:" +msgstr "" + +#: ckan/templates/package/resource_data.html:48 +msgid "Status" +msgstr "" + +#: ckan/templates/package/resource_data.html:52 +>>>>>>> 1596665... Update translations #: ckan/templates/package/resource_read.html:157 msgid "Last updated" msgstr "" +<<<<<<< HEAD #: ckan/templates/package/resource_data.html:53 msgid "Never" msgstr "" @@ -3381,6 +4325,21 @@ msgid "Details" msgstr "" #: ckan/templates/package/resource_data.html:78 +======= +#: ckan/templates/package/resource_data.html:56 +msgid "Never" +msgstr "" + +#: ckan/templates/package/resource_data.html:62 +msgid "Upload Log" +msgstr "" + +#: ckan/templates/package/resource_data.html:74 +msgid "Details" +msgstr "" + +#: ckan/templates/package/resource_data.html:81 +>>>>>>> 1596665... Update translations msgid "End of log" msgstr "" @@ -3484,7 +4443,11 @@ msgid "unknown" msgstr "" #: ckan/templates/package/resource_read.html:161 +<<<<<<< HEAD #: ckan/templates/package/snippets/additional_info.html:68 +======= +#: ckan/templates/package/snippets/additional_info.html:70 +>>>>>>> 1596665... Update translations msgid "Created" msgstr "" @@ -3520,6 +4483,13 @@ msgid "" "add some?

" msgstr "" +<<<<<<< HEAD +======= +#: ckan/templates/package/search.html:52 +msgid "API" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckan/templates/package/search.html:53 msgid "API Docs" msgstr "" @@ -3576,7 +4546,11 @@ msgid "Version" msgstr "" #: ckan/templates/package/snippets/additional_info.html:56 +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:107 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:108 +>>>>>>> 1596665... Update translations #: ckan/templates/user/read_base.html:91 msgid "State" msgstr "" @@ -3591,7 +4565,10 @@ msgstr "" #: ckan/templates/package/snippets/package_basic_fields.html:4 #: ckan/templates/package/snippets/view_form.html:8 +<<<<<<< HEAD #: ckan/templates/related/snippets/related_form.html:18 +======= +>>>>>>> 1596665... Update translations msgid "Title" msgstr "" @@ -3611,17 +4588,26 @@ msgstr "" msgid "eg. economy, mental health, government" msgstr "" +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:40 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:41 +>>>>>>> 1596665... Update translations msgid "" " License definitions and additional information can be found at opendefinition.org " msgstr "" +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:69 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:70 +>>>>>>> 1596665... Update translations #: ckan/templates/snippets/organization.html:23 msgid "Organization" msgstr "" +<<<<<<< HEAD #: ckan/templates/package/snippets/package_basic_fields.html:73 msgid "No organization" msgstr "" @@ -3635,6 +4621,21 @@ msgid "Public" msgstr "" #: ckan/templates/package/snippets/package_basic_fields.html:110 +======= +#: ckan/templates/package/snippets/package_basic_fields.html:74 +msgid "No organization" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:89 +msgid "Visibility" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:92 +msgid "Public" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:111 +>>>>>>> 1596665... Update translations msgid "Active" msgstr "" @@ -3847,6 +4848,7 @@ msgstr "" msgid "A view is a representation of the data held against a resource" msgstr "" +<<<<<<< HEAD #: ckan/templates/related/base_form_page.html:12 msgid "Related Form" msgstr "" @@ -3980,6 +4982,8 @@ msgstr "" msgid "Go to {related_item_type}" msgstr "" +======= +>>>>>>> 1596665... Update translations #: ckan/templates/revision/diff.html:6 msgid "Differences" msgstr "" @@ -4067,7 +5071,10 @@ msgid "There are no {facet_type} that match this search" msgstr "" #: ckan/templates/snippets/home_breadcrumb_item.html:2 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:51 +======= +>>>>>>> 1596665... Update translations msgid "Home" msgstr "" @@ -4118,6 +5125,13 @@ msgstr "" msgid "Order by" msgstr "" +<<<<<<< HEAD +======= +#: ckan/templates/snippets/search_form.html:71 +msgid "Filter Results" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckan/templates/snippets/search_form.html:78 msgid "

Please try another search.

" msgstr "" @@ -4532,15 +5546,25 @@ msgstr "" msgid "DataStore resource not found" msgstr "" +<<<<<<< HEAD #: ckanext/datastore/db.py:656 +======= +#: ckanext/datastore/db.py:663 +>>>>>>> 1596665... Update translations msgid "" "The data was invalid (for example: a numeric value is out of range or was " "inserted into a text field)." msgstr "" +<<<<<<< HEAD #: ckanext/datastore/logic/action.py:210 ckanext/datastore/logic/action.py:250 #: ckanext/datastore/logic/action.py:327 ckanext/datastore/logic/action.py:411 #: ckanext/datastore/logic/action.py:493 ckanext/datastore/logic/action.py:519 +======= +#: ckanext/datastore/logic/action.py:215 ckanext/datastore/logic/action.py:255 +#: ckanext/datastore/logic/action.py:332 ckanext/datastore/logic/action.py:422 +#: ckanext/datastore/logic/action.py:504 ckanext/datastore/logic/action.py:530 +>>>>>>> 1596665... Update translations msgid "Resource \"{0}\" was not found." msgstr "" @@ -4582,6 +5606,13 @@ msgstr "" msgid "custom resource text" msgstr "" +<<<<<<< HEAD +======= +#: ckanext/example_itranslation/templates/home/index.html:4 +msgid "This is an untranslated string" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 #: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 msgid "This group has no description" @@ -4611,10 +5642,21 @@ msgstr "" msgid "Graph" msgstr "" +<<<<<<< HEAD #: ckanext/reclineview/plugin.py:212 msgid "Map" msgstr "" +======= +#: ckanext/reclineview/plugin.py:214 +msgid "Map" +msgstr "" + +#: ckanext/reclineview/theme/public/recline_view.js:34 +msgid "error loading view" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "Row offset" @@ -4672,7 +5714,10 @@ msgid "Cluster markers" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:10 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:57 +======= +>>>>>>> 1596665... Update translations msgid "Total number of Datasets" msgstr "" @@ -4700,33 +5745,51 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:58 #: ckanext/stats/templates/ckanext/stats/index.html:180 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:63 +======= +>>>>>>> 1596665... Update translations msgid "Top Rated Datasets" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:64 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:65 +======= +>>>>>>> 1596665... Update translations msgid "Average rating" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:65 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:65 +======= +>>>>>>> 1596665... Update translations msgid "Number of ratings" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:79 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:70 +======= +>>>>>>> 1596665... Update translations msgid "No ratings" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:84 #: ckanext/stats/templates/ckanext/stats/index.html:181 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:72 +======= +>>>>>>> 1596665... Update translations msgid "Most Edited Datasets" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:90 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:74 +======= +>>>>>>> 1596665... Update translations msgid "Number of edits" msgstr "" @@ -4736,12 +5799,18 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:108 #: ckanext/stats/templates/ckanext/stats/index.html:182 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:80 +======= +>>>>>>> 1596665... Update translations msgid "Largest Groups" msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:114 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:82 +======= +>>>>>>> 1596665... Update translations msgid "Number of datasets" msgstr "" @@ -4751,7 +5820,10 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:132 #: ckanext/stats/templates/ckanext/stats/index.html:183 +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:88 +======= +>>>>>>> 1596665... Update translations msgid "Top Tags" msgstr "" @@ -4766,7 +5838,11 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 +<<<<<<< HEAD msgid "Users Owning Most Datasets" +======= +msgid "Users Creating Most Datasets" +>>>>>>> 1596665... Update translations msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:175 @@ -4777,6 +5853,7 @@ msgstr "" msgid "Total Number of Datasets" msgstr "" +<<<<<<< HEAD #: ckanext/stats/templates_legacy/ckanext/stats/index.html:6 #: ckanext/stats/templates_legacy/ckanext/stats/index.html:8 msgid "Statistics" @@ -4812,10 +5889,20 @@ msgstr "" msgid "Choose area" msgstr "" +======= +>>>>>>> 1596665... Update translations #: ckanext/textview/plugin.py:65 ckanext/textview/plugin.py:67 msgid "Text" msgstr "" +<<<<<<< HEAD +======= +#: ckanext/textview/theme/public/text_view.js:5 +#, python-format +msgid "An error occurred: %(text)s %(error)s" +msgstr "" + +>>>>>>> 1596665... Update translations #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" msgstr "" diff --git a/ckan/i18n/nl/LC_MESSAGES/ckan.mo b/ckan/i18n/nl/LC_MESSAGES/ckan.mo index 88751b589bb..3a380cfef45 100644 Binary files a/ckan/i18n/nl/LC_MESSAGES/ckan.mo and b/ckan/i18n/nl/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/nl/LC_MESSAGES/ckan.po b/ckan/i18n/nl/LC_MESSAGES/ckan.po index ec7b139b3aa..8cf9a3be911 100644 --- a/ckan/i18n/nl/LC_MESSAGES/ckan.po +++ b/ckan/i18n/nl/LC_MESSAGES/ckan.po @@ -16,7 +16,7 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-12-14 16:48+0000\n" +"PO-Revision-Date: 2015-12-16 13:08+0000\n" "Last-Translator: Peter Vos \n" "Language-Team: Dutch (http://www.transifex.com/okfn/ckan/language/nl/)\n" "MIME-Version: 1.0\n" diff --git a/ckan/i18n/no/LC_MESSAGES/ckan.mo b/ckan/i18n/no/LC_MESSAGES/ckan.mo index a81d7c54120..f94a71aa03f 100644 Binary files a/ckan/i18n/no/LC_MESSAGES/ckan.mo and b/ckan/i18n/no/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/no/LC_MESSAGES/ckan.po b/ckan/i18n/no/LC_MESSAGES/ckan.po index 715ffc66986..ceb62988edb 100644 --- a/ckan/i18n/no/LC_MESSAGES/ckan.po +++ b/ckan/i18n/no/LC_MESSAGES/ckan.po @@ -4,7 +4,7 @@ # # Translators: # geirmund , 2012 -# Hilde Austlid , 2015 +# Hilde Austlid , 2015-2016 # , 2011 # Olav A. Øvrebø , 2014 # Open Knowledge Foundation , 2011 @@ -15,8 +15,8 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-11-26 14:22+0000\n" -"Last-Translator: dread \n" +"PO-Revision-Date: 2016-01-07 07:44+0000\n" +"Last-Translator: Hilde Austlid \n" "Language-Team: Norwegian (http://www.transifex.com/okfn/ckan/language/no/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -244,7 +244,7 @@ msgstr "Fant ikke gruppen" #: ckan/controllers/feed.py:234 msgid "Organization not found" -msgstr "" +msgstr "Fant ikke organisasjon." #: ckan/controllers/group.py:138 ckan/controllers/group.py:609 msgid "Incorrect group type" @@ -292,7 +292,7 @@ msgstr "Grupper" #: ckan/templates/tag/index.html:3 ckan/templates/tag/index.html:6 #: ckan/templates/tag/index.html:12 msgid "Tags" -msgstr "Stikkord (tags)" +msgstr "Stikkord" #: ckan/controllers/group.py:313 ckan/controllers/home.py:70 #: ckan/controllers/package.py:242 ckan/lib/helpers.py:758 @@ -348,7 +348,7 @@ msgstr "Gruppen har blitt slettet." #: ckan/controllers/group.py:653 #, python-format msgid "%s has been deleted." -msgstr "" +msgstr "%s har blitt slettet." #: ckan/controllers/group.py:729 #, python-format @@ -503,7 +503,7 @@ msgstr "Ikke autorisert til å opprette en ressurs" #: ckan/controllers/package.py:726 msgid "Unauthorized to create a resource for this package" -msgstr "" +msgstr "Ikke tilgang til å lage en ressurs for denne pakken" #: ckan/controllers/package.py:951 msgid "Unable to add package to search index." @@ -540,7 +540,7 @@ msgstr "Ikke autorisert til å lese datasettet %s" #: ckan/controllers/package.py:1133 ckan/controllers/package.py:1597 msgid "Resource view not found" -msgstr "" +msgstr "Fant ikke ressursvisning" #: ckan/controllers/package.py:1165 ckan/controllers/package.py:1355 #: ckan/controllers/package.py:1437 ckan/controllers/package.py:1470 @@ -559,33 +559,33 @@ msgstr "Materiale til nedlasting ikke tilgjengelig." #: ckan/controllers/package.py:1505 msgid "Unauthorized to edit resource" -msgstr "" +msgstr "Ikke tlgang til å redigere ressurs" #: ckan/controllers/package.py:1523 msgid "View not found" -msgstr "" +msgstr "Fant ikke visning" #: ckan/controllers/package.py:1525 #, python-format msgid "Unauthorized to view View %s" -msgstr "" +msgstr "Ikke tilgang til visningen %s" #: ckan/controllers/package.py:1531 msgid "View Type Not found" -msgstr "" +msgstr "Fant ikke visningstype" #: ckan/controllers/package.py:1591 msgid "Bad resource view data" -msgstr "" +msgstr "Feil i data for ressursvisning" #: ckan/controllers/package.py:1600 #, python-format msgid "Unauthorized to read resource view %s" -msgstr "" +msgstr "Ikke tillatelse til å se ressursvisningen %s" #: ckan/controllers/package.py:1603 msgid "Resource view not supplied" -msgstr "" +msgstr "Ressursvisning finnes ikke" #: ckan/controllers/package.py:1632 msgid "No preview has been defined." @@ -676,15 +676,15 @@ msgstr "Bruker %s ikke autorisert til å redigere %s" #: ckan/controllers/user.py:354 msgid "Password entered was incorrect" -msgstr "" +msgstr "Feil passord" #: ckan/controllers/user.py:355 ckan/templates/user/edit_user_form.html:27 msgid "Old Password" -msgstr "" +msgstr "Gammelt passord" #: ckan/controllers/user.py:355 msgid "incorrect password" -msgstr "" +msgstr "feil passord" #: ckan/controllers/user.py:396 msgid "Login failed. Bad username or password." @@ -759,7 +759,7 @@ msgstr "Manglende verdi" #: ckan/controllers/util.py:21 msgid "Redirecting to external site is not allowed." -msgstr "" +msgstr "Videresending til eksternt nettsted er ikke tillatt." #: ckan/lib/activity_streams.py:64 msgid "{actor} added the tag {tag} to the dataset {dataset}" @@ -881,8 +881,8 @@ msgstr "Se" #: ckan/lib/email_notifications.py:103 msgid "{n} new activity from {site_title}" msgid_plural "{n} new activities from {site_title}" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{n} ny aktivitet fra {site_title}" +msgstr[1] "{n} nye aktiviteter fra {site_title}" #: ckan/lib/formatters.py:17 msgid "January" @@ -968,7 +968,7 @@ msgstr[1] "mer enn {years} år siden" #: ckan/lib/formatters.py:146 msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" -msgstr "" +msgstr "{day}. {month} {year}, {hour:02}:{min:02} ({timezone})" #: ckan/lib/formatters.py:151 msgid "{month} {day}, {year}" @@ -1087,7 +1087,7 @@ msgid "" "Please click the following link to confirm this request:\n" "\n" " {reset_link}\n" -msgstr "" +msgstr "Du har bedt om å få tilbakestilt passordet ditt på {site_title}.\n\nKlikk på denne lenka for å bekrefte:\n\n {reset_link}\n" #: ckan/lib/mailer.py:119 msgid "" @@ -1096,7 +1096,7 @@ msgid "" "To accept this invite, please reset your password at:\n" "\n" " {reset_link}\n" -msgstr "" +msgstr "Du har fått en invitasjon til {site_title}. Vi har laget en bruker til deg med brukernavnet {user_name}. Du kan forandre det seinere.\n\nBruk denne lenka for å ta imot invitasjonen og resette passordet ditt:\n\n {reset_link}\n" #: ckan/lib/mailer.py:145 ckan/templates/user/request_reset.html:3 #: ckan/templates/user/request_reset.html:13 @@ -1210,7 +1210,7 @@ msgstr "Ingen lenker er tillatt i log_message." #: ckan/logic/validators.py:156 msgid "Dataset id already exists" -msgstr "" +msgstr "Datasett-ID finnes allerede" #: ckan/logic/validators.py:197 ckan/logic/validators.py:288 msgid "Resource" @@ -1240,7 +1240,7 @@ msgstr "Navnet kan ikke brukes" #: ckan/logic/validators.py:361 #, python-format msgid "Must be at least %s characters long" -msgstr "" +msgstr "Må være på minst %s tegn" #: ckan/logic/validators.py:363 ckan/logic/validators.py:646 #, python-format @@ -1251,7 +1251,7 @@ msgstr "Navnet må inneholde maksimalt %i tegn" msgid "" "Must be purely lowercase alphanumeric (ascii) characters and these symbols: " "-_" -msgstr "" +msgstr "Må bestå av små bokstaver, tall og og disse symbolene: -_" #: ckan/logic/validators.py:384 msgid "That URL is already in use." @@ -1389,19 +1389,19 @@ msgstr "Kan ikke sette dette elementet som overordnet, det ville føre til en si #: ckan/logic/validators.py:815 msgid "\"filter_fields\" and \"filter_values\" should have the same length" -msgstr "" +msgstr "\"filter_fields\" og \"filter_values\" må ha samme lengde" #: ckan/logic/validators.py:826 msgid "\"filter_fields\" is required when \"filter_values\" is filled" -msgstr "" +msgstr "\"filter_fields\" er nødvendig når \"filter_values\" er oppgitt" #: ckan/logic/validators.py:829 msgid "\"filter_values\" is required when \"filter_fields\" is filled" -msgstr "" +msgstr "\"filter_values\" er nødvendig når \"filter_fields\" er oppgitt" #: ckan/logic/validators.py:843 msgid "There is a schema field with the same name" -msgstr "" +msgstr "Det finnes et skjema-felt med samme navn" #: ckan/logic/action/create.py:178 ckan/logic/action/create.py:723 #, python-format @@ -1571,7 +1571,7 @@ msgstr "Du må logge inn for å legge til relatert informasjon" #: ckan/logic/auth/create.py:77 msgid "No dataset id provided, cannot check auth." -msgstr "" +msgstr "Datasett-ID ikke oppgitt, kan ikke sjekke autentisering." #: ckan/logic/auth/create.py:84 ckan/logic/auth/delete.py:32 #: ckan/logic/auth/get.py:135 ckan/logic/auth/update.py:61 @@ -1581,7 +1581,7 @@ msgstr "Ingen datakilde funnet for denne ressursen, kan ikke sjekke aut." #: ckan/logic/auth/create.py:92 #, python-format msgid "User %s not authorized to create resources on dataset %s" -msgstr "" +msgstr "Bruker %s har ikke tillatelse til å opprette ressurser for datasettet %s" #: ckan/logic/auth/create.py:124 #, python-format @@ -1635,7 +1635,7 @@ msgstr "Bruker %s ikke autorisert til å slette ressursen %s" #: ckan/logic/auth/delete.py:54 msgid "Resource view not found, cannot check auth." -msgstr "" +msgstr "Fant ikke ressursvisning, kan ikke sjekke autentisering." #: ckan/logic/auth/delete.py:69 ckan/logic/auth/delete.py:83 msgid "Only the owner can delete a related item" @@ -1693,7 +1693,7 @@ msgstr "Bruker %s ikke autorisert til å lese ressurs %s" #: ckan/logic/auth/get.py:166 #, python-format msgid "User %s not authorized to read group %s" -msgstr "" +msgstr "Bruker %s har ikke tilatelse til å lese gruppe %s" #: ckan/logic/auth/get.py:237 msgid "You must be logged in to access your dashboard." @@ -1748,7 +1748,7 @@ msgstr "Bruker %s ikke autorisert til å redigere bruker %s" #: ckan/logic/auth/update.py:228 msgid "User {0} not authorized to update user {1}" -msgstr "" +msgstr "Bruker {0} har ikke tilaltese til å oppdatere bruker {1}" #: ckan/logic/auth/update.py:236 #, python-format @@ -1775,7 +1775,7 @@ msgstr "Gyldig API-nøkkel påkrevet for å redigere en gruppe" #: ckan/model/license.py:220 msgid "License not specified" -msgstr "" +msgstr "Lisens ikke oppgitt" #: ckan/model/license.py:230 msgid "Open Data Commons Public Domain Dedication and License (PDDL)" @@ -2032,7 +2032,7 @@ msgstr "Du laster opp en fil. Er du sikker på at du vil forlate siden og stoppe #: ckan/public/base/javascript/modules/resource-view-reorder.js:8 msgid "Reorder resource view" -msgstr "" +msgstr "Forandre rekkefølgen i ressursvisning" #: ckan/public/base/javascript/modules/slug-preview.js:35 #: ckan/templates/group/snippets/group_form.html:18 @@ -2111,7 +2111,7 @@ msgstr "Rediger innstillinger" #: ckan/templates/header.html:37 msgid "Settings" -msgstr "" +msgstr "Innstillinger" #: ckan/templates/header.html:43 ckan/templates/header.html:45 msgid "Log out" @@ -2155,7 +2155,7 @@ msgstr "Søk" #: ckan/templates/page.html:6 msgid "Skip to content" -msgstr "" +msgstr "Gå til innhold" #: ckan/templates/activity_streams/activity_stream_items.html:9 msgid "Load less" @@ -2229,7 +2229,7 @@ msgstr "Bekreft tilbakestilling" #: ckan/templates/admin/index.html:15 msgid "Administer CKAN" -msgstr "" +msgstr "Administrer CKAN" #: ckan/templates/admin/index.html:20 #, python-format @@ -2237,15 +2237,15 @@ msgid "" "

As a sysadmin user you have full control over this CKAN instance. " "Proceed with care!

For guidance on using sysadmin features, see the " "CKAN sysadmin guide

" -msgstr "" +msgstr "

Som sysadmin-bruker har du full kontroll over denne CKAN-installasjonen. Vær sikker på at du vet hva du gjør!

Du finner mer informasjon i CKANs sysadmin-dokumentasjon

" #: ckan/templates/admin/trash.html:20 msgid "Purge" -msgstr "" +msgstr "Tøm" #: ckan/templates/admin/trash.html:32 msgid "

Purge deleted datasets forever and irreversibly.

" -msgstr "" +msgstr "

Slettede datasett fjernes for alltid og ugjenkallelig.

" #: ckan/templates/ajax_snippets/api_info.html:19 msgid "CKAN Data API" @@ -2260,7 +2260,7 @@ msgid "" " Further information in the main CKAN Data API and DataStore documentation.

" -msgstr "" +msgstr "Les mer i dokumentasjonen for data-API i CKAN og DataStore.

" #: ckan/templates/ajax_snippets/api_info.html:33 msgid "Endpoints" @@ -2528,7 +2528,7 @@ msgstr "Søk i grupper..." #: ckan/templates/snippets/sort_by.html:15 #: ckanext/example_idatasetform/templates/package/search.html:13 msgid "Name Ascending" -msgstr "Navn nedenfra og opp" +msgstr "Navn i stigende rekkefølge" #: ckan/templates/group/index.html:20 ckan/templates/group/read.html:17 #: ckan/templates/organization/bulk_process.html:98 @@ -2539,7 +2539,7 @@ msgstr "Navn nedenfra og opp" #: ckan/templates/snippets/sort_by.html:16 #: ckanext/example_idatasetform/templates/package/search.html:14 msgid "Name Descending" -msgstr "Navn ovenfra og ned" +msgstr "Navn i synkende rekkefølge" #: ckan/templates/group/index.html:29 msgid "There are currently no groups for this site" @@ -2858,11 +2858,11 @@ msgstr "Dette er en framhevet seksjon" #: ckan/templates/home/snippets/search.html:2 msgid "E.g. environment" -msgstr "" +msgstr "F.eks. miljø" #: ckan/templates/home/snippets/search.html:6 msgid "Search data" -msgstr "" +msgstr "Søk i data" #: ckan/templates/home/snippets/search.html:16 msgid "Popular tags" @@ -2870,7 +2870,7 @@ msgstr "Populære stikkord" #: ckan/templates/home/snippets/stats.html:5 msgid "{0} statistics" -msgstr "{0} statistikker" +msgstr "Statistikk for {0}" #: ckan/templates/home/snippets/stats.html:11 msgid "dataset" @@ -2902,7 +2902,7 @@ msgid "" "You can use Markdown formatting here" -msgstr "" +msgstr "Du kan bruke Markdown-formatering her" #: ckan/templates/macros/form.html:265 msgid "This field is required" @@ -3053,7 +3053,7 @@ msgstr "Legg til datasett" #: ckan/templates/organization/snippets/feeds.html:3 msgid "Datasets in organization: {group}" -msgstr "" +msgstr "Datasett i organisasjon: {group}" #: ckan/templates/organization/snippets/help.html:4 #: ckan/templates/organization/snippets/helper.html:4 @@ -3068,7 +3068,7 @@ msgid "" "organizations, admins can assign roles and authorise its members, giving " "individual users the right to publish datasets from that particular " "organisation (e.g. Office of National Statistics).

" -msgstr "" +msgstr "

Organisasjoner fungerer som publiserende enheter for datasett. Dette betyr at datasett kan bli publisert av og tilhøre en enhet (f.eks. Helsedirektoratet eller Statistisk sentralbyrå) i stedet for en enkelt bruker.

Innen en organisasjon kan administratorer tildele roller og gi tillatelser til medlemmene sine, slik at enkeltbrukere kan å publisere datasett fra organisasjonen sin.

" #: ckan/templates/organization/snippets/helper.html:8 msgid "" @@ -3141,7 +3141,7 @@ msgstr "Rediger metadata" #: ckan/templates/package/edit_view.html:8 #: ckan/templates/package/edit_view.html:12 msgid "Edit view" -msgstr "" +msgstr "Rediger visning" #: ckan/templates/package/edit_view.html:20 #: ckan/templates/package/new_view.html:28 @@ -3193,7 +3193,7 @@ msgstr "Ny ressurs" #: ckan/templates/package/new_view.html:8 #: ckan/templates/package/new_view.html:12 msgid "Add view" -msgstr "" +msgstr "Opprett visning" #: ckan/templates/package/new_view.html:19 msgid "" @@ -3202,7 +3202,7 @@ msgid "" "href='http://docs.ckan.org/en/latest/maintaining/data-viewer.html#viewing-" "structured-data-the-data-explorer' target='_blank'>Data Explorer " "documentation. " -msgstr "" +msgstr "Data Explorer-visninger kan være trege og upålitelige hvis ikke DataStore-tillegget er installert. Les mer i dokumentasjonen for Data Explorer. " #: ckan/templates/package/new_view.html:29 #: ckan/templates/package/snippets/resource_form.html:82 @@ -3243,7 +3243,7 @@ msgstr "Feil:" #: ckan/templates/package/resource_data.html:36 msgid "Error traceback:" -msgstr "" +msgstr "Sporing av feil:" #: ckan/templates/package/resource_data.html:48 msgid "Status" @@ -3289,7 +3289,7 @@ msgstr "DataStore" #: ckan/templates/package/resource_edit_base.html:28 msgid "Views" -msgstr "" +msgstr "Visninger" #: ckan/templates/package/resource_read.html:39 msgid "API Endpoint" @@ -3321,30 +3321,30 @@ msgstr "Kilde: %(dataset)s" #: ckan/templates/package/resource_read.html:112 msgid "There are no views created for this resource yet." -msgstr "" +msgstr "Det er ikke laget noen visninger for denne ressursen ennå." #: ckan/templates/package/resource_read.html:116 msgid "Not seeing the views you were expecting?" -msgstr "" +msgstr "Ser du ikke de visningene du ventet deg?" #: ckan/templates/package/resource_read.html:121 msgid "Here are some reasons you may not be seeing expected views:" -msgstr "" +msgstr "Dette er noen mulige grunner til at visningene mangler:" #: ckan/templates/package/resource_read.html:123 msgid "No view has been created that is suitable for this resource" -msgstr "" +msgstr "Det har ikke blitt laget noen visning som passer for denne ressursen" #: ckan/templates/package/resource_read.html:124 msgid "The site administrators may not have enabled the relevant view plugins" -msgstr "" +msgstr "Nettstedsadministratorene har kanskje ikke aktivert de nødvendige tilleggene for visningen." #: ckan/templates/package/resource_read.html:125 msgid "" "If a view requires the DataStore, the DataStore plugin may not be enabled, " "or the data may not have been pushed to the DataStore, or the DataStore " "hasn't finished processing the data yet" -msgstr "" +msgstr "Hvis en visning krever DataStore kan det hende at DataStore-tillegget ikke er aktivert på nettstedet, eller dataene har ikke blitt importert inn i DataStore ennå, eller DataStore er ikke ferdig med å behandle dataene." #: ckan/templates/package/resource_read.html:147 msgid "Additional Information" @@ -3388,11 +3388,11 @@ msgstr "Lisens" #: ckan/templates/package/resource_views.html:10 msgid "New view" -msgstr "" +msgstr "Ny visning" #: ckan/templates/package/resource_views.html:28 msgid "This resource has no views" -msgstr "" +msgstr "Denne ressursen har ingen visninger" #: ckan/templates/package/resources.html:8 msgid "Add new resource" @@ -3434,15 +3434,15 @@ msgstr "Du får også tilgang til dette registeret med %(api_link)s (se %(api_do #: ckan/templates/package/view_edit_base.html:9 msgid "All views" -msgstr "" +msgstr "Alle visninger" #: ckan/templates/package/view_edit_base.html:12 msgid "View view" -msgstr "" +msgstr "Se visning" #: ckan/templates/package/view_edit_base.html:37 msgid "View preview" -msgstr "" +msgstr "Forhåndsvis visning" #: ckan/templates/package/snippets/additional_info.html:2 #: ckan/templates/snippets/additional_info.html:7 @@ -3473,7 +3473,7 @@ msgstr "Status" #: ckan/templates/package/snippets/additional_info.html:62 msgid "Last Updated" -msgstr "" +msgstr "Sist oppdatert" #: ckan/templates/package/snippets/data_api_button.html:10 msgid "Data API" @@ -3504,7 +3504,7 @@ msgstr "f.eks. økonomi, helse, myndigheter" msgid "" " License definitions and additional information can be found at opendefinition.org " -msgstr "Definisjoner av lisenser og tilleggsinformasjon kan bil funnet på opendefinition.org" +msgstr "Du finner definisjoner av lisenser og mer informasjon på opendefinition.org" #: ckan/templates/package/snippets/package_basic_fields.html:70 #: ckan/templates/snippets/organization.html:23 @@ -3534,7 +3534,7 @@ msgid "" "agree to release the metadata values that you enter into the form " "under the Open " "Database License." -msgstr "" +msgstr "Datalisensen du velger over gjelder kun innholdet av ressursfilene du legger til i dette datasettet. Ved å fylle ut dette skjemaet godtar du å publisere metadataene under lisensen Open Database License." #: ckan/templates/package/snippets/package_form.html:39 msgid "Are you sure you want to delete this dataset?" @@ -3594,7 +3594,7 @@ msgstr "f.eks. CSV, XML eller JSON" #: ckan/templates/package/snippets/resource_form.html:40 msgid "This will be guessed automatically. Leave blank if you wish" -msgstr "" +msgstr "Vil bli fylt ut automatisk. Du kan la dette feltet stå tomt." #: ckan/templates/package/snippets/resource_form.html:51 msgid "eg. 2012-06-05" @@ -3656,17 +3656,17 @@ msgstr "Inkluder (embed)" #: ckan/templates/package/snippets/resource_view.html:24 msgid "This resource view is not available at the moment." -msgstr "" +msgstr "Denne ressurvisningen er ikke tilgjengelig nå." #: ckan/templates/package/snippets/resource_view.html:63 msgid "Embed resource view" -msgstr "" +msgstr "Bygg inn ressursvisning" #: ckan/templates/package/snippets/resource_view.html:66 msgid "" "You can copy and paste the embed code into a CMS or blog software that " "supports raw HTML" -msgstr "" +msgstr "Du kan kopiere og lime inn koden for innebygging i et nettpubliseringsverktøy (f.eks. en blogg) som tillater HTML." #: ckan/templates/package/snippets/resource_view.html:69 msgid "Width" @@ -3682,7 +3682,7 @@ msgstr "Kode" #: ckan/templates/package/snippets/resource_views_list.html:8 msgid "Resource Preview" -msgstr "" +msgstr "Forhåndsvisning av ressurs" #: ckan/templates/package/snippets/resources_list.html:13 msgid "Data and Resources" @@ -3690,7 +3690,7 @@ msgstr "Data og ressurser" #: ckan/templates/package/snippets/resources_list.html:29 msgid "This dataset has no data" -msgstr "" +msgstr "Dette datasettet har ingen data" #: ckan/templates/package/snippets/revisions_table.html:24 #, python-format @@ -3700,7 +3700,7 @@ msgstr "Les datasett: %s" #: ckan/templates/package/snippets/stages.html:23 #: ckan/templates/package/snippets/stages.html:25 msgid "Create dataset" -msgstr "Opprett et datasett »" +msgstr "Opprett datasett" #: ckan/templates/package/snippets/stages.html:30 #: ckan/templates/package/snippets/stages.html:34 @@ -3710,31 +3710,31 @@ msgstr "Legg til data" #: ckan/templates/package/snippets/view_form.html:8 msgid "eg. My View" -msgstr "" +msgstr "f.eks. Min visning" #: ckan/templates/package/snippets/view_form.html:9 msgid "eg. Information about my view" -msgstr "" +msgstr "f.eks. Informasjon om visningen min" #: ckan/templates/package/snippets/view_form_filters.html:16 msgid "Add Filter" -msgstr "" +msgstr "Opprett filter" #: ckan/templates/package/snippets/view_form_filters.html:28 msgid "Remove Filter" -msgstr "" +msgstr "Fjern filter" #: ckan/templates/package/snippets/view_form_filters.html:46 msgid "Filters" -msgstr "" +msgstr "Filtere" #: ckan/templates/package/snippets/view_help.html:2 msgid "What's a view?" -msgstr "" +msgstr "Hva er en visning?" #: ckan/templates/package/snippets/view_help.html:4 msgid "A view is a representation of the data held against a resource" -msgstr "" +msgstr "En visning er en representasjon av data i en ressurs." #: ckan/templates/revision/diff.html:6 msgid "Differences" @@ -4080,11 +4080,11 @@ msgstr "Er du sikker på at du vil slette denne brukeren?" #: ckan/templates/user/edit_user_form.html:43 msgid "Are you sure you want to regenerate the API key?" -msgstr "" +msgstr "Er du sikker på at du vil lage ny API-nøkkel? (Den vil erstatte den gamle.)" #: ckan/templates/user/edit_user_form.html:44 msgid "Regenerate API Key" -msgstr "" +msgstr "Lag ny API-nøkkel" #: ckan/templates/user/edit_user_form.html:48 msgid "Update Profile" @@ -4176,7 +4176,7 @@ msgstr "Opprett datasett, grupper og andre interessante ting" #: ckan/templates/user/new_user_form.html:5 msgid "username" -msgstr "" +msgstr "brukernavn" #: ckan/templates/user/new_user_form.html:6 msgid "Full Name" @@ -4238,11 +4238,11 @@ msgstr "API-nøkkel" #: ckan/templates/user/request_reset.html:6 msgid "Password reset" -msgstr "" +msgstr "Tilbakestilling av passord" #: ckan/templates/user/request_reset.html:19 msgid "Request reset" -msgstr "" +msgstr "Be om tilbakestilling" #: ckan/templates/user/request_reset.html:34 msgid "" @@ -4295,7 +4295,7 @@ msgstr "Fant ikke DataStore-ressurs" msgid "" "The data was invalid (for example: a numeric value is out of range or was " "inserted into a text field)." -msgstr "" +msgstr "Ugyldige data (f.eks. et tall utenfor tillatt verdi eller tekst i et felt for tall)." #: ckanext/datastore/logic/action.py:215 ckanext/datastore/logic/action.py:255 #: ckanext/datastore/logic/action.py:332 ckanext/datastore/logic/action.py:422 @@ -4309,19 +4309,19 @@ msgstr "Brukeren {0} har ikke tillatelse til å oppdatere ressursen {1}" #: ckanext/example_iconfigurer/templates/admin/config.html:11 msgid "Datasets per page" -msgstr "" +msgstr "Datasett per side" #: ckanext/example_iconfigurer/templates/admin/config.html:13 msgid "Test conf" -msgstr "" +msgstr "Testkonfigurasjon" #: ckanext/example_idatasetform/templates/package/search.html:16 msgid "Custom Field Ascending" -msgstr "" +msgstr "Egendefinert felt i stigende rekkefølge" #: ckanext/example_idatasetform/templates/package/search.html:17 msgid "Custom Field Descending" -msgstr "" +msgstr "Egendefinert felt i synkende rekkefølge" #: ckanext/example_idatasetform/templates/package/snippets/additional_info.html:6 #: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 @@ -4339,11 +4339,11 @@ msgstr "Landkode" #: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 msgid "custom resource text" -msgstr "" +msgstr "egendefinert ressurstekst" #: ckanext/example_itranslation/templates/home/index.html:4 msgid "This is an untranslated string" -msgstr "" +msgstr "Denne teksten er ikke oversatt" #: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 #: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 @@ -4352,91 +4352,91 @@ msgstr "Denne gruppen har ingen beskrivelse" #: ckanext/example_theme/v12_extra_public_dir/templates/home/snippets/promoted.html:4 msgid "CKAN's data previewing tool has many powerful features" -msgstr "" +msgstr "CKAN har mange nyttige verktøy for forhåndsvisning av data" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "Image url" -msgstr "" +msgstr "URL til bilde" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "eg. http://example.com/image.jpg (if blank uses resource url)" -msgstr "" +msgstr "f.eks. http://example.com/image.jpg (hvis denne er tom brukes URL til ressursen)" #: ckanext/reclineview/plugin.py:84 msgid "Data Explorer" -msgstr "" +msgstr "Data Explorer" #: ckanext/reclineview/plugin.py:111 msgid "Table" -msgstr "" +msgstr "Tabell" #: ckanext/reclineview/plugin.py:154 msgid "Graph" -msgstr "" +msgstr "Graf" #: ckanext/reclineview/plugin.py:214 msgid "Map" -msgstr "" +msgstr "Kart" #: ckanext/reclineview/theme/public/recline_view.js:34 msgid "error loading view" -msgstr "" +msgstr "feil i innlasting av visning" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "Row offset" -msgstr "" +msgstr "Startpunkt i rader" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "eg: 0" -msgstr "" +msgstr "f.eks.: 0" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "Number of rows" -msgstr "" +msgstr "Antall rader" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "eg: 100" -msgstr "" +msgstr "f.eks.: 100" #: ckanext/reclineview/theme/templates/recline_graph_form.html:6 msgid "Graph type" -msgstr "" +msgstr "Type graf" #: ckanext/reclineview/theme/templates/recline_graph_form.html:7 msgid "Group (Axis 1)" -msgstr "" +msgstr "Gruppe (akse 1)" #: ckanext/reclineview/theme/templates/recline_graph_form.html:8 msgid "Series (Axis 2)" -msgstr "" +msgstr "Serie (akse 2)" #: ckanext/reclineview/theme/templates/recline_map_form.html:6 msgid "Field type" -msgstr "" +msgstr "Felttype" #: ckanext/reclineview/theme/templates/recline_map_form.html:7 msgid "Latitude field" -msgstr "" +msgstr "Breddegrad-felt" #: ckanext/reclineview/theme/templates/recline_map_form.html:8 msgid "Longitude field" -msgstr "" +msgstr "Lengdegrad-felt" #: ckanext/reclineview/theme/templates/recline_map_form.html:9 msgid "GeoJSON field" -msgstr "" +msgstr "GeoJSON-felt" #: ckanext/reclineview/theme/templates/recline_map_form.html:10 msgid "Auto zoom to features" -msgstr "" +msgstr "Automatisk zoom til funksjoner" #: ckanext/reclineview/theme/templates/recline_map_form.html:11 msgid "Cluster markers" -msgstr "" +msgstr "Markører for klynger" #: ckanext/stats/templates/ckanext/stats/index.html:10 msgid "Total number of Datasets" @@ -4524,7 +4524,7 @@ msgstr "Antall datasett" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 msgid "Users Creating Most Datasets" -msgstr "" +msgstr "Brukere som har opprettet flest datasett" #: ckanext/stats/templates/ckanext/stats/index.html:175 msgid "Statistics Menu" @@ -4536,21 +4536,21 @@ msgstr "Totalt antall datasett" #: ckanext/textview/plugin.py:65 ckanext/textview/plugin.py:67 msgid "Text" -msgstr "" +msgstr "Tekst" #: ckanext/textview/theme/public/text_view.js:5 #, python-format msgid "An error occurred: %(text)s %(error)s" -msgstr "" +msgstr "En feil har oppstått: %(text)s %(error)s" #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" -msgstr "" +msgstr "Nettside" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "Web Page url" -msgstr "" +msgstr "Nettside-adresse" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "eg. http://example.com (if blank uses resource url)" -msgstr "" +msgstr "f.eks. http://example.com (hvis denne er tom brukes URL til ressursen)" diff --git a/ckan/i18n/pl/LC_MESSAGES/ckan.mo b/ckan/i18n/pl/LC_MESSAGES/ckan.mo index 4864987e2ce..3bdbf0fac0e 100644 Binary files a/ckan/i18n/pl/LC_MESSAGES/ckan.mo and b/ckan/i18n/pl/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/pt_BR/LC_MESSAGES/ckan.mo b/ckan/i18n/pt_BR/LC_MESSAGES/ckan.mo index cb80c066a6e..1260bceca64 100644 Binary files a/ckan/i18n/pt_BR/LC_MESSAGES/ckan.mo and b/ckan/i18n/pt_BR/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/pt_PT/LC_MESSAGES/ckan.mo b/ckan/i18n/pt_PT/LC_MESSAGES/ckan.mo index 89c58a09d6b..08c9f9a6d8b 100644 Binary files a/ckan/i18n/pt_PT/LC_MESSAGES/ckan.mo and b/ckan/i18n/pt_PT/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/pt_PT/LC_MESSAGES/ckan.po b/ckan/i18n/pt_PT/LC_MESSAGES/ckan.po index 6e0f62fda93..00058944308 100644 --- a/ckan/i18n/pt_PT/LC_MESSAGES/ckan.po +++ b/ckan/i18n/pt_PT/LC_MESSAGES/ckan.po @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-11-26 14:23+0000\n" -"Last-Translator: dread \n" +"PO-Revision-Date: 2015-12-16 14:55+0000\n" +"Last-Translator: Jorge Rocha \n" "Language-Team: Portuguese (Portugal) (http://www.transifex.com/okfn/ckan/language/pt_PT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -877,8 +877,8 @@ msgstr "Visualização" #: ckan/lib/email_notifications.py:103 msgid "{n} new activity from {site_title}" msgid_plural "{n} new activities from {site_title}" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{n} nova atividade no {site_title}" +msgstr[1] "{n} novas atividades no {site_title}" #: ckan/lib/formatters.py:17 msgid "January" @@ -964,7 +964,7 @@ msgstr[1] "Há mais de {years} anos" #: ckan/lib/formatters.py:146 msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" -msgstr "" +msgstr "{day} de {month}, {year}, {hour:02}:{min:02} ({timezone})" #: ckan/lib/formatters.py:151 msgid "{month} {day}, {year}" @@ -1381,7 +1381,7 @@ msgstr "Não é um formato texto" #: ckan/logic/validators.py:805 msgid "This parent would create a loop in the hierarchy" -msgstr "" +msgstr "Este superior iria criar um ciclo na hierarquia" #: ckan/logic/validators.py:815 msgid "\"filter_fields\" and \"filter_values\" should have the same length" @@ -1407,12 +1407,12 @@ msgstr "REST API: Cria o objeto %s" #: ckan/logic/action/create.py:602 #, python-format msgid "REST API: Create package relationship: %s %s %s" -msgstr "" +msgstr "REST API: Criar a relação do pacote: %s %s %s" #: ckan/logic/action/create.py:643 #, python-format msgid "REST API: Create member object %s" -msgstr "" +msgstr "REST API: Criar objeto membro %s" #: ckan/logic/action/create.py:862 msgid "Trying to create an organization as a group" @@ -1484,7 +1484,7 @@ msgstr "Os dados não têm ID" #: ckan/logic/action/update.py:997 #, python-format msgid "Could not find vocabulary \"%s\"" -msgstr "" +msgstr "Não foi possível encontrar o vocabulário \"%s\"" #: ckan/logic/action/delete.py:590 #, python-format @@ -1532,11 +1532,11 @@ msgstr "REST API: Atualizar objeto. %s" #: ckan/logic/action/update.py:443 #, python-format msgid "REST API: Update package relationship: %s %s %s" -msgstr "" +msgstr "REST API: Atualizar a relação do pacote: %s %s %s" #: ckan/logic/action/update.py:801 msgid "TaskStatus was not found." -msgstr "" +msgstr "TaskStatus não encontrado." #: ckan/logic/action/update.py:1098 msgid "Organization was not found." @@ -1862,17 +1862,17 @@ msgstr "está ligado desde %s" #: ckan/model/package_relationship.py:55 #, python-format msgid "is a child of %s" -msgstr "" +msgstr "é um inferior de %s" #: ckan/model/package_relationship.py:55 #, python-format msgid "is a parent of %s" -msgstr "" +msgstr "é um superior de %s" #: ckan/model/package_relationship.py:59 #, python-format msgid "has sibling %s" -msgstr "" +msgstr "tem ao mesmo nível %s" #: ckan/public/base/javascript/modules/activity-stream.js:20 #: ckan/public/base/javascript/modules/popover-context.js:45 @@ -2286,7 +2286,7 @@ msgstr "Query (via SQL)" #: ckan/templates/ajax_snippets/api_info.html:66 msgid "Querying" -msgstr "" +msgstr "Pesquisar" #: ckan/templates/ajax_snippets/api_info.html:70 msgid "Query example (first 5 results)" @@ -2306,7 +2306,7 @@ msgstr "Exemplo: Javascript" #: ckan/templates/ajax_snippets/api_info.html:97 msgid "A simple ajax (JSONP) request to the data API using jQuery." -msgstr "" +msgstr "Um pedido AJAX (JSONP) à API usando jQuery." #: ckan/templates/ajax_snippets/api_info.html:118 msgid "Example: Python" @@ -2344,7 +2344,7 @@ msgstr "Mais detalhes ..." #: ckan/templates/dataviewer/snippets/no_preview.html:12 #, python-format msgid "No handler defined for data type: %(type)s." -msgstr "" +msgstr "Nenhum tratamento definido para o tipo de dados: %(type)s." #: ckan/templates/development/snippets/form.html:5 msgid "Standard" @@ -2356,35 +2356,35 @@ msgstr "Entrada padrão " #: ckan/templates/development/snippets/form.html:6 msgid "Medium" -msgstr "" +msgstr "Médio" #: ckan/templates/development/snippets/form.html:6 msgid "Medium Width Input" -msgstr "" +msgstr "Largura média da entrada" #: ckan/templates/development/snippets/form.html:7 msgid "Full" -msgstr "" +msgstr "Total" #: ckan/templates/development/snippets/form.html:7 msgid "Full Width Input" -msgstr "" +msgstr "Largura total da entrada" #: ckan/templates/development/snippets/form.html:8 msgid "Large" -msgstr "" +msgstr "Grande" #: ckan/templates/development/snippets/form.html:8 msgid "Large Input" -msgstr "" +msgstr "Entrada grande" #: ckan/templates/development/snippets/form.html:9 msgid "Prepend" -msgstr "" +msgstr "Prefixo" #: ckan/templates/development/snippets/form.html:9 msgid "Prepend Input" -msgstr "" +msgstr "Prefixo de entrada" #: ckan/templates/development/snippets/form.html:13 msgid "Custom Field (empty)" @@ -2402,7 +2402,7 @@ msgstr "Personalizar Campo" #: ckan/templates/development/snippets/form.html:22 msgid "Markdown" -msgstr "" +msgstr "Markdown" #: ckan/templates/development/snippets/form.html:23 msgid "Textarea" @@ -2793,7 +2793,7 @@ msgstr "Revisão" #: ckan/templates/revision/read.html:53 #: ckan/templates/revision/snippets/revisions_list.html:5 msgid "Timestamp" -msgstr "" +msgstr "Registo temporal" #: ckan/templates/group/snippets/revisions_table.html:9 #: ckan/templates/package/snippets/additional_info.html:25 @@ -3214,7 +3214,7 @@ msgstr "Esta é uma revisão antiga deste conjunto de dados, está tal como edit #: ckan/templates/package/related_list.html:7 msgid "Related Media for {dataset}" -msgstr "" +msgstr "Médias relacionados para {dataset}" #: ckan/templates/package/related_list.html:12 msgid "No related items" @@ -3239,7 +3239,7 @@ msgstr "Erro:" #: ckan/templates/package/resource_data.html:36 msgid "Error traceback:" -msgstr "" +msgstr "Rastreio do erro:" #: ckan/templates/package/resource_data.html:48 msgid "Status" @@ -3256,7 +3256,7 @@ msgstr "Nunca" #: ckan/templates/package/resource_data.html:62 msgid "Upload Log" -msgstr "" +msgstr "Registo de envios" #: ckan/templates/package/resource_data.html:74 msgid "Details" @@ -3333,14 +3333,14 @@ msgstr "Não foi criada nenhuma vista que seja adequada a este recurso" #: ckan/templates/package/resource_read.html:124 msgid "The site administrators may not have enabled the relevant view plugins" -msgstr "" +msgstr "Os administradores do site podem não ter ativado os plugins de visualização relevantes" #: ckan/templates/package/resource_read.html:125 msgid "" "If a view requires the DataStore, the DataStore plugin may not be enabled, " "or the data may not have been pushed to the DataStore, or the DataStore " "hasn't finished processing the data yet" -msgstr "" +msgstr "Se a visualização requer o DataStore, o plugin DataStore pode não estar ativo, ou os dados podem não ter sidos carregados no DataStore, ou o DataStore pode ainda não ter acabado o processamento." #: ckan/templates/package/resource_read.html:147 msgid "Additional Information" @@ -3412,7 +3412,7 @@ msgstr "Documentos API" #: ckan/templates/package/search.html:55 msgid "full {format} dump" -msgstr "" +msgstr "descarregamento integral {format}" #: ckan/templates/package/search.html:56 #, python-format @@ -3578,7 +3578,7 @@ msgstr "Ficheiro" #: ckan/templates/package/snippets/resource_form.html:28 msgid "eg. January 2011 Gold Prices" -msgstr "" +msgstr "p.ex. Preço do Ouro em janeiro de 2011" #: ckan/templates/package/snippets/resource_form.html:32 msgid "Some useful notes about the data" @@ -3662,7 +3662,7 @@ msgstr "Incorporar vista de recurso" msgid "" "You can copy and paste the embed code into a CMS or blog software that " "supports raw HTML" -msgstr "" +msgstr "Pode copiar e colar o código para embutir num CMS ou blogue que suporte a escrita de HTML" #: ckan/templates/package/snippets/resource_view.html:69 msgid "Width" @@ -3739,7 +3739,7 @@ msgstr "Diferenças" #: ckan/templates/revision/diff.html:13 ckan/templates/revision/diff.html:18 #: ckan/templates/revision/diff.html:23 msgid "Revision Differences" -msgstr "" +msgstr "Diferenças da revisão" #: ckan/templates/revision/diff.html:44 msgid "Difference" @@ -3760,7 +3760,7 @@ msgstr "Revisões" #: ckan/templates/revision/read.html:30 msgid "Undelete" -msgstr "" +msgstr "Reverter" #: ckan/templates/revision/read.html:64 msgid "Changes" @@ -3780,11 +3780,11 @@ msgstr "Novo item de atividade" #: ckan/templates/snippets/datapreview_embed_dialog.html:4 msgid "Embed Data Viewer" -msgstr "" +msgstr "Visualizador de Dados Embutido" #: ckan/templates/snippets/datapreview_embed_dialog.html:8 msgid "Embed this view by copying this into your webpage:" -msgstr "" +msgstr "Embutir esta visualização copiando isto para a sua página:" #: ckan/templates/snippets/datapreview_embed_dialog.html:10 msgid "Choose width and height in pixels:" @@ -3800,11 +3800,11 @@ msgstr "Altura:" #: ckan/templates/snippets/datapusher_status.html:8 msgid "Datapusher status: {status}." -msgstr "" +msgstr "Estado do Datapusher: {status}." #: ckan/templates/snippets/disqus_trackback.html:2 msgid "Trackback URL" -msgstr "" +msgstr "URL de rastreio" #: ckan/templates/snippets/facet_list.html:80 msgid "Show More {facet_type}" @@ -4339,7 +4339,7 @@ msgstr "personalizar o texto do recurso" #: ckanext/example_itranslation/templates/home/index.html:4 msgid "This is an untranslated string" -msgstr "" +msgstr "Esta é uma frase por traduzir" #: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 #: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 @@ -4360,7 +4360,7 @@ msgstr "p.ex. http://example.com/image.jpg (se estiver em branco use o URL do re #: ckanext/reclineview/plugin.py:84 msgid "Data Explorer" -msgstr "" +msgstr "Explorador de dados" #: ckanext/reclineview/plugin.py:111 msgid "Table" @@ -4376,12 +4376,12 @@ msgstr "Mapa" #: ckanext/reclineview/theme/public/recline_view.js:34 msgid "error loading view" -msgstr "" +msgstr "erro a carregar a visualização" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "Row offset" -msgstr "" +msgstr "Deslocamento da linha" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 @@ -4428,11 +4428,11 @@ msgstr "Campo GeoJSON" #: ckanext/reclineview/theme/templates/recline_map_form.html:10 msgid "Auto zoom to features" -msgstr "" +msgstr "Zoom automático às entidades" #: ckanext/reclineview/theme/templates/recline_map_form.html:11 msgid "Cluster markers" -msgstr "" +msgstr "Grupo de marcadores " #: ckanext/stats/templates/ckanext/stats/index.html:10 msgid "Total number of Datasets" @@ -4520,7 +4520,7 @@ msgstr "Número de conjuntos de dados" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 msgid "Users Creating Most Datasets" -msgstr "" +msgstr "Utilizadores que criaram mais dados" #: ckanext/stats/templates/ckanext/stats/index.html:175 msgid "Statistics Menu" @@ -4537,7 +4537,7 @@ msgstr "Texto" #: ckanext/textview/theme/public/text_view.js:5 #, python-format msgid "An error occurred: %(text)s %(error)s" -msgstr "" +msgstr "Ocorreu um erro: %(text)s %(error)s" #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" diff --git a/ckan/i18n/ro/LC_MESSAGES/ckan.mo b/ckan/i18n/ro/LC_MESSAGES/ckan.mo index b10526ead51..2700f8649dc 100644 Binary files a/ckan/i18n/ro/LC_MESSAGES/ckan.mo and b/ckan/i18n/ro/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/ru/LC_MESSAGES/ckan.mo b/ckan/i18n/ru/LC_MESSAGES/ckan.mo index 1efa6b7d38e..9d07940fbf1 100644 Binary files a/ckan/i18n/ru/LC_MESSAGES/ckan.mo and b/ckan/i18n/ru/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sk/LC_MESSAGES/ckan.mo b/ckan/i18n/sk/LC_MESSAGES/ckan.mo index cf5f9433823..e75430bd71c 100644 Binary files a/ckan/i18n/sk/LC_MESSAGES/ckan.mo and b/ckan/i18n/sk/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sl/LC_MESSAGES/ckan.mo b/ckan/i18n/sl/LC_MESSAGES/ckan.mo index 01897d927f5..1dfac7b2773 100644 Binary files a/ckan/i18n/sl/LC_MESSAGES/ckan.mo and b/ckan/i18n/sl/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sq/LC_MESSAGES/ckan.mo b/ckan/i18n/sq/LC_MESSAGES/ckan.mo index 294c534a969..90ad45986fd 100644 Binary files a/ckan/i18n/sq/LC_MESSAGES/ckan.mo and b/ckan/i18n/sq/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sr/LC_MESSAGES/ckan.mo b/ckan/i18n/sr/LC_MESSAGES/ckan.mo index cca1214c4e5..3f15868d0e3 100644 Binary files a/ckan/i18n/sr/LC_MESSAGES/ckan.mo and b/ckan/i18n/sr/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sr_Latn/LC_MESSAGES/ckan.mo b/ckan/i18n/sr_Latn/LC_MESSAGES/ckan.mo index 4db291f2948..ce263b44d85 100644 Binary files a/ckan/i18n/sr_Latn/LC_MESSAGES/ckan.mo and b/ckan/i18n/sr_Latn/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/sv/LC_MESSAGES/ckan.mo b/ckan/i18n/sv/LC_MESSAGES/ckan.mo index 0e0be2e9ed0..307699489d2 100644 Binary files a/ckan/i18n/sv/LC_MESSAGES/ckan.mo and b/ckan/i18n/sv/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/th/LC_MESSAGES/ckan.mo b/ckan/i18n/th/LC_MESSAGES/ckan.mo index f7c91f7a6cc..03944caa4c3 100644 Binary files a/ckan/i18n/th/LC_MESSAGES/ckan.mo and b/ckan/i18n/th/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/tl/LC_MESSAGES/ckan.mo b/ckan/i18n/tl/LC_MESSAGES/ckan.mo new file mode 100644 index 00000000000..6695677e8d5 Binary files /dev/null and b/ckan/i18n/tl/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/tl/LC_MESSAGES/ckan.po b/ckan/i18n/tl/LC_MESSAGES/ckan.po new file mode 100644 index 00000000000..6b1e5f9767e --- /dev/null +++ b/ckan/i18n/tl/LC_MESSAGES/ckan.po @@ -0,0 +1,4550 @@ +# Translations template for ckan. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the ckan project. +# +# Translators: +# Joel Natividad , 2015 +msgid "" +msgstr "" +"Project-Id-Version: CKAN\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-11-26 13:42+0000\n" +"PO-Revision-Date: 2015-12-07 16:53+0000\n" +"Last-Translator: Joel Natividad \n" +"Language-Team: Tagalog (http://www.transifex.com/okfn/ckan/language/tl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.1.1\n" +"Language: tl\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: ckan/authz.py:177 +#, python-format +msgid "Authorization function not found: %s" +msgstr "Hindi natagpuan ang function para sa awtorisasyon: %s" + +#: ckan/authz.py:189 ckan/templates/header.html:14 +msgid "Admin" +msgstr "" + +#: ckan/authz.py:193 +msgid "Editor" +msgstr "" + +#: ckan/authz.py:197 +msgid "Member" +msgstr "" + +#: ckan/controllers/admin.py:31 +msgid "Need to be system administrator to administer" +msgstr "Kailangan maging isang system administrator para mag-administer" + +#: ckan/controllers/admin.py:47 +msgid "Site Title" +msgstr "Pamagat ng site" + +#: ckan/controllers/admin.py:48 +msgid "Style" +msgstr "Estilo" + +#: ckan/controllers/admin.py:49 +msgid "Site Tag Line" +msgstr "" + +#: ckan/controllers/admin.py:50 +msgid "Site Tag Logo" +msgstr "" + +#: ckan/controllers/admin.py:51 ckan/templates/header.html:106 +#: ckan/templates/group/about.html:3 ckan/templates/group/read_base.html:19 +#: ckan/templates/home/about.html:3 ckan/templates/home/about.html:6 +#: ckan/templates/home/about.html:16 ckan/templates/organization/about.html:3 +#: ckan/templates/organization/read_base.html:19 +#: ckan/templates/user/edit_user_form.html:14 +msgid "About" +msgstr "" + +#: ckan/controllers/admin.py:51 +msgid "About page text" +msgstr "" + +#: ckan/controllers/admin.py:52 +msgid "Intro Text" +msgstr "" + +#: ckan/controllers/admin.py:52 +msgid "Text on home page" +msgstr "" + +#: ckan/controllers/admin.py:53 +msgid "Custom CSS" +msgstr "" + +#: ckan/controllers/admin.py:53 +msgid "Customisable css inserted into the page header" +msgstr "" + +#: ckan/controllers/admin.py:54 +msgid "Homepage" +msgstr "" + +#: ckan/controllers/admin.py:157 +#, python-format +msgid "" +"Cannot purge package %s as associated revision %s includes non-deleted " +"packages %s" +msgstr "" + +#: ckan/controllers/admin.py:179 +#, python-format +msgid "Problem purging revision %s: %s" +msgstr "" + +#: ckan/controllers/admin.py:181 +msgid "Purge complete" +msgstr "" + +#: ckan/controllers/admin.py:183 +msgid "Action not implemented." +msgstr "" + +#: ckan/controllers/api.py:60 ckan/controllers/group.py:163 +#: ckan/controllers/home.py:26 ckan/controllers/package.py:142 +#: ckan/controllers/revision.py:31 ckan/controllers/tag.py:23 +#: ckan/controllers/user.py:46 ckan/controllers/user.py:73 +#: ckan/controllers/user.py:102 ckan/controllers/user.py:563 +#: ckanext/datapusher/plugin.py:68 +msgid "Not authorized to see this page" +msgstr "" + +#: ckan/controllers/api.py:120 ckan/controllers/api.py:214 +msgid "Access denied" +msgstr "" + +#: ckan/controllers/api.py:126 ckan/controllers/api.py:223 +#: ckan/logic/converters.py:119 ckan/logic/converters.py:144 +#: ckan/logic/converters.py:169 ckan/logic/validators.py:146 +#: ckan/logic/validators.py:167 ckan/logic/validators.py:188 +#: ckan/logic/validators.py:197 ckan/logic/validators.py:211 +#: ckan/logic/validators.py:228 ckan/logic/validators.py:241 +#: ckan/logic/validators.py:255 ckan/logic/validators.py:279 +#: ckan/logic/validators.py:288 ckan/logic/validators.py:729 +#: ckan/logic/action/create.py:966 +msgid "Not found" +msgstr "" + +#: ckan/controllers/api.py:132 +msgid "Bad request" +msgstr "" + +#: ckan/controllers/api.py:166 +#, python-format +msgid "Action name not known: %s" +msgstr "" + +#: ckan/controllers/api.py:188 ckan/controllers/api.py:358 +#: ckan/controllers/api.py:421 +#, python-format +msgid "JSON Error: %s" +msgstr "" + +#: ckan/controllers/api.py:194 +#, python-format +msgid "Bad request data: %s" +msgstr "" + +#: ckan/controllers/api.py:294 +#, python-format +msgid "Cannot list entity of this type: %s" +msgstr "" + +#: ckan/controllers/api.py:324 +#, python-format +msgid "Cannot read entity of this type: %s" +msgstr "" + +#: ckan/controllers/api.py:363 +#, python-format +msgid "Cannot create new entity of this type: %s %s" +msgstr "" + +#: ckan/controllers/api.py:396 +msgid "Unable to add package to search index" +msgstr "" + +#: ckan/controllers/api.py:426 +#, python-format +msgid "Cannot update entity of this type: %s" +msgstr "" + +#: ckan/controllers/api.py:450 +msgid "Unable to update search index" +msgstr "" + +#: ckan/controllers/api.py:474 +#, python-format +msgid "Cannot delete entity of this type: %s %s" +msgstr "" + +#: ckan/controllers/api.py:497 +msgid "No revision specified" +msgstr "" + +#: ckan/controllers/api.py:501 +#, python-format +msgid "There is no revision with id: %s" +msgstr "" + +#: ckan/controllers/api.py:511 +msgid "Missing search term ('since_id=UUID' or 'since_time=TIMESTAMP')" +msgstr "" + +#: ckan/controllers/api.py:523 +#, python-format +msgid "Could not read parameters: %r" +msgstr "" + +#: ckan/controllers/api.py:584 +#, python-format +msgid "Bad search option: %s" +msgstr "" + +#: ckan/controllers/api.py:587 +#, python-format +msgid "Unknown register: %s" +msgstr "" + +#: ckan/controllers/api.py:596 +#, python-format +msgid "Malformed qjson value: %r" +msgstr "" + +#: ckan/controllers/api.py:606 +msgid "Request params must be in form of a json encoded dictionary." +msgstr "" + +#: ckan/controllers/feed.py:223 ckan/controllers/group.py:136 +#: ckan/controllers/group.py:222 ckan/controllers/group.py:408 +#: ckan/controllers/group.py:516 ckan/controllers/group.py:563 +#: ckan/controllers/group.py:595 ckan/controllers/group.py:606 +#: ckan/controllers/group.py:660 ckan/controllers/group.py:679 +#: ckan/controllers/group.py:731 ckan/controllers/group.py:763 +#: ckan/controllers/group.py:796 ckan/controllers/group.py:855 +#: ckan/controllers/group.py:951 ckan/controllers/package.py:1270 +#: ckan/controllers/package.py:1285 +msgid "Group not found" +msgstr "Di matagpuan ang grupo" + +#: ckan/controllers/feed.py:234 +msgid "Organization not found" +msgstr "Di matagpuan ang organisasyon" + +#: ckan/controllers/group.py:138 ckan/controllers/group.py:609 +msgid "Incorrect group type" +msgstr "" + +#: ckan/controllers/group.py:224 ckan/controllers/group.py:410 +#: ckan/controllers/group.py:518 ckan/controllers/group.py:561 +#: ckan/controllers/group.py:593 ckan/controllers/group.py:953 +#, python-format +msgid "Unauthorized to read group %s" +msgstr "" + +#: ckan/controllers/group.py:310 ckan/controllers/home.py:67 +#: ckan/controllers/package.py:239 ckan/lib/helpers.py:755 +#: ckan/templates/header.html:104 ckan/templates/organization/edit_base.html:5 +#: ckan/templates/organization/edit_base.html:8 +#: ckan/templates/organization/index.html:3 +#: ckan/templates/organization/index.html:6 +#: ckan/templates/organization/index.html:18 +#: ckan/templates/organization/read_base.html:3 +#: ckan/templates/organization/read_base.html:6 +#: ckan/templates/package/base.html:14 +msgid "Organizations" +msgstr "Organisasyon" + +#: ckan/controllers/group.py:311 ckan/controllers/home.py:68 +#: ckan/controllers/package.py:240 ckan/lib/helpers.py:756 +#: ckan/templates/header.html:105 ckan/templates/group/base_form_page.html:6 +#: ckan/templates/group/edit.html:4 ckan/templates/group/edit_base.html:3 +#: ckan/templates/group/edit_base.html:8 ckan/templates/group/index.html:3 +#: ckan/templates/group/index.html:6 ckan/templates/group/index.html:18 +#: ckan/templates/group/members.html:3 ckan/templates/group/read_base.html:3 +#: ckan/templates/group/read_base.html:6 +#: ckan/templates/package/group_list.html:5 +#: ckan/templates/package/read_base.html:20 +#: ckan/templates/revision/diff.html:16 ckan/templates/revision/read.html:84 +msgid "Groups" +msgstr "Grupo" + +#: ckan/controllers/group.py:312 ckan/controllers/home.py:69 +#: ckan/controllers/package.py:241 ckan/lib/helpers.py:757 +#: ckan/logic/__init__.py:100 +#: ckan/templates/package/snippets/package_basic_fields.html:24 +#: ckan/templates/snippets/context/dataset.html:17 +#: ckan/templates/tag/index.html:3 ckan/templates/tag/index.html:6 +#: ckan/templates/tag/index.html:12 +msgid "Tags" +msgstr "" + +#: ckan/controllers/group.py:313 ckan/controllers/home.py:70 +#: ckan/controllers/package.py:242 ckan/lib/helpers.py:758 +msgid "Formats" +msgstr "" + +#: ckan/controllers/group.py:314 ckan/controllers/home.py:71 +#: ckan/controllers/package.py:243 ckan/lib/helpers.py:759 +msgid "Licenses" +msgstr "" + +#: ckan/controllers/group.py:453 +msgid "Not authorized to perform bulk update" +msgstr "Walang pahintulot mag-perform ng bulk update" + +#: ckan/controllers/group.py:473 +msgid "Unauthorized to create a group" +msgstr "Walang pahintulot lumikha ng grupo" + +#: ckan/controllers/group.py:527 ckan/controllers/package.py:319 +#: ckan/controllers/package.py:779 ckan/controllers/package.py:1418 +#: ckan/controllers/package.py:1454 +#, python-format +msgid "User %r not authorized to edit %s" +msgstr "Miembro %r walang pahintulot mag-edit ng %s" + +#: ckan/controllers/group.py:565 ckan/controllers/group.py:597 +#: ckan/controllers/package.py:945 ckan/controllers/package.py:993 +#: ckan/controllers/user.py:236 ckan/controllers/user.py:348 +#: ckan/controllers/user.py:517 +msgid "Integrity Error" +msgstr "" + +#: ckan/controllers/group.py:623 +#, python-format +msgid "User %r not authorized to edit %s authorizations" +msgstr "" + +#: ckan/controllers/group.py:643 ckan/controllers/group.py:658 +#: ckan/controllers/group.py:677 ckan/controllers/group.py:761 +#, python-format +msgid "Unauthorized to delete group %s" +msgstr "" + +#: ckan/controllers/group.py:649 +msgid "Organization has been deleted." +msgstr "" + +#: ckan/controllers/group.py:651 +msgid "Group has been deleted." +msgstr "" + +#: ckan/controllers/group.py:653 +#, python-format +msgid "%s has been deleted." +msgstr "" + +#: ckan/controllers/group.py:729 +#, python-format +msgid "Unauthorized to add member to group %s" +msgstr "" + +#: ckan/controllers/group.py:748 +#, python-format +msgid "Unauthorized to delete group %s members" +msgstr "" + +#: ckan/controllers/group.py:755 +msgid "Group member has been deleted." +msgstr "" + +#: ckan/controllers/group.py:779 ckan/controllers/package.py:412 +msgid "Select two revisions before doing the comparison." +msgstr "" + +#: ckan/controllers/group.py:798 +#, python-format +msgid "User %r not authorized to edit %r" +msgstr "" + +#: ckan/controllers/group.py:805 +msgid "CKAN Group Revision History" +msgstr "" + +#: ckan/controllers/group.py:809 +msgid "Recent changes to CKAN Group: " +msgstr "" + +#: ckan/controllers/group.py:830 ckan/controllers/package.py:462 +msgid "Log message: " +msgstr "" + +#: ckan/controllers/group.py:858 +msgid "Unauthorized to read group {group_id}" +msgstr "" + +#: ckan/controllers/group.py:879 ckan/controllers/package.py:1195 +#: ckan/controllers/user.py:684 +msgid "You are now following {0}" +msgstr "" + +#: ckan/controllers/group.py:899 ckan/controllers/package.py:1214 +#: ckan/controllers/user.py:704 +msgid "You are no longer following {0}" +msgstr "" + +#: ckan/controllers/group.py:919 ckan/controllers/user.py:549 +#, python-format +msgid "Unauthorized to view followers %s" +msgstr "" + +#: ckan/controllers/home.py:34 +msgid "This site is currently off-line. Database is not initialised." +msgstr "" + +#: ckan/controllers/home.py:79 +#, python-format +msgid "Please update your profile and add your email address. " +msgstr "" + +#: ckan/controllers/home.py:81 +#, python-format +msgid "%s uses your email address if you need to reset your password." +msgstr "" + +#: ckan/controllers/package.py:293 +msgid "Parameter \"{parameter_name}\" is not an integer" +msgstr "" + +#: ckan/controllers/package.py:317 ckan/controllers/package.py:325 +#: ckan/controllers/package.py:365 ckan/controllers/package.py:431 +#: ckan/controllers/package.py:765 ckan/controllers/package.py:824 +#: ckan/controllers/package.py:842 ckan/controllers/package.py:943 +#: ckan/controllers/package.py:991 ckan/controllers/package.py:1043 +#: ckan/controllers/package.py:1085 ckan/controllers/package.py:1240 +#: ckan/controllers/package.py:1256 ckan/controllers/package.py:1323 +#: ckan/controllers/package.py:1424 ckan/controllers/package.py:1461 +#: ckan/controllers/package.py:1574 +msgid "Dataset not found" +msgstr "" + +#: ckan/controllers/package.py:327 ckan/controllers/package.py:367 +#: ckan/controllers/package.py:429 ckan/controllers/package.py:763 +#: ckan/controllers/package.py:822 ckan/controllers/package.py:840 +#: ckan/controllers/package.py:941 ckan/controllers/package.py:989 +#: ckan/controllers/package.py:1242 +#, python-format +msgid "Unauthorized to read package %s" +msgstr "" + +#: ckan/controllers/package.py:353 ckan/controllers/package.py:355 +#: ckan/controllers/package.py:357 +#, python-format +msgid "Invalid revision format: %r" +msgstr "" + +#: ckan/controllers/package.py:393 +msgid "" +"Viewing {package_type} datasets in {format} format is not supported " +"(template file {file} not found)." +msgstr "" + +#: ckan/controllers/package.py:438 +msgid "CKAN Dataset Revision History" +msgstr "" + +#: ckan/controllers/package.py:441 +msgid "Recent changes to CKAN Dataset: " +msgstr "" + +#: ckan/controllers/package.py:498 +msgid "Unauthorized to create a package" +msgstr "" + +#: ckan/controllers/package.py:576 ckanext/datapusher/plugin.py:59 +msgid "Unauthorized to edit this resource" +msgstr "" + +#: ckan/controllers/package.py:599 ckan/controllers/package.py:1072 +#: ckan/controllers/package.py:1094 ckan/controllers/package.py:1163 +#: ckan/controllers/package.py:1353 ckan/controllers/package.py:1435 +#: ckan/controllers/package.py:1468 ckan/controllers/package.py:1582 +#: ckan/controllers/package.py:1638 ckanext/datapusher/plugin.py:57 +#: ckanext/resourceproxy/controller.py:31 +msgid "Resource not found" +msgstr "" + +#: ckan/controllers/package.py:653 +msgid "Unauthorized to update dataset" +msgstr "" + +#: ckan/controllers/package.py:655 ckan/controllers/package.py:692 +#: ckan/controllers/package.py:721 +msgid "The dataset {id} could not be found." +msgstr "" + +#: ckan/controllers/package.py:659 +msgid "You must add at least one data resource" +msgstr "" + +#: ckan/controllers/package.py:667 ckanext/datapusher/helpers.py:22 +msgid "Error" +msgstr "" + +#: ckan/controllers/package.py:690 +msgid "Unauthorized to create a resource" +msgstr "" + +#: ckan/controllers/package.py:726 +msgid "Unauthorized to create a resource for this package" +msgstr "" + +#: ckan/controllers/package.py:951 +msgid "Unable to add package to search index." +msgstr "" + +#: ckan/controllers/package.py:999 +msgid "Unable to update search index." +msgstr "" + +#: ckan/controllers/package.py:1036 +msgid "Dataset has been deleted." +msgstr "" + +#: ckan/controllers/package.py:1041 ckan/controllers/package.py:1059 +#, python-format +msgid "Unauthorized to delete package %s" +msgstr "" + +#: ckan/controllers/package.py:1064 +msgid "Resource has been deleted." +msgstr "" + +#: ckan/controllers/package.py:1070 +#, python-format +msgid "Unauthorized to delete resource %s" +msgstr "" + +#: ckan/controllers/package.py:1087 ckan/controllers/package.py:1258 +#: ckan/controllers/package.py:1325 ckan/controllers/package.py:1426 +#: ckan/controllers/package.py:1463 ckan/controllers/package.py:1576 +#, python-format +msgid "Unauthorized to read dataset %s" +msgstr "" + +#: ckan/controllers/package.py:1133 ckan/controllers/package.py:1597 +msgid "Resource view not found" +msgstr "" + +#: ckan/controllers/package.py:1165 ckan/controllers/package.py:1355 +#: ckan/controllers/package.py:1437 ckan/controllers/package.py:1470 +#: ckan/controllers/package.py:1584 ckan/controllers/package.py:1640 +#, python-format +msgid "Unauthorized to read resource %s" +msgstr "" + +#: ckan/controllers/package.py:1174 +msgid "Resource data not found" +msgstr "" + +#: ckan/controllers/package.py:1183 +msgid "No download is available" +msgstr "" + +#: ckan/controllers/package.py:1505 +msgid "Unauthorized to edit resource" +msgstr "" + +#: ckan/controllers/package.py:1523 +msgid "View not found" +msgstr "" + +#: ckan/controllers/package.py:1525 +#, python-format +msgid "Unauthorized to view View %s" +msgstr "" + +#: ckan/controllers/package.py:1531 +msgid "View Type Not found" +msgstr "" + +#: ckan/controllers/package.py:1591 +msgid "Bad resource view data" +msgstr "" + +#: ckan/controllers/package.py:1600 +#, python-format +msgid "Unauthorized to read resource view %s" +msgstr "" + +#: ckan/controllers/package.py:1603 +msgid "Resource view not supplied" +msgstr "" + +#: ckan/controllers/package.py:1632 +msgid "No preview has been defined." +msgstr "" + +#: ckan/controllers/revision.py:42 +msgid "CKAN Repository Revision History" +msgstr "" + +#: ckan/controllers/revision.py:44 +msgid "Recent changes to the CKAN repository." +msgstr "" + +#: ckan/controllers/revision.py:108 +#, python-format +msgid "Datasets affected: %s.\n" +msgstr "" + +#: ckan/controllers/revision.py:188 +msgid "Revision updated" +msgstr "" + +#: ckan/controllers/tag.py:56 +msgid "Other" +msgstr "" + +#: ckan/controllers/tag.py:70 +msgid "Tag not found" +msgstr "" + +#: ckan/controllers/user.py:71 ckan/controllers/user.py:219 +#: ckan/controllers/user.py:234 ckan/controllers/user.py:297 +#: ckan/controllers/user.py:346 ckan/controllers/user.py:493 +#: ckan/controllers/user.py:515 ckan/logic/auth/update.py:198 +msgid "User not found" +msgstr "" + +#: ckan/controllers/user.py:149 +msgid "Unauthorized to register as a user." +msgstr "" + +#: ckan/controllers/user.py:166 +msgid "Unauthorized to create a user" +msgstr "" + +#: ckan/controllers/user.py:197 +msgid "Unauthorized to delete user with id \"{user_id}\"." +msgstr "" + +#: ckan/controllers/user.py:211 ckan/controllers/user.py:270 +msgid "No user specified" +msgstr "" + +#: ckan/controllers/user.py:217 ckan/controllers/user.py:295 +#: ckan/controllers/user.py:344 ckan/controllers/user.py:513 +#, python-format +msgid "Unauthorized to edit user %s" +msgstr "" + +#: ckan/controllers/user.py:221 ckan/controllers/user.py:341 +msgid "Profile updated" +msgstr "" + +#: ckan/controllers/user.py:232 +#, python-format +msgid "Unauthorized to create user %s" +msgstr "" + +#: ckan/controllers/user.py:238 +msgid "Bad Captcha. Please try again." +msgstr "" + +#: ckan/controllers/user.py:255 +#, python-format +msgid "" +"User \"%s\" is now registered but you are still logged in as \"%s\" from " +"before" +msgstr "" + +#: ckan/controllers/user.py:276 +msgid "Unauthorized to edit a user." +msgstr "" + +#: ckan/controllers/user.py:303 +#, python-format +msgid "User %s not authorized to edit %s" +msgstr "" + +#: ckan/controllers/user.py:354 +msgid "Password entered was incorrect" +msgstr "" + +#: ckan/controllers/user.py:355 ckan/templates/user/edit_user_form.html:27 +msgid "Old Password" +msgstr "" + +#: ckan/controllers/user.py:355 +msgid "incorrect password" +msgstr "" + +#: ckan/controllers/user.py:396 +msgid "Login failed. Bad username or password." +msgstr "" + +#: ckan/controllers/user.py:430 +msgid "Unauthorized to request reset password." +msgstr "" + +#: ckan/controllers/user.py:459 +#, python-format +msgid "\"%s\" matched several users" +msgstr "" + +#: ckan/controllers/user.py:461 ckan/controllers/user.py:463 +#, python-format +msgid "No such user: %s" +msgstr "" + +#: ckan/controllers/user.py:468 +msgid "Please check your inbox for a reset code." +msgstr "" + +#: ckan/controllers/user.py:472 +#, python-format +msgid "Could not send reset link: %s" +msgstr "" + +#: ckan/controllers/user.py:485 +msgid "Unauthorized to reset password." +msgstr "" + +#: ckan/controllers/user.py:497 +msgid "Invalid reset key. Please try again." +msgstr "" + +#: ckan/controllers/user.py:510 +msgid "Your password has been reset." +msgstr "" + +#: ckan/controllers/user.py:531 +msgid "Your password must be 4 characters or longer." +msgstr "" + +#: ckan/controllers/user.py:534 +msgid "The passwords you entered do not match." +msgstr "" + +#: ckan/controllers/user.py:537 +msgid "You must provide a password" +msgstr "" + +#: ckan/controllers/user.py:602 +msgid "Follow item not found" +msgstr "" + +#: ckan/controllers/user.py:606 +msgid "{0} not found" +msgstr "" + +#: ckan/controllers/user.py:608 +msgid "Unauthorized to read {0} {1}" +msgstr "" + +#: ckan/controllers/user.py:623 +msgid "Everything" +msgstr "" + +#: ckan/controllers/util.py:16 ckan/logic/action/__init__.py:60 +msgid "Missing Value" +msgstr "" + +#: ckan/controllers/util.py:21 +msgid "Redirecting to external site is not allowed." +msgstr "" + +#: ckan/lib/activity_streams.py:64 +msgid "{actor} added the tag {tag} to the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:67 +msgid "{actor} updated the group {group}" +msgstr "" + +#: ckan/lib/activity_streams.py:70 +msgid "{actor} updated the organization {organization}" +msgstr "" + +#: ckan/lib/activity_streams.py:73 +msgid "{actor} updated the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:76 +msgid "{actor} changed the extra {extra} of the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:79 +msgid "{actor} updated the resource {resource} in the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:82 +msgid "{actor} updated their profile" +msgstr "" + +#: ckan/lib/activity_streams.py:86 +msgid "" +"{actor} updated the {related_type} {related_item} of the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:89 +msgid "{actor} updated the {related_type} {related_item}" +msgstr "" + +#: ckan/lib/activity_streams.py:92 +msgid "{actor} deleted the group {group}" +msgstr "" + +#: ckan/lib/activity_streams.py:95 +msgid "{actor} deleted the organization {organization}" +msgstr "" + +#: ckan/lib/activity_streams.py:98 +msgid "{actor} deleted the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:101 +msgid "{actor} deleted the extra {extra} from the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:104 +msgid "{actor} deleted the resource {resource} from the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:108 +msgid "{actor} created the group {group}" +msgstr "" + +#: ckan/lib/activity_streams.py:111 +msgid "{actor} created the organization {organization}" +msgstr "" + +#: ckan/lib/activity_streams.py:114 +msgid "{actor} created the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:117 +msgid "{actor} added the extra {extra} to the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:120 +msgid "{actor} added the resource {resource} to the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:123 +msgid "{actor} signed up" +msgstr "" + +#: ckan/lib/activity_streams.py:126 +msgid "{actor} removed the tag {tag} from the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:129 +msgid "{actor} deleted the related item {related_item}" +msgstr "" + +#: ckan/lib/activity_streams.py:132 +msgid "{actor} started following {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:135 +msgid "{actor} started following {user}" +msgstr "" + +#: ckan/lib/activity_streams.py:138 +msgid "{actor} started following {group}" +msgstr "" + +#: ckan/lib/activity_streams.py:142 +msgid "" +"{actor} added the {related_type} {related_item} to the dataset {dataset}" +msgstr "" + +#: ckan/lib/activity_streams.py:145 +msgid "{actor} added the {related_type} {related_item}" +msgstr "" + +#: ckan/lib/datapreview.py:265 ckan/templates/group/edit_base.html:16 +#: ckan/templates/organization/edit_base.html:17 +#: ckan/templates/package/resource_read.html:37 +#: ckan/templates/package/resource_views.html:4 +msgid "View" +msgstr "" + +#: ckan/lib/email_notifications.py:103 +msgid "{n} new activity from {site_title}" +msgid_plural "{n} new activities from {site_title}" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:17 +msgid "January" +msgstr "" + +#: ckan/lib/formatters.py:21 +msgid "February" +msgstr "" + +#: ckan/lib/formatters.py:25 +msgid "March" +msgstr "" + +#: ckan/lib/formatters.py:29 +msgid "April" +msgstr "" + +#: ckan/lib/formatters.py:33 +msgid "May" +msgstr "" + +#: ckan/lib/formatters.py:37 +msgid "June" +msgstr "" + +#: ckan/lib/formatters.py:41 +msgid "July" +msgstr "" + +#: ckan/lib/formatters.py:45 +msgid "August" +msgstr "" + +#: ckan/lib/formatters.py:49 +msgid "September" +msgstr "" + +#: ckan/lib/formatters.py:53 +msgid "October" +msgstr "" + +#: ckan/lib/formatters.py:57 +msgid "November" +msgstr "" + +#: ckan/lib/formatters.py:61 +msgid "December" +msgstr "" + +#: ckan/lib/formatters.py:114 +msgid "Just now" +msgstr "" + +#: ckan/lib/formatters.py:116 +msgid "{mins} minute ago" +msgid_plural "{mins} minutes ago" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:119 +msgid "{hours} hour ago" +msgid_plural "{hours} hours ago" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:125 +msgid "{days} day ago" +msgid_plural "{days} days ago" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:128 +msgid "{months} month ago" +msgid_plural "{months} months ago" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:130 +msgid "over {years} year ago" +msgid_plural "over {years} years ago" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/formatters.py:146 +msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" +msgstr "" + +#: ckan/lib/formatters.py:151 +msgid "{month} {day}, {year}" +msgstr "" + +#: ckan/lib/formatters.py:167 +msgid "{bytes} bytes" +msgstr "" + +#: ckan/lib/formatters.py:169 +msgid "{kibibytes} KiB" +msgstr "" + +#: ckan/lib/formatters.py:171 +msgid "{mebibytes} MiB" +msgstr "" + +#: ckan/lib/formatters.py:173 +msgid "{gibibytes} GiB" +msgstr "" + +#: ckan/lib/formatters.py:175 +msgid "{tebibytes} TiB" +msgstr "" + +#: ckan/lib/formatters.py:187 +msgid "{n}" +msgstr "" + +#: ckan/lib/formatters.py:189 +msgid "{k}k" +msgstr "" + +#: ckan/lib/formatters.py:191 +msgid "{m}M" +msgstr "" + +#: ckan/lib/formatters.py:193 +msgid "{g}G" +msgstr "" + +#: ckan/lib/formatters.py:195 +msgid "{t}T" +msgstr "" + +#: ckan/lib/formatters.py:197 +msgid "{p}P" +msgstr "" + +#: ckan/lib/formatters.py:199 +msgid "{e}E" +msgstr "" + +#: ckan/lib/formatters.py:201 +msgid "{z}Z" +msgstr "" + +#: ckan/lib/formatters.py:203 +msgid "{y}Y" +msgstr "" + +#: ckan/lib/helpers.py:939 +msgid "Update your avatar at gravatar.com" +msgstr "" + +#: ckan/lib/helpers.py:1145 ckan/lib/helpers.py:1157 +msgid "Unknown" +msgstr "" + +#: ckan/lib/helpers.py:1202 +msgid "Unnamed resource" +msgstr "" + +#: ckan/lib/helpers.py:1250 +msgid "Created new dataset." +msgstr "" + +#: ckan/lib/helpers.py:1252 +msgid "Edited resources." +msgstr "" + +#: ckan/lib/helpers.py:1254 +msgid "Edited settings." +msgstr "" + +#: ckan/lib/helpers.py:1518 +msgid "{number} view" +msgid_plural "{number} views" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/helpers.py:1520 +msgid "{number} recent view" +msgid_plural "{number} recent views" +msgstr[0] "" +msgstr[1] "" + +#: ckan/lib/mailer.py:25 +#, python-format +msgid "Dear %s," +msgstr "" + +#: ckan/lib/mailer.py:38 +#, python-format +msgid "%s <%s>" +msgstr "" + +#: ckan/lib/mailer.py:99 +msgid "No recipient email address available!" +msgstr "" + +#: ckan/lib/mailer.py:104 +msgid "" +"You have requested your password on {site_title} to be reset.\n" +"\n" +"Please click the following link to confirm this request:\n" +"\n" +" {reset_link}\n" +msgstr "" + +#: ckan/lib/mailer.py:119 +msgid "" +"You have been invited to {site_title}. A user has already been created to you with the username {user_name}. You can change it later.\n" +"\n" +"To accept this invite, please reset your password at:\n" +"\n" +" {reset_link}\n" +msgstr "" + +#: ckan/lib/mailer.py:145 ckan/templates/user/request_reset.html:3 +#: ckan/templates/user/request_reset.html:13 +msgid "Reset your password" +msgstr "" + +#: ckan/lib/mailer.py:151 +msgid "Invite for {site_title}" +msgstr "" + +#: ckan/lib/navl/dictization_functions.py:11 +#: ckan/lib/navl/dictization_functions.py:13 +#: ckan/lib/navl/dictization_functions.py:15 +#: ckan/lib/navl/dictization_functions.py:17 +#: ckan/lib/navl/dictization_functions.py:19 +#: ckan/lib/navl/dictization_functions.py:21 +#: ckan/lib/navl/dictization_functions.py:23 +#: ckan/lib/navl/dictization_functions.py:25 ckan/lib/navl/validators.py:23 +#: ckan/lib/navl/validators.py:30 ckan/lib/navl/validators.py:50 +#: ckan/logic/validators.py:630 ckan/logic/action/get.py:2107 +msgid "Missing value" +msgstr "" + +#: ckan/lib/navl/validators.py:64 +#, python-format +msgid "The input field %(name)s was not expected." +msgstr "" + +#: ckan/lib/navl/validators.py:116 +msgid "Please enter an integer value" +msgstr "" + +#: ckan/logic/__init__.py:89 ckan/logic/action/__init__.py:58 +#: ckan/templates/package/edit_base.html:21 +#: ckan/templates/package/resources.html:5 +#: ckan/templates/package/snippets/package_context.html:12 +#: ckan/templates/package/snippets/resources.html:20 +#: ckan/templates/snippets/context/dataset.html:13 +#: ckanext/example_theme/v18_snippet_api/templates/ajax_snippets/example_theme_popover.html:15 +msgid "Resources" +msgstr "" + +#: ckan/logic/__init__.py:89 ckan/logic/action/__init__.py:58 +msgid "Package resource(s) invalid" +msgstr "" + +#: ckan/logic/__init__.py:96 ckan/logic/__init__.py:98 +#: ckan/logic/action/__init__.py:60 ckan/logic/action/__init__.py:62 +msgid "Extras" +msgstr "" + +#: ckan/logic/converters.py:72 ckan/logic/converters.py:87 +#, python-format +msgid "Tag vocabulary \"%s\" does not exist" +msgstr "" + +#: ckan/logic/converters.py:119 ckan/logic/validators.py:211 +#: ckan/logic/validators.py:228 ckan/logic/validators.py:729 +#: ckan/templates/group/members.html:17 +#: ckan/templates/organization/members.html:17 +#: ckanext/stats/templates/ckanext/stats/index.html:156 +msgid "User" +msgstr "" + +#: ckan/logic/converters.py:144 ckan/logic/validators.py:146 +#: ckan/logic/validators.py:188 ckan/templates/package/read_base.html:19 +#: ckanext/stats/templates/ckanext/stats/index.html:89 +msgid "Dataset" +msgstr "" + +#: ckan/logic/converters.py:169 ckan/logic/validators.py:241 +#: ckanext/stats/templates/ckanext/stats/index.html:113 +msgid "Group" +msgstr "" + +#: ckan/logic/converters.py:178 +msgid "Could not parse as valid JSON" +msgstr "" + +#: ckan/logic/validators.py:30 ckan/logic/validators.py:39 +msgid "A organization must be supplied" +msgstr "" + +#: ckan/logic/validators.py:44 +msgid "Organization does not exist" +msgstr "" + +#: ckan/logic/validators.py:49 +msgid "You cannot add a dataset to this organization" +msgstr "" + +#: ckan/logic/validators.py:89 +msgid "Invalid integer" +msgstr "" + +#: ckan/logic/validators.py:94 +msgid "Must be a natural number" +msgstr "" + +#: ckan/logic/validators.py:100 +msgid "Must be a postive integer" +msgstr "" + +#: ckan/logic/validators.py:127 +msgid "Date format incorrect" +msgstr "" + +#: ckan/logic/validators.py:136 +msgid "No links are allowed in the log_message." +msgstr "" + +#: ckan/logic/validators.py:156 +msgid "Dataset id already exists" +msgstr "" + +#: ckan/logic/validators.py:197 ckan/logic/validators.py:288 +msgid "Resource" +msgstr "" + +#: ckan/logic/validators.py:255 ckan/templates/package/related_list.html:4 +#: ckan/templates/snippets/related.html:2 +msgid "Related" +msgstr "" + +#: ckan/logic/validators.py:265 +msgid "That group name or ID does not exist." +msgstr "" + +#: ckan/logic/validators.py:279 +msgid "Activity type" +msgstr "" + +#: ckan/logic/validators.py:354 +msgid "Names must be strings" +msgstr "" + +#: ckan/logic/validators.py:358 +msgid "That name cannot be used" +msgstr "" + +#: ckan/logic/validators.py:361 +#, python-format +msgid "Must be at least %s characters long" +msgstr "" + +#: ckan/logic/validators.py:363 ckan/logic/validators.py:646 +#, python-format +msgid "Name must be a maximum of %i characters long" +msgstr "" + +#: ckan/logic/validators.py:366 +msgid "" +"Must be purely lowercase alphanumeric (ascii) characters and these symbols: " +"-_" +msgstr "" + +#: ckan/logic/validators.py:384 +msgid "That URL is already in use." +msgstr "" + +#: ckan/logic/validators.py:389 +#, python-format +msgid "Name \"%s\" length is less than minimum %s" +msgstr "" + +#: ckan/logic/validators.py:393 +#, python-format +msgid "Name \"%s\" length is more than maximum %s" +msgstr "" + +#: ckan/logic/validators.py:399 +#, python-format +msgid "Version must be a maximum of %i characters long" +msgstr "" + +#: ckan/logic/validators.py:417 +#, python-format +msgid "Duplicate key \"%s\"" +msgstr "" + +#: ckan/logic/validators.py:433 +msgid "Group name already exists in database" +msgstr "" + +#: ckan/logic/validators.py:439 +#, python-format +msgid "Tag \"%s\" length is less than minimum %s" +msgstr "" + +#: ckan/logic/validators.py:443 +#, python-format +msgid "Tag \"%s\" length is more than maximum %i" +msgstr "" + +#: ckan/logic/validators.py:451 +#, python-format +msgid "Tag \"%s\" must be alphanumeric characters or symbols: -_." +msgstr "" + +#: ckan/logic/validators.py:459 +#, python-format +msgid "Tag \"%s\" must not be uppercase" +msgstr "" + +#: ckan/logic/validators.py:568 +msgid "User names must be strings" +msgstr "" + +#: ckan/logic/validators.py:584 +msgid "That login name is not available." +msgstr "" + +#: ckan/logic/validators.py:593 +msgid "Please enter both passwords" +msgstr "" + +#: ckan/logic/validators.py:601 +msgid "Passwords must be strings" +msgstr "" + +#: ckan/logic/validators.py:605 +msgid "Your password must be 4 characters or longer" +msgstr "" + +#: ckan/logic/validators.py:613 +msgid "The passwords you entered do not match" +msgstr "" + +#: ckan/logic/validators.py:634 +msgid "" +"Edit not allowed as it looks like spam. Please avoid links in your " +"description." +msgstr "" + +#: ckan/logic/validators.py:643 +#, python-format +msgid "Name must be at least %s characters long" +msgstr "" + +#: ckan/logic/validators.py:651 +msgid "That vocabulary name is already in use." +msgstr "" + +#: ckan/logic/validators.py:657 +#, python-format +msgid "Cannot change value of key from %s to %s. This key is read-only" +msgstr "" + +#: ckan/logic/validators.py:666 +msgid "Tag vocabulary was not found." +msgstr "" + +#: ckan/logic/validators.py:679 +#, python-format +msgid "Tag %s does not belong to vocabulary %s" +msgstr "" + +#: ckan/logic/validators.py:685 +msgid "No tag name" +msgstr "" + +#: ckan/logic/validators.py:698 +#, python-format +msgid "Tag %s already belongs to vocabulary %s" +msgstr "" + +#: ckan/logic/validators.py:721 +msgid "Please provide a valid URL" +msgstr "" + +#: ckan/logic/validators.py:735 +msgid "role does not exist." +msgstr "" + +#: ckan/logic/validators.py:764 +msgid "Datasets with no organization can't be private." +msgstr "" + +#: ckan/logic/validators.py:770 +msgid "Not a list" +msgstr "" + +#: ckan/logic/validators.py:773 +msgid "Not a string" +msgstr "" + +#: ckan/logic/validators.py:805 +msgid "This parent would create a loop in the hierarchy" +msgstr "" + +#: ckan/logic/validators.py:815 +msgid "\"filter_fields\" and \"filter_values\" should have the same length" +msgstr "" + +#: ckan/logic/validators.py:826 +msgid "\"filter_fields\" is required when \"filter_values\" is filled" +msgstr "" + +#: ckan/logic/validators.py:829 +msgid "\"filter_values\" is required when \"filter_fields\" is filled" +msgstr "" + +#: ckan/logic/validators.py:843 +msgid "There is a schema field with the same name" +msgstr "" + +#: ckan/logic/action/create.py:178 ckan/logic/action/create.py:723 +#, python-format +msgid "REST API: Create object %s" +msgstr "" + +#: ckan/logic/action/create.py:602 +#, python-format +msgid "REST API: Create package relationship: %s %s %s" +msgstr "" + +#: ckan/logic/action/create.py:643 +#, python-format +msgid "REST API: Create member object %s" +msgstr "" + +#: ckan/logic/action/create.py:862 +msgid "Trying to create an organization as a group" +msgstr "" + +#: ckan/logic/action/create.py:951 +msgid "You must supply a package id or name (parameter \"package\")." +msgstr "" + +#: ckan/logic/action/create.py:954 +msgid "You must supply a rating (parameter \"rating\")." +msgstr "" + +#: ckan/logic/action/create.py:959 +msgid "Rating must be an integer value." +msgstr "" + +#: ckan/logic/action/create.py:963 +#, python-format +msgid "Rating must be between %i and %i." +msgstr "" + +#: ckan/logic/action/create.py:1312 ckan/logic/action/create.py:1319 +msgid "You must be logged in to follow users" +msgstr "" + +#: ckan/logic/action/create.py:1332 +msgid "You cannot follow yourself" +msgstr "" + +#: ckan/logic/action/create.py:1340 ckan/logic/action/create.py:1397 +#: ckan/logic/action/create.py:1530 +msgid "You are already following {0}" +msgstr "" + +#: ckan/logic/action/create.py:1371 ckan/logic/action/create.py:1379 +msgid "You must be logged in to follow a dataset." +msgstr "" + +#: ckan/logic/action/create.py:1431 +msgid "User {username} does not exist." +msgstr "" + +#: ckan/logic/action/create.py:1506 ckan/logic/action/create.py:1514 +msgid "You must be logged in to follow a group." +msgstr "" + +#: ckan/logic/action/delete.py:72 +#, python-format +msgid "REST API: Delete Package: %s" +msgstr "" + +#: ckan/logic/action/delete.py:241 ckan/logic/action/delete.py:370 +#, python-format +msgid "REST API: Delete %s" +msgstr "" + +#: ckan/logic/action/delete.py:330 +#, python-format +msgid "REST API: Delete Member: %s" +msgstr "" + +#: ckan/logic/action/delete.py:556 ckan/logic/action/delete.py:582 +#: ckan/logic/action/get.py:2506 ckan/logic/action/update.py:993 +msgid "id not in data" +msgstr "" + +#: ckan/logic/action/delete.py:560 ckan/logic/action/get.py:2509 +#: ckan/logic/action/update.py:997 +#, python-format +msgid "Could not find vocabulary \"%s\"" +msgstr "" + +#: ckan/logic/action/delete.py:590 +#, python-format +msgid "Could not find tag \"%s\"" +msgstr "" + +#: ckan/logic/action/delete.py:616 ckan/logic/action/delete.py:620 +msgid "You must be logged in to unfollow something." +msgstr "" + +#: ckan/logic/action/delete.py:631 +msgid "You are not following {0}." +msgstr "" + +#: ckan/logic/action/get.py:1147 ckan/logic/action/update.py:133 +#: ckan/logic/action/update.py:147 +msgid "Resource was not found." +msgstr "" + +#: ckan/logic/action/get.py:2111 +msgid "Do not specify if using \"query\" parameter" +msgstr "" + +#: ckan/logic/action/get.py:2120 +msgid "Must be : pair(s)" +msgstr "" + +#: ckan/logic/action/get.py:2152 +msgid "Field \"{field}\" not recognised in resource_search." +msgstr "" + +#: ckan/logic/action/update.py:68 +msgid "Item was not found." +msgstr "" + +#: ckan/logic/action/update.py:297 ckan/logic/action/update.py:1094 +msgid "Package was not found." +msgstr "" + +#: ckan/logic/action/update.py:340 ckan/logic/action/update.py:561 +#, python-format +msgid "REST API: Update object %s" +msgstr "" + +#: ckan/logic/action/update.py:443 +#, python-format +msgid "REST API: Update package relationship: %s %s %s" +msgstr "" + +#: ckan/logic/action/update.py:801 +msgid "TaskStatus was not found." +msgstr "" + +#: ckan/logic/action/update.py:1098 +msgid "Organization was not found." +msgstr "" + +#: ckan/logic/auth/create.py:25 ckan/logic/auth/create.py:43 +#, python-format +msgid "User %s not authorized to create packages" +msgstr "" + +#: ckan/logic/auth/create.py:29 ckan/logic/auth/update.py:43 +#, python-format +msgid "User %s not authorized to edit these groups" +msgstr "" + +#: ckan/logic/auth/create.py:36 +#, python-format +msgid "User %s not authorized to add dataset to this organization" +msgstr "" + +#: ckan/logic/auth/create.py:58 +msgid "You must be a sysadmin to create a featured related item" +msgstr "" + +#: ckan/logic/auth/create.py:62 +msgid "You must be logged in to add a related item" +msgstr "" + +#: ckan/logic/auth/create.py:77 +msgid "No dataset id provided, cannot check auth." +msgstr "" + +#: ckan/logic/auth/create.py:84 ckan/logic/auth/delete.py:32 +#: ckan/logic/auth/get.py:135 ckan/logic/auth/update.py:61 +msgid "No package found for this resource, cannot check auth." +msgstr "" + +#: ckan/logic/auth/create.py:92 +#, python-format +msgid "User %s not authorized to create resources on dataset %s" +msgstr "" + +#: ckan/logic/auth/create.py:124 +#, python-format +msgid "User %s not authorized to edit these packages" +msgstr "" + +#: ckan/logic/auth/create.py:135 +#, python-format +msgid "User %s not authorized to create groups" +msgstr "" + +#: ckan/logic/auth/create.py:145 +#, python-format +msgid "User %s not authorized to create organizations" +msgstr "" + +#: ckan/logic/auth/create.py:161 +msgid "User {user} not authorized to create users via the API" +msgstr "" + +#: ckan/logic/auth/create.py:164 +msgid "Not authorized to create users" +msgstr "" + +#: ckan/logic/auth/create.py:207 +msgid "Group was not found." +msgstr "" + +#: ckan/logic/auth/create.py:227 +msgid "Valid API key needed to create a package" +msgstr "" + +#: ckan/logic/auth/create.py:235 +msgid "Valid API key needed to create a group" +msgstr "" + +#: ckan/logic/auth/create.py:255 +#, python-format +msgid "User %s not authorized to add members" +msgstr "" + +#: ckan/logic/auth/create.py:279 ckan/logic/auth/update.py:113 +#, python-format +msgid "User %s not authorized to edit group %s" +msgstr "" + +#: ckan/logic/auth/delete.py:38 +#, python-format +msgid "User %s not authorized to delete resource %s" +msgstr "" + +#: ckan/logic/auth/delete.py:54 +msgid "Resource view not found, cannot check auth." +msgstr "" + +#: ckan/logic/auth/delete.py:69 ckan/logic/auth/delete.py:83 +msgid "Only the owner can delete a related item" +msgstr "" + +#: ckan/logic/auth/delete.py:95 +#, python-format +msgid "User %s not authorized to delete relationship %s" +msgstr "" + +#: ckan/logic/auth/delete.py:104 +#, python-format +msgid "User %s not authorized to delete groups" +msgstr "" + +#: ckan/logic/auth/delete.py:108 +#, python-format +msgid "User %s not authorized to delete group %s" +msgstr "" + +#: ckan/logic/auth/delete.py:125 +#, python-format +msgid "User %s not authorized to delete organizations" +msgstr "" + +#: ckan/logic/auth/delete.py:129 +#, python-format +msgid "User %s not authorized to delete organization %s" +msgstr "" + +#: ckan/logic/auth/delete.py:142 +#, python-format +msgid "User %s not authorized to delete task_status" +msgstr "" + +#: ckan/logic/auth/get.py:10 ckan/logic/auth/get.py:270 +msgid "Not authorized" +msgstr "" + +#: ckan/logic/auth/get.py:97 +#, python-format +msgid "User %s not authorized to read these packages" +msgstr "" + +#: ckan/logic/auth/get.py:119 +#, python-format +msgid "User %s not authorized to read package %s" +msgstr "" + +#: ckan/logic/auth/get.py:141 +#, python-format +msgid "User %s not authorized to read resource %s" +msgstr "" + +#: ckan/logic/auth/get.py:166 +#, python-format +msgid "User %s not authorized to read group %s" +msgstr "" + +#: ckan/logic/auth/get.py:237 +msgid "You must be logged in to access your dashboard." +msgstr "" + +#: ckan/logic/auth/update.py:37 +#, python-format +msgid "User %s not authorized to edit package %s" +msgstr "" + +#: ckan/logic/auth/update.py:69 +#, python-format +msgid "User %s not authorized to edit resource %s" +msgstr "" + +#: ckan/logic/auth/update.py:98 +#, python-format +msgid "User %s not authorized to change state of package %s" +msgstr "" + +#: ckan/logic/auth/update.py:126 +#, python-format +msgid "User %s not authorized to edit organization %s" +msgstr "" + +#: ckan/logic/auth/update.py:137 ckan/logic/auth/update.py:143 +msgid "Only the owner can update a related item" +msgstr "" + +#: ckan/logic/auth/update.py:148 +msgid "You must be a sysadmin to change a related item's featured field." +msgstr "" + +#: ckan/logic/auth/update.py:165 +#, python-format +msgid "User %s not authorized to change state of group %s" +msgstr "" + +#: ckan/logic/auth/update.py:182 +#, python-format +msgid "User %s not authorized to edit permissions of group %s" +msgstr "" + +#: ckan/logic/auth/update.py:209 +msgid "Have to be logged in to edit user" +msgstr "" + +#: ckan/logic/auth/update.py:217 +#, python-format +msgid "User %s not authorized to edit user %s" +msgstr "" + +#: ckan/logic/auth/update.py:228 +msgid "User {0} not authorized to update user {1}" +msgstr "" + +#: ckan/logic/auth/update.py:236 +#, python-format +msgid "User %s not authorized to change state of revision" +msgstr "" + +#: ckan/logic/auth/update.py:245 +#, python-format +msgid "User %s not authorized to update task_status table" +msgstr "" + +#: ckan/logic/auth/update.py:259 +#, python-format +msgid "User %s not authorized to update term_translation table" +msgstr "" + +#: ckan/logic/auth/update.py:281 +msgid "Valid API key needed to edit a package" +msgstr "" + +#: ckan/logic/auth/update.py:291 +msgid "Valid API key needed to edit a group" +msgstr "" + +#: ckan/model/license.py:220 +msgid "License not specified" +msgstr "" + +#: ckan/model/license.py:230 +msgid "Open Data Commons Public Domain Dedication and License (PDDL)" +msgstr "" + +#: ckan/model/license.py:240 +msgid "Open Data Commons Open Database License (ODbL)" +msgstr "" + +#: ckan/model/license.py:250 +msgid "Open Data Commons Attribution License" +msgstr "" + +#: ckan/model/license.py:261 +msgid "Creative Commons CCZero" +msgstr "" + +#: ckan/model/license.py:270 +msgid "Creative Commons Attribution" +msgstr "" + +#: ckan/model/license.py:280 +msgid "Creative Commons Attribution Share-Alike" +msgstr "" + +#: ckan/model/license.py:289 +msgid "GNU Free Documentation License" +msgstr "" + +#: ckan/model/license.py:299 +msgid "Other (Open)" +msgstr "" + +#: ckan/model/license.py:309 +msgid "Other (Public Domain)" +msgstr "" + +#: ckan/model/license.py:319 +msgid "Other (Attribution)" +msgstr "" + +#: ckan/model/license.py:331 +msgid "UK Open Government Licence (OGL)" +msgstr "" + +#: ckan/model/license.py:339 +msgid "Creative Commons Non-Commercial (Any)" +msgstr "" + +#: ckan/model/license.py:347 +msgid "Other (Non-Commercial)" +msgstr "" + +#: ckan/model/license.py:355 +msgid "Other (Not Open)" +msgstr "" + +#: ckan/model/package_relationship.py:52 +#, python-format +msgid "depends on %s" +msgstr "" + +#: ckan/model/package_relationship.py:52 +#, python-format +msgid "is a dependency of %s" +msgstr "" + +#: ckan/model/package_relationship.py:53 +#, python-format +msgid "derives from %s" +msgstr "" + +#: ckan/model/package_relationship.py:53 +#, python-format +msgid "has derivation %s" +msgstr "" + +#: ckan/model/package_relationship.py:54 +#, python-format +msgid "links to %s" +msgstr "" + +#: ckan/model/package_relationship.py:54 +#, python-format +msgid "is linked from %s" +msgstr "" + +#: ckan/model/package_relationship.py:55 +#, python-format +msgid "is a child of %s" +msgstr "" + +#: ckan/model/package_relationship.py:55 +#, python-format +msgid "is a parent of %s" +msgstr "" + +#: ckan/model/package_relationship.py:59 +#, python-format +msgid "has sibling %s" +msgstr "" + +#: ckan/public/base/javascript/modules/activity-stream.js:20 +#: ckan/public/base/javascript/modules/popover-context.js:45 +#: ckan/templates/package/snippets/data_api_button.html:8 +#: ckan/templates/tests/mock_json_resource_preview_template.html:7 +#: ckan/templates/tests/mock_resource_preview_template.html:7 +#: ckanext/reclineview/theme/templates/recline_view.html:12 +#: ckanext/textview/theme/templates/text_view.html:9 +msgid "Loading..." +msgstr "" + +#: ckan/public/base/javascript/modules/api-info.js:20 +msgid "There is no API data to load for this resource" +msgstr "" + +#: ckan/public/base/javascript/modules/api-info.js:21 +msgid "Failed to load data API information" +msgstr "" + +#: ckan/public/base/javascript/modules/autocomplete.js:31 +msgid "No matches found" +msgstr "" + +#: ckan/public/base/javascript/modules/autocomplete.js:32 +msgid "Start typing…" +msgstr "" + +#: ckan/public/base/javascript/modules/autocomplete.js:34 +msgid "Input is too short, must be at least one character" +msgstr "" + +#: ckan/public/base/javascript/modules/basic-form.js:4 +msgid "There are unsaved modifications to this form" +msgstr "" + +#: ckan/public/base/javascript/modules/confirm-action.js:7 +msgid "Please Confirm Action" +msgstr "" + +#: ckan/public/base/javascript/modules/confirm-action.js:8 +msgid "Are you sure you want to perform this action?" +msgstr "" + +#: ckan/public/base/javascript/modules/confirm-action.js:9 +#: ckan/templates/user/new_user_form.html:9 +#: ckan/templates/user/perform_reset.html:21 +msgid "Confirm" +msgstr "" + +#: ckan/public/base/javascript/modules/confirm-action.js:10 +#: ckan/public/base/javascript/modules/resource-reorder.js:11 +#: ckan/public/base/javascript/modules/resource-view-reorder.js:11 +#: ckan/templates/admin/confirm_reset.html:9 +#: ckan/templates/group/confirm_delete.html:14 +#: ckan/templates/group/confirm_delete_member.html:15 +#: ckan/templates/organization/confirm_delete.html:14 +#: ckan/templates/organization/confirm_delete_member.html:15 +#: ckan/templates/package/confirm_delete.html:14 +#: ckan/templates/package/confirm_delete_resource.html:14 +msgid "Cancel" +msgstr "" + +#: ckan/public/base/javascript/modules/follow.js:23 +#: ckan/templates/snippets/follow_button.html:14 +msgid "Follow" +msgstr "" + +#: ckan/public/base/javascript/modules/follow.js:24 +#: ckan/templates/snippets/follow_button.html:9 +msgid "Unfollow" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:15 +msgid "Upload" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:16 +msgid "Link" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:17 +#: ckan/templates/group/snippets/group_item.html:43 +#: ckan/templates/macros/form.html:235 +#: ckan/templates/snippets/search_form.html:66 +msgid "Remove" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:18 +#: ckan/templates/macros/form.html:412 ckanext/imageview/plugin.py:21 +#: ckanext/imageview/plugin.py:26 +msgid "Image" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:19 +msgid "Upload a file on your computer" +msgstr "" + +#: ckan/public/base/javascript/modules/image-upload.js:20 +msgid "Link to a URL on the internet (you can also link to an API)" +msgstr "" + +#: ckan/public/base/javascript/modules/related-item.js:25 +msgid "show more" +msgstr "" + +#: ckan/public/base/javascript/modules/related-item.js:26 +msgid "show less" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-reorder.js:8 +msgid "Reorder resources" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-reorder.js:9 +#: ckan/public/base/javascript/modules/resource-view-reorder.js:9 +msgid "Save order" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-reorder.js:10 +#: ckan/public/base/javascript/modules/resource-view-reorder.js:10 +msgid "Saving..." +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:25 +msgid "Upload a file" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:26 +msgid "An Error Occurred" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:27 +msgid "Resource uploaded" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:28 +msgid "Unable to upload file" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:29 +msgid "Unable to authenticate upload" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:30 +msgid "Unable to get data for uploaded file" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-upload-field.js:31 +msgid "" +"You are uploading a file. Are you sure you want to navigate away and stop " +"this upload?" +msgstr "" + +#: ckan/public/base/javascript/modules/resource-view-reorder.js:8 +msgid "Reorder resource view" +msgstr "" + +#: ckan/public/base/javascript/modules/slug-preview.js:35 +#: ckan/templates/group/snippets/group_form.html:18 +#: ckan/templates/organization/snippets/organization_form.html:18 +#: ckan/templates/package/snippets/package_basic_fields.html:13 +#: ckan/templates/package/snippets/resource_form.html:24 +msgid "URL" +msgstr "" + +#: ckan/public/base/javascript/modules/slug-preview.js:36 +#: ckan/templates/group/edit_base.html:20 ckan/templates/group/members.html:32 +#: ckan/templates/organization/bulk_process.html:65 +#: ckan/templates/organization/edit.html:3 +#: ckan/templates/organization/edit_base.html:22 +#: ckan/templates/organization/members.html:32 +#: ckan/templates/package/edit_base.html:11 +#: ckan/templates/package/resource_edit.html:3 +#: ckan/templates/package/resource_edit_base.html:12 +#: ckan/templates/package/snippets/resource_item.html:57 +msgid "Edit" +msgstr "" + +#: ckan/public/base/javascript/modules/table-toggle-more.js:9 +msgid "Show more" +msgstr "" + +#: ckan/public/base/javascript/modules/table-toggle-more.js:10 +msgid "Hide" +msgstr "" + +#: ckan/templates/error_document_template.html:3 +#, python-format +msgid "Error %(error_code)s" +msgstr "" + +#: ckan/templates/footer.html:9 +msgid "About {0}" +msgstr "" + +#: ckan/templates/footer.html:15 +msgid "CKAN API" +msgstr "" + +#: ckan/templates/footer.html:16 +msgid "Open Knowledge Foundation" +msgstr "" + +#: ckan/templates/footer.html:24 +msgid "" +"Powered by CKAN" +msgstr "" + +#: ckan/templates/header.html:12 +msgid "Sysadmin settings" +msgstr "" + +#: ckan/templates/header.html:19 +msgid "View profile" +msgstr "" + +#: ckan/templates/header.html:26 +#, python-format +msgid "Dashboard (%(num)d new item)" +msgid_plural "Dashboard (%(num)d new items)" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/header.html:29 ckan/templates/user/dashboard.html:6 +msgid "Dashboard" +msgstr "" + +#: ckan/templates/header.html:35 ckan/templates/user/dashboard.html:16 +msgid "Edit settings" +msgstr "" + +#: ckan/templates/header.html:37 +msgid "Settings" +msgstr "" + +#: ckan/templates/header.html:43 ckan/templates/header.html:45 +msgid "Log out" +msgstr "" + +#: ckan/templates/header.html:56 ckan/templates/user/logout_first.html:15 +msgid "Log in" +msgstr "" + +#: ckan/templates/header.html:58 ckan/templates/user/new.html:3 +msgid "Register" +msgstr "" + +#: ckan/templates/header.html:103 ckan/templates/group/read_base.html:17 +#: ckan/templates/group/snippets/info.html:36 +#: ckan/templates/organization/bulk_process.html:20 +#: ckan/templates/organization/edit_base.html:23 +#: ckan/templates/organization/read_base.html:17 +#: ckan/templates/package/base.html:7 ckan/templates/package/base.html:17 +#: ckan/templates/package/base.html:21 ckan/templates/package/search.html:4 +#: ckan/templates/package/search.html:7 +#: ckan/templates/package/snippets/new_package_breadcrumb.html:1 +#: ckan/templates/revision/diff.html:11 ckan/templates/revision/read.html:65 +#: ckan/templates/snippets/organization.html:59 +#: ckan/templates/snippets/context/group.html:17 +#: ckan/templates/snippets/context/user.html:19 +#: ckan/templates/user/read.html:5 ckan/templates/user/read_base.html:19 +#: ckan/templates/user/read_base.html:53 +msgid "Datasets" +msgstr "" + +#: ckan/templates/header.html:116 +msgid "Search Datasets" +msgstr "" + +#: ckan/templates/header.html:117 ckan/templates/home/snippets/search.html:11 +#: ckan/templates/snippets/simple_search.html:5 +#: ckan/templates/user/snippets/user_search.html:6 +msgid "Search" +msgstr "" + +#: ckan/templates/page.html:6 +msgid "Skip to content" +msgstr "" + +#: ckan/templates/activity_streams/activity_stream_items.html:9 +msgid "Load less" +msgstr "" + +#: ckan/templates/activity_streams/activity_stream_items.html:17 +msgid "Load more" +msgstr "" + +#: ckan/templates/activity_streams/activity_stream_items.html:23 +msgid "No activities are within this activity stream" +msgstr "" + +#: ckan/templates/admin/base.html:3 +msgid "Administration" +msgstr "" + +#: ckan/templates/admin/base.html:8 +msgid "Sysadmins" +msgstr "" + +#: ckan/templates/admin/base.html:9 +msgid "Config" +msgstr "" + +#: ckan/templates/admin/base.html:10 ckan/templates/admin/trash.html:29 +msgid "Trash" +msgstr "" + +#: ckan/templates/admin/config.html:16 +#: ckan/templates/admin/confirm_reset.html:7 +msgid "Are you sure you want to reset the config?" +msgstr "" + +#: ckan/templates/admin/config.html:17 +msgid "Reset" +msgstr "" + +#: ckan/templates/admin/config.html:18 +msgid "Update Config" +msgstr "" + +#: ckan/templates/admin/config.html:27 +msgid "CKAN config options" +msgstr "" + +#: ckan/templates/admin/config.html:34 +#, python-format +msgid "" +"

Site Title: This is the title of this CKAN instance It " +"appears in various places throughout CKAN.

Style: " +"Choose from a list of simple variations of the main colour scheme to get a " +"very quick custom theme working.

Site Tag Logo: This" +" is the logo that appears in the header of all the CKAN instance " +"templates.

About: This text will appear on this CKAN" +" instances about page.

Intro " +"Text: This text will appear on this CKAN instances home page as a welcome to visitors.

" +"

Custom CSS: This is a block of CSS that appears in " +"<head> tag of every page. If you wish to customize the " +"templates more fully we recommend reading the documentation.

" +"

Homepage: This is for choosing a predefined layout for " +"the modules that appear on your homepage.

" +msgstr "" + +#: ckan/templates/admin/confirm_reset.html:3 +#: ckan/templates/admin/confirm_reset.html:10 +msgid "Confirm Reset" +msgstr "" + +#: ckan/templates/admin/index.html:15 +msgid "Administer CKAN" +msgstr "" + +#: ckan/templates/admin/index.html:20 +#, python-format +msgid "" +"

As a sysadmin user you have full control over this CKAN instance. " +"Proceed with care!

For guidance on using sysadmin features, see the " +"CKAN sysadmin guide

" +msgstr "" + +#: ckan/templates/admin/trash.html:20 +msgid "Purge" +msgstr "" + +#: ckan/templates/admin/trash.html:32 +msgid "

Purge deleted datasets forever and irreversibly.

" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:19 +msgid "CKAN Data API" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:23 +msgid "Access resource data via a web API with powerful query support" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:24 +msgid "" +" Further information in the main CKAN Data API and DataStore documentation.

" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:33 +msgid "Endpoints" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:37 +msgid "" +"The Data API can be accessed via the following actions of the CKAN action " +"API." +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:42 +msgid "Create" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:46 +msgid "Update / Insert" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:50 +msgid "Query" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:54 +msgid "Query (via SQL)" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:66 +msgid "Querying" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:70 +msgid "Query example (first 5 results)" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:75 +msgid "Query example (results containing 'jones')" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:81 +msgid "Query example (via SQL statement)" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:93 +msgid "Example: Javascript" +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:97 +msgid "A simple ajax (JSONP) request to the data API using jQuery." +msgstr "" + +#: ckan/templates/ajax_snippets/api_info.html:118 +msgid "Example: Python" +msgstr "" + +#: ckan/templates/dataviewer/snippets/data_preview.html:9 +msgid "This resource can not be previewed at the moment." +msgstr "" + +#: ckan/templates/dataviewer/snippets/data_preview.html:11 +#: ckan/templates/package/resource_read.html:118 +#: ckan/templates/package/snippets/resource_view.html:26 +msgid "Click here for more information." +msgstr "" + +#: ckan/templates/dataviewer/snippets/data_preview.html:18 +#: ckan/templates/package/snippets/resource_view.html:33 +msgid "Download resource" +msgstr "" + +#: ckan/templates/dataviewer/snippets/data_preview.html:23 +#: ckan/templates/package/snippets/resource_view.html:56 +#: ckanext/webpageview/theme/templates/webpage_view.html:2 +msgid "Your browser does not support iframes." +msgstr "" + +#: ckan/templates/dataviewer/snippets/no_preview.html:3 +msgid "No preview available." +msgstr "" + +#: ckan/templates/dataviewer/snippets/no_preview.html:5 +msgid "More details..." +msgstr "" + +#: ckan/templates/dataviewer/snippets/no_preview.html:12 +#, python-format +msgid "No handler defined for data type: %(type)s." +msgstr "" + +#: ckan/templates/development/snippets/form.html:5 +msgid "Standard" +msgstr "" + +#: ckan/templates/development/snippets/form.html:5 +msgid "Standard Input" +msgstr "" + +#: ckan/templates/development/snippets/form.html:6 +msgid "Medium" +msgstr "" + +#: ckan/templates/development/snippets/form.html:6 +msgid "Medium Width Input" +msgstr "" + +#: ckan/templates/development/snippets/form.html:7 +msgid "Full" +msgstr "" + +#: ckan/templates/development/snippets/form.html:7 +msgid "Full Width Input" +msgstr "" + +#: ckan/templates/development/snippets/form.html:8 +msgid "Large" +msgstr "" + +#: ckan/templates/development/snippets/form.html:8 +msgid "Large Input" +msgstr "" + +#: ckan/templates/development/snippets/form.html:9 +msgid "Prepend" +msgstr "" + +#: ckan/templates/development/snippets/form.html:9 +msgid "Prepend Input" +msgstr "" + +#: ckan/templates/development/snippets/form.html:13 +msgid "Custom Field (empty)" +msgstr "" + +#: ckan/templates/development/snippets/form.html:19 +#: ckan/templates/group/snippets/group_form.html:35 +#: ckan/templates/group/snippets/group_form.html:48 +#: ckan/templates/organization/snippets/organization_form.html:35 +#: ckan/templates/organization/snippets/organization_form.html:48 +#: ckan/templates/snippets/custom_form_fields.html:20 +#: ckan/templates/snippets/custom_form_fields.html:37 +msgid "Custom Field" +msgstr "" + +#: ckan/templates/development/snippets/form.html:22 +msgid "Markdown" +msgstr "" + +#: ckan/templates/development/snippets/form.html:23 +msgid "Textarea" +msgstr "" + +#: ckan/templates/development/snippets/form.html:24 +msgid "Select" +msgstr "" + +#: ckan/templates/group/activity_stream.html:3 +#: ckan/templates/group/activity_stream.html:6 +#: ckan/templates/group/read_base.html:18 +#: ckan/templates/organization/activity_stream.html:3 +#: ckan/templates/organization/activity_stream.html:6 +#: ckan/templates/organization/read_base.html:18 +#: ckan/templates/package/activity.html:3 +#: ckan/templates/package/activity.html:6 +#: ckan/templates/package/read_base.html:21 +#: ckan/templates/user/activity_stream.html:3 +#: ckan/templates/user/activity_stream.html:6 +#: ckan/templates/user/read_base.html:20 +msgid "Activity Stream" +msgstr "" + +#: ckan/templates/group/admins.html:3 ckan/templates/group/admins.html:6 +#: ckan/templates/organization/admins.html:3 +#: ckan/templates/organization/admins.html:6 +msgid "Administrators" +msgstr "" + +#: ckan/templates/group/base_form_page.html:7 +msgid "Add a Group" +msgstr "" + +#: ckan/templates/group/base_form_page.html:11 +msgid "Group Form" +msgstr "" + +#: ckan/templates/group/confirm_delete.html:3 +#: ckan/templates/group/confirm_delete.html:15 +#: ckan/templates/group/confirm_delete_member.html:3 +#: ckan/templates/group/confirm_delete_member.html:16 +#: ckan/templates/organization/confirm_delete.html:3 +#: ckan/templates/organization/confirm_delete.html:15 +#: ckan/templates/organization/confirm_delete_member.html:3 +#: ckan/templates/organization/confirm_delete_member.html:16 +#: ckan/templates/package/confirm_delete.html:3 +#: ckan/templates/package/confirm_delete.html:15 +#: ckan/templates/package/confirm_delete_resource.html:3 +#: ckan/templates/package/confirm_delete_resource.html:15 +msgid "Confirm Delete" +msgstr "" + +#: ckan/templates/group/confirm_delete.html:11 +msgid "Are you sure you want to delete group - {name}?" +msgstr "" + +#: ckan/templates/group/confirm_delete_member.html:11 +#: ckan/templates/organization/confirm_delete_member.html:11 +msgid "Are you sure you want to delete member - {name}?" +msgstr "" + +#: ckan/templates/group/edit.html:7 ckan/templates/group/edit_base.html:3 +#: ckan/templates/group/edit_base.html:11 +#: ckan/templates/group/read_base.html:12 +#: ckan/templates/organization/edit_base.html:11 +#: ckan/templates/organization/read_base.html:12 +#: ckan/templates/package/read_base.html:14 +#: ckan/templates/package/resource_read.html:31 +#: ckan/templates/user/edit.html:8 ckan/templates/user/edit_base.html:3 +#: ckan/templates/user/read_base.html:14 +msgid "Manage" +msgstr "" + +#: ckan/templates/group/edit.html:12 +msgid "Edit Group" +msgstr "" + +#: ckan/templates/group/edit_base.html:21 ckan/templates/group/members.html:3 +#: ckan/templates/organization/edit_base.html:24 +#: ckan/templates/organization/members.html:3 +msgid "Members" +msgstr "" + +#: ckan/templates/group/followers.html:3 ckan/templates/group/followers.html:6 +#: ckan/templates/group/snippets/info.html:32 +#: ckan/templates/package/followers.html:3 +#: ckan/templates/package/followers.html:6 +#: ckan/templates/package/snippets/info.html:23 +#: ckan/templates/snippets/organization.html:55 +#: ckan/templates/snippets/context/group.html:13 +#: ckan/templates/snippets/context/user.html:15 +#: ckan/templates/user/followers.html:3 ckan/templates/user/followers.html:7 +#: ckan/templates/user/read_base.html:49 +#: ckanext/example_theme/v18_snippet_api/templates/ajax_snippets/example_theme_popover.html:12 +msgid "Followers" +msgstr "" + +#: ckan/templates/group/history.html:3 ckan/templates/group/history.html:6 +#: ckan/templates/package/history.html:3 ckan/templates/package/history.html:6 +msgid "History" +msgstr "" + +#: ckan/templates/group/index.html:13 +#: ckan/templates/user/dashboard_groups.html:7 +msgid "Add Group" +msgstr "" + +#: ckan/templates/group/index.html:20 +msgid "Search groups..." +msgstr "" + +#: ckan/templates/group/index.html:20 ckan/templates/group/read.html:16 +#: ckan/templates/organization/bulk_process.html:97 +#: ckan/templates/organization/read.html:20 +#: ckan/templates/package/search.html:30 +#: ckan/templates/snippets/search_form.html:4 +#: ckan/templates/snippets/simple_search.html:10 +#: ckan/templates/snippets/sort_by.html:15 +#: ckanext/example_idatasetform/templates/package/search.html:13 +msgid "Name Ascending" +msgstr "" + +#: ckan/templates/group/index.html:20 ckan/templates/group/read.html:17 +#: ckan/templates/organization/bulk_process.html:98 +#: ckan/templates/organization/read.html:21 +#: ckan/templates/package/search.html:31 +#: ckan/templates/snippets/search_form.html:4 +#: ckan/templates/snippets/simple_search.html:10 +#: ckan/templates/snippets/sort_by.html:16 +#: ckanext/example_idatasetform/templates/package/search.html:14 +msgid "Name Descending" +msgstr "" + +#: ckan/templates/group/index.html:29 +msgid "There are currently no groups for this site" +msgstr "" + +#: ckan/templates/group/index.html:31 +#: ckan/templates/organization/index.html:31 +msgid "How about creating one?" +msgstr "" + +#: ckan/templates/group/member_new.html:8 +#: ckan/templates/organization/member_new.html:10 +msgid "Back to all members" +msgstr "" + +#: ckan/templates/group/member_new.html:10 +#: ckan/templates/organization/member_new.html:7 +#: ckan/templates/organization/member_new.html:12 +msgid "Edit Member" +msgstr "" + +#: ckan/templates/group/member_new.html:10 +#: ckan/templates/group/member_new.html:65 ckan/templates/group/members.html:6 +#: ckan/templates/organization/member_new.html:7 +#: ckan/templates/organization/member_new.html:12 +#: ckan/templates/organization/member_new.html:66 +#: ckan/templates/organization/members.html:6 +msgid "Add Member" +msgstr "" + +#: ckan/templates/group/member_new.html:18 +#: ckan/templates/organization/member_new.html:20 +msgid "Existing User" +msgstr "" + +#: ckan/templates/group/member_new.html:21 +#: ckan/templates/organization/member_new.html:23 +msgid "If you wish to add an existing user, search for their username below." +msgstr "" + +#: ckan/templates/group/member_new.html:38 +#: ckan/templates/organization/member_new.html:40 +msgid "or" +msgstr "" + +#: ckan/templates/group/member_new.html:42 +#: ckan/templates/organization/member_new.html:44 +msgid "New User" +msgstr "" + +#: ckan/templates/group/member_new.html:45 +#: ckan/templates/organization/member_new.html:47 +msgid "If you wish to invite a new user, enter their email address." +msgstr "" + +#: ckan/templates/group/member_new.html:55 +#: ckan/templates/group/members.html:18 +#: ckan/templates/organization/member_new.html:56 +#: ckan/templates/organization/members.html:18 +msgid "Role" +msgstr "" + +#: ckan/templates/group/member_new.html:58 +#: ckan/templates/group/members.html:30 +#: ckan/templates/organization/member_new.html:59 +#: ckan/templates/organization/members.html:30 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: ckan/templates/group/member_new.html:59 +#: ckan/templates/group/members.html:35 +#: ckan/templates/group/snippets/group_form.html:61 +#: ckan/templates/organization/bulk_process.html:47 +#: ckan/templates/organization/member_new.html:60 +#: ckan/templates/organization/members.html:35 +#: ckan/templates/organization/snippets/organization_form.html:61 +#: ckan/templates/package/edit_view.html:19 +#: ckan/templates/package/snippets/package_form.html:40 +#: ckan/templates/package/snippets/resource_form.html:66 +#: ckan/templates/revision/read.html:24 +#: ckan/templates/user/edit_user_form.html:38 +msgid "Delete" +msgstr "" + +#: ckan/templates/group/member_new.html:61 +msgid "Save" +msgstr "" + +#: ckan/templates/group/member_new.html:78 +#: ckan/templates/organization/member_new.html:79 +msgid "What are roles?" +msgstr "" + +#: ckan/templates/group/member_new.html:81 +msgid "" +"

Admin: Can edit group information, as well as manage " +"organization members.

Member: Can add/remove " +"datasets from groups

" +msgstr "" + +#: ckan/templates/group/new.html:3 ckan/templates/group/new.html:5 +#: ckan/templates/group/new.html:7 +msgid "Create a Group" +msgstr "" + +#: ckan/templates/group/new_group_form.html:17 +msgid "Update Group" +msgstr "" + +#: ckan/templates/group/new_group_form.html:19 +msgid "Create Group" +msgstr "" + +#: ckan/templates/group/read.html:15 ckan/templates/organization/read.html:19 +#: ckan/templates/package/search.html:29 +#: ckan/templates/snippets/sort_by.html:14 +#: ckanext/example_idatasetform/templates/package/search.html:12 +msgid "Relevance" +msgstr "" + +#: ckan/templates/group/read.html:18 +#: ckan/templates/organization/bulk_process.html:99 +#: ckan/templates/organization/read.html:22 +#: ckan/templates/package/search.html:32 +#: ckan/templates/package/snippets/resource_form.html:51 +#: ckan/templates/snippets/sort_by.html:17 +#: ckanext/example_idatasetform/templates/package/search.html:15 +msgid "Last Modified" +msgstr "" + +#: ckan/templates/group/read.html:19 ckan/templates/organization/read.html:23 +#: ckan/templates/package/search.html:33 +#: ckan/templates/snippets/package_item.html:50 +#: ckan/templates/snippets/popular.html:3 +#: ckan/templates/snippets/sort_by.html:19 +#: ckanext/example_idatasetform/templates/package/search.html:18 +msgid "Popular" +msgstr "" + +#: ckan/templates/group/read.html:21 ckan/templates/organization/read.html:25 +#: ckan/templates/snippets/search_form.html:3 +msgid "Search datasets..." +msgstr "" + +#: ckan/templates/group/snippets/feeds.html:3 +msgid "Datasets in group: {group}" +msgstr "" + +#: ckan/templates/group/snippets/feeds.html:4 +#: ckan/templates/organization/snippets/feeds.html:4 +msgid "Recent Revision History" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:10 +#: ckan/templates/organization/snippets/organization_form.html:10 +#: ckan/templates/package/snippets/resource_form.html:28 +msgid "Name" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:10 +msgid "My Group" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:18 +msgid "my-group" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:20 +#: ckan/templates/organization/snippets/organization_form.html:20 +#: ckan/templates/package/snippets/package_basic_fields.html:19 +#: ckan/templates/package/snippets/resource_form.html:32 +#: ckan/templates/package/snippets/view_form.html:9 +msgid "Description" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:20 +msgid "A little information about my group..." +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:60 +msgid "Are you sure you want to delete this Group?" +msgstr "" + +#: ckan/templates/group/snippets/group_form.html:64 +msgid "Save Group" +msgstr "" + +#: ckan/templates/group/snippets/group_item.html:32 +#: ckan/templates/organization/snippets/organization_item.html:31 +#: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:23 +#: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:22 +msgid "{num} Dataset" +msgid_plural "{num} Datasets" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/group/snippets/group_item.html:34 +#: ckan/templates/organization/snippets/organization_item.html:33 +#: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:25 +#: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:24 +msgid "0 Datasets" +msgstr "" + +#: ckan/templates/group/snippets/group_item.html:38 +#: ckan/templates/group/snippets/group_item.html:39 +msgid "View {name}" +msgstr "" + +#: ckan/templates/group/snippets/group_item.html:43 +msgid "Remove dataset from this group" +msgstr "" + +#: ckan/templates/group/snippets/helper.html:4 +msgid "What are Groups?" +msgstr "" + +#: ckan/templates/group/snippets/helper.html:8 +msgid "" +" You can use CKAN Groups to create and manage collections of datasets. This " +"could be to catalogue datasets for a particular project or team, or on a " +"particular theme, or as a very simple way to help people find and search " +"your own published datasets. " +msgstr "" + +#: ckan/templates/group/snippets/history_revisions.html:10 +#: ckan/templates/package/snippets/history_revisions.html:10 +msgid "Compare" +msgstr "" + +#: ckan/templates/group/snippets/info.html:16 +#: ckan/templates/organization/bulk_process.html:72 +#: ckan/templates/package/read.html:21 +#: ckan/templates/package/snippets/package_basic_fields.html:112 +#: ckan/templates/snippets/organization.html:37 +#: ckan/templates/snippets/package_item.html:42 +msgid "Deleted" +msgstr "" + +#: ckan/templates/group/snippets/info.html:24 +#: ckan/templates/package/snippets/package_context.html:7 +#: ckan/templates/snippets/organization.html:45 +msgid "read more" +msgstr "" + +#: ckan/templates/group/snippets/revisions_table.html:7 +#: ckan/templates/package/snippets/revisions_table.html:7 +#: ckan/templates/revision/read.html:5 ckan/templates/revision/read.html:9 +#: ckan/templates/revision/read.html:39 +#: ckan/templates/revision/snippets/revisions_list.html:4 +msgid "Revision" +msgstr "" + +#: ckan/templates/group/snippets/revisions_table.html:8 +#: ckan/templates/package/snippets/revisions_table.html:8 +#: ckan/templates/revision/read.html:53 +#: ckan/templates/revision/snippets/revisions_list.html:5 +msgid "Timestamp" +msgstr "" + +#: ckan/templates/group/snippets/revisions_table.html:9 +#: ckan/templates/package/snippets/additional_info.html:25 +#: ckan/templates/package/snippets/additional_info.html:30 +#: ckan/templates/package/snippets/package_metadata_fields.html:14 +#: ckan/templates/package/snippets/revisions_table.html:9 +#: ckan/templates/revision/read.html:50 +#: ckan/templates/revision/snippets/revisions_list.html:6 +msgid "Author" +msgstr "" + +#: ckan/templates/group/snippets/revisions_table.html:10 +#: ckan/templates/package/snippets/revisions_table.html:10 +#: ckan/templates/revision/read.html:56 +#: ckan/templates/revision/snippets/revisions_list.html:8 +msgid "Log Message" +msgstr "" + +#: ckan/templates/home/index.html:4 +msgid "Welcome" +msgstr "" + +#: ckan/templates/home/snippets/about_text.html:1 +msgid "" +"

CKAN is the world’s leading open-source data portal platform.

" +"

CKAN is a complete out-of-the-box software solution that makes data " +"accessible and usable – by providing tools to streamline publishing, " +"sharing, finding and using data (including storage of data and provision of " +"robust data APIs). CKAN is aimed at data publishers (national and regional " +"governments, companies and organizations) wanting to make their data open " +"and available.

CKAN is used by governments and user groups worldwide " +"and powers a variety of official and community data portals including " +"portals for local, national and international government, such as the UK’s " +"data.gov.uk and the European Union’s publicdata.eu, the Brazilian dados.gov.br, Dutch and Netherland " +"government portals, as well as city and municipal sites in the US, UK, " +"Argentina, Finland and elsewhere.

CKAN: http://ckan.org/
CKAN Tour: http://ckan.org/tour/
Features " +"overview: http://ckan.org/features/

" +msgstr "" + +#: ckan/templates/home/snippets/promoted.html:8 +msgid "Welcome to CKAN" +msgstr "" + +#: ckan/templates/home/snippets/promoted.html:10 +msgid "" +"This is a nice introductory paragraph about CKAN or the site in general. We " +"don't have any copy to go here yet but soon we will " +msgstr "" + +#: ckan/templates/home/snippets/promoted.html:19 +msgid "This is a featured section" +msgstr "" + +#: ckan/templates/home/snippets/search.html:2 +msgid "E.g. environment" +msgstr "" + +#: ckan/templates/home/snippets/search.html:6 +msgid "Search data" +msgstr "" + +#: ckan/templates/home/snippets/search.html:16 +msgid "Popular tags" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:5 +msgid "{0} statistics" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:11 +msgid "dataset" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:11 +msgid "datasets" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:17 +msgid "organization" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:17 +msgid "organizations" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:23 +msgid "group" +msgstr "" + +#: ckan/templates/home/snippets/stats.html:23 +msgid "groups" +msgstr "" + +#: ckan/templates/macros/form.html:126 +#, python-format +msgid "" +"You can use Markdown formatting here" +msgstr "" + +#: ckan/templates/macros/form.html:265 +msgid "This field is required" +msgstr "" + +#: ckan/templates/macros/form.html:265 +msgid "Custom" +msgstr "" + +#: ckan/templates/macros/form.html:290 +msgid "The form contains invalid entries:" +msgstr "" + +#: ckan/templates/macros/form.html:395 +msgid "Required field" +msgstr "" + +#: ckan/templates/macros/form.html:410 +msgid "http://example.com/my-image.jpg" +msgstr "" + +#: ckan/templates/macros/form.html:411 +msgid "Image URL" +msgstr "" + +#: ckan/templates/macros/form.html:424 +msgid "Clear Upload" +msgstr "" + +#: ckan/templates/organization/base_form_page.html:5 +msgid "Organization Form" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:3 +#: ckan/templates/organization/bulk_process.html:11 +msgid "Edit datasets" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:6 +msgid "Add dataset" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:16 +msgid " found for \"{query}\"" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:18 +msgid "Sorry no datasets found for \"{query}\"" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:37 +msgid "Make public" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:41 +msgid "Make private" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:70 +#: ckan/templates/package/read.html:18 +#: ckan/templates/snippets/package_item.html:40 +msgid "Draft" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:75 +#: ckan/templates/package/read.html:11 +#: ckan/templates/package/snippets/package_basic_fields.html:92 +#: ckan/templates/snippets/package_item.html:31 +#: ckan/templates/snippets/private.html:2 +#: ckan/templates/user/read_base.html:82 ckan/templates/user/read_base.html:96 +msgid "Private" +msgstr "" + +#: ckan/templates/organization/bulk_process.html:88 +msgid "This organization has no datasets associated to it" +msgstr "" + +#: ckan/templates/organization/confirm_delete.html:11 +msgid "Are you sure you want to delete organization - {name}?" +msgstr "" + +#: ckan/templates/organization/edit.html:6 +#: ckan/templates/organization/snippets/info.html:13 +#: ckan/templates/organization/snippets/info.html:16 +msgid "Edit Organization" +msgstr "" + +#: ckan/templates/organization/index.html:13 +#: ckan/templates/user/dashboard_organizations.html:7 +msgid "Add Organization" +msgstr "" + +#: ckan/templates/organization/index.html:20 +msgid "Search organizations..." +msgstr "" + +#: ckan/templates/organization/index.html:29 +msgid "There are currently no organizations for this site" +msgstr "" + +#: ckan/templates/organization/member_new.html:32 +#: ckan/templates/user/edit_user_form.html:8 +#: ckan/templates/user/logout_first.html:11 +#: ckan/templates/user/new_user_form.html:5 +#: ckan/templates/user/read_base.html:76 +#: ckan/templates/user/request_reset.html:16 +#: ckan/templates/user/snippets/login_form.html:20 +msgid "Username" +msgstr "" + +#: ckan/templates/organization/member_new.html:50 +msgid "Email address" +msgstr "" + +#: ckan/templates/organization/member_new.html:62 +msgid "Update Member" +msgstr "" + +#: ckan/templates/organization/member_new.html:82 +msgid "" +"

Admin: Can add/edit and delete datasets, as well as " +"manage organization members.

Editor: Can add and " +"edit datasets, but not manage organization members.

" +"

Member: Can view the organization's private datasets, " +"but not add new datasets.

" +msgstr "" + +#: ckan/templates/organization/new.html:3 +#: ckan/templates/organization/new.html:5 +#: ckan/templates/organization/new.html:7 +#: ckan/templates/organization/new.html:12 +msgid "Create an Organization" +msgstr "" + +#: ckan/templates/organization/new_organization_form.html:17 +msgid "Update Organization" +msgstr "" + +#: ckan/templates/organization/new_organization_form.html:19 +msgid "Create Organization" +msgstr "" + +#: ckan/templates/organization/read.html:5 +#: ckan/templates/package/search.html:16 +#: ckan/templates/user/dashboard_datasets.html:7 +msgid "Add Dataset" +msgstr "" + +#: ckan/templates/organization/snippets/feeds.html:3 +msgid "Datasets in organization: {group}" +msgstr "" + +#: ckan/templates/organization/snippets/help.html:4 +#: ckan/templates/organization/snippets/helper.html:4 +msgid "What are Organizations?" +msgstr "" + +#: ckan/templates/organization/snippets/help.html:7 +msgid "" +"

Organizations act like publishing departments for datasets (for example," +" the Department of Health). This means that datasets can be published by and" +" belong to a department instead of an individual user.

Within " +"organizations, admins can assign roles and authorise its members, giving " +"individual users the right to publish datasets from that particular " +"organisation (e.g. Office of National Statistics).

" +msgstr "" + +#: ckan/templates/organization/snippets/helper.html:8 +msgid "" +" CKAN Organizations are used to create, manage and publish collections of " +"datasets. Users can have different roles within an Organization, depending " +"on their level of authorisation to create, edit and publish. " +msgstr "" + +#: ckan/templates/organization/snippets/organization_form.html:10 +msgid "My Organization" +msgstr "" + +#: ckan/templates/organization/snippets/organization_form.html:18 +msgid "my-organization" +msgstr "" + +#: ckan/templates/organization/snippets/organization_form.html:20 +msgid "A little information about my organization..." +msgstr "" + +#: ckan/templates/organization/snippets/organization_form.html:60 +msgid "" +"Are you sure you want to delete this Organization? This will delete all the " +"public and private datasets belonging to this organization." +msgstr "" + +#: ckan/templates/organization/snippets/organization_form.html:64 +msgid "Save Organization" +msgstr "" + +#: ckan/templates/organization/snippets/organization_item.html:37 +#: ckan/templates/organization/snippets/organization_item.html:38 +msgid "View {organization_name}" +msgstr "" + +#: ckan/templates/package/base.html:22 ckan/templates/package/new.html:3 +#: ckan/templates/package/snippets/new_package_breadcrumb.html:2 +msgid "Create Dataset" +msgstr "" + +#: ckan/templates/package/base_form_page.html:22 +msgid "What are datasets?" +msgstr "" + +#: ckan/templates/package/base_form_page.html:25 +msgid "" +" A CKAN Dataset is a collection of data resources (such as files), together " +"with a description and other information, at a fixed URL. Datasets are what " +"users see when searching for data. " +msgstr "" + +#: ckan/templates/package/confirm_delete.html:11 +msgid "Are you sure you want to delete dataset - {name}?" +msgstr "" + +#: ckan/templates/package/confirm_delete_resource.html:11 +msgid "Are you sure you want to delete resource - {name}?" +msgstr "" + +#: ckan/templates/package/edit_base.html:16 +msgid "View dataset" +msgstr "" + +#: ckan/templates/package/edit_base.html:20 +msgid "Edit metadata" +msgstr "" + +#: ckan/templates/package/edit_view.html:3 +#: ckan/templates/package/edit_view.html:4 +#: ckan/templates/package/edit_view.html:8 +#: ckan/templates/package/edit_view.html:12 +msgid "Edit view" +msgstr "" + +#: ckan/templates/package/edit_view.html:20 +#: ckan/templates/package/new_view.html:28 +#: ckan/templates/package/snippets/resource_item.html:33 +#: ckan/templates/snippets/datapreview_embed_dialog.html:16 +msgid "Preview" +msgstr "" + +#: ckan/templates/package/edit_view.html:21 +msgid "Update" +msgstr "" + +#: ckan/templates/package/group_list.html:14 +msgid "Associate this group with this dataset" +msgstr "" + +#: ckan/templates/package/group_list.html:14 +msgid "Add to group" +msgstr "" + +#: ckan/templates/package/group_list.html:23 +msgid "There are no groups associated with this dataset" +msgstr "" + +#: ckan/templates/package/new_package_form.html:15 +msgid "Update Dataset" +msgstr "" + +#: ckan/templates/package/new_resource.html:5 +msgid "Add data to the dataset" +msgstr "" + +#: ckan/templates/package/new_resource.html:11 +#: ckan/templates/package/new_resource_not_draft.html:8 +msgid "Add New Resource" +msgstr "" + +#: ckan/templates/package/new_resource_not_draft.html:3 +#: ckan/templates/package/new_resource_not_draft.html:4 +msgid "Add resource" +msgstr "" + +#: ckan/templates/package/new_resource_not_draft.html:16 +msgid "New resource" +msgstr "" + +#: ckan/templates/package/new_view.html:3 +#: ckan/templates/package/new_view.html:4 +#: ckan/templates/package/new_view.html:8 +#: ckan/templates/package/new_view.html:12 +msgid "Add view" +msgstr "" + +#: ckan/templates/package/new_view.html:19 +msgid "" +" Data Explorer views may be slow and unreliable unless the DataStore " +"extension is enabled. For more information, please see the Data Explorer " +"documentation. " +msgstr "" + +#: ckan/templates/package/new_view.html:29 +#: ckan/templates/package/snippets/resource_form.html:82 +msgid "Add" +msgstr "" + +#: ckan/templates/package/read_base.html:32 +#, python-format +msgid "" +"This is an old revision of this dataset, as edited at %(timestamp)s. It may " +"differ significantly from the current revision." +msgstr "" + +#: ckan/templates/package/related_list.html:7 +msgid "Related Media for {dataset}" +msgstr "" + +#: ckan/templates/package/related_list.html:12 +msgid "No related items" +msgstr "" + +#: ckan/templates/package/related_list.html:17 +msgid "Add Related Item" +msgstr "" + +#: ckan/templates/package/resource_data.html:12 +msgid "Upload to DataStore" +msgstr "" + +#: ckan/templates/package/resource_data.html:19 +msgid "Upload error:" +msgstr "" + +#: ckan/templates/package/resource_data.html:25 +#: ckan/templates/package/resource_data.html:27 +msgid "Error:" +msgstr "" + +#: ckan/templates/package/resource_data.html:36 +msgid "Error traceback:" +msgstr "" + +#: ckan/templates/package/resource_data.html:48 +msgid "Status" +msgstr "" + +#: ckan/templates/package/resource_data.html:52 +#: ckan/templates/package/resource_read.html:157 +msgid "Last updated" +msgstr "" + +#: ckan/templates/package/resource_data.html:56 +msgid "Never" +msgstr "" + +#: ckan/templates/package/resource_data.html:62 +msgid "Upload Log" +msgstr "" + +#: ckan/templates/package/resource_data.html:74 +msgid "Details" +msgstr "" + +#: ckan/templates/package/resource_data.html:81 +msgid "End of log" +msgstr "" + +#: ckan/templates/package/resource_edit_base.html:17 +msgid "All resources" +msgstr "" + +#: ckan/templates/package/resource_edit_base.html:19 +msgid "View resource" +msgstr "" + +#: ckan/templates/package/resource_edit_base.html:24 +#: ckan/templates/package/resource_edit_base.html:32 +msgid "Edit resource" +msgstr "" + +#: ckan/templates/package/resource_edit_base.html:26 +msgid "DataStore" +msgstr "" + +#: ckan/templates/package/resource_edit_base.html:28 +msgid "Views" +msgstr "" + +#: ckan/templates/package/resource_read.html:39 +msgid "API Endpoint" +msgstr "" + +#: ckan/templates/package/resource_read.html:41 +#: ckan/templates/package/snippets/resource_item.html:48 +msgid "Go to resource" +msgstr "" + +#: ckan/templates/package/resource_read.html:43 +#: ckan/templates/package/snippets/resource_item.html:45 +msgid "Download" +msgstr "" + +#: ckan/templates/package/resource_read.html:59 +#: ckan/templates/package/resource_read.html:61 +msgid "URL:" +msgstr "" + +#: ckan/templates/package/resource_read.html:69 +msgid "From the dataset abstract" +msgstr "" + +#: ckan/templates/package/resource_read.html:71 +#, python-format +msgid "Source: %(dataset)s" +msgstr "" + +#: ckan/templates/package/resource_read.html:112 +msgid "There are no views created for this resource yet." +msgstr "" + +#: ckan/templates/package/resource_read.html:116 +msgid "Not seeing the views you were expecting?" +msgstr "" + +#: ckan/templates/package/resource_read.html:121 +msgid "Here are some reasons you may not be seeing expected views:" +msgstr "" + +#: ckan/templates/package/resource_read.html:123 +msgid "No view has been created that is suitable for this resource" +msgstr "" + +#: ckan/templates/package/resource_read.html:124 +msgid "The site administrators may not have enabled the relevant view plugins" +msgstr "" + +#: ckan/templates/package/resource_read.html:125 +msgid "" +"If a view requires the DataStore, the DataStore plugin may not be enabled, " +"or the data may not have been pushed to the DataStore, or the DataStore " +"hasn't finished processing the data yet" +msgstr "" + +#: ckan/templates/package/resource_read.html:147 +msgid "Additional Information" +msgstr "" + +#: ckan/templates/package/resource_read.html:151 +#: ckan/templates/package/snippets/additional_info.html:6 +#: ckan/templates/revision/diff.html:43 +#: ckan/templates/snippets/additional_info.html:11 +msgid "Field" +msgstr "" + +#: ckan/templates/package/resource_read.html:152 +#: ckan/templates/package/snippets/additional_info.html:7 +#: ckan/templates/snippets/additional_info.html:12 +msgid "Value" +msgstr "" + +#: ckan/templates/package/resource_read.html:158 +#: ckan/templates/package/resource_read.html:162 +#: ckan/templates/package/resource_read.html:166 +msgid "unknown" +msgstr "" + +#: ckan/templates/package/resource_read.html:161 +#: ckan/templates/package/snippets/additional_info.html:70 +msgid "Created" +msgstr "" + +#: ckan/templates/package/resource_read.html:165 +#: ckan/templates/package/snippets/resource_form.html:37 +#: ckan/templates/package/snippets/resource_info.html:16 +msgid "Format" +msgstr "" + +#: ckan/templates/package/resource_read.html:169 +#: ckan/templates/package/snippets/package_basic_fields.html:30 +#: ckan/templates/snippets/license.html:21 +msgid "License" +msgstr "" + +#: ckan/templates/package/resource_views.html:10 +msgid "New view" +msgstr "" + +#: ckan/templates/package/resource_views.html:28 +msgid "This resource has no views" +msgstr "" + +#: ckan/templates/package/resources.html:8 +msgid "Add new resource" +msgstr "" + +#: ckan/templates/package/resources.html:19 +#: ckan/templates/package/snippets/resources_list.html:25 +#, python-format +msgid "" +"

This dataset has no data, why not " +"add some?

" +msgstr "" + +#: ckan/templates/package/search.html:52 +msgid "API" +msgstr "" + +#: ckan/templates/package/search.html:53 +msgid "API Docs" +msgstr "" + +#: ckan/templates/package/search.html:55 +msgid "full {format} dump" +msgstr "" + +#: ckan/templates/package/search.html:56 +#, python-format +msgid "" +" You can also access this registry using the %(api_link)s (see " +"%(api_doc_link)s) or download a %(dump_link)s. " +msgstr "" + +#: ckan/templates/package/search.html:60 +#, python-format +msgid "" +" You can also access this registry using the %(api_link)s (see " +"%(api_doc_link)s). " +msgstr "" + +#: ckan/templates/package/view_edit_base.html:9 +msgid "All views" +msgstr "" + +#: ckan/templates/package/view_edit_base.html:12 +msgid "View view" +msgstr "" + +#: ckan/templates/package/view_edit_base.html:37 +msgid "View preview" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:2 +#: ckan/templates/snippets/additional_info.html:7 +msgid "Additional Info" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:14 +#: ckan/templates/package/snippets/package_metadata_fields.html:6 +msgid "Source" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:37 +#: ckan/templates/package/snippets/additional_info.html:42 +#: ckan/templates/package/snippets/package_metadata_fields.html:20 +msgid "Maintainer" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:49 +#: ckan/templates/package/snippets/package_metadata_fields.html:10 +msgid "Version" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:56 +#: ckan/templates/package/snippets/package_basic_fields.html:108 +#: ckan/templates/user/read_base.html:91 +msgid "State" +msgstr "" + +#: ckan/templates/package/snippets/additional_info.html:62 +msgid "Last Updated" +msgstr "" + +#: ckan/templates/package/snippets/data_api_button.html:10 +msgid "Data API" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:4 +#: ckan/templates/package/snippets/view_form.html:8 +msgid "Title" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:4 +msgid "eg. A descriptive title" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:13 +msgid "eg. my-dataset" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:19 +msgid "eg. Some useful notes about the data" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:24 +msgid "eg. economy, mental health, government" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:41 +msgid "" +" License definitions and additional information can be found at opendefinition.org " +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:70 +#: ckan/templates/snippets/organization.html:23 +msgid "Organization" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:74 +msgid "No organization" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:89 +msgid "Visibility" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:92 +msgid "Public" +msgstr "" + +#: ckan/templates/package/snippets/package_basic_fields.html:111 +msgid "Active" +msgstr "" + +#: ckan/templates/package/snippets/package_form.html:28 +msgid "" +"The data license you select above only applies to the contents of any" +" resource files that you add to this dataset. By submitting this form, you " +"agree to release the metadata values that you enter into the form " +"under the Open " +"Database License." +msgstr "" + +#: ckan/templates/package/snippets/package_form.html:39 +msgid "Are you sure you want to delete this dataset?" +msgstr "" + +#: ckan/templates/package/snippets/package_form.html:44 +msgid "Next: Add Data" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:6 +msgid "http://example.com/dataset.json" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:10 +msgid "1.0" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:14 +#: ckan/templates/package/snippets/package_metadata_fields.html:20 +#: ckan/templates/user/new_user_form.html:6 +msgid "Joe Bloggs" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:16 +msgid "Author Email" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:16 +#: ckan/templates/package/snippets/package_metadata_fields.html:22 +#: ckan/templates/user/new_user_form.html:7 +msgid "joe@example.com" +msgstr "" + +#: ckan/templates/package/snippets/package_metadata_fields.html:22 +msgid "Maintainer Email" +msgstr "" + +#: ckan/templates/package/snippets/resource_edit_form.html:12 +msgid "Update Resource" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:24 +msgid "File" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:28 +msgid "eg. January 2011 Gold Prices" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:32 +msgid "Some useful notes about the data" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:37 +msgid "eg. CSV, XML or JSON" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:40 +msgid "This will be guessed automatically. Leave blank if you wish" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:51 +msgid "eg. 2012-06-05" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:53 +msgid "File Size" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:53 +msgid "eg. 1024" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:55 +#: ckan/templates/package/snippets/resource_form.html:57 +msgid "MIME Type" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:55 +#: ckan/templates/package/snippets/resource_form.html:57 +msgid "eg. application/json" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:65 +msgid "Are you sure you want to delete this resource?" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:72 +msgid "Previous" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:75 +msgid "Save & add another" +msgstr "" + +#: ckan/templates/package/snippets/resource_form.html:78 +msgid "Finish" +msgstr "" + +#: ckan/templates/package/snippets/resource_help.html:2 +msgid "What's a resource?" +msgstr "" + +#: ckan/templates/package/snippets/resource_help.html:4 +msgid "A resource can be any file or link to a file containing useful data." +msgstr "" + +#: ckan/templates/package/snippets/resource_item.html:24 +msgid "Explore" +msgstr "" + +#: ckan/templates/package/snippets/resource_item.html:36 +msgid "More information" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:11 +msgid "Embed" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:24 +msgid "This resource view is not available at the moment." +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:63 +msgid "Embed resource view" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:66 +msgid "" +"You can copy and paste the embed code into a CMS or blog software that " +"supports raw HTML" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:69 +msgid "Width" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:72 +msgid "Height" +msgstr "" + +#: ckan/templates/package/snippets/resource_view.html:75 +msgid "Code" +msgstr "" + +#: ckan/templates/package/snippets/resource_views_list.html:8 +msgid "Resource Preview" +msgstr "" + +#: ckan/templates/package/snippets/resources_list.html:13 +msgid "Data and Resources" +msgstr "" + +#: ckan/templates/package/snippets/resources_list.html:29 +msgid "This dataset has no data" +msgstr "" + +#: ckan/templates/package/snippets/revisions_table.html:24 +#, python-format +msgid "Read dataset as of %s" +msgstr "" + +#: ckan/templates/package/snippets/stages.html:23 +#: ckan/templates/package/snippets/stages.html:25 +msgid "Create dataset" +msgstr "" + +#: ckan/templates/package/snippets/stages.html:30 +#: ckan/templates/package/snippets/stages.html:34 +#: ckan/templates/package/snippets/stages.html:36 +msgid "Add data" +msgstr "" + +#: ckan/templates/package/snippets/view_form.html:8 +msgid "eg. My View" +msgstr "" + +#: ckan/templates/package/snippets/view_form.html:9 +msgid "eg. Information about my view" +msgstr "" + +#: ckan/templates/package/snippets/view_form_filters.html:16 +msgid "Add Filter" +msgstr "" + +#: ckan/templates/package/snippets/view_form_filters.html:28 +msgid "Remove Filter" +msgstr "" + +#: ckan/templates/package/snippets/view_form_filters.html:46 +msgid "Filters" +msgstr "" + +#: ckan/templates/package/snippets/view_help.html:2 +msgid "What's a view?" +msgstr "" + +#: ckan/templates/package/snippets/view_help.html:4 +msgid "A view is a representation of the data held against a resource" +msgstr "" + +#: ckan/templates/revision/diff.html:6 +msgid "Differences" +msgstr "" + +#: ckan/templates/revision/diff.html:13 ckan/templates/revision/diff.html:18 +#: ckan/templates/revision/diff.html:23 +msgid "Revision Differences" +msgstr "" + +#: ckan/templates/revision/diff.html:44 +msgid "Difference" +msgstr "" + +#: ckan/templates/revision/diff.html:54 +msgid "No Differences" +msgstr "" + +#: ckan/templates/revision/list.html:3 ckan/templates/revision/list.html:6 +#: ckan/templates/revision/list.html:10 +msgid "Revision History" +msgstr "" + +#: ckan/templates/revision/list.html:6 ckan/templates/revision/read.html:8 +msgid "Revisions" +msgstr "" + +#: ckan/templates/revision/read.html:30 +msgid "Undelete" +msgstr "" + +#: ckan/templates/revision/read.html:64 +msgid "Changes" +msgstr "" + +#: ckan/templates/revision/read.html:74 +msgid "Datasets' Tags" +msgstr "" + +#: ckan/templates/revision/snippets/revisions_list.html:7 +msgid "Entity" +msgstr "" + +#: ckan/templates/snippets/activity_item.html:3 +msgid "New activity item" +msgstr "" + +#: ckan/templates/snippets/datapreview_embed_dialog.html:4 +msgid "Embed Data Viewer" +msgstr "" + +#: ckan/templates/snippets/datapreview_embed_dialog.html:8 +msgid "Embed this view by copying this into your webpage:" +msgstr "" + +#: ckan/templates/snippets/datapreview_embed_dialog.html:10 +msgid "Choose width and height in pixels:" +msgstr "" + +#: ckan/templates/snippets/datapreview_embed_dialog.html:11 +msgid "Width:" +msgstr "" + +#: ckan/templates/snippets/datapreview_embed_dialog.html:13 +msgid "Height:" +msgstr "" + +#: ckan/templates/snippets/datapusher_status.html:8 +msgid "Datapusher status: {status}." +msgstr "" + +#: ckan/templates/snippets/disqus_trackback.html:2 +msgid "Trackback URL" +msgstr "" + +#: ckan/templates/snippets/facet_list.html:80 +msgid "Show More {facet_type}" +msgstr "" + +#: ckan/templates/snippets/facet_list.html:83 +msgid "Show Only Popular {facet_type}" +msgstr "" + +#: ckan/templates/snippets/facet_list.html:87 +msgid "There are no {facet_type} that match this search" +msgstr "" + +#: ckan/templates/snippets/home_breadcrumb_item.html:2 +msgid "Home" +msgstr "" + +#: ckan/templates/snippets/language_selector.html:4 +msgid "Language" +msgstr "" + +#: ckan/templates/snippets/language_selector.html:12 +#: ckan/templates/snippets/search_form.html:41 +#: ckan/templates/snippets/simple_search.html:15 +#: ckan/templates/snippets/sort_by.html:22 +msgid "Go" +msgstr "" + +#: ckan/templates/snippets/license.html:14 +msgid "No License Provided" +msgstr "" + +#: ckan/templates/snippets/license.html:28 +msgid "This dataset satisfies the Open Definition." +msgstr "" + +#: ckan/templates/snippets/organization.html:48 +msgid "There is no description for this organization" +msgstr "" + +#: ckan/templates/snippets/package_item.html:57 +msgid "This dataset has no description" +msgstr "" + +#: ckan/templates/snippets/related.html:15 +msgid "" +"No apps, ideas, news stories or images have been related to this dataset " +"yet." +msgstr "" + +#: ckan/templates/snippets/related.html:18 +msgid "Add Item" +msgstr "" + +#: ckan/templates/snippets/search_form.html:17 +msgid "Submit" +msgstr "" + +#: ckan/templates/snippets/search_form.html:32 +#: ckan/templates/snippets/simple_search.html:8 +#: ckan/templates/snippets/sort_by.html:12 +msgid "Order by" +msgstr "" + +#: ckan/templates/snippets/search_form.html:71 +msgid "Filter Results" +msgstr "" + +#: ckan/templates/snippets/search_form.html:78 +msgid "

Please try another search.

" +msgstr "" + +#: ckan/templates/snippets/search_form.html:84 +msgid "" +"

There was an error while searching. Please try " +"again.

" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:15 +msgid "{number} dataset found for \"{query}\"" +msgid_plural "{number} datasets found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:16 +msgid "No datasets found for \"{query}\"" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:17 +msgid "{number} dataset found" +msgid_plural "{number} datasets found" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:18 +msgid "No datasets found" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:21 +msgid "{number} group found for \"{query}\"" +msgid_plural "{number} groups found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:22 +msgid "No groups found for \"{query}\"" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:23 +msgid "{number} group found" +msgid_plural "{number} groups found" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:24 +msgid "No groups found" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:27 +msgid "{number} organization found for \"{query}\"" +msgid_plural "{number} organizations found for \"{query}\"" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:28 +msgid "No organizations found for \"{query}\"" +msgstr "" + +#: ckan/templates/snippets/search_result_text.html:29 +msgid "{number} organization found" +msgid_plural "{number} organizations found" +msgstr[0] "" +msgstr[1] "" + +#: ckan/templates/snippets/search_result_text.html:30 +msgid "No organizations found" +msgstr "" + +#: ckan/templates/snippets/social.html:5 +msgid "Social" +msgstr "" + +#: ckan/templates/snippets/subscribe.html:2 +msgid "Subscribe" +msgstr "" + +#: ckan/templates/snippets/subscribe.html:4 +#: ckan/templates/user/edit_user_form.html:12 +#: ckan/templates/user/new_user_form.html:7 +#: ckan/templates/user/read_base.html:82 +msgid "Email" +msgstr "" + +#: ckan/templates/snippets/subscribe.html:5 +msgid "RSS" +msgstr "" + +#: ckan/templates/snippets/context/user.html:23 +#: ckan/templates/user/read_base.html:57 +msgid "Edits" +msgstr "" + +#: ckan/templates/tag/index.html:33 ckan/templates/tag/index.html:34 +msgid "Search Tags" +msgstr "" + +#: ckan/templates/user/dashboard.html:19 ckan/templates/user/dashboard.html:37 +msgid "News feed" +msgstr "" + +#: ckan/templates/user/dashboard.html:20 +#: ckan/templates/user/dashboard_datasets.html:12 +msgid "My Datasets" +msgstr "" + +#: ckan/templates/user/dashboard.html:21 +#: ckan/templates/user/dashboard_organizations.html:12 +msgid "My Organizations" +msgstr "" + +#: ckan/templates/user/dashboard.html:22 +#: ckan/templates/user/dashboard_groups.html:12 +msgid "My Groups" +msgstr "" + +#: ckan/templates/user/dashboard.html:39 +msgid "Activity from items that I'm following" +msgstr "" + +#: ckan/templates/user/dashboard_datasets.html:17 +#: ckan/templates/user/read.html:14 +msgid "You haven't created any datasets." +msgstr "" + +#: ckan/templates/user/dashboard_datasets.html:19 +#: ckan/templates/user/dashboard_groups.html:22 +#: ckan/templates/user/dashboard_organizations.html:22 +#: ckan/templates/user/read.html:16 +msgid "Create one now?" +msgstr "" + +#: ckan/templates/user/dashboard_groups.html:20 +msgid "You are not a member of any groups." +msgstr "" + +#: ckan/templates/user/dashboard_organizations.html:20 +msgid "You are not a member of any organizations." +msgstr "" + +#: ckan/templates/user/edit.html:6 ckan/templates/user/edit_base.html:3 +#: ckan/templates/user/list.html:6 ckan/templates/user/list.html:13 +#: ckan/templates/user/read_base.html:5 ckan/templates/user/read_base.html:8 +#: ckan/templates/user/snippets/user_search.html:2 +msgid "Users" +msgstr "" + +#: ckan/templates/user/edit.html:17 +msgid "Account Info" +msgstr "" + +#: ckan/templates/user/edit.html:19 +msgid "" +" Your profile lets other CKAN users know about who you are and what you do. " +msgstr "" + +#: ckan/templates/user/edit_user_form.html:7 +msgid "Change details" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:10 +msgid "Full name" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:10 +msgid "eg. Joe Bloggs" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:12 +msgid "eg. joe@example.com" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:14 +msgid "A little information about yourself" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:17 +msgid "Subscribe to notification emails" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:26 +msgid "Change password" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:29 +#: ckan/templates/user/logout_first.html:12 +#: ckan/templates/user/new_user_form.html:8 +#: ckan/templates/user/perform_reset.html:20 +#: ckan/templates/user/snippets/login_form.html:22 +msgid "Password" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:31 +msgid "Confirm Password" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:37 +msgid "Are you sure you want to delete this User?" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:43 +msgid "Are you sure you want to regenerate the API key?" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:44 +msgid "Regenerate API Key" +msgstr "" + +#: ckan/templates/user/edit_user_form.html:48 +msgid "Update Profile" +msgstr "" + +#: ckan/templates/user/list.html:3 +#: ckan/templates/user/snippets/user_search.html:11 +msgid "All Users" +msgstr "" + +#: ckan/templates/user/login.html:3 ckan/templates/user/login.html:6 +#: ckan/templates/user/login.html:12 +#: ckan/templates/user/snippets/login_form.html:28 +msgid "Login" +msgstr "" + +#: ckan/templates/user/login.html:25 +msgid "Need an Account?" +msgstr "" + +#: ckan/templates/user/login.html:27 +msgid "Then sign right up, it only takes a minute." +msgstr "" + +#: ckan/templates/user/login.html:30 +msgid "Create an Account" +msgstr "" + +#: ckan/templates/user/login.html:42 +msgid "Forgotten your password?" +msgstr "" + +#: ckan/templates/user/login.html:44 +msgid "No problem, use our password recovery form to reset it." +msgstr "" + +#: ckan/templates/user/login.html:47 +msgid "Forgot your password?" +msgstr "" + +#: ckan/templates/user/logout.html:3 ckan/templates/user/logout.html:9 +msgid "Logged Out" +msgstr "" + +#: ckan/templates/user/logout.html:11 +msgid "You are now logged out." +msgstr "" + +#: ckan/templates/user/logout_first.html:9 +msgid "You're already logged in as {user}." +msgstr "" + +#: ckan/templates/user/logout_first.html:9 +msgid "Logout" +msgstr "" + +#: ckan/templates/user/logout_first.html:13 +#: ckan/templates/user/snippets/login_form.html:24 +msgid "Remember me" +msgstr "" + +#: ckan/templates/user/logout_first.html:22 +msgid "You're already logged in" +msgstr "" + +#: ckan/templates/user/logout_first.html:24 +msgid "You need to log out before you can log in with another account." +msgstr "" + +#: ckan/templates/user/logout_first.html:25 +msgid "Log out now" +msgstr "" + +#: ckan/templates/user/new.html:6 +msgid "Registration" +msgstr "" + +#: ckan/templates/user/new.html:14 +msgid "Register for an Account" +msgstr "" + +#: ckan/templates/user/new.html:26 +msgid "Why Sign Up?" +msgstr "" + +#: ckan/templates/user/new.html:28 +msgid "Create datasets, groups and other exciting things" +msgstr "" + +#: ckan/templates/user/new_user_form.html:5 +msgid "username" +msgstr "" + +#: ckan/templates/user/new_user_form.html:6 +msgid "Full Name" +msgstr "" + +#: ckan/templates/user/new_user_form.html:17 +msgid "Create Account" +msgstr "" + +#: ckan/templates/user/perform_reset.html:4 +#: ckan/templates/user/perform_reset.html:14 +msgid "Reset Your Password" +msgstr "" + +#: ckan/templates/user/perform_reset.html:7 +msgid "Password Reset" +msgstr "" + +#: ckan/templates/user/perform_reset.html:24 +msgid "Update Password" +msgstr "" + +#: ckan/templates/user/perform_reset.html:38 +#: ckan/templates/user/request_reset.html:32 +msgid "How does this work?" +msgstr "" + +#: ckan/templates/user/perform_reset.html:40 +msgid "Simply enter a new password and we'll update your account" +msgstr "" + +#: ckan/templates/user/read.html:21 +msgid "User hasn't created any datasets." +msgstr "" + +#: ckan/templates/user/read_base.html:39 +msgid "You have not provided a biography." +msgstr "" + +#: ckan/templates/user/read_base.html:41 +msgid "This user has no biography." +msgstr "" + +#: ckan/templates/user/read_base.html:73 +msgid "Open ID" +msgstr "" + +#: ckan/templates/user/read_base.html:82 ckan/templates/user/read_base.html:96 +msgid "This means only you can see this" +msgstr "" + +#: ckan/templates/user/read_base.html:87 +msgid "Member Since" +msgstr "" + +#: ckan/templates/user/read_base.html:96 +msgid "API Key" +msgstr "" + +#: ckan/templates/user/request_reset.html:6 +msgid "Password reset" +msgstr "" + +#: ckan/templates/user/request_reset.html:19 +msgid "Request reset" +msgstr "" + +#: ckan/templates/user/request_reset.html:34 +msgid "" +"Enter your username into the box and we will send you an email with a link " +"to enter a new password." +msgstr "" + +#: ckan/templates/user/snippets/followee_dropdown.html:14 +#: ckan/templates/user/snippets/followee_dropdown.html:15 +msgid "Activity from:" +msgstr "" + +#: ckan/templates/user/snippets/followee_dropdown.html:23 +msgid "Search list..." +msgstr "" + +#: ckan/templates/user/snippets/followee_dropdown.html:44 +msgid "You are not following anything" +msgstr "" + +#: ckan/templates/user/snippets/followers.html:9 +msgid "No followers" +msgstr "" + +#: ckan/templates/user/snippets/user_search.html:5 +msgid "Search Users" +msgstr "" + +#: ckanext/datapusher/helpers.py:19 +msgid "Complete" +msgstr "" + +#: ckanext/datapusher/helpers.py:20 +msgid "Pending" +msgstr "" + +#: ckanext/datapusher/helpers.py:21 +msgid "Submitting" +msgstr "" + +#: ckanext/datapusher/helpers.py:27 +msgid "Not Uploaded Yet" +msgstr "" + +#: ckanext/datastore/controller.py:31 +msgid "DataStore resource not found" +msgstr "" + +#: ckanext/datastore/db.py:663 +msgid "" +"The data was invalid (for example: a numeric value is out of range or was " +"inserted into a text field)." +msgstr "" + +#: ckanext/datastore/logic/action.py:215 ckanext/datastore/logic/action.py:255 +#: ckanext/datastore/logic/action.py:332 ckanext/datastore/logic/action.py:422 +#: ckanext/datastore/logic/action.py:504 ckanext/datastore/logic/action.py:530 +msgid "Resource \"{0}\" was not found." +msgstr "" + +#: ckanext/datastore/logic/auth.py:16 +msgid "User {0} not authorized to update resource {1}" +msgstr "" + +#: ckanext/example_iconfigurer/templates/admin/config.html:11 +msgid "Datasets per page" +msgstr "" + +#: ckanext/example_iconfigurer/templates/admin/config.html:13 +msgid "Test conf" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/search.html:16 +msgid "Custom Field Ascending" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/search.html:17 +msgid "Custom Field Descending" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/snippets/additional_info.html:6 +#: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 +#: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 +msgid "Custom Text" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 +msgid "custom text" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/snippets/package_metadata_fields.html:11 +msgid "Country Code" +msgstr "" + +#: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 +msgid "custom resource text" +msgstr "" + +#: ckanext/example_itranslation/templates/home/index.html:4 +msgid "This is an untranslated string" +msgstr "" + +#: ckanext/example_theme/v10_custom_snippet/templates/snippets/example_theme_most_popular_groups.html:20 +#: ckanext/example_theme/v11_HTML_and_CSS/templates/snippets/example_theme_most_popular_groups.html:19 +msgid "This group has no description" +msgstr "" + +#: ckanext/example_theme/v12_extra_public_dir/templates/home/snippets/promoted.html:4 +msgid "CKAN's data previewing tool has many powerful features" +msgstr "" + +#: ckanext/imageview/theme/templates/image_form.html:3 +msgid "Image url" +msgstr "" + +#: ckanext/imageview/theme/templates/image_form.html:3 +msgid "eg. http://example.com/image.jpg (if blank uses resource url)" +msgstr "" + +#: ckanext/reclineview/plugin.py:84 +msgid "Data Explorer" +msgstr "" + +#: ckanext/reclineview/plugin.py:111 +msgid "Table" +msgstr "" + +#: ckanext/reclineview/plugin.py:154 +msgid "Graph" +msgstr "" + +#: ckanext/reclineview/plugin.py:214 +msgid "Map" +msgstr "" + +#: ckanext/reclineview/theme/public/recline_view.js:34 +msgid "error loading view" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:3 +#: ckanext/reclineview/theme/templates/recline_map_form.html:3 +msgid "Row offset" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:3 +#: ckanext/reclineview/theme/templates/recline_map_form.html:3 +msgid "eg: 0" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:4 +#: ckanext/reclineview/theme/templates/recline_map_form.html:4 +msgid "Number of rows" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:4 +#: ckanext/reclineview/theme/templates/recline_map_form.html:4 +msgid "eg: 100" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:6 +msgid "Graph type" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:7 +msgid "Group (Axis 1)" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_graph_form.html:8 +msgid "Series (Axis 2)" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:6 +msgid "Field type" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:7 +msgid "Latitude field" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:8 +msgid "Longitude field" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:9 +msgid "GeoJSON field" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:10 +msgid "Auto zoom to features" +msgstr "" + +#: ckanext/reclineview/theme/templates/recline_map_form.html:11 +msgid "Cluster markers" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:10 +msgid "Total number of Datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:17 +#: ckanext/stats/templates/ckanext/stats/index.html:40 +msgid "Date" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:18 +msgid "Total datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:33 +#: ckanext/stats/templates/ckanext/stats/index.html:179 +msgid "Dataset Revisions per Week" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:41 +msgid "All dataset revisions" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:42 +msgid "New datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:58 +#: ckanext/stats/templates/ckanext/stats/index.html:180 +msgid "Top Rated Datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:64 +msgid "Average rating" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:65 +msgid "Number of ratings" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:79 +msgid "No ratings" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:84 +#: ckanext/stats/templates/ckanext/stats/index.html:181 +msgid "Most Edited Datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:90 +msgid "Number of edits" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:103 +msgid "No edited datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:108 +#: ckanext/stats/templates/ckanext/stats/index.html:182 +msgid "Largest Groups" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:114 +msgid "Number of datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:127 +msgid "No groups" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:132 +#: ckanext/stats/templates/ckanext/stats/index.html:183 +msgid "Top Tags" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:136 +msgid "Tag Name" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:137 +#: ckanext/stats/templates/ckanext/stats/index.html:157 +msgid "Number of Datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:152 +#: ckanext/stats/templates/ckanext/stats/index.html:184 +msgid "Users Creating Most Datasets" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:175 +msgid "Statistics Menu" +msgstr "" + +#: ckanext/stats/templates/ckanext/stats/index.html:178 +msgid "Total Number of Datasets" +msgstr "" + +#: ckanext/textview/plugin.py:65 ckanext/textview/plugin.py:67 +msgid "Text" +msgstr "" + +#: ckanext/textview/theme/public/text_view.js:5 +#, python-format +msgid "An error occurred: %(text)s %(error)s" +msgstr "" + +#: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 +msgid "Website" +msgstr "" + +#: ckanext/webpageview/theme/templates/webpage_form.html:3 +msgid "Web Page url" +msgstr "" + +#: ckanext/webpageview/theme/templates/webpage_form.html:3 +msgid "eg. http://example.com (if blank uses resource url)" +msgstr "" diff --git a/ckan/i18n/tr/LC_MESSAGES/ckan.mo b/ckan/i18n/tr/LC_MESSAGES/ckan.mo index 58bf39da768..3d5171965aa 100644 Binary files a/ckan/i18n/tr/LC_MESSAGES/ckan.mo and b/ckan/i18n/tr/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/uk_UA/LC_MESSAGES/ckan.mo b/ckan/i18n/uk_UA/LC_MESSAGES/ckan.mo index 7826ed7ff82..319ae499e3c 100644 Binary files a/ckan/i18n/uk_UA/LC_MESSAGES/ckan.mo and b/ckan/i18n/uk_UA/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/uk_UA/LC_MESSAGES/ckan.po b/ckan/i18n/uk_UA/LC_MESSAGES/ckan.po index 3597fa5d3ce..eba95d98a6e 100644 --- a/ckan/i18n/uk_UA/LC_MESSAGES/ckan.po +++ b/ckan/i18n/uk_UA/LC_MESSAGES/ckan.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-12-07 15:14+0000\n" +"PO-Revision-Date: 2016-01-03 01:34+0000\n" "Last-Translator: Zoriana Zaiats\n" "Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/okfn/ckan/language/uk_UA/)\n" "MIME-Version: 1.0\n" diff --git a/ckan/i18n/vi/LC_MESSAGES/ckan.mo b/ckan/i18n/vi/LC_MESSAGES/ckan.mo index 245503365e5..78223117222 100644 Binary files a/ckan/i18n/vi/LC_MESSAGES/ckan.mo and b/ckan/i18n/vi/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/zh_CN/LC_MESSAGES/ckan.mo b/ckan/i18n/zh_CN/LC_MESSAGES/ckan.mo index c82f4a7a9a2..9ea0d453dea 100644 Binary files a/ckan/i18n/zh_CN/LC_MESSAGES/ckan.mo and b/ckan/i18n/zh_CN/LC_MESSAGES/ckan.mo differ diff --git a/ckan/i18n/zh_CN/LC_MESSAGES/ckan.po b/ckan/i18n/zh_CN/LC_MESSAGES/ckan.po index 3978bf314a0..51d55adfbd8 100644 --- a/ckan/i18n/zh_CN/LC_MESSAGES/ckan.po +++ b/ckan/i18n/zh_CN/LC_MESSAGES/ckan.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the ckan project. # # Translators: +# bo wang , 2016 # okfncn , 2013 # Sean Hammond , 2013 # Xiaohong Zhang , 2013 @@ -11,8 +12,8 @@ msgstr "" "Project-Id-Version: CKAN\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2015-11-26 13:42+0000\n" -"PO-Revision-Date: 2015-11-26 14:23+0000\n" -"Last-Translator: dread \n" +"PO-Revision-Date: 2016-02-25 16:05+0000\n" +"Last-Translator: bo wang \n" "Language-Team: Chinese (China) (http://www.transifex.com/okfn/ckan/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -240,7 +241,7 @@ msgstr "未找到群组" #: ckan/controllers/feed.py:234 msgid "Organization not found" -msgstr "" +msgstr "未发现该组织名" #: ckan/controllers/group.py:138 ckan/controllers/group.py:609 msgid "Incorrect group type" @@ -344,7 +345,7 @@ msgstr "群组已经被删除." #: ckan/controllers/group.py:653 #, python-format msgid "%s has been deleted." -msgstr "" +msgstr "%s 已经被删除" #: ckan/controllers/group.py:729 #, python-format @@ -536,7 +537,7 @@ msgstr "没有权限读取数据集 %s" #: ckan/controllers/package.py:1133 ckan/controllers/package.py:1597 msgid "Resource view not found" -msgstr "" +msgstr "未找到资源视图" #: ckan/controllers/package.py:1165 ckan/controllers/package.py:1355 #: ckan/controllers/package.py:1437 ckan/controllers/package.py:1470 @@ -555,20 +556,20 @@ msgstr "没有可下载的内容" #: ckan/controllers/package.py:1505 msgid "Unauthorized to edit resource" -msgstr "" +msgstr "没有权限编辑该资源" #: ckan/controllers/package.py:1523 msgid "View not found" -msgstr "" +msgstr "未发现该视图" #: ckan/controllers/package.py:1525 #, python-format msgid "Unauthorized to view View %s" -msgstr "" +msgstr "未被授权查看视图 %s" #: ckan/controllers/package.py:1531 msgid "View Type Not found" -msgstr "" +msgstr "无法找到该视图类型" #: ckan/controllers/package.py:1591 msgid "Bad resource view data" @@ -577,7 +578,7 @@ msgstr "" #: ckan/controllers/package.py:1600 #, python-format msgid "Unauthorized to read resource view %s" -msgstr "" +msgstr "未被授权读取资源视图%s" #: ckan/controllers/package.py:1603 msgid "Resource view not supplied" @@ -672,15 +673,15 @@ msgstr "用户 %s没有编辑权限%s" #: ckan/controllers/user.py:354 msgid "Password entered was incorrect" -msgstr "" +msgstr "密码不正确" #: ckan/controllers/user.py:355 ckan/templates/user/edit_user_form.html:27 msgid "Old Password" -msgstr "" +msgstr "旧密码" #: ckan/controllers/user.py:355 msgid "incorrect password" -msgstr "" +msgstr "不正确的密码" #: ckan/controllers/user.py:396 msgid "Login failed. Bad username or password." @@ -755,7 +756,7 @@ msgstr "缺失值" #: ckan/controllers/util.py:21 msgid "Redirecting to external site is not allowed." -msgstr "" +msgstr "不允许重定向至其它站点。" #: ckan/lib/activity_streams.py:64 msgid "{actor} added the tag {tag} to the dataset {dataset}" @@ -958,7 +959,7 @@ msgstr[0] "{years}年前" #: ckan/lib/formatters.py:146 msgid "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" -msgstr "" +msgstr "{month} {day}, {year}, {hour:02}:{min:02} ({timezone})" #: ckan/lib/formatters.py:151 msgid "{month} {day}, {year}" @@ -1198,7 +1199,7 @@ msgstr "在 log_message 中不允许超链接" #: ckan/logic/validators.py:156 msgid "Dataset id already exists" -msgstr "" +msgstr "数据集id已经存在" #: ckan/logic/validators.py:197 ckan/logic/validators.py:288 msgid "Resource" @@ -1228,7 +1229,7 @@ msgstr "这个名称不可使用" #: ckan/logic/validators.py:361 #, python-format msgid "Must be at least %s characters long" -msgstr "" +msgstr "长度不得少于 %s 个字符" #: ckan/logic/validators.py:363 ckan/logic/validators.py:646 #, python-format @@ -1377,15 +1378,15 @@ msgstr "作为父结点,它将会在层次结构中创造环" #: ckan/logic/validators.py:815 msgid "\"filter_fields\" and \"filter_values\" should have the same length" -msgstr "" +msgstr "\"filter_fields\" 和 \"filter_values\" 长度应该相同" #: ckan/logic/validators.py:826 msgid "\"filter_fields\" is required when \"filter_values\" is filled" -msgstr "" +msgstr " \"filter_fields\" 与\"filter_values\"需被同时填写" #: ckan/logic/validators.py:829 msgid "\"filter_values\" is required when \"filter_fields\" is filled" -msgstr "" +msgstr " \"filter_fields\" 与\"filter_values\"需被同时填写" #: ckan/logic/validators.py:843 msgid "There is a schema field with the same name" @@ -1569,7 +1570,7 @@ msgstr "本资源中没发现组件,无法授权." #: ckan/logic/auth/create.py:92 #, python-format msgid "User %s not authorized to create resources on dataset %s" -msgstr "" +msgstr "用户 %s 未被授权在数据集 %s 上创建资源" #: ckan/logic/auth/create.py:124 #, python-format @@ -1681,7 +1682,7 @@ msgstr "用户 %s没有权限读取资源 %s" #: ckan/logic/auth/get.py:166 #, python-format msgid "User %s not authorized to read group %s" -msgstr "" +msgstr "用户 %s 未被授权读取组 %s" #: ckan/logic/auth/get.py:237 msgid "You must be logged in to access your dashboard." @@ -1736,7 +1737,7 @@ msgstr "用户 %s没有编辑用户%s的劝限" #: ckan/logic/auth/update.py:228 msgid "User {0} not authorized to update user {1}" -msgstr "" +msgstr "用户 {0} 未被授权 更新用户 {1}" #: ckan/logic/auth/update.py:236 #, python-format @@ -1763,7 +1764,7 @@ msgstr "需要有效的API Key编辑一個群組" #: ckan/model/license.py:220 msgid "License not specified" -msgstr "" +msgstr "未指定许可证" #: ckan/model/license.py:230 msgid "Open Data Commons Public Domain Dedication and License (PDDL)" @@ -2098,7 +2099,7 @@ msgstr "编辑设置" #: ckan/templates/header.html:37 msgid "Settings" -msgstr "" +msgstr "设置" #: ckan/templates/header.html:43 ckan/templates/header.html:45 msgid "Log out" @@ -2170,7 +2171,7 @@ msgstr "配置" #: ckan/templates/admin/base.html:10 ckan/templates/admin/trash.html:29 msgid "Trash" -msgstr "" +msgstr "垃圾桶" #: ckan/templates/admin/config.html:16 #: ckan/templates/admin/confirm_reset.html:7 @@ -2216,7 +2217,7 @@ msgstr "确认重设" #: ckan/templates/admin/index.html:15 msgid "Administer CKAN" -msgstr "" +msgstr "管理 CKAN" #: ckan/templates/admin/index.html:20 #, python-format @@ -2224,7 +2225,7 @@ msgid "" "

As a sysadmin user you have full control over this CKAN instance. " "Proceed with care!

For guidance on using sysadmin features, see the " "CKAN sysadmin guide

" -msgstr "" +msgstr "

作为系统管理员,您对该CKAN实例具有完全控制权。请谨慎操作!

\n

该链接可以引导您使用系统管理员特性 系统管理员引导

" #: ckan/templates/admin/trash.html:20 msgid "Purge" @@ -2844,15 +2845,15 @@ msgstr "这是一个特殊的区段" #: ckan/templates/home/snippets/search.html:2 msgid "E.g. environment" -msgstr "" +msgstr "比如 环境" #: ckan/templates/home/snippets/search.html:6 msgid "Search data" -msgstr "" +msgstr "检索数据" #: ckan/templates/home/snippets/search.html:16 msgid "Popular tags" -msgstr "" +msgstr "热门标签" #: ckan/templates/home/snippets/stats.html:5 msgid "{0} statistics" @@ -2888,7 +2889,7 @@ msgid "" "You can use Markdown formatting here" -msgstr "" +msgstr "在这里你可以使用Markdown 格式" #: ckan/templates/macros/form.html:265 msgid "This field is required" @@ -3001,7 +3002,7 @@ msgstr "用户名" #: ckan/templates/organization/member_new.html:50 msgid "Email address" -msgstr "" +msgstr "Email 地址" #: ckan/templates/organization/member_new.html:62 msgid "Update Member" @@ -3127,7 +3128,7 @@ msgstr "编辑元数据" #: ckan/templates/package/edit_view.html:8 #: ckan/templates/package/edit_view.html:12 msgid "Edit view" -msgstr "" +msgstr "编辑视图" #: ckan/templates/package/edit_view.html:20 #: ckan/templates/package/new_view.html:28 @@ -3179,7 +3180,7 @@ msgstr "新资源" #: ckan/templates/package/new_view.html:8 #: ckan/templates/package/new_view.html:12 msgid "Add view" -msgstr "" +msgstr "增加视图" #: ckan/templates/package/new_view.html:19 msgid "" @@ -3229,7 +3230,7 @@ msgstr "错误:" #: ckan/templates/package/resource_data.html:36 msgid "Error traceback:" -msgstr "" +msgstr "错误追踪:" #: ckan/templates/package/resource_data.html:48 msgid "Status" @@ -3275,7 +3276,7 @@ msgstr "数据仓库" #: ckan/templates/package/resource_edit_base.html:28 msgid "Views" -msgstr "" +msgstr "视图" #: ckan/templates/package/resource_read.html:39 msgid "API Endpoint" @@ -3307,23 +3308,23 @@ msgstr "来源: %(dataset)s" #: ckan/templates/package/resource_read.html:112 msgid "There are no views created for this resource yet." -msgstr "" +msgstr "该资源暂时没有视图" #: ckan/templates/package/resource_read.html:116 msgid "Not seeing the views you were expecting?" -msgstr "" +msgstr "没有看到期望的视图?" #: ckan/templates/package/resource_read.html:121 msgid "Here are some reasons you may not be seeing expected views:" -msgstr "" +msgstr "这里列出了一些您未找到期望视图的原因:" #: ckan/templates/package/resource_read.html:123 msgid "No view has been created that is suitable for this resource" -msgstr "" +msgstr "该资源没有已创建的视图" #: ckan/templates/package/resource_read.html:124 msgid "The site administrators may not have enabled the relevant view plugins" -msgstr "" +msgstr "该站点管理员并没有开启相应的视图插件" #: ckan/templates/package/resource_read.html:125 msgid "" @@ -3374,11 +3375,11 @@ msgstr "授权" #: ckan/templates/package/resource_views.html:10 msgid "New view" -msgstr "" +msgstr "新视图" #: ckan/templates/package/resource_views.html:28 msgid "This resource has no views" -msgstr "" +msgstr "该资源没有视图" #: ckan/templates/package/resources.html:8 msgid "Add new resource" @@ -3420,15 +3421,15 @@ msgstr "您也可以使用%(api_link)s (看 %(api_doc_link)s)注册." #: ckan/templates/package/view_edit_base.html:9 msgid "All views" -msgstr "" +msgstr "所有视图" #: ckan/templates/package/view_edit_base.html:12 msgid "View view" -msgstr "" +msgstr "查看视图" #: ckan/templates/package/view_edit_base.html:37 msgid "View preview" -msgstr "" +msgstr "视图预览" #: ckan/templates/package/snippets/additional_info.html:2 #: ckan/templates/snippets/additional_info.html:7 @@ -3459,7 +3460,7 @@ msgstr "状态" #: ckan/templates/package/snippets/additional_info.html:62 msgid "Last Updated" -msgstr "" +msgstr "最近更新" #: ckan/templates/package/snippets/data_api_button.html:10 msgid "Data API" @@ -3580,7 +3581,7 @@ msgstr "例如:CSV, XML, 或者 JSON" #: ckan/templates/package/snippets/resource_form.html:40 msgid "This will be guessed automatically. Leave blank if you wish" -msgstr "" +msgstr "该项可以自动填充,您可以留空" #: ckan/templates/package/snippets/resource_form.html:51 msgid "eg. 2012-06-05" @@ -3642,7 +3643,7 @@ msgstr "" #: ckan/templates/package/snippets/resource_view.html:24 msgid "This resource view is not available at the moment." -msgstr "" +msgstr "该资源暂时不可使用。" #: ckan/templates/package/snippets/resource_view.html:63 msgid "Embed resource view" @@ -3656,19 +3657,19 @@ msgstr "" #: ckan/templates/package/snippets/resource_view.html:69 msgid "Width" -msgstr "" +msgstr "宽度" #: ckan/templates/package/snippets/resource_view.html:72 msgid "Height" -msgstr "" +msgstr "高度" #: ckan/templates/package/snippets/resource_view.html:75 msgid "Code" -msgstr "" +msgstr "代码" #: ckan/templates/package/snippets/resource_views_list.html:8 msgid "Resource Preview" -msgstr "" +msgstr "资源预览" #: ckan/templates/package/snippets/resources_list.html:13 msgid "Data and Resources" @@ -3676,7 +3677,7 @@ msgstr "数据与资源" #: ckan/templates/package/snippets/resources_list.html:29 msgid "This dataset has no data" -msgstr "" +msgstr "该数据集没有数据" #: ckan/templates/package/snippets/revisions_table.html:24 #, python-format @@ -3696,27 +3697,27 @@ msgstr "添加数据" #: ckan/templates/package/snippets/view_form.html:8 msgid "eg. My View" -msgstr "" +msgstr "比如 我的视图" #: ckan/templates/package/snippets/view_form.html:9 msgid "eg. Information about my view" -msgstr "" +msgstr "比如 我的视图的相关信息" #: ckan/templates/package/snippets/view_form_filters.html:16 msgid "Add Filter" -msgstr "" +msgstr "增加筛选条件" #: ckan/templates/package/snippets/view_form_filters.html:28 msgid "Remove Filter" -msgstr "" +msgstr "移除筛选条件" #: ckan/templates/package/snippets/view_form_filters.html:46 msgid "Filters" -msgstr "" +msgstr "筛选" #: ckan/templates/package/snippets/view_help.html:2 msgid "What's a view?" -msgstr "" +msgstr "什么是视图?" #: ckan/templates/package/snippets/view_help.html:4 msgid "A view is a representation of the data held against a resource" @@ -3916,7 +3917,7 @@ msgstr[0] "查找到匹配「{query}」的{number}个组织" #: ckan/templates/snippets/search_result_text.html:28 msgid "No organizations found for \"{query}\"" -msgstr "" +msgstr "\"{query}\"该查询未发现任何组织" #: ckan/templates/snippets/search_result_text.html:29 msgid "{number} organization found" @@ -4060,75 +4061,75 @@ msgstr "你确定要删除此用户吗?" #: ckan/templates/user/edit_user_form.html:43 msgid "Are you sure you want to regenerate the API key?" -msgstr "" +msgstr "你确定要重新生成API密钥吗?" #: ckan/templates/user/edit_user_form.html:44 msgid "Regenerate API Key" -msgstr "" +msgstr "记住API密钥" #: ckan/templates/user/edit_user_form.html:48 msgid "Update Profile" -msgstr "" +msgstr "更改个人信息" #: ckan/templates/user/list.html:3 #: ckan/templates/user/snippets/user_search.html:11 msgid "All Users" -msgstr "" +msgstr "所有用户" #: ckan/templates/user/login.html:3 ckan/templates/user/login.html:6 #: ckan/templates/user/login.html:12 #: ckan/templates/user/snippets/login_form.html:28 msgid "Login" -msgstr "" +msgstr "登陆" #: ckan/templates/user/login.html:25 msgid "Need an Account?" -msgstr "" +msgstr "需要一个账户?" #: ckan/templates/user/login.html:27 msgid "Then sign right up, it only takes a minute." -msgstr "" +msgstr "注册仅需要几分钟" #: ckan/templates/user/login.html:30 msgid "Create an Account" -msgstr "" +msgstr "创建用户" #: ckan/templates/user/login.html:42 msgid "Forgotten your password?" -msgstr "" +msgstr "忘记密码?" #: ckan/templates/user/login.html:44 msgid "No problem, use our password recovery form to reset it." -msgstr "" +msgstr "没问题,使用密码恢复功能重置密码。" #: ckan/templates/user/login.html:47 msgid "Forgot your password?" -msgstr "" +msgstr "忘记密码?" #: ckan/templates/user/logout.html:3 ckan/templates/user/logout.html:9 msgid "Logged Out" -msgstr "" +msgstr "登出" #: ckan/templates/user/logout.html:11 msgid "You are now logged out." -msgstr "" +msgstr "你已经登出。" #: ckan/templates/user/logout_first.html:9 msgid "You're already logged in as {user}." -msgstr "" +msgstr "你已经以{user}的身份登陆了。" #: ckan/templates/user/logout_first.html:9 msgid "Logout" -msgstr "" +msgstr "登出" #: ckan/templates/user/logout_first.html:13 #: ckan/templates/user/snippets/login_form.html:24 msgid "Remember me" -msgstr "" +msgstr "记住我" #: ckan/templates/user/logout_first.html:22 msgid "You're already logged in" -msgstr "" +msgstr "你已经登陆了" #: ckan/templates/user/logout_first.html:24 msgid "You need to log out before you can log in with another account." @@ -4136,19 +4137,19 @@ msgstr "" #: ckan/templates/user/logout_first.html:25 msgid "Log out now" -msgstr "" +msgstr "现在登出" #: ckan/templates/user/new.html:6 msgid "Registration" -msgstr "" +msgstr "注册" #: ckan/templates/user/new.html:14 msgid "Register for an Account" -msgstr "" +msgstr "注册一个账户" #: ckan/templates/user/new.html:26 msgid "Why Sign Up?" -msgstr "" +msgstr "为什么注册?" #: ckan/templates/user/new.html:28 msgid "Create datasets, groups and other exciting things" @@ -4156,33 +4157,33 @@ msgstr "" #: ckan/templates/user/new_user_form.html:5 msgid "username" -msgstr "" +msgstr "用户名" #: ckan/templates/user/new_user_form.html:6 msgid "Full Name" -msgstr "" +msgstr "全名" #: ckan/templates/user/new_user_form.html:17 msgid "Create Account" -msgstr "" +msgstr "创建用户" #: ckan/templates/user/perform_reset.html:4 #: ckan/templates/user/perform_reset.html:14 msgid "Reset Your Password" -msgstr "" +msgstr "重置您的密码" #: ckan/templates/user/perform_reset.html:7 msgid "Password Reset" -msgstr "" +msgstr "密码重置" #: ckan/templates/user/perform_reset.html:24 msgid "Update Password" -msgstr "" +msgstr "更改密码" #: ckan/templates/user/perform_reset.html:38 #: ckan/templates/user/request_reset.html:32 msgid "How does this work?" -msgstr "" +msgstr "它是如何工作的?" #: ckan/templates/user/perform_reset.html:40 msgid "Simply enter a new password and we'll update your account" @@ -4190,15 +4191,15 @@ msgstr "" #: ckan/templates/user/read.html:21 msgid "User hasn't created any datasets." -msgstr "" +msgstr "用户尚未创建数据集。" #: ckan/templates/user/read_base.html:39 msgid "You have not provided a biography." -msgstr "" +msgstr "您尚未提交个人信息。" #: ckan/templates/user/read_base.html:41 msgid "This user has no biography." -msgstr "" +msgstr "该用户没有任何记录。" #: ckan/templates/user/read_base.html:73 msgid "Open ID" @@ -4206,29 +4207,29 @@ msgstr "" #: ckan/templates/user/read_base.html:82 ckan/templates/user/read_base.html:96 msgid "This means only you can see this" -msgstr "" +msgstr "这意味着只有您可以看到该项" #: ckan/templates/user/read_base.html:87 msgid "Member Since" -msgstr "" +msgstr "注册时长" #: ckan/templates/user/read_base.html:96 msgid "API Key" -msgstr "" +msgstr "API 密钥" #: ckan/templates/user/request_reset.html:6 msgid "Password reset" -msgstr "" +msgstr "密码重置" #: ckan/templates/user/request_reset.html:19 msgid "Request reset" -msgstr "" +msgstr "请求重置" #: ckan/templates/user/request_reset.html:34 msgid "" "Enter your username into the box and we will send you an email with a link " "to enter a new password." -msgstr "" +msgstr "将您的用户名输入文本框,我们将给您发包含链接的送电子邮件以输入新密码。" #: ckan/templates/user/snippets/followee_dropdown.html:14 #: ckan/templates/user/snippets/followee_dropdown.html:15 @@ -4237,35 +4238,35 @@ msgstr "" #: ckan/templates/user/snippets/followee_dropdown.html:23 msgid "Search list..." -msgstr "" +msgstr "搜索列表" #: ckan/templates/user/snippets/followee_dropdown.html:44 msgid "You are not following anything" -msgstr "" +msgstr "你没有关注任何内容" #: ckan/templates/user/snippets/followers.html:9 msgid "No followers" -msgstr "" +msgstr "没有粉丝" #: ckan/templates/user/snippets/user_search.html:5 msgid "Search Users" -msgstr "" +msgstr "搜索用户" #: ckanext/datapusher/helpers.py:19 msgid "Complete" -msgstr "" +msgstr "完成" #: ckanext/datapusher/helpers.py:20 msgid "Pending" -msgstr "" +msgstr "挂起中" #: ckanext/datapusher/helpers.py:21 msgid "Submitting" -msgstr "" +msgstr "提交中" #: ckanext/datapusher/helpers.py:27 msgid "Not Uploaded Yet" -msgstr "" +msgstr "尚未完成上传" #: ckanext/datastore/controller.py:31 msgid "DataStore resource not found" @@ -4275,25 +4276,25 @@ msgstr "" msgid "" "The data was invalid (for example: a numeric value is out of range or was " "inserted into a text field)." -msgstr "" +msgstr "数据不合法(比如: 数值越界或数值插入字符字段)。" #: ckanext/datastore/logic/action.py:215 ckanext/datastore/logic/action.py:255 #: ckanext/datastore/logic/action.py:332 ckanext/datastore/logic/action.py:422 #: ckanext/datastore/logic/action.py:504 ckanext/datastore/logic/action.py:530 msgid "Resource \"{0}\" was not found." -msgstr "" +msgstr "未发现资源 \"{0}\"" #: ckanext/datastore/logic/auth.py:16 msgid "User {0} not authorized to update resource {1}" -msgstr "" +msgstr "用户{0}未被授权修改资源{1}" #: ckanext/example_iconfigurer/templates/admin/config.html:11 msgid "Datasets per page" -msgstr "" +msgstr "每页的数据集" #: ckanext/example_iconfigurer/templates/admin/config.html:13 msgid "Test conf" -msgstr "" +msgstr "测试配置" #: ckanext/example_idatasetform/templates/package/search.html:16 msgid "Custom Field Ascending" @@ -4307,19 +4308,19 @@ msgstr "" #: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 #: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 msgid "Custom Text" -msgstr "" +msgstr "自定义文字" #: ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html:4 msgid "custom text" -msgstr "" +msgstr "自定义文字" #: ckanext/example_idatasetform/templates/package/snippets/package_metadata_fields.html:11 msgid "Country Code" -msgstr "" +msgstr "国家代码" #: ckanext/example_idatasetform/templates/package/snippets/resource_form.html:6 msgid "custom resource text" -msgstr "" +msgstr "自定义资源文字" #: ckanext/example_itranslation/templates/home/index.html:4 msgid "This is an untranslated string" @@ -4332,15 +4333,15 @@ msgstr "此群组沒有说明" #: ckanext/example_theme/v12_extra_public_dir/templates/home/snippets/promoted.html:4 msgid "CKAN's data previewing tool has many powerful features" -msgstr "" +msgstr "CKAN的数据预览工具有很多实用功能" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "Image url" -msgstr "" +msgstr "图片url" #: ckanext/imageview/theme/templates/image_form.html:3 msgid "eg. http://example.com/image.jpg (if blank uses resource url)" -msgstr "" +msgstr "例如. http://example.com/image.jpg (如果为空将使用资源路径)" #: ckanext/reclineview/plugin.py:84 msgid "Data Explorer" @@ -4348,11 +4349,11 @@ msgstr "" #: ckanext/reclineview/plugin.py:111 msgid "Table" -msgstr "" +msgstr "表格" #: ckanext/reclineview/plugin.py:154 msgid "Graph" -msgstr "" +msgstr "图" #: ckanext/reclineview/plugin.py:214 msgid "Map" @@ -4370,21 +4371,21 @@ msgstr "" #: ckanext/reclineview/theme/templates/recline_graph_form.html:3 #: ckanext/reclineview/theme/templates/recline_map_form.html:3 msgid "eg: 0" -msgstr "" +msgstr "例如:0" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "Number of rows" -msgstr "" +msgstr "行数" #: ckanext/reclineview/theme/templates/recline_graph_form.html:4 #: ckanext/reclineview/theme/templates/recline_map_form.html:4 msgid "eg: 100" -msgstr "" +msgstr "例如:100" #: ckanext/reclineview/theme/templates/recline_graph_form.html:6 msgid "Graph type" -msgstr "" +msgstr "图片类型" #: ckanext/reclineview/theme/templates/recline_graph_form.html:7 msgid "Group (Axis 1)" @@ -4396,19 +4397,19 @@ msgstr "" #: ckanext/reclineview/theme/templates/recline_map_form.html:6 msgid "Field type" -msgstr "" +msgstr "字段类型" #: ckanext/reclineview/theme/templates/recline_map_form.html:7 msgid "Latitude field" -msgstr "" +msgstr "纬度字段" #: ckanext/reclineview/theme/templates/recline_map_form.html:8 msgid "Longitude field" -msgstr "" +msgstr "经度字段" #: ckanext/reclineview/theme/templates/recline_map_form.html:9 msgid "GeoJSON field" -msgstr "" +msgstr "GeoJSON字段" #: ckanext/reclineview/theme/templates/recline_map_form.html:10 msgid "Auto zoom to features" @@ -4420,16 +4421,16 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:10 msgid "Total number of Datasets" -msgstr "" +msgstr "数据集总数" #: ckanext/stats/templates/ckanext/stats/index.html:17 #: ckanext/stats/templates/ckanext/stats/index.html:40 msgid "Date" -msgstr "" +msgstr "日期" #: ckanext/stats/templates/ckanext/stats/index.html:18 msgid "Total datasets" -msgstr "" +msgstr "所有数据集" #: ckanext/stats/templates/ckanext/stats/index.html:33 #: ckanext/stats/templates/ckanext/stats/index.html:179 @@ -4442,33 +4443,33 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:42 msgid "New datasets" -msgstr "" +msgstr "新数据集" #: ckanext/stats/templates/ckanext/stats/index.html:58 #: ckanext/stats/templates/ckanext/stats/index.html:180 msgid "Top Rated Datasets" -msgstr "" +msgstr "最高分数据集" #: ckanext/stats/templates/ckanext/stats/index.html:64 msgid "Average rating" -msgstr "" +msgstr "平均评分" #: ckanext/stats/templates/ckanext/stats/index.html:65 msgid "Number of ratings" -msgstr "" +msgstr "评分数目" #: ckanext/stats/templates/ckanext/stats/index.html:79 msgid "No ratings" -msgstr "" +msgstr "暂无评分" #: ckanext/stats/templates/ckanext/stats/index.html:84 #: ckanext/stats/templates/ckanext/stats/index.html:181 msgid "Most Edited Datasets" -msgstr "" +msgstr "编辑数做多的数据集" #: ckanext/stats/templates/ckanext/stats/index.html:90 msgid "Number of edits" -msgstr "" +msgstr "编辑数" #: ckanext/stats/templates/ckanext/stats/index.html:103 msgid "No edited datasets" @@ -4477,15 +4478,15 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:108 #: ckanext/stats/templates/ckanext/stats/index.html:182 msgid "Largest Groups" -msgstr "" +msgstr "最大群组" #: ckanext/stats/templates/ckanext/stats/index.html:114 msgid "Number of datasets" -msgstr "" +msgstr "数据集数目" #: ckanext/stats/templates/ckanext/stats/index.html:127 msgid "No groups" -msgstr "" +msgstr "没有群组" #: ckanext/stats/templates/ckanext/stats/index.html:132 #: ckanext/stats/templates/ckanext/stats/index.html:183 @@ -4494,12 +4495,12 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:136 msgid "Tag Name" -msgstr "" +msgstr "标签名" #: ckanext/stats/templates/ckanext/stats/index.html:137 #: ckanext/stats/templates/ckanext/stats/index.html:157 msgid "Number of Datasets" -msgstr "" +msgstr "数据集数目" #: ckanext/stats/templates/ckanext/stats/index.html:152 #: ckanext/stats/templates/ckanext/stats/index.html:184 @@ -4508,15 +4509,15 @@ msgstr "" #: ckanext/stats/templates/ckanext/stats/index.html:175 msgid "Statistics Menu" -msgstr "" +msgstr "统计目录" #: ckanext/stats/templates/ckanext/stats/index.html:178 msgid "Total Number of Datasets" -msgstr "" +msgstr "数据集总数" #: ckanext/textview/plugin.py:65 ckanext/textview/plugin.py:67 msgid "Text" -msgstr "" +msgstr "文本" #: ckanext/textview/theme/public/text_view.js:5 #, python-format @@ -4525,12 +4526,12 @@ msgstr "" #: ckanext/webpageview/plugin.py:19 ckanext/webpageview/plugin.py:24 msgid "Website" -msgstr "" +msgstr "网站" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "Web Page url" -msgstr "" +msgstr "页面url" #: ckanext/webpageview/theme/templates/webpage_form.html:3 msgid "eg. http://example.com (if blank uses resource url)" -msgstr "" +msgstr "例如. http://example.com (如果为空将使用资源路径)" diff --git a/ckan/i18n/zh_TW/LC_MESSAGES/ckan.mo b/ckan/i18n/zh_TW/LC_MESSAGES/ckan.mo index 9c2acfb60a4..f563b7581bf 100644 Binary files a/ckan/i18n/zh_TW/LC_MESSAGES/ckan.mo and b/ckan/i18n/zh_TW/LC_MESSAGES/ckan.mo differ diff --git a/ckan/lib/activity_streams.py b/ckan/lib/activity_streams.py index dc054e8bfd8..e5807b2cc3e 100644 --- a/ckan/lib/activity_streams.py +++ b/ckan/lib/activity_streams.py @@ -26,7 +26,8 @@ def get_snippet_user(activity, detail): def get_snippet_dataset(activity, detail): data = activity['data'] - link = h.dataset_link(data.get('package') or data.get('dataset')) + pkg_dict = data.get('package') or data.get('dataset') + link = h.dataset_link(pkg_dict) if pkg_dict else '' return literal('''%s''' % (link) ) diff --git a/ckan/lib/activity_streams_session_extension.py b/ckan/lib/activity_streams_session_extension.py index 6490658f0d2..1a940d62cac 100644 --- a/ckan/lib/activity_streams_session_extension.py +++ b/ckan/lib/activity_streams_session_extension.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config +from ckan.common import config from sqlalchemy.orm.session import SessionExtension from paste.deploy.converters import asbool import logging diff --git a/ckan/lib/app_globals.py b/ckan/lib/app_globals.py index 9f0b32d6659..31054ae2578 100644 --- a/ckan/lib/app_globals.py +++ b/ckan/lib/app_globals.py @@ -8,11 +8,12 @@ import re from paste.deploy.converters import asbool -from pylons import config +from ckan.common import config import ckan import ckan.model as model import ckan.logic as logic +from logic.schema import update_configuration_schema log = logging.getLogger(__name__) @@ -162,20 +163,17 @@ def get_config_value(key, default=''): # update the config config[key] = value + return value # update the config settings in auto update - schema = logic.schema.update_configuration_schema() + schema = update_configuration_schema() for key in schema.keys(): get_config_value(key) - # cusom styling + # custom styling main_css = get_config_value('ckan.main_css', '/base/css/main.css') set_main_css(main_css) - # site_url_nice - site_url_nice = app_globals.site_url.replace('http://', '') - site_url_nice = site_url_nice.replace('www.', '') - app_globals.site_url_nice = site_url_nice if app_globals.site_logo: app_globals.header_class = 'header-image' diff --git a/ckan/lib/auth_tkt.py b/ckan/lib/auth_tkt.py index fee688c34c3..9f82e10dbd8 100644 --- a/ckan/lib/auth_tkt.py +++ b/ckan/lib/auth_tkt.py @@ -3,7 +3,7 @@ import math import os -from pylons import config +from ckan.common import config from repoze.who.plugins import auth_tkt as repoze_auth_tkt _bool = repoze_auth_tkt._bool diff --git a/ckan/lib/base.py b/ckan/lib/base.py index 72446b366be..d6e5ff2fb2d 100644 --- a/ckan/lib/base.py +++ b/ckan/lib/base.py @@ -8,10 +8,9 @@ import time from paste.deploy.converters import asbool -from pylons import cache, config, session +from pylons import cache, session from pylons.controllers import WSGIController from pylons.controllers.util import abort as _abort -from pylons.controllers.util import redirect_to, redirect from pylons.decorators import jsonify from pylons.i18n import N_, gettext, ngettext from pylons.templating import cached_template, pylons_globals @@ -30,17 +29,13 @@ # These imports are for legacy usages and will be removed soon these should # be imported directly from ckan.common for internal ckan code and via the # plugins.toolkit for extensions. -from ckan.common import json, _, ungettext, c, g, request, response +from ckan.common import json, _, ungettext, c, g, request, response, config log = logging.getLogger(__name__) -PAGINATE_ITEMS_PER_PAGE = 50 - APIKEY_HEADER_NAME_KEY = 'apikey_header_name' APIKEY_HEADER_NAME_DEFAULT = 'X-CKAN-API-Key' -ALLOWED_FIELDSET_PARAMS = ['package_form', 'restrict'] - def abort(status_code=None, detail='', headers=None, comment=None): '''Abort the current request immediately by returning an HTTP exception. @@ -197,7 +192,6 @@ class BaseController(WSGIController): def __before__(self, action, **params): c.__timer = time.time() - c.__version__ = ckan.__version__ app_globals.app_globals._check_uptodate() self._identify_user() @@ -310,15 +304,14 @@ def __call__(self, environ, start_response): break if not is_valid_cookie_data: if session.id: - if not session.get('lang'): - self.log.debug('No session data any more - ' - 'deleting session') - self.log.debug('Session: %r', session.items()) - session.delete() + self.log.debug('No valid session data - ' + 'deleting session') + self.log.debug('Session: %r', session.items()) + session.delete() else: - response.delete_cookie(cookie) - self.log.debug('No session data any more - ' + self.log.debug('No session id - ' 'deleting session cookie') + response.delete_cookie(cookie) # Remove auth_tkt repoze.who cookie if user not logged in. elif cookie == 'auth_tkt' and not session.id: response.delete_cookie(cookie) @@ -340,6 +333,7 @@ def _set_cors(self): True, or the request Origin is in the origin_whitelist. ''' cors_origin_allowed = None + if asbool(config.get('ckan.cors.origin_allow_all')): cors_origin_allowed = "*" elif config.get('ckan.cors.origin_whitelist') and \ @@ -347,7 +341,6 @@ def _set_cors(self): in config['ckan.cors.origin_whitelist'].split(): # set var to the origin to allow it. cors_origin_allowed = request.headers.get('Origin') - if cors_origin_allowed is not None: response.headers['Access-Control-Allow-Origin'] = \ cors_origin_allowed @@ -378,24 +371,6 @@ def _get_user_for_apikey(self): user = query.filter_by(apikey=apikey).first() return user - def _get_page_number(self, params, key='page', default=1): - """ - Returns the page number from the provided params after - verifies that it is an integer. - - If it fails it will abort the request with a 400 error - """ - p = params.get(key, default) - - try: - p = int(p) - if p < 1: - raise ValueError("Negative number not allowed") - except ValueError, e: - abort(400, ('"page" parameter must be a positive integer')) - - return p - # Include the '_' function in the public names __all__ = [__name for __name in locals().keys() if not __name.startswith('_') diff --git a/ckan/lib/captcha.py b/ckan/lib/captcha.py index b6749a3d300..bee39f68406 100644 --- a/ckan/lib/captcha.py +++ b/ckan/lib/captcha.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config +from ckan.common import config import urllib import urllib2 diff --git a/ckan/lib/celery_app.py b/ckan/lib/celery_app.py index ee648724cdd..047acfa1f01 100644 --- a/ckan/lib/celery_app.py +++ b/ckan/lib/celery_app.py @@ -1,14 +1,22 @@ # encoding: utf-8 +''' +Celery background tasks management. + +This module is DEPRECATED, use ``ckan.lib.jobs`` instead. +''' + import ConfigParser import os import logging -from pylons import config as pylons_config +from ckan.common import config as ckan_config from pkg_resources import iter_entry_points, VersionConflict log = logging.getLogger(__name__) +log.warning('ckan.lib.celery_app is deprecated, use ckan.lib.jobs instead.') + LIST_PARAMS = """CELERY_IMPORTS ADMINS ROUTES""".split() from celery import Celery @@ -25,7 +33,7 @@ config.read(config_file) -sqlalchemy_url = pylons_config.get('sqlalchemy.url') +sqlalchemy_url = ckan_config.get('sqlalchemy.url') if not sqlalchemy_url: sqlalchemy_url = config.get('app:main', 'sqlalchemy.url') diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py index 86537863c3b..888889a8fee 100644 --- a/ckan/lib/cli.py +++ b/ckan/lib/cli.py @@ -11,33 +11,58 @@ import itertools import json import logging +import urlparse +from optparse import OptionConflictError + +import sqlalchemy as sa +import routes +import paste.script +from paste.registry import Registry +from paste.script.util.logging_config import fileConfig + import ckan.logic as logic import ckan.model as model import ckan.include.rjsmin as rjsmin import ckan.include.rcssmin as rcssmin import ckan.lib.fanstatic_resources as fanstatic_resources import ckan.plugins as p -import sqlalchemy as sa -import urlparse -import routes -from pylons import config +from ckan.common import config -import paste.script -from paste.registry import Registry -from paste.script.util.logging_config import fileConfig #NB No CKAN imports are allowed until after the config file is loaded. # i.e. do the imports in methods, after _load_config is called. # Otherwise loggers get disabled. +def deprecation_warning(message=None): + ''' + Print a deprecation warning to STDERR. + + If ``message`` is given it is also printed to STDERR. + ''' + sys.stderr.write(u'WARNING: This function is deprecated.') + if message: + sys.stderr.write(u' ' + message.strip()) + sys.stderr.write(u'\n') + + +def error(msg): + ''' + Print an error message to STDOUT and exit with return code 1. + ''' + sys.stderr.write(msg) + if not msg.endswith('\n'): + sys.stderr.write('\n') + sys.exit(1) + + def parse_db_config(config_key='sqlalchemy.url'): ''' Takes a config key for a database connection url and parses it into a dictionary. Expects a url like: 'postgres://tester:pass@localhost/ckantest3' ''' - from pylons import config + from ckan.common import config url = config[config_key] regex = [ '^\s*(?P\w*)', @@ -187,10 +212,10 @@ class ManageDb(CkanCommand): search index db upgrade [version no.] - Data migrate db version - returns current version of data schema - db dump FILE_PATH - dump to a pg_dump file - db load FILE_PATH - load a pg_dump from a file + db dump FILE_PATH - dump to a pg_dump file [DEPRECATED] + db load FILE_PATH - load a pg_dump from a file [DEPRECATED] db load-only FILE_PATH - load a pg_dump from a file but don\'t do - the schema upgrade or search indexing + the schema upgrade or search indexing [DEPRECATED] db create-from-model - create database from the model (indexes not made) db migrate-filestore - migrate all uploaded data from the 2.1 filesore. ''' @@ -245,8 +270,7 @@ def command(self): elif cmd == 'migrate-filestore': self.migrate_filestore() else: - print 'Command %s not recognized' % cmd - sys.exit(1) + error('Command %s not recognized' % cmd) def _get_db_config(self): return parse_db_config() @@ -291,6 +315,7 @@ def _run_cmd(self, command_line): raise SystemError('Command exited with errorcode: %i' % retcode) def dump(self): + deprecation_warning(u"Use PostgreSQL's pg_dump instead.") if len(self.args) < 2: print 'Need pg_dump filepath' return @@ -300,6 +325,7 @@ def dump(self): pg_cmd = self._postgres_dump(dump_path) def load(self, only_load=False): + deprecation_warning(u"Use PostgreSQL's pg_restore instead.") if len(self.args) < 2: print 'Need pg_dump filepath' return @@ -569,7 +595,7 @@ def export_datasets(self, out_folder): ''' import urlparse import urllib2 - import pylons.config as config + from ckan.common import config import ckan.model as model import ckan.logic as logic import ckan.lib.helpers as h @@ -596,9 +622,8 @@ def export_datasets(self, out_folder): r = urllib2.urlopen(url).read() except urllib2.HTTPError, e: if e.code == 404: - print ('Please install ckanext-dcat and enable the ' + - '`dcat` plugin to use the RDF serializations') - sys.exit(1) + error('Please install ckanext-dcat and enable the ' + + '`dcat` plugin to use the RDF serializations') with open(fname, 'wb') as f: f.write(r) except IOError, ioe: @@ -787,16 +812,14 @@ def password_prompt(cls): password1 = getpass.getpass('Password: ') password2 = getpass.getpass('Confirm password: ') if password1 != password2: - print 'Passwords do not match' - sys.exit(1) + error('Passwords do not match') return password1 def add(self): import ckan.model as model if len(self.args) < 2: - print 'Need name of the user.' - sys.exit(1) + error('Need name of the user.') username = self.args[1] # parse args into data_dict @@ -828,8 +851,7 @@ def add(self): user_dict = logic.get_action('user_create')(context, data_dict) pprint(user_dict) except logic.ValidationError, e: - print e - sys.exit(1) + error(e) def remove(self): import ckan.model as model @@ -925,7 +947,9 @@ def purge(self, dataset_ref): class Celery(CkanCommand): - '''Celery daemon + '''Celery daemon [DEPRECATED] + + This command is DEPRECATED, use `paster jobs` instead. Usage: celeryd - run the celery daemon @@ -951,10 +975,10 @@ def command(self): elif cmd == 'clean': self.clean() else: - print 'Command %s not recognized' % cmd - sys.exit(1) + error('Command %s not recognized' % cmd) def run_(self): + deprecation_warning(u'Use `paster jobs worker` instead.') default_ini = os.path.join(os.getcwd(), 'development.ini') if self.options.config: @@ -962,8 +986,7 @@ def run_(self): elif os.path.isfile(default_ini): os.environ['CKAN_CONFIG'] = default_ini else: - print 'No .ini specified and none was found in current directory' - sys.exit(1) + error('No .ini specified and none was found in current directory') from ckan.lib.celery_app import celery celery_args = [] @@ -972,6 +995,7 @@ def run_(self): celery.worker_main(argv=['celeryd', '--loglevel=INFO'] + celery_args) def view(self): + deprecation_warning(u'Use `paster jobs list` instead.') self._load_config() import ckan.model as model from kombu.transport.sqlalchemy.models import Message @@ -986,6 +1010,7 @@ def view(self): print '%i: Invisible Sent:%s' % (message.id, message.sent_at) def clean(self): + deprecation_warning(u'Use `paster jobs clear` instead.') self._load_config() import ckan.model as model query = model.Session.execute("select * from kombu_message") @@ -999,8 +1024,7 @@ def clean(self): print '%i of %i tasks deleted' % (tasks_initially - tasks_afterwards, tasks_initially) if tasks_afterwards: - print 'ERROR: Failed to delete all tasks' - sys.exit(1) + error('Failed to delete all tasks') model.repo.commit_and_remove() @@ -1080,15 +1104,13 @@ def command(self): self.update_all(engine, start_date) elif cmd == 'export': if len(self.args) <= 1: - print self.__class__.__doc__ - sys.exit(1) + error(self.__class__.__doc__) output_file = self.args[1] start_date = self.args[2] if len(self.args) > 2 else None self.update_all(engine, start_date) self.export_tracking(engine, output_file) else: - print self.__class__.__doc__ - sys.exit(1) + error(self.__class__.__doc__) def update_all(self, engine, start_date=None): if start_date: @@ -1759,7 +1781,7 @@ class TranslationsCommand(CkanCommand): def command(self): self._load_config() - from pylons import config + from ckan.common import config self.ckan_path = os.path.join(os.path.dirname(__file__), '..') i18n_path = os.path.join(self.ckan_path, 'i18n') self.i18n_path = config.get('ckan.i18n_directory', i18n_path) @@ -2236,11 +2258,9 @@ def _get_view_plugins(self, view_plugin_types, set(loaded_view_plugins)) if plugins_not_found: - msg = ('View plugin(s) not found : {0}. '.format(plugins_not_found) - + 'Have they been added to the `ckan.plugins` configuration' - + ' option?') - log.error(msg) - sys.exit(1) + error('View plugin(s) not found : {0}. '.format(plugins_not_found) + + 'Have they been added to the `ckan.plugins` configuration' + + ' option?') return loaded_view_plugins @@ -2324,8 +2344,7 @@ def _update_search_params(self, search_data_dict): try: user_search_params = json.loads(self.options.search_params) except ValueError, e: - log.error('Unable to parse JSON search parameters: {0}'.format(e)) - sys.exit(1) + error('Unable to parse JSON search parameters: {0}'.format(e)) if user_search_params.get('q'): search_data_dict['q'] = user_search_params['q'] @@ -2353,6 +2372,7 @@ def _search_datasets(self, page=1, view_types=[]): 'q': '', 'fq': '', 'fq_list': [], + 'include_private': True, 'rows': n, 'start': n * (page - 1), } @@ -2376,8 +2396,7 @@ def _search_datasets(self, page=1, view_types=[]): search_data_dict['q'] = '*:*' query = p.toolkit.get_action('package_search')( - {'ignore_capacity_check': True}, - search_data_dict) + {}, search_data_dict) return query @@ -2399,8 +2418,7 @@ def create_views(self, view_plugin_types=[]): query = self._search_datasets(page, loaded_view_plugins) if page == 1 and query['count'] == 0: - log.info('No datasets to create resource views on, exiting...') - sys.exit(1) + error('No datasets to create resource views on, exiting...') elif page == 1 and not self.options.assume_yes: @@ -2412,8 +2430,7 @@ def create_views(self, view_plugin_types=[]): loaded_view_plugins)) if confirm == 'no': - log.info('Command aborted by user') - sys.exit(1) + error('Command aborted by user') if query['results']: for dataset_dict in query['results']: @@ -2458,8 +2475,7 @@ def clear_views(self, view_plugin_types=[]): result = query_yes_no(msg, default='no') if result == 'no': - log.info('Command aborted by user') - sys.exit(1) + error('Command aborted by user') context = {'user': self.site_user['name']} logic.get_action('resource_view_clear')( @@ -2537,15 +2553,161 @@ def command(self): if options: for option in options: if '=' not in option: - sys.stderr.write( + error( 'An option does not have an equals sign: %r ' 'It should be \'key=value\'. If there are spaces ' 'you\'ll need to quote the option.\n' % option) - sys.exit(1) try: config_tool.config_edit_using_option_strings( config_filepath, options, self.options.section, edit=self.options.edit) except config_tool.ConfigToolError, e: - sys.stderr.write(e.message) - sys.exit(1) + error(e) + + +class JobsCommand(CkanCommand): + '''Manage background jobs + + Usage: + + paster jobs worker [--burst] [QUEUES] + + Start a worker that fetches jobs from queues and executes + them. If no queue names are given then the worker listens + to the default queue, this is equivalent to + + paster jobs worker default + + If queue names are given then the worker listens to those + queues and only those: + + paster jobs worker my-custom-queue + + Hence, if you want the worker to listen to the default queue + and some others then you must list the default queue explicitly: + + paster jobs worker default my-custom-queue + + If the `--burst` option is given then the worker will exit + as soon as all its queues are empty. + + paster jobs list [QUEUES] + + List currently enqueued jobs from the given queues. If no queue + names are given then the jobs from all queues are listed. + + paster jobs show ID + + Show details about a specific job. + + paster jobs cancel ID + + Cancel a specific job. Jobs can only be canceled while they are + enqueued. Once a worker has started executing a job it cannot + be aborted anymore. + + paster jobs clear [QUEUES] + + Cancel all jobs on the given queues. If no queue names are + given then ALL queues are cleared. + + paster jobs test [QUEUES] + + Enqueue a test job. If no queue names are given then the job is + added to the default queue. If queue names are given then a + separate test job is added to each of the queues. + ''' + + summary = __doc__.split(u'\n')[0] + usage = __doc__ + min_args = 0 + + + def __init__(self, *args, **kwargs): + super(JobsCommand, self).__init__(*args, **kwargs) + try: + self.parser.add_option(u'--burst', action='store_true', + default=False, + help=u'Start worker in burst mode.') + except OptionConflictError: + # Option has already been added in previous call + pass + + def command(self): + self._load_config() + try: + cmd = self.args.pop(0) + except IndexError: + print(self.__doc__) + sys.exit(0) + if cmd == u'worker': + self.worker() + elif cmd == u'list': + self.list() + elif cmd == u'show': + self.show() + elif cmd == u'cancel': + self.cancel() + elif cmd == u'clear': + self.clear() + elif cmd == u'test': + self.test() + else: + error(u'Unknown command "{}"'.format(cmd)) + + def worker(self): + from ckan.lib.jobs import Worker + Worker(self.args).work(burst=self.options.burst) + + def list(self): + data_dict = { + u'queues': self.args, + } + jobs = p.toolkit.get_action(u'job_list')({}, data_dict) + for job in jobs: + if job[u'title'] is None: + job[u'title'] = '' + else: + job[u'title'] = u'"{}"'.format(job[u'title']) + print(u'{created} {id} {queue} {title}'.format(**job)) + + def show(self): + if not self.args: + error(u'You must specify a job ID') + id = self.args[0] + try: + job = p.toolkit.get_action(u'job_show')({}, {u'id': id}) + except logic.NotFound: + error(u'There is no job with ID "{}"'.format(id)) + print(u'ID: {}'.format(job[u'id'])) + if job[u'title'] is None: + title = u'None' + else: + title = u'"{}"'.format(job[u'title']) + print(u'Title: {}'.format(title)) + print(u'Created: {}'.format(job[u'created'])) + print(u'Queue: {}'.format(job[u'queue'])) + + def cancel(self): + if not self.args: + error(u'You must specify a job ID') + id = self.args[0] + try: + p.toolkit.get_action(u'job_cancel')({}, {u'id': id}) + except logic.NotFound: + error(u'There is no job with ID "{}"'.format(id)) + print(u'Cancelled job {}'.format(id)) + + def clear(self): + data_dict = { + u'queues': self.args, + } + queues = p.toolkit.get_action(u'job_clear')({}, data_dict) + queues = (u'"{}"'.format(q) for q in queues) + print(u'Cleared queue(s) {}'.format(u', '.join(queues))) + + def test(self): + from ckan.lib.jobs import DEFAULT_QUEUE_NAME, enqueue, test_job + for queue in (self.args or [DEFAULT_QUEUE_NAME]): + job = enqueue(test_job, [u'A test job'], title=u'A test job', queue=queue) + print(u'Added test job {} to queue "{}"'.format(job.id, queue)) diff --git a/ckan/lib/config_tool.py b/ckan/lib/config_tool.py index d5118c9d436..54060b77671 100644 --- a/ckan/lib/config_tool.py +++ b/ckan/lib/config_tool.py @@ -189,9 +189,9 @@ def insert_new_sections(new_sections): # at start of new section, write the 'add'ed options for option in changes.get(section, 'add'): write_option(option) - options_to_edit_in_this_section = dict( - [(option.key, option) - for option in changes.get(section, 'edit')]) + options_to_edit_in_this_section = {option.key: option + for option + in changes.get(section, 'edit')} continue existing_option = parse_option_string(section, line) if not existing_option: diff --git a/ckan/lib/create_test_data.py b/ckan/lib/create_test_data.py index 0dc4df0bda7..73ee6633053 100644 --- a/ckan/lib/create_test_data.py +++ b/ckan/lib/create_test_data.py @@ -187,7 +187,7 @@ def create_arbitrary(cls, package_dicts, relationships=[], if not isinstance(v, datetime.datetime): v = unicode(v) non_extras[str(k)] = v - extras = dict([(str(k), unicode(v)) for k, v in res_dict.get('extras', {}).items()]) + extras = {str(k): unicode(v) for k, v in res_dict.get('extras', {}).items()} pkg.add_resource(extras=extras, **non_extras) elif attr == 'tags': if isinstance(val, (str, unicode)): diff --git a/ckan/lib/datapreview.py b/ckan/lib/datapreview.py index e17168e14fa..cb995de2437 100644 --- a/ckan/lib/datapreview.py +++ b/ckan/lib/datapreview.py @@ -8,7 +8,7 @@ import urlparse import logging -import pylons.config as config +from ckan.common import config import ckan.plugins as p from ckan import logic diff --git a/ckan/lib/dictization/__init__.py b/ckan/lib/dictization/__init__.py index 96c4c281a9f..2df79363231 100644 --- a/ckan/lib/dictization/__init__.py +++ b/ckan/lib/dictization/__init__.py @@ -3,7 +3,7 @@ import datetime from sqlalchemy.orm import class_mapper import sqlalchemy -from pylons import config +from ckan.common import config try: RowProxy = sqlalchemy.engine.result.RowProxy diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py index bf38416f493..f3914cbd3ff 100644 --- a/ckan/lib/dictization/model_dictize.py +++ b/ckan/lib/dictization/model_dictize.py @@ -14,7 +14,7 @@ import datetime import urlparse -from pylons import config +from ckan.common import config from sqlalchemy.sql import select import ckan.logic as logic @@ -386,7 +386,7 @@ def get_packages_for_this_group(group_, just_the_count=False): authz.has_user_permission_for_group_or_org( group_.id, context.get('user'), 'read')) if is_group_member: - context['ignore_capacity_check'] = True + q['include_private'] = True if not just_the_count: # Is there a packages limit in the context? diff --git a/ckan/lib/dictization/model_save.py b/ckan/lib/dictization/model_save.py index 9420b86f5f1..60476666682 100644 --- a/ckan/lib/dictization/model_save.py +++ b/ckan/lib/dictization/model_save.py @@ -157,10 +157,8 @@ def package_tag_list_save(tag_dicts, package, context): for package_tag in package.package_tag_all) - tag_package_tag_inactive = dict( - [ (tag,pt) for tag,pt in tag_package_tag.items() if - pt.state in ['deleted'] ] - ) + tag_package_tag_inactive = {tag: pt for tag,pt in tag_package_tag.items() if + pt.state in ['deleted']} tag_name_vocab = set() tags = set() diff --git a/ckan/lib/email_notifications.py b/ckan/lib/email_notifications.py index 20a6965f5f9..3d05228d632 100644 --- a/ckan/lib/email_notifications.py +++ b/ckan/lib/email_notifications.py @@ -9,13 +9,11 @@ import datetime import re -import pylons - import ckan.model as model import ckan.logic as logic import ckan.lib.base as base -from ckan.common import ungettext +from ckan.common import ungettext, config def string_to_timedelta(s): @@ -103,7 +101,7 @@ def _notifications_for_activities(activities, user_dict): "{n} new activity from {site_title}", "{n} new activities from {site_title}", len(activities)).format( - site_title=pylons.config.get('ckan.site_title'), + site_title=config.get('ckan.site_title'), n=len(activities)) body = base.render( 'activity_streams/activity_stream_email_notifications.text', @@ -190,7 +188,7 @@ def get_and_send_notifications_for_user(user): # Parse the email_notifications_since config setting, email notifications # from longer ago than this time will not be sent. - email_notifications_since = pylons.config.get( + email_notifications_since = config.get( 'ckan.email_notifications_since', '2 days') email_notifications_since = string_to_timedelta( email_notifications_since) @@ -208,7 +206,6 @@ def get_and_send_notifications_for_user(user): activity_stream_last_viewed) notifications = get_notifications(user, since) - # TODO: Handle failures from send_email_notification. for notification in notifications: send_notification(user, notification) diff --git a/ckan/lib/formatters.py b/ckan/lib/formatters.py index f731144c606..6c927a53399 100644 --- a/ckan/lib/formatters.py +++ b/ckan/lib/formatters.py @@ -99,12 +99,10 @@ def months_between(date1, date2): return months if not show_date: - now = datetime.datetime.utcnow() - if datetime_.tzinfo is not None: - now = now.replace(tzinfo=datetime_.tzinfo) - else: - now = now.replace(tzinfo=pytz.utc) + now = datetime.datetime.now(pytz.utc) + if datetime_.tzinfo is None: datetime_ = datetime_.replace(tzinfo=pytz.utc) + date_diff = now - datetime_ days = date_diff.days if days < 1 and now > datetime_: diff --git a/ckan/lib/hash.py b/ckan/lib/hash.py index 5f423ee650a..87a15ab7348 100644 --- a/ckan/lib/hash.py +++ b/ckan/lib/hash.py @@ -3,7 +3,7 @@ import hmac import hashlib -from pylons import config, request +from ckan.common import config, request secret = None diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 5ae9929416a..dabe561101b 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -26,12 +26,13 @@ from markdown import markdown from bleach import clean as clean_html from pylons import url as _pylons_default_url -from pylons import config -from routes import redirect_to as _redirect_to +from ckan.common import config, is_flask_request +from flask import redirect as _flask_redirect +from routes import redirect_to as _routes_redirect_to from routes import url_for as _routes_default_url_for import i18n -import ckan.exceptions +import ckan.exceptions import ckan.lib.fanstatic_resources as fanstatic_resources import ckan.model as model import ckan.lib.formatters as formatters @@ -41,6 +42,7 @@ import ckan.lib.uploader as uploader import ckan.authz as authz import ckan.plugins as p +import ckan from ckan.common import _, ungettext, g, c, request, session, json @@ -139,7 +141,18 @@ def redirect_to(*args, **kw): ''' if are_there_flash_messages(): kw['__no_cache__'] = True - return _redirect_to(url_for(*args, **kw)) + + # Routes router doesn't like unicode args + uargs = map(lambda arg: str(arg) if isinstance(arg, unicode) else arg, + args) + _url = url_for(*uargs, **kw) + if _url.startswith('/'): + _url = str(config['ckan.site_url'].rstrip('/') + _url) + + if is_flask_request(): + return _flask_redirect(_url) + else: + return _routes_redirect_to(_url) @maintain.deprecated('h.url is deprecated please use h.url_for') @@ -364,6 +377,12 @@ def lang(): return request.environ.get('CKAN_LANG') +@core_helper +def ckan_version(): + '''Return CKAN version''' + return ckan.__version__ + + @core_helper def lang_native_name(lang=None): ''' Return the langage name currently used in it's localised form @@ -1081,6 +1100,28 @@ def _range(self, regexp_match): return re.sub(current_page_span, current_page_link, html) +@core_helper +def get_page_number(params, key='page', default=1): + ''' + Return the page number from the provided params after verifying that it is + an positive integer. + + If it fails it will abort the request with a 400 error. + ''' + p = params.get(key, default) + + try: + p = int(p) + if p < 1: + raise ValueError("Negative number not allowed") + except ValueError: + import ckan.lib.base as base + base.abort(400, ('"{key}" parameter must be a positive integer' + .format(key=key))) + + return p + + @core_helper def get_display_timezone(): ''' Returns a pytz timezone for the display_timezone setting in the diff --git a/ckan/lib/i18n.py b/ckan/lib/i18n.py index 9a04a610c3d..5fb13e1cec5 100644 --- a/ckan/lib/i18n.py +++ b/ckan/lib/i18n.py @@ -8,10 +8,11 @@ UnknownLocaleError) from babel.support import Translations from paste.deploy.converters import aslist -from pylons import config from pylons import i18n import pylons + +from ckan.common import config import ckan.i18n from ckan.plugins import PluginImplementations from ckan.plugins.interfaces import ITranslation @@ -173,7 +174,7 @@ def _set_lang(lang): if config.get('ckan.i18n_directory'): fake_config = {'pylons.paths': {'root': config['ckan.i18n_directory']}, 'pylons.package': config['pylons.package']} - i18n.set_lang(lang, pylons_config=fake_config, class_=Translations) + i18n.set_lang(lang, config=fake_config, class_=Translations) else: i18n.set_lang(lang, class_=Translations) diff --git a/ckan/lib/io.py b/ckan/lib/io.py new file mode 100644 index 00000000000..edafe8fc549 --- /dev/null +++ b/ckan/lib/io.py @@ -0,0 +1,52 @@ +# encoding: utf-8 + +u''' +Utility functions for I/O. +''' + +import sys + + +_FILESYSTEM_ENCODING = unicode(sys.getfilesystemencoding() + or sys.getdefaultencoding()) + + +def encode_path(p): + u''' + Convert a Unicode path string to a byte string. + + Intended to be used for encoding paths that are known to be + compatible with the filesystem, for example paths of existing files + that were previously decoded using :py:func:`decode_path`. If you're + dynamically constructing names for new files using unknown inputs + then pass them through :py:func:`ckan.lib.munge.munge_filename` + before encoding them. + + Raises a ``UnicodeEncodeError`` if the path cannot be encoded using + the filesystem's encoding. That will never happen for paths returned + by :py:func:`decode_path`. + + Raises a ``TypeError`` is the input is not a Unicode string. + ''' + if not isinstance(p, unicode): + raise TypeError(u'Can only encode unicode, not {}'.format(type(p))) + return p.encode(_FILESYSTEM_ENCODING) + + +def decode_path(p): + u''' + Convert a byte path string to a Unicode string. + + Intended to be used for decoding byte paths to existing files as + returned by some of Python's built-in I/O functions. + + Raises a ``UnicodeDecodeError`` if the path cannot be decoded using + the filesystem's encoding. Assuming the path was returned by one of + Python's I/O functions this means that the environment Python is + running in is set up incorrectly. + + Raises a ``TypeError`` if the input is not a byte string. + ''' + if not isinstance(p, str): + raise TypeError(u'Can only decode str, not {}'.format(type(p))) + return p.decode(_FILESYSTEM_ENCODING) diff --git a/ckan/lib/jobs.py b/ckan/lib/jobs.py new file mode 100644 index 00000000000..2f90ec57c77 --- /dev/null +++ b/ckan/lib/jobs.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# encoding: utf-8 + +u''' +Asynchronous background jobs. + +Note that most job management functions are not available from this +module but via the various ``job_*`` API functions. + +Internally, RQ queue names are prefixed with a string containing the +CKAN site ID to avoid key collisions when the same Redis database is +used for multiple CKAN instances. The functions of this module expect +unprefixed queue names (e.g. ``'default'``) unless noted otherwise. The +raw RQ objects (e.g. a queue returned by ``get_queue``) use the full, +prefixed names. Use the functions ``add_queue_name_prefix`` and +``remove_queue_name_prefix`` to manage queue name prefixes. + +.. versionadded:: 2.7 +''' + +import logging + +import rq +from rq.connections import push_connection +from rq.exceptions import NoSuchJobError +from rq.job import Job +from rq.utils import ensure_list + +from ckan.lib.redis import connect_to_redis +from ckan.common import config + + +log = logging.getLogger(__name__) + +DEFAULT_QUEUE_NAME = u'default' + +# RQ job queues. Do not use this directly, use ``get_queue`` instead. +_queues = {} + + +def _connect(): + u''' + Connect to Redis and tell RQ about it. + + Workaround for https://github.com/nvie/rq/issues/479. + ''' + conn = connect_to_redis() + push_connection(conn) + return conn + + +def _get_queue_name_prefix(): + u''' + Get the queue name prefix. + ''' + # This must be done at runtime since we need a loaded config + return u'ckan:{}:'.format(config[u'ckan.site_id']) + + +def add_queue_name_prefix(name): + u''' + Prefix a queue name. + + .. seealso:: :py:func:`remove_queue_name_prefix` + ''' + return _get_queue_name_prefix() + name + + +def remove_queue_name_prefix(name): + u''' + Remove a queue name's prefix. + + :raises ValueError: if the given name is not prefixed. + + .. seealso:: :py:func:`add_queue_name_prefix` + ''' + prefix = _get_queue_name_prefix() + if not name.startswith(prefix): + raise ValueError(u'Queue name "{}" is not prefixed.'.format(name)) + return name[len(prefix):] + + +def get_all_queues(): + u''' + Return all job queues currently in use. + + :returns: The queues. + :rtype: List of ``rq.queue.Queue`` instances + + .. seealso:: :py:func:`get_queue` + ''' + redis_conn = _connect() + prefix = _get_queue_name_prefix() + return [q for q in rq.Queue.all(connection=redis_conn) if + q.name.startswith(prefix)] + + +def get_queue(name=DEFAULT_QUEUE_NAME): + u''' + Get a job queue. + + The job queue is initialized if that hasn't happened before. + + :param string name: The name of the queue. If not given then the + default queue is returned. + + :returns: The job queue. + :rtype: ``rq.queue.Queue`` + + .. seealso:: :py:func:`get_all_queues` + ''' + global _queues + fullname = add_queue_name_prefix(name) + try: + return _queues[fullname] + except KeyError: + log.debug(u'Initializing background job queue "{}"'.format(name)) + redis_conn = _connect() + queue = _queues[fullname] = rq.Queue(fullname, connection=redis_conn) + return queue + + +def enqueue(fn, args=None, kwargs=None, title=None, queue=DEFAULT_QUEUE_NAME): + u''' + Enqueue a job to be run in the background. + + :param function fn: Function to be executed in the background + + :param list args: List of arguments to be passed to the function. + Pass an empty list if there are no arguments (default). + + :param dict kwargs: Dict of keyword arguments to be passed to the + function. Pass an empty dict if there are no keyword arguments + (default). + + :param string title: Optional human-readable title of the job. + + :param string queue: Name of the queue. If not given then the + default queue is used. + + :returns: The enqueued job. + :rtype: ``rq.job.Job`` + ''' + if args is None: + args = [] + if kwargs is None: + kwargs = {} + job = get_queue(queue).enqueue_call(func=fn, args=args, kwargs=kwargs) + job.meta[u'title'] = title + job.save() + msg = u'Added background job {}'.format(job.id) + if title: + msg = u'{} ("{}")'.format(msg, title) + msg = u'{} to queue "{}"'.format(msg, queue) + log.info(msg) + return job + + +def job_from_id(id): + u''' + Look up an enqueued job by its ID. + + :param string id: The ID of the job. + + :returns: The job. + :rtype: ``rq.job.Job`` + + :raises KeyError: if no job with that ID exists. + ''' + try: + return Job.fetch(id, connection=_connect()) + except NoSuchJobError: + raise KeyError(u'There is no job with ID "{}".'.format(id)) + + +def dictize_job(job): + u'''Convert a job to a dict. + + In contrast to ``rq.job.Job.to_dict`` this function includes only + the attributes that are relevant to our use case and promotes the + meta attributes that we use (e.g. ``title``). + + :param rq.job.Job job: The job to dictize. + + :returns: The dictized job. + :rtype: dict + ''' + return { + u'id': job.id, + u'title': job.meta.get(u'title'), + u'created': job.created_at.strftime(u'%Y-%m-%dT%H:%M:%S'), + u'queue': remove_queue_name_prefix(job.origin), + } + + +def test_job(*args): + u'''Test job. + + A test job for debugging purposes. Prints out any arguments it + receives. Can be scheduled via ``paster jobs test``. + ''' + print(args) + + +class Worker(rq.Worker): + u''' + CKAN-specific worker. + ''' + def __init__(self, queues=None, *args, **kwargs): + u''' + Constructor. + + Accepts the same arguments as the constructor of + ``rq.worker.Worker``. However, the behavior of the ``queues`` + parameter is different. + + :param queues: The job queue(s) to listen on. Can be a string + with the name of a single queue or a list of queue names. + If not given then the default queue is used. + ''' + queues = queues or [DEFAULT_QUEUE_NAME] + queues = [get_queue(q) for q in ensure_list(queues)] + rq.worker.logger.setLevel(logging.INFO) + super(Worker, self).__init__(queues, *args, **kwargs) + + def register_birth(self, *args, **kwargs): + result = super(Worker, self).register_birth(*args, **kwargs) + names = [remove_queue_name_prefix(n) for n in self.queue_names()] + names = u', '.join(u'"{}"'.format(n) for n in names) + log.info(u'Worker {} (PID {}) has started on queue(s) {} '.format( + self.key, self.pid, names)) + return result + + def execute_job(self, job, *args, **kwargs): + queue = remove_queue_name_prefix(job.origin) + log.info(u'Worker {} has started job {} from queue "{}"'.format( + self.key, job.id, queue)) + result = super(Worker, self).execute_job(job, *args, **kwargs) + log.info(u'Worker {} has finished job {} from queue "{}"'.format( + self.key, job.id, queue)) + return result + + def register_death(self, *args, **kwargs): + result = super(Worker, self).register_death(*args, **kwargs) + log.info(u'Worker {} (PID {}) has stopped'.format(self.key, self.pid)) + return result + + def handle_exception(self, job, *exc_info): + log.exception(u'Job {} on worker {} raised an exception: {}'.format( + job.id, self.key, exc_info[1])) + return super(Worker, self).handle_exception(job, *exc_info) diff --git a/ckan/lib/mailer.py b/ckan/lib/mailer.py index a93242fd15b..9eaf2fd6813 100644 --- a/ckan/lib/mailer.py +++ b/ckan/lib/mailer.py @@ -1,6 +1,7 @@ # encoding: utf-8 import smtplib +import socket import logging import uuid from time import time @@ -9,7 +10,7 @@ from email import Utils from urlparse import urljoin -from pylons import config +from ckan.common import config import paste.deploy.converters import ckan @@ -56,7 +57,13 @@ def _mail_recipient(recipient_name, recipient_email, config.get('smtp.starttls')) smtp_user = config.get('smtp.user') smtp_password = config.get('smtp.password') - smtp_connection.connect(smtp_server) + + try: + smtp_connection.connect(smtp_server) + except socket.error, e: + log.exception(e) + raise MailerException('SMTP server could not be connected to: "%s" %s' + % (smtp_server, e)) try: # Identify ourselves and prompt the server for supported features. smtp_connection.ehlo() diff --git a/ckan/lib/munge.py b/ckan/lib/munge.py index 2f501adc422..7570b588c39 100644 --- a/ckan/lib/munge.py +++ b/ckan/lib/munge.py @@ -9,6 +9,17 @@ import os.path from ckan import model +from ckan.lib.io import decode_path + + +# Maximum length of a filename's extension (including the '.') +MAX_FILENAME_EXTENSION_LENGTH = 21 + +# Maximum total length of a filename (including extension) +MAX_FILENAME_TOTAL_LENGTH = 100 + +# Minimum total length of a filename (including extension) +MIN_FILENAME_TOTAL_LENGTH = 3 def munge_name(name): @@ -134,22 +145,27 @@ def munge_filename(filename): Keeps the filename extension (e.g. .csv). Strips off any path on the front. + + Returns a Unicode string. ''' + if not isinstance(filename, unicode): + filename = decode_path(filename) - # just get the filename ignore the path - path, filename = os.path.split(filename) - # clean up - filename = substitute_ascii_equivalents(filename) + # Ignore path + filename = os.path.split(filename)[1] + + # Clean up filename = filename.lower().strip() - filename = re.sub(r'[^a-zA-Z0-9_. -]', '', filename).replace(' ', '-') - # resize if needed but keep extension + filename = substitute_ascii_equivalents(filename) + filename = re.sub(ur'[^a-zA-Z0-9_. -]', '', filename).replace(u' ', u'-') + filename = re.sub(ur'-+', u'-', filename) + + # Enforce length constraints name, ext = os.path.splitext(filename) - # limit overly long extensions - if len(ext) > 21: - ext = ext[:21] - # max/min size - ext_length = len(ext) - name = _munge_to_length(name, max(3 - ext_length, 1), 100 - ext_length) + ext = ext[:MAX_FILENAME_EXTENSION_LENGTH] + ext_len = len(ext) + name = _munge_to_length(name, max(1, MIN_FILENAME_TOTAL_LENGTH - ext_len), + MAX_FILENAME_TOTAL_LENGTH - ext_len) filename = name + ext return filename diff --git a/ckan/lib/navl/dictization_functions.py b/ckan/lib/navl/dictization_functions.py index d106008ee09..ff4bae72cd5 100644 --- a/ckan/lib/navl/dictization_functions.py +++ b/ckan/lib/navl/dictization_functions.py @@ -4,7 +4,7 @@ import formencode as fe import inspect import json -from pylons import config +from ckan.common import config from ckan.common import _ @@ -12,34 +12,46 @@ class Missing(object): def __unicode__(self): raise Invalid(_('Missing value')) + def __str__(self): raise Invalid(_('Missing value')) + def __int__(self): raise Invalid(_('Missing value')) + def __complex__(self): raise Invalid(_('Missing value')) + def __long__(self): raise Invalid(_('Missing value')) + def __float__(self): raise Invalid(_('Missing value')) + def __oct__(self): raise Invalid(_('Missing value')) + def __hex__(self): raise Invalid(_('Missing value')) + def __nonzero__(self): return False + missing = Missing() + class State(object): pass + class DictizationError(Exception): def __str__(self): if hasattr(self, 'error') and self.error: return repr(self.error) return '' + class Invalid(DictizationError): '''Exception raised by some validator, converter and dictization functions when the given value is invalid. @@ -48,22 +60,42 @@ class Invalid(DictizationError): def __init__(self, error, key=None): self.error = error + class DataError(DictizationError): def __init__(self, error): self.error = error + class StopOnError(DictizationError): '''error to stop validations for a particualar key''' pass + def flattened_order_key(key): '''order by key length first then values''' return tuple([len(key)] + list(key)) + def flatten_schema(schema, flattened=None, key=None): - '''convert schema into flat dict where the keys are tuples''' + '''convert schema into flat dict, where the keys become tuples + e.g. + { + "toplevel": [validators], + "parent": { + "child1": [validators], + "child2": [validators], + } + } + becomes: + { + ('toplevel',): [validators], + ('parent', 'child1'): [validators], + ('parent', 'child2'): [validators], + } + See also: test_flatten_schema() + ''' flattened = flattened or {} old_key = key or [] @@ -76,34 +108,36 @@ def flatten_schema(schema, flattened=None, key=None): return flattened -def get_all_key_combinations(data, flattented_schema): + +def get_all_key_combinations(data, flattened_schema): '''Compare the schema against the given data and get all valid tuples that match the schema ignoring the last value in the tuple. ''' - schema_prefixes = set([key[:-1] for key in flattented_schema]) + schema_prefixes = set([key[:-1] for key in flattened_schema]) combinations = set([()]) for key in sorted(data.keys(), key=flattened_order_key): - ## make sure the tuple key is a valid one in the schema + # make sure the tuple key is a valid one in the schema key_prefix = key[:-1:2] if key_prefix not in schema_prefixes: continue - ## make sure the parent key exists, this is assured by sorting the keys - ## first + # make sure the parent key exists, this is assured by sorting the keys + # first if tuple(tuple(key[:-3])) not in combinations: continue combinations.add(tuple(key[:-1])) return combinations + def make_full_schema(data, schema): - '''make schema by getting all valid combinations and making sure that all keys - are available''' + '''make schema by getting all valid combinations and making sure that all + keys are available''' - flattented_schema = flatten_schema(schema) + flattened_schema = flatten_schema(schema) - key_combinations = get_all_key_combinations(data, flattented_schema) + key_combinations = get_all_key_combinations(data, flattened_schema) full_schema = {} @@ -118,28 +152,37 @@ def make_full_schema(data, schema): return full_schema + def augment_data(data, schema): - '''add missing, extras and junk data''' - flattented_schema = flatten_schema(schema) - key_combinations = get_all_key_combinations(data, flattented_schema) + '''Takes 'flattened' data, compares it with the schema, and returns it with + any problems marked, as follows: + + * keys in the data not in the schema are moved into a list under new key + ('__junk') + * keys in the schema but not data are added as keys with value 'missing' + + ''' + flattened_schema = flatten_schema(schema) + key_combinations = get_all_key_combinations(data, flattened_schema) full_schema = make_full_schema(data, schema) new_data = copy.copy(data) - ## fill junk and extras + # fill junk and extras for key, value in new_data.items(): if key in full_schema: continue - ## check if any thing naugthy is placed against subschemas + # check if any thing naughty is placed against subschemas initial_tuple = key[::2] if initial_tuple in [initial_key[:len(initial_tuple)] - for initial_key in flattented_schema]: - if data[key] <> []: + for initial_key in flattened_schema]: + if data[key] != []: raise DataError('Only lists of dicts can be placed against ' - 'subschema %s, not %s' % (key,type(data[key]))) + 'subschema %s, not %s' % + (key, type(data[key]))) if key[:-1] in key_combinations: extras_key = key[:-1] + ('__extras',) @@ -152,7 +195,7 @@ def augment_data(data, schema): new_data[("__junk",)] = junk new_data.pop(key) - ## add missing + # add missing for key, value in full_schema.items(): if key not in new_data and not key[-1].startswith("__"): @@ -160,6 +203,7 @@ def augment_data(data, schema): return new_data + def convert(converter, key, converted_data, errors, context): if inspect.isclass(converter) and issubclass(converter, fe.Validator): @@ -183,9 +227,9 @@ def convert(converter, key, converted_data, errors, context): converted_data[key] = value return except TypeError, e: - ## hack to make sure the type error was caused by the wrong - ## number of arguments given. - if not converter.__name__ in str(e): + # hack to make sure the type error was caused by the wrong + # number of arguments given. + if converter.__name__ not in str(e): raise except Invalid, e: errors[key].append(e.error) @@ -198,9 +242,9 @@ def convert(converter, key, converted_data, errors, context): errors[key].append(e.error) return except TypeError, e: - ## hack to make sure the type error was caused by the wrong - ## number of arguments given. - if not converter.__name__ in str(e): + # hack to make sure the type error was caused by the wrong + # number of arguments given. + if converter.__name__ not in str(e): raise try: @@ -211,6 +255,7 @@ def convert(converter, key, converted_data, errors, context): errors[key].append(e.error) return + def _remove_blank_keys(schema): for key, value in schema.items(): @@ -222,6 +267,7 @@ def _remove_blank_keys(schema): return schema + def validate(data, schema, context=None): '''Validate an unflattened nested dict against a schema.''' context = context or {} @@ -250,7 +296,7 @@ def validate(data, schema, context=None): errors_unflattened = unflatten(errors) - ##remove validators that passed + # remove validators that passed dicts_to_process = [errors_unflattened] while dicts_to_process: dict_to_process = dicts_to_process.pop() @@ -265,18 +311,6 @@ def validate(data, schema, context=None): return converted_data, errors_unflattened -def validate_flattened(data, schema, context=None): - - context = context or {} - assert isinstance(data, dict) - converted_data, errors = _validate(data, schema, context) - - for key, value in errors.items(): - if not value: - errors.pop(key) - - return converted_data, errors - def _validate(data, schema, context): '''validate a flattened dict against a schema''' @@ -285,7 +319,7 @@ def _validate(data, schema, context): errors = dict((key, []) for key in full_schema) - ## before run + # before run for key in sorted(full_schema, key=flattened_order_key): if key[-1] == '__before': for converter in full_schema[key]: @@ -294,7 +328,7 @@ def _validate(data, schema, context): except StopOnError: break - ## main run + # main run for key in sorted(full_schema, key=flattened_order_key): if not key[-1].startswith('__'): for converter in full_schema[key]: @@ -303,7 +337,7 @@ def _validate(data, schema, context): except StopOnError: break - ## extras run + # extras run for key in sorted(full_schema, key=flattened_order_key): if key[-1] == '__extras': for converter in full_schema[key]: @@ -312,7 +346,7 @@ def _validate(data, schema, context): except StopOnError: break - ## after run + # after run for key in reversed(sorted(full_schema, key=flattened_order_key)): if key[-1] == '__after': for converter in full_schema[key]: @@ -321,11 +355,12 @@ def _validate(data, schema, context): except StopOnError: break - ## junk + # junk if ('__junk',) in full_schema: for converter in full_schema[('__junk',)]: try: - convert(converter, ('__junk',), converted_data, errors, context) + convert(converter, ('__junk',), converted_data, errors, + context) except StopOnError: break @@ -346,8 +381,9 @@ def flatten_list(data, flattened=None, old_key=None): return flattened + def flatten_dict(data, flattened=None, old_key=None): - '''flatten a dict''' + '''Flatten a dict''' flattened = flattened or {} old_key = old_key or [] @@ -397,8 +433,8 @@ def unflatten(data): current_pos = unflattened if (len(flattend_key) > 1 - and not flattend_key[0] in convert_to_list - and not flattend_key[0] in unflattened): + and not flattend_key[0] in convert_to_list + and not flattend_key[0] in unflattened): convert_to_list.append(flattend_key[0]) for key in flattend_key[:-1]: @@ -411,7 +447,8 @@ def unflatten(data): current_pos[flattend_key[-1]] = data[flattend_key] for key in convert_to_list: - unflattened[key] = [unflattened[key][s] for s in sorted(unflattened[key])] + unflattened[key] = [unflattened[key][s] + for s in sorted(unflattened[key])] return unflattened diff --git a/ckan/lib/plugins.py b/ckan/lib/plugins.py index 0f0067bb901..dfb4df0c39f 100644 --- a/ckan/lib/plugins.py +++ b/ckan/lib/plugins.py @@ -243,6 +243,13 @@ def plugin_validate(plugin, context, data_dict, schema, action): return toolkit.navl_validate(data_dict, schema, context) +def get_permission_labels(): + '''Return the permission label plugin (or default implementation)''' + for plugin in plugins.PluginImplementations(plugins.IPermissionLabels): + return plugin + return DefaultPermissionLabels() + + class DefaultDatasetForm(object): '''The default implementation of :py:class:`~ckan.plugins.interfaces.IDatasetForm`. @@ -574,3 +581,32 @@ def i18n_domain(self): ckanext-{extension name}, hence your pot, po and mo files should be named ckanext-{extension name}.mo''' return 'ckanext-{name}'.format(name=self.name) + + +class DefaultPermissionLabels(object): + u''' + Default permissions for package_search/package_show: + - everyone can read public datasets "public" + - users can read their own drafts "creator-(user id)" + - users can read datasets belonging to their orgs "member-(org id)" + ''' + def get_dataset_labels(self, dataset_obj): + if dataset_obj.state == u'active' and not dataset_obj.private: + return [u'public'] + + if dataset_obj.owner_org: + return [u'member-%s' % dataset_obj.owner_org] + + return [u'creator-%s' % dataset_obj.creator_user_id] + + def get_user_dataset_labels(self, user_obj): + labels = [u'public'] + if not user_obj: + return labels + + labels.append(u'creator-%s' % user_obj.id) + + orgs = logic.get_action(u'organization_list_for_user')( + {u'user': user_obj.id}, {u'permission': u'read'}) + labels.extend(u'member-%s' % o[u'id'] for o in orgs) + return labels diff --git a/ckan/lib/redis.py b/ckan/lib/redis.py new file mode 100644 index 00000000000..0e1c5f80d9b --- /dev/null +++ b/ckan/lib/redis.py @@ -0,0 +1,64 @@ +# encoding: utf-8 + +u''' +Redis utilities. + +.. versionadded:: 2.7 +''' + +from __future__ import absolute_import + +import datetime +import logging + +from redis import ConnectionPool, Redis + +from ckan.common import config + + +log = logging.getLogger(__name__) + +REDIS_URL_SETTING_NAME = u'ckan.redis.url' + +REDIS_URL_DEFAULT_VALUE = u'redis://localhost:6379/0' + +# Redis connection pool. Do not use this directly, use ``connect_to_redis`` +# instead. +_connection_pool = None + + +def connect_to_redis(): + u''' + (Lazily) connect to Redis. + + The connection is set up but not actually established. The latter + happens automatically once the connection is used. + + :returns: A lazy Redis connection. + :rtype: ``redis.Redis`` + + .. seealso:: :py:func:`is_redis_available` + ''' + global _connection_pool + if _connection_pool is None: + url = config.get(REDIS_URL_SETTING_NAME, REDIS_URL_DEFAULT_VALUE) + log.debug(u'Using Redis at {}'.format(url)) + _connection_pool = ConnectionPool.from_url(url) + return Redis(connection_pool=_connection_pool) + + +def is_redis_available(): + u''' + Check whether Redis is available. + + :returns: The availability of Redis. + :rtype: boolean + + .. seealso:: :py:func:`connect_to_redis` + ''' + redis_conn = connect_to_redis() + try: + return redis_conn.ping() + except Exception: + log.exception(u'Redis is not available') + return False diff --git a/ckan/lib/render.py b/ckan/lib/render.py index fa2507c2192..c22c35680d1 100644 --- a/ckan/lib/render.py +++ b/ckan/lib/render.py @@ -4,7 +4,7 @@ import re import logging -from pylons import config +from ckan.common import config log = logging.getLogger(__name__) diff --git a/ckan/lib/search/__init__.py b/ckan/lib/search/__init__.py index 7807f430bd5..287a216ead0 100644 --- a/ckan/lib/search/__init__.py +++ b/ckan/lib/search/__init__.py @@ -7,7 +7,6 @@ import xml.dom.minidom import urllib2 -from pylons import config from paste.deploy.converters import asbool import ckan.model as model @@ -23,7 +22,6 @@ log = logging.getLogger(__name__) - def text_traceback(): with warnings.catch_warnings(): warnings.simplefilter("ignore") @@ -32,9 +30,8 @@ def text_traceback(): ).strip() return res -SIMPLE_SEARCH = asbool(config.get('ckan.simple_search', False)) -SUPPORTED_SCHEMA_VERSIONS = ['2.3'] +SUPPORTED_SCHEMA_VERSIONS = ['2.7'] DEFAULT_OPTIONS = { 'limit': 20, @@ -60,11 +57,6 @@ def text_traceback(): SOLR_SCHEMA_FILE_OFFSET = '/admin/file/?file=schema.xml' -if SIMPLE_SEARCH: - import sql as sql - _INDICES['package'] = NoopSearchIndex - _QUERIES['package'] = sql.PackageSearchQuery - def _normalize_type(_type): if isinstance(_type, model.domain_object.DomainObject): @@ -219,6 +211,7 @@ def commit(): package_index.commit() log.info('Commited pending changes on the search index') + def check(): package_query = query_for(model.Package) @@ -249,10 +242,9 @@ def clear(package_reference): def clear_all(): - if not SIMPLE_SEARCH: - package_index = index_for(model.Package) - log.debug("Clearing search index...") - package_index.clear() + package_index = index_for(model.Package) + log.debug("Clearing search index...") + package_index.clear() def check_solr_schema_version(schema_file=None): @@ -276,11 +268,6 @@ def check_solr_schema_version(schema_file=None): be only used for testing purposes (Default is None) ''' - - if SIMPLE_SEARCH: - # Not using the SOLR search backend - return False - if not is_available(): # Something is wrong with the SOLR server log.warn('Problems were found while connecting to the SOLR server') diff --git a/ckan/lib/search/index.py b/ckan/lib/search/index.py index 5bdf82c0796..937dbefeeff 100644 --- a/ckan/lib/search/index.py +++ b/ckan/lib/search/index.py @@ -11,7 +11,7 @@ import re import pysolr -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool from common import SearchIndexError, make_connection @@ -279,6 +279,13 @@ def index_package(self, pkg_dict, defer_commit=False): assert pkg_dict, 'Plugin must return non empty package dict on index' + # permission labels determine visibility in search, can't be set + # in original dataset or before_index plugins + labels = lib_plugins.get_permission_labels() + dataset = model.Package.get(pkg_dict['id']) + pkg_dict['permission_labels'] = labels.get_dataset_labels( + dataset) if dataset else [] # TestPackageSearchIndex-workaround + # send to solr: try: conn = make_connection() diff --git a/ckan/lib/search/query.py b/ckan/lib/search/query.py index 15607e0baa7..6ec64588cab 100644 --- a/ckan/lib/search/query.py +++ b/ckan/lib/search/query.py @@ -3,7 +3,7 @@ import re import logging -from pylons import config +from ckan.common import config import pysolr from paste.deploy.converters import asbool from paste.util.multidict import MultiDict @@ -280,12 +280,17 @@ def get_index(self,reference): return solr_response.docs[0] - def run(self, query): + def run(self, query, permission_labels=None, **kwargs): ''' Performs a dataset search using the given query. - @param query - dictionary with keys like: q, fq, sort, rows, facet - @return - dictionary with keys results and count + :param query: dictionary with keys like: q, fq, sort, rows, facet + :type query: dict + :param permission_labels: filter results to those that include at + least one of these labels. None to not filter (return everything) + :type permission_labels: list of unicode strings; or None + + :returns: dictionary with keys results and count May raise SearchQueryError or SearchError. ''' @@ -310,18 +315,23 @@ def run(self, query): rows_to_query = rows_to_return query['rows'] = rows_to_query + fq = [] + if 'fq' in query: + fq.append(query['fq']) + fq.extend(query.get('fq_list', [])) + # show only results from this CKAN instance - fq = query.get('fq', '') - if not '+site_id:' in fq: - fq += ' +site_id:"%s"' % config.get('ckan.site_id') + fq.append('+site_id:%s' % solr_literal(config.get('ckan.site_id'))) # filter for package status - if not '+state:' in fq: - fq += " +state:active" - query['fq'] = [fq] + if not '+state:' in query.get('fq', ''): + fq.append('+state:active') - fq_list = query.get('fq_list', []) - query['fq'].extend(fq_list) + # only return things we should be able to see + if permission_labels is not None: + fq.append('+permission_labels:(%s)' % ' OR '.join( + solr_literal(p) for p in permission_labels)) + query['fq'] = fq # faceting query['facet'] = query.get('facet', 'true') @@ -387,3 +397,13 @@ def run(self, query): self.facets[field] = dict(zip(values[0::2], values[1::2])) return {'results': self.results, 'count': self.count} + + +def solr_literal(t): + ''' + return a safe literal string for a solr query. Instead of escaping + each of + - && || ! ( ) { } [ ] ^ " ~ * ? : \ / we're just dropping + double quotes -- this method currently only used by tokens like site_id + and permission labels. + ''' + return u'"' + t.replace(u'"', u'') + u'"' diff --git a/ckan/lib/search/sql.py b/ckan/lib/search/sql.py deleted file mode 100644 index 6db022815b9..00000000000 --- a/ckan/lib/search/sql.py +++ /dev/null @@ -1,42 +0,0 @@ -# encoding: utf-8 - -from sqlalchemy import or_ -from ckan.lib.search.query import SearchQuery -import ckan.model as model - -class PackageSearchQuery(SearchQuery): - def get_all_entity_ids(self, max_results=100): - """ - Return a list of the IDs of all indexed packages. - """ - # could make this a pure sql query which would be much more efficient! - q = model.Session.query(model.Package).filter_by(state='active').limit(max_results) - - return [r.id for r in q] - - def run(self, query): - assert isinstance(query, dict) - # no support for faceting atm - self.facets = {} - limit = min(1000, int(query.get('rows', 10))) - - q = query.get('q') - ourq = model.Session.query(model.Package.id).filter_by(state='active') - - def makelike(field): - _attr = getattr(model.Package, field) - return _attr.ilike('%' + term + '%') - if q and q not in ('""', "''", '*:*'): - terms = q.split() - # TODO: tags ...? - fields = ['name', 'title', 'notes'] - for term in terms: - args = [makelike(field) for field in fields] - subq = or_(*args) - ourq = ourq.filter(subq) - self.count = ourq.count() - ourq = ourq.limit(limit) - self.results = [{'id': r[0]} for r in ourq.all()] - - return {'results': self.results, 'count': self.count} - diff --git a/ckan/lib/uploader.py b/ckan/lib/uploader.py index 9164cdbb1f2..d65d15a3888 100644 --- a/ckan/lib/uploader.py +++ b/ckan/lib/uploader.py @@ -2,15 +2,14 @@ import os import cgi -import pylons import datetime import logging import ckan.lib.munge as munge import ckan.logic as logic import ckan.plugins as plugins +from ckan.common import config -config = pylons.config log = logging.getLogger(__name__) _storage_path = None diff --git a/ckan/lib/util.py b/ckan/lib/util.py deleted file mode 100644 index 3cf19c348d1..00000000000 --- a/ckan/lib/util.py +++ /dev/null @@ -1,47 +0,0 @@ -# encoding: utf-8 - -'''Shared utility functions for any Python code to use. - -Unlike :py:mod:`ckan.lib.helpers`, the functions in this module are not -available to templates. - -''' -import subprocess - - -# We implement our own check_output() function because -# subprocess.check_output() isn't in Python 2.6. -# This code is copy-pasted from Python 2.7 and adapted to make it work with -# Python 2.6. -# http://hg.python.org/cpython/file/d37f963394aa/Lib/subprocess.py#l544 -def check_output(*popenargs, **kwargs): - r"""Run command with arguments and return its output as a byte string. - - If the exit code was non-zero it raises a CalledProcessError. The - CalledProcessError object will have the return code in the returncode - attribute and output in the output attribute. - - The arguments are the same as for the Popen constructor. Example: - - >>> check_output(["ls", "-l", "/dev/null"]) - 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' - - The stdout argument is not allowed as it is used internally. - To capture standard error in the result, use stderr=STDOUT. - - >>> check_output(["/bin/sh", "-c", - ... "ls -l non_existent_file ; exit 0"], - ... stderr=STDOUT) - 'ls: non_existent_file: No such file or directory\n' - """ - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - raise subprocess.CalledProcessError(retcode, cmd) - return output diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index 264fe0ca2ee..799f1064430 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -7,7 +7,6 @@ import re from socket import error as socket_error -from pylons import config import paste.deploy.converters from sqlalchemy import func @@ -25,7 +24,7 @@ import ckan.lib.mailer as mailer import ckan.lib.datapreview -from ckan.common import _ +from ckan.common import _, config # FIXME this looks nasty and should be shared better from ckan.logic.action.update import _update_package_relationship @@ -197,6 +196,7 @@ def package_create(context, data_dict): context_org_update = context.copy() context_org_update['ignore_auth'] = True context_org_update['defer_commit'] = True + context_org_update['add_revision'] = False _get_action('package_owner_org_update')(context_org_update, {'id': pkg.id, 'organization_id': pkg.owner_org}) @@ -1372,6 +1372,9 @@ def _group_or_org_member_create(context, data_dict, is_org=False): schema = ckan.logic.schema.member_schema() data, errors = _validate(data_dict, schema, context) + if errors: + model.Session.rollback() + raise ValidationError(errors) username = _get_or_bust(data_dict, 'username') role = data_dict.get('role') diff --git a/ckan/logic/action/delete.py b/ckan/logic/action/delete.py index 87f9050a4d1..e4529d5ff0a 100644 --- a/ckan/logic/action/delete.py +++ b/ckan/logic/action/delete.py @@ -2,8 +2,11 @@ '''API functions for deleting data from CKAN.''' +import logging + import sqlalchemy as sqla +import ckan.lib.jobs as jobs import ckan.logic import ckan.logic.action import ckan.plugins as plugins @@ -12,6 +15,9 @@ from ckan.common import _ + +log = logging.getLogger('ckan.logic') + validate = ckan.lib.navl.dictization_functions.validate # Define some shortcuts @@ -117,6 +123,8 @@ def dataset_purge(context, data_dict): :type id: string ''' + from sqlalchemy import or_ + model = context['model'] id = _get_or_bust(data_dict, 'id') @@ -134,6 +142,11 @@ def dataset_purge(context, data_dict): for m in members.all(): m.purge() + for r in model.Session.query(model.PackageRelationship).filter( + or_(model.PackageRelationship.subject_package_id == pkg.id, + model.PackageRelationship.object_package_id == pkg.id)).all(): + r.purge() + pkg = model.Package.get(id) # no new_revision() needed since there are no object_revisions created # during purge @@ -694,3 +707,48 @@ def unfollow_group(context, data_dict): ckan.logic.schema.default_follow_group_schema()) _unfollow(context, data_dict, schema, context['model'].UserFollowingGroup) + + +@ckan.logic.validate(ckan.logic.schema.job_clear_schema) +def job_clear(context, data_dict): + '''Clear background job queues. + + Does not affect jobs that are already being processed. + + :param list queues: The queues to clear. If not given then ALL + queues are cleared. + + :returns: The cleared queues. + :rtype: list + + .. versionadded:: 2.7 + ''' + _check_access(u'job_clear', context, data_dict) + queues = data_dict.get(u'queues') + if queues: + queues = [jobs.get_queue(q) for q in queues] + else: + queues = jobs.get_all_queues() + names = [jobs.remove_queue_name_prefix(queue.name) for queue in queues] + for queue, name in zip(queues, names): + queue.empty() + log.info(u'Cleared background job queue "{}"'.format(name)) + return names + + +def job_cancel(context, data_dict): + '''Cancel a queued background job. + + Removes the job from the queue and deletes it. + + :param string id: The ID of the background job. + + .. versionadded:: 2.7 + ''' + _check_access(u'job_cancel', context, data_dict) + id = _get_or_bust(data_dict, u'id') + try: + jobs.job_from_id(id).delete() + log.info(u'Cancelled background job {}'.format(id)) + except KeyError: + raise NotFound diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 20d765e5654..5f583a496fa 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -8,7 +8,7 @@ import datetime import socket -from pylons import config +from ckan.common import config import sqlalchemy from paste.deploy.converters import asbool @@ -17,6 +17,7 @@ import ckan.logic.action import ckan.logic.schema import ckan.lib.dictization.model_dictize as model_dictize +import ckan.lib.jobs as jobs import ckan.lib.navl.dictization_functions import ckan.model as model import ckan.model.misc as misc @@ -180,10 +181,9 @@ def current_package_list_with_resources(context, data_dict): _check_access('current_package_list_with_resources', context, data_dict) - is_sysadmin = authz.is_sysadmin(user) - q = '+capacity:public' if not is_sysadmin else '*:*' - context['ignore_capacity_check'] = True - search = package_search(context, {'q': q, 'rows': limit, 'start': offset}) + search = package_search(context, { + 'q': '', 'rows': limit, 'start': offset, + 'include_private': authz.is_sysadmin(user) }) return search.get('results', []) @@ -1114,6 +1114,8 @@ def resource_view_list(context, data_dict): def resource_status_show(context, data_dict): '''Return the statuses of a resource's tasks. + This function is DEPRECATED. + :param id: the id of the resource :type id: string @@ -1453,8 +1455,9 @@ def user_show(context, data_dict): search_dict = {'rows': 50} if include_private_and_draft_datasets: - context['ignore_capacity_check'] = True - search_dict.update({'include_drafts': True}) + search_dict.update({ + 'include_private': True, + 'include_drafts': True}) search_dict.update({'fq': fq}) @@ -1711,6 +1714,10 @@ def package_search(context, data_dict): sysadmin will be returned all draft datasets. Optional, the default is ``False``. :type include_drafts: boolean + :param include_private: if ``True``, private datasets will be included in + the results. Only private datasets from the user's organizations will + be returned and sysadmins will be returned all private datasets. + Optional, the default is ``False``. :param use_default_schema: use default package schema instead of a custom schema defined with an IDatasetForm plugin (default: False) :type use_default_schema: bool @@ -1823,35 +1830,27 @@ def package_search(context, data_dict): # return a list of package ids data_dict['fl'] = 'id {0}'.format(data_source) - # If this query hasn't come from a controller that has set this flag - # then we should remove any mention of capacity from the fq and - # instead set it to only retrieve public datasets - fq = data_dict.get('fq', '') - if not context.get('ignore_capacity_check', False): - fq = ' '.join(p for p in fq.split() if 'capacity:' not in p) - data_dict['fq'] = fq + ' capacity:"public"' - - # Solr doesn't need 'include_drafts`, so pop it. - include_drafts = data_dict.pop('include_drafts', False) - fq = data_dict.get('fq', '') + # Remove before these hit solr FIXME: whitelist instead + include_private = asbool(data_dict.pop('include_private', False)) + include_drafts = asbool(data_dict.pop('include_drafts', False)) + data_dict.setdefault('fq', '') + if not include_private: + data_dict['fq'] += ' +capacity:public' if include_drafts: - user_id = authz.get_user_id_for_username(user, allow_none=True) - if authz.is_sysadmin(user): - data_dict['fq'] = fq + ' +state:(active OR draft)' - elif user_id: - # Query to return all active datasets, and all draft datasets - # for this user. - data_dict['fq'] = fq + \ - ' ((creator_user_id:{0} AND +state:(draft OR active))' \ - ' OR state:active)'.format(user_id) - elif not authz.is_sysadmin(user): - data_dict['fq'] = fq + ' +state:active' + data_dict['fq'] += ' +state:(active OR draft)' # Pop these ones as Solr does not need them extras = data_dict.pop('extras', None) + # enforce permission filter based on user + if context.get('ignore_auth') or (user and authz.is_sysadmin(user)): + labels = None + else: + labels = lib_plugins.get_permission_labels( + ).get_user_dataset_labels(context['auth_user_obj']) + query = search.query_for(model.Package) - query.run(data_dict) + query.run(data_dict, permission_labels=labels) # Add them back so extensions can use them on after_search data_dict['extras'] = extras @@ -3256,7 +3255,8 @@ def _group_or_org_followee_list(context, data_dict, is_org=False): @logic.validate(logic.schema.default_pagination_schema) def dashboard_activity_list(context, data_dict): - '''Return the authorized user's dashboard activity stream. + '''Return the authorized (via login or API key) user's dashboard activity + stream. Unlike the activity dictionaries returned by other ``*_activity_list`` actions, these activity dictionaries have an extra boolean value with key @@ -3310,13 +3310,12 @@ def dashboard_activity_list(context, data_dict): @logic.validate(ckan.logic.schema.default_pagination_schema) def dashboard_activity_list_html(context, data_dict): - '''Return the authorized user's dashboard activity stream as HTML. + '''Return the authorized (via login or API key) user's dashboard activity + stream as HTML. The activity stream is rendered as a snippet of HTML meant to be included in an HTML page, i.e. it doesn't have any HTML header or footer. - :param id: the id or name of the user - :type id: string :param offset: where to start getting activity items from (optional, default: 0) :type offset: int @@ -3479,3 +3478,46 @@ def config_option_list(context, data_dict): schema = ckan.logic.schema.update_configuration_schema() return schema.keys() + + +@logic.validate(logic.schema.job_list_schema) +def job_list(context, data_dict): + '''List enqueued background jobs. + + :param list queues: Queues to list jobs from. If not given then the + jobs from all queues are listed. + + :returns: The currently enqueued background jobs. + :rtype: list + + .. versionadded:: 2.7 + ''' + _check_access(u'job_list', context, data_dict) + dictized_jobs = [] + queues = data_dict.get(u'queues') + if queues: + queues = [jobs.get_queue(q) for q in queues] + else: + queues = jobs.get_all_queues() + for queue in queues: + for job in queue.jobs: + dictized_jobs.append(jobs.dictize_job(job)) + return dictized_jobs + + +def job_show(context, data_dict): + '''Show details for a background job. + + :param string id: The ID of the background job. + + :returns: Details about the background job. + :rtype: dict + + .. versionadded:: 2.7 + ''' + _check_access(u'job_show', context, data_dict) + id = _get_or_bust(data_dict, u'id') + try: + return jobs.dictize_job(jobs.job_from_id(id)) + except KeyError: + raise NotFound diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index ae8bf610559..667021cb14e 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -7,7 +7,7 @@ import time import json -from pylons import config +from ckan.common import config import paste.deploy.converters as converters import ckan.plugins as plugins @@ -292,6 +292,7 @@ def package_update(context, data_dict): context_org_update = context.copy() context_org_update['ignore_auth'] = True context_org_update['defer_commit'] = True + context_org_update['add_revision'] = False _get_action('package_owner_org_update')(context_org_update, {'id': pkg.id, 'organization_id': pkg.owner_org}) @@ -385,22 +386,23 @@ def _update_package_relationship(relationship, comment, context): ref_package_by=ref_package_by) return rel_dict + def package_relationship_update(context, data_dict): '''Update a relationship between two datasets (packages). + The subject, object and type parameters are required to identify the + relationship. Only the comment can be updated. + You must be authorized to edit both the subject and the object datasets. - :param id: the id of the package relationship to update - :type id: string :param subject: the name or id of the dataset that is the subject of the - relationship (optional) + relationship :type subject: string :param object: the name or id of the dataset that is the object of the - relationship (optional) + relationship :param type: the type of the relationship, one of ``'depends_on'``, ``'dependency_of'``, ``'derives_from'``, ``'has_derivation'``, ``'links_to'``, ``'linked_from'``, ``'child_of'`` or ``'parent_of'`` - (optional) :type type: string :param comment: a comment about the relationship (optional) :type comment: string @@ -410,7 +412,8 @@ def package_relationship_update(context, data_dict): ''' model = context['model'] - schema = context.get('schema') or schema_.default_update_relationship_schema() + schema = context.get('schema') \ + or schema_.default_update_relationship_schema() id, id2, rel = _get_or_bust(data_dict, ['subject', 'object', 'type']) @@ -1020,6 +1023,7 @@ def package_owner_org_update(context, data_dict): :type id: string ''' model = context['model'] + user = context['user'] name_or_id = data_dict.get('id') organization_id = data_dict.get('organization_id') @@ -1039,10 +1043,17 @@ def package_owner_org_update(context, data_dict): org = None pkg.owner_org = None + if context.get('add_revision', True): + rev = model.repo.new_revision() + rev.author = user + if 'message' in context: + rev.message = context['message'] + else: + rev.message = _(u'REST API: Update object %s') % pkg.get("name") members = model.Session.query(model.Member) \ - .filter(model.Member.table_id == pkg.id) \ - .filter(model.Member.capacity == 'organization') + .filter(model.Member.table_id == pkg.id) \ + .filter(model.Member.capacity == 'organization') need_update = True for member_obj in members: @@ -1073,16 +1084,16 @@ def _bulk_update_dataset(context, data_dict, update_dict): org_id = data_dict.get('org_id') model = context['model'] - model.Session.query(model.package_table) \ .filter(model.Package.id.in_(datasets)) \ .filter(model.Package.owner_org == org_id) \ .update(update_dict, synchronize_session=False) # revisions - model.Session.query(model.package_table) \ - .filter(model.Package.id.in_(datasets)) \ - .filter(model.Package.owner_org == org_id) \ + model.Session.query(model.package_revision_table) \ + .filter(model.PackageRevision.id.in_(datasets)) \ + .filter(model.PackageRevision.owner_org == org_id) \ + .filter(model.PackageRevision.current is True) \ .update(update_dict, synchronize_session=False) model.Session.commit() @@ -1237,7 +1248,7 @@ def config_option_update(context, data_dict): # Save value in database model.set_system_info(key, value) - # Update the pylons `config` object + # Update CKAN's `config` object config[key] = value # Only add it to the app_globals (`g`) object if explicitly defined diff --git a/ckan/logic/auth/delete.py b/ckan/logic/auth/delete.py index ff1a52044bc..4efd48300ad 100644 --- a/ckan/logic/auth/delete.py +++ b/ckan/logic/auth/delete.py @@ -137,3 +137,13 @@ def organization_member_delete(context, data_dict): def member_delete(context, data_dict): return authz.is_authorized('member_create', context, data_dict) + + +def job_clear(context, data_dict): + '''Clear background jobs. Only sysadmins.''' + return {'success': False} + + +def job_cancel(context, data_dict): + '''Cancel a background job. Only sysadmins.''' + return {'success': False} diff --git a/ckan/logic/auth/get.py b/ckan/logic/auth/get.py index 011b19c2e84..8a12d7fdaf3 100644 --- a/ckan/logic/auth/get.py +++ b/ckan/logic/auth/get.py @@ -5,6 +5,7 @@ from ckan.lib.base import _ from ckan.logic.auth import (get_package_object, get_group_object, get_resource_object) +from ckan.lib.plugins import get_permission_labels def sysadmin(context, data_dict): @@ -112,22 +113,15 @@ def package_relationships_list(context, data_dict): def package_show(context, data_dict): user = context.get('user') package = get_package_object(context, data_dict) - # draft state indicates package is still in the creation process - # so we need to check we have creation rights. - if package.state.startswith('draft'): - auth = authz.is_authorized('package_update', - context, data_dict) - authorized = auth.get('success') - elif package.owner_org is None and package.state == 'active': - return {'success': True} - else: - # anyone can see a public package - if not package.private and package.state == 'active': - return {'success': True} - authorized = authz.has_user_permission_for_group_or_org( - package.owner_org, user, 'read') + labels = get_permission_labels() + user_labels = labels.get_user_dataset_labels(context['auth_user_obj']) + authorized = any( + dl in user_labels for dl in labels.get_dataset_labels(package)) + if not authorized: - return {'success': False, 'msg': _('User %s not authorized to read package %s') % (user, package.id)} + return { + 'success': False, + 'msg': _('User %s not authorized to read package %s') % (user, package.id)} else: return {'success': True} @@ -343,3 +337,13 @@ def config_option_show(context, data_dict): def config_option_list(context, data_dict): '''List runtime-editable configuration options. Only sysadmins.''' return {'success': False} + + +def job_list(context, data_dict): + '''List background jobs. Only sysadmins.''' + return {'success': False} + + +def job_show(context, data_dict): + '''Show background job. Only sysadmins.''' + return {'success': False} diff --git a/ckan/logic/schema.py b/ckan/logic/schema.py index b16c914f28a..259c74cdc8c 100644 --- a/ckan/logic/schema.py +++ b/ckan/logic/schema.py @@ -55,8 +55,10 @@ resource_id_exists, tag_not_in_vocabulary, group_id_exists, + group_id_or_name_exists, owner_org_validator, user_name_exists, + user_id_or_name_exists, role_exists, datasets_with_no_organization_cannot_be_private, list_of_strings, @@ -520,7 +522,8 @@ def default_create_activity_schema(): def default_follow_user_schema(): schema = {'id': [not_missing, not_empty, unicode, - convert_user_name_or_id_to_id]} + convert_user_name_or_id_to_id], + 'q': [ignore_missing]} return schema @@ -532,9 +535,9 @@ def default_follow_dataset_schema(): def member_schema(): schema = { - 'id': [group_id_exists, unicode], - 'username': [user_name_exists, unicode], - 'role': [role_exists, unicode], + 'id': [not_missing, group_id_or_name_exists, unicode], + 'username': [not_missing, user_id_or_name_exists, unicode], + 'role': [not_missing, role_exists, unicode], } return schema @@ -616,7 +619,7 @@ def create_schema_for_required_keys(keys): ''' helper function that creates a schema definition where each key from keys is validated against ``not_missing``. ''' - schema = dict([(x, [not_missing]) for x in keys]) + schema = {x: [not_missing] for x in keys} return schema @@ -698,3 +701,15 @@ def update_configuration_schema(): schema = plugin.update_config_schema(schema) return schema + + +def job_list_schema(): + return { + u'queues': [ignore_missing, list_of_strings], + } + + +def job_clear_schema(): + return { + u'queues': [ignore_missing, list_of_strings], + } diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index c41bd23f5d1..ad8a91c5a8c 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -29,7 +29,7 @@ def owner_org_validator(key, data, errors, context): if value is missing or value is None: if not authz.check_config_permission('create_unowned_dataset'): - raise Invalid(_('A organization must be supplied')) + raise Invalid(_('An organization must be provided')) data.pop(key, None) raise df.StopOnError @@ -38,7 +38,7 @@ def owner_org_validator(key, data, errors, context): user = model.User.get(user) if value == '': if not authz.check_config_permission('create_unowned_dataset'): - raise Invalid(_('A organization must be supplied')) + raise Invalid(_('An organization must be provided')) return group = model.Group.get(value) diff --git a/ckan/migration/versions/081_set_datastore_active.py b/ckan/migration/versions/081_set_datastore_active.py index 9038450d887..a8219800778 100644 --- a/ckan/migration/versions/081_set_datastore_active.py +++ b/ckan/migration/versions/081_set_datastore_active.py @@ -4,7 +4,7 @@ from sqlalchemy import create_engine from sqlalchemy.sql import text from sqlalchemy.exc import SQLAlchemyError -from pylons import config +from ckan.common import config def upgrade(migrate_engine): diff --git a/ckan/migration/versions/085_adjust_activity_timestamps.py b/ckan/migration/versions/085_adjust_activity_timestamps.py new file mode 100644 index 00000000000..ddcea2ece31 --- /dev/null +++ b/ckan/migration/versions/085_adjust_activity_timestamps.py @@ -0,0 +1,23 @@ +# encoding: utf-8 + +import datetime + + +def upgrade(migrate_engine): + u""" + The script assumes that the current timestamp was + recorded with the server's current set timezone + """ + # choose a fixed date (within DST) so migration depends only on + # server time zone not the current daylight savings state + magic_timestamp = datetime.datetime(2016, 6, 20).toordinal() + + utc_date = datetime.datetime.utcfromtimestamp(magic_timestamp) + local_date = datetime.datetime.fromtimestamp(magic_timestamp) + + if utc_date == local_date: + return + + with migrate_engine.begin() as connection: + sql = u"update activity set timestamp = timestamp + (%s - %s);" + connection.execute(sql, utc_date, local_date) diff --git a/ckan/model/activity.py b/ckan/model/activity.py index cfd6d02e2a5..fdfa52660cc 100644 --- a/ckan/model/activity.py +++ b/ckan/model/activity.py @@ -40,7 +40,7 @@ class Activity(domain_object.DomainObject): def __init__(self, user_id, object_id, revision_id, activity_type, data=None): self.id = _types.make_uuid() - self.timestamp = datetime.datetime.now() + self.timestamp = datetime.datetime.utcnow() self.user_id = user_id self.object_id = object_id self.revision_id = revision_id diff --git a/ckan/model/extension.py b/ckan/model/extension.py index b471534a45a..64c68c8f1fa 100644 --- a/ckan/model/extension.py +++ b/ckan/model/extension.py @@ -4,23 +4,17 @@ Provides bridges between the model and plugin PluginImplementationss """ import logging +from operator import methodcaller from sqlalchemy.orm.interfaces import MapperExtension from sqlalchemy.orm.session import SessionExtension import ckan.plugins as plugins -try: - from operator import methodcaller -except ImportError: - def methodcaller(name, *args, **kwargs): - "Replaces stdlib operator.methodcaller in python <2.6" - def caller(obj): - return getattr(obj, name)(*args, **kwargs) - return caller log = logging.getLogger(__name__) + class ObserverNotifier(object): """ Mixin for hooking into SQLAlchemy @@ -93,7 +87,6 @@ def notify_observers(self, func): for observer in plugins.PluginImplementations(plugins.ISession): func(observer) - def after_begin(self, session, transaction, connection): return self.notify_observers( methodcaller('after_begin', session, transaction, connection) @@ -123,4 +116,3 @@ def after_rollback(self, session): return self.notify_observers( methodcaller('after_rollback', session) ) - diff --git a/ckan/model/license.py b/ckan/model/license.py index 6395f79d4ab..0f2f2b9279e 100644 --- a/ckan/model/license.py +++ b/ckan/model/license.py @@ -4,7 +4,7 @@ import urllib2 import re -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool from ckan.common import _, json diff --git a/ckan/model/meta.py b/ckan/model/meta.py index 7de9773bee5..e4a7429d99f 100644 --- a/ckan/model/meta.py +++ b/ckan/model/meta.py @@ -3,7 +3,7 @@ import datetime from paste.deploy.converters import asbool -from pylons import config +from ckan.common import config """SQLAlchemy Metadata and Session object""" from sqlalchemy import MetaData, and_ import sqlalchemy.orm as orm diff --git a/ckan/model/package.py b/ckan/model/package.py index 60eab5fadef..9ef68277330 100644 --- a/ckan/model/package.py +++ b/ckan/model/package.py @@ -8,7 +8,7 @@ from sqlalchemy.sql import select, and_, union, or_ from sqlalchemy import orm from sqlalchemy import types, Column, Table -from pylons import config +from ckan.common import config import vdm.sqlalchemy import meta @@ -204,7 +204,7 @@ def as_dict(self, ref_package_by='name', ref_group_by='name'): groups = [getattr(group, ref_group_by) for group in self.get_groups()] groups.sort() _dict['groups'] = groups - _dict['extras'] = dict([(key, value) for key, value in self.extras.items()]) + _dict['extras'] = {key: value for key, value in self.extras.items()} _dict['ratings_average'] = self.get_average_rating() _dict['ratings_count'] = len(self.ratings) _dict['resources'] = [res.as_dict(core_columns_only=False) \ diff --git a/ckan/model/resource.py b/ckan/model/resource.py index c7093576953..03a8b512015 100644 --- a/ckan/model/resource.py +++ b/ckan/model/resource.py @@ -5,7 +5,7 @@ from sqlalchemy.util import OrderedDict from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy import orm -from pylons import config +from ckan.common import config import vdm.sqlalchemy import vdm.sqlalchemy.stateful from sqlalchemy import types, func, Column, Table, ForeignKey, and_ diff --git a/ckan/pastertemplates/template/+dot+travis.yml_tmpl b/ckan/pastertemplates/template/+dot+travis.yml_tmpl index 536ae829969..cc4b3d7cd1f 100644 --- a/ckan/pastertemplates/template/+dot+travis.yml_tmpl +++ b/ckan/pastertemplates/template/+dot+travis.yml_tmpl @@ -1,7 +1,6 @@ language: python sudo: required python: - - "2.6" - "2.7" env: PGVERSION=9.1 install: diff --git a/ckan/pastertemplates/template/MANIFEST.in_tmpl b/ckan/pastertemplates/template/MANIFEST.in_tmpl index dc63f8f739d..204b4d35dc8 100644 --- a/ckan/pastertemplates/template/MANIFEST.in_tmpl +++ b/ckan/pastertemplates/template/MANIFEST.in_tmpl @@ -1,2 +1,4 @@ include README.rst +include LICENSE +include requirements.txt recursive-include ckanext/{{ project_shortname }} *.html *.json *.js *.less *.css *.mo diff --git a/ckan/pastertemplates/template/requirements.txt_tmpl b/ckan/pastertemplates/template/requirements.txt_tmpl new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ckan/pastertemplates/template/setup.py_tmpl b/ckan/pastertemplates/template/setup.py_tmpl index 921035fefd2..d6dafcc4cd5 100644 --- a/ckan/pastertemplates/template/setup.py_tmpl +++ b/ckan/pastertemplates/template/setup.py_tmpl @@ -43,7 +43,6 @@ setup( # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', ], @@ -56,11 +55,12 @@ setup( packages=find_packages(exclude=['contrib', 'docs', 'tests*']), namespace_packages=['ckanext'], - # List run-time dependencies here. These will be installed by pip when your - # project is installed. For an analysis of "install_requires" vs pip's - # requirements files see: - # https://packaging.python.org/en/latest/technical.html#install-requires-vs-requirements-files - install_requires=[], + install_requires=[ + # CKAN extensions should not list dependencies here, but in a separate + # ``requirements.txt`` file. + # + # http://docs.ckan.org/en/latest/extensions/best-practices.html#add-third-party-libraries-to-requirements-txt + ], # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these @@ -81,8 +81,9 @@ setup( entry_points=''' [ckan.plugins] {{ project_shortname }}=ckanext.{{ project_shortname }}.plugin:{{ plugin_class_name }} - [babel.extractors] - ckan = ckan.lib.extract:extract_ckan + + [babel.extractors] + ckan = ckan.lib.extract:extract_ckan ''', # If you are changing from the default layout of your extension, you may diff --git a/ckan/plugins/core.py b/ckan/plugins/core.py index f52b8ace507..07456fc8803 100644 --- a/ckan/plugins/core.py +++ b/ckan/plugins/core.py @@ -15,6 +15,9 @@ import interfaces +from ckan.common import config + + __all__ = [ 'PluginImplementations', 'implements', 'PluginNotFoundException', 'Plugin', 'SingletonPlugin', @@ -118,7 +121,7 @@ def plugins_update(): environment.update_config() -def load_all(config): +def load_all(): ''' Load all plugins listed in the 'ckan.plugins' config directive. ''' diff --git a/ckan/plugins/interfaces.py b/ckan/plugins/interfaces.py index b2907c8076a..aa4c3c4fce4 100644 --- a/ckan/plugins/interfaces.py +++ b/ckan/plugins/interfaces.py @@ -25,7 +25,8 @@ 'IFacets', 'IAuthenticator', 'ITranslation', - 'IUploader' + 'IUploader', + 'IPermissionLabels', ] from inspect import isclass @@ -747,7 +748,7 @@ def configure(self, config): class IConfigurer(Interface): """ - Configure CKAN (pylons) environment via the ``pylons.config`` object + Configure CKAN environment via the ``config`` object """ def update_config(self, config): @@ -755,7 +756,7 @@ def update_config(self, config): Called by load_environment at earliest point when config is available to plugins. The config should be updated in place. - :param config: ``pylons.config`` object + :param config: ``config`` object """ def update_config_schema(self, schema): @@ -1568,3 +1569,43 @@ def get_resource_uploader(self): :type id: string ''' + + +class IPermissionLabels(Interface): + ''' + Extensions implementing this interface can override the permission + labels applied to datasets to precisely control which datasets are + visible to each user. + + Implementations might want to consider mixing in + ``ckan.lib.plugins.DefaultPermissionLabels`` which provides + default behaviours for these methods. + + See ``ckanext/example_ipermissionlabels`` for an example plugin. + ''' + + def get_dataset_labels(self, dataset_obj): + ''' + Return a list of unicode strings to be stored in the search index + as the permission lables for a dataset dict. + + :param dataset_obj: dataset details + :type dataset_obj: Package model object + + :returns: permission labels + :rtype: list of unicode strings + ''' + + def get_user_dataset_labels(self, user_obj): + ''' + Return the permission labels that give a user permission to view + a dataset. If any of the labels returned from this method match + any of the labels returned from :py:meth:`.get_dataset_labels` + then this user is permitted to view that dataset. + + :param user_obj: user details + :type user_obj: User model object or None + + :returns: permission labels + :rtype: list of unicode strings + ''' diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py index 06a63722a24..ac475ed5811 100644 --- a/ckan/plugins/toolkit.py +++ b/ckan/plugins/toolkit.py @@ -18,6 +18,8 @@ class _Toolkit(object): # contents should describe the available functions/objects. We check # that this list matches the actual availables in the initialisation contents = [ + # Global CKAN configuration object + 'config', # i18n translation '_', # i18n translation (plural form) @@ -94,6 +96,8 @@ class _Toolkit(object): 'auth_disallow_anonymous_access', # Helper not found error. 'HelperError', + # Enqueue background job + 'enqueue_job', # Fully defined in this file ## 'add_template_directory', @@ -132,6 +136,7 @@ def _initialize(self): CkanVersionException, HelperError ) + from ckan.lib.jobs import enqueue as enqueue_job from paste.deploy import converters import pylons @@ -144,6 +149,14 @@ def _initialize(self): t = self._toolkit # imported functions + t['config'] = common.config + self.docstring_overrides['config'] = '''The CKAN configuration object. + +It stores the configuration values defined in the :ref:`config_file`, eg:: + + title = toolkit.config.get("ckan.site_title") + +''' t['_'] = common._ self.docstring_overrides['_'] = '''The Pylons ``_()`` function. @@ -261,6 +274,7 @@ def _initialize(self): t['check_ckan_version'] = self._check_ckan_version t['CkanVersionException'] = CkanVersionException t['HelperError'] = HelperError + t['enqueue_job'] = enqueue_job # check contents list correct errors = set(t).symmetric_difference(set(self.contents)) diff --git a/ckan/public/base/css/fuchsia.css b/ckan/public/base/css/fuchsia.css index 536788526d4..2308c828e38 100644 --- a/ckan/public/base/css/fuchsia.css +++ b/ckan/public/base/css/fuchsia.css @@ -6891,7 +6891,7 @@ textarea { box-shadow: none; } .js .image-upload #field-image-url { - padding-right: 29px; + padding-right: 90px; } .js .image-upload #field-image-upload { cursor: pointer; @@ -6922,7 +6922,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; diff --git a/ckan/public/base/css/green.css b/ckan/public/base/css/green.css index 3b3143d2c02..78dddffbaeb 100644 --- a/ckan/public/base/css/green.css +++ b/ckan/public/base/css/green.css @@ -6891,7 +6891,7 @@ textarea { box-shadow: none; } .js .image-upload #field-image-url { - padding-right: 29px; + padding-right: 90px; } .js .image-upload #field-image-upload { cursor: pointer; @@ -6922,7 +6922,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index 0bb701cf594..06ff27b7b5d 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -5763,7 +5763,7 @@ a.tag:hover { margin-top: 0; } .ckanext-datapreview > iframe { - min-height: 400px; + min-height: 650px; } .ckanext-datapreview > img { max-height: 500px; @@ -6876,7 +6876,7 @@ textarea { box-shadow: none; } .js .image-upload #field-image-url { - padding-right: 29px; + padding-right: 90px; } .js .image-upload #field-image-upload { cursor: pointer; @@ -6907,7 +6907,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; diff --git a/ckan/public/base/css/maroon.css b/ckan/public/base/css/maroon.css index dbeb455f691..39aa8d02444 100644 --- a/ckan/public/base/css/maroon.css +++ b/ckan/public/base/css/maroon.css @@ -6891,7 +6891,7 @@ textarea { box-shadow: none; } .js .image-upload #field-image-url { - padding-right: 29px; + padding-right: 90px; } .js .image-upload #field-image-upload { cursor: pointer; @@ -6922,7 +6922,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; diff --git a/ckan/public/base/css/red.css b/ckan/public/base/css/red.css index cb43489b74a..9759c39445a 100644 --- a/ckan/public/base/css/red.css +++ b/ckan/public/base/css/red.css @@ -6891,7 +6891,7 @@ textarea { box-shadow: none; } .js .image-upload #field-image-url { - padding-right: 29px; + padding-right: 90px; } .js .image-upload #field-image-upload { cursor: pointer; @@ -6922,7 +6922,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; diff --git a/ckan/public/base/javascript/modules/image-upload.js b/ckan/public/base/javascript/modules/image-upload.js index 7fef17dfcc4..c53dba049e6 100644 --- a/ckan/public/base/javascript/modules/image-upload.js +++ b/ckan/public/base/javascript/modules/image-upload.js @@ -5,7 +5,7 @@ this.ckan.module('image-upload', function($, _) { return { /* options object can be extended using data-module-* attributes */ options: { - is_url: true, + is_url: false, is_upload: false, field_upload: 'image_upload', field_url: 'image_url', @@ -17,6 +17,8 @@ this.ckan.module('image-upload', function($, _) { url: _('Link'), remove: _('Remove'), upload_label: _('Image'), + label_for_url: _('URL'), + label_for_upload: _('File'), upload_tooltip: _('Upload a file on your computer'), url_tooltip: _('Link to a URL on the internet (you can also link to an API)') } @@ -47,11 +49,14 @@ this.ckan.module('image-upload', function($, _) { this.field_image = this.input.parents('.control-group'); this.field_url_input = $('input', this.field_url); this.field_name = this.el.parents('form').find(field_name); + // this is the location for the upload/link data/image label + this.label_location = $('label[for="field-image-url"]'); + // determines if the resource is a data resource + this.is_data_resource = (this.options.field_url === 'url') && (this.options.field_upload === 'upload'); // Is there a clear checkbox on the form already? var checkbox = $(field_clear, this.el); if (checkbox.length > 0) { - options.is_upload = true; checkbox.parents('.control-group').remove(); } @@ -60,7 +65,7 @@ this.ckan.module('image-upload', function($, _) { .appendTo(this.el); // Button to set the field to be a URL - this.button_url = $(' '+this.i18n('url')+'') + this.button_url = $(''+this.i18n('url')+'') .prop('title', this.i18n('url_tooltip')) .on('click', this._onFromWeb) .insertAfter(this.input); @@ -70,12 +75,12 @@ this.ckan.module('image-upload', function($, _) { .insertAfter(this.input); // Button for resetting the form when there is a URL set - $('') + $(''+this.i18n('remove')+'') .prop('title', this.i18n('remove')) .on('click', this._onRemove) .insertBefore(this.field_url_input); - // Update the main label + // Update the main label (this is displayed when no data/image has been uploaded/linked) $('label[for="field-image-upload"]').text(options.upload_label || this.i18n('upload_label')); // Setup the file input @@ -105,25 +110,71 @@ this.ckan.module('image-upload', function($, _) { if (options.is_url) { this._showOnlyFieldUrl(); + + this._updateUrlLabel(this.i18n('label_for_url')); } else if (options.is_upload) { this._showOnlyFieldUrl(); + this.field_url_input.prop('readonly', true); + // If the data is an uploaded file, the filename will display rather than whole url of the site + var filename = this._fileNameFromUpload(this.field_url_input.val()); + this.field_url_input.val(filename); + + this._updateUrlLabel(this.i18n('label_for_upload')); } else { this._showOnlyButtons(); } }, + /* Quick way of getting just the filename from the uri of the resource data + * + * url - The url of the uploaded data file + * + * Returns String. + */ + _fileNameFromUpload: function(url) { + // remove fragment (#) + url = url.substring(0, (url.indexOf("#") === -1) ? url.length : url.indexOf("#")); + // remove query string + url = url.substring(0, (url.indexOf("?") === -1) ? url.length : url.indexOf("?")); + // extract the filename + url = url.substring(url.lastIndexOf("/") + 1, url.length); + + return url; // filename + }, + + /* Update the `this.label_location` text + * + * If the upload/link is for a data resource, rather than an image, + * the text for label[for="field-image-url"] will be updated. + * + * label_text - The text for the label of an uploaded/linked resource + * + * Returns nothing. + */ + _updateUrlLabel: function(label_text) { + if (! this.is_data_resource) { + return; + } + + this.label_location.text(label_text); + }, + /* Event listener for when someone sets the field to URL mode * * Returns nothing. */ _onFromWeb: function() { this._showOnlyFieldUrl(); + this.field_url_input.focus() .on('blur', this._onFromWebBlur); + if (this.options.is_upload) { this.field_clear.val('true'); } + + this._updateUrlLabel(this.i18n('label_for_url')); }, /* Event listener for resetting the field back to the blank state @@ -132,8 +183,10 @@ this.ckan.module('image-upload', function($, _) { */ _onRemove: function() { this._showOnlyButtons(); + this.field_url_input.val(''); this.field_url_input.prop('readonly', false); + this.field_clear.val('true'); }, @@ -145,9 +198,14 @@ this.ckan.module('image-upload', function($, _) { var file_name = this.input.val().split(/^C:\\fakepath\\/).pop(); this.field_url_input.val(file_name); this.field_url_input.prop('readonly', true); + this.field_clear.val(''); + this._showOnlyFieldUrl(); + this._autoName(file_name); + + this._updateUrlLabel(this.i18n('label_for_upload')); }, /* Show only the buttons, hiding all others diff --git a/ckan/public/base/javascript/modules/resource-view-filters-form.js b/ckan/public/base/javascript/modules/resource-view-filters-form.js index aa546285b00..2a7cb3f751c 100644 --- a/ckan/public/base/javascript/modules/resource-view-filters-form.js +++ b/ckan/public/base/javascript/modules/resource-view-filters-form.js @@ -10,7 +10,7 @@ ckan.module('resource-view-filters-form', function (jQuery) { width: 'resolve', minimumInputLength: 0, ajax: { - url: '/api/3/action/datastore_search', + url: ckan.url('/api/3/action/datastore_search'), datatype: 'json', quietMillis: 200, cache: true, diff --git a/ckan/public/base/less/forms.less b/ckan/public/base/less/forms.less index 66d0901dc69..2800dde3e98 100644 --- a/ckan/public/base/less/forms.less +++ b/ckan/public/base/less/forms.less @@ -736,7 +736,7 @@ textarea { .js .image-upload { #field-image-url { - padding-right: 29px; + padding-right: 90px; } #field-image-upload { cursor: pointer; @@ -763,7 +763,7 @@ textarea { margin-right: 0; top: 4px; right: 5px; - padding: 0 4px; + padding: 0 12px; .border-radius(100px); .icon-remove { margin-right: 0; diff --git a/ckan/public/base/less/module.less b/ckan/public/base/less/module.less index 180e6815853..46a4b5c8d8e 100644 --- a/ckan/public/base/less/module.less +++ b/ckan/public/base/less/module.less @@ -170,7 +170,7 @@ margin-top: 0; & > iframe { - min-height: 400px; + min-height: 650px; } & > img { max-height: 500px; diff --git a/ckan/public/base/vendor/moment-with-locales.js b/ckan/public/base/vendor/moment-with-locales.js index 071bdf9afed..edffa0c1f0a 100644 --- a/ckan/public/base/vendor/moment-with-locales.js +++ b/ckan/public/base/vendor/moment-with-locales.js @@ -1,81 +1,673 @@ -!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return Gd.apply(null,arguments)}function b(a){Gd=a}function c(a){return"[object Array]"===Object.prototype.toString.call(a)}function d(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function e(a,b){var c,d=[];for(c=0;c0)for(c in Id)d=Id[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function n(b){m(this,b),this._d=new Date(+b._d),Jd===!1&&(Jd=!0,a.updateOffset(this),Jd=!1)}function o(a){return a instanceof n||null!=a&&null!=a._isAMomentObject}function p(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function q(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&p(a[d])!==p(b[d]))&&g++;return g+f}function r(){}function s(a){return a?a.toLowerCase().replace("_","-"):a}function t(a){for(var b,c,d,e,f=0;f0;){if(d=u(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&q(e,c,!0)>=b-1)break;b--}f++}return null}function u(a){var b=null;if(!Kd[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Hd._abbr,require("./locale/"+a),v(b)}catch(c){}return Kd[a]}function v(a,b){var c;return a&&(c="undefined"==typeof b?x(a):w(a,b),c&&(Hd=c)),Hd._abbr}function w(a,b){return null!==b?(b.abbr=a,Kd[a]||(Kd[a]=new r),Kd[a].set(b),v(a),Kd[a]):(delete Kd[a],null)}function x(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Hd;if(!c(a)){if(b=u(a))return b;a=[a]}return t(a)}function y(a,b){var c=a.toLowerCase();Ld[c]=Ld[c+"s"]=Ld[b]=a}function z(a){return"string"==typeof a?Ld[a]||Ld[a.toLowerCase()]:void 0}function A(a){var b,c,d={};for(c in a)f(a,c)&&(b=z(c),b&&(d[b]=a[c]));return d}function B(b,c){return function(d){return null!=d?(D(this,b,d),a.updateOffset(this,c),this):C(this,b)}}function C(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function D(a,b,c){return a._d["set"+(a._isUTC?"UTC":"")+b](c)}function E(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=z(a),"function"==typeof this[a])return this[a](b);return this}function F(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthb;b++)Pd[d[b]]?d[b]=Pd[d[b]]:d[b]=H(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function J(a,b){return a.isValid()?(b=K(b,a.localeData()),Od[b]||(Od[b]=I(b)),Od[b](a)):a.localeData().invalidDate()}function K(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Nd.lastIndex=0;d>=0&&Nd.test(a);)a=a.replace(Nd,c),Nd.lastIndex=0,d-=1;return a}function L(a,b,c){ce[a]="function"==typeof b?b:function(a){return a&&c?c:b}}function M(a,b){return f(ce,a)?ce[a](b._strict,b._locale):new RegExp(N(a))}function N(a){return a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function O(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=p(a)}),c=0;cd;d++){if(e=h([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function V(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),R(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function W(b){return null!=b?(V(this,b),a.updateOffset(this,!0),this):C(this,"Month")}function X(){return R(this.year(),this.month())}function Y(a){var b,c=a._a;return c&&-2===j(a).overflow&&(b=c[fe]<0||c[fe]>11?fe:c[ge]<1||c[ge]>R(c[ee],c[fe])?ge:c[he]<0||c[he]>24||24===c[he]&&(0!==c[ie]||0!==c[je]||0!==c[ke])?he:c[ie]<0||c[ie]>59?ie:c[je]<0||c[je]>59?je:c[ke]<0||c[ke]>999?ke:-1,j(a)._overflowDayOfYear&&(ee>b||b>ge)&&(b=ge),j(a).overflow=b),a}function Z(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function $(a,b){var c=!0,d=a+"\n"+(new Error).stack;return g(function(){return c&&(Z(d),c=!1),b.apply(this,arguments)},b)}function _(a,b){ne[a]||(Z(b),ne[a]=!0)}function aa(a){var b,c,d=a._i,e=oe.exec(d);if(e){for(j(a).iso=!0,b=0,c=pe.length;c>b;b++)if(pe[b][1].exec(d)){a._f=pe[b][0]+(e[6]||" ");break}for(b=0,c=qe.length;c>b;b++)if(qe[b][1].exec(d)){a._f+=qe[b][0];break}d.match(_d)&&(a._f+="Z"),ta(a)}else a._isValid=!1}function ba(b){var c=re.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(aa(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function ca(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function da(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function ea(a){return fa(a)?366:365}function fa(a){return a%4===0&&a%100!==0||a%400===0}function ga(){return fa(this.year())}function ha(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=Aa(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ia(a){return ha(a,this._week.dow,this._week.doy).week}function ja(){return this._week.dow}function ka(){return this._week.doy}function la(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function ma(a){var b=ha(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function na(a,b,c,d,e){var f,g,h=da(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:ea(a-1)+g}}function oa(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function pa(a,b,c){return null!=a?a:null!=b?b:c}function qa(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function ra(a){var b,c,d,e,f=[];if(!a._d){for(d=qa(a),a._w&&null==a._a[ge]&&null==a._a[fe]&&sa(a),a._dayOfYear&&(e=pa(a._a[ee],d[ee]),a._dayOfYear>ea(e)&&(j(a)._overflowDayOfYear=!0),c=da(e,0,a._dayOfYear),a._a[fe]=c.getUTCMonth(),a._a[ge]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[he]&&0===a._a[ie]&&0===a._a[je]&&0===a._a[ke]&&(a._nextDay=!0,a._a[he]=0),a._d=(a._useUTC?da:ca).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[he]=24)}}function sa(a){var b,c,d,e,f,g,h;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=pa(b.GG,a._a[ee],ha(Aa(),1,4).year),d=pa(b.W,1),e=pa(b.E,1)):(f=a._locale._week.dow,g=a._locale._week.doy,c=pa(b.gg,a._a[ee],ha(Aa(),f,g).year),d=pa(b.w,1),null!=b.d?(e=b.d,f>e&&++d):e=null!=b.e?b.e+f:f),h=na(c,d,e,g,f),a._a[ee]=h.year,a._dayOfYear=h.dayOfYear}function ta(b){if(b._f===a.ISO_8601)return void aa(b);b._a=[],j(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,k=0;for(e=K(b._f,b._locale).match(Md)||[],c=0;c0&&j(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),k+=d.length),Pd[f]?(d?j(b).empty=!1:j(b).unusedTokens.push(f),Q(f,d,b)):b._strict&&!d&&j(b).unusedTokens.push(f);j(b).charsLeftOver=i-k,h.length>0&&j(b).unusedInput.push(h),j(b).bigHour===!0&&b._a[he]<=12&&b._a[he]>0&&(j(b).bigHour=void 0),b._a[he]=ua(b._locale,b._a[he],b._meridiem),ra(b),Y(b)}function ua(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function va(a){var b,c,d,e,f;if(0===a._f.length)return j(a).invalidFormat=!0,void(a._d=new Date(0/0));for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function wa(a){if(!a._d){var b=A(a._i);a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],ra(a)}}function xa(a){var b,e=a._i,f=a._f;return a._locale=a._locale||x(a._l),null===e||void 0===f&&""===e?l({nullInput:!0}):("string"==typeof e&&(a._i=e=a._locale.preparse(e)),o(e)?new n(Y(e)):(c(f)?va(a):f?ta(a):d(e)?a._d=e:ya(a),b=new n(Y(a)),b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b))}function ya(b){var f=b._i;void 0===f?b._d=new Date:d(f)?b._d=new Date(+f):"string"==typeof f?ba(b):c(f)?(b._a=e(f.slice(0),function(a){return parseInt(a,10)}),ra(b)):"object"==typeof f?wa(b):"number"==typeof f?b._d=new Date(f):a.createFromInputFallback(b)}function za(a,b,c,d,e){var f={};return"boolean"==typeof c&&(d=c,c=void 0),f._isAMomentObject=!0,f._useUTC=f._isUTC=e,f._l=c,f._i=a,f._f=b,f._strict=d,xa(f)}function Aa(a,b,c,d){return za(a,b,c,d,!1)}function Ba(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return Aa();for(d=b[0],e=1;ea&&(a=-a,c="-"),c+F(~~(a/60),2)+b+F(~~a%60,2)})}function Ha(a){var b=(a||"").match(_d)||[],c=b[b.length-1]||[],d=(c+"").match(we)||["-",0,0],e=+(60*d[1])+p(d[2]);return"+"===d[0]?e:-e}function Ia(b,c){var e,f;return c._isUTC?(e=c.clone(),f=(o(b)||d(b)?+b:+Aa(b))-+e,e._d.setTime(+e._d+f),a.updateOffset(e,!1),e):Aa(b).local();return c._isUTC?Aa(b).zone(c._offset||0):Aa(b).local()}function Ja(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Ka(b,c){var d,e=this._offset||0;return null!=b?("string"==typeof b&&(b=Ha(b)),Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ja(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?$a(this,Va(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ja(this)}function La(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Ma(a){return this.utcOffset(0,a)}function Na(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ja(this),"m")),this}function Oa(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ha(this._i)),this}function Pa(a){return a=a?Aa(a).utcOffset():0,(this.utcOffset()-a)%60===0}function Qa(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ra(){if(this._a){var a=this._isUTC?h(this._a):Aa(this._a);return this.isValid()&&q(this._a,a.toArray())>0}return!1}function Sa(){return!this._isUTC}function Ta(){return this._isUTC}function Ua(){return this._isUTC&&0===this._offset}function Va(a,b){var c,d,e,g=a,h=null;return Fa(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(g={},b?g[b]=a:g.milliseconds=a):(h=xe.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:p(h[ge])*c,h:p(h[he])*c,m:p(h[ie])*c,s:p(h[je])*c,ms:p(h[ke])*c}):(h=ye.exec(a))?(c="-"===h[1]?-1:1,g={y:Wa(h[2],c),M:Wa(h[3],c),d:Wa(h[4],c),h:Wa(h[5],c),m:Wa(h[6],c),s:Wa(h[7],c),w:Wa(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=Ya(Aa(g.from),Aa(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new Ea(g),Fa(a)&&f(a,"_locale")&&(d._locale=a._locale),d}function Wa(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function Xa(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Ya(a,b){var c;return b=Ia(b,a),a.isBefore(b)?c=Xa(a,b):(c=Xa(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function Za(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(_(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Va(c,d),$a(this,e,a),this}}function $a(b,c,d,e){var f=c._milliseconds,g=c._days,h=c._months;e=null==e?!0:e,f&&b._d.setTime(+b._d+f*d),g&&D(b,"Date",C(b,"Date")+g*d),h&&V(b,C(b,"Month")+h*d),e&&a.updateOffset(b,g||h)}function _a(a){var b=a||Aa(),c=Ia(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.localeData().calendar(e,this,Aa(b)))}function ab(){return new n(this)}function bb(a,b){var c;return b=z("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Aa(a),+this>+a):(c=o(a)?+a:+Aa(a),c<+this.clone().startOf(b))}function cb(a,b){var c;return b=z("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Aa(a),+a>+this):(c=o(a)?+a:+Aa(a),+this.clone().endOf(b)a?Math.ceil(a):Math.floor(a)}function gb(a,b,c){var d,e,f=Ia(a,this),g=6e4*(f.utcOffset()-this.utcOffset());return b=z(b),"year"===b||"month"===b||"quarter"===b?(e=hb(this,f),"quarter"===b?e/=3:"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:fb(e)}function hb(a,b){var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),f=a.clone().add(e,"months");return 0>b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function ib(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function jb(){var a=this.clone().utc();return 0b;b++)if(this._weekdaysParse[b]||(c=Aa([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b}function Mb(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Hb(a,this.localeData()),this.add(a-b,"d")):b}function Nb(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ob(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)}function Pb(a,b){G(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Qb(a,b){return b._meridiemParse}function Rb(a){return"p"===(a+"").toLowerCase().charAt(0)}function Sb(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Tb(a){G(0,[a,3],0,"millisecond")}function Ub(){return this._isUTC?"UTC":""}function Vb(){return this._isUTC?"Coordinated Universal Time":""}function Wb(a){return Aa(1e3*a)}function Xb(){return Aa.apply(null,arguments).parseZone()}function Yb(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.call(b,c):d}function Zb(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b}function $b(){return this._invalidDate}function _b(a){return this._ordinal.replace("%d",a)}function ac(a){return a}function bc(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)}function cc(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)}function dc(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function ec(a,b,c,d){var e=x(),f=h().set(d,b);return e[c](f,a)}function fc(a,b,c,d,e){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return ec(a,b,c,e);var f,g=[];for(f=0;d>f;f++)g[f]=ec(a,f,c,e);return g}function gc(a,b){return fc(a,b,"months",12,"month")}function hc(a,b){return fc(a,b,"monthsShort",12,"month")}function ic(a,b){return fc(a,b,"weekdays",7,"day")}function jc(a,b){return fc(a,b,"weekdaysShort",7,"day")}function kc(a,b){return fc(a,b,"weekdaysMin",7,"day")}function lc(){var a=this._data;return this._milliseconds=Ue(this._milliseconds),this._days=Ue(this._days),this._months=Ue(this._months),a.milliseconds=Ue(a.milliseconds),a.seconds=Ue(a.seconds),a.minutes=Ue(a.minutes),a.hours=Ue(a.hours),a.months=Ue(a.months),a.years=Ue(a.years),this}function mc(a,b,c,d){var e=Va(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function nc(a,b){return mc(this,a,b,1)}function oc(a,b){return mc(this,a,b,-1)}function pc(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;return g.milliseconds=d%1e3,a=fb(d/1e3),g.seconds=a%60,b=fb(a/60),g.minutes=b%60,c=fb(b/60),g.hours=c%24,e+=fb(c/24),h=fb(qc(e)),e-=fb(rc(h)),f+=fb(e/30),e%=30,h+=fb(f/12),f%=12,g.days=e,g.months=f,g.years=h,this}function qc(a){return 400*a/146097}function rc(a){return 146097*a/400}function sc(a){var b,c,d=this._milliseconds;if(a=z(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+12*qc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(rc(this._months/12)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function tc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*p(this._months/12)}function uc(a){return function(){return this.as(a)}}function vc(a){return a=z(a),this[a+"s"]()}function wc(a){return function(){return this._data[a]}}function xc(){return fb(this.days()/7)}function yc(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function zc(a,b,c){var d=Va(a).abs(),e=jf(d.as("s")),f=jf(d.as("m")),g=jf(d.as("h")),h=jf(d.as("d")),i=jf(d.as("M")),j=jf(d.as("y")),k=e0,k[4]=c,yc.apply(null,k)}function Ac(a,b){return void 0===kf[a]?!1:void 0===b?kf[a]:(kf[a]=b,!0)}function Bc(a){var b=this.localeData(),c=zc(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function Cc(){var a=lf(this.years()),b=lf(this.months()),c=lf(this.days()),d=lf(this.hours()),e=lf(this.minutes()),f=lf(this.seconds()+this.milliseconds()/1e3),g=this.asSeconds();return g?(0>g?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"} -//! moment.js locale configuration -//! locale : belarusian (be) -//! author : Dmitry Demidov : https://github.com/demidov91 -//! author: Praleska: http://praleska.pro/ -//! Author : Menelion Elensúle : https://github.com/Oire -function Dc(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function Ec(a,b,c){var d={mm:b?"хвіліна_хвіліны_хвілін":"хвіліну_хвіліны_хвілін",hh:b?"гадзіна_гадзіны_гадзін":"гадзіну_гадзіны_гадзін",dd:"дзень_дні_дзён",MM:"месяц_месяцы_месяцаў",yy:"год_гады_гадоў"};return"m"===c?b?"хвіліна":"хвіліну":"h"===c?b?"гадзіна":"гадзіну":a+" "+Dc(d[c],+a)}function Fc(a,b){var c={nominative:"студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань".split("_"),accusative:"студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function Gc(a,b){var c={nominative:"нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота".split("_"),accusative:"нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу".split("_")},d=/\[ ?[Вв] ?(?:мінулую|наступную)? ?\] ?dddd/.test(b)?"accusative":"nominative";return c[d][a.day()]} -//! moment.js locale configuration -//! locale : breton (br) -//! author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou -function Hc(a,b,c){var d={mm:"munutenn",MM:"miz",dd:"devezh"};return a+" "+Kc(d[c],a)}function Ic(a){switch(Jc(a)){case 1:case 3:case 4:case 5:case 9:return a+" bloaz";default:return a+" vloaz"}}function Jc(a){return a>9?Jc(a%10):a}function Kc(a,b){return 2===b?Lc(a):a}function Lc(a){var b={m:"v",b:"v",d:"z"};return void 0===b[a.charAt(0)]?a:b[a.charAt(0)]+a.substring(1)} -//! moment.js locale configuration -//! locale : bosnian (bs) -//! author : Nedim Cholich : https://github.com/frontyard -//! based on (hr) translation by Bojan Marković -function Mc(a,b,c){var d=a+" ";switch(c){case"m":return b?"jedna minuta":"jedne minute";case"mm":return d+=1===a?"minuta":2===a||3===a||4===a?"minute":"minuta";case"h":return b?"jedan sat":"jednog sata";case"hh":return d+=1===a?"sat":2===a||3===a||4===a?"sata":"sati";case"dd":return d+=1===a?"dan":"dana";case"MM":return d+=1===a?"mjesec":2===a||3===a||4===a?"mjeseca":"mjeseci";case"yy":return d+=1===a?"godina":2===a||3===a||4===a?"godine":"godina"}}function Nc(a){return a>1&&5>a&&1!==~~(a/10)}function Oc(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"pár sekund":"pár sekundami";case"m":return b?"minuta":d?"minutu":"minutou";case"mm":return b||d?e+(Nc(a)?"minuty":"minut"):e+"minutami";break;case"h":return b?"hodina":d?"hodinu":"hodinou";case"hh":return b||d?e+(Nc(a)?"hodiny":"hodin"):e+"hodinami";break;case"d":return b||d?"den":"dnem";case"dd":return b||d?e+(Nc(a)?"dny":"dní"):e+"dny";break;case"M":return b||d?"měsíc":"měsícem";case"MM":return b||d?e+(Nc(a)?"měsíce":"měsíců"):e+"měsíci";break;case"y":return b||d?"rok":"rokem";case"yy":return b||d?e+(Nc(a)?"roky":"let"):e+"lety"}} -//! moment.js locale configuration -//! locale : austrian german (de-at) -//! author : lluchs : https://github.com/lluchs -//! author: Menelion Elensúle: https://github.com/Oire -//! author : Martin Groller : https://github.com/MadMG -function Pc(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]} -//! moment.js locale configuration -//! locale : german (de) -//! author : lluchs : https://github.com/lluchs -//! author: Menelion Elensúle: https://github.com/Oire -function Qc(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]} -//! moment.js locale configuration -//! locale : estonian (et) -//! author : Henry Kehlmann : https://github.com/madhenry -//! improvements : Illimar Tambek : https://github.com/ragulka -function Rc(a,b,c,d){var e={s:["mõne sekundi","mõni sekund","paar sekundit"],m:["ühe minuti","üks minut"],mm:[a+" minuti",a+" minutit"],h:["ühe tunni","tund aega","üks tund"],hh:[a+" tunni",a+" tundi"],d:["ühe päeva","üks päev"],M:["kuu aja","kuu aega","üks kuu"],MM:[a+" kuu",a+" kuud"],y:["ühe aasta","aasta","üks aasta"],yy:[a+" aasta",a+" aastat"]};return b?e[c][2]?e[c][2]:e[c][1]:d?e[c][0]:e[c][1]}function Sc(a,b,c,d){var e="";switch(c){case"s":return d?"muutaman sekunnin":"muutama sekunti";case"m":return d?"minuutin":"minuutti";case"mm":e=d?"minuutin":"minuuttia";break;case"h":return d?"tunnin":"tunti";case"hh":e=d?"tunnin":"tuntia";break;case"d":return d?"päivän":"päivä";case"dd":e=d?"päivän":"päivää";break;case"M":return d?"kuukauden":"kuukausi";case"MM":e=d?"kuukauden":"kuukautta";break;case"y":return d?"vuoden":"vuosi";case"yy":e=d?"vuoden":"vuotta"}return e=Tc(a,d)+" "+e}function Tc(a,b){return 10>a?b?If[a]:Hf[a]:a} -//! moment.js locale configuration -//! locale : hrvatski (hr) -//! author : Bojan Marković : https://github.com/bmarkovic -function Uc(a,b,c){var d=a+" ";switch(c){case"m":return b?"jedna minuta":"jedne minute";case"mm":return d+=1===a?"minuta":2===a||3===a||4===a?"minute":"minuta";case"h":return b?"jedan sat":"jednog sata";case"hh":return d+=1===a?"sat":2===a||3===a||4===a?"sata":"sati";case"dd":return d+=1===a?"dan":"dana";case"MM":return d+=1===a?"mjesec":2===a||3===a||4===a?"mjeseca":"mjeseci";case"yy":return d+=1===a?"godina":2===a||3===a||4===a?"godine":"godina"}}function Vc(a,b,c,d){var e=a;switch(c){case"s":return d||b?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(d||b?" perc":" perce");case"mm":return e+(d||b?" perc":" perce");case"h":return"egy"+(d||b?" óra":" órája");case"hh":return e+(d||b?" óra":" órája");case"d":return"egy"+(d||b?" nap":" napja");case"dd":return e+(d||b?" nap":" napja");case"M":return"egy"+(d||b?" hónap":" hónapja");case"MM":return e+(d||b?" hónap":" hónapja");case"y":return"egy"+(d||b?" év":" éve");case"yy":return e+(d||b?" év":" éve")}return""}function Wc(a){return(a?"":"[múlt] ")+"["+Nf[this.day()]+"] LT[-kor]"} -//! moment.js locale configuration -//! locale : Armenian (hy-am) -//! author : Armendarabyan : https://github.com/armendarabyan -function Xc(a,b){var c={nominative:"հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր".split("_"),accusative:"հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function Yc(a,b){var c="հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ".split("_");return c[a.month()]}function Zc(a,b){var c="կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ".split("_");return c[a.day()]} -//! moment.js locale configuration -//! locale : icelandic (is) -//! author : Hinrik Örn Sigurðsson : https://github.com/hinrik -function $c(a){return a%100===11?!0:a%10===1?!1:!0}function _c(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return b?"mínúta":"mínútu";case"mm":return $c(a)?e+(b||d?"mínútur":"mínútum"):b?e+"mínúta":e+"mínútu";case"hh":return $c(a)?e+(b||d?"klukkustundir":"klukkustundum"):e+"klukkustund";case"d":return b?"dagur":d?"dag":"degi";case"dd":return $c(a)?b?e+"dagar":e+(d?"daga":"dögum"):b?e+"dagur":e+(d?"dag":"degi");case"M":return b?"mánuður":d?"mánuð":"mánuði";case"MM":return $c(a)?b?e+"mánuðir":e+(d?"mánuði":"mánuðum"):b?e+"mánuður":e+(d?"mánuð":"mánuði");case"y":return b||d?"ár":"ári";case"yy":return $c(a)?e+(b||d?"ár":"árum"):e+(b||d?"ár":"ári")}} -//! moment.js locale configuration -//! locale : Georgian (ka) -//! author : Irakli Janiashvili : https://github.com/irakli-janiashvili -function ad(a,b){var c={nominative:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),accusative:"იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს".split("_")},d=/D[oD] *MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function bd(a,b){var c={nominative:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),accusative:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_")},d=/(წინა|შემდეგ)/.test(b)?"accusative":"nominative";return c[d][a.day()]} -//! moment.js locale configuration -//! locale : Luxembourgish (lb) -//! author : mweimerskirch : https://github.com/mweimerskirch, David Raison : https://github.com/kwisatz -function cd(a,b,c,d){var e={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return b?e[c][0]:e[c][1]}function dd(a){var b=a.substr(0,a.indexOf(" "));return fd(b)?"a "+a:"an "+a}function ed(a){var b=a.substr(0,a.indexOf(" "));return fd(b)?"viru "+a:"virun "+a}function fd(a){if(a=parseInt(a,10),isNaN(a))return!1;if(0>a)return!0;if(10>a)return a>=4&&7>=a?!0:!1;if(100>a){var b=a%10,c=a/10;return fd(0===b?c:b)}if(1e4>a){for(;a>=10;)a/=10;return fd(a)}return a/=1e3,fd(a)}function gd(a,b,c,d){return b?"kelios sekundės":d?"kelių sekundžių":"kelias sekundes"}function hd(a,b,c,d){return b?jd(c)[0]:d?jd(c)[1]:jd(c)[2]}function id(a){return a%10===0||a>10&&20>a}function jd(a){return Of[a].split("_")}function kd(a,b,c,d){var e=a+" ";return 1===a?e+hd(a,b,c[0],d):b?e+(id(a)?jd(c)[1]:jd(c)[0]):d?e+jd(c)[1]:e+(id(a)?jd(c)[1]:jd(c)[2])}function ld(a,b){var c=-1===b.indexOf("dddd HH:mm"),d=Pf[a.day()];return c?d:d.substring(0,d.length-2)+"į"}function md(a,b,c){return c?b%10===1&&11!==b?a[2]:a[3]:b%10===1&&11!==b?a[0]:a[1]}function nd(a,b,c){return a+" "+md(Qf[c],a,b)}function od(a,b,c){return md(Qf[c],a,b)}function pd(a,b){return b?"dažas sekundes":"dažām sekundēm"}function qd(a){return 5>a%10&&a%10>1&&~~(a/10)%10!==1}function rd(a,b,c){var d=a+" ";switch(c){case"m":return b?"minuta":"minutę";case"mm":return d+(qd(a)?"minuty":"minut");case"h":return b?"godzina":"godzinę";case"hh":return d+(qd(a)?"godziny":"godzin");case"MM":return d+(qd(a)?"miesiące":"miesięcy");case"yy":return d+(qd(a)?"lata":"lat")}} -//! moment.js locale configuration -//! locale : romanian (ro) -//! author : Vlad Gurdiga : https://github.com/gurdiga -//! author : Valentin Agachi : https://github.com/avaly -function sd(a,b,c){var d={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},e=" ";return(a%100>=20||a>=100&&a%100===0)&&(e=" de "),a+e+d[c]} -//! moment.js locale configuration -//! locale : russian (ru) -//! author : Viktorminator : https://github.com/Viktorminator -//! Author : Menelion Elensúle : https://github.com/Oire -function td(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function ud(a,b,c){var d={mm:b?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===c?b?"минута":"минуту":a+" "+td(d[c],+a)}function vd(a,b){var c={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function wd(a,b){var c={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},d=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function xd(a,b){var c={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},d=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(b)?"accusative":"nominative";return c[d][a.day()]}function yd(a){return a>1&&5>a}function zd(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"pár sekúnd":"pár sekundami";case"m":return b?"minúta":d?"minútu":"minútou";case"mm":return b||d?e+(yd(a)?"minúty":"minút"):e+"minútami";break;case"h":return b?"hodina":d?"hodinu":"hodinou";case"hh":return b||d?e+(yd(a)?"hodiny":"hodín"):e+"hodinami";break;case"d":return b||d?"deň":"dňom";case"dd":return b||d?e+(yd(a)?"dni":"dní"):e+"dňami";break;case"M":return b||d?"mesiac":"mesiacom";case"MM":return b||d?e+(yd(a)?"mesiace":"mesiacov"):e+"mesiacmi";break;case"y":return b||d?"rok":"rokom";case"yy":return b||d?e+(yd(a)?"roky":"rokov"):e+"rokmi"}} -//! moment.js locale configuration -//! locale : slovenian (sl) -//! author : Robert Sedovšek : https://github.com/sedovsek -function Ad(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"nekaj sekund":"nekaj sekundami";case"m":return b?"ena minuta":"eno minuto";case"mm":return e+=1===a?b?"minuta":"minuto":2===a?b||d?"minuti":"minutama":5>a?b||d?"minute":"minutami":b||d?"minut":"minutami";case"h":return b?"ena ura":"eno uro";case"hh":return e+=1===a?b?"ura":"uro":2===a?b||d?"uri":"urama":5>a?b||d?"ure":"urami":b||d?"ur":"urami";case"d":return b||d?"en dan":"enim dnem";case"dd":return e+=1===a?b||d?"dan":"dnem":2===a?b||d?"dni":"dnevoma":b||d?"dni":"dnevi";case"M":return b||d?"en mesec":"enim mesecem";case"MM":return e+=1===a?b||d?"mesec":"mesecem":2===a?b||d?"meseca":"mesecema":5>a?b||d?"mesece":"meseci":b||d?"mesecev":"meseci";case"y":return b||d?"eno leto":"enim letom";case"yy":return e+=1===a?b||d?"leto":"letom":2===a?b||d?"leti":"letoma":5>a?b||d?"leta":"leti":b||d?"let":"leti"}} -//! moment.js locale configuration -//! locale : ukrainian (uk) -//! author : zemlanin : https://github.com/zemlanin -//! Author : Menelion Elensúle : https://github.com/Oire -function Bd(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function Cd(a,b,c){var d={mm:"хвилина_хвилини_хвилин",hh:"година_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===c?b?"хвилина":"хвилину":"h"===c?b?"година":"годину":a+" "+Bd(d[c],+a)}function Dd(a,b){var c={nominative:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_"),accusative:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_")},d=/D[oD]? *MMMM?/.test(b)?"accusative":"nominative";return c[d][a.month()]}function Ed(a,b){var c={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")},d=/(\[[ВвУу]\]) ?dddd/.test(b)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(b)?"genitive":"nominative";return c[d][a.day()]}function Fd(a){return function(){return a+"о"+(11===this.hours()?"б":"")+"] LT"}}var Gd,Hd,Id=a.momentProperties=[],Jd=!1,Kd={},Ld={},Md=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g,Nd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Od={},Pd={},Qd=/\d/,Rd=/\d\d/,Sd=/\d{3}/,Td=/\d{4}/,Ud=/[+-]?\d{6}/,Vd=/\d\d?/,Wd=/\d{1,3}/,Xd=/\d{1,4}/,Yd=/[+-]?\d{1,6}/,Zd=/\d+/,$d=/[+-]?\d+/,_d=/Z|[+-]\d\d:?\d\d/gi,ae=/[+-]?\d+(\.\d{1,3})?/,be=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,ce={},de={},ee=0,fe=1,ge=2,he=3,ie=4,je=5,ke=6;G("M",["MM",2],"Mo",function(){return this.month()+1}),G("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),G("MMMM",0,0,function(a){return this.localeData().months(this,a)}),y("month","M"),L("M",Vd),L("MM",Vd,Rd),L("MMM",be),L("MMMM",be),O(["M","MM"],function(a,b){b[fe]=p(a)-1}),O(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[fe]=e:j(c).invalidMonth=a});var le="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),me="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),ne={};a.suppressDeprecationWarnings=!1;var oe=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,pe=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],qe=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],re=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=$("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),G(0,["YY",2],0,function(){return this.year()%100}),G(0,["YYYY",4],0,"year"),G(0,["YYYYY",5],0,"year"),G(0,["YYYYYY",6,!0],0,"year"),y("year","y"),L("Y",$d),L("YY",Vd,Rd),L("YYYY",Xd,Td),L("YYYYY",Yd,Ud),L("YYYYYY",Yd,Ud),O(["YYYY","YYYYY","YYYYYY"],ee),O("YY",function(b,c){c[ee]=a.parseTwoDigitYear(b)}),a.parseTwoDigitYear=function(a){return p(a)+(p(a)>68?1900:2e3)};var se=B("FullYear",!1);G("w",["ww",2],"wo","week"),G("W",["WW",2],"Wo","isoWeek"),y("week","w"),y("isoWeek","W"),L("w",Vd),L("ww",Vd,Rd),L("W",Vd),L("WW",Vd,Rd),P(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=p(a)});var te={dow:0,doy:6};G("DDD",["DDDD",3],"DDDo","dayOfYear"),y("dayOfYear","DDD"),L("DDD",Wd),L("DDDD",Sd),O(["DDD","DDDD"],function(a,b,c){c._dayOfYear=p(a)}),a.ISO_8601=function(){};var ue=$("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var a=Aa.apply(null,arguments);return this>a?this:a}),ve=$("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var a=Aa.apply(null,arguments);return a>this?this:a});Ga("Z",":"),Ga("ZZ",""),L("Z",_d),L("ZZ",_d),O(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ha(a)});var we=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var xe=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,ye=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Va.fn=Ea.prototype;var ze=Za(1,"add"),Ae=Za(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var Be=$("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});G(0,["gg",2],0,function(){return this.weekYear()%100}),G(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Ab("gggg","weekYear"),Ab("ggggg","weekYear"),Ab("GGGG","isoWeekYear"),Ab("GGGGG","isoWeekYear"),y("weekYear","gg"),y("isoWeekYear","GG"),L("G",$d),L("g",$d),L("GG",Vd,Rd),L("gg",Vd,Rd),L("GGGG",Xd,Td),L("gggg",Xd,Td),L("GGGGG",Yd,Ud),L("ggggg",Yd,Ud),P(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=p(a)}),P(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),G("Q",0,0,"quarter"),y("quarter","Q"),L("Q",Qd),O("Q",function(a,b){b[fe]=3*(p(a)-1)}),G("D",["DD",2],"Do","date"),y("date","D"),L("D",Vd),L("DD",Vd,Rd),L("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),O(["D","DD"],ge),O("Do",function(a,b){b[ge]=p(a.match(Vd)[0],10)});var Ce=B("Date",!0);G("d",0,"do","day"),G("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),G("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),G("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),G("e",0,0,"weekday"),G("E",0,0,"isoWeekday"),y("day","d"),y("weekday","e"),y("isoWeekday","E"),L("d",Vd),L("e",Vd),L("E",Vd),L("dd",be),L("ddd",be),L("dddd",be),P(["dd","ddd","dddd"],function(a,b,c){var d=c._locale.weekdaysParse(a);null!=d?b.d=d:j(c).invalidWeekday=a}),P(["d","e","E"],function(a,b,c,d){b[d]=p(a)});var De="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Ee="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Fe="Su_Mo_Tu_We_Th_Fr_Sa".split("_");G("H",["HH",2],0,"hour"),G("h",["hh",2],0,function(){return this.hours()%12||12}),Pb("a",!0),Pb("A",!1),y("hour","h"),L("a",Qb),L("A",Qb),L("H",Vd),L("h",Vd),L("HH",Vd,Rd),L("hh",Vd,Rd),O(["H","HH"],he),O(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),O(["h","hh"],function(a,b,c){b[he]=p(a),j(c).bigHour=!0});var Ge=/[ap]\.?m?\.?/i,He=B("Hours",!0);G("m",["mm",2],0,"minute"),y("minute","m"),L("m",Vd),L("mm",Vd,Rd),O(["m","mm"],ie);var Ie=B("Minutes",!1);G("s",["ss",2],0,"second"),y("second","s"),L("s",Vd),L("ss",Vd,Rd),O(["s","ss"],je);var Je=B("Seconds",!1);G("S",0,0,function(){return~~(this.millisecond()/100)}),G(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),Tb("SSS"),Tb("SSSS"),y("millisecond","ms"),L("S",Wd,Qd),L("SS",Wd,Rd),L("SSS",Wd,Sd),L("SSSS",Zd),O(["S","SS","SSS","SSSS"],function(a,b){b[ke]=p(1e3*("0."+a))});var Ke=B("Milliseconds",!1);G("z",0,0,"zoneAbbr"),G("zz",0,0,"zoneName");var Le=n.prototype;Le.add=ze,Le.calendar=_a,Le.clone=ab,Le.diff=gb,Le.endOf=sb,Le.format=kb,Le.from=lb,Le.fromNow=mb,Le.to=nb,Le.toNow=ob,Le.get=E,Le.invalidAt=zb,Le.isAfter=bb,Le.isBefore=cb,Le.isBetween=db,Le.isSame=eb,Le.isValid=xb,Le.lang=Be,Le.locale=pb,Le.localeData=qb,Le.max=ve,Le.min=ue,Le.parsingFlags=yb,Le.set=E,Le.startOf=rb,Le.subtract=Ae,Le.toArray=wb,Le.toDate=vb,Le.toISOString=jb,Le.toJSON=jb,Le.toString=ib,Le.unix=ub,Le.valueOf=tb,Le.year=se,Le.isLeapYear=ga,Le.weekYear=Cb,Le.isoWeekYear=Db,Le.quarter=Le.quarters=Gb,Le.month=W,Le.daysInMonth=X,Le.week=Le.weeks=la,Le.isoWeek=Le.isoWeeks=ma,Le.weeksInYear=Fb,Le.isoWeeksInYear=Eb,Le.date=Ce,Le.day=Le.days=Mb,Le.weekday=Nb,Le.isoWeekday=Ob,Le.dayOfYear=oa,Le.hour=Le.hours=He,Le.minute=Le.minutes=Ie,Le.second=Le.seconds=Je,Le.millisecond=Le.milliseconds=Ke,Le.utcOffset=Ka,Le.utc=Ma,Le.local=Na,Le.parseZone=Oa,Le.hasAlignedHourOffset=Pa,Le.isDST=Qa,Le.isDSTShifted=Ra,Le.isLocal=Sa,Le.isUtcOffset=Ta,Le.isUtc=Ua,Le.isUTC=Ua,Le.zoneAbbr=Ub,Le.zoneName=Vb,Le.dates=$("dates accessor is deprecated. Use date instead.",Ce),Le.months=$("months accessor is deprecated. Use month instead",W),Le.years=$("years accessor is deprecated. Use year instead",se),Le.zone=$("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",La);var Me=Le,Ne={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Oe={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},Pe="Invalid date",Qe="%d",Re=/\d{1,2}/,Se={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Te=r.prototype;Te._calendar=Ne,Te.calendar=Yb,Te._longDateFormat=Oe,Te.longDateFormat=Zb,Te._invalidDate=Pe,Te.invalidDate=$b,Te._ordinal=Qe,Te.ordinal=_b,Te._ordinalParse=Re,Te.preparse=ac,Te.postformat=ac,Te._relativeTime=Se,Te.relativeTime=bc,Te.pastFuture=cc,Te.set=dc,Te.months=S,Te._months=le,Te.monthsShort=T,Te._monthsShort=me,Te.monthsParse=U,Te.week=ia,Te._week=te,Te.firstDayOfYear=ka,Te.firstDayOfWeek=ja,Te.weekdays=Ib,Te._weekdays=De,Te.weekdaysMin=Kb,Te._weekdaysMin=Fe,Te.weekdaysShort=Jb,Te._weekdaysShort=Ee,Te.weekdaysParse=Lb,Te.isPM=Rb,Te._meridiemParse=Ge,Te.meridiem=Sb,v("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===p(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=$("moment.lang is deprecated. Use moment.locale instead.",v),a.langData=$("moment.langData is deprecated. Use moment.localeData instead.",x);var Ue=Math.abs,Ve=uc("ms"),We=uc("s"),Xe=uc("m"),Ye=uc("h"),Ze=uc("d"),$e=uc("w"),_e=uc("M"),af=uc("y"),bf=wc("milliseconds"),cf=wc("seconds"),df=wc("minutes"),ef=wc("hours"),ff=wc("days"),gf=wc("months"),hf=wc("years"),jf=Math.round,kf={s:45,m:45,h:22,d:26,M:11},lf=Math.abs,mf=Ea.prototype;mf.abs=lc,mf.add=nc,mf.subtract=oc,mf.as=sc,mf.asMilliseconds=Ve,mf.asSeconds=We,mf.asMinutes=Xe,mf.asHours=Ye,mf.asDays=Ze,mf.asWeeks=$e,mf.asMonths=_e,mf.asYears=af,mf.valueOf=tc,mf._bubble=pc,mf.get=vc,mf.milliseconds=bf,mf.seconds=cf,mf.minutes=df,mf.hours=ef,mf.days=ff,mf.weeks=xc,mf.months=gf,mf.years=hf,mf.humanize=Bc,mf.toISOString=Cc,mf.toString=Cc,mf.toJSON=Cc,mf.locale=pb,mf.localeData=qb,mf.toIsoString=$("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Cc),mf.lang=Be,G("X",0,0,"unix"),G("x",0,0,"valueOf"),L("x",$d),L("X",ae),O("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),O("x",function(a,b,c){c._d=new Date(p(a))}), //! moment.js -//! version : 2.10.3 +//! version : 2.14.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com -a.version="2.10.3",b(Aa),a.fn=Me,a.min=Ca,a.max=Da,a.utc=h,a.unix=Wb,a.months=gc,a.isDate=d,a.locale=v,a.invalid=l,a.duration=Va,a.isMoment=o,a.weekdays=ic,a.parseZone=Xb,a.localeData=x,a.isDuration=Fa,a.monthsShort=hc,a.weekdaysMin=kc,a.defineLocale=w,a.weekdaysShort=jc,a.normalizeUnits=z,a.relativeTimeThreshold=Ac;var nf=a,of=(nf.defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(a){return/^nm$/i.test(a)},meridiem:function(a,b,c){return 12>a?c?"vm":"VM":c?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[Môre om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,doy:4}}),nf.defineLocale("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,doy:12}}),{1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"}),pf={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},qf=(nf.defineLocale("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiemParse:/ص|م/,isPM:function(a){return"م"===a},meridiem:function(a,b,c){return 12>a?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(a){return a.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(a){return pf[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return of[a]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),nf.defineLocale("ar-tn",{months:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}}),{1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"}),rf={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},sf=function(a){return 0===a?0:1===a?1:2===a?2:a%100>=3&&10>=a%100?3:a%100>=11?4:5},tf={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},uf=function(a){return function(b,c,d,e){var f=sf(b),g=tf[a][sf(b)];return 2===f&&(g=g[c?0:1]),g.replace(/%d/i,b)}},vf=["كانون الثاني يناير","شباط فبراير","آذار مارس","نيسان أبريل","أيار مايو","حزيران يونيو","تموز يوليو","آب أغسطس","أيلول سبتمبر","تشرين الأول أكتوبر","تشرين الثاني نوفمبر","كانون الأول ديسمبر"],wf=(nf.defineLocale("ar",{months:vf,monthsShort:vf,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiemParse:/ص|م/,isPM:function(a){return"م"===a},meridiem:function(a,b,c){return 12>a?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:uf("s"),m:uf("m"),mm:uf("m"),h:uf("h"),hh:uf("h"),d:uf("d"),dd:uf("d"),M:uf("M"),MM:uf("M"),y:uf("y"),yy:uf("y")},preparse:function(a){return a.replace(/\u200f/g,"").replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(a){return rf[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return qf[a]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),{1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-üncü",4:"-üncü",100:"-üncü",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"}),xf=(nf.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"),weekdaysMin:"Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dünən] LT",lastWeek:"[keçən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"birneçə saniyyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gündüz|axşam/,isPM:function(a){return/^(gündüz|axşam)$/.test(a)},meridiem:function(a,b,c){return 4>a?"gecə":12>a?"səhər":17>a?"gündüz":"axşam"},ordinalParse:/\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,ordinal:function(a){if(0===a)return a+"-ıncı";var b=a%10,c=a%100-b,d=a>=100?100:null;return a+(wf[b]||wf[c]||wf[d])},week:{dow:1,doy:7}}),nf.defineLocale("be",{months:Fc,monthsShort:"студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж".split("_"),weekdays:Gc,weekdaysShort:"нд_пн_ат_ср_чц_пт_сб".split("_"),weekdaysMin:"нд_пн_ат_ср_чц_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сёння ў] LT",nextDay:"[Заўтра ў] LT",lastDay:"[Учора ў] LT",nextWeek:function(){return"[У] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[У мінулую] dddd [ў] LT";case 1:case 2:case 4:return"[У мінулы] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"праз %s",past:"%s таму",s:"некалькі секунд",m:Ec,mm:Ec,h:Ec,hh:Ec,d:"дзень",dd:Ec,M:"месяц",MM:Ec,y:"год",yy:Ec},meridiemParse:/ночы|раніцы|дня|вечара/,isPM:function(a){return/^(дня|вечара)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночы":12>a?"раніцы":17>a?"дня":"вечара"},ordinalParse:/\d{1,2}-(і|ы|га)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":case"w":case"W":return a%10!==2&&a%10!==3||a%100===12||a%100===13?a+"-ы":a+"-і";case"D":return a+"-га";default:return a}},week:{dow:1,doy:7}}),nf.defineLocale("bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[В изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[В изминалия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дни",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(a){var b=a%10,c=a%100;return 0===a?a+"-ев":0===c?a+"-ен":c>10&&20>c?a+"-ти":1===b?a+"-ви":2===b?a+"-ри":7===b||8===b?a+"-ми":a+"-ти"},week:{dow:1,doy:7}}),{1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"}),yf={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"},zf=(nf.defineLocale("bn",{months:"জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রুবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্রু_শনি".split("_"),weekdaysMin:"রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কএক সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(a){return a.replace(/[১২৩৪৫৬৭৮৯০]/g,function(a){return yf[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return xf[a]})},meridiemParse:/রাত|শকাল|দুপুর|বিকেল|রাত/,isPM:function(a){return/^(দুপুর|বিকেল|রাত)$/.test(a)},meridiem:function(a,b,c){return 4>a?"রাত":10>a?"শকাল":17>a?"দুপুর":20>a?"বিকেল":"রাত"},week:{dow:0,doy:6}}),{1:"༡",2:"༢",3:"༣",4:"༤",5:"༥",6:"༦",7:"༧",8:"༨",9:"༩",0:"༠"}),Af={"༡":"1","༢":"2","༣":"3","༤":"4","༥":"5","༦":"6","༧":"7","༨":"8","༩":"9","༠":"0"},Bf=(nf.defineLocale("bo",{months:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),monthsShort:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),weekdays:"གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་".split("_"),weekdaysShort:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),weekdaysMin:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),longDateFormat:{LT:"A h:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[དི་རིང] LT",nextDay:"[སང་ཉིན] LT",nextWeek:"[བདུན་ཕྲག་རྗེས་མ], LT",lastDay:"[ཁ་སང] LT",lastWeek:"[བདུན་ཕྲག་མཐའ་མ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ལ་",past:"%s སྔན་ལ",s:"ལམ་སང",m:"སྐར་མ་གཅིག",mm:"%d སྐར་མ",h:"ཆུ་ཚོད་གཅིག",hh:"%d ཆུ་ཚོད",d:"ཉིན་གཅིག",dd:"%d ཉིན་",M:"ཟླ་བ་གཅིག",MM:"%d ཟླ་བ",y:"ལོ་གཅིག",yy:"%d ལོ"},preparse:function(a){return a.replace(/[༡༢༣༤༥༦༧༨༩༠]/g,function(a){return Af[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return zf[a]})},meridiemParse:/མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,isPM:function(a){return/^(ཉིན་གུང|དགོང་དག|མཚན་མོ)$/.test(a)},meridiem:function(a,b,c){return 4>a?"མཚན་མོ":10>a?"ཞོགས་ཀས":17>a?"ཉིན་གུང":20>a?"དགོང་དག":"མཚན་མོ"},week:{dow:0,doy:6}}),nf.defineLocale("br",{months:"Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),longDateFormat:{LT:"h[e]mm A",LTS:"h[e]mm:ss A",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY LT",LLLL:"dddd, D [a viz] MMMM YYYY LT"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warc'hoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Dec'h da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s 'zo",s:"un nebeud segondennoù",m:"ur vunutenn",mm:Hc,h:"un eur",hh:"%d eur",d:"un devezh",dd:Hc,M:"ur miz",MM:Hc,y:"ur bloaz",yy:Ic},ordinalParse:/\d{1,2}(añ|vet)/,ordinal:function(a){var b=1===a?"añ":"vet";return a+b},week:{dow:1,doy:4}}),nf.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:Mc,mm:Mc,h:Mc,hh:Mc,d:"dan",dd:Mc,M:"mjesec",MM:Mc,y:"godinu",yy:Mc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),nf.defineLocale("ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(a,b){var c=1===a?"r":2===a?"n":3===a?"r":4===a?"t":"è";return("w"===b||"W"===b)&&(c="a"),a+c},week:{dow:1,doy:4}}),"leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_")),Cf="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_"),Df=(nf.defineLocale("cs",{months:Bf,monthsShort:Cf,monthsParse:function(a,b){var c,d=[];for(c=0;12>c;c++)d[c]=new RegExp("^"+a[c]+"$|^"+b[c]+"$","i");return d}(Bf,Cf),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:Oc,m:Oc,mm:Oc,h:Oc,hh:Oc,d:Oc,dd:Oc,M:Oc,MM:Oc,y:Oc,yy:Oc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("cv",{months:"кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав".split("_"),monthsShort:"кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кӗҫ_эрн_шӑм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кҫ_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD-MM-YYYY",LL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]",LLL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], LT",LLLL:"dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], LT"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ӗнер] LT [сехетре]",nextWeek:"[Ҫитес] dddd LT [сехетре]",lastWeek:"[Иртнӗ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(a){var b=/сехет$/i.exec(a)?"рен":/ҫул$/i.exec(a)?"тан":"ран";return a+b},past:"%s каялла",s:"пӗр-ик ҫеккунт",m:"пӗр минут",mm:"%d минут",h:"пӗр сехет",hh:"%d сехет",d:"пӗр кун",dd:"%d кун",M:"пӗр уйӑх",MM:"%d уйӑх",y:"пӗр ҫул",yy:"%d ҫул"},ordinalParse:/\d{1,2}-мӗш/,ordinal:"%d-мӗш",week:{dow:1,doy:7}}),nf.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn ôl",s:"ychydig eiliadau",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},ordinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(a){var b=a,c="",d=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];return b>20?c=40===b||50===b||60===b||80===b||100===b?"fed":"ain":b>0&&(c=d[b]),a+c},week:{dow:1,doy:4}}),nf.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT [Uhr]",sameElse:"L",nextDay:"[Morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[Gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:Pc,mm:"%d Minuten",h:Pc,hh:"%d Stunden",d:Pc,dd:Pc,M:Pc,MM:Pc,y:Pc,yy:Pc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT [Uhr]",sameElse:"L",nextDay:"[Morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[Gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:Qc,mm:"%d Minuten",h:Qc,hh:"%d Stunden",d:Qc,dd:Qc,M:Qc,MM:Qc,y:Qc,yy:Qc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(a,b){return/D/.test(b.substring(0,b.indexOf("MMMM")))?this._monthsGenitiveEl[a.month()]:this._monthsNominativeEl[a.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(a,b,c){return a>11?c?"μμ":"ΜΜ":c?"πμ":"ΠΜ"},isPM:function(a){return"μ"===(a+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(a,b){var c=this._calendarEl[a],d=b&&b.hours();return"function"==typeof c&&(c=c.apply(b)),c.replace("{}",d%12===1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}}),nf.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,doy:4}}),nf.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"D MMMM, YYYY",LLL:"D MMMM, YYYY LT",LLLL:"dddd, D MMMM, YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),nf.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,doy:4}}),nf.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec".split("_"),weekdays:"Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato".split("_"),weekdaysShort:"Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Ĵa_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D[-an de] MMMM, YYYY",LLL:"D[-an de] MMMM, YYYY LT",LLLL:"dddd, [la] D[-an de] MMMM, YYYY LT"},meridiemParse:/[ap]\.t\.m/i,isPM:function(a){return"p"===a.charAt(0).toLowerCase()},meridiem:function(a,b,c){return a>11?c?"p.t.m.":"P.T.M.":c?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd [je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasinta] dddd [je] LT",sameElse:"L"},relativeTime:{future:"je %s",past:"antaŭ %s",s:"sekundoj",m:"minuto",mm:"%d minutoj",h:"horo",hh:"%d horoj",d:"tago",dd:"%d tagoj",M:"monato",MM:"%d monatoj",y:"jaro",yy:"%d jaroj"},ordinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}}),"Ene._Feb._Mar._Abr._May._Jun._Jul._Ago._Sep._Oct._Nov._Dic.".split("_")),Ef="Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Sep_Oct_Nov_Dic".split("_"),Ff=(nf.defineLocale("es",{months:"Enero_Febrero_Marzo_Abril_Mayo_Junio_Julio_Agosto_Septiembre_Octubre_Noviembre_Diciembre".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Ef[a.month()]:Df[a.month()]},weekdays:"Domingo_Lunes_Martes_Miércoles_Jueves_Viernes_Sábado".split("_"),weekdaysShort:"Dom._Lun._Mar._Mié._Jue._Vie._Sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),nf.defineLocale("et",{months:"jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Täna,] LT",nextDay:"[Homme,] LT",nextWeek:"[Järgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pärast",past:"%s tagasi",s:Rc,m:Rc,mm:Rc,h:Rc,hh:Rc,d:Rc,dd:"%d päeva",M:Rc,MM:Rc,y:Rc,yy:Rc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] LT",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] LT",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] LT",llll:"ddd, YYYY[ko] MMM D[a] LT"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat", -dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),{1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"}),Gf={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"},Hf=(nf.defineLocale("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(a){return/بعد از ظهر/.test(a)},meridiem:function(a,b,c){return 12>a?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(a){return a.replace(/[۰-۹]/g,function(a){return Gf[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return Ff[a]}).replace(/,/g,"،")},ordinalParse:/\d{1,2}م/,ordinal:"%dم",week:{dow:6,doy:12}}),"nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" ")),If=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",Hf[7],Hf[8],Hf[9]],Jf=(nf.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:Sc,m:Sc,mm:Sc,h:Sc,hh:Sc,d:Sc,dd:Sc,M:Sc,MM:Sc,y:Sc,yy:Sc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("fo",{months:"januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mán_týs_mik_hós_frí_ley".split("_"),weekdaysMin:"su_má_tý_mi_hó_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D. MMMM, YYYY LT"},calendar:{sameDay:"[Í dag kl.] LT",nextDay:"[Í morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Í gjár kl.] LT",lastWeek:"[síðstu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s síðani",s:"fá sekund",m:"ein minutt",mm:"%d minuttir",h:"ein tími",hh:"%d tímar",d:"ein dagur",dd:"%d dagar",M:"ein mánaði",MM:"%d mánaðir",y:"eitt ár",yy:"%d ár"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(a){return a+(1===a?"er":"")}}),nf.defineLocale("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(a){return a+(1===a?"er":"")},week:{dow:1,doy:4}}),"jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_")),Kf="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),Lf=(nf.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Kf[a.month()]:Jf[a.month()]},weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[ôfrûne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",m:"ien minút",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,doy:4}}),nf.defineLocale("gl",{months:"Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro".split("_"),monthsShort:"Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.".split("_"),weekdays:"Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado".split("_"),weekdaysShort:"Dom._Lun._Mar._Mér._Xov._Ven._Sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mé_Xo_Ve_Sá".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"ás":"á")+"] LT"},nextDay:function(){return"[mañá "+(1!==this.hours()?"ás":"á")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"ás":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"á":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"ás":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(a){return"uns segundos"===a?"nuns segundos":"en "+a},past:"hai %s",s:"uns segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:7}}),nf.defineLocale("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY LT",LLLL:"dddd, D [ב]MMMM YYYY LT",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(a){return 2===a?"שעתיים":a+" שעות"},d:"יום",dd:function(a){return 2===a?"יומיים":a+" ימים"},M:"חודש",MM:function(a){return 2===a?"חודשיים":a+" חודשים"},y:"שנה",yy:function(a){return 2===a?"שנתיים":a%10===0&&10!==a?a+" שנה":a+" שנים"}}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),Mf={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},Nf=(nf.defineLocale("hi",{months:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return Mf[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Lf[a]})},meridiemParse:/रात|सुबह|दोपहर|शाम/,meridiemHour:function(a,b){return 12===a&&(a=0),"रात"===b?4>a?a:a+12:"सुबह"===b?a:"दोपहर"===b?a>=10?a:a+12:"शाम"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"रात":10>a?"सुबह":17>a?"दोपहर":20>a?"शाम":"रात"},week:{dow:0,doy:6}}),nf.defineLocale("hr",{months:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"),monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:Uc,mm:Uc,h:Uc,hh:Uc,d:"dan",dd:Uc,M:"mjesec",MM:Uc,y:"godinu",yy:Uc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),"vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ")),Of=(nf.defineLocale("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiemParse:/de|du/i,isPM:function(a){return"u"===a.charAt(1).toLowerCase()},meridiem:function(a,b,c){return 12>a?c===!0?"de":"DE":c===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return Wc.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return Wc.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:Vc,m:Vc,mm:Vc,h:Vc,hh:Vc,d:Vc,dd:Vc,M:Vc,MM:Vc,y:Vc,yy:Vc},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),nf.defineLocale("hy-am",{months:Xc,monthsShort:Yc,weekdays:Zc,weekdaysShort:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),weekdaysMin:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY թ.",LLL:"D MMMM YYYY թ., LT",LLLL:"dddd, D MMMM YYYY թ., LT"},calendar:{sameDay:"[այսօր] LT",nextDay:"[վաղը] LT",lastDay:"[երեկ] LT",nextWeek:function(){return"dddd [օրը ժամը] LT"},lastWeek:function(){return"[անցած] dddd [օրը ժամը] LT"},sameElse:"L"},relativeTime:{future:"%s հետո",past:"%s առաջ",s:"մի քանի վայրկյան",m:"րոպե",mm:"%d րոպե",h:"ժամ",hh:"%d ժամ",d:"օր",dd:"%d օր",M:"ամիս",MM:"%d ամիս",y:"տարի",yy:"%d տարի"},meridiemParse:/գիշերվա|առավոտվա|ցերեկվա|երեկոյան/,isPM:function(a){return/^(ցերեկվա|երեկոյան)$/.test(a)},meridiem:function(a){return 4>a?"գիշերվա":12>a?"առավոտվա":17>a?"ցերեկվա":"երեկոյան"},ordinalParse:/\d{1,2}|\d{1,2}-(ին|րդ)/,ordinal:function(a,b){switch(b){case"DDD":case"w":case"W":case"DDDo":return 1===a?a+"-ին":a+"-րդ";default:return a}},week:{dow:1,doy:7}}),nf.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"LT.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"siang"===b?a>=11?a:a+12:"sore"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"siang":19>a?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),nf.defineLocale("is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd, D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:_c,m:_c,mm:_c,h:"klukkustund",hh:_c,d:_c,dd:_c,M:_c,MM:_c,y:_c,yy:_c},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(a){return(/^[0-9].+$/.test(a)?"tra":"in")+" "+a},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),nf.defineLocale("ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"LTs秒",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiemParse:/午前|午後/i,isPM:function(a){return"午後"===a},meridiem:function(a,b,c){return 12>a?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),nf.defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"LT.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(a,b){return 12===a&&(a=0),"enjing"===b?a:"siyang"===b?a>=11?a:a+12:"sonten"===b||"ndalu"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"enjing":15>a?"siyang":19>a?"sonten":"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,doy:7}}),nf.defineLocale("ka",{months:ad,monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:bd,weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(a){return/(წამი|წუთი|საათი|წელი)/.test(a)?a.replace(/ი$/,"ში"):a+"ში"},past:function(a){return/(წამი|წუთი|საათი|დღე|თვე)/.test(a)?a.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(a)?a.replace(/წელი$/,"წლის წინ"):void 0},s:"რამდენიმე წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},ordinalParse:/0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,ordinal:function(a){return 0===a?a:1===a?a+"-ლი":20>a||100>=a&&a%20===0||a%100===0?"მე-"+a:a+"-ე"},week:{dow:1,doy:7}}),nf.defineLocale("km",{months:"មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),monthsShort:"មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),weekdays:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysShort:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysMin:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[ថ្ងៃនៈ ម៉ោង] LT",nextDay:"[ស្អែក ម៉ោង] LT",nextWeek:"dddd [ម៉ោង] LT",lastDay:"[ម្សិលមិញ ម៉ោង] LT",lastWeek:"dddd [សប្តាហ៍មុន] [ម៉ោង] LT",sameElse:"L"},relativeTime:{future:"%sទៀត",past:"%sមុន",s:"ប៉ុន្មានវិនាទី",m:"មួយនាទី",mm:"%d នាទី",h:"មួយម៉ោង",hh:"%d ម៉ោង",d:"មួយថ្ងៃ",dd:"%d ថ្ងៃ",M:"មួយខែ",MM:"%d ខែ",y:"មួយឆ្នាំ",yy:"%d ឆ្នាំ"},week:{dow:1,doy:4}}),nf.defineLocale("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 m분",LTS:"A h시 m분 s초",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 LT",LLLL:"YYYY년 MMMM D일 dddd LT"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇초",ss:"%d초",m:"일분",mm:"%d분",h:"한시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한달",MM:"%d달",y:"일년",yy:"%d년"},ordinalParse:/\d{1,2}일/,ordinal:"%d일",meridiemParse:/오전|오후/,isPM:function(a){return"오후"===a},meridiem:function(a,b,c){return 12>a?"오전":"오후"}}),nf.defineLocale("lb",{months:"Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mé_Dë_Më_Do_Fr_Sa".split("_"),longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gëschter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:dd,past:ed,s:"e puer Sekonnen",m:cd,mm:"%d Minutten",h:cd,hh:"%d Stonnen",d:cd,dd:"%d Deeg",M:cd,MM:"%d Méint",y:cd,yy:"%d Joer"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),{m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"}),Pf="sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_"),Qf=(nf.defineLocale("lt",{months:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:ld,weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], LT [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, LT [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], LT [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, LT [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:gd,m:hd,mm:kd,h:hd,hh:kd,d:hd,dd:kd,M:hd,MM:kd,y:hd,yy:kd},ordinalParse:/\d{1,2}-oji/,ordinal:function(a){return a+"-oji"},week:{dow:1,doy:4}}),{m:"minūtes_minūtēm_minūte_minūtes".split("_"),mm:"minūtes_minūtēm_minūte_minūtes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),MM:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")}),Rf=(nf.defineLocale("lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, LT",LLLL:"YYYY. [gada] D. MMMM, dddd, LT"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:pd,m:od,mm:nd,h:od,hh:nd,d:od,dd:nd,M:od,MM:nd,y:od,yy:nd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),{words:{m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=Rf.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+Rf.correctGrammaticalCase(a,d)}}),Sf=(nf.defineLocale("me",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedjelja","ponedjeljak","utorak","srijeda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sri.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedjelje] [u] LT","[prošlog] [ponedjeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srijede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",m:Rf.translate,mm:Rf.translate,h:Rf.translate,hh:Rf.translate,d:"dan",dd:Rf.translate,M:"mjesec",MM:Rf.translate,y:"godinu",yy:Rf.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),nf.defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Во изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Во изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"после %s",past:"пред %s",s:"неколку секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дена",M:"месец",MM:"%d месеци",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(a){var b=a%10,c=a%100;return 0===a?a+"-ев":0===c?a+"-ен":c>10&&20>c?a+"-ти":1===b?a+"-ви":2===b?a+"-ри":7===b||8===b?a+"-ми":a+"-ти"},week:{dow:1,doy:7}}),nf.defineLocale("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",LTS:"A h:mm:ss -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiemParse:/രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,isPM:function(a){return/^(ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി)$/.test(a)},meridiem:function(a,b,c){return 4>a?"രാത്രി":12>a?"രാവിലെ":17>a?"ഉച്ച കഴിഞ്ഞ്":20>a?"വൈകുന്നേരം":"രാത്രി"}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),Tf={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},Uf=(nf.defineLocale("mr",{months:"जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),monthsShort:"जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),weekdays:"रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm वाजता",LTS:"A h:mm:ss वाजता",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[उद्या] LT",nextWeek:"dddd, LT",lastDay:"[काल] LT",lastWeek:"[मागील] dddd, LT",sameElse:"L"},relativeTime:{future:"%s नंतर",past:"%s पूर्वी",s:"सेकंद",m:"एक मिनिट",mm:"%d मिनिटे",h:"एक तास",hh:"%d तास",d:"एक दिवस",dd:"%d दिवस",M:"एक महिना",MM:"%d महिने",y:"एक वर्ष",yy:"%d वर्षे"},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return Tf[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Sf[a]})},meridiemParse:/रात्री|सकाळी|दुपारी|सायंकाळी/,meridiemHour:function(a,b){return 12===a&&(a=0),"रात्री"===b?4>a?a:a+12:"सकाळी"===b?a:"दुपारी"===b?a>=10?a:a+12:"सायंकाळी"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"रात्री":10>a?"सकाळी":17>a?"दुपारी":20>a?"सायंकाळी":"रात्री"},week:{dow:0,doy:6}}),nf.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"LT.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"tengahari"===b?a>=11?a:a+12:"petang"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"tengahari":19>a?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),{1:"၁",2:"၂",3:"၃",4:"၄",5:"၅",6:"၆",7:"၇",8:"၈",9:"၉",0:"၀"}),Vf={"၁":"1","၂":"2","၃":"3","၄":"4","၅":"5", -"၆":"6","၇":"7","၈":"8","၉":"9","၀":"0"},Wf=(nf.defineLocale("my",{months:"ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ".split("_"),monthsShort:"ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ".split("_"),weekdays:"တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ".split("_"),weekdaysShort:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),weekdaysMin:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[ယနေ.] LT [မှာ]",nextDay:"[မနက်ဖြန်] LT [မှာ]",nextWeek:"dddd LT [မှာ]",lastDay:"[မနေ.က] LT [မှာ]",lastWeek:"[ပြီးခဲ့သော] dddd LT [မှာ]",sameElse:"L"},relativeTime:{future:"လာမည့် %s မှာ",past:"လွန်ခဲ့သော %s က",s:"စက္ကန်.အနည်းငယ်",m:"တစ်မိနစ်",mm:"%d မိနစ်",h:"တစ်နာရီ",hh:"%d နာရီ",d:"တစ်ရက်",dd:"%d ရက်",M:"တစ်လ",MM:"%d လ",y:"တစ်နှစ်",yy:"%d နှစ်"},preparse:function(a){return a.replace(/[၁၂၃၄၅၆၇၈၉၀]/g,function(a){return Vf[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Uf[a]})},week:{dow:1,doy:4}}),nf.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tirs_ons_tors_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"H.mm",LTS:"LT.ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en måned",MM:"%d måneder",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),Xf={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},Yf=(nf.defineLocale("ne",{months:"जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर".split("_"),monthsShort:"जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.".split("_"),weekdays:"आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार".split("_"),weekdaysShort:"आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.".split("_"),weekdaysMin:"आइ._सो._मङ्_बु._बि._शु._श.".split("_"),longDateFormat:{LT:"Aको h:mm बजे",LTS:"Aको h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return Xf[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Wf[a]})},meridiemParse:/राती|बिहान|दिउँसो|बेलुका|साँझ|राती/,meridiemHour:function(a,b){return 12===a&&(a=0),"राती"===b?3>a?a:a+12:"बिहान"===b?a:"दिउँसो"===b?a>=10?a:a+12:"बेलुका"===b||"साँझ"===b?a+12:void 0},meridiem:function(a,b,c){return 3>a?"राती":10>a?"बिहान":15>a?"दिउँसो":18>a?"बेलुका":20>a?"साँझ":"राती"},calendar:{sameDay:"[आज] LT",nextDay:"[भोली] LT",nextWeek:"[आउँदो] dddd[,] LT",lastDay:"[हिजो] LT",lastWeek:"[गएको] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%sमा",past:"%s अगाडी",s:"केही समय",m:"एक मिनेट",mm:"%d मिनेट",h:"एक घण्टा",hh:"%d घण्टा",d:"एक दिन",dd:"%d दिन",M:"एक महिना",MM:"%d महिना",y:"एक बर्ष",yy:"%d बर्ष"},week:{dow:1,doy:7}}),"jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_")),Zf="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),$f=(nf.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Zf[a.month()]:Yf[a.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,doy:4}}),nf.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"sun_mån_tys_ons_tor_fre_lau".split("_"),weekdaysMin:"su_må_ty_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I går klokka] LT",lastWeek:"[Føregåande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s sidan",s:"nokre sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",M:"ein månad",MM:"%d månader",y:"eit år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),"styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_")),_f="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),ag=(nf.defineLocale("pl",{months:function(a,b){return""===b?"("+_f[a.month()]+"|"+$f[a.month()]+")":/D MMMM/.test(b)?_f[a.month()]:$f[a.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:rd,mm:rd,h:rd,hh:rd,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:rd,y:"rok",yy:rd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("pt-br",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"}),nf.defineLocale("pt",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),nf.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:sd,h:"o oră",hh:sd,d:"o zi",dd:sd,M:"o lună",MM:sd,y:"un an",yy:sd},week:{dow:1,doy:7}}),nf.defineLocale("ru",{months:vd,monthsShort:wd,weekdays:xd,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(a){if(a.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:ud,mm:ud,h:"час",hh:ud,d:"день",dd:ud,M:"месяц",MM:ud,y:"год",yy:ud},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(a){return/^(дня|вечера)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночи":12>a?"утра":17>a?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":return a+"-й";case"D":return a+"-го";case"w":case"W":return a+"-я";default:return a}},week:{dow:1,doy:7}}),nf.defineLocale("si",{months:"ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්".split("_"),monthsShort:"ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ".split("_"),weekdays:"ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා".split("_"),weekdaysShort:"ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන".split("_"),weekdaysMin:"ඉ_ස_අ_බ_බ්‍ර_සි_සෙ".split("_"),longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, LT",LLLL:"YYYY MMMM D [වැනි] dddd, LTS"},calendar:{sameDay:"[අද] LT[ට]",nextDay:"[හෙට] LT[ට]",nextWeek:"dddd LT[ට]",lastDay:"[ඊයේ] LT[ට]",lastWeek:"[පසුගිය] dddd LT[ට]",sameElse:"L"},relativeTime:{future:"%sකින්",past:"%sකට පෙර",s:"තත්පර කිහිපය",m:"මිනිත්තුව",mm:"මිනිත්තු %d",h:"පැය",hh:"පැය %d",d:"දිනය",dd:"දින %d",M:"මාසය",MM:"මාස %d",y:"වසර",yy:"වසර %d"},ordinalParse:/\d{1,2} වැනි/,ordinal:function(a){return a+" වැනි"},meridiem:function(a,b,c){return a>11?c?"ප.ව.":"පස් වරු":c?"පෙ.ව.":"පෙර වරු"}}),"január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_")),bg="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_"),cg=(nf.defineLocale("sk",{months:ag,monthsShort:bg,monthsParse:function(a,b){var c,d=[];for(c=0;12>c;c++)d[c]=new RegExp("^"+a[c]+"$|^"+b[c]+"$","i");return d}(ag,bg),weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:zd,m:zd,mm:zd,h:zd,hh:zd,d:zd,dd:zd,M:zd,MM:zd,y:zd,yy:zd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),nf.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejšnjo] [nedeljo] [ob] LT";case 3:return"[prejšnjo] [sredo] [ob] LT";case 6:return"[prejšnjo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:Ad,m:Ad,mm:Ad,h:Ad,hh:Ad,d:Ad,dd:Ad,M:Ad,MM:Ad,y:Ad,yy:Ad},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),nf.defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"),weekdays:"E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"),weekdaysShort:"Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_Më_E_P_Sh".split("_"),meridiemParse:/PD|MD/,isPM:function(a){return"M"===a.charAt(0)},meridiem:function(a,b,c){return 12>a?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Sot në] LT",nextDay:"[Nesër në] LT",nextWeek:"dddd [në] LT",lastDay:"[Dje në] LT",lastWeek:"dddd [e kaluar në] LT",sameElse:"L"},relativeTime:{future:"në %s",past:"%s më parë",s:"disa sekonda",m:"një minutë",mm:"%d minuta",h:"një orë",hh:"%d orë",d:"një ditë",dd:"%d ditë",M:"një muaj",MM:"%d muaj",y:"një vit",yy:"%d vite"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),{words:{m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=cg.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+cg.correctGrammaticalCase(a,d)}}),dg=(nf.defineLocale("sr-cyrl",{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],monthsShort:["јан.","феб.","мар.","апр.","мај","јун","јул","авг.","сеп.","окт.","нов.","дец."],weekdays:["недеља","понедељак","уторак","среда","четвртак","петак","субота"],weekdaysShort:["нед.","пон.","уто.","сре.","чет.","пет.","суб."],weekdaysMin:["не","по","ут","ср","че","пе","су"],longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var a=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",m:cg.translate,mm:cg.translate,h:cg.translate,hh:cg.translate,d:"дан",dd:cg.translate,M:"месец",MM:cg.translate,y:"годину",yy:cg.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),{words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=dg.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+dg.correctGrammaticalCase(a,d)}}),eg=(nf.defineLocale("sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:dg.translate,mm:dg.translate,h:dg.translate,hh:dg.translate,d:"dan",dd:dg.translate,M:"mesec",MM:dg.translate,y:"godinu",yy:dg.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),nf.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"[På] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"e":1===b?"a":2===b?"a":"e";return a+c},week:{dow:1,doy:4}}),nf.defineLocale("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},ordinalParse:/\d{1,2}வது/,ordinal:function(a){return a+"வது"},meridiemParse:/யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,meridiem:function(a,b,c){return 2>a?" யாமம்":6>a?" வைகறை":10>a?" காலை":14>a?" நண்பகல்":18>a?" எற்பாடு":22>a?" மாலை":" யாமம்"},meridiemHour:function(a,b){return 12===a&&(a=0),"யாமம்"===b?2>a?a:a+12:"வைகறை"===b||"காலை"===b?a:"நண்பகல்"===b&&a>=10?a:a+12},week:{dow:0,doy:6}}),nf.defineLocale("th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",LTS:"LT s วินาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(a){return"หลังเที่ยง"===a},meridiem:function(a,b,c){return 12>a?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),nf.defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM DD, YYYY LT"},calendar:{sameDay:"[Ngayon sa] LT",nextDay:"[Bukas sa] LT",nextWeek:"dddd [sa] LT",lastDay:"[Kahapon sa] LT",lastWeek:"dddd [huling linggo] LT",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},ordinalParse:/\d{1,2}/,ordinal:function(a){return a},week:{dow:1,doy:4}}),{1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"}),fg=(nf.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinalParse:/\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,ordinal:function(a){if(0===a)return a+"'ıncı";var b=a%10,c=a%100-b,d=a>=100?100:null;return a+(eg[b]||eg[c]||eg[d])},week:{dow:1,doy:7}}),nf.defineLocale("tzm-latn",{months:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",m:"minuḍ",mm:"%d minuḍ",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}}),nf.defineLocale("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,doy:12}}),nf.defineLocale("uk",{months:Dd,monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:Ed,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., LT",LLLL:"dddd, D MMMM YYYY р., LT"},calendar:{sameDay:Fd("[Сьогодні "),nextDay:Fd("[Завтра "),lastDay:Fd("[Вчора "),nextWeek:Fd("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return Fd("[Минулої] dddd [").call(this);case 1:case 2:case 4:return Fd("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",m:Cd,mm:Cd,h:"годину",hh:Cd,d:"день",dd:Cd,M:"місяць",MM:Cd,y:"рік",yy:Cd},meridiemParse:/ночі|ранку|дня|вечора/,isPM:function(a){return/^(дня|вечора)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночі":12>a?"ранку":17>a?"дня":"вечора"},ordinalParse:/\d{1,2}-(й|го)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":case"w":case"W":return a+"-й";case"D":return a+"-го";default:return a}},week:{dow:1,doy:7}}),nf.defineLocale("uz",{months:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),weekdaysShort:"Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),weekdaysMin:"Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"D MMMM YYYY, dddd LT"},calendar:{sameDay:"[Бугун соат] LT [да]",nextDay:"[Эртага] LT [да]",nextWeek:"dddd [куни соат] LT [да]",lastDay:"[Кеча соат] LT [да]",lastWeek:"[Утган] dddd [куни соат] LT [да]",sameElse:"L"},relativeTime:{future:"Якин %s ичида",past:"Бир неча %s олдин",s:"фурсат",m:"бир дакика",mm:"%d дакика",h:"бир соат",hh:"%d соат",d:"бир кун",dd:"%d кун",M:"бир ой",MM:"%d ой",y:"бир йил",yy:"%d йил"},week:{dow:1,doy:7}}),nf.defineLocale("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY LT",LLLL:"dddd, D MMMM [năm] YYYY LT",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},ordinalParse:/\d{1,2}/,ordinal:function(a){return a},week:{dow:1,doy:4}}),nf.defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm分",LTS:"Ah点m分s秒",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(a,b){return 12===a&&(a=0),"凌晨"===b||"早上"===b||"上午"===b?a:"下午"===b||"晚上"===b?a+12:a>=11?a:a+12},meridiem:function(a,b,c){var d=100*a+b;return 600>d?"凌晨":900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var a,b;return a=nf().startOf("week"),b=this.unix()-a.unix()>=604800?"[下]":"[本]",0===this.minutes()?b+"dddAh点整":b+"dddAh点mm"},lastWeek:function(){var a,b;return a=nf().startOf("week"),b=this.unix()=11?a:a+12:"下午"===b||"晚上"===b?a+12:void 0},meridiem:function(a,b,c){var d=100*a+b; - -return 900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinalParse:/\d{1,2}(日|月|週)/,ordinal:function(a,b){switch(b){case"d":case"D":case"DDD":return a+"日";case"M":return a+"月";case"w":case"W":return a+"週";default:return a}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",m:"一分鐘",mm:"%d分鐘",h:"一小時",hh:"%d小時",d:"一天",dd:"%d天",M:"一個月",MM:"%d個月",y:"一年",yy:"%d年"}}),nf);return fg}); \ No newline at end of file +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return je.apply(null,arguments)} +// This is done to register the method called with moment() +// without creating circular dependencies. +function b(a){je=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){return"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a) +// even if its not own property I'd still call it non-empty +return!1;return!0}function f(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function g(a,b){var c,d=[];for(c=0;c0)for(c in le)d=le[c],e=b[d],o(e)||(a[d]=e);return a} +// Moment prototype object +function q(b){p(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),me===!1&&(me=!0,a.updateOffset(this),me=!1)}function r(a){return a instanceof q||null!=a&&null!=a._isAMomentObject}function s(a){return 0>a?Math.ceil(a)||0:Math.floor(a)}function t(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=s(b)),c} +// compare two arrays, return the number of differences +function u(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&t(a[d])!==t(b[d]))&&g++;return g+f}function v(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function w(b,c){var d=!0;return i(function(){return null!=a.deprecationHandler&&a.deprecationHandler(null,b),d&&(v(b+"\nArguments: "+Array.prototype.slice.call(arguments).join(", ")+"\n"+(new Error).stack),d=!1),c.apply(this,arguments)},c)}function x(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),ne[b]||(v(c),ne[b]=!0)}function y(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function z(a){var b,c;for(c in a)b=a[c],y(b)?this[c]=b:this["_"+c]=b;this._config=a, +// Lenient ordinal parsing accepts just a number in addition to +// number + (possibly) stuff coming from _ordinalParseLenient. +this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function A(a,b){var c,e=i({},a);for(c in b)h(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},i(e[c],a[c]),i(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)h(a,c)&&!h(b,c)&&d(a[c])&&( +// make sure changes to properties don't modify parent config +e[c]=i({},e[c]));return e}function B(a){null!=a&&this.set(a)}function C(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return y(d)?d.call(b,c):d}function D(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function E(){return this._invalidDate}function F(a){return this._ordinal.replace("%d",a)}function G(a,b,c,d){var e=this._relativeTime[c];return y(e)?e(a,b,c,d):e.replace(/%d/i,a)}function H(a,b){var c=this._relativeTime[a>0?"future":"past"];return y(c)?c(b):c.replace(/%s/i,b)}function I(a,b){var c=a.toLowerCase();we[c]=we[c+"s"]=we[b]=a}function J(a){return"string"==typeof a?we[a]||we[a.toLowerCase()]:void 0}function K(a){var b,c,d={};for(c in a)h(a,c)&&(b=J(c),b&&(d[b]=a[c]));return d}function L(a,b){xe[a]=b}function M(a){var b=[];for(var c in a)b.push({unit:c,priority:xe[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function N(b,c){return function(d){return null!=d?(P(this,b,d),a.updateOffset(this,c),this):O(this,b)}}function O(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function P(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} +// MOMENTS +function Q(a){return a=J(a),y(this[a])?this[a]():this}function R(a,b){if("object"==typeof a){a=K(a);for(var c=M(a),d=0;d=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} +// token: 'M' +// padded: ['MM', 2] +// ordinal: 'Mo' +// callback: function () { this.month() + 1 } +function T(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Be[a]=e),b&&(Be[b[0]]=function(){return S(e.apply(this,arguments),b[1],b[2])}),c&&(Be[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function U(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function V(a){var b,c,d=a.match(ye);for(b=0,c=d.length;c>b;b++)Be[d[b]]?d[b]=Be[d[b]]:d[b]=U(d[b]);return function(b){var e,f="";for(e=0;c>e;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}} +// format date using native date object +function W(a,b){return a.isValid()?(b=X(b,a.localeData()),Ae[b]=Ae[b]||V(b),Ae[b](a)):a.localeData().invalidDate()}function X(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(ze.lastIndex=0;d>=0&&ze.test(a);)a=a.replace(ze,c),ze.lastIndex=0,d-=1;return a}function Y(a,b,c){Te[a]=y(b)?b:function(a,d){return a&&c?c:b}}function Z(a,b){return h(Te,a)?Te[a](b._strict,b._locale):new RegExp($(a))} +// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function $(a){return _(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function _(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function aa(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=t(a)}),c=0;cd;++d)f=j([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=pe.call(this._shortMonthsParse,g),-1!==e?e:null):(e=pe.call(this._longMonthsParse,g),-1!==e?e:null):"MMM"===b?(e=pe.call(this._shortMonthsParse,g),-1!==e?e:(e=pe.call(this._longMonthsParse,g),-1!==e?e:null)):(e=pe.call(this._longMonthsParse,g),-1!==e?e:(e=pe.call(this._shortMonthsParse,g),-1!==e?e:null))}function ha(a,b,c){var d,e,f;if(this._monthsParseExact)return ga.call(this,a,b,c); +// TODO: add sorting +// Sorting makes sure if one month (or abbr) is a prefix of another +// see sorting in computeMonthsParse +for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;12>d;d++){ +// test the regex +if(e=j([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}} +// MOMENTS +function ia(a,b){var c;if(!a.isValid()) +// No op +return a;if("string"==typeof b)if(/^\d+$/.test(b))b=t(b);else +// TODO: Another silent failure? +if(b=a.localeData().monthsParse(b),"number"!=typeof b)return a;return c=Math.min(a.date(),da(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ja(b){return null!=b?(ia(this,b),a.updateOffset(this,!0),this):O(this,"Month")}function ka(){return da(this.year(),this.month())}function la(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=ff),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function ma(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=gf),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function na(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;12>b;b++)c=j([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for( +// Sorting makes sure if one month (or abbr) is a prefix of another it +// will match the longer piece. +d.sort(a),e.sort(a),f.sort(a),b=0;12>b;b++)d[b]=_(d[b]),e[b]=_(e[b]);for(b=0;24>b;b++)f[b]=_(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")} +// HELPERS +function oa(a){return pa(a)?366:365}function pa(a){return a%4===0&&a%100!==0||a%400===0}function qa(){return pa(this.year())}function ra(a,b,c,d,e,f,g){ +//can't just apply() to create a date: +//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply +var h=new Date(a,b,c,d,e,f,g); +//the date constructor remaps years 0-99 to 1900-1999 +return 100>a&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function sa(a){var b=new Date(Date.UTC.apply(null,arguments)); +//the Date.UTC function remaps years 0-99 to 1900-1999 +return 100>a&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} +// start-of-first-week - start-of-year +function ta(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) +d=7+b-c, +// first-week day local weekday -- which local weekday is fwd +e=(7+sa(a,0,d).getUTCDay()-b)%7;return-e+d-1} +//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday +function ua(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ta(a,d,e),j=1+7*(b-1)+h+i;return 0>=j?(f=a-1,g=oa(f)+j):j>oa(a)?(f=a+1,g=j-oa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function va(a,b,c){var d,e,f=ta(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return 1>g?(e=a.year()-1,d=g+wa(e,b,c)):g>wa(a.year(),b,c)?(d=g-wa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function wa(a,b,c){var d=ta(a,b,c),e=ta(a+1,b,c);return(oa(a)-d+e)/7} +// HELPERS +// LOCALES +function xa(a){return va(a,this._week.dow,this._week.doy).week}function ya(){return this._week.dow}function za(){return this._week.doy} +// MOMENTS +function Aa(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ba(a){var b=va(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} +// HELPERS +function Ca(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Da(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Ea(a,b){return c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]}function Fa(a){return this._weekdaysShort[a.day()]}function Ga(a){return this._weekdaysMin[a.day()]}function Ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;7>d;++d)f=j([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=pe.call(this._weekdaysParse,g),-1!==e?e:null):"ddd"===b?(e=pe.call(this._shortWeekdaysParse,g),-1!==e?e:null):(e=pe.call(this._minWeekdaysParse,g),-1!==e?e:null):"dddd"===b?(e=pe.call(this._weekdaysParse,g),-1!==e?e:(e=pe.call(this._shortWeekdaysParse,g),-1!==e?e:(e=pe.call(this._minWeekdaysParse,g),-1!==e?e:null))):"ddd"===b?(e=pe.call(this._shortWeekdaysParse,g),-1!==e?e:(e=pe.call(this._weekdaysParse,g),-1!==e?e:(e=pe.call(this._minWeekdaysParse,g),-1!==e?e:null))):(e=pe.call(this._minWeekdaysParse,g),-1!==e?e:(e=pe.call(this._weekdaysParse,g),-1!==e?e:(e=pe.call(this._shortWeekdaysParse,g),-1!==e?e:null)))}function Ia(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ha.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;7>d;d++){ +// test the regex +if(e=j([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} +// MOMENTS +function Ja(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Ca(a,this.localeData()),this.add(a-b,"d")):b}function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function La(a){if(!this.isValid())return null!=a?this:NaN; +// behaves the same as moment#day except +// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) +// as a setter, sunday should belong to the previous week. +if(null!=a){var b=Da(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Ma(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=nf),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Na(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=of),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Oa(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=pf),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Pa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],k=[];for(b=0;7>b;b++)c=j([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),k.push(d),k.push(e),k.push(f);for( +// Sorting makes sure if one weekday (or abbr) is a prefix of another it +// will match the longer piece. +g.sort(a),h.sort(a),i.sort(a),k.sort(a),b=0;7>b;b++)h[b]=_(h[b]),i[b]=_(i[b]),k[b]=_(k[b]);this._weekdaysRegex=new RegExp("^("+k.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} +// FORMATTING +function Qa(){return this.hours()%12||12}function Ra(){return this.hours()||24}function Sa(a,b){T(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} +// PARSING +function Ta(a,b){return b._meridiemParse} +// LOCALES +function Ua(a){ +// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays +// Using charAt should be more compatible. +return"p"===(a+"").toLowerCase().charAt(0)}function Va(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wa(a){return a?a.toLowerCase().replace("_","-"):a} +// pick the locale from the array +// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each +// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root +function Xa(a){for(var b,c,d,e,f=0;f0;){if(d=Ya(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&u(e,c,!0)>=b-1) +//the next array item is better than a shallower substring of this one +break;b--}f++}return null}function Ya(a){var b=null; +// TODO: Find a better way to register and load all the locales in Node +if(!uf[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=qf._abbr,require("./locale/"+a), +// because defineLocale currently also sets the global locale, we +// want to undo that for lazy loaded locales +Za(b)}catch(c){}return uf[a]} +// This function will load locale and then set the global locale. If +// no arguments are passed in, it will simply return the current global +// locale key. +function Za(a,b){var c; +// moment.duration._locale = moment._locale = data; +return a&&(c=o(b)?ab(a):$a(a,b),c&&(qf=c)),qf._abbr}function $a(a,b){if(null!==b){var c=tf; +// treat as if there is no base config +// backwards compat for now: also set the locale +return b.abbr=a,null!=uf[a]?(x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=uf[a]._config):null!=b.parentLocale&&(null!=uf[b.parentLocale]?c=uf[b.parentLocale]._config:x("parentLocaleUndefined","specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/")),uf[a]=new B(A(c,b)),Za(a),uf[a]} +// useful for testing +return delete uf[a],null}function _a(a,b){if(null!=b){var c,d=tf; +// MERGE +null!=uf[a]&&(d=uf[a]._config),b=A(d,b),c=new B(b),c.parentLocale=uf[a],uf[a]=c, +// backwards compat for now: also set the locale +Za(a)}else +// pass null for config to unupdate, useful for tests +null!=uf[a]&&(null!=uf[a].parentLocale?uf[a]=uf[a].parentLocale:null!=uf[a]&&delete uf[a]);return uf[a]} +// returns locale data +function ab(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return qf;if(!c(a)){if(b=Ya(a))return b;a=[a]}return Xa(a)}function bb(){return oe(uf)}function cb(a){var b,c=a._a;return c&&-2===l(a).overflow&&(b=c[We]<0||c[We]>11?We:c[Xe]<1||c[Xe]>da(c[Ve],c[We])?Xe:c[Ye]<0||c[Ye]>24||24===c[Ye]&&(0!==c[Ze]||0!==c[$e]||0!==c[_e])?Ye:c[Ze]<0||c[Ze]>59?Ze:c[$e]<0||c[$e]>59?$e:c[_e]<0||c[_e]>999?_e:-1,l(a)._overflowDayOfYear&&(Ve>b||b>Xe)&&(b=Xe),l(a)._overflowWeeks&&-1===b&&(b=af),l(a)._overflowWeekday&&-1===b&&(b=bf),l(a).overflow=b),a} +// date from iso format +function db(a){var b,c,d,e,f,g,h=a._i,i=vf.exec(h)||wf.exec(h);if(i){for(l(a).iso=!0,b=0,c=yf.length;c>b;b++)if(yf[b][1].exec(i[1])){e=yf[b][0],d=yf[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=zf.length;c>b;b++)if(zf[b][1].exec(i[3])){ +// match[2] should be 'T' or space +f=(i[2]||" ")+zf[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!xf.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),jb(a)}else a._isValid=!1} +// date from iso format or fallback +function eb(b){var c=Af.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(db(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))} +// Pick the first defined of two or three arguments. +function fb(a,b,c){return null!=a?a:null!=b?b:c}function gb(b){ +// hooks is actually the exported moment object +var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]} +// convert an array to a date. +// the array should mirror the parameters below +// note: all values past the year are optional and will default to the lowest possible value. +// [year, month, day , hour, minute, second, millisecond] +function hb(a){var b,c,d,e,f=[];if(!a._d){ +// Default to current date. +// * if no year, month, day of month are given, default to today +// * if day of month is given, default month and year +// * if month is given, default only year +// * if year is given, don't default anything +for(d=gb(a),a._w&&null==a._a[Xe]&&null==a._a[We]&&ib(a),a._dayOfYear&&(e=fb(a._a[Ve],d[Ve]),a._dayOfYear>oa(e)&&(l(a)._overflowDayOfYear=!0),c=sa(e,0,a._dayOfYear),a._a[We]=c.getUTCMonth(),a._a[Xe]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; +// Zero out whatever was not defaulted, including time +for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; +// Check for 24:00:00.000 +24===a._a[Ye]&&0===a._a[Ze]&&0===a._a[$e]&&0===a._a[_e]&&(a._nextDay=!0,a._a[Ye]=0),a._d=(a._useUTC?sa:ra).apply(null,f), +// Apply timezone offset from input. The actual utcOffset can be changed +// with parseZone. +null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[Ye]=24)}}function ib(a){var b,c,d,e,f,g,h,i;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=fb(b.GG,a._a[Ve],va(rb(),1,4).year),d=fb(b.W,1),e=fb(b.E,1),(1>e||e>7)&&(i=!0)):(f=a._locale._week.dow,g=a._locale._week.doy,c=fb(b.gg,a._a[Ve],va(rb(),f,g).year),d=fb(b.w,1),null!=b.d?(e=b.d,(0>e||e>6)&&(i=!0)):null!=b.e?(e=b.e+f,(b.e<0||b.e>6)&&(i=!0)):e=f),1>d||d>wa(c,f,g)?l(a)._overflowWeeks=!0:null!=i?l(a)._overflowWeekday=!0:(h=ua(c,d,e,f,g),a._a[Ve]=h.year,a._dayOfYear=h.dayOfYear)} +// date from string and format string +function jb(b){ +// TODO: Move this to another part of the creation flow to prevent circular deps +if(b._f===a.ISO_8601)return void db(b);b._a=[],l(b).empty=!0; +// This array is used to make a Date, either with `new Date` or `Date.UTC` +var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=X(b._f,b._locale).match(ye)||[],c=0;c0&&l(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),Be[f]?(d?l(b).empty=!1:l(b).unusedTokens.push(f),ca(f,d,b)):b._strict&&!d&&l(b).unusedTokens.push(f); +// add remaining unparsed input length to the string +l(b).charsLeftOver=i-j,h.length>0&&l(b).unusedInput.push(h), +// clear _12h flag if hour is <= 12 +b._a[Ye]<=12&&l(b).bigHour===!0&&b._a[Ye]>0&&(l(b).bigHour=void 0),l(b).parsedDateParts=b._a.slice(0),l(b).meridiem=b._meridiem, +// handle meridiem +b._a[Ye]=kb(b._locale,b._a[Ye],b._meridiem),hb(b),cb(b)}function kb(a,b,c){var d; +// Fallback +return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b} +// date from string and array of format strings +function lb(a){var b,c,d,e,f;if(0===a._f.length)return l(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;ef)&&(d=f,c=b));i(a,c||b)}function mb(a){if(!a._d){var b=K(a._i);a._a=g([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),hb(a)}}function nb(a){var b=new q(cb(ob(a))); +// Adding is smart enough around DST +return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function ob(a){var b=a._i,d=a._f;return a._locale=a._locale||ab(a._l),null===b||void 0===d&&""===b?n({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),r(b)?new q(cb(b)):(c(d)?lb(a):f(b)?a._d=b:d?jb(a):pb(a),m(a)||(a._d=null),a))}function pb(b){var d=b._i;void 0===d?b._d=new Date(a.now()):f(d)?b._d=new Date(d.valueOf()):"string"==typeof d?eb(b):c(d)?(b._a=g(d.slice(0),function(a){return parseInt(a,10)}),hb(b)):"object"==typeof d?mb(b):"number"==typeof d? +// from milliseconds +b._d=new Date(d):a.createFromInputFallback(b)}function qb(a,b,f,g,h){var i={}; +// object construction must be done this way. +// https://github.com/moment/moment/issues/1423 +return"boolean"==typeof f&&(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,nb(i)}function rb(a,b,c,d){return qb(a,b,c,d,!1)} +// Pick a moment m from moments so that m[fn](other) is true for all +// other. This relies on the function fn to be transitive. +// +// moments should either be an array of moment objects or an array, whose +// first element is an array of moment objects. +function sb(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return rb();for(d=b[0],e=1;ea&&(a=-a,c="-"),c+S(~~(a/60),2)+b+S(~~a%60,2)})}function yb(a,b){var c=(b||"").match(a)||[],d=c[c.length-1]||[],e=(d+"").match(Ef)||["-",0,0],f=+(60*e[1])+t(e[2]);return"+"===e[0]?f:-f} +// Return a moment from input, that is local/utc/zone equivalent to model. +function zb(b,c){var d,e; +// Use low-level api, because this fn is low-level api. +return c._isUTC?(d=c.clone(),e=(r(b)||f(b)?b.valueOf():rb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):rb(b).local()}function Ab(a){ +// On Firefox.24 Date#getTimezoneOffset returns a floating point. +// https://github.com/moment/moment/pull/1871 +return 15*-Math.round(a._d.getTimezoneOffset()/15)} +// MOMENTS +// keepLocalTime = true means only change the timezone, without +// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> +// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset +// +0200, so we adjust the time as needed, to be valid. +// +// Keeping the time actually adds/subtracts (one hour) +// from the actual represented time. That is why we call updateOffset +// a second time. In case it wants us to change the offset again +// _changeInProgress == true case, then we have to adjust, because +// there is no such time in the given timezone. +function Bb(b,c){var d,e=this._offset||0;return this.isValid()?null!=b?("string"==typeof b?b=yb(Qe,b):Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ab(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Sb(this,Mb(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ab(this):null!=b?this:NaN}function Cb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Db(a){return this.utcOffset(0,a)}function Eb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ab(this),"m")),this}function Fb(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(yb(Pe,this._i)),this}function Gb(a){return this.isValid()?(a=a?rb(a).utcOffset():0,(this.utcOffset()-a)%60===0):!1}function Hb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ib(){if(!o(this._isDSTShifted))return this._isDSTShifted;var a={};if(p(a,this),a=ob(a),a._a){var b=a._isUTC?j(a._a):rb(a._a);this._isDSTShifted=this.isValid()&&u(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Jb(){return this.isValid()?!this._isUTC:!1}function Kb(){return this.isValid()?this._isUTC:!1}function Lb(){return this.isValid()?this._isUTC&&0===this._offset:!1}function Mb(a,b){var c,d,e,f=a, +// matching against regexp is expensive, do it on demand +g=null;// checks for null or undefined +return wb(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=Ff.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:t(g[Xe])*c,h:t(g[Ye])*c,m:t(g[Ze])*c,s:t(g[$e])*c,ms:t(g[_e])*c}):(g=Gf.exec(a))?(c="-"===g[1]?-1:1,f={y:Nb(g[2],c),M:Nb(g[3],c),w:Nb(g[4],c),d:Nb(g[5],c),h:Nb(g[6],c),m:Nb(g[7],c),s:Nb(g[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Pb(rb(f.from),rb(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new vb(f),wb(a)&&h(a,"_locale")&&(d._locale=a._locale),d}function Nb(a,b){ +// We'd normally use ~~inp for this, but unfortunately it also +// converts floats to ints. +// inp may be undefined, so careful calling replace on it. +var c=a&&parseFloat(a.replace(",",".")); +// apply sign while we're at it +return(isNaN(c)?0:c)*b}function Ob(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Pb(a,b){var c;return a.isValid()&&b.isValid()?(b=zb(b,a),a.isBefore(b)?c=Ob(a,b):(c=Ob(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}}function Qb(a){return 0>a?-1*Math.round(-1*a):Math.round(a)} +// TODO: remove 'name' arg after deprecation is removed +function Rb(a,b){return function(c,d){var e,f; +//invert the arguments, but complain about it +return null===d||isNaN(+d)||(x(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Mb(c,d),Sb(this,e,a),this}}function Sb(b,c,d,e){var f=c._milliseconds,g=Qb(c._days),h=Qb(c._months);b.isValid()&&(e=null==e?!0:e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&P(b,"Date",O(b,"Date")+g*d),h&&ia(b,O(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Tb(a,b){var c=a.diff(b,"days",!0);return-6>c?"sameElse":-1>c?"lastWeek":0>c?"lastDay":1>c?"sameDay":2>c?"nextDay":7>c?"nextWeek":"sameElse"}function Ub(b,c){ +// We want to compare the start of today, vs this. +// Getting start-of-today depends on whether we're local/utc/offset or not. +var d=b||rb(),e=zb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(y(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,rb(d)))}function Vb(){return new q(this)}function Wb(a,b){var c=r(a)?a:rb(a);return this.isValid()&&c.isValid()?(b=J(o(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function cc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function dc(){var a=this.clone().utc();return 0f&&(b=f),Dc.call(this,a,b,c,d,e))}function Dc(a,b,c,d,e){var f=ua(a,b,c,d,e),g=sa(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} +// MOMENTS +function Ec(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} +// HELPERS +// MOMENTS +function Fc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Gc(a,b){b[_e]=t(1e3*("0."+a))} +// MOMENTS +function Hc(){return this._isUTC?"UTC":""}function Ic(){return this._isUTC?"Coordinated Universal Time":""}function Jc(a){return rb(1e3*a)}function Kc(){return rb.apply(null,arguments).parseZone()}function Lc(a){return a}function Mc(a,b,c,d){var e=ab(),f=j().set(d,b);return e[c](f,a)}function Nc(a,b,c){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return Mc(a,b,c,"month");var d,e=[];for(d=0;12>d;d++)e[d]=Mc(a,d,c,"month");return e} +// () +// (5) +// (fmt, 5) +// (fmt) +// (true) +// (true, 5) +// (true, fmt, 5) +// (true, fmt) +function Oc(a,b,c,d){"boolean"==typeof a?("number"==typeof b&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,"number"==typeof b&&(c=b,b=void 0),b=b||"");var e=ab(),f=a?e._week.dow:0;if(null!=c)return Mc(b,(c+f)%7,d,"day");var g,h=[];for(g=0;7>g;g++)h[g]=Mc(b,(g+f)%7,d,"day");return h}function Pc(a,b){return Nc(a,b,"months")}function Qc(a,b){return Nc(a,b,"monthsShort")}function Rc(a,b,c){return Oc(a,b,c,"weekdays")}function Sc(a,b,c){return Oc(a,b,c,"weekdaysShort")}function Tc(a,b,c){return Oc(a,b,c,"weekdaysMin")}function Uc(){var a=this._data;return this._milliseconds=Sf(this._milliseconds),this._days=Sf(this._days),this._months=Sf(this._months),a.milliseconds=Sf(a.milliseconds),a.seconds=Sf(a.seconds),a.minutes=Sf(a.minutes),a.hours=Sf(a.hours),a.months=Sf(a.months),a.years=Sf(a.years),this}function Vc(a,b,c,d){var e=Mb(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} +// supports only 2.0-style add(1, 's') or add(duration) +function Wc(a,b){return Vc(this,a,b,1)} +// supports only 2.0-style subtract(1, 's') or subtract(duration) +function Xc(a,b){return Vc(this,a,b,-1)}function Yc(a){return 0>a?Math.floor(a):Math.ceil(a)}function Zc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; +// if we have a mix of positive and negative values, bubble down first +// check: https://github.com/moment/moment/issues/2166 +// The following code bubbles up values, see the tests for +// examples of what that means. +// convert days to months +// 12 months -> 1 year +return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*Yc(_c(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=s(f/1e3),i.seconds=a%60,b=s(a/60),i.minutes=b%60,c=s(b/60),i.hours=c%24,g+=s(c/24),e=s($c(g)),h+=e,g-=Yc(_c(e)),d=s(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function $c(a){ +// 400 years have 146097 days (taking into account leap year rules) +// 400 years have 12 months === 4800 +return 4800*a/146097}function _c(a){ +// the reverse of daysToMonths +return 146097*a/4800}function ad(a){var b,c,d=this._milliseconds;if(a=J(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+$c(b),"month"===a?c:c/12;switch(b=this._days+Math.round(_c(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; +// Math.floor prevents floating point math errors here +case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} +// TODO: Use this.as('ms')? +function bd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*t(this._months/12)}function cd(a){return function(){return this.as(a)}}function dd(a){return a=J(a),this[a+"s"]()}function ed(a){return function(){return this._data[a]}}function fd(){return s(this.days()/7)} +// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize +function gd(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function hd(a,b,c){var d=Mb(a).abs(),e=gg(d.as("s")),f=gg(d.as("m")),g=gg(d.as("h")),h=gg(d.as("d")),i=gg(d.as("M")),j=gg(d.as("y")),k=e=f&&["m"]||f=g&&["h"]||g=h&&["d"]||h=i&&["M"]||i=j&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,gd.apply(null,k)} +// This function allows you to set the rounding function for relative time strings +function id(a){return void 0===a?gg:"function"==typeof a?(gg=a,!0):!1} +// This function allows you to set a threshold for relative time strings +function jd(a,b){return void 0===hg[a]?!1:void 0===b?hg[a]:(hg[a]=b,!0)}function kd(a){var b=this.localeData(),c=hd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function ld(){ +// for ISO strings we do not use the normal bubbling rules: +// * milliseconds bubble up until they become hours +// * days do not bubble at all +// * months bubble up until they become years +// This is because there is no context-free conversion between hours and days +// (think of clock changes) +// and also not between days and months (28-31 days per month) +var a,b,c,d=ig(this._milliseconds)/1e3,e=ig(this._days),f=ig(this._months);a=s(d/60),b=s(a/60),d%=60,a%=60,c=s(f/12),f%=12; +// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js +var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}function md(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function nd(a,b,c){var d={mm:b?"хвіліна_хвіліны_хвілін":"хвіліну_хвіліны_хвілін",hh:b?"гадзіна_гадзіны_гадзін":"гадзіну_гадзіны_гадзін",dd:"дзень_дні_дзён",MM:"месяц_месяцы_месяцаў",yy:"год_гады_гадоў"};return"m"===c?b?"хвіліна":"хвіліну":"h"===c?b?"гадзіна":"гадзіну":a+" "+md(d[c],+a)}function od(a,b,c){var d={mm:"munutenn",MM:"miz",dd:"devezh"};return a+" "+rd(d[c],a)}function pd(a){switch(qd(a)){case 1:case 3:case 4:case 5:case 9:return a+" bloaz";default:return a+" vloaz"}}function qd(a){return a>9?qd(a%10):a}function rd(a,b){return 2===b?sd(a):a}function sd(a){var b={m:"v",b:"v",d:"z"};return void 0===b[a.charAt(0)]?a:b[a.charAt(0)]+a.substring(1)}function td(a,b,c){var d=a+" ";switch(c){case"m":return b?"jedna minuta":"jedne minute";case"mm":return d+=1===a?"minuta":2===a||3===a||4===a?"minute":"minuta";case"h":return b?"jedan sat":"jednog sata";case"hh":return d+=1===a?"sat":2===a||3===a||4===a?"sata":"sati";case"dd":return d+=1===a?"dan":"dana";case"MM":return d+=1===a?"mjesec":2===a||3===a||4===a?"mjeseca":"mjeseci";case"yy":return d+=1===a?"godina":2===a||3===a||4===a?"godine":"godina"}}function ud(a){return a>1&&5>a&&1!==~~(a/10)}function vd(a,b,c,d){var e=a+" ";switch(c){case"s":// a few seconds / in a few seconds / a few seconds ago +return b||d?"pár sekund":"pár sekundami";case"m":// a minute / in a minute / a minute ago +return b?"minuta":d?"minutu":"minutou";case"mm":// 9 minutes / in 9 minutes / 9 minutes ago +// 9 minutes / in 9 minutes / 9 minutes ago +return b||d?e+(ud(a)?"minuty":"minut"):e+"minutami";break;case"h":// an hour / in an hour / an hour ago +return b?"hodina":d?"hodinu":"hodinou";case"hh":// 9 hours / in 9 hours / 9 hours ago +// 9 hours / in 9 hours / 9 hours ago +return b||d?e+(ud(a)?"hodiny":"hodin"):e+"hodinami";break;case"d":// a day / in a day / a day ago +return b||d?"den":"dnem";case"dd":// 9 days / in 9 days / 9 days ago +// 9 days / in 9 days / 9 days ago +return b||d?e+(ud(a)?"dny":"dní"):e+"dny";break;case"M":// a month / in a month / a month ago +return b||d?"měsíc":"měsícem";case"MM":// 9 months / in 9 months / 9 months ago +// 9 months / in 9 months / 9 months ago +return b||d?e+(ud(a)?"měsíce":"měsíců"):e+"měsíci";break;case"y":// a year / in a year / a year ago +return b||d?"rok":"rokem";case"yy":// 9 years / in 9 years / 9 years ago +// 9 years / in 9 years / 9 years ago +return b||d?e+(ud(a)?"roky":"let"):e+"lety"}}function wd(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]}function xd(a,b,c,d){var e={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[a+" Tage",a+" Tagen"],M:["ein Monat","einem Monat"],MM:[a+" Monate",a+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[a+" Jahre",a+" Jahren"]};return b?e[c][0]:e[c][1]}function yd(a,b,c,d){var e={s:["mõne sekundi","mõni sekund","paar sekundit"],m:["ühe minuti","üks minut"],mm:[a+" minuti",a+" minutit"],h:["ühe tunni","tund aega","üks tund"],hh:[a+" tunni",a+" tundi"],d:["ühe päeva","üks päev"],M:["kuu aja","kuu aega","üks kuu"],MM:[a+" kuu",a+" kuud"],y:["ühe aasta","aasta","üks aasta"],yy:[a+" aasta",a+" aastat"]};return b?e[c][2]?e[c][2]:e[c][1]:d?e[c][0]:e[c][1]}function zd(a,b,c,d){var e="";switch(c){case"s":return d?"muutaman sekunnin":"muutama sekunti";case"m":return d?"minuutin":"minuutti";case"mm":e=d?"minuutin":"minuuttia";break;case"h":return d?"tunnin":"tunti";case"hh":e=d?"tunnin":"tuntia";break;case"d":return d?"päivän":"päivä";case"dd":e=d?"päivän":"päivää";break;case"M":return d?"kuukauden":"kuukausi";case"MM":e=d?"kuukauden":"kuukautta";break;case"y":return d?"vuoden":"vuosi";case"yy":e=d?"vuoden":"vuotta"}return e=Ad(a,d)+" "+e}function Ad(a,b){return 10>a?b?Jg[a]:Ig[a]:a}function Bd(a,b,c){var d=a+" ";switch(c){case"m":return b?"jedna minuta":"jedne minute";case"mm":return d+=1===a?"minuta":2===a||3===a||4===a?"minute":"minuta";case"h":return b?"jedan sat":"jednog sata";case"hh":return d+=1===a?"sat":2===a||3===a||4===a?"sata":"sati";case"dd":return d+=1===a?"dan":"dana";case"MM":return d+=1===a?"mjesec":2===a||3===a||4===a?"mjeseca":"mjeseci";case"yy":return d+=1===a?"godina":2===a||3===a||4===a?"godine":"godina"}}function Cd(a,b,c,d){var e=a;switch(c){case"s":return d||b?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(d||b?" perc":" perce");case"mm":return e+(d||b?" perc":" perce");case"h":return"egy"+(d||b?" óra":" órája");case"hh":return e+(d||b?" óra":" órája");case"d":return"egy"+(d||b?" nap":" napja");case"dd":return e+(d||b?" nap":" napja");case"M":return"egy"+(d||b?" hónap":" hónapja");case"MM":return e+(d||b?" hónap":" hónapja");case"y":return"egy"+(d||b?" év":" éve");case"yy":return e+(d||b?" év":" éve")}return""}function Dd(a){return(a?"":"[múlt] ")+"["+Tg[this.day()]+"] LT[-kor]"}function Ed(a){return a%100===11?!0:a%10!==1}function Fd(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return b?"mínúta":"mínútu";case"mm":return Ed(a)?e+(b||d?"mínútur":"mínútum"):b?e+"mínúta":e+"mínútu";case"hh":return Ed(a)?e+(b||d?"klukkustundir":"klukkustundum"):e+"klukkustund";case"d":return b?"dagur":d?"dag":"degi";case"dd":return Ed(a)?b?e+"dagar":e+(d?"daga":"dögum"):b?e+"dagur":e+(d?"dag":"degi");case"M":return b?"mánuður":d?"mánuð":"mánuði";case"MM":return Ed(a)?b?e+"mánuðir":e+(d?"mánuði":"mánuðum"):b?e+"mánuður":e+(d?"mánuð":"mánuði");case"y":return b||d?"ár":"ári";case"yy":return Ed(a)?e+(b||d?"ár":"árum"):e+(b||d?"ár":"ári")}}function Gd(a,b,c,d){var e={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return b?e[c][0]:e[c][1]}function Hd(a){var b=a.substr(0,a.indexOf(" "));return Jd(b)?"a "+a:"an "+a}function Id(a){var b=a.substr(0,a.indexOf(" "));return Jd(b)?"viru "+a:"virun "+a}/** + * Returns true if the word before the given number loses the '-n' ending. + * e.g. 'an 10 Deeg' but 'a 5 Deeg' + * + * @param number {integer} + * @returns {boolean} + */ +function Jd(a){if(a=parseInt(a,10),isNaN(a))return!1;if(0>a) +// Negative Number --> always true +return!0;if(10>a) +// Only 1 digit +return a>=4&&7>=a;if(100>a){ +// 2 digits +var b=a%10,c=a/10;return Jd(0===b?c:b)}if(1e4>a){ +// 3 or 4 digits --> recursively check first digit +for(;a>=10;)a/=10;return Jd(a)} +// Anything larger than 4 digits: recursively check first n-3 digits +return a/=1e3,Jd(a)}function Kd(a,b,c,d){return b?"kelios sekundės":d?"kelių sekundžių":"kelias sekundes"}function Ld(a,b,c,d){return b?Nd(c)[0]:d?Nd(c)[1]:Nd(c)[2]}function Md(a){return a%10===0||a>10&&20>a}function Nd(a){return Wg[a].split("_")}function Od(a,b,c,d){var e=a+" ";return 1===a?e+Ld(a,b,c[0],d):b?e+(Md(a)?Nd(c)[1]:Nd(c)[0]):d?e+Nd(c)[1]:e+(Md(a)?Nd(c)[1]:Nd(c)[2])}/** + * @param withoutSuffix boolean true = a length of time; false = before/after a period of time. + */ +function Pd(a,b,c){return c?b%10===1&&b%100!==11?a[2]:a[3]:b%10===1&&b%100!==11?a[0]:a[1]}function Qd(a,b,c){return a+" "+Pd(Xg[c],a,b)}function Rd(a,b,c){return Pd(Xg[c],a,b)}function Sd(a,b){return b?"dažas sekundes":"dažām sekundēm"}function Td(a,b,c,d){var e="";if(b)switch(c){case"s":e="काही सेकंद";break;case"m":e="एक मिनिट";break;case"mm":e="%d मिनिटे";break;case"h":e="एक तास";break;case"hh":e="%d तास";break;case"d":e="एक दिवस";break;case"dd":e="%d दिवस";break;case"M":e="एक महिना";break;case"MM":e="%d महिने";break;case"y":e="एक वर्ष";break;case"yy":e="%d वर्षे"}else switch(c){case"s":e="काही सेकंदां";break;case"m":e="एका मिनिटा";break;case"mm":e="%d मिनिटां";break;case"h":e="एका तासा";break;case"hh":e="%d तासां";break;case"d":e="एका दिवसा";break;case"dd":e="%d दिवसां";break;case"M":e="एका महिन्या";break;case"MM":e="%d महिन्यां";break;case"y":e="एका वर्षा";break;case"yy":e="%d वर्षां"}return e.replace(/%d/i,a)}function Ud(a){return 5>a%10&&a%10>1&&~~(a/10)%10!==1}function Vd(a,b,c){var d=a+" ";switch(c){case"m":return b?"minuta":"minutę";case"mm":return d+(Ud(a)?"minuty":"minut");case"h":return b?"godzina":"godzinę";case"hh":return d+(Ud(a)?"godziny":"godzin");case"MM":return d+(Ud(a)?"miesiące":"miesięcy");case"yy":return d+(Ud(a)?"lata":"lat")}}function Wd(a,b,c){var d={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},e=" ";return(a%100>=20||a>=100&&a%100===0)&&(e=" de "),a+e+d[c]}function Xd(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function Yd(a,b,c){var d={mm:b?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===c?b?"минута":"минуту":a+" "+Xd(d[c],+a)}function Zd(a){return a>1&&5>a}function $d(a,b,c,d){var e=a+" ";switch(c){case"s":// a few seconds / in a few seconds / a few seconds ago +return b||d?"pár sekúnd":"pár sekundami";case"m":// a minute / in a minute / a minute ago +return b?"minúta":d?"minútu":"minútou";case"mm":// 9 minutes / in 9 minutes / 9 minutes ago +// 9 minutes / in 9 minutes / 9 minutes ago +return b||d?e+(Zd(a)?"minúty":"minút"):e+"minútami";break;case"h":// an hour / in an hour / an hour ago +return b?"hodina":d?"hodinu":"hodinou";case"hh":// 9 hours / in 9 hours / 9 hours ago +// 9 hours / in 9 hours / 9 hours ago +return b||d?e+(Zd(a)?"hodiny":"hodín"):e+"hodinami";break;case"d":// a day / in a day / a day ago +return b||d?"deň":"dňom";case"dd":// 9 days / in 9 days / 9 days ago +// 9 days / in 9 days / 9 days ago +return b||d?e+(Zd(a)?"dni":"dní"):e+"dňami";break;case"M":// a month / in a month / a month ago +return b||d?"mesiac":"mesiacom";case"MM":// 9 months / in 9 months / 9 months ago +// 9 months / in 9 months / 9 months ago +return b||d?e+(Zd(a)?"mesiace":"mesiacov"):e+"mesiacmi";break;case"y":// a year / in a year / a year ago +return b||d?"rok":"rokom";case"yy":// 9 years / in 9 years / 9 years ago +// 9 years / in 9 years / 9 years ago +return b||d?e+(Zd(a)?"roky":"rokov"):e+"rokmi"}}function _d(a,b,c,d){var e=a+" ";switch(c){case"s":return b||d?"nekaj sekund":"nekaj sekundami";case"m":return b?"ena minuta":"eno minuto";case"mm":return e+=1===a?b?"minuta":"minuto":2===a?b||d?"minuti":"minutama":5>a?b||d?"minute":"minutami":b||d?"minut":"minutami";case"h":return b?"ena ura":"eno uro";case"hh":return e+=1===a?b?"ura":"uro":2===a?b||d?"uri":"urama":5>a?b||d?"ure":"urami":b||d?"ur":"urami";case"d":return b||d?"en dan":"enim dnem";case"dd":return e+=1===a?b||d?"dan":"dnem":2===a?b||d?"dni":"dnevoma":b||d?"dni":"dnevi";case"M":return b||d?"en mesec":"enim mesecem";case"MM":return e+=1===a?b||d?"mesec":"mesecem":2===a?b||d?"meseca":"mesecema":5>a?b||d?"mesece":"meseci":b||d?"mesecev":"meseci";case"y":return b||d?"eno leto":"enim letom";case"yy":return e+=1===a?b||d?"leto":"letom":2===a?b||d?"leti":"letoma":5>a?b||d?"leta":"leti":b||d?"let":"leti"}}function ae(a){var b=a;return b=-1!==a.indexOf("jaj")?b.slice(0,-3)+"leS":-1!==a.indexOf("jar")?b.slice(0,-3)+"waQ":-1!==a.indexOf("DIS")?b.slice(0,-3)+"nem":b+" pIq"}function be(a){var b=a;return b=-1!==a.indexOf("jaj")?b.slice(0,-3)+"Hu’":-1!==a.indexOf("jar")?b.slice(0,-3)+"wen":-1!==a.indexOf("DIS")?b.slice(0,-3)+"ben":b+" ret"}function ce(a,b,c,d){var e=de(a);switch(c){case"mm":return e+" tup";case"hh":return e+" rep";case"dd":return e+" jaj";case"MM":return e+" jar";case"yy":return e+" DIS"}}function de(a){var b=Math.floor(a%1e3/100),c=Math.floor(a%100/10),d=a%10,e="";return b>0&&(e+=qh[b]+"vatlh"),c>0&&(e+=(""!==e?" ":"")+qh[c]+"maH"),d>0&&(e+=(""!==e?" ":"")+qh[d]),""===e?"pagh":e}function ee(a,b,c,d){var e={s:["viensas secunds","'iensas secunds"],m:["'n míut","'iens míut"],mm:[a+" míuts",""+a+" míuts"],h:["'n þora","'iensa þora"],hh:[a+" þoras",""+a+" þoras"],d:["'n ziua","'iensa ziua"],dd:[a+" ziuas",""+a+" ziuas"],M:["'n mes","'iens mes"],MM:[a+" mesen",""+a+" mesen"],y:["'n ar","'iens ar"],yy:[a+" ars",""+a+" ars"]};return d?e[c][0]:b?e[c][0]:e[c][1]}function fe(a,b){var c=a.split("_");return b%10===1&&b%100!==11?c[0]:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?c[1]:c[2]}function ge(a,b,c){var d={mm:b?"хвилина_хвилини_хвилин":"хвилину_хвилини_хвилин",hh:b?"година_години_годин":"годину_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===c?b?"хвилина":"хвилину":"h"===c?b?"година":"годину":a+" "+fe(d[c],+a)}function he(a,b){var c={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")},d=/(\[[ВвУу]\]) ?dddd/.test(b)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(b)?"genitive":"nominative";return c[d][a.day()]}function ie(a){return function(){return a+"о"+(11===this.hours()?"б":"")+"] LT"}}var je,ke;ke=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;c>d;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1}; +// Plugins that add properties should also add the key here (null value), +// so we can properly clone ourselves. +var le=a.momentProperties=[],me=!1,ne={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var oe;oe=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)h(a,b)&&c.push(b);return c};var pe,qe={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},re={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},se="Invalid date",te="%d",ue=/\d{1,2}/,ve={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},we={},xe={},ye=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,ze=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Ae={},Be={},Ce=/\d/,De=/\d\d/,Ee=/\d{3}/,Fe=/\d{4}/,Ge=/[+-]?\d{6}/,He=/\d\d?/,Ie=/\d\d\d\d?/,Je=/\d\d\d\d\d\d?/,Ke=/\d{1,3}/,Le=/\d{1,4}/,Me=/[+-]?\d{1,6}/,Ne=/\d+/,Oe=/[+-]?\d+/,Pe=/Z|[+-]\d\d:?\d\d/gi,Qe=/Z|[+-]\d\d(?::?\d\d)?/gi,Re=/[+-]?\d+(\.\d{1,3})?/,Se=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Te={},Ue={},Ve=0,We=1,Xe=2,Ye=3,Ze=4,$e=5,_e=6,af=7,bf=8;pe=Array.prototype.indexOf?Array.prototype.indexOf:function(a){ +// I know +var b;for(b=0;b=a?""+a:"+"+a}),T(0,["YY",2],0,function(){return this.year()%100}),T(0,["YYYY",4],0,"year"),T(0,["YYYYY",5],0,"year"),T(0,["YYYYYY",6,!0],0,"year"), +// ALIASES +I("year","y"), +// PRIORITIES +L("year",1), +// PARSING +Y("Y",Oe),Y("YY",He,De),Y("YYYY",Le,Fe),Y("YYYYY",Me,Ge),Y("YYYYYY",Me,Ge),aa(["YYYYY","YYYYYY"],Ve),aa("YYYY",function(b,c){c[Ve]=2===b.length?a.parseTwoDigitYear(b):t(b)}),aa("YY",function(b,c){c[Ve]=a.parseTwoDigitYear(b)}),aa("Y",function(a,b){b[Ve]=parseInt(a,10)}), +// HOOKS +a.parseTwoDigitYear=function(a){return t(a)+(t(a)>68?1900:2e3)}; +// MOMENTS +var hf=N("FullYear",!0); +// FORMATTING +T("w",["ww",2],"wo","week"),T("W",["WW",2],"Wo","isoWeek"), +// ALIASES +I("week","w"),I("isoWeek","W"), +// PRIORITIES +L("week",5),L("isoWeek",5), +// PARSING +Y("w",He),Y("ww",He,De),Y("W",He),Y("WW",He,De),ba(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=t(a)});var jf={dow:0,// Sunday is the first day of the week. +doy:6}; +// FORMATTING +T("d",0,"do","day"),T("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),T("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),T("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),T("e",0,0,"weekday"),T("E",0,0,"isoWeekday"), +// ALIASES +I("day","d"),I("weekday","e"),I("isoWeekday","E"), +// PRIORITY +L("day",11),L("weekday",11),L("isoWeekday",11), +// PARSING +Y("d",He),Y("e",He),Y("E",He),Y("dd",function(a,b){return b.weekdaysMinRegex(a)}),Y("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Y("dddd",function(a,b){return b.weekdaysRegex(a)}),ba(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); +// if we didn't get a weekday name, mark the date as invalid +null!=e?b.d=e:l(c).invalidWeekday=a}),ba(["d","e","E"],function(a,b,c,d){b[d]=t(a)}); +// LOCALES +var kf="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),lf="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),mf="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),nf=Se,of=Se,pf=Se;T("H",["HH",2],0,"hour"),T("h",["hh",2],0,Qa),T("k",["kk",2],0,Ra),T("hmm",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)}),T("hmmss",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)+S(this.seconds(),2)}),T("Hmm",0,0,function(){return""+this.hours()+S(this.minutes(),2)}),T("Hmmss",0,0,function(){return""+this.hours()+S(this.minutes(),2)+S(this.seconds(),2)}),Sa("a",!0),Sa("A",!1), +// ALIASES +I("hour","h"), +// PRIORITY +L("hour",13),Y("a",Ta),Y("A",Ta),Y("H",He),Y("h",He),Y("HH",He,De),Y("hh",He,De),Y("hmm",Ie),Y("hmmss",Je),Y("Hmm",Ie),Y("Hmmss",Je),aa(["H","HH"],Ye),aa(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),aa(["h","hh"],function(a,b,c){b[Ye]=t(a),l(c).bigHour=!0}),aa("hmm",function(a,b,c){var d=a.length-2;b[Ye]=t(a.substr(0,d)),b[Ze]=t(a.substr(d)),l(c).bigHour=!0}),aa("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[Ye]=t(a.substr(0,d)),b[Ze]=t(a.substr(d,2)),b[$e]=t(a.substr(e)),l(c).bigHour=!0}),aa("Hmm",function(a,b,c){var d=a.length-2;b[Ye]=t(a.substr(0,d)),b[Ze]=t(a.substr(d))}),aa("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[Ye]=t(a.substr(0,d)),b[Ze]=t(a.substr(d,2)),b[$e]=t(a.substr(e))});var qf,rf=/[ap]\.?m?\.?/i,sf=N("Hours",!0),tf={calendar:qe,longDateFormat:re,invalidDate:se,ordinal:te,ordinalParse:ue,relativeTime:ve,months:df,monthsShort:ef,week:jf,weekdays:kf,weekdaysMin:mf,weekdaysShort:lf,meridiemParse:rf},uf={},vf=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,wf=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,xf=/Z|[+-]\d\d(?::?\d\d)?/,yf=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], +// YYYYMM is NOT allowed by the standard +["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],zf=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Af=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=w("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), +// constant that refers to the ISO standard +a.ISO_8601=function(){};var Bf=w("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?this>a?this:a:n()}),Cf=w("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:n()}),Df=function(){return Date.now?Date.now():+new Date};xb("Z",":"),xb("ZZ",""), +// PARSING +Y("Z",Qe),Y("ZZ",Qe),aa(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=yb(Qe,a)}); +// HELPERS +// timezone chunker +// '+10:00' > ['10', '00'] +// '-1530' > ['-15', '30'] +var Ef=/([\+\-]|\d\d)/gi; +// HOOKS +// This function will be called whenever a moment is mutated. +// It is intended to keep the offset in sync with the timezone. +a.updateOffset=function(){}; +// ASP.NET json date format regex +var Ff=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/,Gf=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Mb.fn=vb.prototype;var Hf=Rb(1,"add"),If=Rb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Jf=w("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); +// FORMATTING +T(0,["gg",2],0,function(){return this.weekYear()%100}),T(0,["GG",2],0,function(){return this.isoWeekYear()%100}),xc("gggg","weekYear"),xc("ggggg","weekYear"),xc("GGGG","isoWeekYear"),xc("GGGGG","isoWeekYear"), +// ALIASES +I("weekYear","gg"),I("isoWeekYear","GG"), +// PRIORITY +L("weekYear",1),L("isoWeekYear",1), +// PARSING +Y("G",Oe),Y("g",Oe),Y("GG",He,De),Y("gg",He,De),Y("GGGG",Le,Fe),Y("gggg",Le,Fe),Y("GGGGG",Me,Ge),Y("ggggg",Me,Ge),ba(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=t(a)}),ba(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), +// FORMATTING +T("Q",0,"Qo","quarter"), +// ALIASES +I("quarter","Q"), +// PRIORITY +L("quarter",7), +// PARSING +Y("Q",Ce),aa("Q",function(a,b){b[We]=3*(t(a)-1)}), +// FORMATTING +T("D",["DD",2],"Do","date"), +// ALIASES +I("date","D"), +// PRIOROITY +L("date",9), +// PARSING +Y("D",He),Y("DD",He,De),Y("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),aa(["D","DD"],Xe),aa("Do",function(a,b){b[Xe]=t(a.match(He)[0],10)}); +// MOMENTS +var Kf=N("Date",!0); +// FORMATTING +T("DDD",["DDDD",3],"DDDo","dayOfYear"), +// ALIASES +I("dayOfYear","DDD"), +// PRIORITY +L("dayOfYear",4), +// PARSING +Y("DDD",Ke),Y("DDDD",Ee),aa(["DDD","DDDD"],function(a,b,c){c._dayOfYear=t(a)}), +// FORMATTING +T("m",["mm",2],0,"minute"), +// ALIASES +I("minute","m"), +// PRIORITY +L("minute",14), +// PARSING +Y("m",He),Y("mm",He,De),aa(["m","mm"],Ze); +// MOMENTS +var Lf=N("Minutes",!1); +// FORMATTING +T("s",["ss",2],0,"second"), +// ALIASES +I("second","s"), +// PRIORITY +L("second",15), +// PARSING +Y("s",He),Y("ss",He,De),aa(["s","ss"],$e); +// MOMENTS +var Mf=N("Seconds",!1); +// FORMATTING +T("S",0,0,function(){return~~(this.millisecond()/100)}),T(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),T(0,["SSS",3],0,"millisecond"),T(0,["SSSS",4],0,function(){return 10*this.millisecond()}),T(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),T(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),T(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),T(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),T(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), +// ALIASES +I("millisecond","ms"), +// PRIORITY +L("millisecond",16), +// PARSING +Y("S",Ke,Ce),Y("SS",Ke,De),Y("SSS",Ke,Ee);var Nf;for(Nf="SSSS";Nf.length<=9;Nf+="S")Y(Nf,Ne);for(Nf="S";Nf.length<=9;Nf+="S")aa(Nf,Gc); +// MOMENTS +var Of=N("Milliseconds",!1); +// FORMATTING +T("z",0,0,"zoneAbbr"),T("zz",0,0,"zoneName");var Pf=q.prototype;Pf.add=Hf,Pf.calendar=Ub,Pf.clone=Vb,Pf.diff=ac,Pf.endOf=mc,Pf.format=ec,Pf.from=fc,Pf.fromNow=gc,Pf.to=hc,Pf.toNow=ic,Pf.get=Q,Pf.invalidAt=vc,Pf.isAfter=Wb,Pf.isBefore=Xb,Pf.isBetween=Yb,Pf.isSame=Zb,Pf.isSameOrAfter=$b,Pf.isSameOrBefore=_b,Pf.isValid=tc,Pf.lang=Jf,Pf.locale=jc,Pf.localeData=kc,Pf.max=Cf,Pf.min=Bf,Pf.parsingFlags=uc,Pf.set=R,Pf.startOf=lc,Pf.subtract=If,Pf.toArray=qc,Pf.toObject=rc,Pf.toDate=pc,Pf.toISOString=dc,Pf.toJSON=sc,Pf.toString=cc,Pf.unix=oc,Pf.valueOf=nc,Pf.creationData=wc, +// Year +Pf.year=hf,Pf.isLeapYear=qa, +// Week Year +Pf.weekYear=yc,Pf.isoWeekYear=zc, +// Quarter +Pf.quarter=Pf.quarters=Ec, +// Month +Pf.month=ja,Pf.daysInMonth=ka, +// Week +Pf.week=Pf.weeks=Aa,Pf.isoWeek=Pf.isoWeeks=Ba,Pf.weeksInYear=Bc,Pf.isoWeeksInYear=Ac, +// Day +Pf.date=Kf,Pf.day=Pf.days=Ja,Pf.weekday=Ka,Pf.isoWeekday=La,Pf.dayOfYear=Fc, +// Hour +Pf.hour=Pf.hours=sf, +// Minute +Pf.minute=Pf.minutes=Lf, +// Second +Pf.second=Pf.seconds=Mf, +// Millisecond +Pf.millisecond=Pf.milliseconds=Of, +// Offset +Pf.utcOffset=Bb,Pf.utc=Db,Pf.local=Eb,Pf.parseZone=Fb,Pf.hasAlignedHourOffset=Gb,Pf.isDST=Hb,Pf.isLocal=Jb,Pf.isUtcOffset=Kb,Pf.isUtc=Lb,Pf.isUTC=Lb, +// Timezone +Pf.zoneAbbr=Hc,Pf.zoneName=Ic, +// Deprecations +Pf.dates=w("dates accessor is deprecated. Use date instead.",Kf),Pf.months=w("months accessor is deprecated. Use month instead",ja),Pf.years=w("years accessor is deprecated. Use year instead",hf),Pf.zone=w("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Cb),Pf.isDSTShifted=w("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Ib);var Qf=Pf,Rf=B.prototype;Rf.calendar=C,Rf.longDateFormat=D,Rf.invalidDate=E,Rf.ordinal=F,Rf.preparse=Lc,Rf.postformat=Lc,Rf.relativeTime=G,Rf.pastFuture=H,Rf.set=z, +// Month +Rf.months=ea,Rf.monthsShort=fa,Rf.monthsParse=ha,Rf.monthsRegex=ma,Rf.monthsShortRegex=la, +// Week +Rf.week=xa,Rf.firstDayOfYear=za,Rf.firstDayOfWeek=ya, +// Day of Week +Rf.weekdays=Ea,Rf.weekdaysMin=Ga,Rf.weekdaysShort=Fa,Rf.weekdaysParse=Ia,Rf.weekdaysRegex=Ma,Rf.weekdaysShortRegex=Na,Rf.weekdaysMinRegex=Oa, +// Hours +Rf.isPM=Ua,Rf.meridiem=Va,Za("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===t(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), +// Side effect imports +a.lang=w("moment.lang is deprecated. Use moment.locale instead.",Za),a.langData=w("moment.langData is deprecated. Use moment.localeData instead.",ab);var Sf=Math.abs,Tf=cd("ms"),Uf=cd("s"),Vf=cd("m"),Wf=cd("h"),Xf=cd("d"),Yf=cd("w"),Zf=cd("M"),$f=cd("y"),_f=ed("milliseconds"),ag=ed("seconds"),bg=ed("minutes"),cg=ed("hours"),dg=ed("days"),eg=ed("months"),fg=ed("years"),gg=Math.round,hg={s:45,// seconds to minute +m:45,// minutes to hour +h:22,// hours to day +d:26,// days to month +M:11},ig=Math.abs,jg=vb.prototype;jg.abs=Uc,jg.add=Wc,jg.subtract=Xc,jg.as=ad,jg.asMilliseconds=Tf,jg.asSeconds=Uf,jg.asMinutes=Vf,jg.asHours=Wf,jg.asDays=Xf,jg.asWeeks=Yf,jg.asMonths=Zf,jg.asYears=$f,jg.valueOf=bd,jg._bubble=Zc,jg.get=dd,jg.milliseconds=_f,jg.seconds=ag,jg.minutes=bg,jg.hours=cg,jg.days=dg,jg.weeks=fd,jg.months=eg,jg.years=fg,jg.humanize=kd,jg.toISOString=ld,jg.toString=ld,jg.toJSON=ld,jg.locale=jc,jg.localeData=kc, +// Deprecations +jg.toIsoString=w("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ld),jg.lang=Jf, +// Side effect imports +// FORMATTING +T("X",0,0,"unix"),T("x",0,0,"valueOf"), +// PARSING +Y("x",Oe),Y("X",Re),aa("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),aa("x",function(a,b,c){c._d=new Date(t(a))}),a.version="2.14.1",b(rb),a.fn=Qf,a.min=tb,a.max=ub,a.now=Df,a.utc=j,a.unix=Jc,a.months=Pc,a.isDate=f,a.locale=Za,a.invalid=n,a.duration=Mb,a.isMoment=r,a.weekdays=Rc,a.parseZone=Kc,a.localeData=ab,a.isDuration=wb,a.monthsShort=Qc,a.weekdaysMin=Tc,a.defineLocale=$a,a.updateLocale=_a,a.locales=bb,a.weekdaysShort=Sc,a.normalizeUnits=J,a.relativeTimeRounding=id,a.relativeTimeThreshold=jd,a.calendarFormat=Tb,a.prototype=Qf;var kg=a,lg=(kg.defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(a){return/^nm$/i.test(a)},meridiem:function(a,b,c){return 12>a?c?"vm":"VM":c?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[Môre om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,// Maandag is die eerste dag van die week. +doy:4}}),kg.defineLocale("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,// Saturday is the first day of the week. +doy:12}}),{1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"}),mg={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},ng=(kg.defineLocale("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(a){return"م"===a},meridiem:function(a,b,c){return 12>a?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(a){return a.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(a){return mg[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return lg[a]}).replace(/,/g,"،")},week:{dow:6,// Saturday is the first day of the week. +doy:12}}),kg.defineLocale("ar-tn",{months:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,// Monday is the first day of the week. +doy:4}}),{1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"}),og={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},pg=function(a){return 0===a?0:1===a?1:2===a?2:a%100>=3&&10>=a%100?3:a%100>=11?4:5},qg={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},rg=function(a){return function(b,c,d,e){var f=pg(b),g=qg[a][pg(b)];return 2===f&&(g=g[c?0:1]),g.replace(/%d/i,b)}},sg=["كانون الثاني يناير","شباط فبراير","آذار مارس","نيسان أبريل","أيار مايو","حزيران يونيو","تموز يوليو","آب أغسطس","أيلول سبتمبر","تشرين الأول أكتوبر","تشرين الثاني نوفمبر","كانون الأول ديسمبر"],tg=(kg.defineLocale("ar",{months:sg,monthsShort:sg,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(a){return"م"===a},meridiem:function(a,b,c){return 12>a?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:rg("s"),m:rg("m"),mm:rg("m"),h:rg("h"),hh:rg("h"),d:rg("d"),dd:rg("d"),M:rg("M"),MM:rg("M"),y:rg("y"),yy:rg("y")},preparse:function(a){return a.replace(/\u200f/g,"").replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(a){return og[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return ng[a]}).replace(/,/g,"،")},week:{dow:6,// Saturday is the first day of the week. +doy:12}}),{1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-üncü",4:"-üncü",100:"-üncü",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"}),ug=(kg.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"),weekdaysMin:"Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dünən] LT",lastWeek:"[keçən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"birneçə saniyyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gündüz|axşam/,isPM:function(a){return/^(gündüz|axşam)$/.test(a)},meridiem:function(a,b,c){return 4>a?"gecə":12>a?"səhər":17>a?"gündüz":"axşam"},ordinalParse:/\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,ordinal:function(a){if(0===a)// special case for zero +return a+"-ıncı";var b=a%10,c=a%100-b,d=a>=100?100:null;return a+(tg[b]||tg[c]||tg[d])},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("be",{months:{format:"студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня".split("_"),standalone:"студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань".split("_")},monthsShort:"студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж".split("_"),weekdays:{format:"нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу".split("_"),standalone:"нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота".split("_"),isFormat:/\[ ?[Вв] ?(?:мінулую|наступную)? ?\] ?dddd/},weekdaysShort:"нд_пн_ат_ср_чц_пт_сб".split("_"),weekdaysMin:"нд_пн_ат_ср_чц_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., HH:mm",LLLL:"dddd, D MMMM YYYY г., HH:mm"},calendar:{sameDay:"[Сёння ў] LT",nextDay:"[Заўтра ў] LT",lastDay:"[Учора ў] LT",nextWeek:function(){return"[У] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[У мінулую] dddd [ў] LT";case 1:case 2:case 4:return"[У мінулы] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"праз %s",past:"%s таму",s:"некалькі секунд",m:nd,mm:nd,h:nd,hh:nd,d:"дзень",dd:nd,M:"месяц",MM:nd,y:"год",yy:nd},meridiemParse:/ночы|раніцы|дня|вечара/,isPM:function(a){return/^(дня|вечара)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночы":12>a?"раніцы":17>a?"дня":"вечара"},ordinalParse:/\d{1,2}-(і|ы|га)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":case"w":case"W":return a%10!==2&&a%10!==3||a%100===12||a%100===13?a+"-ы":a+"-і";case"D":return a+"-га";default:return a}},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[В изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[В изминалия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дни",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(a){var b=a%10,c=a%100;return 0===a?a+"-ев":0===c?a+"-ен":c>10&&20>c?a+"-ти":1===b?a+"-ви":2===b?a+"-ри":7===b||8===b?a+"-ми":a+"-ти"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),{1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"}),vg={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"},wg=(kg.defineLocale("bn",{months:"জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্র_শনি".split("_"),weekdaysMin:"রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(a){return a.replace(/[১২৩৪৫৬৭৮৯০]/g,function(a){return vg[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return ug[a]})},meridiemParse:/রাত|সকাল|দুপুর|বিকাল|রাত/,meridiemHour:function(a,b){return 12===a&&(a=0),"রাত"===b&&a>=4||"দুপুর"===b&&5>a||"বিকাল"===b?a+12:a},meridiem:function(a,b,c){return 4>a?"রাত":10>a?"সকাল":17>a?"দুপুর":20>a?"বিকাল":"রাত"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),{1:"༡",2:"༢",3:"༣",4:"༤",5:"༥",6:"༦",7:"༧",8:"༨",9:"༩",0:"༠"}),xg={"༡":"1","༢":"2","༣":"3","༤":"4","༥":"5","༦":"6","༧":"7","༨":"8","༩":"9","༠":"0"},yg=(kg.defineLocale("bo",{months:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),monthsShort:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),weekdays:"གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་".split("_"),weekdaysShort:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),weekdaysMin:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[དི་རིང] LT",nextDay:"[སང་ཉིན] LT",nextWeek:"[བདུན་ཕྲག་རྗེས་མ], LT",lastDay:"[ཁ་སང] LT",lastWeek:"[བདུན་ཕྲག་མཐའ་མ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ལ་",past:"%s སྔན་ལ",s:"ལམ་སང",m:"སྐར་མ་གཅིག",mm:"%d སྐར་མ",h:"ཆུ་ཚོད་གཅིག",hh:"%d ཆུ་ཚོད",d:"ཉིན་གཅིག",dd:"%d ཉིན་",M:"ཟླ་བ་གཅིག",MM:"%d ཟླ་བ",y:"ལོ་གཅིག",yy:"%d ལོ"},preparse:function(a){return a.replace(/[༡༢༣༤༥༦༧༨༩༠]/g,function(a){return xg[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return wg[a]})},meridiemParse:/མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,meridiemHour:function(a,b){return 12===a&&(a=0),"མཚན་མོ"===b&&a>=4||"ཉིན་གུང"===b&&5>a||"དགོང་དག"===b?a+12:a},meridiem:function(a,b,c){return 4>a?"མཚན་མོ":10>a?"ཞོགས་ཀས":17>a?"ཉིན་གུང":20>a?"དགོང་དག":"མཚན་མོ"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),kg.defineLocale("br",{months:"Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h[e]mm A",LTS:"h[e]mm:ss A",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY h[e]mm A",LLLL:"dddd, D [a viz] MMMM YYYY h[e]mm A"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warc'hoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Dec'h da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s 'zo",s:"un nebeud segondennoù",m:"ur vunutenn",mm:od,h:"un eur",hh:"%d eur",d:"un devezh",dd:od,M:"ur miz",MM:od,y:"ur bloaz",yy:pd},ordinalParse:/\d{1,2}(añ|vet)/,ordinal:function(a){var b=1===a?"añ":"vet";return a+b},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:td,mm:td,h:td,hh:td,d:"dan",dd:td,M:"mjesec",MM:td,y:"godinu",yy:td},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:!0,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd D MMMM YYYY H:mm"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(a,b){var c=1===a?"r":2===a?"n":3===a?"r":4===a?"t":"è";return"w"!==b&&"W"!==b||(c="a"),a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),"leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_")),zg="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_"),Ag=(kg.defineLocale("cs",{months:yg,monthsShort:zg,monthsParse:function(a,b){var c,d=[];for(c=0;12>c;c++) +// use custom parser to solve problem with July (červenec) +d[c]=new RegExp("^"+a[c]+"$|^"+b[c]+"$","i");return d}(yg,zg),shortMonthsParse:function(a){var b,c=[];for(b=0;12>b;b++)c[b]=new RegExp("^"+a[b]+"$","i");return c}(zg),longMonthsParse:function(a){var b,c=[];for(b=0;12>b;b++)c[b]=new RegExp("^"+a[b]+"$","i");return c}(yg),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:vd,m:vd,mm:vd,h:vd,hh:vd,d:vd,dd:vd,M:vd,MM:vd,y:vd,yy:vd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("cv",{months:"кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав".split("_"),monthsShort:"кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кӗҫ_эрн_шӑм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кҫ_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]",LLL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm",LLLL:"dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ӗнер] LT [сехетре]",nextWeek:"[Ҫитес] dddd LT [сехетре]",lastWeek:"[Иртнӗ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(a){var b=/сехет$/i.exec(a)?"рен":/ҫул$/i.exec(a)?"тан":"ран";return a+b},past:"%s каялла",s:"пӗр-ик ҫеккунт",m:"пӗр минут",mm:"%d минут",h:"пӗр сехет",hh:"%d сехет",d:"пӗр кун",dd:"%d кун",M:"пӗр уйӑх",MM:"%d уйӑх",y:"пӗр ҫул",yy:"%d ҫул"},ordinalParse:/\d{1,2}-мӗш/,ordinal:"%d-мӗш",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:!0, +// time formats are the same as en-gb +longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn ôl",s:"ychydig eiliadau",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},ordinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/, +// traditional ordinal numbers above 31 are not commonly used in colloquial Welsh +ordinal:function(a){var b=a,c="",d=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed",// 1af to 10fed +"eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];return b>20?c=40===b||50===b||60===b||80===b||100===b?"fed":"ain":b>0&&(c=d[b]),a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY HH:mm"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:wd,mm:"%d Minuten",h:wd,hh:"%d Stunden",d:wd,dd:wd,M:wd,MM:wd,y:wd,yy:wd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:xd,mm:"%d Minuten",h:xd,hh:"%d Stunden",d:xd,dd:xd,M:xd,MM:xd,y:xd,yy:xd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),["ޖެނުއަރީ","ފެބްރުއަރީ","މާރިޗު","އޭޕްރީލު","މޭ","ޖޫން","ޖުލައި","އޯގަސްޓު","ސެޕްޓެމްބަރު","އޮކްޓޯބަރު","ނޮވެމްބަރު","ޑިސެމްބަރު"]),Bg=["އާދިއްތަ","ހޯމަ","އަންގާރަ","ބުދަ","ބުރާސްފަތި","ހުކުރު","ހޮނިހިރު"],Cg=(kg.defineLocale("dv",{months:Ag,monthsShort:Ag,weekdays:Bg,weekdaysShort:Bg,weekdaysMin:"އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/މކ|މފ/,isPM:function(a){return"މފ"===a},meridiem:function(a,b,c){return 12>a?"މކ":"މފ"},calendar:{sameDay:"[މިއަދު] LT",nextDay:"[މާދަމާ] LT",nextWeek:"dddd LT",lastDay:"[އިއްޔެ] LT",lastWeek:"[ފާއިތުވި] dddd LT",sameElse:"L"},relativeTime:{future:"ތެރޭގައި %s",past:"ކުރިން %s",s:"ސިކުންތުކޮޅެއް",m:"މިނިޓެއް",mm:"މިނިޓު %d",h:"ގަޑިއިރެއް",hh:"ގަޑިއިރު %d",d:"ދުވަހެއް",dd:"ދުވަސް %d",M:"މަހެއް",MM:"މަސް %d",y:"އަހަރެއް",yy:"އަހަރު %d"},preparse:function(a){return a.replace(/،/g,",")},postformat:function(a){return a.replace(/,/g,"،")},week:{dow:7,// Sunday is the first day of the week. +doy:12}}),kg.defineLocale("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(a,b){return/D/.test(b.substring(0,b.indexOf("MMMM")))?this._monthsGenitiveEl[a.month()]:this._monthsNominativeEl[a.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(a,b,c){return a>11?c?"μμ":"ΜΜ":c?"πμ":"ΠΜ"},isPM:function(a){return"μ"===(a+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(a,b){var c=this._calendarEl[a],d=b&&b.hours();return y(c)&&(c=c.apply(b)),c.replace("{}",d%12===1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),kg.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec".split("_"),weekdays:"Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato".split("_"),weekdaysShort:"Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Ĵa_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D[-an de] MMMM, YYYY",LLL:"D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd, [la] D[-an de] MMMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(a){return"p"===a.charAt(0).toLowerCase()},meridiem:function(a,b,c){return a>11?c?"p.t.m.":"P.T.M.":c?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd [je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasinta] dddd [je] LT",sameElse:"L"},relativeTime:{future:"je %s",past:"antaŭ %s",s:"sekundoj",m:"minuto",mm:"%d minutoj",h:"horo",hh:"%d horoj",d:"tago",//ne 'diurno', ĉar estas uzita por proksimumo +dd:"%d tagoj",M:"monato",MM:"%d monatoj",y:"jaro",yy:"%d jaroj"},ordinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,// Monday is the first day of the week. +doy:7}}),"ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_")),Dg="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),Eg=(kg.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Dg[a.month()]:Cg[a.month()]},monthsParseExact:!0,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,// Monday is the first day of the week. +doy:4}}),"ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_")),Fg="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),Gg=(kg.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Fg[a.month()]:Eg[a.month()]},monthsParseExact:!0,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("et",{months:"jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[Täna,] LT",nextDay:"[Homme,] LT",nextWeek:"[Järgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pärast",past:"%s tagasi",s:yd,m:yd,mm:yd,h:yd,hh:yd,d:yd,dd:"%d päeva",M:yd,MM:yd,y:yd,yy:yd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:!0,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),{1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"}),Hg={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"},Ig=(kg.defineLocale("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(a){return/بعد از ظهر/.test(a)},meridiem:function(a,b,c){return 12>a?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(a){return a.replace(/[۰-۹]/g,function(a){return Hg[a]}).replace(/،/g,",")},postformat:function(a){return a.replace(/\d/g,function(a){return Gg[a]}).replace(/,/g,"،")},ordinalParse:/\d{1,2}م/,ordinal:"%dم",week:{dow:6,// Saturday is the first day of the week. +doy:12}}),"nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" ")),Jg=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",Ig[7],Ig[8],Ig[9]],Kg=(kg.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:zd,m:zd,mm:zd,h:zd,hh:zd,d:zd,dd:zd,M:zd,MM:zd,y:zd,yy:zd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("fo",{months:"januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mán_týs_mik_hós_frí_ley".split("_"),weekdaysMin:"su_má_tý_mi_hó_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[Í dag kl.] LT",nextDay:"[Í morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Í gjár kl.] LT",lastWeek:"[síðstu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s síðani",s:"fá sekund",m:"ein minutt",mm:"%d minuttir",h:"ein tími",hh:"%d tímar",d:"ein dagur",dd:"%d dagar",M:"ein mánaði",MM:"%d mánaðir",y:"eitt ár",yy:"%d ár"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|e)/,ordinal:function(a){return a+(1===a?"er":"e")}}),kg.defineLocale("fr-ch",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|e)/,ordinal:function(a){return a+(1===a?"er":"e")},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(a){return a+(1===a?"er":"")},week:{dow:1,// Monday is the first day of the week. +doy:4}}),"jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_")),Lg="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),Mg=(kg.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?Lg[a.month()]:Kg[a.month()]},monthsParseExact:!0,weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[ôfrûne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",m:"ien minút",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,// Monday is the first day of the week. +doy:4}}),["Am Faoilleach","An Gearran","Am Màrt","An Giblean","An Cèitean","An t-Ògmhios","An t-Iuchar","An Lùnastal","An t-Sultain","An Dàmhair","An t-Samhain","An Dùbhlachd"]),Ng=["Faoi","Gear","Màrt","Gibl","Cèit","Ògmh","Iuch","Lùn","Sult","Dàmh","Samh","Dùbh"],Og=["Didòmhnaich","Diluain","Dimàirt","Diciadain","Diardaoin","Dihaoine","Disathairne"],Pg=["Did","Dil","Dim","Dic","Dia","Dih","Dis"],Qg=["Dò","Lu","Mà","Ci","Ar","Ha","Sa"],Rg=(kg.defineLocale("gd",{months:Mg,monthsShort:Ng,monthsParseExact:!0,weekdays:Og,weekdaysShort:Pg,weekdaysMin:Qg,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-màireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-dè aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"mìos",MM:"%d mìosan",y:"bliadhna",yy:"%d bliadhna"},ordinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(a){var b=1===a?"d":a%10===2?"na":"mh";return a+b},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("gl",{months:"Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro".split("_"),monthsShort:"Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.".split("_"),monthsParseExact:!0,weekdays:"Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado".split("_"),weekdaysShort:"Dom._Lun._Mar._Mér._Xov._Ven._Sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mé_Xo_Ve_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd D MMMM YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"ás":"á")+"] LT"},nextDay:function(){return"[mañá "+(1!==this.hours()?"ás":"á")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"ás":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"á":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"ás":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(a){return"uns segundos"===a?"nuns segundos":"en "+a},past:"hai %s",s:"uns segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(a){return 2===a?"שעתיים":a+" שעות"},d:"יום",dd:function(a){return 2===a?"יומיים":a+" ימים"},M:"חודש",MM:function(a){return 2===a?"חודשיים":a+" חודשים"},y:"שנה",yy:function(a){return 2===a?"שנתיים":a%10===0&&10!==a?a+" שנה":a+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,isPM:function(a){return/^(אחה"צ|אחרי הצהריים|בערב)$/.test(a)},meridiem:function(a,b,c){return 5>a?"לפנות בוקר":10>a?"בבוקר":12>a?c?'לפנה"צ':"לפני הצהריים":18>a?c?'אחה"צ':"אחרי הצהריים":"בערב"}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),Sg={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},Tg=(kg.defineLocale("hi",{months:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),monthsParseExact:!0,weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm बजे",LLLL:"dddd, D MMMM YYYY, A h:mm बजे"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return Sg[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Rg[a]})}, +// Hindi notation for meridiems are quite fuzzy in practice. While there exists +// a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi. +meridiemParse:/रात|सुबह|दोपहर|शाम/,meridiemHour:function(a,b){return 12===a&&(a=0),"रात"===b?4>a?a:a+12:"सुबह"===b?a:"दोपहर"===b?a>=10?a:a+12:"शाम"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"रात":10>a?"सुबह":17>a?"दोपहर":20>a?"शाम":"रात"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),kg.defineLocale("hr",{months:{format:"siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:Bd,mm:Bd,h:Bd,hh:Bd,d:"dan",dd:Bd,M:"mjesec",MM:Bd,y:"godinu",yy:Bd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),"vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ")),Ug=(kg.defineLocale("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(a){return"u"===a.charAt(1).toLowerCase()},meridiem:function(a,b,c){return 12>a?c===!0?"de":"DE":c===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return Dd.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return Dd.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:Cd,m:Cd,mm:Cd,h:Cd,hh:Cd,d:Cd,dd:Cd,M:Cd,MM:Cd,y:Cd,yy:Cd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("hy-am",{months:{format:"հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի".split("_"),standalone:"հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր".split("_")},monthsShort:"հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ".split("_"),weekdays:"կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ".split("_"),weekdaysShort:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),weekdaysMin:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY թ.",LLL:"D MMMM YYYY թ., HH:mm",LLLL:"dddd, D MMMM YYYY թ., HH:mm"},calendar:{sameDay:"[այսօր] LT",nextDay:"[վաղը] LT",lastDay:"[երեկ] LT",nextWeek:function(){return"dddd [օրը ժամը] LT"},lastWeek:function(){return"[անցած] dddd [օրը ժամը] LT"},sameElse:"L"},relativeTime:{future:"%s հետո",past:"%s առաջ",s:"մի քանի վայրկյան",m:"րոպե",mm:"%d րոպե",h:"ժամ",hh:"%d ժամ",d:"օր",dd:"%d օր",M:"ամիս",MM:"%d ամիս",y:"տարի",yy:"%d տարի"},meridiemParse:/գիշերվա|առավոտվա|ցերեկվա|երեկոյան/,isPM:function(a){return/^(ցերեկվա|երեկոյան)$/.test(a)},meridiem:function(a){return 4>a?"գիշերվա":12>a?"առավոտվա":17>a?"ցերեկվա":"երեկոյան"},ordinalParse:/\d{1,2}|\d{1,2}-(ին|րդ)/,ordinal:function(a,b){switch(b){case"DDD":case"w":case"W":case"DDDo":return 1===a?a+"-ին":a+"-րդ";default:return a}},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"siang"===b?a>=11?a:a+12:"sore"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"siang":19>a?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:Fd,m:Fd,mm:Fd,h:"klukkustund",hh:Fd,d:Fd,dd:Fd,M:Fd,MM:Fd,y:Fd,yy:Fd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"Do_Lu_Ma_Me_Gi_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(a){return(/^[0-9].+$/.test(a)?"tra":"in")+" "+a},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"Ah時m分s秒",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日Ah時m分",LLLL:"YYYY年M月D日Ah時m分 dddd"},meridiemParse:/午前|午後/i,isPM:function(a){return"午後"===a},meridiem:function(a,b,c){return 12>a?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},ordinalParse:/\d{1,2}日/,ordinal:function(a,b){switch(b){case"d":case"D":case"DDD":return a+"日";default:return a}},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),kg.defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(a,b){return 12===a&&(a=0),"enjing"===b?a:"siyang"===b?a>=11?a:a+12:"sonten"===b||"ndalu"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"enjing":15>a?"siyang":19>a?"sonten":"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("ka",{months:{standalone:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),format:"იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს".split("_")},monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:{standalone:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),format:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_"),isFormat:/(წინა|შემდეგ)/},weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(a){return/(წამი|წუთი|საათი|წელი)/.test(a)?a.replace(/ი$/,"ში"):a+"ში"},past:function(a){return/(წამი|წუთი|საათი|დღე|თვე)/.test(a)?a.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(a)?a.replace(/წელი$/,"წლის წინ"):void 0},s:"რამდენიმე წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},ordinalParse:/0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,ordinal:function(a){return 0===a?a:1===a?a+"-ლი":20>a||100>=a&&a%20===0||a%100===0?"მე-"+a:a+"-ე"},week:{dow:1,doy:7}}),{0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"}),Vg=(kg.defineLocale("kk",{months:"қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан".split("_"),monthsShort:"қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел".split("_"),weekdays:"жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі".split("_"),weekdaysShort:"жек_дүй_сей_сәр_бей_жұм_сен".split("_"),weekdaysMin:"жк_дй_сй_ср_бй_жм_сн".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгін сағат] LT",nextDay:"[Ертең сағат] LT",nextWeek:"dddd [сағат] LT",lastDay:"[Кеше сағат] LT",lastWeek:"[Өткен аптаның] dddd [сағат] LT",sameElse:"L"},relativeTime:{future:"%s ішінде",past:"%s бұрын",s:"бірнеше секунд",m:"бір минут",mm:"%d минут",h:"бір сағат",hh:"%d сағат",d:"бір күн",dd:"%d күн",M:"бір ай",MM:"%d ай",y:"бір жыл",yy:"%d жыл"},ordinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(a){var b=a%10,c=a>=100?100:null;return a+(Ug[a]||Ug[b]||Ug[c])},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("km",{months:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),monthsShort:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),weekdays:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysShort:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysMin:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[ថ្ងៃនេះ ម៉ោង] LT",nextDay:"[ស្អែក ម៉ោង] LT",nextWeek:"dddd [ម៉ោង] LT",lastDay:"[ម្សិលមិញ ម៉ោង] LT",lastWeek:"dddd [សប្តាហ៍មុន] [ម៉ោង] LT",sameElse:"L"},relativeTime:{future:"%sទៀត",past:"%sមុន",s:"ប៉ុន្មានវិនាទី",m:"មួយនាទី",mm:"%d នាទី",h:"មួយម៉ោង",hh:"%d ម៉ោង",d:"មួយថ្ងៃ",dd:"%d ថ្ងៃ",M:"មួយខែ",MM:"%d ខែ",y:"មួយឆ្នាំ",yy:"%d ឆ្នាំ"},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 m분",LTS:"A h시 m분 s초",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 A h시 m분",LLLL:"YYYY년 MMMM D일 dddd A h시 m분"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇 초",ss:"%d초",m:"일분",mm:"%d분",h:"한 시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한 달",MM:"%d달",y:"일 년",yy:"%d년"},ordinalParse:/\d{1,2}일/,ordinal:"%d일",meridiemParse:/오전|오후/,isPM:function(a){return"오후"===a},meridiem:function(a,b,c){return 12>a?"오전":"오후"}}),{0:"-чү",1:"-чи",2:"-чи",3:"-чү",4:"-чү",5:"-чи",6:"-чы",7:"-чи",8:"-чи",9:"-чу",10:"-чу",20:"-чы",30:"-чу",40:"-чы",50:"-чү",60:"-чы",70:"-чи",80:"-чи",90:"-чу",100:"-чү"}),Wg=(kg.defineLocale("ky",{months:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),monthsShort:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),weekdays:"Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби".split("_"),weekdaysShort:"Жек_Дүй_Шей_Шар_Бей_Жум_Ише".split("_"),weekdaysMin:"Жк_Дй_Шй_Шр_Бй_Жм_Иш".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгүн саат] LT",nextDay:"[Эртең саат] LT",nextWeek:"dddd [саат] LT",lastDay:"[Кече саат] LT",lastWeek:"[Өткен аптанын] dddd [күнү] [саат] LT",sameElse:"L"},relativeTime:{future:"%s ичинде",past:"%s мурун",s:"бирнече секунд",m:"бир мүнөт",mm:"%d мүнөт",h:"бир саат",hh:"%d саат",d:"бир күн",dd:"%d күн",M:"бир ай",MM:"%d ай",y:"бир жыл",yy:"%d жыл"},ordinalParse:/\d{1,2}-(чи|чы|чү|чу)/,ordinal:function(a){var b=a%10,c=a>=100?100:null;return a+(Vg[a]||Vg[b]||Vg[c])},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("lb",{months:"Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mé_Dë_Më_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gëschter um] LT",lastWeek:function(){ +// Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule +switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:Hd,past:Id,s:"e puer Sekonnen",m:Gd,mm:"%d Minutten",h:Gd,hh:"%d Stonnen",d:Gd,dd:"%d Deeg",M:Gd,MM:"%d Méint",y:Gd,yy:"%d Joer"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("lo",{months:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),monthsShort:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),weekdays:"ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysShort:"ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysMin:"ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ວັນdddd D MMMM YYYY HH:mm"},meridiemParse:/ຕອນເຊົ້າ|ຕອນແລງ/,isPM:function(a){return"ຕອນແລງ"===a},meridiem:function(a,b,c){return 12>a?"ຕອນເຊົ້າ":"ຕອນແລງ"},calendar:{sameDay:"[ມື້ນີ້ເວລາ] LT",nextDay:"[ມື້ອື່ນເວລາ] LT",nextWeek:"[ວັນ]dddd[ໜ້າເວລາ] LT",lastDay:"[ມື້ວານນີ້ເວລາ] LT",lastWeek:"[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT",sameElse:"L"},relativeTime:{future:"ອີກ %s",past:"%sຜ່ານມາ",s:"ບໍ່ເທົ່າໃດວິນາທີ",m:"1 ນາທີ",mm:"%d ນາທີ",h:"1 ຊົ່ວໂມງ",hh:"%d ຊົ່ວໂມງ",d:"1 ມື້",dd:"%d ມື້",M:"1 ເດືອນ",MM:"%d ເດືອນ",y:"1 ປີ",yy:"%d ປີ"},ordinalParse:/(ທີ່)\d{1,2}/,ordinal:function(a){return"ທີ່"+a}}),{m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"}),Xg=(kg.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?|MMMM?(\[[^\[\]]*\]|\s+)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:Kd,m:Ld,mm:Od,h:Ld,hh:Od,d:Ld,dd:Od,M:Ld,MM:Od,y:Ld,yy:Od},ordinalParse:/\d{1,2}-oji/,ordinal:function(a){return a+"-oji"},week:{dow:1,// Monday is the first day of the week. +doy:4}}),{m:"minūtes_minūtēm_minūte_minūtes".split("_"),mm:"minūtes_minūtēm_minūte_minūtes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),MM:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")}),Yg=(kg.defineLocale("lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:Sd,m:Rd,mm:Qd,h:Rd,hh:Qd,d:Rd,dd:Qd,M:Rd,MM:Qd,y:Rd,yy:Qd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),{words:{//Different grammatical cases +m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=Yg.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+Yg.correctGrammaticalCase(a,d)}}),Zg=(kg.defineLocale("me",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedjelje] [u] LT","[prošlog] [ponedjeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srijede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",m:Yg.translate,mm:Yg.translate,h:Yg.translate,hh:Yg.translate,d:"dan",dd:Yg.translate,M:"mjesec",MM:Yg.translate,y:"godinu",yy:Yg.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"[Во] dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"после %s",past:"пред %s",s:"неколку секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дена",M:"месец",MM:"%d месеци",y:"година",yy:"%d години"},ordinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(a){var b=a%10,c=a%100;return 0===a?a+"-ев":0===c?a+"-ен":c>10&&20>c?a+"-ти":1===b?a+"-ви":2===b?a+"-ри":7===b||8===b?a+"-ми":a+"-ти"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),monthsParseExact:!0,weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",LTS:"A h:mm:ss -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -നു",LLLL:"dddd, D MMMM YYYY, A h:mm -നു"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiemParse:/രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,meridiemHour:function(a,b){return 12===a&&(a=0),"രാത്രി"===b&&a>=4||"ഉച്ച കഴിഞ്ഞ്"===b||"വൈകുന്നേരം"===b?a+12:a},meridiem:function(a,b,c){return 4>a?"രാത്രി":12>a?"രാവിലെ":17>a?"ഉച്ച കഴിഞ്ഞ്":20>a?"വൈകുന്നേരം":"രാത്രി"}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),$g={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},_g=(kg.defineLocale("mr",{months:"जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),monthsShort:"जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm वाजता",LTS:"A h:mm:ss वाजता",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm वाजता",LLLL:"dddd, D MMMM YYYY, A h:mm वाजता"},calendar:{sameDay:"[आज] LT",nextDay:"[उद्या] LT",nextWeek:"dddd, LT",lastDay:"[काल] LT",lastWeek:"[मागील] dddd, LT",sameElse:"L"},relativeTime:{future:"%sमध्ये",past:"%sपूर्वी",s:Td,m:Td,mm:Td,h:Td,hh:Td,d:Td,dd:Td,M:Td,MM:Td,y:Td,yy:Td},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return $g[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return Zg[a]})},meridiemParse:/रात्री|सकाळी|दुपारी|सायंकाळी/,meridiemHour:function(a,b){return 12===a&&(a=0),"रात्री"===b?4>a?a:a+12:"सकाळी"===b?a:"दुपारी"===b?a>=10?a:a+12:"सायंकाळी"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"रात्री":10>a?"सकाळी":17>a?"दुपारी":20>a?"सायंकाळी":"रात्री"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),kg.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"tengahari"===b?a>=11?a:a+12:"petang"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"tengahari":19>a?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(a,b){return 12===a&&(a=0),"pagi"===b?a:"tengahari"===b?a>=11?a:a+12:"petang"===b||"malam"===b?a+12:void 0},meridiem:function(a,b,c){return 11>a?"pagi":15>a?"tengahari":19>a?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),{1:"၁",2:"၂",3:"၃",4:"၄",5:"၅",6:"၆",7:"၇",8:"၈",9:"၉",0:"၀"}),ah={"၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","၀":"0"},bh=(kg.defineLocale("my",{months:"ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ".split("_"),monthsShort:"ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ".split("_"),weekdays:"တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ".split("_"),weekdaysShort:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),weekdaysMin:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ယနေ.] LT [မှာ]",nextDay:"[မနက်ဖြန်] LT [မှာ]",nextWeek:"dddd LT [မှာ]",lastDay:"[မနေ.က] LT [မှာ]",lastWeek:"[ပြီးခဲ့သော] dddd LT [မှာ]",sameElse:"L"},relativeTime:{future:"လာမည့် %s မှာ",past:"လွန်ခဲ့သော %s က",s:"စက္ကန်.အနည်းငယ်",m:"တစ်မိနစ်",mm:"%d မိနစ်",h:"တစ်နာရီ",hh:"%d နာရီ",d:"တစ်ရက်",dd:"%d ရက်",M:"တစ်လ",MM:"%d လ",y:"တစ်နှစ်",yy:"%d နှစ်"},preparse:function(a){return a.replace(/[၁၂၃၄၅၆၇၈၉၀]/g,function(a){return ah[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return _g[a]})},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"sø._ma._ti._on._to._fr._lø.".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en måned",MM:"%d måneder",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),{1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"}),ch={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},dh=(kg.defineLocale("ne",{months:"जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर".split("_"),monthsShort:"जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.".split("_"),monthsParseExact:!0,weekdays:"आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार".split("_"),weekdaysShort:"आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.".split("_"),weekdaysMin:"आ._सो._मं._बु._बि._शु._श.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"Aको h:mm बजे",LTS:"Aको h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, Aको h:mm बजे",LLLL:"dddd, D MMMM YYYY, Aको h:mm बजे"},preparse:function(a){return a.replace(/[१२३४५६७८९०]/g,function(a){return ch[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return bh[a]})},meridiemParse:/राति|बिहान|दिउँसो|साँझ/,meridiemHour:function(a,b){return 12===a&&(a=0),"राति"===b?4>a?a:a+12:"बिहान"===b?a:"दिउँसो"===b?a>=10?a:a+12:"साँझ"===b?a+12:void 0},meridiem:function(a,b,c){return 3>a?"राति":12>a?"बिहान":16>a?"दिउँसो":20>a?"साँझ":"राति"},calendar:{sameDay:"[आज] LT",nextDay:"[भोलि] LT",nextWeek:"[आउँदो] dddd[,] LT",lastDay:"[हिजो] LT",lastWeek:"[गएको] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%sमा",past:"%s अगाडि",s:"केही क्षण",m:"एक मिनेट",mm:"%d मिनेट",h:"एक घण्टा",hh:"%d घण्टा",d:"एक दिन",dd:"%d दिन",M:"एक महिना",MM:"%d महिना",y:"एक बर्ष",yy:"%d बर्ष"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),"jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_")),eh="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),fh=(kg.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(a,b){return/-MMM-/.test(b)?eh[a.month()]:dh[a.month()]},monthsParseExact:!0,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(a){return a+(1===a||8===a||a>=20?"ste":"de")},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"sun_mån_tys_ons_tor_fre_lau".split("_"),weekdaysMin:"su_må_ty_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I går klokka] LT",lastWeek:"[Føregåande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",M:"ein månad",MM:"%d månader",y:"eit år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),{1:"੧",2:"੨",3:"੩",4:"੪",5:"੫",6:"੬",7:"੭",8:"੮",9:"੯",0:"੦"}),gh={"੧":"1","੨":"2","੩":"3","੪":"4","੫":"5","੬":"6","੭":"7","੮":"8","੯":"9","੦":"0"},hh=(kg.defineLocale("pa-in",{ +// There are months name as per Nanakshahi Calender but they are not used as rigidly in modern Punjabi. +months:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),monthsShort:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),weekdays:"ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ".split("_"),weekdaysShort:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),weekdaysMin:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),longDateFormat:{LT:"A h:mm ਵਜੇ",LTS:"A h:mm:ss ਵਜੇ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ਵਜੇ",LLLL:"dddd, D MMMM YYYY, A h:mm ਵਜੇ"},calendar:{sameDay:"[ਅਜ] LT",nextDay:"[ਕਲ] LT",nextWeek:"dddd, LT",lastDay:"[ਕਲ] LT",lastWeek:"[ਪਿਛਲੇ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ਵਿੱਚ",past:"%s ਪਿਛਲੇ",s:"ਕੁਝ ਸਕਿੰਟ",m:"ਇਕ ਮਿੰਟ",mm:"%d ਮਿੰਟ",h:"ਇੱਕ ਘੰਟਾ",hh:"%d ਘੰਟੇ",d:"ਇੱਕ ਦਿਨ",dd:"%d ਦਿਨ",M:"ਇੱਕ ਮਹੀਨਾ",MM:"%d ਮਹੀਨੇ",y:"ਇੱਕ ਸਾਲ",yy:"%d ਸਾਲ"},preparse:function(a){return a.replace(/[੧੨੩੪੫੬੭੮੯੦]/g,function(a){return gh[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return fh[a]})}, +// Punjabi notation for meridiems are quite fuzzy in practice. While there exists +// a rigid notion of a 'Pahar' it is not used as rigidly in modern Punjabi. +meridiemParse:/ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/,meridiemHour:function(a,b){return 12===a&&(a=0),"ਰਾਤ"===b?4>a?a:a+12:"ਸਵੇਰ"===b?a:"ਦੁਪਹਿਰ"===b?a>=10?a:a+12:"ਸ਼ਾਮ"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"ਰਾਤ":10>a?"ਸਵੇਰ":17>a?"ਦੁਪਹਿਰ":20>a?"ਸ਼ਾਮ":"ਰਾਤ"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),"styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_")),ih="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),jh=(kg.defineLocale("pl",{months:function(a,b){return""===b?"("+ih[a.month()]+"|"+hh[a.month()]+")":/D MMMM/.test(b)?ih[a.month()]:hh[a.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:Vd,mm:Vd,h:Vd,hh:Vd,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:Vd,y:"rok",yy:Vd},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("pt-br",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [às] HH:mm"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){// Saturday + Sunday +return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"poucos segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"}),kg.defineLocale("pt",{months:"Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_Sáb".split("_"),weekdaysMin:"Dom_2ª_3ª_4ª_5ª_6ª_Sáb".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){// Saturday + Sunday +return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:Wd,h:"o oră",hh:Wd,d:"o zi",dd:Wd,M:"o lună",MM:Wd,y:"un an",yy:Wd},week:{dow:1,// Monday is the first day of the week. +doy:7}}),[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i]),kh=(kg.defineLocale("ru",{months:{format:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_"),standalone:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_")},monthsShort:{ +// по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ? +format:"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.".split("_"),standalone:"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.".split("_")},weekdays:{standalone:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),format:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/},weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:jh,longMonthsParse:jh,shortMonthsParse:jh, +// полные названия с падежами, по три буквы, для некоторых, по 4 буквы, сокращения с точкой и без точки +monthsRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, +// копия предыдущего +monthsShortRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, +// полные названия с падежами +monthsStrictRegex:/^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i, +// Выражение, которое соотвествует только сокращённым формам +monthsShortStrictRegex:/^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., HH:mm",LLLL:"dddd, D MMMM YYYY г., HH:mm"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(a){if(a.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В следующее] dddd [в] LT";case 1:case 2:case 4:return"[В следующий] dddd [в] LT";case 3:case 5:case 6:return"[В следующую] dddd [в] LT"}},lastWeek:function(a){if(a.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:Yd,mm:Yd,h:"час",hh:Yd,d:"день",dd:Yd,M:"месяц",MM:Yd,y:"год",yy:Yd},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(a){return/^(дня|вечера)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночи":12>a?"утра":17>a?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":return a+"-й";case"D":return a+"-го";case"w":case"W":return a+"-я";default:return a}},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("se",{months:"ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov".split("_"),weekdays:"sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_láv".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mánnu",MM:"%d mánut",y:"okta jahki",yy:"%d jagit"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("si",{months:"ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්".split("_"),monthsShort:"ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ".split("_"),weekdays:"ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා".split("_"),weekdaysShort:"ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන".split("_"),weekdaysMin:"ඉ_ස_අ_බ_බ්‍ර_සි_සෙ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, a h:mm",LLLL:"YYYY MMMM D [වැනි] dddd, a h:mm:ss"},calendar:{sameDay:"[අද] LT[ට]",nextDay:"[හෙට] LT[ට]",nextWeek:"dddd LT[ට]",lastDay:"[ඊයේ] LT[ට]",lastWeek:"[පසුගිය] dddd LT[ට]",sameElse:"L"},relativeTime:{future:"%sකින්",past:"%sකට පෙර",s:"තත්පර කිහිපය",m:"මිනිත්තුව",mm:"මිනිත්තු %d",h:"පැය",hh:"පැය %d",d:"දිනය",dd:"දින %d",M:"මාසය",MM:"මාස %d",y:"වසර",yy:"වසර %d"},ordinalParse:/\d{1,2} වැනි/,ordinal:function(a){return a+" වැනි"},meridiemParse:/පෙර වරු|පස් වරු|පෙ.ව|ප.ව./,isPM:function(a){return"ප.ව."===a||"පස් වරු"===a},meridiem:function(a,b,c){return a>11?c?"ප.ව.":"පස් වරු":c?"පෙ.ව.":"පෙර වරු"}}),"január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_")),lh="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_"),mh=(kg.defineLocale("sk",{months:kh,monthsShort:lh,weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:$d,m:$d,mm:$d,h:$d,hh:$d,d:$d,dd:$d,M:$d,MM:$d,y:$d,yy:$d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejšnjo] [nedeljo] [ob] LT";case 3:return"[prejšnjo] [sredo] [ob] LT";case 6:return"[prejšnjo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:_d,m:_d,mm:_d,h:_d,hh:_d,d:_d,dd:_d,M:_d,MM:_d,y:_d,yy:_d},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"),weekdays:"E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"),weekdaysShort:"Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_Më_E_P_Sh".split("_"),weekdaysParseExact:!0,meridiemParse:/PD|MD/,isPM:function(a){return"M"===a.charAt(0)},meridiem:function(a,b,c){return 12>a?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Sot në] LT",nextDay:"[Nesër në] LT",nextWeek:"dddd [në] LT",lastDay:"[Dje në] LT",lastWeek:"dddd [e kaluar në] LT",sameElse:"L"},relativeTime:{future:"në %s",past:"%s më parë",s:"disa sekonda",m:"një minutë",mm:"%d minuta",h:"një orë",hh:"%d orë",d:"një ditë",dd:"%d ditë",M:"një muaj",MM:"%d muaj",y:"një vit",yy:"%d vite"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),{words:{//Different grammatical cases +m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=mh.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+mh.correctGrammaticalCase(a,d)}}),nh=(kg.defineLocale("sr-cyrl",{months:"јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар".split("_"),monthsShort:"јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.".split("_"),monthsParseExact:!0,weekdays:"недеља_понедељак_уторак_среда_четвртак_петак_субота".split("_"),weekdaysShort:"нед._пон._уто._сре._чет._пет._суб.".split("_"),weekdaysMin:"не_по_ут_ср_че_пе_су".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var a=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",m:mh.translate,mm:mh.translate,h:mh.translate,hh:mh.translate,d:"дан",dd:mh.translate,M:"месец",MM:mh.translate,y:"годину",yy:mh.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),{words:{//Different grammatical cases +m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(a,b){return 1===a?b[0]:a>=2&&4>=a?b[1]:b[2]},translate:function(a,b,c){var d=nh.words[c];return 1===c.length?b?d[0]:d[1]:a+" "+nh.correctGrammaticalCase(a,d)}}),oh=(kg.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var a=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return a[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:nh.translate,mm:nh.translate,h:nh.translate,hh:nh.translate,d:"dan",dd:nh.translate,M:"mesec",MM:nh.translate,y:"godinu",yy:nh.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("ss",{months:"Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split("_"),monthsShort:"Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo".split("_"),weekdays:"Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo".split("_"),weekdaysShort:"Lis_Umb_Lsb_Les_Lsi_Lsh_Umg".split("_"),weekdaysMin:"Li_Us_Lb_Lt_Ls_Lh_Ug".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Namuhla nga] LT",nextDay:"[Kusasa nga] LT",nextWeek:"dddd [nga] LT",lastDay:"[Itolo nga] LT",lastWeek:"dddd [leliphelile] [nga] LT",sameElse:"L"},relativeTime:{future:"nga %s",past:"wenteka nga %s",s:"emizuzwana lomcane",m:"umzuzu",mm:"%d emizuzu",h:"lihora",hh:"%d emahora",d:"lilanga",dd:"%d emalanga",M:"inyanga",MM:"%d tinyanga",y:"umnyaka",yy:"%d iminyaka"},meridiemParse:/ekuseni|emini|entsambama|ebusuku/,meridiem:function(a,b,c){return 11>a?"ekuseni":15>a?"emini":19>a?"entsambama":"ebusuku"},meridiemHour:function(a,b){return 12===a&&(a=0),"ekuseni"===b?a:"emini"===b?a>=11?a:a+12:"entsambama"===b||"ebusuku"===b?0===a?0:a+12:void 0},ordinalParse:/\d{1,2}/,ordinal:"%d",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"[På] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"e":1===b?"a":2===b?"a":"e";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"masiku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),{1:"௧",2:"௨",3:"௩",4:"௪",5:"௫",6:"௬",7:"௭",8:"௮",9:"௯",0:"௦"}),ph={"௧":"1","௨":"2","௩":"3","௪":"4","௫":"5","௬":"6","௭":"7","௮":"8","௯":"9","௦":"0"},qh=(kg.defineLocale("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},ordinalParse:/\d{1,2}வது/,ordinal:function(a){return a+"வது"},preparse:function(a){return a.replace(/[௧௨௩௪௫௬௭௮௯௦]/g,function(a){return ph[a]})},postformat:function(a){return a.replace(/\d/g,function(a){return oh[a]})}, +// refer http://ta.wikipedia.org/s/1er1 +meridiemParse:/யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,meridiem:function(a,b,c){return 2>a?" யாமம்":6>a?" வைகறை":10>a?" காலை":14>a?" நண்பகல்":18>a?" எற்பாடு":22>a?" மாலை":" யாமம்"},meridiemHour:function(a,b){return 12===a&&(a=0),"யாமம்"===b?2>a?a:a+12:"வைகறை"===b||"காலை"===b?a:"நண்பகல்"===b&&a>=10?a:a+12},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),kg.defineLocale("te",{months:"జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జూలై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్".split("_"),monthsShort:"జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జూలై_ఆగ._సెప్._అక్టో._నవ._డిసె.".split("_"),monthsParseExact:!0,weekdays:"ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం".split("_"),weekdaysShort:"ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని".split("_"),weekdaysMin:"ఆ_సో_మం_బు_గు_శు_శ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[నేడు] LT",nextDay:"[రేపు] LT",nextWeek:"dddd, LT",lastDay:"[నిన్న] LT",lastWeek:"[గత] dddd, LT",sameElse:"L"},relativeTime:{future:"%s లో",past:"%s క్రితం",s:"కొన్ని క్షణాలు",m:"ఒక నిమిషం",mm:"%d నిమిషాలు",h:"ఒక గంట",hh:"%d గంటలు",d:"ఒక రోజు",dd:"%d రోజులు",M:"ఒక నెల",MM:"%d నెలలు",y:"ఒక సంవత్సరం",yy:"%d సంవత్సరాలు"},ordinalParse:/\d{1,2}వ/,ordinal:"%dవ",meridiemParse:/రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/,meridiemHour:function(a,b){return 12===a&&(a=0),"రాత్రి"===b?4>a?a:a+12:"ఉదయం"===b?a:"మధ్యాహ్నం"===b?a>=10?a:a+12:"సాయంత్రం"===b?a+12:void 0},meridiem:function(a,b,c){return 4>a?"రాత్రి":10>a?"ఉదయం":17>a?"మధ్యాహ్నం":20>a?"సాయంత్రం":"రాత్రి"},week:{dow:0,// Sunday is the first day of the week. +doy:6}}),kg.defineLocale("th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.".split("_"),monthsParseExact:!0,weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),// yes, three characters difference +weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H นาฬิกา m นาที",LTS:"H นาฬิกา m นาที s วินาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา H นาฬิกา m นาที",LLLL:"วันddddที่ D MMMM YYYY เวลา H นาฬิกา m นาที"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(a){return"หลังเที่ยง"===a},meridiem:function(a,b,c){return 12>a?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),kg.defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"[Ngayon sa] LT",nextDay:"[Bukas sa] LT",nextWeek:"dddd [sa] LT",lastDay:"[Kahapon sa] LT",lastWeek:"dddd [huling linggo] LT",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},ordinalParse:/\d{1,2}/,ordinal:function(a){return a},week:{dow:1,// Monday is the first day of the week. +doy:4}}),"pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_")),rh=(kg.defineLocale("tlh",{months:"tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’".split("_"),monthsShort:"jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’".split("_"),monthsParseExact:!0,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[wa’leS] LT",nextWeek:"LLL",lastDay:"[wa’Hu’] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:ae,past:be,s:"puS lup",m:"wa’ tup",mm:ce,h:"wa’ rep",hh:ce,d:"wa’ jaj",dd:ce,M:"wa’ jar",MM:ce,y:"wa’ DIS",yy:ce},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),{1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"}),sh=(kg.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinalParse:/\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,ordinal:function(a){if(0===a)// special case for zero +return a+"'ıncı";var b=a%10,c=a%100-b,d=a>=100?100:null;return a+(rh[b]||rh[c]||rh[d])},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("tzl",{months:"Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar".split("_"),monthsShort:"Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec".split("_"),weekdays:"Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi".split("_"),weekdaysShort:"Súl_Lún_Mai_Már_Xhú_Vié_Sát".split("_"),weekdaysMin:"Sú_Lú_Ma_Má_Xh_Vi_Sá".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"D. MMMM [dallas] YYYY",LLL:"D. MMMM [dallas] YYYY HH.mm",LLLL:"dddd, [li] D. MMMM [dallas] YYYY HH.mm"},meridiemParse:/d\'o|d\'a/i,isPM:function(a){return"d'o"===a.toLowerCase()},meridiem:function(a,b,c){return a>11?c?"d'o":"D'O":c?"d'a":"D'A"},calendar:{sameDay:"[oxhi à] LT",nextDay:"[demà à] LT",nextWeek:"dddd [à] LT",lastDay:"[ieiri à] LT",lastWeek:"[sür el] dddd [lasteu à] LT",sameElse:"L"},relativeTime:{future:"osprei %s",past:"ja%s",s:ee,m:ee,mm:ee,h:ee,hh:ee,d:ee,dd:ee,M:ee,MM:ee,y:ee,yy:ee},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("tzm-latn",{months:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",m:"minuḍ",mm:"%d minuḍ",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,// Saturday is the first day of the week. +doy:12}}),kg.defineLocale("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,// Saturday is the first day of the week. +doy:12}}),kg.defineLocale("uk",{months:{format:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_"),standalone:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_")},monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:he,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:ie("[Сьогодні "),nextDay:ie("[Завтра "),lastDay:ie("[Вчора "),nextWeek:ie("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return ie("[Минулої] dddd [").call(this);case 1:case 2:case 4:return ie("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",m:ge,mm:ge,h:"годину",hh:ge,d:"день",dd:ge,M:"місяць",MM:ge,y:"рік",yy:ge}, +// M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason +meridiemParse:/ночі|ранку|дня|вечора/,isPM:function(a){return/^(дня|вечора)$/.test(a)},meridiem:function(a,b,c){return 4>a?"ночі":12>a?"ранку":17>a?"дня":"вечора"},ordinalParse:/\d{1,2}-(й|го)/,ordinal:function(a,b){switch(b){case"M":case"d":case"DDD":case"w":case"W":return a+"-й";case"D":return a+"-го";default:return a}},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("uz",{months:"январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр".split("_"),monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),weekdaysShort:"Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),weekdaysMin:"Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Бугун соат] LT [да]",nextDay:"[Эртага] LT [да]",nextWeek:"dddd [куни соат] LT [да]",lastDay:"[Кеча соат] LT [да]",lastWeek:"[Утган] dddd [куни соат] LT [да]",sameElse:"L"},relativeTime:{future:"Якин %s ичида",past:"Бир неча %s олдин",s:"фурсат",m:"бир дакика",mm:"%d дакика",h:"бир соат",hh:"%d соат",d:"бир кун",dd:"%d кун",M:"бир ой",MM:"%d ой",y:"бир йил",yy:"%d йил"},week:{dow:1,// Monday is the first day of the week. +doy:7}}),kg.defineLocale("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),monthsParseExact:!0,weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:!0,meridiemParse:/sa|ch/i,isPM:function(a){return/^ch$/i.test(a)},meridiem:function(a,b,c){return 12>a?c?"sa":"SA":c?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},ordinalParse:/\d{1,2}/,ordinal:function(a){return a},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("x-pseudo",{months:"J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér".split("_"),monthsShort:"J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc".split("_"),monthsParseExact:!0,weekdays:"S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý".split("_"),weekdaysShort:"S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát".split("_"),weekdaysMin:"S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~ódá~ý át] LT",nextDay:"[T~ómó~rró~w át] LT",nextWeek:"dddd [át] LT",lastDay:"[Ý~ést~érdá~ý át] LT",lastWeek:"[L~ást] dddd [át] LT",sameElse:"L"},relativeTime:{future:"í~ñ %s",past:"%s á~gó",s:"á ~féw ~sécó~ñds",m:"á ~míñ~úté",mm:"%d m~íñú~tés",h:"á~ñ hó~úr",hh:"%d h~óúrs",d:"á ~dáý",dd:"%d d~áýs",M:"á ~móñ~th",MM:"%d m~óñt~hs",y:"á ~ýéár",yy:"%d ý~éárs"},ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c},week:{dow:1,// Monday is the first day of the week. +doy:4}}),kg.defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm分",LTS:"Ah点m分s秒",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日Ah点mm分",LLLL:"YYYY年MMMD日ddddAh点mm分",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日Ah点mm分",llll:"YYYY年MMMD日ddddAh点mm分"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(a,b){return 12===a&&(a=0),"凌晨"===b||"早上"===b||"上午"===b?a:"下午"===b||"晚上"===b?a+12:a>=11?a:a+12},meridiem:function(a,b,c){var d=100*a+b;return 600>d?"凌晨":900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var a,b;return a=kg().startOf("week"),b=this.diff(a,"days")>=7?"[下]":"[本]",0===this.minutes()?b+"dddAh点整":b+"dddAh点mm"},lastWeek:function(){var a,b;return a=kg().startOf("week"),b=this.unix()=11?a:a+12:"下午"===b||"晚上"===b?a+12:void 0},meridiem:function(a,b,c){var d=100*a+b;return 600>d?"凌晨":900>d?"早上":1130>d?"上午":1230>d?"中午":1800>d?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinalParse:/\d{1,2}(日|月|週)/,ordinal:function(a,b){switch(b){case"d":case"D":case"DDD":return a+"日";case"M":return a+"月";case"w":case"W":return a+"週";default:return a}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}}),kg);return sh.locale("en"),sh}); \ No newline at end of file diff --git a/ckan/templates/base.html b/ckan/templates/base.html index 97f9301d71c..2d13f4eb520 100644 --- a/ckan/templates/base.html +++ b/ckan/templates/base.html @@ -26,7 +26,7 @@ #} {%- block meta -%} - {% block meta_generator %}{% endblock %} + {% block meta_generator %}{% endblock %} {% block meta_viewport %}{% endblock %} {%- endblock -%} diff --git a/ckan/templates/organization/bulk_process.html b/ckan/templates/organization/bulk_process.html index 6bdbe039eac..dcb335071eb 100644 --- a/ckan/templates/organization/bulk_process.html +++ b/ckan/templates/organization/bulk_process.html @@ -98,7 +98,7 @@

(_('Name Descending'), 'title_string desc'), (_('Last Modified'), 'data_modified desc') ] %} - {% snippet 'snippets/search_form.html', form_id='organization-datasets-search-form', type='dataset', query=c.q, sorting=sorting, sorting_selected=c.sort_by_selected, no_title=true, search_class=' ' %} + {% snippet 'snippets/search_form.html', form_id='organization-datasets-search-form', type='dataset', query=c.q, count=c.page.item_count, sorting=sorting, sorting_selected=c.sort_by_selected, no_title=true, search_class=' ' %} {% endblock %} {#{% snippet 'snippets/simple_search.html', q=c.q, sort=c.sort_by_selected, placeholder=_('Search datasets...'), extra_sort=[(_('Last Modified'), 'data_modified asc')], input_class='search-normal', form_class='search-aside' %}#} diff --git a/ckan/templates/package/new.html b/ckan/templates/package/new.html index a8eea92ca48..9ef0635c727 100644 --- a/ckan/templates/package/new.html +++ b/ckan/templates/package/new.html @@ -1,3 +1,10 @@ -{% extends "package/base_form_page.html" %} +{% if not h.organizations_available('create_dataset') + and not h.check_config_permission('ckan.auth.create_unowned_dataset') %} -{% block subtitle %}{{ _('Create Dataset') }}{% endblock %} + {% include "package/snippets/cannot_create_package.html" %} + +{% else %} + {% extends "package/base_form_page.html" %} + + {% block subtitle %}{{ _('Create Dataset') }}{% endblock %} +{% endif %} \ No newline at end of file diff --git a/ckan/templates/package/snippets/cannot_create_package.html b/ckan/templates/package/snippets/cannot_create_package.html new file mode 100644 index 00000000000..b3711571bbf --- /dev/null +++ b/ckan/templates/package/snippets/cannot_create_package.html @@ -0,0 +1,27 @@ +{% extends "package/base_form_page.html" %} + +{% block primary_content %} + +{% block page_header %}{% endblock %} +
+
+ {% block primary_content_inner %} + {% if h.check_access('organization_create') %} +
{{ _('Before you can create a dataset you need to create an organization.') }}
+ + + {{ _('Create a new organization') }} + + + {% else %} +
+

{{ _('There are no organizations to which you can assign this dataset.') }}

+

{{ _('Ask a system administrator to create an organization before you can continue.') }}

+
+ + {% endif %} + + {% endblock %} +
+
+{% endblock %} \ No newline at end of file diff --git a/ckan/templates/package/snippets/resource_form.html b/ckan/templates/package/snippets/resource_form.html index 2a737064c02..29108012b68 100644 --- a/ckan/templates/package/snippets/resource_form.html +++ b/ckan/templates/package/snippets/resource_form.html @@ -21,7 +21,7 @@ {% set is_upload = (data.url_type == 'upload') %} {{ form.image_upload(data, errors, field_url='url', field_upload='upload', field_clear='clear_upload', is_upload_enabled=h.uploads_enabled(), is_url=data.url and not is_upload, is_upload=is_upload, - upload_label=_('File'), url_label=_('URL')) }} + upload_label=_('Data'), url_label=_('URL'), placeholder=_('http://example.com/external-data.csv')) }} {% endblock %} {% block basic_fields_name %} diff --git a/ckan/templates/package/snippets/resource_view.html b/ckan/templates/package/snippets/resource_view.html index 77cf21a4e82..93f1373bc9d 100644 --- a/ckan/templates/package/snippets/resource_view.html +++ b/ckan/templates/package/snippets/resource_view.html @@ -10,7 +10,7 @@ {{ _("Embed") }} -

{{ resource_view['description'] }}

+

{{ h.render_markdown(resource_view['description']) }}

{% if not to_preview and h.resource_view_is_filterable(resource_view) %} {% snippet 'package/snippets/resource_view_filters.html', resource=resource %} diff --git a/ckan/tests/config/test_environment.py b/ckan/tests/config/test_environment.py index f9c5533a89e..4212fb5dfe0 100644 --- a/ckan/tests/config/test_environment.py +++ b/ckan/tests/config/test_environment.py @@ -3,7 +3,7 @@ import os from nose import tools as nosetools -from pylons import config +from ckan.common import config import ckan.tests.helpers as h import ckan.plugins as p diff --git a/ckan/tests/config/test_middleware.py b/ckan/tests/config/test_middleware.py index 623341185b7..fad66fa4b6d 100644 --- a/ckan/tests/config/test_middleware.py +++ b/ckan/tests/config/test_middleware.py @@ -8,8 +8,9 @@ import ckan.plugins as p import ckan.tests.helpers as helpers -from ckan.config.middleware import AskAppDispatcherMiddleware, CKANFlask -from ckan.controllers.partyline import PartylineController +from ckan.config.middleware import AskAppDispatcherMiddleware +from ckan.config.middleware.flask_app import CKANFlask +from ckan.config.middleware.pylons_app import CKANPylonsApp class TestPylonsResponseCleanupMiddleware(helpers.FunctionalTestBase): @@ -39,20 +40,6 @@ class TestAppDispatcherPlain(object): the mocks, so they don't extend FunctionalTestBase ''' - def test_invitations_are_sent(self): - - with mock.patch.object(AskAppDispatcherMiddleware, 'send_invitations') as \ - mock_send_invitations: - - # This will create the whole WSGI stack - helpers._get_test_app() - - assert mock_send_invitations.called - eq_(len(mock_send_invitations.call_args[0]), 1) - - eq_(sorted(mock_send_invitations.call_args[0][0].keys()), - ['flask_app', 'pylons_app']) - def test_flask_can_handle_request_is_called_with_environ(self): with mock.patch.object(CKANFlask, 'can_handle_request') as \ @@ -76,7 +63,7 @@ def test_flask_can_handle_request_is_called_with_environ(self): def test_pylons_can_handle_request_is_called_with_environ(self): - with mock.patch.object(PartylineController, 'can_handle_request') as \ + with mock.patch.object(CKANPylonsApp, 'can_handle_request') as \ mock_can_handle_request: # We need set this otherwise the mock object is returned @@ -141,7 +128,7 @@ def test_ask_around_is_called_with_args(self): ckan_app(environ, start_response) assert mock_ask_around.called - mock_ask_around.assert_called_with('can_handle_request', environ) + mock_ask_around.assert_called_with(environ) def test_ask_around_flask_core_route_get(self): @@ -156,15 +143,14 @@ def test_ask_around_flask_core_route_get(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) # Even though this route is defined in Flask, there is catch all route # in Pylons for all requests to point arbitrary urls to templates with # the same name, so we get two positive answers - eq_(len(answers), 2) - eq_([a[0] for a in answers], [True, True]) - eq_(sorted([a[1] for a in answers]), ['flask_app', 'pylons_app']) - # TODO: check origin (core/extension) when that is in place + eq_(answers, [(True, 'flask_app'), (True, 'pylons_app', 'core')]) + # TODO: check Flask origin (core/extension) when that is in place + # (also on the following tests) def test_ask_around_flask_core_route_post(self): @@ -179,15 +165,12 @@ def test_ask_around_flask_core_route_post(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) # Even though this route is defined in Flask, there is catch all route # in Pylons for all requests to point arbitrary urls to templates with # the same name, so we get two positive answers - eq_(len(answers), 2) - eq_([a[0] for a in answers], [True, True]) - eq_(sorted([a[1] for a in answers]), ['flask_app', 'pylons_app']) - # TODO: check origin (core/extension) when that is in place + eq_(answers, [(True, 'flask_app'), (True, 'pylons_app', 'core')]) def test_ask_around_pylons_core_route_get(self): @@ -202,12 +185,9 @@ def test_ask_around_pylons_core_route_get(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'core') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) def test_ask_around_pylons_core_route_post(self): @@ -222,12 +202,9 @@ def test_ask_around_pylons_core_route_post(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'core') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) def test_ask_around_pylons_extension_route_get_before_map(self): @@ -245,12 +222,9 @@ def test_ask_around_pylons_extension_route_get_before_map(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'extension') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) p.unload('test_routing_plugin') @@ -270,12 +244,9 @@ def test_ask_around_pylons_extension_route_post(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'extension') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) p.unload('test_routing_plugin') @@ -295,14 +266,11 @@ def test_ask_around_pylons_extension_route_post_using_get(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) # We are going to get an answer from Pylons, but just because it will # match the catch-all template route, hence the `core` origin. - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'core') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) p.unload('test_routing_plugin') @@ -322,12 +290,9 @@ def test_ask_around_pylons_extension_route_get_after_map(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) - eq_(len(answers), 1) - eq_(answers[0][0], True) - eq_(answers[0][1], 'pylons_app') - eq_(answers[0][2], 'extension') + eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) p.unload('test_routing_plugin') @@ -347,17 +312,10 @@ def test_ask_around_flask_core_and_pylons_extension_route(self): } wsgiref.util.setup_testing_defaults(environ) - answers = app.ask_around('can_handle_request', environ) + answers = app.ask_around(environ) answers = sorted(answers, key=lambda a: a[1]) - eq_(len(answers), 2) - eq_([a[0] for a in answers], [True, True]) - eq_([a[1] for a in answers], ['flask_app', 'pylons_app']) - - # TODO: we still can't distinguish between Flask core and extension - # eq_(answers[0][2], 'extension') - - eq_(answers[1][2], 'extension') + eq_(answers, [(True, 'flask_app'), (True, 'pylons_app', 'extension')]) p.unload('test_routing_plugin') diff --git a/ckan/tests/controllers/test_admin.py b/ckan/tests/controllers/test_admin.py index 3e1d623011f..8915c489d92 100644 --- a/ckan/tests/controllers/test_admin.py +++ b/ckan/tests/controllers/test_admin.py @@ -4,7 +4,7 @@ from bs4 import BeautifulSoup from routes import url_for -from pylons import config +from ckan.common import config import ckan.model as model import ckan.tests.helpers as helpers diff --git a/ckan/tests/controllers/test_api.py b/ckan/tests/controllers/test_api.py index 9cab9d2c1dd..d674bb02f66 100644 --- a/ckan/tests/controllers/test_api.py +++ b/ckan/tests/controllers/test_api.py @@ -5,12 +5,12 @@ controller itself. ''' import json +import re from routes import url_for -from nose.tools import assert_equal +from nose.tools import assert_equal, assert_in, eq_ import ckan.tests.helpers as helpers -from ckan.tests.helpers import assert_in from ckan.tests import factories from ckan import model @@ -203,6 +203,52 @@ def test_config_option_list_access_sysadmin_jsonp(self): status=403, ) + def test_jsonp_works_on_get_requests(self): + + dataset1 = factories.Dataset() + dataset2 = factories.Dataset() + + url = url_for( + controller='api', + action='action', + logic_function='package_list', + ver='/3') + app = self._get_test_app() + res = app.get( + url=url, + params={'callback': 'my_callback'}, + ) + assert re.match('my_callback\(.*\);', res.body), res + # Unwrap JSONP callback (we want to look at the data). + msg = res.body[len('my_callback') + 1:-2] + res_dict = json.loads(msg) + eq_(res_dict['success'], True) + eq_(sorted(res_dict['result']), + sorted([dataset1['name'], dataset2['name']])) + + def test_jsonp_does_not_work_on_post_requests(self): + + dataset1 = factories.Dataset() + dataset2 = factories.Dataset() + + url = url_for( + controller='api', + action='action', + logic_function='package_list', + ver='/3', + callback='my_callback', + ) + app = self._get_test_app() + res = app.post( + url=url, + ) + # The callback param is ignored and the normal response is returned + assert not res.body.startswith('my_callback') + res_dict = json.loads(res.body) + eq_(res_dict['success'], True) + eq_(sorted(res_dict['result']), + sorted([dataset1['name'], dataset2['name']])) + class TestRevisionSearch(helpers.FunctionalTestBase): diff --git a/ckan/tests/controllers/test_feed.py b/ckan/tests/controllers/test_feed.py index 0fc448544eb..1e4c516e029 100644 --- a/ckan/tests/controllers/test_feed.py +++ b/ckan/tests/controllers/test_feed.py @@ -2,13 +2,16 @@ from routes import url_for -from ckan import model import ckan.tests.helpers as helpers import ckan.tests.factories as factories class TestFeedNew(helpers.FunctionalTestBase): + @classmethod + def teardown_class(cls): + helpers.reset_db() + def test_atom_feed_page_zero_gives_error(self): group = factories.Group() offset = url_for(controller='feed', action='group', @@ -32,3 +35,49 @@ def test_atom_feed_page_not_int_gives_error(self): app = self._get_test_app() res = app.get(offset, status=400) assert '"page" parameter must be a positive integer' in res, res + + def test_general_atom_feed_works(self): + dataset = factories.Dataset() + offset = url_for(controller='feed', action='general') + app = self._get_test_app() + res = app.get(offset) + + assert '{0}'.format(dataset['title']) in res.body + + def test_group_atom_feed_works(self): + group = factories.Group() + dataset = factories.Dataset(groups=[{'id': group['id']}]) + offset = url_for(controller='feed', action='group', + id=group['name']) + app = self._get_test_app() + res = app.get(offset) + + assert '{0}'.format(dataset['title']) in res.body + + def test_organization_atom_feed_works(self): + group = factories.Organization() + dataset = factories.Dataset(owner_org=group['id']) + offset = url_for(controller='feed', action='organization', + id=group['name']) + app = self._get_test_app() + res = app.get(offset) + + assert '{0}'.format(dataset['title']) in res.body + + def test_custom_atom_feed_works(self): + dataset1 = factories.Dataset( + title='Test weekly', + extras=[{'key': 'frequency', 'value': 'weekly'}]) + dataset2 = factories.Dataset( + title='Test daily', + extras=[{'key': 'frequency', 'value': 'daily'}]) + offset = url_for(controller='feed', action='custom') + params = { + 'q': 'frequency:weekly' + } + app = self._get_test_app() + res = app.get(offset, params=params) + + assert '{0}'.format(dataset1['title']) in res.body + + assert '{0}'.format(dataset2['title']) not in res.body diff --git a/ckan/tests/controllers/test_group.py b/ckan/tests/controllers/test_group.py index bae63cafc45..67e06c92ce9 100644 --- a/ckan/tests/controllers/test_group.py +++ b/ckan/tests/controllers/test_group.py @@ -1,7 +1,7 @@ # encoding: utf-8 from bs4 import BeautifulSoup -from nose.tools import assert_equal, assert_true +from nose.tools import assert_equal, assert_true, assert_in from routes import url_for @@ -9,7 +9,6 @@ import ckan.model as model from ckan.tests import factories -assert_in = helpers.assert_in webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow diff --git a/ckan/tests/controllers/test_organization.py b/ckan/tests/controllers/test_organization.py index b8e038bcf1e..ed8da4a48a8 100644 --- a/ckan/tests/controllers/test_organization.py +++ b/ckan/tests/controllers/test_organization.py @@ -1,12 +1,12 @@ # encoding: utf-8 from bs4 import BeautifulSoup -from nose.tools import assert_equal, assert_true +from nose.tools import assert_equal, assert_true, assert_in from routes import url_for from mock import patch from ckan.tests import factories, helpers -from ckan.tests.helpers import webtest_submit, submit_and_follow, assert_in +from ckan.tests.helpers import webtest_submit, submit_and_follow class TestOrganizationNew(helpers.FunctionalTestBase): diff --git a/ckan/tests/controllers/test_package.py b/ckan/tests/controllers/test_package.py index cab8b9e4092..0f30518efe0 100644 --- a/ckan/tests/controllers/test_package.py +++ b/ckan/tests/controllers/test_package.py @@ -6,8 +6,10 @@ assert_not_equal, assert_raises, assert_true, + assert_in ) +from mock import patch, MagicMock from routes import url_for import ckan.model as model @@ -16,10 +18,8 @@ import ckan.tests.helpers as helpers import ckan.tests.factories as factories -from ckan.tests.helpers import assert_in -assert_in = helpers.assert_in webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow @@ -40,6 +40,48 @@ def test_form_renders(self): env, response = _get_package_new_page(app) assert_true('dataset-edit' in response.forms) + @helpers.change_config('ckan.auth.create_unowned_dataset', 'false') + def test_needs_organization_but_no_organizations_has_button(self): + ''' Scenario: The settings say every dataset needs an organization + but there are no organizations. If the user is allowed to create an + organization they should be prompted to do so when they try to create + a new dataset''' + app = self._get_test_app() + sysadmin = factories.Sysadmin() + + env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} + response = app.get( + url=url_for(controller='package', action='new'), + extra_environ=env + ) + assert 'dataset-edit' not in response.forms + assert url_for(controller='organization', action='new') in response + + @helpers.mock_auth('ckan.logic.auth.create.package_create') + @helpers.change_config('ckan.auth.create_unowned_dataset', 'false') + @helpers.change_config('ckan.auth.user_create_organizations', 'false') + def test_needs_organization_but_no_organizations_no_button(self, + mock_p_create): + ''' Scenario: The settings say every dataset needs an organization + but there are no organizations. If the user is not allowed to create an + organization they should be told to ask the admin but no link should be + presented. Note: This cannot happen with the default ckan and requires + a plugin to overwrite the package_create behavior''' + mock_p_create.return_value = {'success': True} + + app = self._get_test_app() + user = factories.User() + + env = {'REMOTE_USER': user['name'].encode('ascii')} + response = app.get( + url=url_for(controller='package', action='new'), + extra_environ=env + ) + + assert 'dataset-edit' not in response.forms + assert url_for(controller='organization', action='new') not in response + assert 'Ask a system administrator' in response + def test_name_required(self): app = self._get_test_app() env, response = _get_package_new_page(app) @@ -376,12 +418,6 @@ def test_unauthed_user_creating_dataset(self): class TestPackageEdit(helpers.FunctionalTestBase): - @classmethod - def setup_class(cls): - super(cls, cls).setup_class() - helpers.reset_db() - search.clear_all() - def test_organization_admin_can_edit(self): user = factories.User() organization = factories.Organization( @@ -521,14 +557,6 @@ def test_edit_a_dataset_that_does_not_exist_404s(self): class TestPackageRead(helpers.FunctionalTestBase): - @classmethod - def setup_class(cls): - super(cls, cls).setup_class() - helpers.reset_db() - - def setup(self): - model.repo.rebuild_db() - def test_read(self): dataset = factories.Dataset() app = helpers._get_test_app() @@ -976,9 +1004,6 @@ def setup_class(cls): helpers.reset_db() - def setup(self): - model.repo.rebuild_db() - @classmethod def teardown_class(cls): p.unload('image_view') @@ -1007,17 +1032,19 @@ def test_inexistent_resource_view_page_returns_not_found_code(self): app = self._get_test_app() app.get(url, status=404) + def test_resource_view_description_is_rendered_as_markdown(self): + resource_view = factories.ResourceView(description="Some **Markdown**") + url = url_for(controller='package', + action='resource_read', + id=resource_view['package_id'], + resource_id=resource_view['resource_id'], + view_id=resource_view['id']) + app = self._get_test_app() + response = app.get(url) + response.mustcontain('Some Markdown') -class TestResourceRead(helpers.FunctionalTestBase): - @classmethod - def setup_class(cls): - super(TestResourceRead, cls).setup_class() - helpers.reset_db() - search.clear_all() - - def setup(self): - model.repo.rebuild_db() +class TestResourceRead(helpers.FunctionalTestBase): def test_existing_resource_with_not_associated_dataset(self): dataset = factories.Dataset() @@ -1276,11 +1303,6 @@ def test_confirm_and_cancel_deleting_a_resource(self): class TestSearch(helpers.FunctionalTestBase): - @classmethod - def setup_class(cls): - super(cls, cls).setup_class() - helpers.reset_db() - def test_search_basic(self): dataset1 = factories.Dataset() @@ -1465,6 +1487,104 @@ def test_search_page_results_private(self): assert_true('Dataset Two' in ds_titles) assert_true('Dataset Three' in ds_titles) + def test_user_not_in_organization_cannot_search_private_datasets(self): + app = helpers._get_test_app() + user = factories.User() + organization = factories.Organization() + dataset = factories.Dataset( + owner_org=organization['id'], + private=True, + ) + env = {'REMOTE_USER': user['name'].encode('ascii')} + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url, extra_environ=env) + + search_response_html = BeautifulSoup(search_response.body) + ds_titles = search_response_html.select('.dataset-list ' + '.dataset-item ' + '.dataset-heading a') + assert_equal([n.string for n in ds_titles], []) + + def test_user_in_organization_can_search_private_datasets(self): + app = helpers._get_test_app() + user = factories.User() + organization = factories.Organization( + users=[{'name': user['id'], 'capacity': 'member'}]) + dataset = factories.Dataset( + title='A private dataset', + owner_org=organization['id'], + private=True, + ) + env = {'REMOTE_USER': user['name'].encode('ascii')} + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url, extra_environ=env) + + search_response_html = BeautifulSoup(search_response.body) + ds_titles = search_response_html.select('.dataset-list ' + '.dataset-item ' + '.dataset-heading a') + assert_equal([n.string for n in ds_titles], ['A private dataset']) + + def test_user_in_different_organization_cannot_search_private_datasets(self): + app = helpers._get_test_app() + user = factories.User() + org1 = factories.Organization( + users=[{'name': user['id'], 'capacity': 'member'}]) + org2 = factories.Organization() + dataset = factories.Dataset( + title='A private dataset', + owner_org=org2['id'], + private=True, + ) + env = {'REMOTE_USER': user['name'].encode('ascii')} + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url, extra_environ=env) + + search_response_html = BeautifulSoup(search_response.body) + ds_titles = search_response_html.select('.dataset-list ' + '.dataset-item ' + '.dataset-heading a') + assert_equal([n.string for n in ds_titles], []) + + @helpers.change_config('ckan.search.default_include_private', 'false') + def test_search_default_include_private_false(self): + app = helpers._get_test_app() + user = factories.User() + organization = factories.Organization( + users=[{'name': user['id'], 'capacity': 'member'}]) + dataset = factories.Dataset( + owner_org=organization['id'], + private=True, + ) + env = {'REMOTE_USER': user['name'].encode('ascii')} + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url, extra_environ=env) + + search_response_html = BeautifulSoup(search_response.body) + ds_titles = search_response_html.select('.dataset-list ' + '.dataset-item ' + '.dataset-heading a') + assert_equal([n.string for n in ds_titles], []) + + def test_sysadmin_can_search_private_datasets(self): + app = helpers._get_test_app() + user = factories.Sysadmin() + organization = factories.Organization() + dataset = factories.Dataset( + title='A private dataset', + owner_org=organization['id'], + private=True, + ) + env = {'REMOTE_USER': user['name'].encode('ascii')} + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url, extra_environ=env) + + search_response_html = BeautifulSoup(search_response.body) + ds_titles = search_response_html.select('.dataset-list ' + '.dataset-item ' + '.dataset-heading a') + assert_equal([n.string for n in ds_titles], ['A private dataset']) + class TestPackageFollow(helpers.FunctionalTestBase): diff --git a/ckan/tests/controllers/test_tags.py b/ckan/tests/controllers/test_tags.py index d8c17933515..46b4b255b6f 100644 --- a/ckan/tests/controllers/test_tags.py +++ b/ckan/tests/controllers/test_tags.py @@ -3,7 +3,7 @@ import math import string -from nose.tools import assert_equal, assert_true, assert_false +from nose.tools import assert_equal, assert_true, assert_false, assert_in from bs4 import BeautifulSoup from routes import url_for @@ -11,7 +11,6 @@ import ckan.tests.helpers as helpers from ckan.tests import factories -assert_in = helpers.assert_in webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow @@ -125,7 +124,7 @@ def test_tag_read_redirects_to_dataset_search(self): tag_url = url_for(controller='tag', action='read', id='find-me') tag_response = app.get(tag_url, status=302) assert_equal(tag_response.headers['Location'], - 'http://localhost/dataset?tags=find-me') + 'http://test.ckan.net/dataset?tags=find-me') def test_tag_read_not_found(self): '''Attempting access to non-existing tag returns a 404''' diff --git a/ckan/tests/controllers/test_user.py b/ckan/tests/controllers/test_user.py index 5929dc528d6..b3f63d12bc7 100644 --- a/ckan/tests/controllers/test_user.py +++ b/ckan/tests/controllers/test_user.py @@ -59,6 +59,37 @@ def test_register_user_bad_password(self): response = form.submit('save') assert_true('The passwords you entered do not match' in response) + def test_create_user_as_sysadmin(self): + admin_pass = 'pass' + sysadmin = factories.Sysadmin(password=admin_pass) + app = self._get_test_app() + + # Have to do an actual login as this test relies on repoze + # cookie handling. + + # get the form + response = app.get('/user/login') + # ...it's the second one + login_form = response.forms[1] + # fill it in + login_form['login'] = sysadmin['name'] + login_form['password'] = admin_pass + # submit it + login_form.submit('save') + + response = app.get( + url=url_for(controller='user', action='register'), + ) + assert "user-register-form" in response.forms + form = response.forms['user-register-form'] + form['name'] = 'newestuser' + form['fullname'] = 'Newest User' + form['email'] = 'test@test.com' + form['password1'] = 'testpassword' + form['password2'] = 'testpassword' + response2 = form.submit('save') + assert '/user/activity' in response2.location + class TestLoginView(helpers.FunctionalTestBase): def test_registered_user_login(self): @@ -143,7 +174,9 @@ def test_user_logout_url_redirect(self): @helpers.change_config('ckan.root_path', '/my/prefix') def test_non_root_user_logout_url_redirect(self): - '''_logout url redirects to logged out page. + ''' + _logout url redirects to logged out page with `ckan.root_path` + prefixed. Note: this doesn't test the actual logout of a logged in user, just the associated redirect. @@ -152,10 +185,8 @@ def test_non_root_user_logout_url_redirect(self): logout_url = url_for(controller='user', action='logout') logout_response = app.get(logout_url, status=302) - try: - final_response = helpers.webtest_maybe_follow(logout_response) - except Exception as e: - assert_true('/my/prefix/user/logout' in e.message) + assert_equal(logout_response.status_int, 302) + assert_true('/my/prefix/user/logout' in logout_response.location) class TestUser(helpers.FunctionalTestBase): diff --git a/ckan/tests/controllers/test_util.py b/ckan/tests/controllers/test_util.py index adb02e03657..db76196e677 100644 --- a/ckan/tests/controllers/test_util.py +++ b/ckan/tests/controllers/test_util.py @@ -18,7 +18,7 @@ def test_redirect_ok(self): status=302, ) assert_equal(response.headers.get('Location'), - 'http://localhost/dataset') + 'http://test.ckan.net/dataset') def test_redirect_external(self): app = self._get_test_app() diff --git a/ckan/tests/helpers.py b/ckan/tests/helpers.py index 62f8df16591..5f8e61dba0a 100644 --- a/ckan/tests/helpers.py +++ b/ckan/tests/helpers.py @@ -19,28 +19,30 @@ This module is reserved for these very useful functions. ''' + +import collections +import contextlib +import errno +import functools +import logging +import os +import re + import webtest -from pylons import config import nose.tools +from nose.tools import assert_in, assert_not_in import mock +import rq +from ckan.common import config +import ckan.lib.jobs as jobs +from ckan.lib.redis import connect_to_redis import ckan.lib.search as search import ckan.config.middleware import ckan.model as model import ckan.logic as logic -try: - from nose.tools import assert_in, assert_not_in -except ImportError: - # Python 2.6 doesn't have these, so define them here - def assert_in(a, b, msg=None): - assert a in b, msg or '%r was not in %r' % (a, b) - - def assert_not_in(a, b, msg=None): - assert a not in b, msg or '%r was in %r' % (a, b) - - def reset_db(): '''Reset CKAN's database. @@ -142,6 +144,21 @@ def call_auth(auth_name, context, **kwargs): return logic.check_access(auth_name, context, data_dict=kwargs) +class CKANTestApp(webtest.TestApp): + '''A wrapper around webtest.TestApp + + It adds some convenience methods for CKAN + ''' + + _flask_app = None + + @property + def flask_app(self): + if not self._flask_app: + self._flask_app = self.app.apps['flask_app']._wsgi_app + return self._flask_app + + def _get_test_app(): '''Return a webtest.TestApp for CKAN, with legacy templates disabled. @@ -151,7 +168,7 @@ def _get_test_app(): ''' config['ckan.legacy_templates'] = False app = ckan.config.middleware.make_app(config['global_conf'], **config) - app = webtest.TestApp(app) + app = CKANTestApp(app) return app @@ -202,6 +219,49 @@ def teardown_class(cls): config.update(cls._original_config) +class RQTestBase(object): + ''' + Base class for tests of RQ functionality. + ''' + def setup(self): + u''' + Delete all RQ queues and jobs. + ''' + # See https://github.com/nvie/rq/issues/731 + redis_conn = connect_to_redis() + for queue in rq.Queue.all(connection=redis_conn): + queue.empty() + redis_conn.srem(rq.Queue.redis_queues_keys, queue._key) + redis_conn.delete(queue._key) + + def all_jobs(self): + u''' + Get a list of all RQ jobs. + ''' + jobs = [] + redis_conn = connect_to_redis() + for queue in rq.Queue.all(connection=redis_conn): + jobs.extend(queue.jobs) + return jobs + + def enqueue(self, job=None, *args, **kwargs): + u''' + Enqueue a test job. + ''' + if job is None: + job = jobs.test_job + return jobs.enqueue(job, *args, **kwargs) + + +class FunctionalRQTestBase(FunctionalTestBase, RQTestBase): + ''' + Base class for functional tests of RQ functionality. + ''' + def setup(self): + FunctionalTestBase.setup(self) + RQTestBase.setup(self) + + def submit_and_follow(app, form, extra_environ=None, name=None, value=None, **args): ''' @@ -294,7 +354,7 @@ def webtest_maybe_follow(response, **kw): def change_config(key, value): - '''Decorator to temporarily changes Pylons' config to a new value + '''Decorator to temporarily change CKAN's config to a new value This allows you to easily create tests that need specific config values to be set, making sure it'll be reverted to what it was originally, after your @@ -304,26 +364,93 @@ def change_config(key, value): @helpers.change_config('ckan.site_title', 'My Test CKAN') def test_ckan_site_title(self): - assert pylons.config['ckan.site_title'] == 'My Test CKAN' + assert config['ckan.site_title'] == 'My Test CKAN' :param key: the config key to be changed, e.g. ``'ckan.site_title'`` :type key: string :param value: the new config key's value, e.g. ``'My Test CKAN'`` :type value: string + + .. seealso:: The context manager :py:func:`changed_config` ''' + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + with changed_config(key, value): + return func(*args, **kwargs) + return wrapper + return decorator + + +@contextlib.contextmanager +def changed_config(key, value): + ''' + Context manager for temporarily changing a config value. + + Allows you to temporarily change the value of a CKAN configuration + option. The original value is restored once the context manager is + left. + + Usage:: + + with changed_config(u'ckan.site_title', u'My Test CKAN'): + assert config[u'ckan.site_title'] == u'My Test CKAN' + + .. seealso:: The decorator :py:func:`change_config` + ''' + _original_config = config.copy() + config[key] = value + try: + yield + finally: + config.clear() + config.update(_original_config) + + +def mock_auth(auth_function_path): + ''' + Decorator to easily mock a CKAN auth method in the context of a test + function + + It adds a mock object for the provided auth_function_path as a parameter to + the test function. + + Essentially it makes sure that `ckan.authz.clear_auth_functions_cache` is + called before and after to make sure that the auth functions pick up + the newly changed values. + + Usage:: + + @helpers.mock_auth('ckan.logic.auth.create.package_create') + def test_mock_package_create(self, mock_package_create): + from ckan import logic + mock_package_create.return_value = {'success': True} + + # package_create is mocked + eq_(logic.check_access('package_create', {}), True) + + assert mock_package_create.called + + :param action_name: the full path to the auth function to be mocked, + e.g. ``ckan.logic.auth.create.package_create`` + :type action_name: string + + ''' + from ckan.authz import clear_auth_functions_cache + def decorator(func): def wrapper(*args, **kwargs): - _original_config = config.copy() - config[key] = value try: - return_value = func(*args, **kwargs) + with mock.patch(auth_function_path) as mocked_auth: + clear_auth_functions_cache() + new_args = args + tuple([mocked_auth]) + return_value = func(*new_args, **kwargs) finally: - config.clear() - config.update(_original_config) - + clear_auth_functions_cache() return return_value + return nose.tools.make_decorator(func)(wrapper) return decorator @@ -416,3 +543,128 @@ def wrapper(*args, **kwargs): return return_value return nose.tools.make_decorator(func)(wrapper) return decorator + + +@contextlib.contextmanager +def recorded_logs(logger=None, level=logging.DEBUG, + override_disabled=True, override_global_level=True): + u''' + Context manager for recording log messages. + + :param logger: The logger to record messages from. Can either be a + :py:class:`logging.Logger` instance or a string with the + logger's name. Defaults to the root logger. + + :param int level: Temporary log level for the target logger while + the context manager is active. Pass ``None`` if you don't want + the level to be changed. The level is automatically reset to its + original value when the context manager is left. + + :param bool override_disabled: A logger can be disabled by setting + its ``disabled`` attribute. By default, this context manager + sets that attribute to ``False`` at the beginning of its + execution and resets it when the context manager is left. Set + ``override_disabled`` to ``False`` to keep the current value + of the attribute. + + :param bool override_global_level: The ``logging.disable`` function + allows one to install a global minimum log level that takes + precedence over a logger's own level. By default, this context + manager makes sure that the global limit is at most ``level``, + and reduces it if necessary during its execution. Set + ``override_global_level`` to ``False`` to keep the global limit. + + :returns: A recording log handler that listens to ``logger`` during + the execution of the context manager. + :rtype: :py:class:`RecordingLogHandler` + + Example:: + + import logging + + logger = logging.getLogger(__name__) + + with recorded_logs(logger) as logs: + logger.info(u'Hello, world!') + + logs.assert_log(u'info', u'world') + ''' + if logger is None: + logger = logging.getLogger() + elif not isinstance(logger, logging.Logger): + logger = logging.getLogger(logger) + handler = RecordingLogHandler() + old_level = logger.level + manager_level = logger.manager.disable + disabled = logger.disabled + logger.addHandler(handler) + try: + if level is not None: + logger.setLevel(level) + if override_disabled: + logger.disabled = False + if override_global_level: + if (level is None) and (manager_level > old_level): + logger.manager.disable = old_level + elif (level is not None) and (manager_level > level): + logger.manager.disable = level + yield handler + finally: + logger.handlers.remove(handler) + logger.setLevel(old_level) + logger.disabled = disabled + logger.manager.disable = manager_level + + +class RecordingLogHandler(logging.Handler): + u''' + Log handler that records log messages for later inspection. + + You can inspect the recorded messages via the ``messages`` attribute + (a dict that maps log levels to lists of messages) or by using + ``assert_log``. + + This class is rarely useful on its own, instead use + :py:func:`recorded_logs` to temporarily record log messages. + ''' + def __init__(self, *args, **kwargs): + super(RecordingLogHandler, self).__init__(*args, **kwargs) + self.clear() + + def emit(self, record): + self.messages[record.levelname.lower()].append(record.getMessage()) + + def assert_log(self, level, pattern, msg=None): + u''' + Assert that a certain message has been logged. + + :param string pattern: A regex which the message has to match. + The match is done using ``re.search``. + + :param string level: The message level (``'debug'``, ...). + + :param string msg: Optional failure message in case the expected + log message was not logged. + + :raises AssertionError: If the expected message was not logged. + ''' + compiled_pattern = re.compile(pattern) + for log_msg in self.messages[level]: + if compiled_pattern.search(log_msg): + return + if not msg: + if self.messages[level]: + lines = u'\n '.join(self.messages[level]) + msg = (u'Pattern "{}" was not found in the log messages for ' + + u'level "{}":\n {}').format(pattern, level, lines) + else: + msg = (u'Pattern "{}" was not found in the log messages for ' + + u'level "{}" (no messages were recorded for that ' + + u'level).').format(pattern, level) + raise AssertionError(msg) + + def clear(self): + u''' + Clear all captured log messages. + ''' + self.messages = collections.defaultdict(list) diff --git a/ckan/tests/legacy/__init__.py b/ckan/tests/legacy/__init__.py index acaafc60b52..3a056855f95 100644 --- a/ckan/tests/legacy/__init__.py +++ b/ckan/tests/legacy/__init__.py @@ -18,7 +18,7 @@ from nose.plugins.skip import SkipTest import time -from pylons import config +from ckan.common import config from pylons.test import pylonsapp from paste.script.appinstall import SetupCommand @@ -355,15 +355,6 @@ def skip_test(*args): def clear_flash(res=None): messages = h._flash.pop_messages() -try: - from nose.tools import assert_in, assert_not_in -except ImportError: - def assert_in(a, b, msg=None): - assert a in b, msg or '%r was not in %r' % (a, b) - def assert_not_in(a, b, msg=None): - assert a not in b, msg or '%r was in %r' % (a, b) - - class StatusCodes: STATUS_200_OK = 200 STATUS_201_CREATED = 201 diff --git a/ckan/tests/legacy/functional/api/base.py b/ckan/tests/legacy/functional/api/base.py index 9948d0ff3d4..fefedae0f65 100644 --- a/ckan/tests/legacy/functional/api/base.py +++ b/ckan/tests/legacy/functional/api/base.py @@ -8,7 +8,7 @@ import urllib -from pylons import config +from ckan.common import config import webhelpers.util from nose.tools import assert_equal from paste.fixture import TestRequest diff --git a/ckan/tests/legacy/functional/api/model/test_package.py b/ckan/tests/legacy/functional/api/model/test_package.py index ddf83a328cd..22472d7dac3 100644 --- a/ckan/tests/legacy/functional/api/model/test_package.py +++ b/ckan/tests/legacy/functional/api/model/test_package.py @@ -253,7 +253,7 @@ def test_register_post_indexerror(self): """ Test that we can't add a package if Solr is down. """ - bad_solr_url = 'http://127.0.0.1/badsolrurl' + bad_solr_url = 'http://example.com/badsolrurl' original_settings = SolrSettings.get()[0] try: SolrSettings.init(bad_solr_url) @@ -643,7 +643,7 @@ def test_entity_update_indexerror(self): """ Test that we can't update a package if Solr is down. """ - bad_solr_url = 'http://127.0.0.1/badsolrurl' + bad_solr_url = 'http://example.com/badsolrurl' original_settings = SolrSettings.get()[0] try: SolrSettings.init(bad_solr_url) diff --git a/ckan/tests/legacy/functional/api/test_activity.py b/ckan/tests/legacy/functional/api/test_activity.py index 33b614713a6..1350b7acaba 100644 --- a/ckan/tests/legacy/functional/api/test_activity.py +++ b/ckan/tests/legacy/functional/api/test_activity.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) import pylons.test -from pylons import config +from ckan.common import config from paste.deploy.converters import asbool import paste.fixture from nose import SkipTest diff --git a/ckan/tests/legacy/functional/api/test_api.py b/ckan/tests/legacy/functional/api/test_api.py index 7c7a1756008..7f172ed1129 100644 --- a/ckan/tests/legacy/functional/api/test_api.py +++ b/ckan/tests/legacy/functional/api/test_api.py @@ -2,10 +2,10 @@ import json +from nose.tools import assert_in + from ckan.tests.legacy.functional.api.base import * -import ckan.tests.legacy -assert_in = ckan.tests.legacy.assert_in class ApiTestCase(ApiTestCase, ControllerTestCase): diff --git a/ckan/tests/legacy/functional/api/test_email_notifications.py b/ckan/tests/legacy/functional/api/test_email_notifications.py index fe562756570..35bea72dbcb 100644 --- a/ckan/tests/legacy/functional/api/test_email_notifications.py +++ b/ckan/tests/legacy/functional/api/test_email_notifications.py @@ -14,7 +14,8 @@ import paste.deploy import pylons.test -from pylons import config +from ckan.common import config + class TestEmailNotifications(mock_mail_server.SmtpServerHarness, pylons_controller.PylonsTestCase): diff --git a/ckan/tests/legacy/functional/api/test_user.py b/ckan/tests/legacy/functional/api/test_user.py index 846ae8b9efe..e470ce2d8bd 100644 --- a/ckan/tests/legacy/functional/api/test_user.py +++ b/ckan/tests/legacy/functional/api/test_user.py @@ -1,7 +1,7 @@ # encoding: utf-8 import paste -from pylons import config +from ckan.common import config from nose.tools import assert_equal import ckan.logic as logic diff --git a/ckan/tests/legacy/functional/test_activity.py b/ckan/tests/legacy/functional/test_activity.py index dccdf303bad..f40f1397d78 100644 --- a/ckan/tests/legacy/functional/test_activity.py +++ b/ckan/tests/legacy/functional/test_activity.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from pylons import config +from ckan.common import config from pylons.test import pylonsapp from paste.deploy.converters import asbool import paste.fixture diff --git a/ckan/tests/legacy/functional/test_package.py b/ckan/tests/legacy/functional/test_package.py index ee342985768..b09a0f55ef9 100644 --- a/ckan/tests/legacy/functional/test_package.py +++ b/ckan/tests/legacy/functional/test_package.py @@ -2,7 +2,7 @@ import datetime -from pylons import config, c +from ckan.common import config, c from difflib import unified_diff from nose.tools import assert_equal @@ -612,7 +612,7 @@ def test_after_create_plugin_hook(self): plugins.unload('test_package_controller_plugin') def test_new_indexerror(self): - bad_solr_url = 'http://127.0.0.1/badsolrurl' + bad_solr_url = 'http://example.com/badsolrurl' solr_url = SolrSettings.get()[0] try: SolrSettings.init(bad_solr_url) diff --git a/ckan/tests/legacy/functional/test_user.py b/ckan/tests/legacy/functional/test_user.py index 2aa5e6f7abe..d432e0789ac 100644 --- a/ckan/tests/legacy/functional/test_user.py +++ b/ckan/tests/legacy/functional/test_user.py @@ -1,8 +1,8 @@ # encoding: utf-8 -from routes import url_for +from ckan.lib.helpers import url_for from nose.tools import assert_equal -from pylons import config +from ckan.common import config import hashlib from ckan.tests.legacy import CreateTestData diff --git a/ckan/tests/legacy/lib/test_dictization.py b/ckan/tests/legacy/lib/test_dictization.py index 864a22b06c0..6a7cfbf8399 100644 --- a/ckan/tests/legacy/lib/test_dictization.py +++ b/ckan/tests/legacy/lib/test_dictization.py @@ -1,10 +1,10 @@ # encoding: utf-8 -from ckan.tests.legacy import assert_equal, assert_not_in, assert_in +from nose.tools import assert_equal, assert_not_in, assert_in from pprint import pprint, pformat from difflib import unified_diff -import ckan.lib.search as search +import ckan.lib.search as search from ckan.lib.create_test_data import CreateTestData from ckan import model from ckan.lib.dictization import (table_dictize, diff --git a/ckan/tests/legacy/lib/test_helpers.py b/ckan/tests/legacy/lib/test_helpers.py index fe5d0ad7f1b..b0bfb9ba20b 100644 --- a/ckan/tests/legacy/lib/test_helpers.py +++ b/ckan/tests/legacy/lib/test_helpers.py @@ -3,7 +3,7 @@ import datetime from nose.tools import assert_equal, assert_raises -from pylons import config +from ckan.common import config from ckan.tests.legacy import * import ckan.lib.helpers as h diff --git a/ckan/tests/legacy/lib/test_i18n.py b/ckan/tests/legacy/lib/test_i18n.py index 3851fb8df2e..91e78f87258 100644 --- a/ckan/tests/legacy/lib/test_i18n.py +++ b/ckan/tests/legacy/lib/test_i18n.py @@ -1,10 +1,11 @@ # encoding: utf-8 from nose.tools import assert_equal -from pylons import config, session +from pylons import session import pylons import ckan.lib.i18n +from ckan.common import config from ckan.tests.legacy.pylons_controller import PylonsTestCase diff --git a/ckan/tests/legacy/lib/test_navl.py b/ckan/tests/legacy/lib/test_navl.py index 04cfe17e158..8eb44c4e6a3 100644 --- a/ckan/tests/legacy/lib/test_navl.py +++ b/ckan/tests/legacy/lib/test_navl.py @@ -8,7 +8,7 @@ missing, augment_data, validate, - validate_flattened) + _validate) from pprint import pprint, pformat from ckan.lib.navl.validators import (identity_converter, empty, @@ -352,5 +352,14 @@ def test_range_validator(): assert errors == {'name': [u'Missing value'], 'email': [u'Please enter a number that is 10 or smaller']}, errors +def validate_flattened(data, schema, context=None): + context = context or {} + assert isinstance(data, dict) + converted_data, errors = _validate(data, schema, context) + for key, value in errors.items(): + if not value: + errors.pop(key) + + return converted_data, errors diff --git a/ckan/tests/legacy/lib/test_simple_search.py b/ckan/tests/legacy/lib/test_simple_search.py deleted file mode 100644 index d271b31cf2f..00000000000 --- a/ckan/tests/legacy/lib/test_simple_search.py +++ /dev/null @@ -1,37 +0,0 @@ -# encoding: utf-8 - -from nose.tools import assert_equal - -from ckan import model -from ckan.lib.create_test_data import CreateTestData -from ckan.lib.search.sql import PackageSearchQuery - -class TestSimpleSearch: - @classmethod - def setup_class(cls): - CreateTestData.create() - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_get_all_entity_ids(self): - ids = PackageSearchQuery().get_all_entity_ids() - anna = model.Package.by_name(u'annakarenina') - assert anna.id in ids - assert len(ids) >= 2, len(ids) - - def test_run_query_basic(self): - res = PackageSearchQuery().run({'q':'annakarenina'}) - anna = model.Package.by_name(u'annakarenina') - assert_equal(res, {'results': [{'id': anna.id}], 'count': 1}) - - def test_run_query_home(self): - # This is the query from the CKAN home page - res = PackageSearchQuery().run({'q': '*:*'}) - assert res['count'] >= 2, res['count'] - - def test_run_query_all(self): - # This is the default query from the search page - res = PackageSearchQuery().run({'q': u''}) - assert res['count'] >= 2, res['count'] diff --git a/ckan/tests/legacy/lib/test_solr_search_index.py b/ckan/tests/legacy/lib/test_solr_search_index.py index e1bbce3a228..55a6d5977f7 100644 --- a/ckan/tests/legacy/lib/test_solr_search_index.py +++ b/ckan/tests/legacy/lib/test_solr_search_index.py @@ -1,7 +1,7 @@ # encoding: utf-8 import pysolr -from pylons import config +from ckan.common import config from ckan import model import ckan.lib.search as search from ckan.tests.legacy import TestController, CreateTestData, setup_test_search_index, is_search_supported diff --git a/ckan/tests/legacy/logic/test_action.py b/ckan/tests/legacy/logic/test_action.py index 558b7c20407..7d669137744 100644 --- a/ckan/tests/legacy/logic/test_action.py +++ b/ckan/tests/legacy/logic/test_action.py @@ -6,7 +6,7 @@ from pprint import pprint from nose.tools import assert_equal, assert_raises from nose.plugins.skip import SkipTest -from pylons import config +from ckan.common import config import datetime import mock @@ -116,22 +116,6 @@ def test_01_package_list_private(self): assert 'public_dataset' in res assert not 'private_dataset' in res - def test_01_package_show_with_jsonp(self): - anna_id = model.Package.by_name(u'annakarenina').id - postparams = '%s=1' % json.dumps({'id': anna_id}) - res = self.app.post('/api/action/package_show?callback=jsoncallback', params=postparams) - - assert re.match('jsoncallback\(.*\);', res.body), res - # Unwrap JSONP callback (we want to look at the data). - msg = res.body[len('jsoncallback')+1:-2] - res_dict = json.loads(msg) - assert_equal(res_dict['success'], True) - assert "/api/3/action/help_show?name=package_show" in res_dict['help'] - pkg = res_dict['result'] - assert_equal(pkg['name'], 'annakarenina') - missing_keys = set(('title', 'groups')) - set(pkg.keys()) - assert not missing_keys, missing_keys - def test_02_package_autocomplete_match_name(self): postparams = '%s=1' % json.dumps({'q':'war', 'limit': 5}) res = self.app.post('/api/action/package_autocomplete', params=postparams) diff --git a/ckan/tests/legacy/logic/test_auth.py b/ckan/tests/legacy/logic/test_auth.py index f7b94a681c5..4544d8cab8b 100644 --- a/ckan/tests/legacy/logic/test_auth.py +++ b/ckan/tests/legacy/logic/test_auth.py @@ -1,7 +1,7 @@ # encoding: utf-8 import paste -from pylons import config +from ckan.common import config import ckan.config.middleware import ckan.tests.legacy as tests from ckan.logic import get_action diff --git a/ckan/tests/legacy/misc/test_mock_mail_server.py b/ckan/tests/legacy/misc/test_mock_mail_server.py index a0a1ae28a5d..6d657d821e1 100644 --- a/ckan/tests/legacy/misc/test_mock_mail_server.py +++ b/ckan/tests/legacy/misc/test_mock_mail_server.py @@ -2,7 +2,7 @@ import time from nose.tools import assert_equal -from pylons import config +from ckan.common import config from email.mime.text import MIMEText import hashlib diff --git a/ckan/tests/legacy/misc/test_sync.py b/ckan/tests/legacy/misc/test_sync.py index 10b355e47fb..7c7bf3669aa 100644 --- a/ckan/tests/legacy/misc/test_sync.py +++ b/ckan/tests/legacy/misc/test_sync.py @@ -5,7 +5,7 @@ import urllib2 import time -from pylons import config +from ckan.common import config import ckan.model as model from ckan.tests.legacy import * diff --git a/ckan/tests/legacy/mock_mail_server.py b/ckan/tests/legacy/mock_mail_server.py index 81db8c4c09a..501cdd1055c 100644 --- a/ckan/tests/legacy/mock_mail_server.py +++ b/ckan/tests/legacy/mock_mail_server.py @@ -5,7 +5,7 @@ import socket from smtpd import SMTPServer -from pylons import config +from ckan.common import config class MockSmtpServer(SMTPServer): diff --git a/ckan/tests/legacy/models/test_group.py b/ckan/tests/legacy/models/test_group.py index 9d0e76acd96..a4da7148088 100644 --- a/ckan/tests/legacy/models/test_group.py +++ b/ckan/tests/legacy/models/test_group.py @@ -1,9 +1,12 @@ # encoding: utf-8 -from ckan.tests.legacy import assert_equal, assert_in, assert_not_in, CreateTestData +from nose.tools import assert_in, assert_not_in, assert_equal + +from ckan.tests.legacy import CreateTestData import ckan.model as model + class TestGroup(object): @classmethod diff --git a/ckan/tests/legacy/test_coding_standards.py b/ckan/tests/legacy/test_coding_standards.py index b3c556833d5..4f592b94cb5 100644 --- a/ckan/tests/legacy/test_coding_standards.py +++ b/ckan/tests/legacy/test_coding_standards.py @@ -14,7 +14,7 @@ Please do not add new files to the list as any new files should meet the current coding standards. Please add comments by files that fail if there -are legitamate reasons for the failure. +are legitimate reasons for the failure. ''' import sys @@ -398,7 +398,6 @@ class TestPep8(object): 'ckan/lib/jinja_extensions.py', 'ckan/lib/jsonp.py', 'ckan/lib/maintain.py', - 'ckan/lib/navl/dictization_functions.py', 'ckan/lib/navl/validators.py', 'ckan/lib/package_saver.py', 'ckan/lib/plugins.py', @@ -488,7 +487,6 @@ class TestPep8(object): 'ckan/model/authz.py', 'ckan/model/dashboard.py', 'ckan/model/domain_object.py', - 'ckan/model/extension.py', 'ckan/model/follower.py', 'ckan/model/group.py', 'ckan/model/group_extra.py', diff --git a/ckan/tests/legacy/test_plugins.py b/ckan/tests/legacy/test_plugins.py index 61343f12a57..3af1524ad4b 100644 --- a/ckan/tests/legacy/test_plugins.py +++ b/ckan/tests/legacy/test_plugins.py @@ -6,7 +6,7 @@ from nose.tools import raises, assert_equal from unittest import TestCase from pyutilib.component.core import PluginGlobals -from pylons import config +from ckan.common import config import ckan.logic as logic import ckan.authz as authz @@ -97,14 +97,14 @@ def test_plugins_load(self): config_plugins = config['ckan.plugins'] config['ckan.plugins'] = 'mapper_plugin routes_plugin' - plugins.load_all(config) + plugins.load_all() # synchronous_search automatically gets loaded current_plugins = set([plugins.get_plugin(p) for p in ['mapper_plugin', 'routes_plugin', 'synchronous_search'] + find_system_plugins()]) assert PluginGlobals.env().services == current_plugins # cleanup config['ckan.plugins'] = config_plugins - plugins.load_all(config) + plugins.load_all() def test_only_configured_plugins_loaded(self): with plugins.use_plugin('mapper_plugin') as p: @@ -119,7 +119,7 @@ def test_plugin_loading_order(self): """ config_plugins = config['ckan.plugins'] config['ckan.plugins'] = 'test_observer_plugin mapper_plugin mapper_plugin2' - plugins.load_all(config) + plugins.load_all() observerplugin = plugins.get_plugin('test_observer_plugin') @@ -133,7 +133,7 @@ def test_plugin_loading_order(self): assert observerplugin.after_load.calls[:3] == expected_order config['ckan.plugins'] = 'test_observer_plugin mapper_plugin2 mapper_plugin' - plugins.load_all(config) + plugins.load_all() expected_order = _make_calls(plugins.get_plugin('mapper_plugin2'), plugins.get_plugin('mapper_plugin')) @@ -144,7 +144,7 @@ def test_plugin_loading_order(self): assert observerplugin.after_load.calls[:3] == expected_order # cleanup config['ckan.plugins'] = config_plugins - plugins.load_all(config) + plugins.load_all() def test_mapper_plugin_fired_on_insert(self): with plugins.use_plugin('mapper_plugin') as mapper_plugin: diff --git a/ckan/tests/lib/search/test_index.py b/ckan/tests/lib/search/test_index.py index dd783e6d500..fcf34694376 100644 --- a/ckan/tests/lib/search/test_index.py +++ b/ckan/tests/lib/search/test_index.py @@ -3,17 +3,13 @@ import datetime import hashlib import json -import nose.tools import nose +from nose.tools import assert_equal, assert_in, assert_not_in -from pylons import config +from ckan.common import config import ckan.lib.search as search import ckan.tests.helpers as helpers -assert_equal = nose.tools.assert_equal -assert_in = helpers.assert_in -assert_not_in = helpers.assert_not_in - class TestSearchIndex(object): diff --git a/ckan/tests/lib/test_cli.py b/ckan/tests/lib/test_cli.py index c8ddd77222d..e94664687d4 100644 --- a/ckan/tests/lib/test_cli.py +++ b/ckan/tests/lib/test_cli.py @@ -1,22 +1,78 @@ # encoding: utf-8 +import datetime import logging +import os +import os.path +from StringIO import StringIO +import sys +import tempfile -from nose.tools import assert_raises +from nose.tools import (assert_raises, eq_ as eq, ok_ as ok, assert_in, + assert_not_in, assert_not_equal as neq, assert_false as nok) +from paste.script.command import run -from ckan.lib.cli import UserCmd +import ckan.lib.cli as cli +import ckan.lib.jobs as jobs import ckan.tests.helpers as helpers +from ckan.common import config log = logging.getLogger(__name__) +def paster(*args, **kwargs): + ''' + Call a paster command. + + All arguments are parsed and passed on to the command. The + ``--config`` option is automatically appended. + + By default, an ``AssertionError`` is raised if the command exits + with a non-zero return code or if anything is written to STDERR. + Pass ``fail_on_error=False`` to disable this behavior. + + Example:: + + code, stdout, stderr = paster(u'jobs', u'list') + assert u'My Job Title' in stdout + + code, stdout, stderr = paster(u'jobs', u'foobar', + fail_on_error=False) + assert code == 1 + assert u'Unknown command' in stderr + + Any ``SystemExit`` raised by the command is swallowed. + + :returns: A tuple containing the return code, the content of + STDOUT, and the content of STDERR. + ''' + fail_on_error = kwargs.pop(u'fail_on_error', True) + args = list(args) + [u'--config=' + config[u'__file__']] + sys.stdout, sys.stderr = StringIO(u''), StringIO(u'') + code = 0 + try: + run(args) + except SystemExit as e: + code = e.code + finally: + stdout, stderr = sys.stdout.getvalue(), sys.stderr.getvalue() + sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__ + if code != 0 and fail_on_error: + raise AssertionError(u'Paster command exited with non-zero ' + + u'return code {}: {}'.format(code, stderr)) + if stderr.strip() and fail_on_error: + raise AssertionError(u'Paster command wrote to STDERR: {}'.format( + stderr)) + return code, stdout, stderr + + class TestUserAdd(object): '''Tests for UserCmd.add''' @classmethod def setup_class(cls): - cls.user_cmd = UserCmd('user-command') + cls.user_cmd = cli.UserCmd('user-command') def setup(self): helpers.reset_db() @@ -70,3 +126,220 @@ def test_cli_user_add_unicode_fullname_system_exit(self): self.user_cmd.add() except SystemExit: assert False, "SystemExit exception shouldn't be raised" + + +class TestJobsUnknown(helpers.RQTestBase): + ''' + Test unknown sub-command for ``paster jobs``. + ''' + def test_unknown_command(self): + ''' + Test error handling for unknown ``paster jobs`` sub-command. + ''' + code, stdout, stderr = paster(u'jobs', u'does-not-exist', + fail_on_error=False) + neq(code, 0) + assert_in(u'Unknown command', stderr) + + +class TestJobsList(helpers.RQTestBase): + ''' + Tests for ``paster jobs list``. + ''' + def test_list_default_queue(self): + ''' + Test output of ``jobs list`` for default queue. + ''' + job = self.enqueue() + stdout = paster(u'jobs', u'list')[1] + fields = stdout.split() + eq(len(fields), 3) + dt = datetime.datetime.strptime(fields[0], u'%Y-%m-%dT%H:%M:%S') + now = datetime.datetime.utcnow() + ok(abs((now - dt).total_seconds()) < 10) + eq(fields[1], job.id) + eq(fields[2], jobs.DEFAULT_QUEUE_NAME) + + def test_list_other_queue(self): + ''' + Test output of ``jobs.list`` for non-default queue. + ''' + job = self.enqueue(queue=u'my_queue') + stdout = paster(u'jobs', u'list')[1] + fields = stdout.split() + eq(len(fields), 3) + eq(fields[2], u'my_queue') + + def test_list_title(self): + ''' + Test title output of ``jobs list``. + ''' + job = self.enqueue(title=u'My_Title') + stdout = paster(u'jobs', u'list')[1] + fields = stdout.split() + eq(len(fields), 4) + eq(fields[3], u'"My_Title"') + + def test_list_filter(self): + ''' + Test filtering by queues for ``jobs list``. + ''' + job1 = self.enqueue(queue=u'q1') + job2 = self.enqueue(queue=u'q2') + job3 = self.enqueue(queue=u'q3') + stdout = paster(u'jobs', u'list', u'q1', u'q2')[1] + assert_in(u'q1', stdout) + assert_in(u'q2', stdout) + assert_not_in(u'q3', stdout) + + +class TestJobShow(helpers.RQTestBase): + ''' + Tests for ``paster jobs show``. + ''' + def test_show_existing(self): + ''' + Test ``jobs show`` for an existing job. + ''' + job = self.enqueue(queue=u'my_queue', title=u'My Title') + stdout = paster(u'jobs', u'show', job.id)[1] + assert_in(job.id, stdout) + assert_in(jobs.remove_queue_name_prefix(job.origin), stdout) + + def test_show_missing_id(self): + ''' + Test ``jobs show`` with a missing ID. + ''' + code, stdout, stderr = paster(u'jobs', u'show', fail_on_error=False) + neq(code, 0) + ok(stderr) + + +class TestJobsCancel(helpers.RQTestBase): + ''' + Tests for ``paster jobs cancel``. + ''' + def test_cancel_existing(self): + ''' + Test ``jobs cancel`` for an existing job. + ''' + job1 = self.enqueue() + job2 = self.enqueue() + stdout = paster(u'jobs', u'cancel', job1.id)[1] + all_jobs = self.all_jobs() + eq(len(all_jobs), 1) + eq(all_jobs[0].id, job2.id) + assert_in(job1.id, stdout) + + def test_cancel_not_existing(self): + ''' + Test ``jobs cancel`` for a not existing job. + ''' + code, stdout, stderr = paster(u'jobs', u'cancel', u'does-not-exist', + fail_on_error=False) + neq(code, 0) + assert_in(u'does-not-exist', stderr) + + def test_cancel_missing_id(self): + ''' + Test ``jobs cancel`` with a missing ID. + ''' + code, stdout, stderr = paster(u'jobs', u'cancel', fail_on_error=False) + neq(code, 0) + ok(stderr) + + +class TestJobsClear(helpers.RQTestBase): + ''' + Tests for ``paster jobs clear``. + ''' + def test_clear_all_queues(self): + ''' + Test clearing all queues via ``jobs clear``. + ''' + self.enqueue() + self.enqueue() + self.enqueue(queue=u'q1') + self.enqueue(queue=u'q2') + stdout = paster(u'jobs', u'clear')[1] + assert_in(jobs.DEFAULT_QUEUE_NAME, stdout) + assert_in(u'q1', stdout) + assert_in(u'q2', stdout) + eq(self.all_jobs(), []) + + def test_clear_specific_queues(self): + ''' + Test clearing specific queues via ``jobs clear``. + ''' + job1 = self.enqueue() + job2 = self.enqueue(queue=u'q1') + self.enqueue(queue=u'q2') + self.enqueue(queue=u'q2') + self.enqueue(queue=u'q3') + stdout = paster(u'jobs', u'clear', u'q2', u'q3')[1] + assert_in(u'q2', stdout) + assert_in(u'q3', stdout) + assert_not_in(jobs.DEFAULT_QUEUE_NAME, stdout) + assert_not_in(u'q1', stdout) + all_jobs = self.all_jobs() + eq(set(all_jobs), {job1, job2}) + + +class TestJobsTest(helpers.RQTestBase): + ''' + Tests for ``paster jobs test``. + ''' + def test_test_default_queue(self): + ''' + Test ``jobs test`` for the default queue. + ''' + stdout = paster(u'jobs', u'test')[1] + all_jobs = self.all_jobs() + eq(len(all_jobs), 1) + eq(jobs.remove_queue_name_prefix(all_jobs[0].origin), + jobs.DEFAULT_QUEUE_NAME) + + def test_test_specific_queues(self): + ''' + Test ``jobs test`` for specific queues. + ''' + stdout = paster(u'jobs', u'test', u'q1', u'q2')[1] + all_jobs = self.all_jobs() + eq(len(all_jobs), 2) + eq({jobs.remove_queue_name_prefix(j.origin) for j in all_jobs}, + {u'q1', u'q2'}) + + +class TestJobsWorker(helpers.RQTestBase): + ''' + Tests for ``paster jobs worker``. + ''' + # All tests of ``jobs worker`` must use the ``--burst`` option to + # make sure that the worker exits. + + def test_worker_default_queue(self): + ''' + Test ``jobs worker`` with the default queue. + ''' + with tempfile.NamedTemporaryFile(delete=False) as f: + self.enqueue(os.remove, args=[f.name]) + paster(u'jobs', u'worker', u'--burst') + all_jobs = self.all_jobs() + eq(all_jobs, []) + nok(os.path.isfile(f.name)) + + def test_worker_specific_queues(self): + ''' + Test ``jobs worker`` with specific queues. + ''' + with tempfile.NamedTemporaryFile(delete=False) as f: + with tempfile.NamedTemporaryFile(delete=False) as g: + job1 = self.enqueue() + job2 = self.enqueue(queue=u'q2') + self.enqueue(os.remove, args=[f.name], queue=u'q3') + self.enqueue(os.remove, args=[g.name], queue=u'q4') + paster(u'jobs', u'worker', u'--burst', u'q3', u'q4') + all_jobs = self.all_jobs() + eq(set(all_jobs), {job1, job2}) + nok(os.path.isfile(f.name)) + nok(os.path.isfile(g.name)) diff --git a/ckan/tests/lib/test_datapreview.py b/ckan/tests/lib/test_datapreview.py index de80b55b5c3..dd4c85dc3df 100644 --- a/ckan/tests/lib/test_datapreview.py +++ b/ckan/tests/lib/test_datapreview.py @@ -1,7 +1,7 @@ # encoding: utf-8 import nose -from pylons import config +from ckan.common import config import ckan.plugins as p import ckan.lib.datapreview as datapreview diff --git a/ckan/tests/lib/test_io.py b/ckan/tests/lib/test_io.py new file mode 100644 index 00000000000..13e06a37aab --- /dev/null +++ b/ckan/tests/lib/test_io.py @@ -0,0 +1,41 @@ +# encoding: utf-8 + +import io +import os.path +import shutil +import tempfile + +from nose.tools import eq_, ok_, raises + +import ckan.lib.io as ckan_io + + +class TestDecodeEncodePath(object): + + @raises(TypeError) + def test_decode_path_fails_for_unicode(self): + ckan_io.decode_path(u'just_a_unicode') + + @raises(TypeError) + def test_encode_path_fails_for_str(self): + ckan_io.encode_path(b'just_a_str') + + def test_decode_path_returns_unicode(self): + ok_(isinstance(ckan_io.decode_path(b'just_a_str'), unicode)) + + def test_encode_path_returns_str(self): + ok_(isinstance(ckan_io.encode_path(u'just_a_unicode'), str)) + + def test_decode_encode_path(self): + temp_dir = ckan_io.decode_path(tempfile.mkdtemp()) + try: + filename = u'\xf6\xe4\xfc.txt' + path = os.path.join(temp_dir, filename) + with io.open(ckan_io.encode_path(path), u'w', + encoding=u'utf-8') as f: + f.write(u'foo') + # Force str return type + filenames = os.listdir(ckan_io.encode_path(temp_dir)) + eq_(ckan_io.decode_path(filenames[0]), filename) + finally: + shutil.rmtree(temp_dir) diff --git a/ckan/tests/lib/test_jobs.py b/ckan/tests/lib/test_jobs.py new file mode 100644 index 00000000000..b69e7ab3bd2 --- /dev/null +++ b/ckan/tests/lib/test_jobs.py @@ -0,0 +1,201 @@ +# encoding: utf-8 + +u''' +Tests for ``ckan.lib.jobs``. +''' + +import datetime + +from nose.tools import ok_, assert_equal, raises +import rq + +import ckan.lib.jobs as jobs +from ckan.tests.helpers import changed_config, recorded_logs, RQTestBase +from ckan.common import config + + +class TestQueueNamePrefixes(RQTestBase): + + def test_queue_name_prefix_contains_site_id(self): + prefix = jobs.add_queue_name_prefix(u'') + ok_(config[u'ckan.site_id'] in prefix) + + def test_queue_name_removal_with_prefix(self): + plain = u'foobar' + prefixed = jobs.add_queue_name_prefix(plain) + assert_equal(jobs.remove_queue_name_prefix(prefixed), plain) + + @raises(ValueError) + def test_queue_name_removal_without_prefix(self): + jobs.remove_queue_name_prefix(u'foobar') + + +class TestEnqueue(RQTestBase): + + def test_enqueue_return_value(self): + job = self.enqueue() + ok_(isinstance(job, rq.job.Job)) + + def test_enqueue_args(self): + self.enqueue() + self.enqueue(args=[1, 2]) + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 2) + assert_equal(len(all_jobs[0].args), 0) + assert_equal(all_jobs[1].args, [1, 2]) + + def test_enqueue_kwargs(self): + self.enqueue() + self.enqueue(kwargs={u'foo': 1}) + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 2) + assert_equal(len(all_jobs[0].kwargs), 0) + assert_equal(all_jobs[1].kwargs, {u'foo': 1}) + + def test_enqueue_title(self): + self.enqueue() + self.enqueue(title=u'Title') + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 2) + assert_equal(all_jobs[0].meta[u'title'], None) + assert_equal(all_jobs[1].meta[u'title'], u'Title') + + def test_enqueue_queue(self): + self.enqueue() + self.enqueue(queue=u'my_queue') + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 2) + assert_equal(all_jobs[0].origin, + jobs.add_queue_name_prefix(jobs.DEFAULT_QUEUE_NAME)) + assert_equal(all_jobs[1].origin, + jobs.add_queue_name_prefix(u'my_queue')) + + +class TestGetAllQueues(RQTestBase): + + def test_foreign_queues_are_ignored(self): + u''' + Test that foreign RQ-queues are ignored. + ''' + # Create queues for this CKAN instance + self.enqueue(queue=u'q1') + self.enqueue(queue=u'q2') + # Create queue for another CKAN instance + with changed_config(u'ckan.site_id', u'some-other-ckan-instance'): + self.enqueue(queue=u'q2') + # Create queue not related to CKAN + rq.Queue(u'q4').enqueue_call(jobs.test_job) + all_queues = jobs.get_all_queues() + names = {jobs.remove_queue_name_prefix(q.name) for q in all_queues} + assert_equal(names, {u'q1', u'q2'}) + + +class TestGetQueue(RQTestBase): + + def test_get_queue_default_queue(self): + u''' + Test that the default queue is returned if no queue is given. + ''' + q = jobs.get_queue() + assert_equal(jobs.remove_queue_name_prefix(q.name), + jobs.DEFAULT_QUEUE_NAME) + + def test_get_queue_other_queue(self): + u''' + Test that a different queue can be given. + ''' + q = jobs.get_queue(u'my_queue') + assert_equal(jobs.remove_queue_name_prefix(q.name), u'my_queue') + + +class TestJobFromID(RQTestBase): + + def test_job_from_id_existing(self): + job = self.enqueue() + assert_equal(jobs.job_from_id(job.id), job) + job = self.enqueue(queue=u'my_queue') + assert_equal(jobs.job_from_id(job.id), job) + + @raises(KeyError) + def test_job_from_id_not_existing(self): + jobs.job_from_id(u'does-not-exist') + + +class TestDictizeJob(RQTestBase): + + def test_dictize_job(self): + job = self.enqueue(title=u'Title', queue=u'my_queue') + d = jobs.dictize_job(job) + assert_equal(d[u'id'], job.id) + assert_equal(d[u'title'], u'Title') + assert_equal(d[u'queue'], u'my_queue') + dt = datetime.datetime.strptime(d[u'created'], u'%Y-%m-%dT%H:%M:%S') + now = datetime.datetime.utcnow() + ok_(abs((now - dt).total_seconds()) < 10) + + +def failing_job(): + u''' + A background job that fails. + ''' + raise RuntimeError(u'JOB FAILURE') + + +class TestWorker(RQTestBase): + + def test_worker_logging_lifecycle(self): + u''' + Test that a logger's lifecycle is logged. + ''' + queue = u'my_queue' + job = self.enqueue(queue=queue) + with recorded_logs(u'ckan.lib.jobs') as logs: + worker = jobs.Worker([queue]) + worker.work(burst=True) + messages = logs.messages[u'info'] + # We expect 4 log messages: Worker start, job start, job end, + # worker end. + assert_equal(len(messages), 4) + ok_(worker.key in messages[0]) + ok_(queue in messages[0]) + ok_(worker.key in messages[1]) + ok_(job.id in messages[1]) + ok_(worker.key in messages[2]) + ok_(job.id in messages[2]) + ok_(worker.key in messages[3]) + + def test_worker_exception_logging(self): + u''' + Test that exceptions in a job are logged. + ''' + job = self.enqueue(failing_job) + worker = jobs.Worker() + + # Prevent worker from forking so that we can capture log + # messages from within the job + def execute_job(*args, **kwargs): + return worker.perform_job(*args, **kwargs) + + worker.execute_job = execute_job + with recorded_logs(u'ckan.lib.jobs') as logs: + worker.work(burst=True) + logs.assert_log(u'error', u'JOB FAILURE') + + def test_worker_default_queue(self): + self.enqueue() + self.enqueue(queue=u'my_queue') + jobs.Worker().work(burst=True) + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 1) + assert_equal(jobs.remove_queue_name_prefix(all_jobs[0].origin), + u'my_queue') + + def test_worker_multiple_queues(self): + self.enqueue() + self.enqueue(queue=u'queue1') + self.enqueue(queue=u'queue2') + jobs.Worker([u'queue1', u'queue2']).work(burst=True) + all_jobs = self.all_jobs() + assert_equal(len(all_jobs), 1) + assert_equal(jobs.remove_queue_name_prefix(all_jobs[0].origin), + jobs.DEFAULT_QUEUE_NAME) diff --git a/ckan/tests/lib/test_mailer.py b/ckan/tests/lib/test_mailer.py index b45b41b6e47..2ea839371cb 100644 --- a/ckan/tests/lib/test_mailer.py +++ b/ckan/tests/lib/test_mailer.py @@ -1,13 +1,13 @@ # encoding: utf-8 -from nose.tools import assert_equal, assert_raises -from pylons import config +from nose.tools import assert_equal, assert_raises, assert_in, assert_raises from email.mime.text import MIMEText from email.parser import Parser from email.header import decode_header import hashlib import base64 +from ckan.common import config import ckan.model as model import ckan.lib.helpers as h import ckan.lib.mailer as mailer @@ -16,8 +16,6 @@ import ckan.tests.helpers as helpers import ckan.tests.factories as factories -assert_in = helpers.assert_in - class MailerBase(SmtpServerHarness): @@ -225,3 +223,13 @@ def test_send_invite_email_with_org(self): body = self.get_email_body(msg[3]) assert_in(org['title'], body) assert_in(h.roles_translated()[role], body) + + @helpers.change_config('smtp.test_server', '999.999.999.999') + def test_bad_smtp_host(self): + test_email = {'recipient_name': 'Bob', + 'recipient_email': 'b@example.com', + 'subject': 'Meeting', + 'body': 'The meeting is cancelled.', + 'headers': {'header1': 'value1'}} + assert_raises(mailer.MailerException, + mailer.mail_recipient, **test_email) diff --git a/ckan/tests/lib/test_munge.py b/ckan/tests/lib/test_munge.py index 50225ed7e68..0f89fc3096a 100644 --- a/ckan/tests/lib/test_munge.py +++ b/ckan/tests/lib/test_munge.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from nose.tools import assert_equal +from nose.tools import assert_equal, ok_ from ckan.lib.munge import (munge_filename_legacy, munge_filename, munge_name, munge_title_to_name, munge_tag) @@ -57,6 +57,7 @@ class TestMungeFilename(object): ('path/to/file.csv', 'file.csv'), ('.longextension', '.longextension'), ('a.longextension', 'a.longextension'), + ('a.now_that_extension_is_too_long', 'a.now_that_extension_i'), ('.1', '.1_'), ] @@ -65,6 +66,7 @@ def test_munge_filename(self): for org, exp in self.munge_list: munge = munge_filename(org) assert_equal(munge, exp) + ok_(isinstance(munge, unicode)) def test_munge_filename_multiple_pass(self): '''Munging filename multiple times produces same result.''' diff --git a/ckan/tests/logic/action/test_create.py b/ckan/tests/logic/action/test_create.py index b48f65c5f9d..41bad556f69 100644 --- a/ckan/tests/logic/action/test_create.py +++ b/ckan/tests/logic/action/test_create.py @@ -4,7 +4,7 @@ ''' -from pylons import config +from ckan.common import config import mock import nose.tools @@ -152,7 +152,6 @@ def setup_class(cls): @classmethod def teardown_class(cls): p.unload('image_view') - helpers.reset_db() def setup(self): @@ -485,6 +484,48 @@ def test_organization_member_creation(self): assert_equals(new_membership['table_id'], user['id']) assert_equals(new_membership['capacity'], 'member') + def test_group_member_creation_raises_validation_error_if_id_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'group_member_create', + username='someuser', + role='member',) + + def test_group_member_creation_raises_validation_error_if_username_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'group_member_create', + id='someid', + role='member',) + + def test_group_member_creation_raises_validation_error_if_role_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'group_member_create', + id='someid', + username='someuser',) + + def test_org_member_creation_raises_validation_error_if_id_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'organization_member_create', + username='someuser', + role='member',) + + def test_org_member_creation_raises_validation_error_if_username_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'organization_member_create', + id='someid', + role='member',) + + def test_org_member_creation_raises_validation_error_if_role_missing(self): + + assert_raises(logic.ValidationError, + helpers.call_action, 'organization_member_create', + id='someid', + username='someuser',) + class TestDatasetCreate(helpers.FunctionalTestBase): diff --git a/ckan/tests/logic/action/test_delete.py b/ckan/tests/logic/action/test_delete.py index 94b750a8ff2..6563cb926b5 100644 --- a/ckan/tests/logic/action/test_delete.py +++ b/ckan/tests/logic/action/test_delete.py @@ -1,5 +1,7 @@ # encoding: utf-8 +import re + import nose.tools import ckan.tests.helpers as helpers @@ -7,10 +9,15 @@ import ckan.logic as logic import ckan.model as model import ckan.plugins as p +import ckan.lib.jobs as jobs import ckan.lib.search as search + assert_equals = nose.tools.assert_equals assert_raises = nose.tools.assert_raises +eq = nose.tools.eq_ +ok = nose.tools.ok_ +raises = nose.tools.raises class TestDelete: @@ -451,6 +458,30 @@ def test_purged_dataset_leaves_no_trace_in_the_model(self): # No Revision objects were purged or created assert_equals(num_revisions_after - num_revisions_before, 0) + def test_purged_dataset_removed_from_relationships(self): + child = factories.Dataset() + parent = factories.Dataset() + grandparent = factories.Dataset() + + helpers.call_action('package_relationship_create', + subject=child['id'], + type='child_of', + object=parent['id']) + + helpers.call_action('package_relationship_create', + subject=parent['id'], + type='child_of', + object=grandparent['id']) + + assert_equals(len( + model.Session.query(model.PackageRelationship).all()), 2) + + helpers.call_action('dataset_purge', + context={'ignore_auth': True}, + id=parent['name']) + + assert_equals(model.Session.query(model.PackageRelationship).all(), []) + def test_missing_id_returns_error(self): assert_raises(logic.ValidationError, helpers.call_action, 'dataset_purge') @@ -458,3 +489,57 @@ def test_missing_id_returns_error(self): def test_bad_id_returns_404(self): assert_raises(logic.NotFound, helpers.call_action, 'dataset_purge', id='123') + + +class TestJobClear(helpers.FunctionalRQTestBase): + + def test_all_queues(self): + ''' + Test clearing all queues. + ''' + self.enqueue() + self.enqueue(queue=u'q') + self.enqueue(queue=u'q') + self.enqueue(queue=u'q') + queues = helpers.call_action(u'job_clear') + eq({jobs.DEFAULT_QUEUE_NAME, u'q'}, set(queues)) + all_jobs = self.all_jobs() + eq(len(all_jobs), 0) + + def test_specific_queues(self): + ''' + Test clearing specific queues. + ''' + job1 = self.enqueue() + job2 = self.enqueue(queue=u'q1') + job3 = self.enqueue(queue=u'q1') + job4 = self.enqueue(queue=u'q2') + with helpers.recorded_logs(u'ckan.logic') as logs: + queues = helpers.call_action(u'job_clear', queues=[u'q1', u'q2']) + eq({u'q1', u'q2'}, set(queues)) + all_jobs = self.all_jobs() + eq(len(all_jobs), 1) + eq(all_jobs[0], job1) + logs.assert_log(u'info', u'q1') + logs.assert_log(u'info', u'q2') + + +class TestJobCancel(helpers.FunctionalRQTestBase): + + def test_existing_job(self): + ''' + Test cancelling an existing job. + ''' + job1 = self.enqueue(queue=u'q') + job2 = self.enqueue(queue=u'q') + with helpers.recorded_logs(u'ckan.logic') as logs: + helpers.call_action(u'job_cancel', id=job1.id) + all_jobs = self.all_jobs() + eq(len(all_jobs), 1) + eq(all_jobs[0], job2) + assert_raises(KeyError, jobs.job_from_id, job1.id) + logs.assert_log(u'info', re.escape(job1.id)) + + @raises(logic.NotFound) + def test_not_existing_job(self): + helpers.call_action(u'job_cancel', id=u'does-not-exist') diff --git a/ckan/tests/logic/action/test_get.py b/ckan/tests/logic/action/test_get.py index c664d817b18..d0649cf3415 100644 --- a/ckan/tests/logic/action/test_get.py +++ b/ckan/tests/logic/action/test_get.py @@ -1,5 +1,7 @@ # encoding: utf-8 +import datetime + import nose.tools import ckan.logic as logic @@ -11,6 +13,7 @@ eq = nose.tools.eq_ +ok = nose.tools.ok_ assert_raises = nose.tools.assert_raises @@ -961,10 +964,10 @@ def test_package_search_with_fq_excludes_private(self): factories.Dataset(user=user, private=True, owner_org=org['name']) fq = "capacity:private" - results = helpers.call_action('package_search', fq=fq)['results'] + results = logic.get_action('package_search')( + {}, {'fq': fq})['results'] - eq(len(results), 1) - eq(results[0]['name'], dataset['name']) + eq(len(results), 0) def test_package_search_with_fq_excludes_drafts(self): ''' @@ -997,7 +1000,8 @@ def test_package_search_with_include_drafts_option_excludes_drafts_for_anon_user draft_dataset = factories.Dataset(user=user, state='draft') factories.Dataset(user=user, private=True, owner_org=org['name']) - results = helpers.call_action('package_search', include_drafts=True)['results'] + results = logic.get_action('package_search')( + {}, {'include_drafts': True})['results'] eq(len(results), 1) nose.tools.assert_not_equals(results[0]['name'], draft_dataset['name']) @@ -1018,8 +1022,8 @@ def test_package_search_with_include_drafts_option_includes_drafts_for_sysadmin( other_draft_dataset = factories.Dataset(user=other_user, state='draft') factories.Dataset(user=user, private=True, owner_org=org['name']) - results = helpers.call_action('package_search', include_drafts=True, - context={'user': sysadmin['name']})['results'] + results = logic.get_action('package_search')( + {'user': sysadmin['name']}, {'include_drafts': True})['results'] eq(len(results), 3) names = [r['name'] for r in results] @@ -1042,8 +1046,8 @@ def test_package_search_with_include_drafts_false_option_doesnot_include_drafts_ other_draft_dataset = factories.Dataset(user=other_user, state='draft') factories.Dataset(user=user, private=True, owner_org=org['name']) - results = helpers.call_action('package_search', include_drafts=False, - context={'user': sysadmin['name']})['results'] + results = logic.get_action('package_search')( + {'user': sysadmin['name']}, {'include_drafts': False})['results'] eq(len(results), 1) names = [r['name'] for r in results] @@ -1066,8 +1070,8 @@ def test_package_search_with_include_drafts_option_includes_drafts_for_user(self other_draft_dataset = factories.Dataset(user=other_user, state='draft', name="other-draft-dataset") factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") - results = helpers.call_action('package_search', include_drafts=True, - context={'user': user['name']})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, {'include_drafts': True})['results'] eq(len(results), 3) names = [r['name'] for r in results] @@ -1092,8 +1096,8 @@ def test_package_search_with_fq_for_create_user_id_will_include_datasets_for_oth factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") fq = "creator_user_id:{0}".format(other_user['id']) - results = helpers.call_action('package_search', fq=fq, - context={'user': user['name']})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, {'fq': fq})['results'] eq(len(results), 1) names = [r['name'] for r in results] @@ -1118,8 +1122,9 @@ def test_package_search_with_fq_for_create_user_id_will_not_include_drafts_for_o factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") fq = "(creator_user_id:{0} AND +state:draft)".format(other_user['id']) - results = helpers.call_action('package_search', fq=fq, - context={'user': user['name']})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, + {'fq': fq, 'include_drafts': True})['results'] eq(len(results), 0) @@ -1139,8 +1144,9 @@ def test_package_search_with_fq_for_creator_user_id_and_drafts_and_include_draft factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") fq = "(creator_user_id:{0} AND +state:draft)".format(other_user['id']) - results = helpers.call_action('package_search', fq=fq, include_drafts=True, - context={'user': user['name']})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, + {'fq': fq, 'include_drafts': True})['results'] eq(len(results), 0) @@ -1160,8 +1166,9 @@ def test_package_search_with_fq_for_creator_user_id_and_include_drafts_option_wi factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") fq = "creator_user_id:{0}".format(other_user['id']) - results = helpers.call_action('package_search', fq=fq, include_drafts=True, - context={'user': user['name']})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, + {'fq': fq, 'include_drafts': True})['results'] names = [r['name'] for r in results] eq(len(results), 1) @@ -1184,32 +1191,53 @@ def test_package_search_with_fq_for_create_user_id_will_include_drafts_for_other factories.Dataset(user=user, private=True, owner_org=org['name'], name="private-dataset") fq = "(creator_user_id:{0} AND +state:draft)".format(user['id']) - results = helpers.call_action('package_search', fq=fq, - context={'user': sysadmin['name']})['results'] + results = logic.get_action('package_search')( + {'user': sysadmin['name']}, + {'fq': fq})['results'] names = [r['name'] for r in results] eq(len(results), 1) nose.tools.assert_true(dataset['name'] not in names) nose.tools.assert_true(draft_dataset['name'] in names) - def test_package_search_private_with_ignore_capacity_check(self): + def test_package_search_private_with_include_private(self): ''' package_search() can return private datasets when - `ignore_capacity_check` present in context. + `include_private=True` ''' user = factories.User() org = factories.Organization(user=user) - factories.Dataset(user=user) factories.Dataset(user=user, state='deleted') factories.Dataset(user=user, state='draft') private_dataset = factories.Dataset(user=user, private=True, owner_org=org['name']) - fq = '+capacity:"private"' - results = helpers.call_action('package_search', fq=fq, - context={'ignore_capacity_check': True})['results'] + results = logic.get_action('package_search')( + {'user': user['name']}, {'include_private': True})['results'] - eq(len(results), 1) - eq(results[0]['name'], private_dataset['name']) + eq([r['name'] for r in results], [private_dataset['name']]) + + def test_package_search_private_with_include_private_wont_show_other_orgs_private(self): + user = factories.User() + user2 = factories.User() + org = factories.Organization(user=user) + org2 = factories.Organization(user=user2) + private_dataset = factories.Dataset(user=user2, private=True, owner_org=org2['name']) + + results = logic.get_action('package_search')( + {'user': user['name']}, {'include_private': True})['results'] + + eq([r['name'] for r in results], []) + + def test_package_search_private_with_include_private_syadmin(self): + user = factories.User() + sysadmin = factories.Sysadmin() + org = factories.Organization(user=user) + private_dataset = factories.Dataset(user=user, private=True, owner_org=org['name']) + + results = logic.get_action('package_search')( + {'user': sysadmin['name']}, {'include_private': True})['results'] + + eq([r['name'] for r in results], [private_dataset['name']]) def test_package_works_without_user_in_context(self): ''' @@ -2018,3 +2046,98 @@ def test_user_delete_marks_membership_of_org_as_deleted(self): capacity='member') eq(len(org_members), 0) + + +class TestFollow(helpers.FunctionalTestBase): + + def test_followee_list(self): + + group1 = factories.Group(title='Finance') + group2 = factories.Group(title='Environment') + group3 = factories.Group(title='Education') + + user = factories.User() + + context = {'user': user['name']} + + helpers.call_action('follow_group', context, id=group1['id']) + helpers.call_action('follow_group', context, id=group2['id']) + + followee_list = helpers.call_action('followee_list', context, + id=user['name']) + + eq(len(followee_list), 2) + eq(sorted([f['display_name'] for f in followee_list]), + ['Environment', 'Finance']) + + def test_followee_list_with_q(self): + + group1 = factories.Group(title='Finance') + group2 = factories.Group(title='Environment') + group3 = factories.Group(title='Education') + + user = factories.User() + + context = {'user': user['name']} + + helpers.call_action('follow_group', context, id=group1['id']) + helpers.call_action('follow_group', context, id=group2['id']) + + followee_list = helpers.call_action('followee_list', context, + id=user['name'], + q='E') + + eq(len(followee_list), 1) + eq(followee_list[0]['display_name'], 'Environment') + + +class TestJobList(helpers.FunctionalRQTestBase): + + def test_all_queues(self): + ''' + Test getting jobs from all queues. + ''' + job1 = self.enqueue() + job2 = self.enqueue() + job3 = self.enqueue(queue=u'my_queue') + jobs = helpers.call_action(u'job_list') + eq(len(jobs), 3) + eq({job[u'id'] for job in jobs}, {job1.id, job2.id, job3.id}) + + def test_specific_queues(self): + ''' + Test getting jobs from specific queues. + ''' + job1 = self.enqueue() + job2 = self.enqueue(queue=u'q2') + job3 = self.enqueue(queue=u'q3') + job4 = self.enqueue(queue=u'q3') + jobs = helpers.call_action(u'job_list', queues=[u'q2']) + eq(len(jobs), 1) + eq(jobs[0][u'id'], job2.id) + jobs = helpers.call_action(u'job_list', queues=[u'q2', u'q3']) + eq(len(jobs), 3) + eq({job[u'id'] for job in jobs}, {job2.id, job3.id, job4.id}) + + +class TestJobShow(helpers.FunctionalRQTestBase): + + def test_existing_job(self): + ''' + Test showing an existing job. + ''' + job = self.enqueue(queue=u'my_queue', title=u'Title') + d = helpers.call_action(u'job_show', id=job.id) + eq(d[u'id'], job.id) + eq(d[u'title'], u'Title') + eq(d[u'queue'], u'my_queue') + dt = datetime.datetime.strptime(d[u'created'], u'%Y-%m-%dT%H:%M:%S') + now = datetime.datetime.utcnow() + ok(abs((now - dt).total_seconds()) < 10) + + @nose.tools.raises(logic.NotFound) + def test_not_existing_job(self): + ''' + Test showing a not existing job. + ''' + helpers.call_action(u'job_show', id=u'does-not-exist') diff --git a/ckan/tests/logic/action/test_patch.py b/ckan/tests/logic/action/test_patch.py index e910b1618dc..4de8a8765c5 100644 --- a/ckan/tests/logic/action/test_patch.py +++ b/ckan/tests/logic/action/test_patch.py @@ -5,7 +5,7 @@ from nose.tools import assert_equals, assert_raises import mock -import pylons.config as config +from ckan.common import config from ckan.tests import helpers, factories diff --git a/ckan/tests/logic/action/test_update.py b/ckan/tests/logic/action/test_update.py index 73e766368ef..13061d34640 100644 --- a/ckan/tests/logic/action/test_update.py +++ b/ckan/tests/logic/action/test_update.py @@ -5,7 +5,7 @@ import nose.tools import mock -import pylons.config as config +from ckan.common import config import ckan.logic as logic import ckan.lib.app_globals as app_globals @@ -14,7 +14,7 @@ import ckan.tests.factories as factories from ckan import model -assert_equals = nose.tools.assert_equals +assert_equals = eq_ = nose.tools.assert_equals assert_raises = nose.tools.assert_raises @@ -858,3 +858,165 @@ def test_user_create_password_hash_not_for_normal_users(self): user_obj = model.User.get(user['id']) assert user_obj.password != 'pretend-this-is-a-valid-hash' + + +class TestPackageOwnerOrgUpdate(object): + + @classmethod + def teardown_class(cls): + helpers.reset_db() + + def setup(self): + helpers.reset_db() + + def test_package_owner_org_added(self): + '''A package without an owner_org can have one added.''' + sysadmin = factories.Sysadmin() + org = factories.Organization() + dataset = factories.Dataset() + context = { + 'user': sysadmin['name'], + } + assert dataset['owner_org'] is None + helpers.call_action('package_owner_org_update', + context=context, + id=dataset['id'], + organization_id=org['id']) + dataset_obj = model.Package.get(dataset['id']) + assert dataset_obj.owner_org == org['id'] + + def test_package_owner_org_changed(self): + '''A package with an owner_org can have it changed.''' + + sysadmin = factories.Sysadmin() + org_1 = factories.Organization() + org_2 = factories.Organization() + dataset = factories.Dataset(owner_org=org_1['id']) + context = { + 'user': sysadmin['name'], + } + assert dataset['owner_org'] == org_1['id'] + helpers.call_action('package_owner_org_update', + context=context, + id=dataset['id'], + organization_id=org_2['id']) + dataset_obj = model.Package.get(dataset['id']) + assert dataset_obj.owner_org == org_2['id'] + + def test_package_owner_org_removed(self): + '''A package with an owner_org can have it removed.''' + sysadmin = factories.Sysadmin() + org = factories.Organization() + dataset = factories.Dataset(owner_org=org['id']) + context = { + 'user': sysadmin['name'], + } + assert dataset['owner_org'] == org['id'] + helpers.call_action('package_owner_org_update', + context=context, + id=dataset['id'], + organization_id=None) + dataset_obj = model.Package.get(dataset['id']) + assert dataset_obj.owner_org is None + + +class TestBulkOperations(object): + + @classmethod + def teardown_class(cls): + helpers.reset_db() + + def setup(self): + helpers.reset_db() + + def test_bulk_make_private(self): + + org = factories.Organization() + + dataset1 = factories.Dataset(owner_org=org['id']) + dataset2 = factories.Dataset(owner_org=org['id']) + + helpers.call_action('bulk_update_private', {}, + datasets=[dataset1['id'], dataset2['id']], + org_id=org['id']) + + # Check search index + datasets = helpers.call_action('package_search', {}, + q='owner_org:{0}'.format(org['id'])) + + for dataset in datasets['results']: + eq_(dataset['private'], True) + + # Check DB + datasets = model.Session.query(model.Package) \ + .filter(model.Package.owner_org == org['id']).all() + for dataset in datasets: + eq_(dataset.private, True) + + revisions = model.Session.query(model.PackageRevision) \ + .filter(model.PackageRevision.owner_org == org['id']) \ + .filter(model.PackageRevision.current is True) \ + .all() + for revision in revisions: + eq_(revision.private, True) + + def test_bulk_make_public(self): + + org = factories.Organization() + + dataset1 = factories.Dataset(owner_org=org['id'], private=True) + dataset2 = factories.Dataset(owner_org=org['id'], private=True) + + helpers.call_action('bulk_update_public', {}, + datasets=[dataset1['id'], dataset2['id']], + org_id=org['id']) + + # Check search index + datasets = helpers.call_action('package_search', {}, + q='owner_org:{0}'.format(org['id'])) + + for dataset in datasets['results']: + eq_(dataset['private'], False) + + # Check DB + datasets = model.Session.query(model.Package) \ + .filter(model.Package.owner_org == org['id']).all() + for dataset in datasets: + eq_(dataset.private, False) + + revisions = model.Session.query(model.PackageRevision) \ + .filter(model.PackageRevision.owner_org == org['id']) \ + .filter(model.PackageRevision.current is True) \ + .all() + for revision in revisions: + eq_(revision.private, False) + + def test_bulk_delete(self): + + org = factories.Organization() + + dataset1 = factories.Dataset(owner_org=org['id']) + dataset2 = factories.Dataset(owner_org=org['id']) + + helpers.call_action('bulk_update_delete', {}, + datasets=[dataset1['id'], dataset2['id']], + org_id=org['id']) + + # Check search index + datasets = helpers.call_action('package_search', {}, + q='owner_org:{0}'.format(org['id'])) + + eq_(datasets['results'], []) + + # Check DB + datasets = model.Session.query(model.Package) \ + .filter(model.Package.owner_org == org['id']).all() + for dataset in datasets: + eq_(dataset.state, 'deleted') + + revisions = model.Session.query(model.PackageRevision) \ + .filter(model.PackageRevision.owner_org == org['id']) \ + .filter(model.PackageRevision.current is True) \ + .all() + for revision in revisions: + eq_(revision.state, 'deleted') diff --git a/ckan/tests/logic/test_validators.py b/ckan/tests/logic/test_validators.py index 8a5d854e1ce..9e8e1090259 100644 --- a/ckan/tests/logic/test_validators.py +++ b/ckan/tests/logic/test_validators.py @@ -11,13 +11,13 @@ import mock import nose.tools -import ckan.tests.factories as factories # Import some test helper functions from another module. # This is bad (test modules shouldn't share code with eachother) but because of # the way validator functions are organised in CKAN (in different modules in # different places in the code) we have to either do this or introduce a shared # test helper functions module (which we also don't want to do). import ckan.tests.lib.navl.test_validators as t + import ckan.lib.navl.dictization_functions as df import ckan.logic.validators as validators import ckan.tests.factories as factories diff --git a/ckan/tests/model/test_license.py b/ckan/tests/model/test_license.py index 50c9510c420..733cbdb5f4b 100644 --- a/ckan/tests/model/test_license.py +++ b/ckan/tests/model/test_license.py @@ -3,7 +3,7 @@ import os from nose.tools import assert_equal -from pylons import config +from ckan.common import config from ckan.model.license import LicenseRegister from ckan.tests import helpers diff --git a/ckan/tests/test_coding_standards.py b/ckan/tests/test_coding_standards.py index 8726f277bd3..679ca764859 100644 --- a/ckan/tests/test_coding_standards.py +++ b/ckan/tests/test_coding_standards.py @@ -1,6 +1,6 @@ # encoding: utf-8 -'''A module for coding standards tests. +u'''A module for coding standards tests. These are tests that are not functional- or unit-testing any particular piece of CKAN code, but are checking coding standards. For example: checking that @@ -9,17 +9,55 @@ ''' +import ast import io import os import os.path import re import subprocess +import sys -import ckan.lib.util as util + +FILESYSTEM_ENCODING = unicode(sys.getfilesystemencoding() + or sys.getdefaultencoding()) + +HERE = os.path.abspath(os.path.dirname(__file__.decode(FILESYSTEM_ENCODING))) + +PROJECT_ROOT = os.path.normpath(os.path.join(HERE, u'..', u'..')) + +# Directories which are ignored when checking Python source code files +IGNORED_DIRS = [ + u'ckan/include', +] + + +def walk_python_files(): + u''' + Generator that yields all CKAN Python source files. + + Yields 2-tuples containing the filename in absolute and relative (to + the project root) form. + ''' + def _is_dir_ignored(root, d): + if d.startswith(u'.'): + return True + return os.path.join(rel_root, d) in IGNORED_DIRS + + for abs_root, dirnames, filenames in os.walk(PROJECT_ROOT): + rel_root = os.path.relpath(abs_root, PROJECT_ROOT) + if rel_root == u'.': + rel_root = u'' + dirnames[:] = [d for d in dirnames if not _is_dir_ignored(rel_root, d)] + for filename in filenames: + if not filename.endswith(u'.py'): + continue + abs_name = os.path.join(abs_root, filename) + rel_name = os.path.join(rel_root, filename) + yield abs_name, rel_name def test_building_the_docs(): - '''There should be no warnings or errors when building the Sphinx docs. + u'''There should be no warnings or errors when building the Sphinx docs. This test unfortunately does take quite a long time to run - rebuilding the docs from scratch just takes a long time. @@ -28,35 +66,39 @@ def test_building_the_docs(): ''' try: - output = util.check_output( - ['python', 'setup.py', 'build_sphinx', '--all-files', '--fresh-env'], + output = subprocess.check_output( + [b'python', + b'setup.py', + b'build_sphinx', + b'--all-files', + b'--fresh-env'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as err: assert False, ( - "Building the docs failed with return code: {code}".format( + u"Building the docs failed with return code: {code}".format( code=err.returncode)) - output_lines = output.split('\n') + output_lines = output.split(u'\n') - errors = [line for line in output_lines if 'ERROR' in line] + errors = [line for line in output_lines if u'ERROR' in line] if errors: - assert False, ("Don't add any errors to the Sphinx build: " - "{errors}".format(errors=errors)) + assert False, (u"Don't add any errors to the Sphinx build: " + u"{errors}".format(errors=errors)) - warnings = [line for line in output_lines if 'WARNING' in line] + warnings = [line for line in output_lines if u'WARNING' in line] # Some warnings have been around for a long time and aren't easy to fix. # These are allowed, but no more should be added. allowed_warnings = [ - 'WARNING: duplicate label ckan.auth.create_user_via_web', - 'WARNING: duplicate label ckan.auth.create_unowned_dataset', - 'WARNING: duplicate label ckan.auth.user_create_groups', - 'WARNING: duplicate label ckan.auth.anon_create_dataset', - 'WARNING: duplicate label ckan.auth.user_delete_organizations', - 'WARNING: duplicate label ckan.auth.create_user_via_api', - 'WARNING: duplicate label ckan.auth.create_dataset_if_not_in_organization', - 'WARNING: duplicate label ckan.auth.user_delete_groups', - 'WARNING: duplicate label ckan.auth.user_create_organizations', - 'WARNING: duplicate label ckan.auth.roles_that_cascade_to_sub_groups' + u'WARNING: duplicate label ckan.auth.create_user_via_web', + u'WARNING: duplicate label ckan.auth.create_unowned_dataset', + u'WARNING: duplicate label ckan.auth.user_create_groups', + u'WARNING: duplicate label ckan.auth.anon_create_dataset', + u'WARNING: duplicate label ckan.auth.user_delete_organizations', + u'WARNING: duplicate label ckan.auth.create_user_via_api', + u'WARNING: duplicate label ckan.auth.create_dataset_if_not_in_organization', + u'WARNING: duplicate label ckan.auth.user_delete_groups', + u'WARNING: duplicate label ckan.auth.user_create_organizations', + u'WARNING: duplicate label ckan.auth.roles_that_cascade_to_sub_groups' ] # Remove the allowed warnings from the list of collected warnings. @@ -71,56 +113,599 @@ def test_building_the_docs(): if warning not in warnings_to_remove] if new_warnings: - assert False, ("Don't add any new warnings to the Sphinx build: " - "{warnings}".format(warnings=new_warnings)) + assert False, (u"Don't add any new warnings to the Sphinx build: " + u"{warnings}".format(warnings=new_warnings)) def test_source_files_specify_encoding(): - ''' + u''' Test that *.py files have a PEP 263 UTF-8 encoding specification. Empty files and files that only contain comments are ignored. ''' - root_dir = os.path.join(os.path.dirname(__file__), '..', '..') - test_dirs = ['ckan', 'ckanext'] - ignored_dirs = ['ckan/include'] - pattern = re.compile(r'#.*?coding[:=][ \t]*utf-?8') + pattern = re.compile(ur'#.*?coding[:=][ \t]*utf-?8') decode_errors = [] no_specification = [] - - def check_file(filename): + for abs_path, rel_path in walk_python_files(): try: - with io.open(filename, encoding='utf-8') as f: + with io.open(abs_path, encoding=u'utf-8') as f: for line in f: line = line.strip() if pattern.match(line): # Pattern found - return - elif line and not line.startswith('#'): + break + elif line and not line.startswith(u'#'): # File contains non-empty non-comment line - no_specification.append(os.path.relpath(filename, - root_dir)) - return + no_specification.append(rel_path) + break except UnicodeDecodeError: - decode_errors.append(filename) - - for test_dir in test_dirs: - base_dir = os.path.join(root_dir, test_dir) - for root, dirnames, filenames in os.walk(base_dir): - dirnames[:] = [d for d in dirnames if not - os.path.relpath(os.path.join(root, d), root_dir) - in ignored_dirs] - for filename in filenames: - if not filename.endswith('.py'): - continue - check_file(os.path.join(root, filename)) + decode_errors.append(rel_path) msgs = [] if no_specification: - msgs.append('The following files are missing an encoding ' - + 'specification: {}'.format(no_specification)) + msgs.append(u'The following files are missing an encoding ' + + u'specification: {}'.format(no_specification)) if decode_errors: - msgs.append('The following files are not valid UTF-8: {}'.format( + msgs.append(u'The following files are not valid UTF-8: {}'.format( decode_errors)) if msgs: - assert False, '\n\n'.join(msgs) + assert False, u'\n\n'.join(msgs) + + +def renumerate(it): + u''' + Reverse enumerate. + + Yields tuples ``(i, x)`` where ``x`` are the items of ``it`` in + reverse order and ``i`` is the corresponding (decreasing) index. + ``it`` must support ``len``. + ''' + return zip(xrange(len(it) - 1, -1, -1), reversed(it)) + + +def find_unprefixed_string_literals(filename): + u''' + Find unprefixed string literals in a Python source file. + + Returns a list of ``(line_number, column)`` tuples (both 1-based) of + positions where string literals without a ``u`` or ``b`` prefix + start. + + Note: Due to limitations in Python's ``ast`` module this does not + check the rear parts of auto-concatenated string literals + (``'foo' 'bar'``). + ''' + with io.open(filename, encoding=u'utf-8') as f: + lines = f.readlines() + # In some versions of Python, the ast module cannot deal with + # encoding declarations (http://bugs.python.org/issue22221). We + # therefore replace all comment lines at the beginning of the file + # with empty lines (to keep the line numbers correct). + for i, line in enumerate(lines): + line = line.strip() + if line.startswith(u'#'): + lines[i] = u'\n' + elif line: + break + root = ast.parse(u''.join(lines), filename.encode(FILESYSTEM_ENCODING)) + problems = [] + for node in ast.walk(root): + if isinstance(node, ast.Str): + lineno = node.lineno - 1 + col_offset = node.col_offset + if col_offset == -1: + # `lineno` and `col_offset` are broken for literals that span + # multiple lines: For these, `lineno` contains the line of the + # *closing* quotes, and `col_offset` is always -1, see + # https://bugs.python.org/issue16806. We therefore have to + # find the start of the literal manually, which is difficult + # since '''-literals can contain """ and vice versa. The + # following code assumes that no ''' or """ literal begins on + # the same line where a multi-line literal ends. + last_line = lines[lineno] + if last_line.rfind(u'"""') > last_line.rfind(u"'''"): + quotes = u'"""' + else: + quotes = u"'''" + for lineno, line in renumerate(lines[:lineno]): + try: + i = line.rindex(quotes) + if (i > 1) and (line[i - 2:i].lower() == u'ur'): + col_offset = i - 2 + elif (i > 0) and (line[i - 1].lower() in u'rbu'): + col_offset = i - 1 + else: + col_offset = 0 + break + except ValueError: + continue + leading = lines[lineno][col_offset - 1:col_offset + 1] + if leading[:-1] == u'[': # data['id'] is unambiguous, ignore these + continue + if leading[-1:] not in u'ub': # Don't allow capital U and B either + problems.append((lineno + 1, col_offset + 1)) + return sorted(problems) + + +# List of files white-listed for the string literal prefix test. Files on the +# list are expected to be fixed over time and removed from the list. DO NOT ADD +# NEW FILES TO THE LIST. +_STRING_LITERALS_WHITELIST = [ + u'bin/running_stats.py', + u'ckan/__init__.py', + u'ckan/authz.py', + u'ckan/ckan_nose_plugin.py', + u'ckan/config/environment.py', + u'ckan/config/install.py', + u'ckan/config/middleware/__init__.py', + u'ckan/config/middleware/common_middleware.py', + u'ckan/config/middleware/flask_app.py', + u'ckan/config/middleware/pylons_app.py', + u'ckan/config/routing.py', + u'ckan/controllers/admin.py', + u'ckan/controllers/api.py', + u'ckan/controllers/error.py', + u'ckan/controllers/feed.py', + u'ckan/controllers/group.py', + u'ckan/controllers/home.py', + u'ckan/controllers/organization.py', + u'ckan/controllers/package.py', + u'ckan/controllers/partyline.py', + u'ckan/controllers/revision.py', + u'ckan/controllers/storage.py', + u'ckan/controllers/tag.py', + u'ckan/controllers/template.py', + u'ckan/controllers/user.py', + u'ckan/controllers/util.py', + u'ckan/exceptions.py', + u'ckan/i18n/check_po_files.py', + u'ckan/lib/activity_streams.py', + u'ckan/lib/activity_streams_session_extension.py', + u'ckan/lib/alphabet_paginate.py', + u'ckan/lib/app_globals.py', + u'ckan/lib/auth_tkt.py', + u'ckan/lib/authenticator.py', + u'ckan/lib/base.py', + u'ckan/lib/captcha.py', + u'ckan/lib/celery_app.py', + u'ckan/lib/cli.py', + u'ckan/lib/config_tool.py', + u'ckan/lib/create_test_data.py', + u'ckan/lib/datapreview.py', + u'ckan/lib/dictization/__init__.py', + u'ckan/lib/dictization/model_dictize.py', + u'ckan/lib/dictization/model_save.py', + u'ckan/lib/email_notifications.py', + u'ckan/lib/extract.py', + u'ckan/lib/fanstatic_extensions.py', + u'ckan/lib/fanstatic_resources.py', + u'ckan/lib/formatters.py', + u'ckan/lib/hash.py', + u'ckan/lib/helpers.py', + u'ckan/lib/i18n.py', + u'ckan/lib/jinja_extensions.py', + u'ckan/lib/jsonp.py', + u'ckan/lib/mailer.py', + u'ckan/lib/maintain.py', + u'ckan/lib/munge.py', + u'ckan/lib/navl/__init__.py', + u'ckan/lib/navl/dictization_functions.py', + u'ckan/lib/navl/validators.py', + u'ckan/lib/plugins.py', + u'ckan/lib/render.py', + u'ckan/lib/search/__init__.py', + u'ckan/lib/search/common.py', + u'ckan/lib/search/index.py', + u'ckan/lib/search/query.py', + u'ckan/lib/search/sql.py', + u'ckan/lib/uploader.py', + u'ckan/logic/__init__.py', + u'ckan/logic/action/__init__.py', + u'ckan/logic/action/create.py', + u'ckan/logic/action/delete.py', + u'ckan/logic/action/get.py', + u'ckan/logic/action/patch.py', + u'ckan/logic/action/update.py', + u'ckan/logic/auth/__init__.py', + u'ckan/logic/auth/create.py', + u'ckan/logic/auth/delete.py', + u'ckan/logic/auth/get.py', + u'ckan/logic/auth/patch.py', + u'ckan/logic/auth/update.py', + u'ckan/logic/converters.py', + u'ckan/logic/schema.py', + u'ckan/logic/validators.py', + u'ckan/migration/manage.py', + u'ckan/migration/versions/001_add_existing_tables.py', + u'ckan/migration/versions/002_add_author_and_maintainer.py', + u'ckan/migration/versions/003_add_user_object.py', + u'ckan/migration/versions/004_add_group_object.py', + u'ckan/migration/versions/005_add_authorization_tables.py', + u'ckan/migration/versions/006_add_ratings.py', + u'ckan/migration/versions/007_add_system_roles.py', + u'ckan/migration/versions/008_update_vdm_ids.py', + u'ckan/migration/versions/009_add_creation_timestamps.py', + u'ckan/migration/versions/010_add_user_about.py', + u'ckan/migration/versions/011_add_package_search_vector.py', + u'ckan/migration/versions/012_add_resources.py', + u'ckan/migration/versions/013_add_hash.py', + u'ckan/migration/versions/014_hash_2.py', + u'ckan/migration/versions/015_remove_state_object.py', + u'ckan/migration/versions/016_uuids_everywhere.py', + u'ckan/migration/versions/017_add_pkg_relationships.py', + u'ckan/migration/versions/018_adjust_licenses.py', + u'ckan/migration/versions/019_pkg_relationships_state.py', + u'ckan/migration/versions/020_add_changeset.py', + u'ckan/migration/versions/022_add_group_extras.py', + u'ckan/migration/versions/023_add_harvesting.py', + u'ckan/migration/versions/024_add_harvested_document.py', + u'ckan/migration/versions/025_add_authorization_groups.py', + u'ckan/migration/versions/026_authorization_group_user_pk.py', + u'ckan/migration/versions/027_adjust_harvester.py', + u'ckan/migration/versions/028_drop_harvest_source_status.py', + u'ckan/migration/versions/029_version_groups.py', + u'ckan/migration/versions/030_additional_user_attributes.py', + u'ckan/migration/versions/031_move_openid_to_new_field.py', + u'ckan/migration/versions/032_add_extra_info_field_to_resources.py', + u'ckan/migration/versions/033_auth_group_user_id_add_conditional.py', + u'ckan/migration/versions/034_resource_group_table.py', + u'ckan/migration/versions/035_harvesting_doc_versioning.py', + u'ckan/migration/versions/036_lockdown_roles.py', + u'ckan/migration/versions/037_role_anon_editor.py', + u'ckan/migration/versions/038_delete_migration_tables.py', + u'ckan/migration/versions/039_add_expired_id_and_dates.py', + u'ckan/migration/versions/040_reset_key_on_user.py', + u'ckan/migration/versions/041_resource_new_fields.py', + u'ckan/migration/versions/042_user_revision_indexes.py', + u'ckan/migration/versions/043_drop_postgres_search.py', + u'ckan/migration/versions/044_add_task_status.py', + u'ckan/migration/versions/045_user_name_unique.py', + u'ckan/migration/versions/046_drop_changesets.py', + u'ckan/migration/versions/047_rename_package_group_member.py', + u'ckan/migration/versions/048_add_activity_streams_tables.py', + u'ckan/migration/versions/049_add_group_approval_status.py', + u'ckan/migration/versions/050_term_translation_table.py', + u'ckan/migration/versions/051_add_tag_vocabulary.py', + u'ckan/migration/versions/052_update_member_capacities.py', + u'ckan/migration/versions/053_add_group_logo.py', + u'ckan/migration/versions/054_add_resource_created_date.py', + u'ckan/migration/versions/055_update_user_and_activity_detail.py', + u'ckan/migration/versions/056_add_related_table.py', + u'ckan/migration/versions/057_tracking.py', + u'ckan/migration/versions/058_add_follower_tables.py', + u'ckan/migration/versions/059_add_related_count_and_flag.py', + u'ckan/migration/versions/060_add_system_info_table.py', + u'ckan/migration/versions/061_add_follower__group_table.py', + u'ckan/migration/versions/062_add_dashboard_table.py', + u'ckan/migration/versions/063_org_changes.py', + u'ckan/migration/versions/064_add_email_last_sent_column.py', + u'ckan/migration/versions/065_add_email_notifications_preference.py', + u'ckan/migration/versions/066_default_package_type.py', + u'ckan/migration/versions/067_turn_extras_to_strings.py', + u'ckan/migration/versions/068_add_package_extras_index.py', + u'ckan/migration/versions/069_resource_url_and_metadata_modified.py', + u'ckan/migration/versions/070_add_activity_and_resource_indexes.py', + u'ckan/migration/versions/071_add_state_column_to_user_table.py', + u'ckan/migration/versions/072_add_resource_view.py', + u'ckan/migration/versions/073_update_resource_view_resource_id_constraint.py', + u'ckan/migration/versions/074_remove_resource_groups.py', + u'ckan/migration/versions/075_rename_view_plugins.py', + u'ckan/migration/versions/076_rename_view_plugins_2.py', + u'ckan/migration/versions/077_add_revisions_to_system_info.py', + u'ckan/migration/versions/078_remove_old_authz_model.py', + u'ckan/migration/versions/079_resource_revision_index.py', + u'ckan/migration/versions/080_continuity_id_indexes.py', + u'ckan/migration/versions/081_set_datastore_active.py', + u'ckan/migration/versions/082_create_index_creator_user_id.py', + u'ckan/migration/versions/083_remove_related_items.py', + u'ckan/migration/versions/084_add_metadata_created.py', + u'ckan/model/__init__.py', + u'ckan/model/activity.py', + u'ckan/model/core.py', + u'ckan/model/dashboard.py', + u'ckan/model/domain_object.py', + u'ckan/model/extension.py', + u'ckan/model/follower.py', + u'ckan/model/group.py', + u'ckan/model/group_extra.py', + u'ckan/model/license.py', + u'ckan/model/meta.py', + u'ckan/model/misc.py', + u'ckan/model/modification.py', + u'ckan/model/package.py', + u'ckan/model/package_extra.py', + u'ckan/model/package_relationship.py', + u'ckan/model/rating.py', + u'ckan/model/resource.py', + u'ckan/model/resource_view.py', + u'ckan/model/system_info.py', + u'ckan/model/tag.py', + u'ckan/model/task_status.py', + u'ckan/model/term_translation.py', + u'ckan/model/tracking.py', + u'ckan/model/types.py', + u'ckan/model/user.py', + u'ckan/model/vocabulary.py', + u'ckan/pastertemplates/__init__.py', + u'ckan/plugins/core.py', + u'ckan/plugins/interfaces.py', + u'ckan/plugins/toolkit.py', + u'ckan/plugins/toolkit_sphinx_extension.py', + u'ckan/tests/config/test_environment.py', + u'ckan/tests/config/test_middleware.py', + u'ckan/tests/controllers/__init__.py', + u'ckan/tests/controllers/test_admin.py', + u'ckan/tests/controllers/test_api.py', + u'ckan/tests/controllers/test_feed.py', + u'ckan/tests/controllers/test_group.py', + u'ckan/tests/controllers/test_home.py', + u'ckan/tests/controllers/test_organization.py', + u'ckan/tests/controllers/test_package.py', + u'ckan/tests/controllers/test_tags.py', + u'ckan/tests/controllers/test_user.py', + u'ckan/tests/controllers/test_util.py', + u'ckan/tests/factories.py', + u'ckan/tests/helpers.py', + u'ckan/tests/i18n/test_check_po_files.py', + u'ckan/tests/legacy/__init__.py', + u'ckan/tests/legacy/ckantestplugins.py', + u'ckan/tests/legacy/functional/api/__init__.py', + u'ckan/tests/legacy/functional/api/base.py', + u'ckan/tests/legacy/functional/api/model/test_group.py', + u'ckan/tests/legacy/functional/api/model/test_licenses.py', + u'ckan/tests/legacy/functional/api/model/test_package.py', + u'ckan/tests/legacy/functional/api/model/test_ratings.py', + u'ckan/tests/legacy/functional/api/model/test_relationships.py', + u'ckan/tests/legacy/functional/api/model/test_revisions.py', + u'ckan/tests/legacy/functional/api/model/test_tag.py', + u'ckan/tests/legacy/functional/api/model/test_vocabulary.py', + u'ckan/tests/legacy/functional/api/test_activity.py', + u'ckan/tests/legacy/functional/api/test_api.py', + u'ckan/tests/legacy/functional/api/test_dashboard.py', + u'ckan/tests/legacy/functional/api/test_email_notifications.py', + u'ckan/tests/legacy/functional/api/test_follow.py', + u'ckan/tests/legacy/functional/api/test_misc.py', + u'ckan/tests/legacy/functional/api/test_package_search.py', + u'ckan/tests/legacy/functional/api/test_resource.py', + u'ckan/tests/legacy/functional/api/test_resource_search.py', + u'ckan/tests/legacy/functional/api/test_user.py', + u'ckan/tests/legacy/functional/api/test_util.py', + u'ckan/tests/legacy/functional/test_activity.py', + u'ckan/tests/legacy/functional/test_admin.py', + u'ckan/tests/legacy/functional/test_error.py', + u'ckan/tests/legacy/functional/test_group.py', + u'ckan/tests/legacy/functional/test_package.py', + u'ckan/tests/legacy/functional/test_pagination.py', + u'ckan/tests/legacy/functional/test_preview_interface.py', + u'ckan/tests/legacy/functional/test_revision.py', + u'ckan/tests/legacy/functional/test_tag.py', + u'ckan/tests/legacy/functional/test_tracking.py', + u'ckan/tests/legacy/functional/test_user.py', + u'ckan/tests/legacy/html_check.py', + u'ckan/tests/legacy/lib/__init__.py', + u'ckan/tests/legacy/lib/test_alphabet_pagination.py', + u'ckan/tests/legacy/lib/test_authenticator.py', + u'ckan/tests/legacy/lib/test_cli.py', + u'ckan/tests/legacy/lib/test_dictization.py', + u'ckan/tests/legacy/lib/test_dictization_schema.py', + u'ckan/tests/legacy/lib/test_email_notifications.py', + u'ckan/tests/legacy/lib/test_hash.py', + u'ckan/tests/legacy/lib/test_helpers.py', + u'ckan/tests/legacy/lib/test_i18n.py', + u'ckan/tests/legacy/lib/test_navl.py', + u'ckan/tests/legacy/lib/test_resource_search.py', + u'ckan/tests/legacy/lib/test_simple_search.py', + u'ckan/tests/legacy/lib/test_solr_package_search.py', + u'ckan/tests/legacy/lib/test_solr_package_search_synchronous_update.py', + u'ckan/tests/legacy/lib/test_solr_schema_version.py', + u'ckan/tests/legacy/lib/test_solr_search_index.py', + u'ckan/tests/legacy/lib/test_tag_search.py', + u'ckan/tests/legacy/logic/test_action.py', + u'ckan/tests/legacy/logic/test_auth.py', + u'ckan/tests/legacy/logic/test_init.py', + u'ckan/tests/legacy/logic/test_member.py', + u'ckan/tests/legacy/logic/test_tag.py', + u'ckan/tests/legacy/logic/test_tag_vocab.py', + u'ckan/tests/legacy/logic/test_validators.py', + u'ckan/tests/legacy/misc/test_format_text.py', + u'ckan/tests/legacy/misc/test_mock_mail_server.py', + u'ckan/tests/legacy/misc/test_sync.py', + u'ckan/tests/legacy/mock_mail_server.py', + u'ckan/tests/legacy/mock_plugin.py', + u'ckan/tests/legacy/models/test_activity.py', + u'ckan/tests/legacy/models/test_extras.py', + u'ckan/tests/legacy/models/test_follower.py', + u'ckan/tests/legacy/models/test_group.py', + u'ckan/tests/legacy/models/test_misc.py', + u'ckan/tests/legacy/models/test_package.py', + u'ckan/tests/legacy/models/test_package_relationships.py', + u'ckan/tests/legacy/models/test_purge_revision.py', + u'ckan/tests/legacy/models/test_resource.py', + u'ckan/tests/legacy/models/test_revision.py', + u'ckan/tests/legacy/models/test_user.py', + u'ckan/tests/legacy/pylons_controller.py', + u'ckan/tests/legacy/schema/test_schema.py', + u'ckan/tests/legacy/test_coding_standards.py', + u'ckan/tests/legacy/test_plugins.py', + u'ckan/tests/legacy/test_versions.py', + u'ckan/tests/lib/__init__.py', + u'ckan/tests/lib/dictization/test_model_dictize.py', + u'ckan/tests/lib/navl/test_dictization_functions.py', + u'ckan/tests/lib/navl/test_validators.py', + u'ckan/tests/lib/search/test_index.py', + u'ckan/tests/lib/test_app_globals.py', + u'ckan/tests/lib/test_auth_tkt.py', + u'ckan/tests/lib/test_base.py', + u'ckan/tests/lib/test_cli.py', + u'ckan/tests/lib/test_config_tool.py', + u'ckan/tests/lib/test_datapreview.py', + u'ckan/tests/lib/test_helpers.py', + u'ckan/tests/lib/test_mailer.py', + u'ckan/tests/lib/test_munge.py', + u'ckan/tests/lib/test_navl.py', + u'ckan/tests/logic/action/__init__.py', + u'ckan/tests/logic/action/test_create.py', + u'ckan/tests/logic/action/test_delete.py', + u'ckan/tests/logic/action/test_get.py', + u'ckan/tests/logic/action/test_patch.py', + u'ckan/tests/logic/action/test_update.py', + u'ckan/tests/logic/auth/__init__.py', + u'ckan/tests/logic/auth/test_create.py', + u'ckan/tests/logic/auth/test_delete.py', + u'ckan/tests/logic/auth/test_get.py', + u'ckan/tests/logic/auth/test_init.py', + u'ckan/tests/logic/auth/test_update.py', + u'ckan/tests/logic/test_conversion.py', + u'ckan/tests/logic/test_converters.py', + u'ckan/tests/logic/test_schema.py', + u'ckan/tests/logic/test_validators.py', + u'ckan/tests/migration/__init__.py', + u'ckan/tests/model/__init__.py', + u'ckan/tests/model/test_license.py', + u'ckan/tests/model/test_resource.py', + u'ckan/tests/model/test_resource_view.py', + u'ckan/tests/model/test_system_info.py', + u'ckan/tests/model/test_user.py', + u'ckan/tests/plugins/__init__.py', + u'ckan/tests/plugins/test_toolkit.py', + u'ckan/tests/test_authz.py', + u'ckan/tests/test_factories.py', + u'ckan/websetup.py', + u'ckanext/datapusher/cli.py', + u'ckanext/datapusher/helpers.py', + u'ckanext/datapusher/interfaces.py', + u'ckanext/datapusher/logic/action.py', + u'ckanext/datapusher/logic/schema.py', + u'ckanext/datapusher/plugin.py', + u'ckanext/datapusher/tests/test.py', + u'ckanext/datapusher/tests/test_action.py', + u'ckanext/datapusher/tests/test_default_views.py', + u'ckanext/datapusher/tests/test_interfaces.py', + u'ckanext/datastore/commands.py', + u'ckanext/datastore/controller.py', + u'ckanext/datastore/db.py', + u'ckanext/datastore/helpers.py', + u'ckanext/datastore/interfaces.py', + u'ckanext/datastore/logic/action.py', + u'ckanext/datastore/logic/auth.py', + u'ckanext/datastore/logic/schema.py', + u'ckanext/datastore/plugin.py', + u'ckanext/datastore/tests/helpers.py', + u'ckanext/datastore/tests/sample_datastore_plugin.py', + u'ckanext/datastore/tests/test_configure.py', + u'ckanext/datastore/tests/test_create.py', + u'ckanext/datastore/tests/test_db.py', + u'ckanext/datastore/tests/test_delete.py', + u'ckanext/datastore/tests/test_disable.py', + u'ckanext/datastore/tests/test_dump.py', + u'ckanext/datastore/tests/test_helpers.py', + u'ckanext/datastore/tests/test_info.py', + u'ckanext/datastore/tests/test_interface.py', + u'ckanext/datastore/tests/test_plugin.py', + u'ckanext/datastore/tests/test_search.py', + u'ckanext/datastore/tests/test_unit.py', + u'ckanext/datastore/tests/test_upsert.py', + u'ckanext/example_iauthfunctions/plugin_v2.py', + u'ckanext/example_iauthfunctions/plugin_v3.py', + u'ckanext/example_iauthfunctions/plugin_v4.py', + u'ckanext/example_iauthfunctions/plugin_v5_custom_config_setting.py', + u'ckanext/example_iauthfunctions/plugin_v6_parent_auth_functions.py', + u'ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py', + u'ckanext/example_iconfigurer/controller.py', + u'ckanext/example_iconfigurer/plugin.py', + u'ckanext/example_iconfigurer/plugin_v1.py', + u'ckanext/example_iconfigurer/plugin_v2.py', + u'ckanext/example_iconfigurer/tests/test_example_iconfigurer.py', + u'ckanext/example_iconfigurer/tests/test_iconfigurer_toolkit.py', + u'ckanext/example_iconfigurer/tests/test_iconfigurer_update_config.py', + u'ckanext/example_idatasetform/plugin.py', + u'ckanext/example_idatasetform/plugin_v1.py', + u'ckanext/example_idatasetform/plugin_v2.py', + u'ckanext/example_idatasetform/plugin_v3.py', + u'ckanext/example_idatasetform/plugin_v4.py', + u'ckanext/example_idatasetform/tests/test_controllers.py', + u'ckanext/example_idatasetform/tests/test_example_idatasetform.py', + u'ckanext/example_igroupform/plugin.py', + u'ckanext/example_igroupform/tests/test_controllers.py', + u'ckanext/example_iresourcecontroller/plugin.py', + u'ckanext/example_iresourcecontroller/tests/test_example_iresourcecontroller.py', + u'ckanext/example_itemplatehelpers/plugin.py', + u'ckanext/example_itranslation/plugin.py', + u'ckanext/example_itranslation/plugin_v1.py', + u'ckanext/example_itranslation/tests/test_plugin.py', + u'ckanext/example_iuploader/plugin.py', + u'ckanext/example_iuploader/test/test_plugin.py', + u'ckanext/example_ivalidators/plugin.py', + u'ckanext/example_ivalidators/tests/test_ivalidators.py', + u'ckanext/example_theme/custom_config_setting/plugin.py', + u'ckanext/example_theme/custom_emails/plugin.py', + u'ckanext/example_theme/custom_emails/tests.py', + u'ckanext/example_theme/v01_empty_extension/plugin.py', + u'ckanext/example_theme/v02_empty_template/plugin.py', + u'ckanext/example_theme/v03_jinja/plugin.py', + u'ckanext/example_theme/v04_ckan_extends/plugin.py', + u'ckanext/example_theme/v05_block/plugin.py', + u'ckanext/example_theme/v06_super/plugin.py', + u'ckanext/example_theme/v07_helper_function/plugin.py', + u'ckanext/example_theme/v08_custom_helper_function/plugin.py', + u'ckanext/example_theme/v09_snippet/plugin.py', + u'ckanext/example_theme/v10_custom_snippet/plugin.py', + u'ckanext/example_theme/v11_HTML_and_CSS/plugin.py', + u'ckanext/example_theme/v12_extra_public_dir/plugin.py', + u'ckanext/example_theme/v13_custom_css/plugin.py', + u'ckanext/example_theme/v14_more_custom_css/plugin.py', + u'ckanext/example_theme/v15_fanstatic/plugin.py', + u'ckanext/example_theme/v16_initialize_a_javascript_module/plugin.py', + u'ckanext/example_theme/v17_popover/plugin.py', + u'ckanext/example_theme/v18_snippet_api/plugin.py', + u'ckanext/example_theme/v19_01_error/plugin.py', + u'ckanext/example_theme/v19_02_error_handling/plugin.py', + u'ckanext/example_theme/v20_pubsub/plugin.py', + u'ckanext/example_theme/v21_custom_jquery_plugin/plugin.py', + u'ckanext/imageview/plugin.py', + u'ckanext/imageview/tests/test_view.py', + u'ckanext/multilingual/plugin.py', + u'ckanext/multilingual/tests/test_multilingual_plugin.py', + u'ckanext/reclineview/plugin.py', + u'ckanext/reclineview/tests/test_view.py', + u'ckanext/resourceproxy/controller.py', + u'ckanext/resourceproxy/plugin.py', + u'ckanext/resourceproxy/tests/test_proxy.py', + u'ckanext/stats/__init__.py', + u'ckanext/stats/controller.py', + u'ckanext/stats/plugin.py', + u'ckanext/stats/stats.py', + u'ckanext/stats/tests/__init__.py', + u'ckanext/stats/tests/test_stats_lib.py', + u'ckanext/stats/tests/test_stats_plugin.py', + u'ckanext/test_tag_vocab_plugin.py', + u'ckanext/textview/plugin.py', + u'ckanext/textview/tests/test_view.py', + u'ckanext/webpageview/plugin.py', + u'ckanext/webpageview/tests/test_view.py', + u'doc/conf.py', + u'profile_tests.py', + u'setup.py', +] + + +def test_string_literals_are_prefixed(): + u''' + Test that string literals are prefixed by ``u``, ``b`` or ``ur``. + + See http://docs.ckan.org/en/latest/contributing/unicode.html. + ''' + errors = [] + for abs_path, rel_path in walk_python_files(): + if rel_path in _STRING_LITERALS_WHITELIST: + continue + problems = find_unprefixed_string_literals(abs_path) + if problems: + errors.append((rel_path, problems)) + if errors: + lines = [u'Unprefixed string literals:'] + for filename, problems in errors: + lines.append(u' ' + filename) + for line_no, col_no in problems: + lines.append(u' line {}, column {}'.format(line_no, col_no)) + raise AssertionError(u'\n'.join(lines)) diff --git a/ckan/tests/test_common.py b/ckan/tests/test_common.py new file mode 100644 index 00000000000..cf3dda812a2 --- /dev/null +++ b/ckan/tests/test_common.py @@ -0,0 +1,214 @@ +# encoding: utf-8 + +import flask +import pylons + +from nose.tools import eq_, assert_not_equal as neq_ + +from ckan.tests import helpers +from ckan.common import CKANConfig, config as ckan_config + + +class TestConfigObject(object): + + def test_del_works(self): + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + + del my_conf[u'test_key_1'] + + assert u'test_key_1' not in my_conf + + def test_get_item_works(self): + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + + eq_(my_conf.get(u'test_key_1'), u'Test value 1') + + def test_repr_works(self): + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + + eq_(repr(my_conf), u"{u'test_key_1': u'Test value 1'}") + + def test_len_works(self): + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + my_conf[u'test_key_2'] = u'Test value 2' + + eq_(len(my_conf), 2) + + def test_keys_works(self): + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + my_conf[u'test_key_2'] = u'Test value 2' + + eq_(sorted(my_conf.keys()), [u'test_key_1', u'test_key_2']) + + def test_clear_works(self): + + # Keep a copy of the original Pylons config + _original_pylons_config = pylons.config.copy() + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + my_conf[u'test_key_2'] = u'Test value 2' + + eq_(len(my_conf.keys()), 2) + + my_conf.clear() + + eq_(len(my_conf.keys()), 0) + + # Restore Pylons config + pylons.config.update(_original_pylons_config) + + def test_for_in_works(self): + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + my_conf[u'test_key_2'] = u'Test value 2' + + cnt = 0 + for key in my_conf: + cnt += 1 + assert key.startswith(u'test_key_') + + eq_(cnt, 2) + + def test_iteritems_works(self): + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + my_conf[u'test_key_2'] = u'Test value 2' + + cnt = 0 + for key, value in my_conf.iteritems(): + cnt += 1 + assert key.startswith(u'test_key_') + assert value.startswith(u'Test value') + + eq_(cnt, 2) + + def test_not_true_if_empty(self): + + my_conf = CKANConfig() + + assert not my_conf + + def test_true_if_not_empty(self): + + my_conf = CKANConfig() + + my_conf[u'test_key_1'] = u'Test value 1' + + assert my_conf + + +class TestCommonConfig(object): + + def setup(self): + self._original_config = ckan_config.copy() + + def teardown(self): + ckan_config.clear() + ckan_config.update(self._original_config) + + @classmethod + def teardown_class(cls): + helpers.reset_db() + + def test_setting_a_key_sets_it_on_pylons_config(self): + + ckan_config[u'ckan.site_title'] = u'Example title' + eq_(pylons.config[u'ckan.site_title'], u'Example title') + + def test_setting_a_key_sets_it_on_flask_config_if_app_context(self): + + app = helpers._get_test_app() + with app.flask_app.app_context(): + + ckan_config[u'ckan.site_title'] = u'Example title' + eq_(flask.current_app.config[u'ckan.site_title'], u'Example title') + + def test_setting_a_key_does_not_set_it_on_flask_config_if_outside_app_context(self): + + ckan_config[u'ckan.site_title'] = u'Example title' + + app = helpers._get_test_app() + with app.flask_app.app_context(): + + neq_(flask.current_app.config[u'ckan.site_title'], u'Example title') + + def test_deleting_a_key_deletes_it_on_pylons_config(self): + + ckan_config[u'ckan.site_title'] = u'Example title' + del ckan_config[u'ckan.site_title'] + + assert u'ckan.site_title' not in ckan_config + + def test_deleting_a_key_delets_it_on_flask_config(self): + + app = helpers._get_test_app() + with app.flask_app.app_context(): + + ckan_config[u'ckan.site_title'] = u'Example title' + del ckan_config[u'ckan.site_title'] + + assert u'ckan.site_title' not in flask.current_app.config + + def test_update_works_on_pylons_config(self): + + ckan_config[u'ckan.site_title'] = u'Example title' + + ckan_config.update({ + u'ckan.site_title': u'Example title 2', + u'ckan.new_key': u'test'}) + + eq_(pylons.config[u'ckan.site_title'], u'Example title 2') + eq_(pylons.config[u'ckan.new_key'], u'test') + + def test_update_works_on_flask_config(self): + + app = helpers._get_test_app() + with app.flask_app.app_context(): + + ckan_config[u'ckan.site_title'] = u'Example title' + + ckan_config.update({ + u'ckan.site_title': u'Example title 2', + u'ckan.new_key': u'test'}) + + eq_(flask.current_app.config[u'ckan.site_title'], u'Example title 2') + eq_(flask.current_app.config[u'ckan.new_key'], u'test') + + def test_config_option_update_action_works_on_pylons(self): + params = { + u'ckan.site_title': u'Example title action', + } + + helpers.call_action(u'config_option_update', {}, **params) + + eq_(pylons.config[u'ckan.site_title'], u'Example title action') + + def test_config_option_update_action_works_on_flask(self): + app = helpers._get_test_app() + with app.flask_app.app_context(): + + params = { + u'ckan.site_title': u'Example title action', + } + + helpers.call_action(u'config_option_update', {}, **params) + + eq_(pylons.config[u'ckan.site_title'], u'Example title action') diff --git a/ckanext/datapusher/cli.py b/ckanext/datapusher/cli.py index 4edb8939e72..fd4adf27003 100644 --- a/ckanext/datapusher/cli.py +++ b/ckanext/datapusher/cli.py @@ -16,6 +16,9 @@ class DatapusherCommand(cli.CkanCommand): ignoring if their files haven't changed. submit - Submits all resources from the package identified by pkgname (either the short name or ID). + submit_all - Submit every package to the datastore. + This is useful if you're setting up datastore + for a ckan that already has datasets. ''' summary = __doc__.split('\n')[0] @@ -26,7 +29,12 @@ def command(self): self._confirm_or_abort() self._load_config() - self._submit_all() + self._resubmit_all() + elif self.args and self.args[0] == 'submit_all': + self._confirm_or_abort() + + self._load_config() + self._submit_all_packages() elif self.args and self.args[0] == 'submit': self._confirm_or_abort() @@ -51,10 +59,19 @@ def _confirm_or_abort(self): print "Aborting..." sys.exit(0) - def _submit_all(self): - resources_ids = datastore_db.get_all_resources_ids_in_datastore() + def _resubmit_all(self): + resource_ids = datastore_db.get_all_resources_ids_in_datastore() self._submit(resource_ids) + def _submit_all_packages(self): + # submit every package + # for each package in the package list, + # submit each resource w/ _submit_package + import ckan.model as model + package_list = p.toolkit.get_action('package_list') + for p_id in package_list({'model': model, 'ignore_auth': True}, {}): + self._submit_package(p_id) + def _submit_package(self, pkg_id): import ckan.model as model diff --git a/ckanext/datapusher/logic/action.py b/ckanext/datapusher/logic/action.py index 44089dd9656..c246cd775e5 100644 --- a/ckanext/datapusher/logic/action.py +++ b/ckanext/datapusher/logic/action.py @@ -7,12 +7,12 @@ from dateutil.parser import parse as parse_date -import pylons import requests import ckan.lib.navl.dictization_functions import ckan.logic as logic import ckan.plugins as p +from ckan.common import config import ckanext.datapusher.logic.schema as dpschema import ckanext.datapusher.interfaces as interfaces @@ -57,9 +57,9 @@ def datapusher_submit(context, data_dict): except logic.NotFound: return False - datapusher_url = pylons.config.get('ckan.datapusher.url') + datapusher_url = config.get('ckan.datapusher.url') - site_url = pylons.config['ckan.site_url'] + site_url = config['ckan.site_url'] callback_url = site_url.rstrip('/') + '/api/3/action/datapusher_hook' user = p.toolkit.get_action('user_show')(context, {'id': context['user']}) @@ -252,7 +252,7 @@ def datapusher_status(context, data_dict): 'key': 'datapusher' }) - datapusher_url = pylons.config.get('ckan.datapusher.url') + datapusher_url = config.get('ckan.datapusher.url') if not datapusher_url: raise p.toolkit.ValidationError( {'configuration': ['ckan.datapusher.url not in config file']}) diff --git a/ckanext/datapusher/plugin.py b/ckanext/datapusher/plugin.py index ea9a5fc8bb4..a7db0f66aa7 100644 --- a/ckanext/datapusher/plugin.py +++ b/ckanext/datapusher/plugin.py @@ -41,11 +41,11 @@ def resource_data(self, id, resource_id): except logic.ValidationError: pass - base.redirect(core_helpers.url_for( + core_helpers.redirect_to( controller='ckanext.datapusher.plugin:ResourceDataController', action='resource_data', id=id, - resource_id=resource_id) + resource_id=resource_id ) try: diff --git a/ckanext/datapusher/tests/test.py b/ckanext/datapusher/tests/test.py index 2d44c0b14e1..870361b514f 100644 --- a/ckanext/datapusher/tests/test.py +++ b/ckanext/datapusher/tests/test.py @@ -4,11 +4,8 @@ import httpretty import httpretty.core import nose -import sys import datetime -import pylons -from pylons import config import sqlalchemy.orm as orm import paste.fixture @@ -18,6 +15,8 @@ import ckan.tests.legacy as tests import ckan.config.middleware as middleware +from ckan.common import config + import ckanext.datastore.db as db from ckanext.datastore.tests.helpers import rebuild_all_dbs, set_url_type @@ -56,19 +55,12 @@ def __getattr__(self, attr): httpretty.core.fakesock.socket = HTTPrettyFix -# avoid hanging tests https://github.com/gabrielfalcao/HTTPretty/issues/34 -if sys.version_info < (2, 7, 0): - import socket - socket.setdefaulttimeout(1) - - class TestDatastoreCreate(tests.WsgiAppCase): sysadmin_user = None normal_user = None @classmethod def setup_class(cls): - wsgiapp = middleware.make_app(config['global_conf'], **config) cls.app = paste.fixture.TestApp(wsgiapp) if not tests.is_datastore_supported(): @@ -79,25 +71,16 @@ def setup_class(cls): cls.sysadmin_user = model.User.get('testsysadmin') cls.normal_user = model.User.get('annafan') engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) set_url_type( model.Package.get('annakarenina').resources, cls.sysadmin_user) - # Httpretty crashes with Solr on Python 2.6, - # skip the tests - if (sys.version_info[0] == 2 and sys.version_info[1] == 6): - raise nose.SkipTest() - @classmethod def teardown_class(cls): rebuild_all_dbs(cls.Session) p.unload('datastore') p.unload('datapusher') - # Reenable Solr indexing - if (sys.version_info[0] == 2 and sys.version_info[1] == 6 - and not p.plugin_loaded('synchronous_search')): - p.load('synchronous_search') def test_create_ckan_resource_in_package(self): package = model.Package.get('annakarenina') @@ -119,7 +102,7 @@ def test_create_ckan_resource_in_package(self): @httpretty.activate def test_providing_res_with_url_calls_datapusher_correctly(self): - pylons.config['datapusher.url'] = 'http://datapusher.ckan.org' + config['datapusher.url'] = 'http://datapusher.ckan.org' httpretty.HTTPretty.register_uri( httpretty.HTTPretty.POST, 'http://datapusher.ckan.org/job', @@ -145,7 +128,7 @@ def test_providing_res_with_url_calls_datapusher_correctly(self): @httpretty.activate def test_pass_the_received_ignore_hash_param_to_the_datapusher(self): - pylons.config['datapusher.url'] = 'http://datapusher.ckan.org' + config['datapusher.url'] = 'http://datapusher.ckan.org' httpretty.HTTPretty.register_uri( httpretty.HTTPretty.POST, 'http://datapusher.ckan.org/job', diff --git a/ckanext/datapusher/tests/test_interfaces.py b/ckanext/datapusher/tests/test_interfaces.py index d21bf3ab2b7..49b8efd7fad 100644 --- a/ckanext/datapusher/tests/test_interfaces.py +++ b/ckanext/datapusher/tests/test_interfaces.py @@ -7,7 +7,7 @@ import datetime from nose.tools import raises -from pylons import config +from ckan.common import config import sqlalchemy.orm as orm import paste.fixture diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index 4e6b47956db..46d4cbbac9a 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -11,7 +11,6 @@ import copy import hashlib -import pylons import distutils.version import sqlalchemy from sqlalchemy.exc import (ProgrammingError, IntegrityError, @@ -22,7 +21,7 @@ import ckan.plugins.toolkit as toolkit import ckanext.datastore.interfaces as interfaces import ckanext.datastore.helpers as datastore_helpers -from ckan.common import OrderedDict +from ckan.common import OrderedDict, config log = logging.getLogger(__name__) @@ -104,9 +103,8 @@ def _get_engine(data_dict): engine = _engines.get(connection_url) if not engine: - import pylons extras = {'url': connection_url} - engine = sqlalchemy.engine_from_config(pylons.config, + engine = sqlalchemy.engine_from_config(config, 'ckan.datastore.sqlalchemy.', **extras) _engines[connection_url] = engine @@ -129,7 +127,7 @@ def _cache_types(context): native_json)) data_dict = { - 'connection_url': pylons.config['ckan.datastore.write_url']} + 'connection_url': config['ckan.datastore.write_url']} engine = _get_engine(data_dict) with engine.begin() as connection: connection.execute( @@ -471,7 +469,7 @@ def _build_fts_indexes(connection, data_dict, sql_index_str_method, fields): fts_indexes = [] resource_id = data_dict['resource_id'] # FIXME: This is repeated on the plugin.py, we should keep it DRY - default_fts_lang = pylons.config.get('ckan.datastore.default_fts_lang') + default_fts_lang = config.get('ckan.datastore.default_fts_lang') if default_fts_lang is None: default_fts_lang = u'english' fts_lang = data_dict.get('lang', default_fts_lang) @@ -506,7 +504,7 @@ def _generate_index_name(resource_id, field): def _get_fts_index_method(): - method = pylons.config.get('ckan.datastore.default_fts_index_method') + method = config.get('ckan.datastore.default_fts_index_method') return method or 'gist' @@ -1327,8 +1325,8 @@ def make_public(context, data_dict): def get_all_resources_ids_in_datastore(): - read_url = pylons.config.get('ckan.datastore.read_url') - write_url = pylons.config.get('ckan.datastore.write_url') + read_url = config.get('ckan.datastore.read_url') + write_url = config.get('ckan.datastore.write_url') data_dict = { 'connection_url': read_url or write_url } diff --git a/ckanext/datastore/logic/action.py b/ckanext/datastore/logic/action.py index 9869d14baaa..77379c96a9a 100644 --- a/ckanext/datastore/logic/action.py +++ b/ckanext/datastore/logic/action.py @@ -2,13 +2,12 @@ import logging -import pylons import sqlalchemy -import ckan.lib.base as base import ckan.lib.navl.dictization_functions import ckan.logic as logic import ckan.plugins as p +from ckan.common import config import ckanext.datastore.db as db import ckanext.datastore.logic.schema as dsschema import ckanext.datastore.helpers as datastore_helpers @@ -122,7 +121,7 @@ def datastore_create(context, data_dict): resource_id = data_dict['resource_id'] _check_read_only(context, resource_id) - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] # validate aliases aliases = datastore_helpers.get_list(data_dict.get('aliases', [])) @@ -135,7 +134,7 @@ def datastore_create(context, data_dict): # create a private datastore resource, if necessary model = _get_or_bust(context, 'model') resource = model.Resource.get(data_dict['resource_id']) - legacy_mode = 'ckan.datastore.read_url' not in pylons.config + legacy_mode = 'ckan.datastore.read_url' not in config if not legacy_mode and resource.package.private: data_dict['private'] = True @@ -209,7 +208,7 @@ def datastore_upsert(context, data_dict): resource_id = data_dict['resource_id'] _check_read_only(context, resource_id) - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] res_id = data_dict['resource_id'] resources_sql = sqlalchemy.text(u'''SELECT 1 FROM "_table_metadata" @@ -251,7 +250,7 @@ def _type_lookup(t): resource_id = _get_or_bust(data_dict, 'id') resource = p.toolkit.get_action('resource_show')(context, {'id':resource_id}) - data_dict['connection_url'] = pylons.config['ckan.datastore.read_url'] + data_dict['connection_url'] = config['ckan.datastore.read_url'] resources_sql = sqlalchemy.text(u'''SELECT 1 FROM "_table_metadata" WHERE name = :id AND alias_of IS NULL''') @@ -336,7 +335,7 @@ def datastore_delete(context, data_dict): resource_id = data_dict['resource_id'] _check_read_only(context, resource_id) - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] res_id = data_dict['resource_id'] resources_sql = sqlalchemy.text(u'''SELECT 1 FROM "_table_metadata" @@ -435,7 +434,7 @@ def datastore_search(context, data_dict): raise p.toolkit.ValidationError(errors) res_id = data_dict['resource_id'] - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] resources_sql = sqlalchemy.text(u'''SELECT alias_of FROM "_table_metadata" WHERE name = :id''') @@ -499,7 +498,7 @@ def datastore_search_sql(context, data_dict): p.toolkit.check_access('datastore_search_sql', context, data_dict) - data_dict['connection_url'] = pylons.config['ckan.datastore.read_url'] + data_dict['connection_url'] = config['ckan.datastore.read_url'] result = db.search_sql(context, data_dict) result.pop('id', None) @@ -522,7 +521,7 @@ def datastore_make_private(context, data_dict): data_dict['resource_id'] = data_dict['id'] res_id = _get_or_bust(data_dict, 'resource_id') - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] if not _resource_exists(context, data_dict): raise p.toolkit.ObjectNotFound(p.toolkit._( @@ -548,7 +547,7 @@ def datastore_make_public(context, data_dict): data_dict['resource_id'] = data_dict['id'] res_id = _get_or_bust(data_dict, 'resource_id') - data_dict['connection_url'] = pylons.config['ckan.datastore.write_url'] + data_dict['connection_url'] = config['ckan.datastore.write_url'] if not _resource_exists(context, data_dict): raise p.toolkit.ObjectNotFound(p.toolkit._( diff --git a/ckanext/datastore/plugin.py b/ckanext/datastore/plugin.py index 5847b4302af..7d7fe15e16b 100644 --- a/ckanext/datastore/plugin.py +++ b/ckanext/datastore/plugin.py @@ -4,12 +4,12 @@ import logging import re -import pylons import sqlalchemy.engine.url as sa_url import ckan.plugins as p import ckan.logic as logic import ckan.model as model +from ckan.common import config import ckanext.datastore.logic.action as action import ckanext.datastore.logic.auth as auth import ckanext.datastore.db as db @@ -494,7 +494,7 @@ def _textsearch_query(self, data_dict): return statements_str, rank_columns_str def _fts_lang(self, lang=None): - default_fts_lang = pylons.config.get('ckan.datastore.default_fts_lang') + default_fts_lang = config.get('ckan.datastore.default_fts_lang') if default_fts_lang is None: default_fts_lang = u'english' return lang or default_fts_lang diff --git a/ckanext/datastore/tests/test_create.py b/ckanext/datastore/tests/test_create.py index a36340ec82d..086a3c0b739 100644 --- a/ckanext/datastore/tests/test_create.py +++ b/ckanext/datastore/tests/test_create.py @@ -5,11 +5,10 @@ import sys from nose.tools import assert_equal, raises -import pylons -from pylons import config import sqlalchemy.orm as orm import paste.fixture +from ckan.common import config import ckan.plugins as p import ckan.lib.create_test_data as ctd import ckan.model as model @@ -22,12 +21,6 @@ from ckanext.datastore.tests.helpers import rebuild_all_dbs, set_url_type -# avoid hanging tests https://github.com/gabrielfalcao/HTTPretty/issues/34 -if sys.version_info < (2, 7, 0): - import socket - socket.setdefaulttimeout(1) - - class TestDatastoreCreateNewTests(object): @classmethod def setup_class(cls): @@ -187,7 +180,7 @@ def _get_index_names(self, resource_id): def _execute_sql(self, sql, *args): engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) session = orm.scoped_session(orm.sessionmaker(bind=engine)) return session.connection().execute(sql, *args) @@ -249,7 +242,7 @@ def setup_class(cls): cls.sysadmin_user = model.User.get('testsysadmin') cls.normal_user = model.User.get('annafan') engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) set_url_type( model.Package.get('annakarenina').resources, cls.sysadmin_user) diff --git a/ckanext/datastore/tests/test_delete.py b/ckanext/datastore/tests/test_delete.py index f0af7b861e9..53f9d487f06 100644 --- a/ckanext/datastore/tests/test_delete.py +++ b/ckanext/datastore/tests/test_delete.py @@ -3,7 +3,6 @@ import json import nose -import pylons import sqlalchemy import sqlalchemy.orm as orm @@ -11,6 +10,7 @@ import ckan.lib.create_test_data as ctd import ckan.model as model import ckan.tests.legacy as tests +from ckan.common import config import ckanext.datastore.db as db from ckanext.datastore.tests.helpers import rebuild_all_dbs, set_url_type @@ -43,7 +43,7 @@ def setup_class(cls): } engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) set_url_type( model.Package.get('annakarenina').resources, cls.sysadmin_user) diff --git a/ckanext/datastore/tests/test_disable.py b/ckanext/datastore/tests/test_disable.py index dbe925d5971..ad6912fe2cc 100644 --- a/ckanext/datastore/tests/test_disable.py +++ b/ckanext/datastore/tests/test_disable.py @@ -2,7 +2,7 @@ import nose -import pylons.config as config +from ckan.common import config import ckan.plugins as p import nose.tools as t diff --git a/ckanext/datastore/tests/test_dump.py b/ckanext/datastore/tests/test_dump.py index 15e4b447238..b966bedf7fa 100644 --- a/ckanext/datastore/tests/test_dump.py +++ b/ckanext/datastore/tests/test_dump.py @@ -4,7 +4,7 @@ import nose from nose.tools import assert_equals -from pylons import config +from ckan.common import config import sqlalchemy.orm as orm import paste.fixture diff --git a/ckanext/datastore/tests/test_helpers.py b/ckanext/datastore/tests/test_helpers.py index 6a3e121bdad..9cc05c3dded 100644 --- a/ckanext/datastore/tests/test_helpers.py +++ b/ckanext/datastore/tests/test_helpers.py @@ -1,9 +1,9 @@ # encoding: utf-8 -import pylons import sqlalchemy.orm as orm import nose +from ckan.common import config import ckanext.datastore.helpers as datastore_helpers import ckanext.datastore.tests.helpers as datastore_test_helpers import ckanext.datastore.db as db @@ -67,11 +67,11 @@ class TestGetTables(object): @classmethod def setup_class(cls): - if not pylons.config.get('ckan.datastore.read_url'): + if not config.get('ckan.datastore.read_url'): raise nose.SkipTest('Datastore runs on legacy mode, skipping...') engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']} + {'connection_url': config['ckan.datastore.write_url']} ) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) diff --git a/ckanext/datastore/tests/test_search.py b/ckanext/datastore/tests/test_search.py index 1ef119c320c..c472b52bdc3 100644 --- a/ckanext/datastore/tests/test_search.py +++ b/ckanext/datastore/tests/test_search.py @@ -4,7 +4,6 @@ import nose import pprint -import pylons import sqlalchemy.orm as orm import ckan.plugins as p @@ -12,6 +11,7 @@ import ckan.model as model import ckan.tests.legacy as tests +from ckan.common import config import ckanext.datastore.db as db from ckanext.datastore.tests.helpers import extract, rebuild_all_dbs @@ -166,7 +166,7 @@ def setup_class(cls): u'rating with %': u'99%'}] engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']} + {'connection_url': config['ckan.datastore.write_url']} ) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) @@ -839,7 +839,7 @@ def setup_class(cls): cls.expected_join_results = [{u'first': 1, u'second': 1}, {u'first': 1, u'second': 2}] engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) @classmethod @@ -942,7 +942,7 @@ def test_read_private(self): 'model': model} data_dict = { 'resource_id': self.data['resource_id'], - 'connection_url': pylons.config['ckan.datastore.write_url']} + 'connection_url': config['ckan.datastore.write_url']} p.toolkit.get_action('datastore_make_private')(context, data_dict) query = 'SELECT * FROM "{0}"'.format(self.data['resource_id']) data = {'sql': query} diff --git a/ckanext/datastore/tests/test_unit.py b/ckanext/datastore/tests/test_unit.py index 653fc9699f1..c02d30c60d6 100644 --- a/ckanext/datastore/tests/test_unit.py +++ b/ckanext/datastore/tests/test_unit.py @@ -1,11 +1,10 @@ # encoding: utf-8 import unittest -import pylons import nose import mock -from pylons import config +from ckan.common import config import ckan.tests.legacy as tests import ckanext.datastore.db as db @@ -37,7 +36,7 @@ def test_pg_version_check(self): if not tests.is_datastore_supported(): raise nose.SkipTest("Datastore not supported") engine = db._get_engine( - {'connection_url': pylons.config['sqlalchemy.url']}) + {'connection_url': config['sqlalchemy.url']}) connection = engine.connect() assert db._pg_version_is_at_least(connection, '8.0') assert not db._pg_version_is_at_least(connection, '10.0') diff --git a/ckanext/datastore/tests/test_upsert.py b/ckanext/datastore/tests/test_upsert.py index 22fedccab35..b7a09bf7dae 100644 --- a/ckanext/datastore/tests/test_upsert.py +++ b/ckanext/datastore/tests/test_upsert.py @@ -4,7 +4,6 @@ import nose import datetime -import pylons import sqlalchemy.orm as orm import ckan.plugins as p @@ -14,6 +13,7 @@ import ckan.tests.helpers as helpers import ckan.tests.factories as factories +from ckan.common import config import ckanext.datastore.db as db from ckanext.datastore.tests.helpers import rebuild_all_dbs, set_url_type @@ -114,7 +114,7 @@ def setup_class(cls): assert res_dict['success'] is True engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) @classmethod @@ -366,7 +366,7 @@ def setup_class(cls): assert res_dict['success'] is True engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) @classmethod @@ -473,7 +473,7 @@ def setup_class(cls): assert res_dict['success'] is True engine = db._get_engine( - {'connection_url': pylons.config['ckan.datastore.write_url']}) + {'connection_url': config['ckan.datastore.write_url']}) cls.Session = orm.scoped_session(orm.sessionmaker(bind=engine)) @classmethod diff --git a/ckanext/example_iauthfunctions/plugin_v5_custom_config_setting.py b/ckanext/example_iauthfunctions/plugin_v5_custom_config_setting.py index f32cba56dbc..e7eb60fa47d 100644 --- a/ckanext/example_iauthfunctions/plugin_v5_custom_config_setting.py +++ b/ckanext/example_iauthfunctions/plugin_v5_custom_config_setting.py @@ -1,6 +1,6 @@ # encoding: utf-8 -import pylons.config as config +from ckan.common import config import ckan.plugins as plugins import ckan.plugins.toolkit as toolkit diff --git a/ckanext/example_iauthfunctions/plugin_v6_parent_auth_functions.py b/ckanext/example_iauthfunctions/plugin_v6_parent_auth_functions.py index aca2f50612c..6ee8416ccf6 100644 --- a/ckanext/example_iauthfunctions/plugin_v6_parent_auth_functions.py +++ b/ckanext/example_iauthfunctions/plugin_v6_parent_auth_functions.py @@ -1,6 +1,6 @@ # encoding: utf-8 -import pylons.config as config +from ckan.common import config import ckan.plugins as plugins import ckan.plugins.toolkit as toolkit diff --git a/ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py b/ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py index d2a482049c1..f5bafc15bb6 100644 --- a/ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py +++ b/ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py @@ -5,7 +5,6 @@ ''' import paste.fixture import pylons.test -import pylons.config as config import webtest from nose.tools import assert_raises @@ -16,6 +15,7 @@ import ckan.plugins import ckan.tests.factories as factories import ckan.logic as logic +from ckan.common import config class TestExampleIAuthFunctionsPluginV6ParentAuthFunctions(object): @@ -89,7 +89,7 @@ class TestExampleIAuthFunctionsCustomConfigSetting(object): ''' def _get_app(self, users_can_create_groups): - # Set the custom config option in pylons.config. + # Set the custom config option in config. config['ckan.iauthfunctions.users_can_create_groups'] = ( users_can_create_groups) @@ -103,7 +103,7 @@ def _get_app(self, users_can_create_groups): def teardown(self): - # Remove the custom config option from pylons.config. + # Remove the custom config option from config. del config['ckan.iauthfunctions.users_can_create_groups'] # Delete any stuff that's been created in the db, so it doesn't diff --git a/ckanext/example_iconfigurer/tests/test_iconfigurer_update_config.py b/ckanext/example_iconfigurer/tests/test_iconfigurer_update_config.py index f5f44b957f9..5f358488227 100644 --- a/ckanext/example_iconfigurer/tests/test_iconfigurer_update_config.py +++ b/ckanext/example_iconfigurer/tests/test_iconfigurer_update_config.py @@ -2,7 +2,7 @@ import nose.tools -from pylons import config +from ckan.common import config import ckan.lib.app_globals as app_globals diff --git a/ckanext/example_idatasetform/tests/test_example_idatasetform.py b/ckanext/example_idatasetform/tests/test_example_idatasetform.py index 5952ba0f54d..42e0e85c5e4 100644 --- a/ckanext/example_idatasetform/tests/test_example_idatasetform.py +++ b/ckanext/example_idatasetform/tests/test_example_idatasetform.py @@ -2,7 +2,7 @@ import nose.tools as nt -import pylons.config as config +from ckan.common import config import ckan.model as model import ckan.plugins as plugins diff --git a/ckanext/example_igroupform/tests/test_controllers.py b/ckanext/example_igroupform/tests/test_controllers.py index b6f03eaf7e6..6f64973ff5a 100644 --- a/ckanext/example_igroupform/tests/test_controllers.py +++ b/ckanext/example_igroupform/tests/test_controllers.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from nose.tools import assert_equal +from nose.tools import assert_equal, assert_in from routes import url_for import ckan.plugins as plugins @@ -8,7 +8,6 @@ import ckan.model as model from ckan.tests import factories -assert_in = helpers.assert_in webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow @@ -137,7 +136,7 @@ def test_save(self): response = submit_and_follow(app, form, env, 'save') # check correct redirect assert_equal(response.req.url, - 'http://localhost/%s/saved' % custom_group_type) + 'http://test.ckan.net/%s/saved' % custom_group_type) # check saved ok group = model.Group.by_name(u'saved') assert_equal(group.title, u'') @@ -173,7 +172,7 @@ def test_save(self): response = submit_and_follow(app, form, env, 'save') # check correct redirect assert_equal(response.req.url, - 'http://localhost/%s/saved' % group_type) + 'http://test.ckan.net/%s/saved' % group_type) # check saved ok group = model.Group.by_name(u'saved') assert_equal(group.title, u'') diff --git a/ckanext/example_ipermissionlabels/__init__.py b/ckanext/example_ipermissionlabels/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ckanext/example_ipermissionlabels/plugin.py b/ckanext/example_ipermissionlabels/plugin.py new file mode 100644 index 00000000000..7987fe85b5b --- /dev/null +++ b/ckanext/example_ipermissionlabels/plugin.py @@ -0,0 +1,42 @@ +# encoding: utf-8 + +from ckan import plugins +from ckan.lib.plugins import DefaultPermissionLabels +from ckan.plugins.toolkit import get_action + + +class ExampleIPermissionLabelsPlugin( + plugins.SingletonPlugin, DefaultPermissionLabels): + u''' + Example permission labels plugin that makes datasets whose + notes field starts with "Proposed:" visible only to their + creator and Admin users in the organization assigned to the + dataset. + ''' + plugins.implements(plugins.IPermissionLabels) + + def get_dataset_labels(self, dataset_obj): + u''' + Use creator-*, admin-* labels for proposed datasets + ''' + if dataset_obj.notes.startswith(u'Proposed:'): + labels = [u'creator-%s' % dataset_obj.creator_user_id] + if dataset_obj.owner_org: + return labels + [u'admin-%s' % dataset_obj.owner_org] + return labels + + return super(ExampleIPermissionLabelsPlugin, self).get_dataset_labels( + dataset_obj) + + def get_user_dataset_labels(self, user_obj): + u''' + Include admin-* labels for users in addition to default labels + creator-*, member-* and public + ''' + labels = super(ExampleIPermissionLabelsPlugin, self + ).get_user_dataset_labels(user_obj) + if user_obj: + orgs = get_action(u'organization_list_for_user')( + {u'user': user_obj.id}, {u'permission': u'admin'}) + labels.extend(u'admin-%s' % o['id'] for o in orgs) + return labels diff --git a/ckanext/example_ipermissionlabels/tests/__init__.py b/ckanext/example_ipermissionlabels/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ckanext/example_ipermissionlabels/tests/test_example_ipermissionlabels.py b/ckanext/example_ipermissionlabels/tests/test_example_ipermissionlabels.py new file mode 100644 index 00000000000..77a1572dedb --- /dev/null +++ b/ckanext/example_ipermissionlabels/tests/test_example_ipermissionlabels.py @@ -0,0 +1,112 @@ +# encoding: utf-8 + +''' +Tests for the ckanext.example_ipermissionlabels extension +''' +from nose.tools import assert_raises, assert_equal + +import ckan.plugins +from ckan.plugins.toolkit import get_action, NotAuthorized +from ckan.tests.helpers import FunctionalTestBase, call_auth +from ckan.tests import factories +from ckan import model + + +class TestExampleIPermissionLabels(FunctionalTestBase): + @classmethod + def setup_class(cls): + # Test code should use CKAN's plugins.load() function to load plugins + # to be tested. + ckan.plugins.load(u'example_ipermissionlabels') + + @classmethod + def teardown_class(cls): + ckan.plugins.unload(u'example_ipermissionlabels') + + def test_normal_dataset_permissions_are_normal(self): + user = factories.User() + user2 = factories.User() + user3 = factories.User() + org = factories.Organization(user=user) + org2 = factories.Organization( + user=user2, + users=[{u'name': user3['id'], u'capacity': u'member'}]) + + dataset = factories.Dataset( + name=u'd1', user=user, private=True, owner_org=org['id']) + dataset2 = factories.Dataset( + name=u'd2', user=user2, private=True, owner_org=org2['id']) + + results = get_action(u'package_search')( + {u'user': user['name']}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, [u'd1']) + + results = get_action(u'package_search')( + {u'user': user3['name']}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, [u'd2']) + + def test_proposed_overrides_public(self): + user = factories.User() + dataset = factories.Dataset( + name=u'd1', notes=u'Proposed:', user=user) + + results = get_action(u'package_search')( + {}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, []) + + assert_raises( + NotAuthorized, call_auth, u'package_show', + {u'user': u'', u'model': model}, id=u'd1') + + def test_proposed_dataset_visible_to_creator(self): + user = factories.User() + dataset = factories.Dataset( + name=u'd1', notes=u'Proposed:', user=user) + + results = get_action(u'package_search')( + {u'user': user['name']}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, [u'd1']) + + ret = call_auth(u'package_show', + {u'user': user['name'], u'model': model}, id=u'd1') + assert ret + + def test_proposed_dataset_visible_to_org_admin(self): + user = factories.User() + user2 = factories.User() + org = factories.Organization( + user=user2, + users=[{u'name': user['id'], u'capacity': u'editor'}]) + dataset = factories.Dataset( + name=u'd1', notes=u'Proposed:', user=user, owner_org=org['id']) + + results = get_action(u'package_search')( + {u'user': user2[u'name']}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, [u'd1']) + + ret = call_auth(u'package_show', + {u'user': user2['name'], u'model': model}, id=u'd1') + assert ret + + def test_proposed_dataset_invisible_to_another_editor(self): + user = factories.User() + user2 = factories.User() + org = factories.Organization( + user=user2, + users=[{u'name': user['id'], u'capacity': u'editor'}]) + dataset = factories.Dataset( + name=u'd1', notes=u'Proposed:', user=user2, owner_org=org['id']) + + results = get_action(u'package_search')( + {u'user': user['name']}, {u'include_private': True})['results'] + names = [r['name'] for r in results] + assert_equal(names, []) + + assert_raises( + NotAuthorized, call_auth, u'package_show', + {u'user': user['name'], u'model': model}, id=u'd1') diff --git a/ckanext/example_iresourcecontroller/tests/test_example_iresourcecontroller.py b/ckanext/example_iresourcecontroller/tests/test_example_iresourcecontroller.py index 3481ded4e40..b4ccf746b02 100644 --- a/ckanext/example_iresourcecontroller/tests/test_example_iresourcecontroller.py +++ b/ckanext/example_iresourcecontroller/tests/test_example_iresourcecontroller.py @@ -3,7 +3,7 @@ '''Tests for the ckanext.example_iauthfunctions extension. ''' -import pylons.config as config +from ckan.common import config import webtest import ckan.model as model diff --git a/ckanext/example_iuploader/test/test_plugin.py b/ckanext/example_iuploader/test/test_plugin.py index f99dc6b15f9..3259fa9ec6c 100644 --- a/ckanext/example_iuploader/test/test_plugin.py +++ b/ckanext/example_iuploader/test/test_plugin.py @@ -6,20 +6,20 @@ from mock import patch from nose.tools import ( assert_equal, + assert_in, assert_is_instance ) from pyfakefs import fake_filesystem -from pylons import config from routes import url_for import ckan.lib.uploader import ckan.model as model import ckan.plugins as plugins +from ckan.common import config import ckan.tests.factories as factories import ckan.tests.helpers as helpers import ckanext.example_iuploader.plugin as plugin -assert_in = helpers.assert_in webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow diff --git a/ckanext/example_ivalidators/tests/test_ivalidators.py b/ckanext/example_ivalidators/tests/test_ivalidators.py index 47f64103da6..ca8d292397f 100644 --- a/ckanext/example_ivalidators/tests/test_ivalidators.py +++ b/ckanext/example_ivalidators/tests/test_ivalidators.py @@ -1,7 +1,7 @@ # encoding: utf-8 from nose.tools import assert_equals, assert_raises -import pylons.config as config +from ckan.common import config from ckan.plugins.toolkit import get_validator, Invalid from ckan import plugins diff --git a/ckanext/example_theme/custom_config_setting/plugin.py b/ckanext/example_theme/custom_config_setting/plugin.py index 74439633949..c89f4e6296f 100644 --- a/ckanext/example_theme/custom_config_setting/plugin.py +++ b/ckanext/example_theme/custom_config_setting/plugin.py @@ -1,9 +1,9 @@ # encoding: utf-8 -import pylons.config as config import ckan.plugins as plugins import ckan.plugins.toolkit as toolkit +from ckan.common import config def show_most_popular_groups(): diff --git a/ckanext/example_theme/custom_emails/tests.py b/ckanext/example_theme/custom_emails/tests.py index 3aaf30e40f4..66a095e4590 100644 --- a/ckanext/example_theme/custom_emails/tests.py +++ b/ckanext/example_theme/custom_emails/tests.py @@ -1,20 +1,17 @@ # encoding: utf-8 import os -from pylons import config from ckan import plugins import ckan.model as model import ckan.lib.mailer as mailer from ckan.tests import factories from ckan.lib.base import render_jinja2 +from ckan.common import config from ckan.tests.lib.test_mailer import MailerBase import ckan.tests.helpers as helpers -from nose.tools import assert_equal - - -assert_in = helpers.assert_in +from nose.tools import assert_equal, assert_in class TestExampleCustomEmailsPlugin(MailerBase): diff --git a/ckanext/multilingual/plugin.py b/ckanext/multilingual/plugin.py index 5c99321d793..9a519b40a2f 100644 --- a/ckanext/multilingual/plugin.py +++ b/ckanext/multilingual/plugin.py @@ -3,9 +3,9 @@ import ckan from ckan.plugins import SingletonPlugin, implements, IPackageController from ckan.plugins import IGroupController, IOrganizationController, ITagController, IResourceController -import pylons + +from ckan.common import request, config, c from ckan.logic import get_action -from pylons import config LANGS = ['en', 'fr', 'de', 'es', 'it', 'nl', 'ro', 'pt', 'pl'] @@ -14,8 +14,8 @@ def translate_data_dict(data_dict): as possible translated into the desired or the fallback language. ''' - desired_lang_code = pylons.request.environ['CKAN_LANG'] - fallback_lang_code = pylons.config.get('ckan.locale_default', 'en') + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default', 'en') # Get a flattened copy of data_dict to do the translation on. flattened = ckan.lib.navl.dictization_functions.flatten_dict( @@ -109,8 +109,8 @@ def translate_resource_data_dict(data_dict): as possible translated into the desired or the fallback language. ''' - desired_lang_code = pylons.request.environ['CKAN_LANG'] - fallback_lang_code = pylons.config.get('ckan.locale_default', 'en') + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default', 'en') # Get a flattened copy of data_dict to do the translation on. flattened = ckan.lib.navl.dictization_functions.flatten_dict( @@ -199,13 +199,13 @@ class MultilingualDataset(SingletonPlugin): def before_index(self, search_data): default_lang = search_data.get( - 'lang_code', - pylons.config.get('ckan.locale_default', 'en') + 'lang_code', + config.get('ckan.locale_default', 'en') ) ## translate title title = search_data.get('title') - search_data['title_' + default_lang] = title + search_data['title_' + default_lang] = title title_translations = get_action('term_translation_show')( {'model': ckan.model}, {'terms': [title], @@ -232,7 +232,7 @@ def before_index(self, search_data): 'lang_codes': LANGS}) text_field_items = dict(('text_' + lang, []) for lang in LANGS) - + text_field_items['text_' + default_lang].extend(all_terms) for translation in sorted(field_translations): @@ -241,14 +241,14 @@ def before_index(self, search_data): for key, value in text_field_items.iteritems(): search_data[key] = ' '.join(value) - + return search_data def before_search(self, search_params): lang_set = set(LANGS) try: - current_lang = pylons.request.environ['CKAN_LANG'] + current_lang = request.environ['CKAN_LANG'] except TypeError as err: if err.message == ('No object (name: request) has been registered ' 'for this thread'): @@ -284,8 +284,8 @@ def after_search(self, search_results, search_params): if not facets: return search_results - desired_lang_code = pylons.request.environ['CKAN_LANG'] - fallback_lang_code = pylons.config.get('ckan.locale_default', 'en') + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default', 'en') # Look up translations for all of the facets in one db query. terms = set() @@ -323,9 +323,8 @@ def before_view(self, dataset_dict): # all the terms in c.fields (c.fields contains the selected facets) # and save them in c.translated_fields where the templates can # retrieve them later. - c = pylons.c - desired_lang_code = pylons.request.environ['CKAN_LANG'] - fallback_lang_code = pylons.config.get('ckan.locale_default', 'en') + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default', 'en') terms = [value for param, value in c.fields] translations = get_action('term_translation_show')( {'model': ckan.model}, diff --git a/ckanext/reclineview/plugin.py b/ckanext/reclineview/plugin.py index 44de9b385dc..3c52a3c4e60 100644 --- a/ckanext/reclineview/plugin.py +++ b/ckanext/reclineview/plugin.py @@ -2,7 +2,7 @@ from logging import getLogger -from ckan.common import json +from ckan.common import json, config import ckan.plugins as p import ckan.plugins.toolkit as toolkit @@ -12,6 +12,15 @@ Invalid = p.toolkit.Invalid +def get_mapview_config(): + ''' + Extracts and returns map view configuration of the reclineview extension. + ''' + namespace = 'ckanext.spatial.common_map.' + return dict([(k.replace(namespace, ''), v) for k, v in config.iteritems() + if k.startswith(namespace)]) + + def in_list(list_possible_values): ''' Validator that checks that the input value is one of the given @@ -49,6 +58,7 @@ class ReclineViewBase(p.SingletonPlugin): ''' p.implements(p.IConfigurer, inherit=True) p.implements(p.IResourceView, inherit=True) + p.implements(p.ITemplateHelpers, inherit=True) def update_config(self, config): ''' @@ -71,6 +81,11 @@ def setup_template_variables(self, context, data_dict): def view_template(self, context, data_dict): return 'recline_view.html' + def get_helpers(self): + return { + 'get_map_config': get_mapview_config + } + class ReclineView(ReclineViewBase): ''' diff --git a/ckanext/reclineview/tests/test_view.py b/ckanext/reclineview/tests/test_view.py index 897be8048f2..0cbd353ec42 100644 --- a/ckanext/reclineview/tests/test_view.py +++ b/ckanext/reclineview/tests/test_view.py @@ -1,7 +1,7 @@ # encoding: utf-8 import paste.fixture -import pylons.config as config +from ckan.common import config import ckan.model as model import ckan.tests.legacy as tests diff --git a/ckanext/reclineview/theme/public/css/recline.css b/ckanext/reclineview/theme/public/css/recline.css index 34b491742c6..722fab08867 100644 --- a/ckanext/reclineview/theme/public/css/recline.css +++ b/ckanext/reclineview/theme/public/css/recline.css @@ -20,7 +20,7 @@ body { .recline-slickgrid { height: 600px; } - +/* .recline-timeline .vmm-timeline { height: 600px; } @@ -39,11 +39,12 @@ body { margin: 0; padding: 2px 4px; } - +*/ +/* .recline-query-editor { float: right; } - +*/ .loading-spinner { float: right; background-image: url('../img/ajaxload-circle.gif'); @@ -51,6 +52,328 @@ body { height: 32px; } -.recline-pager label, .recline-query-editor label { +.recline-data-explorer .data-view-sidebar { + float: right; + margin-left: 8px; + width: 220px; +} + +.recline-data-explorer .header .navigation { + margin-bottom: 8px; +} + +.recline-data-explorer .header .navigation, +.recline-data-explorer .header .pagination, +.recline-data-explorer .header .pagination form +{ + display: inline; +} + +.recline-data-explorer .header .navigation { + float: left; +} + +.recline-data-explorer .header .menu-right { + float: right; + margin-left: 5px; + padding-left: 5px; +} + +.recline-results-info { + line-height: 35px; + margin-left: 20px; + float: left; +} + +.recline-data-explorer .data-view-sidebar > div { + margin-top: 5px; + margin-bottom: 10px; +} + +.recline-data-explorer .radio, +.recline-data-explorer .checkbox { + padding-left: 20px; +} + +.recline-data-explorer .editor-update-map { + margin: 30px 0px 20px 0px; +} + +.recline-data-explorer label { + font-weight: normal; +} + +/********************************************************** + * Query Editor + *********************************************************/ + +.recline-query-editor { + float: right; + height: 35px; + padding-right: 5px; + margin-right: 5px; + border-right: solid 2px #ddd; +} + +.header .input-prepend { + margin-bottom: auto; +} + +.header .add-on { + float: left; +} + +/* needed for Chrome but not FF */ +.header .add-on { + margin-left: -27px; +} + +/* needed for FF but not chrome */ +.header .input-prepend { + vertical-align: top; +} + +.recline-query-editor form button { + vertical-align: top; +} + +/* label for screen reader */ +.recline-query-editor .form-inline label { + position: absolute; + top:0; + left:-9999px +} + +/********************************************************** + * Pager + *********************************************************/ + +.recline-pager { + float: left; + margin: auto; + display: block; + margin-left: 20px; +} + +.recline-pager .pagination li { + display: inline-block; +} + +.recline-pager .pagination label { + display:none; +} + +.recline-pager .pagination input { + width: 40px; + height: 25px; + padding: 2px 4px; + margin: 0; + margin-top: -2px; + + border: 1px solid #cccccc; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border linear 0.2s, box-shadow linear 0.2s; + border-radius: 4px; + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -webkit-border-radius: 4px; + + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-border-radius: 4px; + + -o-transition: border linear 0.2s, box-shadow linear 0.2s; +} + +.recline-pager .pagination a { + float: none; + margin-left: -5px; + color: #555; +} + +.recline-pager .pagination .page-range { + height: 34px; + padding: 5px 8px; + margin-left: -5px; + border: 1px solid #ddd; +} + +.recline-pager .pagination .page-range a { + padding: 0px 12px; + border: none; +} + +.recline-pager .pagination .page-range a:hover { + background-color: #ffffff; +} + +.recline-pager .pagination > li:first-child > a { + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; + height: 34px; +} + +.recline-pager .pagination > li:last-child > a { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; + height: 34px; +} + +/********************************************************** + * Filter Editor + *********************************************************/ + +.recline-filter-editor { + padding: 8px; + display: none; +} + +.recline-filter-editor .filters { + margin: 20px 0px; +} + +.recline-filter-editor h3 { + margin-top: 4px; +} + +.recline-filter-editor .filter { + margin-top: 20px; +} + +.recline-filter-editor .filter .form-group { + margin-bottom: 0px; +} + +.recline-filter-editor .filter input, +.recline-filter-editor .filter label { + margin: 0px; +} + +.recline-filter-editor .js-edit button { + margin: 25px 0px 0px 0px; +} + +.recline-filter-editor .filter-term a { + font-size: 18px; +} + +.recline-filter-editor input, +.recline-filter-editor select +{ + width: 175px; +} + +.recline-filter-editor input { + margin-top: 0.5em; + margin-bottom: 10px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: 1px solid #cccccc; +} + +.recline-filter-editor label { + font-weight: normal; + display: block; +} + +.recline-filter-editor legend { + margin-bottom: 5px; +} + +.recline-filter-editor .add-filter { + margin-top: 1em; + margin-bottom: 2em; +} + +.recline-filter-editor .update-filter { + margin-top: 1em; +} + +/********************************************************** + * Fields Widget + *********************************************************/ + +.recline-fields-view { display: none; } + +.recline-fields-view .fields-list { + padding: 0; +} + +.recline-fields-view .panel { + background-color: #f5f5f5; + border: 1px solid #e5e5e5; +} + +.recline-fields-view .panel-group h3 { + padding-left: 10px; +} + +.recline-fields-view .fields-list .panel-heading { + padding: 2px 5px; + margin: 1px 0px 1px 5px; +} + +.recline-fields-view .panel a, +.recline-fields-view .panel h4 { + display: inline; +} + +.recline-fields-view .panel a { + padding: 0; +} + +.recline-fields-view .panel h4 { + word-wrap: break-word +} + +.recline-fields-view .clear { + clear: both; +} + +.recline-fields-view .facet-items { + list-style-type: none; + margin-left: 0; +} + +.recline-fields-view .facet-item .term { + font-weight: bold; +} + +.recline-fields-view .facet-item .count { +} + +/********************************************************** + * Notifications + *********************************************************/ + +.recline-data-explorer .notification-loader { + width: 18px; + margin-left: 5px; + background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA%3D%3D); + display: inline-block; +} + +.recline-data-explorer .alert-loader { + position: absolute; + width: 200px; + left: 50%; + margin-left: -100px; + z-index: 10000; + padding: 40px 0px 40px 0px; + margin-top: -10px; + text-align: center; + font-size: 16px; + font-weight: bold; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; + border-top: none; +} diff --git a/ckanext/reclineview/theme/public/recline_view.js b/ckanext/reclineview/theme/public/recline_view.js index f1888592541..23aa5e91322 100644 --- a/ckanext/reclineview/theme/public/recline_view.js +++ b/ckanext/reclineview/theme/public/recline_view.js @@ -46,7 +46,7 @@ this.ckan.module('recline_view', function (jQuery, _) { } } - var errorMsg, dataset; + var errorMsg, dataset, map_config; if (!resourceData.datastore_active) { recline.Backend.DataProxy.timeout = 10000; @@ -58,6 +58,8 @@ this.ckan.module('recline_view', function (jQuery, _) { dataset = new recline.Model.Dataset(resourceData); + map_config = this.options.map_config; + var query = new recline.Model.Query(); query.set({ size: reclineView.limit || 100 }); query.set({ from: reclineView.offset || 0 }); @@ -119,9 +121,9 @@ this.ckan.module('recline_view', function (jQuery, _) { state.lonField = reclineView.longitude_field; } - view = new recline.View.Map({model: dataset, state: state}); + view = new recline.View.Map(this._reclineMapViewOptions(dataset, this.options.map_config)); } else if(reclineView.view_type === "recline_view") { - view = this._newDataExplorer(dataset); + view = this._newDataExplorer(dataset, this.options.map_config); } else { // default to Grid view = new recline.View.SlickGrid({model: dataset}); @@ -148,7 +150,41 @@ this.ckan.module('recline_view', function (jQuery, _) { } }, - _newDataExplorer: function (dataset) { + _reclineMapViewOptions: function(dataset, map_config) { + var tile_url, attribution, subdomains; + tile_url = attribution = subdomains = ''; + + if (map_config.type == 'mapbox') { + // MapBox base map + if (!map_config['mapbox.map_id'] || !map_config['mapbox.access_token']) { + throw '[CKAN Map Widgets] You need to provide a map ID ([account].[handle]) and an access token when using a MapBox layer. ' + + 'See http://www.mapbox.com/developers/api-overview/ for details'; + } + + tile_url = '//{s}.tiles.mapbox.com/v4/' + map_config['mapbox.map_id'] + '/{z}/{x}/{y}.png?access_token=' + map_config['mapbox.access_token']; + handle = map_config['mapbox.map_id']; + subdomains = map_config.subdomains || 'abcd'; + attribution = map_config.attribution || 'Data: OpenStreetMap, Design: MapBox'; + + } else if (map_config.type == 'custom') { + // Custom XYZ layer + tile_url = map_config['custom.url'] || ''; + attribution = map_config.attribution || ''; + subdomains = map_config.subdomains || ''; + + if (map_config['custom.tms']) + var tms = map_config['custom.tms']; + } + + return { + model: dataset, + mapTilesURL: tile_url, + mapTilesAttribution: attribution, + mapTilesSubdomains: subdomains + }; + }, + + _newDataExplorer: function (dataset, map_config) { var views = [ { id: 'grid', @@ -167,9 +203,7 @@ this.ckan.module('recline_view', function (jQuery, _) { { id: 'map', label: 'Map', - view: new recline.View.Map({ - model: dataset - }) + view: new recline.View.Map(this._reclineMapViewOptions(dataset, map_config)) } ]; diff --git a/ckanext/reclineview/theme/public/resource.config b/ckanext/reclineview/theme/public/resource.config index b2705e40a2a..9ec647d0c51 100644 --- a/ckanext/reclineview/theme/public/resource.config +++ b/ckanext/reclineview/theme/public/resource.config @@ -17,18 +17,22 @@ main = vendor/underscore/1.4.4/underscore.js vendor/backbone/1.0.0/backbone.js vendor/mustache/0.5.0-dev/mustache.js - vendor/bootstrap/2.3.2/bootstrap.js + vendor/bootstrap/3.2.0/js/bootstrap.js vendor/json/json2.js vendor/flot/excanvas.js vendor/flot/jquery.flot.js vendor/flot/jquery.flot.time.js vendor/leaflet/0.7.3/leaflet.js vendor/leaflet.markercluster/leaflet.markercluster.js - vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.js - vendor/slickgrid/2.0.1/jquery.event.drag-2.0.js - vendor/slickgrid/2.0.1/slick.grid.js - vendor/slickgrid/2.0.1/plugins/slick.rowselectionmodel.js - vendor/slickgrid/2.0.1/plugins/slick.rowmovemanager.js + vendor/slickgrid/2.2/jquery-ui-1.8.16.custom.js + vendor/slickgrid/2.2/jquery.event.drag-2.2.js + vendor/slickgrid/2.2/jquery.event.drop-2.2.js + vendor/slickgrid/2.2/slick.core.js + vendor/slickgrid/2.2/slick.formatters.js + vendor/slickgrid/2.2/slick.editors.js + vendor/slickgrid/2.2/slick.grid.js + vendor/slickgrid/2.2/plugins/slick.rowselectionmodel.js + vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js vendor/moment/2.0.0/moment.js vendor/ckan.js/ckan.js @@ -37,11 +41,14 @@ main = widget.recordcount.js recline_view.js - vendor/bootstrap/2.3.2/css/bootstrap.css + + vendor/bootstrap/3.2.0/css/bootstrap.css vendor/leaflet/0.7.3/leaflet.css vendor/leaflet.markercluster/MarkerCluster.css vendor/leaflet.markercluster/MarkerCluster.Default.css - vendor/slickgrid/2.0.1/slick.grid.css - vendor/recline/recline.css + vendor/slickgrid/2.2/slick.grid.css + vendor/recline/slickgrid.css + vendor/recline/flot.css + vendor/recline/map.css css/recline.css diff --git a/ckanext/reclineview/theme/public/vendor/bootstrap/2.3.2/bootstrap.js b/ckanext/reclineview/theme/public/vendor/bootstrap/2.3.2/bootstrap.js deleted file mode 100644 index 44109f62d47..00000000000 --- a/ckanext/reclineview/theme/public/vendor/bootstrap/2.3.2/bootstrap.js +++ /dev/null @@ -1,2280 +0,0 @@ -/* =================================================== - * bootstrap-transition.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#transitions - * =================================================== - * Copyright 2013 Twitter, Inc. - * - * 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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) - * ======================================================= */ - - $(function () { - - $.support.transition = (function () { - - var transitionEnd = (function () { - - var el = document.createElement('bootstrap') - , transEndEventNames = { - 'WebkitTransition' : 'webkitTransitionEnd' - , 'MozTransition' : 'transitionend' - , 'OTransition' : 'oTransitionEnd otransitionend' - , 'transition' : 'transitionend' - } - , name - - for (name in transEndEventNames){ - if (el.style[name] !== undefined) { - return transEndEventNames[name] - } - } - - }()) - - return transitionEnd && { - end: transitionEnd - } - - })() - - }) - -}(window.jQuery);/* ========================================================== - * bootstrap-alert.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#alerts - * ========================================================== - * Copyright 2013 Twitter, Inc. - * - * 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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* ALERT CLASS DEFINITION - * ====================== */ - - var dismiss = '[data-dismiss="alert"]' - , Alert = function (el) { - $(el).on('click', dismiss, this.close) - } - - Alert.prototype.close = function (e) { - var $this = $(this) - , selector = $this.attr('data-target') - , $parent - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - $parent = $(selector) - - e && e.preventDefault() - - $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) - - $parent.trigger(e = $.Event('close')) - - if (e.isDefaultPrevented()) return - - $parent.removeClass('in') - - function removeElement() { - $parent - .trigger('closed') - .remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent.on($.support.transition.end, removeElement) : - removeElement() - } - - - /* ALERT PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.alert - - $.fn.alert = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('alert') - if (!data) $this.data('alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - $.fn.alert.Constructor = Alert - - - /* ALERT NO CONFLICT - * ================= */ - - $.fn.alert.noConflict = function () { - $.fn.alert = old - return this - } - - - /* ALERT DATA-API - * ============== */ - - $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) - -}(window.jQuery);/* ============================================================ - * bootstrap-button.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#buttons - * ============================================================ - * Copyright 2013 Twitter, Inc. - * - * 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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* BUTTON PUBLIC CLASS DEFINITION - * ============================== */ - - var Button = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.button.defaults, options) - } - - Button.prototype.setState = function (state) { - var d = 'disabled' - , $el = this.$element - , data = $el.data() - , val = $el.is('input') ? 'val' : 'html' - - state = state + 'Text' - data.resetText || $el.data('resetText', $el[val]()) - - $el[val](data[state] || this.options[state]) - - // push to event loop to allow forms to submit - setTimeout(function () { - state == 'loadingText' ? - $el.addClass(d).attr(d, d) : - $el.removeClass(d).removeAttr(d) - }, 0) - } - - Button.prototype.toggle = function () { - var $parent = this.$element.closest('[data-toggle="buttons-radio"]') - - $parent && $parent - .find('.active') - .removeClass('active') - - this.$element.toggleClass('active') - } - - - /* BUTTON PLUGIN DEFINITION - * ======================== */ - - var old = $.fn.button - - $.fn.button = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('button') - , options = typeof option == 'object' && option - if (!data) $this.data('button', (data = new Button(this, options))) - if (option == 'toggle') data.toggle() - else if (option) data.setState(option) - }) - } - - $.fn.button.defaults = { - loadingText: 'loading...' - } - - $.fn.button.Constructor = Button - - - /* BUTTON NO CONFLICT - * ================== */ - - $.fn.button.noConflict = function () { - $.fn.button = old - return this - } - - - /* BUTTON DATA-API - * =============== */ - - $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - $btn.button('toggle') - }) - -}(window.jQuery);/* ========================================================== - * bootstrap-carousel.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#carousel - * ========================================================== - * Copyright 2013 Twitter, Inc. - * - * 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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* CAROUSEL CLASS DEFINITION - * ========================= */ - - var Carousel = function (element, options) { - this.$element = $(element) - this.$indicators = this.$element.find('.carousel-indicators') - this.options = options - this.options.pause == 'hover' && this.$element - .on('mouseenter', $.proxy(this.pause, this)) - .on('mouseleave', $.proxy(this.cycle, this)) - } - - Carousel.prototype = { - - cycle: function (e) { - if (!e) this.paused = false - if (this.interval) clearInterval(this.interval); - this.options.interval - && !this.paused - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) - return this - } - - , getActiveIndex: function () { - this.$active = this.$element.find('.item.active') - this.$items = this.$active.parent().children() - return this.$items.index(this.$active) - } - - , to: function (pos) { - var activeIndex = this.getActiveIndex() - , that = this - - if (pos > (this.$items.length - 1) || pos < 0) return - - if (this.sliding) { - return this.$element.one('slid', function () { - that.to(pos) - }) - } - - if (activeIndex == pos) { - return this.pause().cycle() - } - - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) - } - - , pause: function (e) { - if (!e) this.paused = true - if (this.$element.find('.next, .prev').length && $.support.transition.end) { - this.$element.trigger($.support.transition.end) - this.cycle(true) - } - clearInterval(this.interval) - this.interval = null - return this - } - - , next: function () { - if (this.sliding) return - return this.slide('next') - } - - , prev: function () { - if (this.sliding) return - return this.slide('prev') - } - - , slide: function (type, next) { - var $active = this.$element.find('.item.active') - , $next = next || $active[type]() - , isCycling = this.interval - , direction = type == 'next' ? 'left' : 'right' - , fallback = type == 'next' ? 'first' : 'last' - , that = this - , e - - this.sliding = true - - isCycling && this.pause() - - $next = $next.length ? $next : this.$element.find('.item')[fallback]() - - e = $.Event('slide', { - relatedTarget: $next[0] - , direction: direction - }) - - if ($next.hasClass('active')) return - - if (this.$indicators.length) { - this.$indicators.find('.active').removeClass('active') - this.$element.one('slid', function () { - var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) - $nextIndicator && $nextIndicator.addClass('active') - }) - } - - if ($.support.transition && this.$element.hasClass('slide')) { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $next.addClass(type) - $next[0].offsetWidth // force reflow - $active.addClass(direction) - $next.addClass(direction) - this.$element.one($.support.transition.end, function () { - $next.removeClass([type, direction].join(' ')).addClass('active') - $active.removeClass(['active', direction].join(' ')) - that.sliding = false - setTimeout(function () { that.$element.trigger('slid') }, 0) - }) - } else { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger('slid') - } - - isCycling && this.cycle() - - return this - } - - } - - - /* CAROUSEL PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.carousel - - $.fn.carousel = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('carousel') - , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) - , action = typeof option == 'string' ? option : options.slide - if (!data) $this.data('carousel', (data = new Carousel(this, options))) - if (typeof option == 'number') data.to(option) - else if (action) data[action]() - else if (options.interval) data.pause().cycle() - }) - } - - $.fn.carousel.defaults = { - interval: 5000 - , pause: 'hover' - } - - $.fn.carousel.Constructor = Carousel - - - /* CAROUSEL NO CONFLICT - * ==================== */ - - $.fn.carousel.noConflict = function () { - $.fn.carousel = old - return this - } - - /* CAROUSEL DATA-API - * ================= */ - - $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { - var $this = $(this), href - , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - , options = $.extend({}, $target.data(), $this.data()) - , slideIndex - - $target.carousel(options) - - if (slideIndex = $this.attr('data-slide-to')) { - $target.data('carousel').pause().to(slideIndex).cycle() - } - - e.preventDefault() - }) - -}(window.jQuery);/* ============================================================= - * bootstrap-collapse.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#collapse - * ============================================================= - * Copyright 2013 Twitter, Inc. - * - * 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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* COLLAPSE PUBLIC CLASS DEFINITION - * ================================ */ - - var Collapse = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.collapse.defaults, options) - - if (this.options.parent) { - this.$parent = $(this.options.parent) - } - - this.options.toggle && this.toggle() - } - - Collapse.prototype = { - - constructor: Collapse - - , dimension: function () { - var hasWidth = this.$element.hasClass('width') - return hasWidth ? 'width' : 'height' - } - - , show: function () { - var dimension - , scroll - , actives - , hasData - - if (this.transitioning || this.$element.hasClass('in')) return - - dimension = this.dimension() - scroll = $.camelCase(['scroll', dimension].join('-')) - actives = this.$parent && this.$parent.find('> .accordion-group > .in') - - if (actives && actives.length) { - hasData = actives.data('collapse') - if (hasData && hasData.transitioning) return - actives.collapse('hide') - hasData || actives.data('collapse', null) - } - - this.$element[dimension](0) - this.transition('addClass', $.Event('show'), 'shown') - $.support.transition && this.$element[dimension](this.$element[0][scroll]) - } - - , hide: function () { - var dimension - if (this.transitioning || !this.$element.hasClass('in')) return - dimension = this.dimension() - this.reset(this.$element[dimension]()) - this.transition('removeClass', $.Event('hide'), 'hidden') - this.$element[dimension](0) - } - - , reset: function (size) { - var dimension = this.dimension() - - this.$element - .removeClass('collapse') - [dimension](size || 'auto') - [0].offsetWidth - - this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') - - return this - } - - , transition: function (method, startEvent, completeEvent) { - var that = this - , complete = function () { - if (startEvent.type == 'show') that.reset() - that.transitioning = 0 - that.$element.trigger(completeEvent) - } - - this.$element.trigger(startEvent) - - if (startEvent.isDefaultPrevented()) return - - this.transitioning = 1 - - this.$element[method]('in') - - $.support.transition && this.$element.hasClass('collapse') ? - this.$element.one($.support.transition.end, complete) : - complete() - } - - , toggle: function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - } - - } - - - /* COLLAPSE PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.collapse - - $.fn.collapse = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('collapse') - , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) - if (!data) $this.data('collapse', (data = new Collapse(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.collapse.defaults = { - toggle: true - } - - $.fn.collapse.Constructor = Collapse - - - /* COLLAPSE NO CONFLICT - * ==================== */ - - $.fn.collapse.noConflict = function () { - $.fn.collapse = old - return this - } - - - /* COLLAPSE DATA-API - * ================= */ - - $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { - var $this = $(this), href - , target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 - , option = $(target).data('collapse') ? 'toggle' : $this.data() - $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - $(target).collapse(option) - }) - -}(window.jQuery);/* ============================================================ - * bootstrap-dropdown.js v2.3.2 - * http://getbootstrap.com/2.3.2/javascript.html#dropdowns - * ============================================================ - * Copyright 2013 Twitter, Inc. - * - * 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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* DROPDOWN CLASS DEFINITION - * ========================= */ - - var toggle = '[data-toggle=dropdown]' - , Dropdown = function (element) { - var $el = $(element).on('click.dropdown.data-api', this.toggle) - $('html').on('click.dropdown.data-api', function () { - $el.parent().removeClass('open') - }) - } - - Dropdown.prototype = { - - constructor: Dropdown - - , toggle: function (e) { - var $this = $(this) - , $parent - , isActive - - if ($this.is('.disabled, :disabled')) return - - $parent = getParent($this) - - isActive = $parent.hasClass('open') - - clearMenus() - - if (!isActive) { - if ('ontouchstart' in document.documentElement) { - // if mobile we we use a backdrop because click events don't delegate - $('