diff --git a/.gitignore b/.gitignore index 6158c700f..0a6792259 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,10 @@ build dist test-db.sqlite +### frontend +node_modules/ + /tmp + +### vite dev cache +.vite/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f0fda227..ae4be0ecf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,3 +15,17 @@ repos: args: [--fix] # Run the formatter. - id: ruff-format + + - repo: local + hooks: + - id: biome-check + name: biome check + entry: bun run biome check --write --files-ignore-unknown=true --no-errors-on-unmatched + language: system + types_or: [javascript, jsx, ts, tsx, json, css] + - id: typecheck + name: tsc typecheck + entry: bun run typecheck + language: system + types_or: [ts, tsx] + pass_filenames: false diff --git a/Dockerfile b/Dockerfile index 23c57f573..1d2c8dc59 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,13 @@ +# Frontend build stage: compiles the web-ui bundle with bun + vite. +FROM oven/bun:1-slim AS frontend +WORKDIR /build +COPY package.json bun.lockb bunfig.toml ./ +RUN bun install --frozen-lockfile +COPY vite.config.ts tsconfig.json openapi-ts.config.ts ./ +COPY web-ui ./web-ui +RUN bun run build + + FROM python:3.14-slim RUN mkdir /app @@ -50,8 +60,9 @@ COPY pyproject.toml uv.lock ./ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked -# Collect static files. Use fake versions of required env variables -# since they're not relevant at this step. +# Bring in the built frontend, then collect static files. Use fake +# versions of required env variables since they're not relevant here. +COPY --from=frontend /build/web-ui/dist ./web-ui/dist RUN DATABASE_URL=mysql:// \ REDIS_URL=redis:// \ KEGBOT_SECRET_KEY=changeme \ diff --git a/README.md b/README.md index 874ba1ed7..589462430 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,31 @@ $ open http://localhost:8000/ For much more detail, see the complete [Kegbot Server documentation](https://docs.kegbot.org/projects/kegbot-server/en/latest/). +## Development + +The web interface is a React single-page app (in `web-ui/`) served by the +Django backend (in `pykeg/`). For development, run both servers and browse +the vite dev server, which proxies API requests to Django: + +``` +$ uv sync # python dependencies +$ bun install # frontend dependencies +$ kegbot runserver # django, on port 8001 +$ bun run dev # vite, on http://localhost:8000 <-- browse here +``` + +Other useful commands: + +``` +$ uv run pytest # backend tests +$ bun run test # frontend tests +$ bun run check # frontend lint + typecheck +$ bun run build # production frontend build (web-ui/dist) +$ bun run generate-api # regenerate the API client from the schema +$ bun run generate-constants # regenerate web-ui/lib/shared-constants.ts +``` + + ## Documentation and Help * Main project page: https://kegbot.org/ diff --git a/bin/check-upgraded-site.py b/bin/check-upgraded-site.py index e8a8b353d..de9583ca8 100755 --- a/bin/check-upgraded-site.py +++ b/bin/check-upgraded-site.py @@ -3,7 +3,7 @@ Run after `kegbot restore ` + `kegbot upgrade`. Compares the database against testdata/demo-site.json (the data the legacy backups were -built from) and smoke-tests key pages. Exits nonzero on any failure. +built from) and smoke-tests key API endpoints. Exits nonzero on any failure. """ import json @@ -45,8 +45,14 @@ def check(label, expected, actual): check("kegs on tap", 2, models.Keg.objects.filter(status=models.Keg.STATUS_ON_TAP).count()) client = Client() -for url in ["/", "/kegs/", "/stats/", "/sessions/", "/accounts/login/"]: - response = client.get(url, follow=True) +for url in [ + "/api/users/me", + "/api/status", + "/api/kegs", + "/api/sessions", + "/api/stats/system", +]: + response = client.get(url) check(f"GET {url}", 200, response.status_code) api_key = models.ApiKey.objects.first() diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..86cc2520f --- /dev/null +++ b/biome.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": [ + "web-ui/**", + "vite.config.ts", + "openapi-ts.config.ts", + "!web-ui/api-client", + "!web-ui/dist", + "!web-ui/lib/shared-constants.ts" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedImports": "error", + "noUnusedVariables": "error", + "useExhaustiveDependencies": "off" + }, + "style": { + "noRestrictedImports": { + "level": "error", + "options": { + "patterns": [ + { + "group": ["../*", "./../*"], + "message": "Use @/ root-relative imports instead of parent-relative paths." + } + ] + } + } + } + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 000000000..1ed13ec9a Binary files /dev/null and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 000000000..d4f7f0242 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["./web-ui/test/setup.ts"] diff --git a/docs/source/developers.rst b/docs/source/developers.rst index 2dbbba624..add969cfe 100644 --- a/docs/source/developers.rst +++ b/docs/source/developers.rst @@ -35,7 +35,37 @@ Run the server, or any other command, through ``uv run``: $ uv run bin/kegbot version $ uv run bin/kegbot migrate - $ uv run bin/kegbot run_server + +Running the dev servers +----------------------- + +The web interface is a React single-page app in ``web-ui/``, built with +`bun `_ and vite. Install the frontend dependencies once: + +.. code-block:: console + + $ bun install + +For development, run two servers in separate terminals: + +.. code-block:: console + + $ uv run bin/kegbot runserver # django backend, on port 8001 + $ bun run dev # vite dev server, on port 8000 + +Then browse http://localhost:8000. The vite dev server serves the frontend +with hot reload and proxies ``/api``, ``/media``, and ``/static`` requests +to Django, so everything is same-origin and CSRF just works. + +Other useful frontend commands: + +.. code-block:: console + + $ bun run test # frontend tests + $ bun run check # biome lint + tsc typecheck + $ bun run build # production build (web-ui/dist) + $ bun run generate-api # regenerate the API client from the schema + $ bun run generate-constants # regenerate web-ui/lib/shared-constants.ts Running tests ------------- @@ -50,12 +80,14 @@ no redis server: Code format and lint -------------------- -We use `ruff` to format and lint all code: +We use `ruff` to format and lint Python, and `biome` plus ``tsc`` for the +frontend: .. code-block:: console $ uv run ruff format $ uv run ruff check + $ bun run check To run these checks automatically before each commit, install the `pre-commit` hooks: diff --git a/docs/source/releases/changelog.rst b/docs/source/releases/changelog.rst index f46188dac..1d4ecb222 100644 --- a/docs/source/releases/changelog.rst +++ b/docs/source/releases/changelog.rst @@ -14,6 +14,24 @@ brought up to date. **Highlights** +* **The web interface is completely new.** A React single-page app + (Material UI) replaces the server-rendered Django UI. Every part of the + interface was rebuilt: browsing (home, kegs, drinkers, drinks, session + archives, system stats with charts), the fullscreen/kiosk mode (which now + updates in place instead of reloading), account management, registration + and password flows, the full admin area, and the setup wizard (which now + also works in production, not just ``DEBUG``). Public URLs are preserved, + including short links (``/d/``, ``/s/``) and links in older + notification e-mails. +* **The new API now covers everything the web UI does**, including: + list filtering and page sizing; a ``/api/users/me`` boot endpoint; + per-user/keg/session/system stats; keg and tap lifecycle operations + (attach/start/end kegs, record drinks and spills, connect hardware); + drink management and picture uploads; account self-service and + authentication flows (registration, password reset, e-mail change, + activation); admin user management and site settings; backups, logs, + test e-mail, and bugreport endpoints; an API-driven setup wizard; and + plugin settings. * Python 3.14 is now required (was 3.10). * Django 5.2 LTS (was 3.2). * Web server switched from gunicorn/gevent to waitress. @@ -45,6 +63,19 @@ brought up to date. jobs are handed to the worker only after the surrounding database transaction commits. Ensure ``run_workers`` is running (unchanged) to process them. +* **Building from source now requires** `bun `_ **for the + frontend.** Run ``bun install && bun run build`` before + ``kegbot collectstatic``; the Docker image does this automatically. For + development, run ``bun run dev`` (vite, http://localhost:8000) alongside + ``kegbot runserver`` (Django, now defaulting to port 8001); the dev + server proxies API requests to Django. +* **Site privacy is now enforced by the API** and rendered by the frontend; + the server-side privacy interstitials (and + ``KEGBOT_EXTRA_PRIVACY_EXEMPT_PATHS``) are gone. +* Plugins no longer provide Django template views; plugin settings are + managed through the plugin settings API. The webhook plugin is otherwise + unchanged. +* The legacy ``ga.js`` Google Analytics snippet is no longer emitted. * ``run_gunicorn`` was removed. Use ``kegbot run_server`` (now waitress). * The Docker image no longer publishes a ``linux/arm/v7`` variant (amd64 and arm64 only). diff --git a/openapi-ts.config.ts b/openapi-ts.config.ts new file mode 100644 index 000000000..bec2010ab --- /dev/null +++ b/openapi-ts.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "@hey-api/openapi-ts"; + +export default defineConfig({ + // The ./ prefix matters: a bare "a/b" path parses as a Hey API + // platform shorthand and crashes the generator. + input: "./web-ui/schema.yaml", + output: "web-ui/api-client", + plugins: ["@hey-api/client-fetch"], +}); diff --git a/package.json b/package.json new file mode 100644 index 000000000..2d24574bb --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "kegbot-server-ui", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "test": "bun test web-ui/", + "typecheck": "tsc --noEmit", + "check": "biome check web-ui && tsc --noEmit", + "format": "biome format --write web-ui", + "generate-api": "uv run python bin/kegbot spectacular --file web-ui/schema.yaml && openapi-ts", + "generate-constants": "uv run python bin/kegbot print_constants" + }, + "dependencies": { + "@emotion/react": "^11", + "@emotion/styled": "^11", + "@fontsource/ibm-plex-mono": "^5.3.0", + "@fontsource/ibm-plex-sans": "^5.3.0", + "@mui/icons-material": "^9", + "@mui/material": "^9", + "@mui/x-charts": "^9", + "react": "^19", + "react-dom": "^19", + "react-router": "^8" + }, + "devDependencies": { + "@biomejs/biome": "^2", + "@happy-dom/global-registrator": "^18", + "@hey-api/openapi-ts": "^0.80", + "@testing-library/react": "^16", + "@types/bun": "^1", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^4", + "typescript": "^5", + "vite": "^7" + } +} diff --git a/pykeg/api/filters.py b/pykeg/api/filters.py new file mode 100644 index 000000000..2f2f2920d --- /dev/null +++ b/pykeg/api/filters.py @@ -0,0 +1,131 @@ +"""FilterSets for API list endpoints. + +These power the query parameters the frontend uses for drill-down pages +(per-user drink lists, keg detail, session date archives) and search. +""" + +import datetime + +from django.db.models import Q +from django.utils import timezone +from django_filters import rest_framework as filters + +from pykeg.core import models + + +class DrinkFilter(filters.FilterSet): + username = filters.CharFilter(field_name="user__username") + + class Meta: + model = models.Drink + fields = ["user", "keg", "session", "username"] + + +class KegFilter(filters.FilterSet): + class Meta: + model = models.Keg + fields = ["status"] + + +def session_date_range(year, month=None, day=None): + """[start, end) datetimes covering a year/month/day in the site timezone. + + Returns None for out-of-range dates (month 13, February 30, ...). + """ + tz = timezone.get_current_timezone() + try: + if day is not None: + start = datetime.datetime(year, month, day) + end = start + datetime.timedelta(days=1) + elif month is not None: + start = datetime.datetime(year, month, 1) + end = ( + datetime.datetime(year + 1, 1, 1) + if month == 12 + else datetime.datetime(year, month + 1, 1) + ) + else: + start = datetime.datetime(year, 1, 1) + end = datetime.datetime(year + 1, 1, 1) + except ValueError: + return None + return start.replace(tzinfo=tz), end.replace(tzinfo=tz) + + +class DrinkingSessionFilter(filters.FilterSet): + # Declared for form parsing and schema generation; applied together + # in filter_queryset as a datetime range. A range keeps the site + # timezone conversion in Python: date lookups (start_time__year) + # compile to CONVERT_TZ on MySQL, which silently returns NULL when + # the server's timezone tables aren't loaded. + year = filters.NumberFilter(method="noop") + month = filters.NumberFilter(method="noop") + day = filters.NumberFilter(method="noop") + + class Meta: + model = models.DrinkingSession + fields = ["year", "month", "day"] + + def noop(self, queryset, name, value): + return queryset + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + data = self.form.cleaned_data + year = data.get("year") + month = data.get("month") + day = data.get("day") + if year is None: + return queryset + # A day is meaningless without a month; ignore it in that case. + month = int(month) if month is not None else None + day = int(day) if day is not None and month is not None else None + bounds = session_date_range(int(year), month, day) + if bounds is None: + return queryset.none() + start, end = bounds + return queryset.filter(start_time__gte=start, start_time__lt=end) + + +class SystemEventFilter(filters.FilterSet): + since = filters.NumberFilter(field_name="id", lookup_expr="gt") + username = filters.CharFilter(field_name="user__username") + + class Meta: + model = models.SystemEvent + fields = ["since", "kind", "user", "username", "keg", "session"] + + +class ThermologFilter(filters.FilterSet): + since = filters.IsoDateTimeFilter(field_name="time", lookup_expr="gte") + until = filters.IsoDateTimeFilter(field_name="time", lookup_expr="lt") + + class Meta: + model = models.Thermolog + fields = ["sensor", "since", "until"] + + +class UserFilter(filters.FilterSet): + search = filters.CharFilter(method="filter_search") + + class Meta: + model = models.User + fields = ["is_active", "is_staff", "search"] + + def filter_search(self, queryset, name, value): + return queryset.filter(Q(username__icontains=value) | Q(display_name__icontains=value)) + + +class AuthenticationTokenFilter(filters.FilterSet): + search = filters.CharFilter(method="filter_search") + + class Meta: + model = models.AuthenticationToken + fields = ["auth_device", "enabled", "user", "search"] + + def filter_search(self, queryset, name, value): + return queryset.filter( + Q(token_value__icontains=value) + | Q(nice_name__icontains=value) + | Q(user__username__icontains=value) + ) diff --git a/pykeg/api/forms.py b/pykeg/api/forms.py new file mode 100644 index 000000000..2cc42ab6c --- /dev/null +++ b/pykeg/api/forms.py @@ -0,0 +1,64 @@ +"""Django forms reused by API endpoints.""" + +import urllib.parse + +from django import forms +from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.auth.tokens import default_token_generator +from django.template import loader +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode + +from pykeg.core import models +from pykeg.web.util import get_base_url + +User = get_user_model() + + +class PasswordResetForm(forms.Form): + """Builds and mails password-reset links (relocated from the old + registration app; the reset link lands on a frontend route).""" + + email = forms.EmailField(max_length=254) + + def save( + self, + subject_template_name="registration/password_reset_subject.txt", + email_template_name="registration/password_reset_email.html", + token_generator=default_token_generator, + from_email=None, + request=None, + **kwargs, + ): + """Generates a one-use only link for resetting password and sends + it to the user.""" + from django.core.mail import send_mail + + email = self.cleaned_data["email"] + active_users = User._default_manager.filter(email__iexact=email, is_active=True) + for user in active_users: + # Make sure that no email is sent to a user that actually has + # a password marked as unusable. + if not user.has_usable_password(): + continue + from_email = settings.DEFAULT_FROM_EMAIL or from_email + + base_url = get_base_url() + parsed = urllib.parse.urlparse(base_url) + + kbsite = models.KegbotSite.get() + context = { + "email": user.email, + "site_name": kbsite.title, + "uid": urlsafe_base64_encode(force_bytes(user.pk)), + "user": user, + "token": token_generator.make_token(user), + "domain": parsed.netloc, + "protocol": parsed.scheme, + } + subject = loader.render_to_string(subject_template_name, context) + # Email subject *must not* contain newlines. + subject = "".join(subject.splitlines()) + body = loader.render_to_string(email_template_name, context) + send_mail(subject, body, from_email, [user.email]) diff --git a/pykeg/api/pagination.py b/pykeg/api/pagination.py index b94a25c0a..2615e6140 100644 --- a/pykeg/api/pagination.py +++ b/pykeg/api/pagination.py @@ -13,5 +13,8 @@ class CursorPagination(BaseCursorPagination): cursor. We can fix the implementation later. """ + page_size_query_param = "page_size" + max_page_size = 100 + def get_ordering(self, request, queryset, view): return ("-id",) diff --git a/pykeg/api/permissions.py b/pykeg/api/permissions.py index 56a9f6037..3dd4a73f9 100644 --- a/pykeg/api/permissions.py +++ b/pykeg/api/permissions.py @@ -51,3 +51,30 @@ def has_permission(self, request, view): if request.method in permissions.SAFE_METHODS: return super().has_permission(request, view) return bool(request.user and request.user.is_staff) + + +class SetupAccess(permissions.BasePermission): + """Setup endpoints are open while setup/upgrade is required. + + Once the site is set up and current, they respond 403: setup views use + no authenticators (the database may not exist yet), so nobody — staff + included — can reach them afterwards. + """ + + message = "Setup is not required" + + def has_permission(self, request, view): + return bool( + getattr(request, "need_setup", False) or getattr(request, "need_upgrade", False) + ) + + +class IsOwnerOrAdmin(IsAuthenticated): + """Requires the object's owning user (its `user` attribute) or an admin.""" + + message = "You must own this object or be an admin to do that" + + def has_object_permission(self, request, view, obj): + if request.user.is_staff: + return True + return getattr(obj, "user", None) == request.user diff --git a/pykeg/api/serializers.py b/pykeg/api/serializers.py index a39bbdf15..21efdaa29 100644 --- a/pykeg/api/serializers.py +++ b/pykeg/api/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_framework.exceptions import ValidationError -from pykeg.core import models +from pykeg.core import kb_common, keg_sizes, models class PictureSerializer(serializers.ModelSerializer): @@ -145,6 +145,8 @@ class Meta: "picture", ] + picture = PictureSerializer(read_only=True) + class BeverageSerializer(serializers.ModelSerializer): class Meta: @@ -175,6 +177,7 @@ class Meta: producer_id = serializers.PrimaryKeyRelatedField( queryset=models.BeverageProducer.objects.all(), source="producer", write_only=True ) + picture = PictureSerializer(read_only=True) class ControllerSerializer(serializers.ModelSerializer): @@ -243,6 +246,14 @@ class Meta: "illustration_thumbnail", "stats", ] + # Status and volumes change only through the keg lifecycle + # endpoints (attach/end/reactivate/spill), never by direct edit. + read_only_fields = [ + "status", + "spilled_ml", + "start_time", + "end_time", + ] beverage = BeverageSerializer(source="type", read_only=True) illustration = serializers.URLField(source="get_illustration", read_only=True) @@ -264,6 +275,9 @@ class Meta: ] current_keg = KegSerializer(read_only=True) + # Connections change only through the attach-keg/connect-* endpoints. + current_keg_id = serializers.IntegerField(read_only=True) + temperature_sensor_id = serializers.IntegerField(read_only=True) class DrinkSerializer(serializers.ModelSerializer): @@ -296,12 +310,18 @@ class Meta: "token_value", "nice_name", "pin", + "user", "user_id", "enabled", "created_time", "expire_time", ] + # Assign/unassign the token's user by pk on writes; reads use user_id. + user = serializers.PrimaryKeyRelatedField( + queryset=models.User.objects.all(), allow_null=True, required=False, write_only=True + ) + class DrinkingSessionSerializer(serializers.ModelSerializer): class Meta: @@ -382,6 +402,244 @@ class Meta: ] +class SessionDirectoryMonthSerializer(serializers.Serializer): + month = serializers.IntegerField() + days = serializers.ListField(child=serializers.IntegerField()) + count = serializers.IntegerField() + + +class SessionDirectoryYearSerializer(serializers.Serializer): + year = serializers.IntegerField() + months = SessionDirectoryMonthSerializer(many=True) + count = serializers.IntegerField() + + +class SessionDirectorySerializer(serializers.Serializer): + """The session archive tree: which dates have sessions.""" + + years = SessionDirectoryYearSerializer(many=True) + + +class TapAttachKegRequestSerializer(serializers.Serializer): + keg_id = serializers.PrimaryKeyRelatedField(queryset=models.Keg.objects.all(), source="keg") + + +class NewKegRequestSerializer(serializers.Serializer): + """Parameters for creating a keg. + + The beverage may be given as an existing `beverage_id`, or described by + the (`beverage_name`, `producer_name`, `style_name`, `beverage_type`) + tuple, which matches or creates one. + """ + + beverage_id = serializers.PrimaryKeyRelatedField( + queryset=models.Beverage.objects.all(), + source="beverage", + required=False, + allow_null=True, + default=None, + ) + beverage_name = serializers.CharField(required=False, allow_blank=True, default="") + beverage_type = serializers.ChoiceField( + choices=models.Beverage.TYPES, required=False, default=models.Beverage.TYPE_BEER + ) + producer_name = serializers.CharField(required=False, allow_blank=True, default="") + style_name = serializers.CharField(required=False, allow_blank=True, default="") + keg_type = serializers.ChoiceField(choices=keg_sizes.CHOICES, default=keg_sizes.HALF_BARREL) + full_volume_ml = serializers.FloatField(required=False, allow_null=True, default=None) + + def validate(self, data): + if not data.get("beverage") and not data.get("beverage_name"): + raise ValidationError( + "Give either beverage_id, or beverage_name with " + "producer_name/style_name/beverage_type." + ) + return data + + +class KegCreateRequestSerializer(NewKegRequestSerializer): + description = serializers.CharField(required=False, allow_blank=True, default="") + notes = serializers.CharField(required=False, allow_blank=True, default="") + + +class TapConnectMeterRequestSerializer(serializers.Serializer): + meter_id = serializers.PrimaryKeyRelatedField( + queryset=models.FlowMeter.objects.all(), source="meter", allow_null=True + ) + + +class TapConnectToggleRequestSerializer(serializers.Serializer): + toggle_id = serializers.PrimaryKeyRelatedField( + queryset=models.FlowToggle.objects.all(), source="toggle", allow_null=True + ) + + +class TapConnectThermoRequestSerializer(serializers.Serializer): + thermo_sensor_id = serializers.PrimaryKeyRelatedField( + queryset=models.ThermoSensor.objects.all(), source="thermo_sensor", allow_null=True + ) + + +class TapRecordDrinkRequestSerializer(serializers.Serializer): + volume_ml = serializers.FloatField(min_value=0.0) + username = serializers.CharField(required=False, allow_blank=True, default="") + pour_time = serializers.DateTimeField(required=False, allow_null=True, default=None) + duration = serializers.IntegerField(required=False, min_value=0, default=0) + shout = serializers.CharField(required=False, allow_blank=True, default="") + spilled = serializers.BooleanField(default=False) + + def validate_username(self, value): + if value and not models.User.objects.filter(username=value).exists(): + raise ValidationError("No such user.") + return value + + +class KegSpillRequestSerializer(serializers.Serializer): + volume_ml = serializers.FloatField(min_value=0.0) + + +class DrinkUpdateRequestSerializer(serializers.Serializer): + shout = serializers.CharField(required=False, allow_blank=True) + volume_ml = serializers.FloatField(required=False, min_value=0.0) + + +class DrinkReassignRequestSerializer(serializers.Serializer): + username = serializers.CharField() + + def validate_username(self, value): + if not models.User.objects.filter(username=value).exists(): + raise ValidationError("No such user.") + return value + + +class PictureUploadRequestSerializer(serializers.Serializer): + image = serializers.ImageField() + caption = serializers.CharField(required=False, allow_blank=True, default="") + + +class ProfileUpdateRequestSerializer(serializers.Serializer): + display_name = serializers.CharField(required=False, allow_blank=True, max_length=127) + + +class PasswordChangeRequestSerializer(serializers.Serializer): + current_password = serializers.CharField() + new_password = serializers.CharField(min_length=1) + + +class EmailChangeRequestSerializer(serializers.Serializer): + email = serializers.EmailField() + + +class ConfirmEmailRequestSerializer(serializers.Serializer): + token = serializers.CharField() + + +class ActivateAccountRequestSerializer(serializers.Serializer): + activation_key = serializers.CharField() + password = serializers.CharField(min_length=1) + + +class RegisterRequestSerializer(serializers.Serializer): + username = serializers.RegexField(regex=kb_common.USERNAME_REGEX, max_length=30) + email = serializers.EmailField() + password = serializers.CharField(min_length=1) + invite_code = serializers.CharField(required=False, allow_blank=True, default="") + + +class PasswordResetRequestSerializer(serializers.Serializer): + email = serializers.EmailField() + + +class PasswordResetConfirmRequestSerializer(serializers.Serializer): + uid = serializers.CharField() + token = serializers.CharField() + new_password = serializers.CharField(min_length=1) + + +class AdminUserCreateRequestSerializer(serializers.Serializer): + username = serializers.RegexField(regex=kb_common.USERNAME_REGEX, max_length=30) + email = serializers.EmailField(required=False, allow_blank=True, default="") + password = serializers.CharField(min_length=1) + is_staff = serializers.BooleanField(default=False) + + +class AdminUserUpdateRequestSerializer(serializers.Serializer): + email = serializers.EmailField(required=False, allow_blank=True) + display_name = serializers.CharField(required=False, allow_blank=True, max_length=127) + is_staff = serializers.BooleanField(required=False) + is_active = serializers.BooleanField(required=False) + + +class SetPasswordRequestSerializer(serializers.Serializer): + password = serializers.CharField(min_length=1) + + +class SetupStatusSerializer(serializers.Serializer): + need_setup = serializers.BooleanField() + need_upgrade = serializers.BooleanField() + installed_version = serializers.CharField(allow_null=True) + current_version = serializers.CharField() + + +class SetupSiteSettingsRequestSerializer(serializers.ModelSerializer): + """The subset of site settings collected during the setup wizard.""" + + class Meta: + model = models.KegbotSite + fields = [ + "title", + "privacy", + "timezone", + "volume_display_units", + "temperature_display_units", + "enable_sensing", + "enable_users", + ] + + +class SetupAdminUserRequestSerializer(serializers.Serializer): + username = serializers.RegexField(regex=kb_common.USERNAME_REGEX, max_length=30) + email = serializers.EmailField() + password = serializers.CharField(min_length=1) + + +class AdminDashboardSerializer(serializers.Serializer): + email_configured = serializers.BooleanField() + redis_error = serializers.CharField(allow_null=True) + num_users = serializers.IntegerField() + num_new_users = serializers.IntegerField() + + +class EmailTestRequestSerializer(serializers.Serializer): + address = serializers.EmailField() + + +class SiteSettingsSerializer(serializers.ModelSerializer): + """Admin-editable site settings, covering the old settings forms.""" + + class Meta: + model = models.KegbotSite + fields = [ + "name", + "server_version", + "is_setup", + "title", + "privacy", + "registration_mode", + "enable_sensing", + "enable_users", + "volume_display_units", + "temperature_display_units", + "timezone", + "session_timeout_minutes", + "google_analytics_id", + "email_config", + "background_image", + ] + + background_image = PictureSerializer(read_only=True) + + class LoginSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField() @@ -421,3 +679,54 @@ class SystemStatusSerializer(serializers.Serializer): site = KegbotSiteSerializer() taps = KegTapSerializer(many=True) events = SystemEventSerializer(many=True) + + +class PluginInfoSerializer(serializers.Serializer): + short_name = serializers.CharField() + name = serializers.CharField() + + +class SiteConfigSerializer(serializers.ModelSerializer): + """The privacy-safe subset of site settings, embedded in the boot payload. + + Unlike `KegbotSiteSerializer`, this contains no data derived from pours + (no stats): it is served to anonymous users regardless of site privacy, + since the frontend needs it to render the login and interstitial screens. + """ + + class Meta: + model = models.KegbotSite + fields = [ + "server_version", + "title", + "privacy", + "registration_mode", + "volume_display_units", + "temperature_display_units", + "timezone", + "session_timeout_minutes", + "enable_sensing", + "enable_users", + "google_analytics_id", + "background_image", + ] + + background_image = PictureSerializer(read_only=True) + + +class MeSerializer(serializers.Serializer): + """The boot payload: current user plus always-needed site metadata. + + Served to every caller with status 200; `user` is null when the caller + is not authenticated. Static constants (choice lists, keg sizes, and + similar) are NOT served here: they are baked into the frontend build + via the `print_constants` management command. + """ + + user = CurrentUserSerializer(allow_null=True) + site = SiteConfigSerializer() + can_invite = serializers.BooleanField() + have_sessions = serializers.BooleanField() + sso_login_url = serializers.CharField(allow_blank=True) + sso_logout_url = serializers.CharField(allow_blank=True) + plugins = PluginInfoSerializer(many=True) diff --git a/pykeg/api/tests.py b/pykeg/api/tests.py index 2b15ac6a8..f1cf8d32c 100644 --- a/pykeg/api/tests.py +++ b/pykeg/api/tests.py @@ -1,6 +1,15 @@ import base64 +import datetime +import re +from django.contrib.auth.tokens import default_token_generator +from django.core import mail as django_mail +from django.core.cache import cache from django.test import TestCase +from django.test.utils import override_settings +from django.utils import timezone +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode from rest_framework.test import APIClient from pykeg.core import models @@ -129,13 +138,26 @@ def setUp(self): self.member = models.User.objects.get(username="alice") self.member_key = models.ApiKey.objects.get_or_create(user=self.member)[0] - def test_users_are_read_only(self): + def test_users_are_not_editable_by_members(self): self.client.api_key = self.member_key.key self.client.add_auth() response = self.client.client.patch( - f"/api/users/{self.member.id}", {"display_name": "hax"}, format="json" + f"/api/users/{self.member.username}", {"display_name": "hax"}, format="json" ) - self.assertEqual(405, response.status_code) + self.assertEqual(403, response.status_code) + + def test_user_detail_is_looked_up_by_username(self): + status, data = self.client.get(f"/api/users/{self.member.username}") + self.assertEqual(200, status) + self.assertEqual(self.member.id, data["id"]) + + def test_user_list_requires_authentication(self): + status, _ = self.client.get("/api/users") + self.assertEqual(403, status) + + self.client.api_key = self.member_key.key + status, _ = self.client.get("/api/users") + self.assertEqual(200, status) def test_plugin_data_requires_admin(self): self.client.api_key = self.member_key.key @@ -157,6 +179,1158 @@ def test_notification_settings_are_scoped_to_caller(self): self.assertEqual([], data["results"]) +class FilteringTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.admin = models.User.objects.filter(is_staff=True).first() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + + def test_drinks_filtered_by_username(self): + drink = models.Drink.objects.exclude(user__isnull=True).first() + username = drink.user.username + expected = models.Drink.objects.filter(user__username=username).count() + + status, data = self.client.get(f"/api/drinks?username={username}&page_size=100") + self.assertEqual(200, status) + self.assertEqual(expected, len(data["results"])) + + status, data = self.client.get("/api/drinks?username=no-such-user") + self.assertEqual(200, status) + self.assertEqual([], data["results"]) + + def test_drinks_filtered_by_keg_and_session(self): + drink = models.Drink.objects.exclude(session__isnull=True).first() + + status, data = self.client.get(f"/api/drinks?keg={drink.keg_id}&page_size=100") + self.assertEqual(200, status) + self.assertGreater(len(data["results"]), 0) + self.assertTrue(all(d["keg"]["id"] == drink.keg_id for d in data["results"])) + + status, data = self.client.get(f"/api/drinks?session={drink.session_id}&page_size=100") + self.assertEqual(200, status) + self.assertGreater(len(data["results"]), 0) + self.assertTrue(all(d["session_id"] == drink.session_id for d in data["results"])) + + def test_kegs_filtered_by_status(self): + expected = models.Keg.objects.filter(status=models.Keg.STATUS_ON_TAP).count() + status, data = self.client.get("/api/kegs?status=on_tap&page_size=100") + self.assertEqual(200, status) + self.assertEqual(expected, len(data["results"])) + + def test_sessions_filtered_by_date(self): + session = models.DrinkingSession.objects.first() + dt = session.start_time + status, data = self.client.get( + f"/api/sessions?year={dt.year}&month={dt.month}&page_size=100" + ) + self.assertEqual(200, status) + self.assertIn(session.id, [s["id"] for s in data["results"]]) + + status, data = self.client.get("/api/sessions?year=1999") + self.assertEqual(200, status) + self.assertEqual([], data["results"]) + + def test_events_filtered_by_since(self): + max_id = models.SystemEvent.objects.latest("id").id + status, data = self.client.get(f"/api/events?since={max_id}") + self.assertEqual(200, status) + self.assertEqual([], data["results"]) + + status, data = self.client.get(f"/api/events?since={max_id - 2}") + self.assertEqual(200, status) + self.assertEqual(2, len(data["results"])) + + def test_users_search(self): + self.client.api_key = self.admin_key.key + status, data = self.client.get("/api/users?search=alic&page_size=100") + self.assertEqual(200, status) + self.assertEqual(["alice"], [u["username"] for u in data["results"]]) + + def test_page_size_is_honored_and_capped(self): + total = models.Drink.objects.count() + self.assertGreater(total, 10) + + status, data = self.client.get("/api/drinks?page_size=100") + self.assertEqual(200, status) + self.assertEqual(total, len(data["results"])) + + status, data = self.client.get("/api/drinks?page_size=3") + self.assertEqual(200, status) + self.assertEqual(3, len(data["results"])) + + +class CurrentSessionTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + + def test_no_active_session_returns_404(self): + # Fixture sessions are long in the past. + status, _ = self.client.get("/api/sessions/current") + self.assertEqual(404, status) + + def test_directory_enumerates_session_dates(self): + status, data = self.client.get("/api/sessions/directory") + self.assertEqual(200, status) + years = data["years"] + self.assertGreater(len(years), 0) + + total = 0 + for year in years: + self.assertGreater(len(year["months"]), 0) + year_total = 0 + for month in year["months"]: + self.assertGreater(len(month["days"]), 0) + self.assertEqual(sorted(month["days"], reverse=True), month["days"]) + year_total += month["count"] + self.assertEqual(year_total, year["count"]) + total += year["count"] + self.assertEqual(models.DrinkingSession.objects.count(), total) + + # Every directory bucket matches its filtered listing. + first_year = years[0] + first_month = first_year["months"][0] + status, listing = self.client.get( + f"/api/sessions?year={first_year['year']}&month={first_month['month']}&page_size=100" + ) + self.assertEqual(200, status) + self.assertEqual(first_month["count"], len(listing["results"])) + + def test_active_session_is_returned(self): + session = models.DrinkingSession.objects.latest() + session.start_time = timezone.now() - datetime.timedelta(minutes=10) + session.end_time = timezone.now() + datetime.timedelta(minutes=10) + session.save() + + status, data = self.client.get("/api/sessions/current") + self.assertEqual(200, status) + self.assertEqual(session.id, data["id"]) + + +class KegTapOperationsTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.admin = models.User.objects.get(username="admin") + self.admin.is_staff = True + self.admin.save() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + self.member = models.User.objects.get(username="alice") + self.member_key = models.ApiKey.objects.get_or_create(user=self.member)[0] + self.tap = models.KegTap.objects.get(name="Main Tap") + + def post(self, path, data=None, key=None): + self.client.api_key = key or self.admin_key.key + self.client.add_auth() + return self.client.client.post(path, data or {}, format="json") + + def test_tap_operations_require_admin(self): + for path in ( + f"/api/taps/{self.tap.id}/end-keg", + f"/api/taps/{self.tap.id}/attach-keg", + "/api/taps", + ): + response = self.post(path, key=self.member_key.key) + self.assertEqual(403, response.status_code, path) + + def test_end_and_attach_keg(self): + keg = self.tap.current_keg + response = self.post(f"/api/taps/{self.tap.id}/end-keg") + self.assertEqual(200, response.status_code) + self.assertIsNone(response.json()["current_keg"]) + keg.refresh_from_db() + self.assertEqual(models.Keg.STATUS_FINISHED, keg.status) + + response = self.post(f"/api/taps/{self.tap.id}/end-keg") + self.assertEqual(400, response.status_code) + + response = self.post(f"/api/taps/{self.tap.id}/attach-keg", {"keg_id": keg.id}) + self.assertEqual(200, response.status_code) + self.assertEqual(keg.id, response.json()["current_keg"]["id"]) + keg.refresh_from_db() + self.assertEqual(models.Keg.STATUS_ON_TAP, keg.status) + + def test_attach_fails_when_tap_active(self): + other_keg = models.Keg.objects.exclude(id=self.tap.current_keg_id).first() + response = self.post(f"/api/taps/{self.tap.id}/attach-keg", {"keg_id": other_keg.id}) + self.assertEqual(400, response.status_code) + + def test_start_keg_creates_and_attaches(self): + self.post(f"/api/taps/{self.tap.id}/end-keg") + response = self.post( + f"/api/taps/{self.tap.id}/start-keg", + { + "beverage_name": "Test Brew", + "producer_name": "Test Brewery", + "style_name": "IPA", + "keg_type": "half-barrel", + }, + ) + self.assertEqual(200, response.status_code) + data = response.json() + self.assertEqual("Test Brew", data["current_keg"]["beverage"]["name"]) + self.assertEqual("on_tap", data["current_keg"]["status"]) + + def test_start_keg_requires_beverage(self): + self.post(f"/api/taps/{self.tap.id}/end-keg") + response = self.post(f"/api/taps/{self.tap.id}/start-keg", {}) + self.assertEqual(400, response.status_code) + + def test_record_drink_and_spill(self): + keg = self.tap.current_keg + served_before = keg.served_volume_ml + + response = self.post( + f"/api/taps/{self.tap.id}/record-drink", + {"volume_ml": 400.0, "username": "alice", "shout": "cheers!"}, + ) + self.assertEqual(201, response.status_code) + data = response.json() + self.assertEqual("alice", data["user"]["username"]) + self.assertEqual("cheers!", data["shout"]) + keg.refresh_from_db() + self.assertEqual(served_before + 400.0, keg.served_volume_ml) + + spilled_before = keg.spilled_ml + response = self.post( + f"/api/taps/{self.tap.id}/record-drink", + {"volume_ml": 100.0, "spilled": True}, + ) + self.assertEqual(204, response.status_code) + keg.refresh_from_db() + self.assertEqual(spilled_before + 100.0, keg.spilled_ml) + + def test_record_drink_unknown_user(self): + response = self.post( + f"/api/taps/{self.tap.id}/record-drink", + {"volume_ml": 100.0, "username": "nobody"}, + ) + self.assertEqual(400, response.status_code) + + def test_connect_meter(self): + other_meter = models.FlowMeter.objects.exclude(tap=self.tap).first() + response = self.post(f"/api/taps/{self.tap.id}/connect-meter", {"meter_id": other_meter.id}) + self.assertEqual(200, response.status_code) + other_meter.refresh_from_db() + self.assertEqual(self.tap, other_meter.tap) + + response = self.post(f"/api/taps/{self.tap.id}/connect-meter", {"meter_id": None}) + self.assertEqual(200, response.status_code) + other_meter.refresh_from_db() + self.assertIsNone(other_meter.tap) + + def test_tap_crud(self): + response = self.post("/api/taps", {"name": "Third Tap"}) + self.assertEqual(201, response.status_code) + tap_id = response.json()["id"] + + self.client.add_auth() + response = self.client.client.patch( + f"/api/taps/{tap_id}", {"name": "Renamed Tap"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual("Renamed Tap", response.json()["name"]) + + response = self.client.client.delete(f"/api/taps/{tap_id}") + self.assertEqual(204, response.status_code) + self.assertFalse(models.KegTap.objects.filter(id=tap_id).exists()) + + +class KegOperationsTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.admin = models.User.objects.get(username="admin") + self.admin.is_staff = True + self.admin.save() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + + def post(self, path, data=None): + self.client.api_key = self.admin_key.key + self.client.add_auth() + return self.client.client.post(path, data or {}, format="json") + + def test_create_keg_with_existing_beverage(self): + beverage = models.Beverage.objects.first() + response = self.post( + "/api/kegs", + {"beverage_id": beverage.id, "keg_type": "corny", "description": "spare"}, + ) + self.assertEqual(201, response.status_code) + data = response.json() + self.assertEqual("available", data["status"]) + self.assertEqual(beverage.name, data["beverage"]["name"]) + self.assertEqual("spare", data["description"]) + + def test_create_keg_with_new_beverage(self): + response = self.post( + "/api/kegs", + { + "beverage_name": "New Beer", + "producer_name": "New Brewery", + "style_name": "Stout", + }, + ) + self.assertEqual(201, response.status_code) + self.assertEqual("New Beer", response.json()["beverage"]["name"]) + + def test_end_and_reactivate(self): + keg = models.Keg.objects.create( + type=models.Beverage.objects.first(), status=models.Keg.STATUS_AVAILABLE + ) + response = self.post(f"/api/kegs/{keg.id}/end") + self.assertEqual(200, response.status_code) + self.assertEqual("finished", response.json()["status"]) + + response = self.post(f"/api/kegs/{keg.id}/reactivate") + self.assertEqual(200, response.status_code) + self.assertEqual("available", response.json()["status"]) + + # Reactivate requires a finished keg. + response = self.post(f"/api/kegs/{keg.id}/reactivate") + self.assertEqual(400, response.status_code) + + def test_end_fails_while_on_tap(self): + keg = models.KegTap.objects.get(name="Main Tap").current_keg + response = self.post(f"/api/kegs/{keg.id}/end") + self.assertEqual(400, response.status_code) + + def test_spill(self): + keg = models.Keg.objects.first() + before = keg.spilled_ml + response = self.post(f"/api/kegs/{keg.id}/spill", {"volume_ml": 250.0}) + self.assertEqual(200, response.status_code) + keg.refresh_from_db() + self.assertEqual(before + 250.0, keg.spilled_ml) + + def test_edit_keg_notes_but_not_status(self): + keg = models.Keg.objects.first() + self.client.api_key = self.admin_key.key + self.client.add_auth() + response = self.client.client.patch( + f"/api/kegs/{keg.id}", + {"notes": "updated", "status": "finished"}, + format="json", + ) + self.assertEqual(200, response.status_code) + keg.refresh_from_db() + self.assertEqual("updated", keg.notes) + # Status is read-only on direct edits. + self.assertEqual(models.Keg.STATUS_ON_TAP, keg.status) + + def test_delete_keg_destroys_drinks(self): + keg = models.KegTap.objects.get(name="Main Tap").current_keg + self.assertTrue(keg.drinks.exists()) + self.client.api_key = self.admin_key.key + self.client.add_auth() + response = self.client.client.delete(f"/api/kegs/{keg.id}") + self.assertEqual(204, response.status_code) + self.assertFalse(models.Keg.objects.filter(id=keg.id).exists()) + self.assertFalse(models.Drink.objects.filter(keg_id=keg.id).exists()) + + +TINY_GIF = base64.b64decode("R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==") + + +class DrinkManagementTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.admin = models.User.objects.get(username="admin") + self.admin.is_staff = True + self.admin.save() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + self.alice = models.User.objects.get(username="alice") + self.alice_key = models.ApiKey.objects.get_or_create(user=self.alice)[0] + self.bob = models.User.objects.get(username="bob") + self.bob_key = models.ApiKey.objects.get_or_create(user=self.bob)[0] + self.alice_drink = models.Drink.objects.filter(user=self.alice).first() + + def as_user(self, key): + self.client.api_key = key + self.client.add_auth() + return self.client.client + + def test_owner_may_edit_shout(self): + response = self.as_user(self.alice_key.key).patch( + f"/api/drinks/{self.alice_drink.id}", {"shout": "tasty"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.alice_drink.refresh_from_db() + self.assertEqual("tasty", self.alice_drink.shout) + + def test_non_owner_may_not_edit_shout(self): + response = self.as_user(self.bob_key.key).patch( + f"/api/drinks/{self.alice_drink.id}", {"shout": "graffiti"}, format="json" + ) + self.assertEqual(403, response.status_code) + + def test_volume_adjustment_is_admin_only(self): + response = self.as_user(self.alice_key.key).patch( + f"/api/drinks/{self.alice_drink.id}", {"volume_ml": 9999.0}, format="json" + ) + self.assertEqual(403, response.status_code) + + keg = self.alice_drink.keg + served_before = keg.served_volume_ml + old_volume = self.alice_drink.volume_ml + response = self.as_user(self.admin_key.key).patch( + f"/api/drinks/{self.alice_drink.id}", {"volume_ml": old_volume + 10}, format="json" + ) + self.assertEqual(200, response.status_code) + keg.refresh_from_db() + self.assertEqual(served_before + 10, keg.served_volume_ml) + + def test_reassign(self): + response = self.as_user(self.alice_key.key).post( + f"/api/drinks/{self.alice_drink.id}/reassign", {"username": "bob"}, format="json" + ) + self.assertEqual(403, response.status_code) + + response = self.as_user(self.admin_key.key).post( + f"/api/drinks/{self.alice_drink.id}/reassign", {"username": "bob"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual("bob", response.json()["user"]["username"]) + + def test_destroy_is_admin_only_and_supports_spill(self): + response = self.as_user(self.alice_key.key).delete(f"/api/drinks/{self.alice_drink.id}") + self.assertEqual(403, response.status_code) + + keg = self.alice_drink.keg + spilled_before = keg.spilled_ml + volume = self.alice_drink.volume_ml + response = self.as_user(self.admin_key.key).delete( + f"/api/drinks/{self.alice_drink.id}?spilled=true" + ) + self.assertEqual(204, response.status_code) + self.assertFalse(models.Drink.objects.filter(id=self.alice_drink.id).exists()) + keg.refresh_from_db() + self.assertEqual(spilled_before + volume, keg.spilled_ml) + + def test_picture_upload_and_delete(self): + from django.core.files.uploadedfile import SimpleUploadedFile + + image = SimpleUploadedFile("pour.gif", TINY_GIF, content_type="image/gif") + response = self.as_user(self.alice_key.key).post( + f"/api/drinks/{self.alice_drink.id}/picture", {"image": image}, format="multipart" + ) + self.assertEqual(200, response.status_code) + self.assertIsNotNone(response.json()["picture"]) + self.alice_drink.refresh_from_db() + self.assertIsNotNone(self.alice_drink.picture) + + response = self.as_user(self.bob_key.key).delete( + f"/api/drinks/{self.alice_drink.id}/picture" + ) + self.assertEqual(403, response.status_code) + + response = self.as_user(self.alice_key.key).delete( + f"/api/drinks/{self.alice_drink.id}/picture" + ) + self.assertEqual(204, response.status_code) + self.alice_drink.refresh_from_db() + self.assertIsNone(self.alice_drink.picture) + + def test_beverage_picture_upload(self): + from django.core.files.uploadedfile import SimpleUploadedFile + + beverage = models.Beverage.objects.first() + image = SimpleUploadedFile("label.gif", TINY_GIF, content_type="image/gif") + response = self.as_user(self.alice_key.key).post( + f"/api/beverages/{beverage.id}/picture", {"image": image}, format="multipart" + ) + self.assertEqual(403, response.status_code) + + image = SimpleUploadedFile("label.gif", TINY_GIF, content_type="image/gif") + response = self.as_user(self.admin_key.key).post( + f"/api/beverages/{beverage.id}/picture", {"image": image}, format="multipart" + ) + self.assertEqual(200, response.status_code) + self.assertIsNotNone(response.json()["picture"]) + + +class StatsEndpointsTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + + def test_system_stats(self): + status, data = self.client.get("/api/stats/system") + self.assertEqual(200, status) + self.assertEqual(models.Drink.objects.count(), data["total_pours"]) + # Drinker keys must be usernames, not numeric user ids. + for name in data["volume_by_drinker"]: + self.assertFalse(name.isdigit(), name) + # Weekday keys are strftime("%w") strings; the frontend relies on this. + for key in data["volume_by_day_of_week"]: + self.assertIn(key, {"0", "1", "2", "3", "4", "5", "6"}) + + def test_user_stats(self): + drink = models.Drink.objects.exclude(user__isnull=True).first() + username = drink.user.username + expected = models.Drink.objects.filter(user__username=username).count() + status, data = self.client.get(f"/api/users/{username}/stats") + self.assertEqual(200, status) + self.assertEqual(expected, data["total_pours"]) + + def test_keg_stats(self): + keg = models.Keg.objects.first() + status, data = self.client.get(f"/api/kegs/{keg.id}/stats") + self.assertEqual(200, status) + self.assertEqual(models.Drink.objects.filter(keg=keg).count(), data["total_pours"]) + + def test_session_stats(self): + session = models.DrinkingSession.objects.first() + status, data = self.client.get(f"/api/sessions/{session.id}/stats") + self.assertEqual(200, status) + self.assertEqual(models.Drink.objects.filter(session=session).count(), data["total_pours"]) + + def test_user_stats_respect_site_privacy(self): + self.site.privacy = models.KegbotSite.PRIVACY_CHOICE_MEMBERS + self.site.save() + username = models.Drink.objects.exclude(user__isnull=True).first().user.username + status, _ = self.client.get(f"/api/users/{username}/stats") + self.assertEqual(403, status) + + +@override_settings(EMAIL_BACKEND="pykeg.core.mail.KegbotEmailBackend") +class AccountFlowsTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + cache.clear() # Reset auth-endpoint throttle state between tests. + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.email_config = "memory://?_default_from_email=test@example.com" + self.site.save() + self.alice = models.User.objects.get(username="alice") + self.alice.set_password("oldpassword") + self.alice.email = "alice@example.com" + self.alice.save() + self.alice_key = models.ApiKey.objects.get_or_create(user=self.alice)[0] + + def as_alice(self): + self.client.api_key = self.alice_key.key + self.client.add_auth() + return self.client.client + + def as_anon(self): + self.client.api_key = None + self.client.add_auth() + return self.client.client + + def test_update_profile(self): + response = self.as_alice().patch( + "/api/users/me", {"display_name": "Alice A."}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual("Alice A.", response.json()["user"]["display_name"]) + self.alice.refresh_from_db() + self.assertEqual("Alice A.", self.alice.display_name) + + def test_update_profile_requires_auth(self): + response = self.as_anon().patch("/api/users/me", {"display_name": "Nope"}, format="json") + self.assertIn(response.status_code, (401, 403)) + + def test_change_password(self): + response = self.as_alice().post( + "/api/account/password", + {"current_password": "wrong", "new_password": "newpassword"}, + format="json", + ) + self.assertEqual(400, response.status_code) + + response = self.as_alice().post( + "/api/account/password", + {"current_password": "oldpassword", "new_password": "newpassword"}, + format="json", + ) + self.assertEqual(200, response.status_code) + self.alice.refresh_from_db() + self.assertTrue(self.alice.check_password("newpassword")) + + def test_change_email_and_confirm(self): + response = self.as_alice().post( + "/api/account/email", {"email": "alice-new@example.com"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(django_mail.outbox)) + body = django_mail.outbox[0].body + match = re.search(r"/account/confirm-email/(\S+)", body) + self.assertIsNotNone(match, body) + token = match.group(1).rstrip("/") + + response = self.as_alice().post( + "/api/account/confirm-email", {"token": token}, format="json" + ) + self.assertEqual(200, response.status_code) + self.alice.refresh_from_db() + self.assertEqual("alice-new@example.com", self.alice.email) + + def test_mugshot_upload(self): + from django.core.files.uploadedfile import SimpleUploadedFile + + image = SimpleUploadedFile("me.gif", TINY_GIF, content_type="image/gif") + response = self.as_alice().post( + "/api/account/mugshot", {"image": image}, format="multipart" + ) + self.assertEqual(200, response.status_code) + self.assertIsNotNone(response.json()["picture"]) + self.alice.refresh_from_db() + self.assertIsNotNone(self.alice.mugshot) + + def test_regenerate_api_key(self): + old_key = self.alice_key.key + response = self.as_alice().post("/api/account/regenerate-api-key") + self.assertEqual(200, response.status_code) + self.assertNotEqual(old_key, response.json()["key"]) + + def test_register_public(self): + response = self.as_anon().post( + "/api/auth/register", + {"username": "newuser", "email": "new@example.com", "password": "s3cret"}, + format="json", + ) + self.assertEqual(201, response.status_code) + self.assertEqual("newuser", response.json()["username"]) + user = models.User.objects.get(username="newuser") + self.assertTrue(user.check_password("s3cret")) + + def test_register_duplicate_username(self): + response = self.as_anon().post( + "/api/auth/register", + {"username": "alice", "email": "x@example.com", "password": "pw"}, + format="json", + ) + self.assertEqual(400, response.status_code) + + def test_register_invite_only(self): + self.site.registration_mode = "staff-invite-only" + self.site.save() + + response = self.as_anon().post( + "/api/auth/register", + {"username": "invitee", "email": "i@example.com", "password": "pw"}, + format="json", + ) + self.assertEqual(403, response.status_code) + + invite = models.Invitation.objects.create(for_email="i@example.com", invited_by=self.alice) + response = self.as_anon().post( + "/api/auth/register", + { + "username": "invitee", + "email": "i@example.com", + "password": "pw", + "invite_code": invite.invite_code, + }, + format="json", + ) + self.assertEqual(201, response.status_code) + self.assertFalse(models.Invitation.objects.filter(id=invite.id).exists()) + + def test_register_bad_invite_code(self): + self.site.registration_mode = "staff-invite-only" + self.site.save() + response = self.as_anon().post( + "/api/auth/register", + { + "username": "invitee", + "email": "i@example.com", + "password": "pw", + "invite_code": "bogus", + }, + format="json", + ) + self.assertEqual(403, response.status_code) + + def test_password_reset_sends_mail(self): + response = self.as_anon().post( + "/api/auth/password-reset", {"email": "alice@example.com"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(django_mail.outbox)) + + django_mail.outbox.clear() + response = self.as_anon().post( + "/api/auth/password-reset", {"email": "nobody@example.com"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual(0, len(django_mail.outbox)) + + def test_password_reset_confirm(self): + uid = urlsafe_base64_encode(force_bytes(self.alice.pk)) + token = default_token_generator.make_token(self.alice) + + response = self.as_anon().post( + "/api/auth/password-reset-confirm", + {"uid": uid, "token": "bad-token", "new_password": "resetpw"}, + format="json", + ) + self.assertEqual(400, response.status_code) + + response = self.as_anon().post( + "/api/auth/password-reset-confirm", + {"uid": uid, "token": token, "new_password": "resetpw"}, + format="json", + ) + self.assertEqual(200, response.status_code) + self.alice.refresh_from_db() + self.assertTrue(self.alice.check_password("resetpw")) + + def test_activate_account(self): + user = models.User.objects.create(username="pending", email="p@example.com") + user.set_unusable_password() + user.activation_key = "activation123" + user.save() + + response = self.as_anon().post( + "/api/account/activate", + {"activation_key": "activation123", "password": "mypw"}, + format="json", + ) + self.assertEqual(200, response.status_code) + self.assertEqual("pending", response.json()["username"]) + user.refresh_from_db() + self.assertIsNone(user.activation_key) + self.assertTrue(user.check_password("mypw")) + + # Key is single-use. + response = self.as_anon().post( + "/api/account/activate", + {"activation_key": "activation123", "password": "mypw"}, + format="json", + ) + self.assertEqual(404, response.status_code) + + def test_invitation_create_and_destroy(self): + response = self.as_alice().post( + "/api/invitations", {"for_email": "friend@example.com"}, format="json" + ) + self.assertEqual(201, response.status_code) + self.assertEqual(1, len(django_mail.outbox)) + invite_id = response.json()["id"] + + response = self.as_alice().delete(f"/api/invitations/{invite_id}") + self.assertEqual(204, response.status_code) + + def test_invitation_denied_when_not_allowed(self): + self.site.registration_mode = "staff-invite-only" + self.site.save() + response = self.as_alice().post( + "/api/invitations", {"for_email": "friend@example.com"}, format="json" + ) + self.assertEqual(403, response.status_code) + + +class AdminUsersAndSiteTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.admin = models.User.objects.get(username="admin") + self.admin.is_staff = True + self.admin.save() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + self.alice = models.User.objects.get(username="alice") + self.alice_key = models.ApiKey.objects.get_or_create(user=self.alice)[0] + + def as_admin(self): + self.client.api_key = self.admin_key.key + self.client.add_auth() + return self.client.client + + def as_member(self): + self.client.api_key = self.alice_key.key + self.client.add_auth() + return self.client.client + + def test_admin_creates_user(self): + response = self.as_member().post( + "/api/users", {"username": "nope", "password": "pw"}, format="json" + ) + self.assertEqual(403, response.status_code) + + response = self.as_admin().post( + "/api/users", + {"username": "staffer", "password": "pw", "is_staff": True}, + format="json", + ) + self.assertEqual(201, response.status_code) + user = models.User.objects.get(username="staffer") + self.assertTrue(user.is_staff) + self.assertTrue(user.check_password("pw")) + + def test_admin_edits_user(self): + response = self.as_admin().patch( + "/api/users/alice", {"is_active": False, "is_staff": True}, format="json" + ) + self.assertEqual(200, response.status_code) + self.alice.refresh_from_db() + self.assertFalse(self.alice.is_active) + self.assertTrue(self.alice.is_staff) + + def test_guest_cannot_be_disabled(self): + response = self.as_admin().patch("/api/users/guest", {"is_active": False}, format="json") + self.assertEqual(400, response.status_code) + + def test_admin_sets_password(self): + response = self.as_admin().post( + "/api/users/alice/set-password", {"password": "newpw"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.alice.refresh_from_db() + self.assertTrue(self.alice.check_password("newpw")) + + def test_site_settings_read_and_update(self): + response = self.as_member().get("/api/site") + self.assertEqual(403, response.status_code) + + response = self.as_admin().get("/api/site") + self.assertEqual(200, response.status_code) + self.assertEqual(self.site.title, response.json()["title"]) + + response = self.as_admin().patch( + "/api/site", + {"title": "Renamed Bar", "privacy": "members", "session_timeout_minutes": 30}, + format="json", + ) + self.assertEqual(200, response.status_code) + self.site.refresh_from_db() + self.assertEqual("Renamed Bar", self.site.title) + self.assertEqual("members", self.site.privacy) + self.assertEqual(30, self.site.session_timeout_minutes) + + def test_site_settings_rejects_bad_email_config(self): + response = self.as_admin().patch("/api/site", {"email_config": "bogus:"}, format="json") + self.assertEqual(400, response.status_code) + + def test_site_background_image(self): + from django.core.files.uploadedfile import SimpleUploadedFile + + image = SimpleUploadedFile("bg.gif", TINY_GIF, content_type="image/gif") + response = self.as_admin().post( + "/api/site/background-image", {"image": image}, format="multipart" + ) + self.assertEqual(200, response.status_code) + self.assertIsNotNone(response.json()["background_image"]) + self.site.refresh_from_db() + self.assertIsNotNone(self.site.background_image) + + def test_token_user_assignment(self): + response = self.as_admin().post( + "/api/auth-tokens", + {"auth_device": "core.rfid", "token_value": "deadbeef", "user": self.alice.id}, + format="json", + ) + self.assertEqual(201, response.status_code) + token = models.AuthenticationToken.objects.get( + auth_device="core.rfid", token_value="deadbeef" + ) + self.assertEqual(self.alice, token.user) + + response = self.as_admin().patch( + f"/api/auth-tokens/{token.id}", {"user": None}, format="json" + ) + self.assertEqual(200, response.status_code) + token.refresh_from_db() + self.assertIsNone(token.user) + + +@override_settings(EMAIL_BACKEND="pykeg.core.mail.KegbotEmailBackend") +class AdminOpsTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.email_config = "memory://?_default_from_email=test@example.com" + self.site.save() + self.admin = models.User.objects.get(username="admin") + self.admin.is_staff = True + self.admin.save() + self.admin_key = models.ApiKey.objects.get_or_create(user=self.admin)[0] + self.alice = models.User.objects.get(username="alice") + self.alice_key = models.ApiKey.objects.get_or_create(user=self.alice)[0] + + def as_admin(self): + self.client.api_key = self.admin_key.key + self.client.add_auth() + return self.client.client + + def test_ops_require_admin(self): + self.client.api_key = self.alice_key.key + for path in ("/api/admin/dashboard", "/api/admin/backups", "/api/admin/logs"): + status_code, _ = self.client.get(path) + self.assertEqual(403, status_code, path) + + def test_dashboard(self): + response = self.as_admin().get("/api/admin/dashboard") + self.assertEqual(200, response.status_code) + data = response.json() + expected_users = ( + models.User.objects.filter(is_active=True).exclude(username="guest").count() + ) + self.assertEqual(expected_users, data["num_users"]) + self.assertIn("email_configured", data) + self.assertIn("redis_error", data) + + def test_backups_list_empty(self): + response = self.as_admin().get("/api/admin/backups") + self.assertEqual(200, response.status_code) + self.assertEqual([], response.json()) + + def test_backup_build_is_enqueued(self): + from unittest import mock + + with mock.patch("pykeg.core.tasks.build_backup.delay") as delay: + response = self.as_admin().post("/api/admin/backups") + self.assertEqual(202, response.status_code) + delay.assert_called_once() + + def test_delete_unknown_backup(self): + response = self.as_admin().delete("/api/admin/backups/nope.zip") + self.assertEqual(404, response.status_code) + + def test_logs(self): + response = self.as_admin().get("/api/admin/logs") + self.assertEqual(200, response.status_code) + self.assertIn("logs", response.json()) + + def test_email_test(self): + response = self.as_admin().post( + "/api/admin/email-test", {"address": "check@example.com"}, format="json" + ) + self.assertEqual(200, response.status_code) + self.assertEqual(1, len(django_mail.outbox)) + self.assertEqual(["check@example.com"], django_mail.outbox[0].to) + + def test_plugin_list(self): + self.client.api_key = self.alice_key.key + status_code, _ = self.client.get("/api/admin/plugins") + self.assertEqual(403, status_code) + + response = self.as_admin().get("/api/admin/plugins") + self.assertEqual(200, response.status_code) + plugins = response.json() + self.assertEqual(["webhook"], [p["short_name"] for p in plugins]) + self.assertTrue(plugins[0]["has_settings"]) + + def test_plugin_settings_roundtrip(self): + response = self.as_admin().get("/api/admin/plugins/webhook/settings") + self.assertEqual(200, response.status_code) + # Unconfigured fields are still present, so the settings page + # has an input to fill in. + self.assertIn("webhook_urls", response.json()) + + response = self.as_admin().put( + "/api/admin/plugins/webhook/settings", + {"webhook_urls": "http://example.com/hook"}, + format="json", + ) + self.assertEqual(200, response.status_code) + self.assertEqual("http://example.com/hook", response.json()["webhook_urls"]) + + response = self.as_admin().get("/api/admin/plugins/webhook/settings") + self.assertEqual("http://example.com/hook", response.json()["webhook_urls"]) + + def test_unknown_plugin_settings(self): + response = self.as_admin().get("/api/admin/plugins/nope/settings") + self.assertEqual(404, response.status_code) + + +class MeEndpointTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + self.user = models.User.objects.get(username="alice") + self.api_key = models.ApiKey.objects.get_or_create(user=self.user)[0] + + def test_anonymous_caller_gets_null_user_even_on_private_site(self): + self.site.privacy = models.KegbotSite.PRIVACY_CHOICE_STAFF + self.site.save() + + status, data = self.client.get("/api/users/me") + self.assertEqual(200, status) + self.assertIsNone(data["user"]) + self.assertEqual("staff", data["site"]["privacy"]) + self.assertEqual(self.site.title, data["site"]["title"]) + # The boot payload must not leak pour-derived data. + self.assertNotIn("stats", data["site"]) + + def test_authenticated_caller_gets_user(self): + self.client.api_key = self.api_key.key + status, data = self.client.get("/api/users/me") + self.assertEqual(200, status) + self.assertEqual("alice", data["user"]["username"]) + + def test_metadata_fields_are_present(self): + status, data = self.client.get("/api/users/me") + self.assertEqual(200, status) + self.assertTrue(data["have_sessions"]) + self.assertIn("can_invite", data) + self.assertEqual( + ["webhook"], + [p["short_name"] for p in data["plugins"]], + ) + + def test_sets_csrf_cookie(self): + response = self.client.client.get("/api/users/me") + self.assertEqual(200, response.status_code) + self.assertIn("csrftoken", response.cookies) + + +class SetupGateTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.client = ApiClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + + def test_setup_required_returns_json(self): + self.site.is_setup = False + self.site.save() + status, data = self.client.get("/api/users/me") + self.assertEqual(403, status) + self.assertEqual({"error": "setup_required"}, data) + + def test_upgrade_required_returns_json(self): + self.site.server_version = "0.0.1" + self.site.save() + status, data = self.client.get("/api/status") + self.assertEqual(403, status) + self.assertEqual("upgrade_required", data["error"]) + self.assertEqual("0.0.1", data["installed_version"]) + + +class SetupApiTestCase(TestCase): + fixtures = ["testdata/demo-site.json"] + + def setUp(self): + self.api = APIClient() + self.site = models.KegbotSite.objects.all().first() + self.site.server_version = get_version() + self.site.save() + + def test_endpoints_closed_when_site_is_setup(self): + response = self.api.get("/api/setup/status") + self.assertEqual(403, response.status_code) + response = self.api.post("/api/setup/finish") + self.assertEqual(403, response.status_code) + + def test_setup_flow(self): + self.site.is_setup = False + self.site.save() + + response = self.api.get("/api/setup/status") + self.assertEqual(200, response.status_code) + data = response.json() + self.assertTrue(data["need_setup"]) + self.assertEqual(get_version(), data["current_version"]) + + response = self.api.post("/api/setup/migrate") + self.assertEqual(200, response.status_code) + + response = self.api.post( + "/api/setup/settings", + { + "title": "Fresh Bar", + "privacy": "members", + "timezone": "America/Los_Angeles", + "volume_display_units": "metric", + "enable_sensing": True, + "enable_users": False, + }, + format="json", + ) + self.assertEqual(200, response.status_code) + self.site.refresh_from_db() + self.assertEqual("Fresh Bar", self.site.title) + self.assertEqual("members", self.site.privacy) + self.assertFalse(self.site.enable_users) + + response = self.api.post( + "/api/setup/admin-user", + {"username": "root", "email": "root@example.com", "password": "adminpw"}, + format="json", + ) + self.assertEqual(201, response.status_code) + user = models.User.objects.get(username="root") + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + self.assertTrue(user.check_password("adminpw")) + + response = self.api.post("/api/setup/finish") + self.assertEqual(200, response.status_code) + self.site.refresh_from_db() + self.assertTrue(self.site.is_setup) + + # The wizard is closed once setup completes. + response = self.api.get("/api/setup/status") + self.assertEqual(403, response.status_code) + + def test_upgrade_flow(self): + self.site.server_version = "0.0.1" + self.site.save() + + response = self.api.get("/api/setup/status") + self.assertEqual(200, response.status_code) + data = response.json() + self.assertTrue(data["need_upgrade"]) + self.assertEqual("0.0.1", data["installed_version"]) + + response = self.api.post("/api/setup/upgrade") + self.assertEqual(200, response.status_code) + self.site.refresh_from_db() + self.assertEqual(get_version(), self.site.server_version) + + response = self.api.post("/api/setup/upgrade") + self.assertEqual(403, response.status_code) + + def test_settings_conflict_when_only_upgrade_needed(self): + self.site.server_version = "0.0.1" + self.site.save() + response = self.api.post("/api/setup/settings", {"title": "X"}, format="json") + self.assertEqual(409, response.status_code) + + class SchemaTestCase(TestCase): fixtures = ["testdata/demo-site.json"] diff --git a/pykeg/api/urls.py b/pykeg/api/urls.py index f7e395d1f..715eda58b 100644 --- a/pykeg/api/urls.py +++ b/pykeg/api/urls.py @@ -2,7 +2,7 @@ from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView from rest_framework import routers -from . import views +from . import views, views_account, views_admin, views_setup router = routers.DefaultRouter(trailing_slash=False) router.register("api-keys", views.ApiKeyViewSet) @@ -27,11 +27,37 @@ router.register("users", views.UserViewSet) urlpatterns = [ + # Must precede the router so it wins over the users/{pk} detail route. + path("users/me", views.me), path("", include(router.urls)), + path("admin/backups", views_admin.backups), + path("admin/backups/", views_admin.delete_backup), + path("admin/bugreport", views_admin.bugreport), + path("admin/dashboard", views_admin.dashboard), + path("admin/email-test", views_admin.email_test), + path("admin/logs", views_admin.logs), + path("admin/plugins", views_admin.plugins), + path("admin/plugins//settings", views_admin.plugin_settings), + path("account/activate", views_account.activate), + path("account/confirm-email", views_account.confirm_email), + path("account/email", views_account.change_email), + path("account/mugshot", views_account.mugshot), + path("account/password", views_account.change_password), + path("account/regenerate-api-key", views_account.regenerate_api_key), path("auth/api-auth/", include("rest_framework.urls", namespace="rest_framework")), - path("auth/current-user", views.current_user), path("auth/login", views.login), path("auth/logout", views.logout), + path("auth/password-reset", views_account.password_reset), + path("auth/password-reset-confirm", views_account.password_reset_confirm), + path("auth/register", views_account.register), + path("site", views.site_settings), + path("site/background-image", views.site_background_image), + path("setup/status", views_setup.setup_status), + path("setup/migrate", views_setup.migrate), + path("setup/settings", views_setup.site_settings), + path("setup/admin-user", views_setup.admin_user), + path("setup/finish", views_setup.finish), + path("setup/upgrade", views_setup.upgrade), path("status", views.system_status), path("schema", SpectacularAPIView.as_view(), name="api-schema"), path("docs", SpectacularSwaggerView.as_view(url_name="api-schema"), name="api-docs"), diff --git a/pykeg/api/views.py b/pykeg/api/views.py index e41dc8ff6..f7c6c4b05 100644 --- a/pykeg/api/views.py +++ b/pykeg/api/views.py @@ -1,34 +1,129 @@ +from collections import defaultdict + +from django.conf import settings from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout +from django.utils import timezone +from django.views.decorators.csrf import ensure_csrf_cookie from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema -from rest_framework import viewsets +from rest_framework import mixins, status, viewsets from rest_framework.decorators import ( + action, api_view, authentication_classes, + parser_classes, permission_classes, ) +from rest_framework.exceptions import ( + NotAuthenticated, + NotFound, + PermissionDenied, + ValidationError, +) +from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.response import Response from pykeg.core import models -from . import permissions, serializers +from . import filters, permissions, serializers -class UserViewSet(viewsets.ReadOnlyModelViewSet): +class UserViewSet( + mixins.CreateModelMixin, + mixins.RetrieveModelMixin, + mixins.ListModelMixin, + viewsets.GenericViewSet, +): """Lists all users in the system. - Read-only view for any authenticated caller; user management happens - in the admin dashboard. + Individual users (and their stats) are viewable by anyone the site + privacy setting admits, mirroring the public drinker pages; the full + user listing requires authentication. User management (create, edit, + enable/disable, staff status, set-password) is admin-only; there is + no delete — accounts are disabled instead. """ queryset = models.User.objects.all() serializer_class = serializers.UserSerializer permission_classes = [permissions.IsAuthenticated] - - -class InvitationViewSet(viewsets.ReadOnlyModelViewSet): - """Lists all of the *current user's* invitations.""" + filterset_class = filters.UserFilter + lookup_field = "username" + # Default lookup regex excludes ".", which usernames may contain. + lookup_value_regex = "[^/]+" + + def get_permissions(self): + if self.action in ("retrieve", "stats"): + return [permissions.DashboardViewer()] + if self.action in ("create", "partial_update", "set_password"): + return [permissions.IsAdminUser()] + return super().get_permissions() + + @extend_schema(responses=OpenApiTypes.OBJECT) + @action(detail=True) + def stats(self, request, username=None): + """Returns the latest stats blob for this user.""" + return Response(self.get_object().get_stats()) + + @extend_schema( + request=serializers.AdminUserCreateRequestSerializer, + responses=serializers.UserSerializer, + ) + def create(self, request, *args, **kwargs): + req = serializers.AdminUserCreateRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + if models.User.objects.filter(username=data["username"]).exists(): + raise ValidationError({"username": ["A user with that username already exists."]}) + user = models.User.create_new_user( + username=data["username"], email=data["email"], password=data["password"] + ) + if data["is_staff"]: + user.is_staff = True + user.save(update_fields=["is_staff"]) + return Response(self.get_serializer(user).data, status=status.HTTP_201_CREATED) + + @extend_schema( + request=serializers.AdminUserUpdateRequestSerializer, + responses=serializers.UserSerializer, + ) + def partial_update(self, request, username=None): + user = self.get_object() + req = serializers.AdminUserUpdateRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + if user.is_guest() and ("is_active" in data or "is_staff" in data): + raise ValidationError("The guest account cannot be disabled or made staff.") + for field in ("email", "display_name", "is_staff", "is_active"): + if field in data: + setattr(user, field, data[field]) + user.save() + return Response(self.get_serializer(user).data) + + @extend_schema(request=serializers.SetPasswordRequestSerializer, responses=OpenApiTypes.BOOL) + @action(detail=True, methods=["post"], url_path="set-password") + def set_password(self, request, username=None): + """Sets a new password for this user.""" + user = self.get_object() + req = serializers.SetPasswordRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + user.set_password(req.validated_data["password"]) + user.save() + return Response(True) + + +class InvitationViewSet( + mixins.CreateModelMixin, + mixins.RetrieveModelMixin, + mixins.ListModelMixin, + mixins.DestroyModelMixin, + viewsets.GenericViewSet, +): + """Lists all of the *current user's* invitations. + + Creating an invitation (when the site's registration mode allows the + caller to invite) also sends the invitation e-mail. + """ queryset = models.Invitation.objects.all() serializer_class = serializers.InvitationSerializer @@ -44,6 +139,13 @@ def get_queryset(self): ) ) + def perform_create(self, serializer): + site = getattr(self.request, "kbsite", None) or models.KegbotSite.get() + if not site.can_invite(self.request.user): + raise PermissionDenied("You may not send invitations.") + invitation = serializer.save(invited_by=self.request.user) + invitation.send() + class DeviceViewSet(viewsets.ModelViewSet): """Lists all devices in the system. @@ -77,7 +179,29 @@ def get_queryset(self): ) -class BeverageProducerViewSet(viewsets.ModelViewSet): +class PictureAttachMixin: + """Adds a POST {id}/picture action that sets the object's picture.""" + + @extend_schema(request=serializers.PictureUploadRequestSerializer) + @action( + detail=True, + methods=["post"], + parser_classes=[MultiPartParser, FormParser], + ) + def picture(self, request, pk=None): + """Uploads and sets this object's picture.""" + obj = self.get_object() + req = serializers.PictureUploadRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + obj.picture = models.Picture.objects.create( + image=req.validated_data["image"], + caption=req.validated_data["caption"], + ) + obj.save(update_fields=["picture"]) + return Response(self.get_serializer(obj).data) + + +class BeverageProducerViewSet(PictureAttachMixin, viewsets.ModelViewSet): """Lists all beverage producers in the system.""" queryset = models.BeverageProducer.objects.all() @@ -85,7 +209,7 @@ class BeverageProducerViewSet(viewsets.ModelViewSet): permission_classes = [permissions.AdminWriteDashboardRead] -class BeverageViewSet(viewsets.ModelViewSet): +class BeverageViewSet(PictureAttachMixin, viewsets.ModelViewSet): """Lists all beverages in the system.""" queryset = models.Beverage.objects.all() @@ -93,12 +217,146 @@ class BeverageViewSet(viewsets.ModelViewSet): permission_classes = [permissions.AdminWriteDashboardRead] -class KegTapViewSet(viewsets.ReadOnlyModelViewSet): - """Lists all KegTaps in the system.""" +class KegTapViewSet(viewsets.ModelViewSet): + """Lists all KegTaps in the system. + + Reads follow site privacy; tap management (including the keg and + hardware-connection operations below) requires an admin. + """ queryset = models.KegTap.objects.all() serializer_class = serializers.KegTapSerializer - permission_classes = [permissions.DashboardViewer] + permission_classes = [permissions.AdminWriteDashboardRead] + + def _tap_response(self, tap): + tap.refresh_from_db() + return Response(self.get_serializer(tap).data) + + @extend_schema( + request=serializers.TapAttachKegRequestSerializer, + responses=serializers.KegTapSerializer, + ) + @action(detail=True, methods=["post"], url_path="attach-keg") + def attach_keg(self, request, pk=None): + """Attaches an existing (available) keg to this tap.""" + tap = self.get_object() + req = serializers.TapAttachKegRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + try: + tap.attach_keg(req.validated_data["keg"]) + except ValueError as e: + raise ValidationError(str(e)) from e + return self._tap_response(tap) + + @extend_schema( + request=serializers.NewKegRequestSerializer, + responses=serializers.KegTapSerializer, + ) + @action(detail=True, methods=["post"], url_path="start-keg") + def start_keg(self, request, pk=None): + """Creates a new keg and attaches it to this tap.""" + tap = self.get_object() + req = serializers.NewKegRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + params = req.validated_data + try: + models.Keg.start_keg( + tap, + beverage=params["beverage"], + keg_type=params["keg_type"], + full_volume_ml=params["full_volume_ml"], + beverage_name=params["beverage_name"] or None, + beverage_type=params["beverage_type"] if not params["beverage"] else None, + producer_name=params["producer_name"] or None, + style_name=params["style_name"] or None, + ) + except ValueError as e: + raise ValidationError(str(e)) from e + return self._tap_response(tap) + + @extend_schema(request=None, responses=serializers.KegTapSerializer) + @action(detail=True, methods=["post"], url_path="end-keg") + def end_keg(self, request, pk=None): + """Takes the tap's current keg offline.""" + tap = self.get_object() + if not tap.current_keg: + raise ValidationError("Tap has no active keg.") + tap.end_current_keg() + return self._tap_response(tap) + + @extend_schema( + request=serializers.TapConnectMeterRequestSerializer, + responses=serializers.KegTapSerializer, + ) + @action(detail=True, methods=["post"], url_path="connect-meter") + def connect_meter(self, request, pk=None): + """Assigns a flow meter to this tap (null to disconnect).""" + tap = self.get_object() + req = serializers.TapConnectMeterRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + tap.connect_meter(req.validated_data["meter"]) + return self._tap_response(tap) + + @extend_schema( + request=serializers.TapConnectToggleRequestSerializer, + responses=serializers.KegTapSerializer, + ) + @action(detail=True, methods=["post"], url_path="connect-toggle") + def connect_toggle(self, request, pk=None): + """Assigns a flow toggle to this tap (null to disconnect).""" + tap = self.get_object() + req = serializers.TapConnectToggleRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + tap.connect_toggle(req.validated_data["toggle"]) + return self._tap_response(tap) + + @extend_schema( + request=serializers.TapConnectThermoRequestSerializer, + responses=serializers.KegTapSerializer, + ) + @action(detail=True, methods=["post"], url_path="connect-thermo") + def connect_thermo(self, request, pk=None): + """Assigns a temperature sensor to this tap (null to disconnect).""" + tap = self.get_object() + req = serializers.TapConnectThermoRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + tap.connect_thermo(req.validated_data["thermo_sensor"]) + return self._tap_response(tap) + + @extend_schema( + request=serializers.TapRecordDrinkRequestSerializer, + responses=serializers.DrinkSerializer, + ) + @action(detail=True, methods=["post"], url_path="record-drink") + def record_drink(self, request, pk=None): + """Manually records a drink (or spill) against this tap's keg. + + Returns the new drink (201), or no content (204) when recorded + as a spill. + """ + tap = self.get_object() + req = serializers.TapRecordDrinkRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + params = req.validated_data + try: + drink = models.Drink.record_drink( + tap, + ticks=0, + volume_ml=params["volume_ml"], + username=params["username"] or None, + pour_time=params["pour_time"], + duration=params["duration"], + shout=params["shout"], + spilled=params["spilled"], + ) + except ValueError as e: + raise ValidationError(str(e)) from e + if drink is None: + return Response(status=status.HTTP_204_NO_CONTENT) + return Response( + serializers.DrinkSerializer(drink).data, + status=status.HTTP_201_CREATED, + ) class ControllerViewSet(viewsets.ModelViewSet): @@ -125,20 +383,183 @@ class FlowToggleViewSet(viewsets.ModelViewSet): permission_classes = [permissions.IsAdminUser] -class KegViewSet(viewsets.ReadOnlyModelViewSet): - """Lists all Kegs in the system.""" +class KegViewSet(viewsets.ModelViewSet): + """Lists all Kegs in the system. + + Reads follow site privacy; keg management requires an admin. Deleting + a keg permanently destroys it and ALL of its drinks. + """ queryset = models.Keg.objects.all() serializer_class = serializers.KegSerializer - permission_classes = [permissions.DashboardViewer] - - -class DrinkViewSet(viewsets.ReadOnlyModelViewSet): - """Lists all Drinks in the system.""" + permission_classes = [permissions.AdminWriteDashboardRead] + filterset_class = filters.KegFilter + + @extend_schema(request=serializers.KegCreateRequestSerializer) + def create(self, request, *args, **kwargs): + """Adds a new keg to the keg room (unattached).""" + req = serializers.KegCreateRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + params = req.validated_data + try: + keg = models.Keg.create_keg( + beverage=params["beverage"], + keg_type=params["keg_type"], + full_volume_ml=params["full_volume_ml"], + beverage_name=params["beverage_name"] or None, + beverage_type=params["beverage_type"] if not params["beverage"] else None, + producer_name=params["producer_name"] or None, + style_name=params["style_name"] or None, + notes=params["notes"] or None, + description=params["description"] or None, + ) + except ValueError as e: + raise ValidationError(str(e)) from e + return Response(self.get_serializer(keg).data, status=status.HTTP_201_CREATED) + + def perform_destroy(self, instance): + instance.cancel() + + @extend_schema(responses=OpenApiTypes.OBJECT) + @action(detail=True) + def stats(self, request, pk=None): + """Returns the latest stats blob for this keg.""" + return Response(self.get_object().get_stats()) + + @extend_schema(request=None, responses=serializers.KegSerializer) + @action(detail=True, methods=["post"]) + def end(self, request, pk=None): + """Marks an untapped keg as finished.""" + keg = self.get_object() + try: + keg.end_keg() + except ValueError as e: + raise ValidationError(str(e)) from e + return Response(self.get_serializer(keg).data) + + @extend_schema(request=None, responses=serializers.KegSerializer) + @action(detail=True, methods=["post"]) + def reactivate(self, request, pk=None): + """Returns a finished keg to the available pool.""" + keg = self.get_object() + try: + keg.reactivate_keg() + except ValueError as e: + raise ValidationError(str(e)) from e + return Response(self.get_serializer(keg).data) + + @extend_schema( + request=serializers.KegSpillRequestSerializer, + responses=serializers.KegSerializer, + ) + @action(detail=True, methods=["post"]) + def spill(self, request, pk=None): + """Records spilled volume against this keg.""" + keg = self.get_object() + req = serializers.KegSpillRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + keg.spilled_ml += req.validated_data["volume_ml"] + keg.save(update_fields=["spilled_ml"]) + return Response(self.get_serializer(keg).data) + + +class DrinkViewSet( + mixins.RetrieveModelMixin, + mixins.ListModelMixin, + mixins.DestroyModelMixin, + viewsets.GenericViewSet, +): + """Lists all Drinks in the system. + + Drinks are created by pours (or the tap record-drink endpoint), never + directly. The drink's owner may edit its shout and manage its picture; + volume adjustment, reassignment, and deletion are admin operations. + """ queryset = models.Drink.objects.all() serializer_class = serializers.DrinkSerializer permission_classes = [permissions.DashboardViewer] + filterset_class = filters.DrinkFilter + + def get_permissions(self): + if self.action in ("destroy", "reassign"): + return [permissions.IsAdminUser()] + if self.action in ("partial_update", "picture"): + return [permissions.IsOwnerOrAdmin()] + return super().get_permissions() + + def perform_destroy(self, instance): + """Cancels the drink; pass ?spilled=true to move its volume to spillage.""" + spilled = self.request.query_params.get("spilled") in ("1", "true") + instance.cancel_drink(spilled=spilled) + + @extend_schema( + request=serializers.DrinkUpdateRequestSerializer, + responses=serializers.DrinkSerializer, + ) + def partial_update(self, request, pk=None): + drink = self.get_object() + req = serializers.DrinkUpdateRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + if "volume_ml" in data and data["volume_ml"] != drink.volume_ml: + if not request.user.is_staff: + raise PermissionDenied("Only admins may adjust drink volume.") + drink.set_volume(data["volume_ml"]) + if "shout" in data: + drink.shout = data["shout"] + drink.save(update_fields=["shout"]) + return Response(self.get_serializer(drink).data) + + @extend_schema( + request=serializers.DrinkReassignRequestSerializer, + responses=serializers.DrinkSerializer, + ) + @action(detail=True, methods=["post"]) + def reassign(self, request, pk=None): + """Reassigns this drink to another user.""" + drink = self.get_object() + req = serializers.DrinkReassignRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + user = models.User.objects.get(username=req.validated_data["username"]) + drink.reassign(user) + drink.refresh_from_db() + return Response(self.get_serializer(drink).data) + + @extend_schema( + request=serializers.PictureUploadRequestSerializer, + responses=serializers.DrinkSerializer, + ) + @action( + detail=True, + methods=["post", "delete"], + parser_classes=[MultiPartParser, FormParser], + ) + def picture(self, request, pk=None): + """Attaches (POST) or erases (DELETE) this drink's picture.""" + drink = self.get_object() + old_picture = drink.picture + + if request.method == "DELETE": + if old_picture: + old_picture.erase_and_delete() + drink.refresh_from_db() + return Response(status=status.HTTP_204_NO_CONTENT) + + req = serializers.PictureUploadRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + picture = models.Picture.objects.create( + image=req.validated_data["image"], + caption=req.validated_data["caption"], + user=drink.user, + keg=drink.keg, + session=drink.session, + ) + drink.picture = picture + drink.save(update_fields=["picture"]) + if old_picture: + old_picture.erase_and_delete() + return Response(self.get_serializer(drink).data) class AuthenticationTokenViewSet(viewsets.ModelViewSet): @@ -147,6 +568,7 @@ class AuthenticationTokenViewSet(viewsets.ModelViewSet): queryset = models.AuthenticationToken.objects.all() serializer_class = serializers.AuthenticationTokenSerializer permission_classes = [permissions.IsAdminUser] + filterset_class = filters.AuthenticationTokenFilter class DrinkingSessionViewSet(viewsets.ReadOnlyModelViewSet): @@ -155,6 +577,58 @@ class DrinkingSessionViewSet(viewsets.ReadOnlyModelViewSet): queryset = models.DrinkingSession.objects.all() serializer_class = serializers.DrinkingSessionSerializer permission_classes = [permissions.DashboardViewer] + filterset_class = filters.DrinkingSessionFilter + + @extend_schema(responses=serializers.DrinkingSessionSerializer) + @action(detail=False) + def current(self, request): + """Returns the currently-active session, or 404 if there is none.""" + try: + latest = models.DrinkingSession.objects.latest() + except models.DrinkingSession.DoesNotExist: + latest = None + if not latest or not latest.IsActiveNow(): + raise NotFound("There is no active session.") + return Response(self.get_serializer(latest).data) + + @extend_schema(responses=OpenApiTypes.OBJECT) + @action(detail=True) + def stats(self, request, pk=None): + """Returns the latest stats blob for this session.""" + return Response(self.get_object().get_stats()) + + @extend_schema(responses=serializers.SessionDirectorySerializer) + @action(detail=False) + def directory(self, request): + """Enumerates the dates that have sessions, newest first. + + Buckets use the site's active timezone — the same conversion the + year/month/day list filters apply — so a directory entry always + matches the corresponding filtered listing. + """ + # Bucketing happens in Python: SQL date extraction under USE_TZ + # compiles to CONVERT_TZ on MySQL, which silently yields NULL + # when the server's timezone tables aren't loaded. + tz = timezone.get_current_timezone() + counts: dict[tuple[int, int, int], int] = defaultdict(int) + for start_time in models.DrinkingSession.objects.values_list("start_time", flat=True): + local = start_time.astimezone(tz) + counts[(local.year, local.month, local.day)] += 1 + + years: list[dict] = [] + for (year, month, day), count in sorted(counts.items(), reverse=True): + if not years or years[-1]["year"] != year: + years.append({"year": year, "months": [], "count": 0}) + year_entry = years[-1] + months = year_entry["months"] + if not months or months[-1]["month"] != month: + months.append({"month": month, "days": [], "count": 0}) + month_entry = months[-1] + month_entry["days"].append(day) + month_entry["count"] += count + year_entry["count"] += count + + return Response(serializers.SessionDirectorySerializer(instance={"years": years}).data) class ThermoSensorViewSet(viewsets.ModelViewSet): @@ -171,6 +645,7 @@ class ThermologViewSet(viewsets.ReadOnlyModelViewSet): queryset = models.Thermolog.objects.all() serializer_class = serializers.ThermologSerializer permission_classes = [permissions.DashboardViewer] + filterset_class = filters.ThermologFilter class StatsViewSet(viewsets.ReadOnlyModelViewSet): @@ -180,6 +655,13 @@ class StatsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.StatsSerializer permission_classes = [permissions.DashboardViewer] + @extend_schema(responses=OpenApiTypes.OBJECT) + @action(detail=False) + def system(self, request): + """Returns the latest system-wide (all-time) stats blob.""" + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + return Response(site.get_stats()) + class SystemEventViewSet(viewsets.ReadOnlyModelViewSet): """Lists all SystemEvents in the system.""" @@ -187,6 +669,7 @@ class SystemEventViewSet(viewsets.ReadOnlyModelViewSet): queryset = models.SystemEvent.objects.all() serializer_class = serializers.SystemEventSerializer permission_classes = [permissions.DashboardViewer] + filterset_class = filters.SystemEventFilter class NotificationSettingsViewSet(viewsets.ModelViewSet): @@ -235,6 +718,41 @@ def system_status(request): return Response(serializer.data) +@extend_schema( + request=serializers.SiteSettingsSerializer, responses=serializers.SiteSettingsSerializer +) +@api_view(["GET", "PATCH"]) +@permission_classes([permissions.IsAdminUser]) +def site_settings(request): + """Reads (GET) or updates (PATCH) the site settings singleton.""" + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + if request.method == "PATCH": + serializer = serializers.SiteSettingsSerializer(site, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializers.SiteSettingsSerializer(site).data) + + +@extend_schema( + request=serializers.PictureUploadRequestSerializer, + responses=serializers.SiteSettingsSerializer, +) +@api_view(["POST"]) +@permission_classes([permissions.IsAdminUser]) +@parser_classes([MultiPartParser, FormParser]) +def site_background_image(request): + """Uploads and sets the site background image.""" + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + req = serializers.PictureUploadRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + site.background_image = models.Picture.objects.create( + image=req.validated_data["image"], + caption=req.validated_data["caption"], + ) + site.save() + return Response(serializers.SiteSettingsSerializer(site).data) + + @extend_schema(request=serializers.LoginSerializer, responses=serializers.CurrentUserSerializer) @api_view(["POST"]) @authentication_classes(()) @@ -254,9 +772,45 @@ def logout(request): return Response(True) -@extend_schema(responses=serializers.CurrentUserSerializer) -@api_view(["GET"]) -def current_user(request): - user = request.user - serializer = serializers.CurrentUserSerializer(instance=user) - return Response(serializer.data) +@ensure_csrf_cookie +@extend_schema( + request=serializers.ProfileUpdateRequestSerializer, responses=serializers.MeSerializer +) +@api_view(["GET", "PATCH"]) +@permission_classes(()) +def me(request): + """The frontend boot endpoint. + + GET always responds 200, regardless of authentication and site + privacy: `user` is null for anonymous callers, and the rest of the + payload is limited to privacy-safe configuration the frontend always + needs (to render login screens, privacy interstitials, forms, and + navigation). It also sets the CSRF cookie, so a fresh browser session + can make authenticated POSTs after calling this. + + PATCH updates the current user's profile and returns the same payload. + """ + if request.method == "PATCH": + if not request.user.is_authenticated: + raise NotAuthenticated() + req = serializers.ProfileUpdateRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + if "display_name" in req.validated_data: + request.user.display_name = req.validated_data["display_name"] + request.user.save(update_fields=["display_name"]) + + user = request.user if request.user.is_authenticated else None + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + plugins = getattr(request, "plugins", {}) or {} + payload = { + "user": user, + "site": site, + "can_invite": site.can_invite(user), + "have_sessions": models.DrinkingSession.objects.exists(), + "sso_login_url": getattr(settings, "SSO_LOGIN_URL", "") or "", + "sso_logout_url": getattr(settings, "SSO_LOGOUT_URL", "") or "", + "plugins": [ + {"short_name": p.get_short_name(), "name": p.get_name()} for p in plugins.values() + ], + } + return Response(serializers.MeSerializer(instance=payload).data) diff --git a/pykeg/api/views_account.py b/pykeg/api/views_account.py new file mode 100644 index 000000000..537fe00a3 --- /dev/null +++ b/pykeg/api/views_account.py @@ -0,0 +1,228 @@ +"""Account self-service and authentication-flow endpoints. + +These replace the server-rendered account, registration, and password +management pages. Endpoints that operate before login (register, password +reset, activation) are unauthenticated and rate-limited. +""" + +from django.contrib.auth import authenticate, update_session_auth_hash +from django.contrib.auth import login as auth_login +from django.contrib.auth.tokens import default_token_generator +from django.utils.encoding import force_str +from django.utils.http import urlsafe_base64_decode +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema +from rest_framework.decorators import ( + api_view, + authentication_classes, + parser_classes, + permission_classes, + throttle_classes, +) +from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError +from rest_framework.parsers import FormParser, MultiPartParser +from rest_framework.response import Response +from rest_framework.throttling import AnonRateThrottle + +from pykeg.core import models +from pykeg.util import email as email_util +from pykeg.web.auth import UserExistsException + +from . import serializers +from .forms import PasswordResetForm + + +class AuthAttemptThrottle(AnonRateThrottle): + scope = "auth" + + +@extend_schema(request=serializers.PasswordChangeRequestSerializer, responses=OpenApiTypes.BOOL) +@api_view(["POST"]) +def change_password(request): + """Changes the current user's password, keeping the session valid.""" + req = serializers.PasswordChangeRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + if not request.user.check_password(data["current_password"]): + raise ValidationError({"current_password": ["Incorrect password."]}) + request.user.set_password(data["new_password"]) + request.user.save() + update_session_auth_hash(request, request.user) + return Response(True) + + +@extend_schema(request=serializers.EmailChangeRequestSerializer, responses=OpenApiTypes.BOOL) +@api_view(["POST"]) +def change_email(request): + """Requests an email change; a confirmation link is mailed to the new address.""" + req = serializers.EmailChangeRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + new_email = req.validated_data["email"] + if new_email == request.user.email: + raise ValidationError({"email": ["E-mail address unchanged."]}) + + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + token = email_util.build_email_change_token(request.user, new_email) + url = site.reverse_full("account-confirm-email", args=(), kwargs={"token": token}) + message = email_util.build_message( + new_email, + "registration/email_confirm_email_change.html", + {"url": url, "site_name": site.title}, + ) + message.send() + return Response(True) + + +@extend_schema( + request=serializers.ConfirmEmailRequestSerializer, + responses=serializers.CurrentUserSerializer, +) +@api_view(["POST"]) +def confirm_email(request): + """Applies an email change, given the token from the confirmation mail.""" + req = serializers.ConfirmEmailRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + try: + uid, new_address = email_util.verify_email_change_token( + request.user, req.validated_data["token"] + ) + except ValueError: + raise ValidationError({"token": ["That token is not valid."]}) from None + if uid != request.user.id: + raise ValidationError({"token": ["E-mail confirmation does not exist for this account."]}) + if request.user.email != new_address: + request.user.email = new_address + request.user.save() + return Response(serializers.CurrentUserSerializer(request.user).data) + + +@extend_schema( + request=serializers.PictureUploadRequestSerializer, + responses=serializers.CurrentUserSerializer, +) +@api_view(["POST"]) +@parser_classes([MultiPartParser, FormParser]) +def mugshot(request): + """Sets the current user's mugshot.""" + req = serializers.PictureUploadRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + picture = models.Picture.objects.create(image=req.validated_data["image"], user=request.user) + request.user.mugshot = picture + request.user.save() + return Response(serializers.CurrentUserSerializer(request.user).data) + + +@extend_schema(request=None, responses=serializers.ApiKeySerializer) +@api_view(["POST"]) +def regenerate_api_key(request): + """Discards and regenerates the current user's API key.""" + key, _ = models.ApiKey.objects.get_or_create(user=request.user) + key.regenerate() + return Response(serializers.ApiKeySerializer(key).data) + + +@extend_schema( + request=serializers.ActivateAccountRequestSerializer, + responses=serializers.CurrentUserSerializer, +) +@api_view(["POST"]) +@authentication_classes(()) +@permission_classes(()) +@throttle_classes([AuthAttemptThrottle]) +def activate(request): + """Activates an invited/created account: sets its password and logs in.""" + req = serializers.ActivateAccountRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + users = models.User.objects.filter(activation_key=data["activation_key"]) + if users.count() != 1: + raise NotFound("No such activation key.") + user = users[0] + if user.has_usable_password(): + raise ValidationError({"activation_key": ["Account is already activated."]}) + + user.set_password(data["password"]) + user.activation_key = None + user.save() + + user = authenticate(username=user.username, password=data["password"]) + auth_login(request, user) + return Response(serializers.CurrentUserSerializer(user).data) + + +@extend_schema( + request=serializers.RegisterRequestSerializer, + responses=serializers.CurrentUserSerializer, +) +@api_view(["POST"]) +@authentication_classes(()) +@permission_classes(()) +@throttle_classes([AuthAttemptThrottle]) +def register(request): + """Registers a new account, honoring the site's registration mode.""" + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + req = serializers.RegisterRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + + invite = None + if site.registration_mode != "public": + invite_code = data.get("invite_code") or "" + if not invite_code: + raise PermissionDenied("An invitation is required to register.") + invite = models.Invitation.objects.filter(invite_code=invite_code).first() + if not invite or invite.is_expired(): + raise PermissionDenied("Invitation is invalid or expired.") + + try: + models.User.create_new_user( + username=data["username"], email=data["email"], password=data["password"] + ) + except UserExistsException: + raise ValidationError({"username": ["A user with that username already exists."]}) from None + + if invite: + invite.delete() + + user = authenticate(username=data["username"], password=data["password"]) + auth_login(request, user) + return Response(serializers.CurrentUserSerializer(user).data, status=201) + + +@extend_schema(request=serializers.PasswordResetRequestSerializer, responses=OpenApiTypes.BOOL) +@api_view(["POST"]) +@authentication_classes(()) +@permission_classes(()) +@throttle_classes([AuthAttemptThrottle]) +def password_reset(request): + """Mails a password-reset link. Always succeeds (no account enumeration).""" + req = serializers.PasswordResetRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + form = PasswordResetForm({"email": req.validated_data["email"]}) + if form.is_valid(): + form.save(request=request) + return Response(True) + + +@extend_schema( + request=serializers.PasswordResetConfirmRequestSerializer, responses=OpenApiTypes.BOOL +) +@api_view(["POST"]) +@authentication_classes(()) +@permission_classes(()) +@throttle_classes([AuthAttemptThrottle]) +def password_reset_confirm(request): + """Sets a new password, given the uid/token pair from a reset mail.""" + req = serializers.PasswordResetConfirmRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + try: + uid = force_str(urlsafe_base64_decode(data["uid"])) + user = models.User.objects.get(pk=uid) + except TypeError, ValueError, OverflowError, models.User.DoesNotExist: + raise ValidationError({"token": ["Invalid password reset link."]}) from None + if not default_token_generator.check_token(user, data["token"]): + raise ValidationError({"token": ["Invalid or expired password reset link."]}) + user.set_password(data["new_password"]) + user.save() + return Response(True) diff --git a/pykeg/api/views_admin.py b/pykeg/api/views_admin.py new file mode 100644 index 000000000..22e4c74d1 --- /dev/null +++ b/pykeg/api/views_admin.py @@ -0,0 +1,204 @@ +"""Admin operations endpoints: dashboard, backups, logs, email test, bugreport. + +These replace the corresponding kegadmin pages. Everything here is +admin-only. +""" + +import datetime +import io +import logging +import os +import zipfile +from operator import itemgetter + +import redis +from django.conf import settings +from django.core.files.storage import default_storage +from django.utils import timezone +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema +from rest_framework import status +from rest_framework.decorators import api_view, permission_classes +from rest_framework.exceptions import NotFound, ValidationError +from rest_framework.response import Response + +from pykeg.backup import backup as backup_lib +from pykeg.core import models, tasks +from pykeg.logging.handlers import RedisListHandler +from pykeg.util import bugreport as bugreport_util +from pykeg.util.email import build_message + +from . import permissions, serializers + +logger = logging.getLogger(__name__) + + +@extend_schema(responses=serializers.AdminDashboardSerializer) +@api_view(["GET"]) +@permission_classes([permissions.IsAdminUser]) +def dashboard(request): + """System health summary for the admin dashboard.""" + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + + redis_error = None + try: + client = redis.StrictRedis.from_url(settings.KEGBOT["REDIS_URL"]) + client.ping() + except redis.RedisError as e: + redis_error = str(e) or "Unknown error." + + guestless_users = models.User.objects.exclude(username="guest") + recent_time = timezone.now() - datetime.timedelta(days=30) + payload = { + "email_configured": site.email_is_configured(), + "redis_error": redis_error, + "num_users": guestless_users.filter(is_active=True).count(), + "num_new_users": guestless_users.filter(date_joined__gte=recent_time).count(), + } + return Response(serializers.AdminDashboardSerializer(instance=payload).data) + + +@extend_schema(request=None, responses=OpenApiTypes.OBJECT) +@api_view(["GET", "POST"]) +@permission_classes([permissions.IsAdminUser]) +def backups(request): + """Lists existing backups (GET) or starts building a new one (POST).""" + if request.method == "POST": + tasks.build_backup.delay() + return Response({"started": True}, status=status.HTTP_202_ACCEPTED) + + storage = default_storage + results = [] + if storage.exists(backup_lib.BACKUPS_DIRNAME): + _, files = storage.listdir(backup_lib.BACKUPS_DIRNAME) + for filename in files: + if not filename.endswith("zip"): + continue + storage_filename = os.path.join(backup_lib.BACKUPS_DIRNAME, filename) + with storage.open(storage_filename, mode="rb") as backup_file: + archive = zipfile.ZipFile(backup_file) + metadata = backup_lib.read_metadata(archive) + metadata["size_bytes"] = storage.size(storage_filename) + metadata["url"] = storage.url(storage_filename) + metadata["backup_name"] = filename + results.append(metadata) + results.sort(key=itemgetter(backup_lib.META_CREATED_TIME), reverse=True) + return Response(results) + + +@extend_schema(request=None, responses=None) +@api_view(["DELETE"]) +@permission_classes([permissions.IsAdminUser]) +def delete_backup(request, filename): + """Deletes a backup archive by filename.""" + backup_file = os.path.normpath(os.path.basename(filename)) + backup_file = os.path.join(backup_lib.BACKUPS_DIRNAME, backup_file) + if not default_storage.exists(backup_file): + raise NotFound("Unknown backup file.") + default_storage.delete(backup_file) + return Response(status=status.HTTP_204_NO_CONTENT) + + +@extend_schema(responses=OpenApiTypes.OBJECT) +@api_view(["GET"]) +@permission_classes([permissions.IsAdminUser]) +def logs(request): + """Returns recent log records (newest first) from the redis log handler.""" + records = [] + error = None + candidates = [logging.getLogger(), logging.getLogger("pykeg")] + handlers = [h for logger_ in candidates for h in logger_.handlers] + for handler in handlers: + if isinstance(handler, RedisListHandler): + try: + records = list(handler.get_logs()) + records.reverse() + except redis.RedisError as e: + error = str(e) or "Unknown error." + break + return Response({"logs": records, "error": error}) + + +@extend_schema(request=serializers.EmailTestRequestSerializer, responses=OpenApiTypes.BOOL) +@api_view(["POST"]) +@permission_classes([permissions.IsAdminUser]) +def email_test(request): + """Sends a test notification email to the given address.""" + req = serializers.EmailTestRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + site = getattr(request, "kbsite", None) or models.KegbotSite.get() + context = { + "site_name": site.title, + "site_url": site.base_url(), + "settings_url": site.base_url() + "/account", + } + message = build_message(req.validated_data["address"], "notification/email_test.html", context) + message.send(fail_silently=True) + return Response(True) + + +@extend_schema(responses=OpenApiTypes.OBJECT) +@api_view(["GET"]) +@permission_classes([permissions.IsAdminUser]) +def plugins(request): + """Lists installed plugins.""" + installed = getattr(request, "plugins", {}) or {} + results = [ + { + "short_name": p.get_short_name(), + "name": p.get_name(), + "description": p.get_description(), + "version": p.get_version(), + "url": p.get_url(), + "has_settings": hasattr(p, "get_site_settings_form"), + } + for p in installed.values() + ] + return Response(results) + + +@extend_schema(request=OpenApiTypes.OBJECT, responses=OpenApiTypes.OBJECT) +@api_view(["GET", "PUT"]) +@permission_classes([permissions.IsAdminUser]) +def plugin_settings(request, short_name): + """Reads or updates a plugin's site settings. + + Writes are validated through the plugin's own settings form; field + errors come back in standard DRF shape. + """ + installed = getattr(request, "plugins", {}) or {} + plugin = installed.get(short_name) + if not plugin: + raise NotFound(f"Plugin {short_name!r} is not installed.") + if not hasattr(plugin, "get_site_settings_form"): + raise NotFound(f"Plugin {short_name!r} has no settings.") + + if request.method == "PUT": + form_cls = type(plugin.get_site_settings_form()) + form = form_cls(request.data) + if not form.is_valid(): + raise ValidationError(dict(form.errors)) + if hasattr(plugin, "save_site_settings_form"): + plugin.save_site_settings_form(form) + else: + plugin.save_form(form, "settings") + + # Every declared field, not just form.initial: unconfigured fields + # are absent there, and the settings page renders one input per key. + form = plugin.get_site_settings_form() + return Response({name: form.initial.get(name) for name in form.fields}) + + +@extend_schema(responses=OpenApiTypes.OBJECT) +@api_view(["GET"]) +@permission_classes([permissions.IsAdminUser]) +def bugreport(request): + """Generates and returns a bugreport (may contain secrets; admin eyes only).""" + out = io.StringIO() + error = None + try: + bugreport_util.bugreport(out) + except Exception as e: # Never fail: partial output is still useful. + logger.exception("Error generating bugreport") + error = str(e) + return Response({"output": out.getvalue(), "error": error}) diff --git a/pykeg/api/views_setup.py b/pykeg/api/views_setup.py new file mode 100644 index 000000000..c4323d4a0 --- /dev/null +++ b/pykeg/api/views_setup.py @@ -0,0 +1,154 @@ +"""Setup wizard and upgrade endpoints. + +These power the frontend's setup flow. They are reachable only while +setup or upgrade is required (`SetupAccess`), use no authenticators (the +database — including the session and user tables — may not exist yet), +and are exempted from the IsSetupMiddleware gate. + +Unlike the old cookie-driven wizard, there is no server-side inter-step +state: the frontend collects choices and submits them in one settings +call. +""" + +import io +import logging + +from django.contrib.auth import authenticate +from django.contrib.auth import login as auth_login +from django.core import management +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema +from rest_framework import status +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.exceptions import ValidationError +from rest_framework.response import Response + +from pykeg.core import defaults, models +from pykeg.core.util import get_version, get_version_object + +from . import permissions, serializers + +logger = logging.getLogger(__name__) + + +def _conflict(message): + return Response({"detail": message}, status=status.HTTP_409_CONFLICT) + + +def setup_api_view(methods): + """Composed decorators common to every setup endpoint.""" + + def decorator(func): + return api_view(methods)( + authentication_classes(())(permission_classes([permissions.SetupAccess])(func)) + ) + + return decorator + + +@extend_schema(responses=serializers.SetupStatusSerializer) +@setup_api_view(["GET"]) +def setup_status(request): + """Reports whether setup or upgrade is required.""" + payload = { + "need_setup": request.need_setup, + "need_upgrade": request.need_upgrade, + "installed_version": getattr(request, "installed_version_string", None), + "current_version": get_version(), + } + return Response(serializers.SetupStatusSerializer(instance=payload).data) + + +@extend_schema(request=None, responses=OpenApiTypes.OBJECT) +@setup_api_view(["POST"]) +def migrate(request): + """Creates or migrates the database (synchronous).""" + out = io.StringIO() + try: + management.call_command("migrate", interactive=False, stdout=out) + except Exception as e: + logger.exception("Error migrating database") + raise ValidationError({"detail": [str(e)]}) from e + return Response({"output": out.getvalue()}) + + +@extend_schema( + request=serializers.SetupSiteSettingsRequestSerializer, + responses=serializers.SetupSiteSettingsRequestSerializer, +) +@setup_api_view(["POST"]) +def site_settings(request): + """Applies initial site settings (after the database is migrated).""" + if not request.need_setup: + return _conflict("Site is already set up.") + try: + defaults.set_defaults() + except defaults.AlreadyInstalledError: + pass + + site = models.KegbotSite.get() + serializer = serializers.SetupSiteSettingsRequestSerializer( + site, data=request.data, partial=True + ) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) + + +@extend_schema( + request=serializers.SetupAdminUserRequestSerializer, + responses=serializers.CurrentUserSerializer, +) +@setup_api_view(["POST"]) +def admin_user(request): + """Creates the initial admin account and logs it in.""" + if not request.need_setup: + return _conflict("Site is already set up.") + req = serializers.SetupAdminUserRequestSerializer(data=request.data) + req.is_valid(raise_exception=True) + data = req.validated_data + if models.User.objects.filter(username=data["username"]).exists(): + raise ValidationError({"username": ["A user with that username already exists."]}) + + user = models.User(username=data["username"], email=data["email"]) + user.is_staff = True + user.is_superuser = True + user.set_password(data["password"]) + user.save() + + # By this point the session table exists (migrated in the first step), + # so the new admin can be logged in for the rest of the flow. + user = authenticate(username=data["username"], password=data["password"]) + auth_login(request, user) + return Response(serializers.CurrentUserSerializer(user).data, status=status.HTTP_201_CREATED) + + +@extend_schema(request=None, responses=OpenApiTypes.BOOL) +@setup_api_view(["POST"]) +def finish(request): + """Marks setup complete.""" + if not request.need_setup: + return _conflict("Site is already set up.") + site = models.KegbotSite.get() + site.is_setup = True + site.server_version = str(get_version_object()) + site.save() + return Response(True) + + +@extend_schema(request=None, responses=OpenApiTypes.OBJECT) +@setup_api_view(["POST"]) +def upgrade(request): + """Migrates the database and stamps the current server version.""" + if not request.need_upgrade: + return _conflict("No upgrade is required.") + out = io.StringIO() + try: + management.call_command("migrate", interactive=False, stdout=out) + site = models.KegbotSite.get() + site.server_version = str(get_version_object()) + site.save() + except Exception as e: + logger.exception("Error upgrading database") + raise ValidationError({"detail": [str(e)]}) from e + return Response({"output": out.getvalue()}) diff --git a/pykeg/contrib/webhook/plugin.py b/pykeg/contrib/webhook/plugin.py index f3d9fd7e9..1d44f1383 100644 --- a/pykeg/contrib/webhook/plugin.py +++ b/pykeg/contrib/webhook/plugin.py @@ -4,7 +4,7 @@ from pykeg.plugin import plugin from pykeg.web.api import serialize -from . import forms, tasks, views +from . import forms, tasks KEY_SITE_SETTINGS = "settings" @@ -16,9 +16,6 @@ class WebhookPlugin(plugin.Plugin): URL = "http://kegbot.org" VERSION = "1.0.0" - def get_admin_settings_view(self): - return views.admin_settings - def handle_new_events(self, events): for event in events: self.handle_event(event) diff --git a/pykeg/contrib/webhook/templates/contrib/webhook/webhook_admin_settings.html b/pykeg/contrib/webhook/templates/contrib/webhook/webhook_admin_settings.html deleted file mode 100644 index 7cc2f700b..000000000 --- a/pykeg/contrib/webhook/templates/contrib/webhook/webhook_admin_settings.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Webhook Settings | {{ block.super }}{% endblock %} -{% block pagetitle %}Webhook Settings{% endblock %} - -{% block kegadmin-main %} - -

Settings

-
-

- Site-wide settings for webhooks. -

-
- {% csrf_token %} - {{ settings_form|crispy }} - - -
-
- -{% endblock %} diff --git a/pykeg/contrib/webhook/views.py b/pykeg/contrib/webhook/views.py deleted file mode 100644 index ca5cd5859..000000000 --- a/pykeg/contrib/webhook/views.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.contrib import messages -from django.shortcuts import render - -from pykeg.web.decorators import staff_member_required - -from . import forms - - -@staff_member_required -def admin_settings(request, plugin): - context = {} - settings_form = plugin.get_site_settings_form() - - if request.method == "POST": - if "submit-settings" in request.POST: - settings_form = forms.SiteSettingsForm(request.POST) - if settings_form.is_valid(): - plugin.save_site_settings_form(settings_form) - messages.success(request, "Settings updated") - - context["plugin"] = plugin - context["settings_form"] = settings_form - - return render(request, "contrib/webhook/webhook_admin_settings.html", context=context) diff --git a/pykeg/core/management/commands/print_constants.py b/pykeg/core/management/commands/print_constants.py new file mode 100644 index 000000000..9d080721d --- /dev/null +++ b/pykeg/core/management/commands/print_constants.py @@ -0,0 +1,21 @@ +import json +import os + +from django.conf import settings +from django.core.management.base import BaseCommand + +from pykeg.util.genconstants import genconstants + +OUTFILE = os.path.join(settings.BASE_DIR, "web-ui/lib/shared-constants.ts") + + +class Command(BaseCommand): + help = f"Writes all Django-managed constants to `{OUTFILE}`." + + def handle(self, *args, **options): + data = json.dumps(genconstants(), indent=2) + ts_content = f"export default {data} as const;\n" + os.makedirs(os.path.dirname(OUTFILE), exist_ok=True) + with open(OUTFILE, "w+") as fp: + fp.write(ts_content) + print(data) diff --git a/pykeg/core/management/commands/runserver.py b/pykeg/core/management/commands/runserver.py new file mode 100644 index 000000000..4baed3185 --- /dev/null +++ b/pykeg/core/management/commands/runserver.py @@ -0,0 +1,16 @@ +"""Dev-server override: default to port 8001. + +In development the vite dev server owns http://localhost:8000 (the +address you actually browse to) and proxies backend paths here. This +command must live in an app listed *before* whitenoise.runserver_nostatic +in INSTALLED_APPS so it takes precedence; it subclasses whitenoise's +command, which in turn wraps the next-lower-priority runserver. +""" + +from whitenoise.runserver_nostatic.management.commands.runserver import ( + Command as RunserverNostaticCommand, +) + + +class Command(RunserverNostaticCommand): + default_port = "8001" diff --git a/pykeg/core/util.py b/pykeg/core/util.py index 194da389f..40a169108 100644 --- a/pykeg/core/util.py +++ b/pykeg/core/util.py @@ -3,7 +3,6 @@ # Note: imports should be limited to python stdlib, since methods here # may be used in models.py, settings.py, etc. -import importlib.util import logging import os import tempfile @@ -14,7 +13,6 @@ from threading import current_thread import requests -from django.core.exceptions import ImproperlyConfigured from packaging.version import Version from redis.exceptions import RedisError @@ -53,19 +51,6 @@ def CtoF(t): return ((9.0 / 5.0) * t) + 32 -def get_plugin_template_dirs(plugin_list): - ret = [] - for plugin in plugin_list: - plugin_module = ".".join(plugin.split(".")[:-1]) - spec = importlib.util.find_spec(plugin_module) - if not spec or not spec.origin: - raise ImproperlyConfigured(f'Cannot find plugin "{plugin}"') - template_dir = os.path.join(os.path.dirname(spec.origin), "templates") - if os.path.isdir(template_dir): - ret.append(template_dir) - return ret - - def get_current_request(): """Retrieve the current request. diff --git a/pykeg/plugin/plugin.py b/pykeg/plugin/plugin.py index ec7143770..82e8be263 100644 --- a/pykeg/plugin/plugin.py +++ b/pykeg/plugin/plugin.py @@ -81,42 +81,6 @@ def get_url(cls): # Plugin methods - def get_admin_settings_view(self): - """Returns the view instance for the main admin settings for this - plugin, or None. - """ - return None - - def get_extra_admin_views(self): - """Returns an iterable of additional views to be installed in the admin - site section this plugin. - - Each item should be a 3-tuple of the form: - (regex, view name, url name) - - Each view will be installed with the name - "plugin--" - """ - return [] - - def get_user_settings_view(self): - """Returns the view instance for the main user settings for this - plugin, or None. - """ - return None - - def get_extra_user_views(self): - """Returns an iterable of additional views to be installed in the - user section for this plugin. - - Each item should be a 3-tuple of the form: - (regex, view name, url name) - - Each view will be installed with the name - "plugin--" - """ - return [] - def handle_new_events(self, event): """Called synchronously when new events are posted. diff --git a/pykeg/plugin/util.py b/pykeg/plugin/util.py index 81dae99c0..d224b8c9d 100644 --- a/pykeg/plugin/util.py +++ b/pykeg/plugin/util.py @@ -4,7 +4,6 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.urls import re_path from django.utils import timezone from .plugin import Plugin @@ -44,29 +43,6 @@ def get_plugins(): return _CACHED_PLUGINS -def get_admin_urls(): - urls = [] - for plugin in list(get_plugins().values()): - urls += _to_urls(plugin.get_extra_admin_views(), plugin.get_short_name()) - return urls - - -def get_account_urls(): - urls = [] - for plugin in list(get_plugins().values()): - urls += _to_urls(plugin.get_extra_user_views(), plugin.get_short_name()) - return urls - - -def _to_urls(urllist, short_name): - urls = [] - for regex, fn, viewname in urllist: - regex = f"plugin/{short_name}/{regex}" - viewname = f"plugin-{short_name}-{viewname}" - urls.append(re_path(regex, fn, name=viewname)) - return urls - - def is_stale(time, now=None): if not now: now = timezone.now() diff --git a/pykeg/settings.py b/pykeg/settings.py index 7a83dbcdf..bcaea433e 100644 --- a/pykeg/settings.py +++ b/pykeg/settings.py @@ -30,15 +30,12 @@ } INSTALLED_APPS = ( - "whitenoise.runserver_nostatic", + # pykeg.core precedes whitenoise so its runserver override (default + # port 8001, behind the vite dev server on 8000) wins. "pykeg.core", + "whitenoise.runserver_nostatic", "pykeg.web", "pykeg.web.api", - "pykeg.web.account", - "pykeg.web.kbregistration", - "pykeg.web.kegadmin", - "pykeg.web.kegweb", - "pykeg.web.setup_wizard", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -47,20 +44,14 @@ "django.contrib.sessions", "django.contrib.staticfiles", "pykeg.api", - "crispy_forms", - "crispy_bootstrap4", "imagekit", - "corsheaders", "rest_framework", "drf_spectacular", "drf_spectacular_sidecar", + "django_filters", "django_rq", ) -LOGIN_REDIRECT_URL = "/account/" - -KEGBOT_ADMIN_LOGIN_URL = "auth_login" - AUTH_USER_MODEL = "core.User" # List of finder classes that know how to find static files in @@ -81,17 +72,13 @@ # Storage backends (Django 5.1+ replaced DEFAULT_FILE_STORAGE / STATICFILES_STORAGE). STORAGES = { "default": { - "BACKEND": "pykeg.web.kegweb.kbstorage.KegbotFileSystemStorage", + "BACKEND": "pykeg.web.kbstorage.KegbotFileSystemStorage", }, "staticfiles": { "BACKEND": _STATICFILES_BACKEND, }, } -# crispy-forms 2.x template pack (Bootstrap 4). -CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap4" -CRISPY_TEMPLATE_PACK = "bootstrap4" - # Default session serialization. SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer" @@ -129,6 +116,9 @@ # Example: "http://media.lawrence.com/static/" STATIC_URL = "/static/" +# The built frontend (vite output) is collected alongside app statics. +STATICFILES_DIRS = [os.path.join(BASE_DIR, "web-ui", "dist")] + # URL prefix for admin static files -- CSS, JavaScript and images. # Make sure to use a trailing slash. # Examples: "http://foo.com/static/admin/", "/static/admin/". @@ -139,7 +129,6 @@ MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", - "corsheaders.middleware.CorsMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "pykeg.web.middleware.ErrorLoggingMiddleware", @@ -154,7 +143,6 @@ "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "pykeg.web.api.middleware.ApiRequestMiddleware", - "pykeg.web.middleware.PrivacyMiddleware", ] AUTHENTICATION_BACKENDS = ("pykeg.web.auth.local.LocalAuthBackend",) @@ -302,12 +290,10 @@ # Storage is configured via STORAGES above (Django 5.1+). -from pykeg.core.util import get_plugin_template_dirs # noqa: E402 (needs KEGBOT_PLUGINS above) - TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": ["web/templates"] + get_plugin_template_dirs(KEGBOT_PLUGINS), + "DIRS": ["web/templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -319,7 +305,6 @@ "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", - "pykeg.web.context_processors.kbsite", ], "debug": DEBUG, }, @@ -335,6 +320,9 @@ REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "pykeg.api.pagination.CursorPagination", "PAGE_SIZE": 10, + "DEFAULT_FILTER_BACKENDS": [ + "django_filters.rest_framework.DjangoFilterBackend", + ], "DEFAULT_PERMISSION_CLASSES": [ "pykeg.api.permissions.IsAuthenticated", ], @@ -342,6 +330,10 @@ "pykeg.api.auth.ApiKeyBasicAuth", "rest_framework.authentication.SessionAuthentication", ), + "DEFAULT_THROTTLE_RATES": { + # Unauthenticated auth flows (register, password reset, activate). + "auth": "30/min", + }, "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } @@ -354,17 +346,14 @@ # Serve the docs UI assets locally (no CDN). "SWAGGER_UI_DIST": "SIDECAR", "SWAGGER_UI_FAVICON_HREF": "SIDECAR", + # Emit separate request components (no readonly fields, binary + # uploads typed as files) so generated clients get correct types. + "COMPONENT_SPLIT_REQUEST": True, + # Names for enums whose auto-derived names would be too generic. + "ENUM_NAME_OVERRIDES": { + "KegStatusEnum": "pykeg.core.models.Keg.STATUS_CHOICES", + "SystemEventKindEnum": "pykeg.core.models.SystemEvent.KINDS", + }, } -CORS_ALLOWED_ORIGINS = [ - "http://localhost:1234", - "http://127.0.0.1:1234", -] -CSRF_TRUSTED_ORIGINS = [ - "http://localhost:1234", - "http://127.0.0.1:1234", -] -CORS_ALLOW_CREDENTIALS = True -SESSION_COOKIE_SAMESITE = None - APPEND_SLASH = True diff --git a/pykeg/util/genconstants.py b/pykeg/util/genconstants.py new file mode 100644 index 000000000..feae0f0d8 --- /dev/null +++ b/pykeg/util/genconstants.py @@ -0,0 +1,40 @@ +"""Builds the constants object shared with the frontend. + +The `print_constants` management command writes this to a TypeScript file +that is baked into the frontend build; regenerate it whenever any of the +source constants change. +""" + +from pykeg.core import keg_sizes, models +from pykeg.core.timezones import timezone_choices + + +def sort_dict_by_keys(d): + return dict(sorted(d.items())) + + +def genconstants(): + """Returns the object written to `web-ui/lib/shared-constants.ts`. + + Note on sorting: most values are sorted by key to keep regeneration + diffs minimal; javascript consumers re-sort for presentation where + ordering matters (e.g. keg types by volume, via KEG_VOLUMES_ML). + """ + return { + "BEVERAGE_TYPES": dict(models.Beverage.TYPES), + "EVENT_KINDS": dict(models.SystemEvent.KINDS), + "KEG_STATUSES": dict(models.Keg.STATUS_CHOICES), + "KEG_STATUS_AVAILABLE": models.Keg.STATUS_AVAILABLE, + "KEG_STATUS_FINISHED": models.Keg.STATUS_FINISHED, + "KEG_STATUS_ON_TAP": models.Keg.STATUS_ON_TAP, + "KEG_TYPES": sort_dict_by_keys(keg_sizes.DESCRIPTIONS), + "KEG_TYPE_OTHER": keg_sizes.OTHER, + "KEG_VOLUMES_ML": sort_dict_by_keys(keg_sizes.VOLUMES_ML), + "PRIVACY_CHOICES": dict(models.KegbotSite.PRIVACY_CHOICES), + "REGISTRATION_MODE_CHOICES": dict(models.KegbotSite.REGISTRATION_MODE_CHOICES), + "TEMPERATURE_DISPLAY_UNITS_CHOICES": dict( + models.KegbotSite.TEMPERATURE_DISPLAY_UNITS_CHOICES + ), + "TIMEZONES": [zone for zone, _ in timezone_choices()], + "VOLUME_DISPLAY_UNITS_CHOICES": dict(models.KegbotSite.VOLUME_DISPLAY_UNITS_CHOICES), + } diff --git a/pykeg/util/genconstants_test.py b/pykeg/util/genconstants_test.py new file mode 100644 index 000000000..4a5436cb9 --- /dev/null +++ b/pykeg/util/genconstants_test.py @@ -0,0 +1,24 @@ +import json + +from django.test import TestCase + +from pykeg.util.genconstants import genconstants + + +class GenConstantsTestCase(TestCase): + def test_output_is_json_serializable(self): + data = genconstants() + round_tripped = json.loads(json.dumps(data)) + self.assertEqual(data, round_tripped) + + def test_expected_keys_present(self): + data = genconstants() + self.assertEqual("on_tap", data["KEG_STATUS_ON_TAP"]) + self.assertIn("half-barrel", data["KEG_TYPES"]) + self.assertIn("half-barrel", data["KEG_VOLUMES_ML"]) + self.assertEqual( + ["public", "members", "staff"], + list(data["PRIVACY_CHOICES"]), + ) + self.assertIn("UTC", data["TIMEZONES"]) + self.assertIn("drink_poured", data["EVENT_KINDS"]) diff --git a/pykeg/web/account/__init__.py b/pykeg/web/account/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/account/templates/account/activate_account.html b/pykeg/web/account/templates/account/activate_account.html deleted file mode 100644 index db7799d95..000000000 --- a/pykeg/web/account/templates/account/activate_account.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Activate Your Account{% endblock %} -{% block pagetitle %}Activate Your Account{% endblock %} - -{% block kb-account-main %} -

Choose a Password

-
-
- {% csrf_token %} - {{ form|crispy }} -
- -
-
-
- -{% endblock %} diff --git a/pykeg/web/account/templates/account/base.html b/pykeg/web/account/templates/account/base.html deleted file mode 100644 index f0f116f81..000000000 --- a/pykeg/web/account/templates/account/base.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block content %} -
-
- -
- -
- {% block kb-account-main %}{% endblock %} -
-
- -{% endblock %} diff --git a/pykeg/web/account/templates/account/index.html b/pykeg/web/account/templates/account/index.html deleted file mode 100644 index 7a1f26181..000000000 --- a/pykeg/web/account/templates/account/index.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "account/base.html" %} -{% load crispy_forms_tags %} - -{% block title %}Account Settings | {{ block.super }}{% endblock %} -{% block pagetitle %}Account Settings{% endblock %} - -{% block kb-account-main %} - -{% if user.is_staff or user.is_superuser %} -

API Access

-

- Use this API key to access the kegbot web service. Ssh! Keep it secret! -

- -
{{ user.get_api_key }}
- -
-{% csrf_token %} -{{ apikey_form.as_p }} - -
- -{% else %} -

Hello, {{user.get_full_name}}!

-{% endif %} - -{% endblock %} diff --git a/pykeg/web/account/templates/account/invite.html b/pykeg/web/account/templates/account/invite.html deleted file mode 100644 index 7a94ccc5b..000000000 --- a/pykeg/web/account/templates/account/invite.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Invite a New User | {{ block.super }}{% endblock %} -{% block pagetitle %}Invite a New User{% endblock %} - -{% block kb-account-main %} -

Invite a New User

-
-
- {% csrf_token %} - {{ form|crispy }} -
- -
-
-
- -{% endblock %} diff --git a/pykeg/web/account/templates/account/notifications.html b/pykeg/web/account/templates/account/notifications.html deleted file mode 100644 index 29bd079c5..000000000 --- a/pykeg/web/account/templates/account/notifications.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Account: E-Mail & Notifications | {{ block.super }}{% endblock %} -{% block pagetitle %}Account: E-Mail & Notifications{% endblock %} - -{% block kb-account-main %} - -

E-Mail Address

-
-
- {% csrf_token %} - {{ email_form|crispy }} - - -
-
- - -

E-Mail Notification Preferences

-
-

- Notify me via e-mail when: -

- -
- {% csrf_token %} - {{ form|crispy }} - - -
- -
- -{% endblock %} diff --git a/pykeg/web/account/templates/account/profile.html b/pykeg/web/account/templates/account/profile.html deleted file mode 100644 index 80ecf2eea..000000000 --- a/pykeg/web/account/templates/account/profile.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Account: Edit Profile | {{ block.super }}{% endblock %} -{% block pagetitle %}Account: Edit Profile{% endblock %} - -{% block kb-account-main %} -

Edit Profile

-
-
- {% csrf_token %} -
- -
- {% mugshot_box user 100 %} -
-
- {{ form|crispy }} -
- -
-
-
- -{% endblock %} diff --git a/pykeg/web/account/urls.py b/pykeg/web/account/urls.py deleted file mode 100644 index f2fa3d78f..000000000 --- a/pykeg/web/account/urls.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.contrib.auth.views import PasswordChangeDoneView, PasswordChangeView -from django.urls import path - -from pykeg.plugin import util -from pykeg.web.account import views - -urlpatterns = [ - path("", views.account_main, name="kb-account-main"), - path( - "activate//", - views.activate_account, - name="activate-account", - ), - path("password/done/", PasswordChangeDoneView.as_view(), name="password_change_done"), - path("password/", PasswordChangeView.as_view(), name="password_change"), - path("profile/", views.edit_profile, name="account-profile"), - path("invite/", views.invite, name="account-invite"), - path("confirm-email/", views.confirm_email, name="account-confirm-email"), - path("notifications/", views.notifications, name="account-notifications"), - path("regenerate-api-key/", views.regenerate_api_key, name="regen-api-key"), - path("plugin//", views.plugin_settings, name="account-plugin-settings"), -] - -urlpatterns += util.get_account_urls() diff --git a/pykeg/web/account/views.py b/pykeg/web/account/views.py deleted file mode 100644 index 378e334d6..000000000 --- a/pykeg/web/account/views.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python -# -"""Kegweb main views.""" - -from django.contrib import messages -from django.contrib.auth import authenticate -from django.contrib.auth import login as auth_login -from django.contrib.auth.decorators import login_required -from django.http import Http404 -from django.shortcuts import redirect, render -from django.views.decorators.cache import never_cache -from django.views.decorators.http import require_POST - -from pykeg.core import models -from pykeg.notification.forms import NotificationSettingsForm -from pykeg.util import email -from pykeg.web.kegweb import forms - - -@login_required -def account_main(request): - context = {} - context["user"] = request.user - return render(request, "account/index.html", context=context) - - -@login_required -def edit_profile(request): - context = {} - user = request.user - - context["form"] = forms.ProfileForm(initial={"display_name": user.get_full_name()}) - - if request.method == "POST": - form = forms.ProfileForm(request.POST, request.FILES) - context["form"] = form - if form.is_valid(): - if "new_mugshot" in request.FILES: - pic = models.Picture.objects.create(user=user) - image = request.FILES["new_mugshot"] - pic.image.save(image.name, image) - pic.save() - user.mugshot = pic - user.display_name = form.cleaned_data["display_name"] - user.save() - return render(request, "account/profile.html", context=context) - - -@login_required -def invite(request): - context = {} - form = forms.InvitationForm() - - if not request.kbsite.can_invite(request.user): - raise Http404("Cannot invite from this account.") - - if request.method == "POST": - form = forms.InvitationForm(request.POST) - if form.is_valid(): - email = form.cleaned_data["email"] - invite = models.Invitation.objects.create(for_email=email, invited_by=request.user) - invite.send() - messages.success(request, "Invitation mailed to " + email) - - context["form"] = form - return render(request, "account/invite.html", context=context) - - -@login_required -def notifications(request): - # TODO(mikey): Dynamically add settings forms for other e-mail - # backends (currently hardcoded to email backend). - - context = {} - existing_settings = models.NotificationSettings.objects.get_or_create( - user=request.user, backend="pykeg.notification.backends.email.EmailNotificationBackend" - )[0] - - if request.method == "POST": - if "submit-settings" in request.POST: - form = NotificationSettingsForm(request.POST, instance=existing_settings) - if form.is_valid(): - instance = form.save(commit=False) - instance.user = request.user - instance.backend = "pykeg.notification.backends.email.EmailNotificationBackend" - instance.save() - messages.success(request, "Settings updated") - existing_settings = instance - - elif "submit-email" in request.POST: - form = forms.ChangeEmailForm(request.POST) - if form.is_valid(): - new_email = form.cleaned_data["email"] - if new_email == request.user.email: - messages.warning(request, "E-mail address unchanged.") - else: - token = email.build_email_change_token(request.user, new_email) - url = models.KegbotSite.get().reverse_full( - "account-confirm-email", args=(), kwargs={"token": token} - ) - - email_context = {"url": url, "site_name": request.kbsite.title} - message = email.build_message( - new_email, "registration/email_confirm_email_change.html", email_context - ) - message.send() - messages.success( - request, f"An e-mail confirmation has been sent to {new_email}" - ) - - else: - messages.error(request, "Unknown request.") - - context["form"] = NotificationSettingsForm(instance=existing_settings) - context["email_form"] = forms.ChangeEmailForm(initial={"email": request.user.email}) - - return render(request, "account/notifications.html", context=context) - - -@login_required -def confirm_email(request, token): - try: - uid, new_address = email.verify_email_change_token(request.user, token) - if uid != request.user.id: - messages.error(request, "E-mail confirmation does not exist for this account.") - elif request.user.email != new_address: - request.user.email = new_address - request.user.save() - messages.success(request, "E-mail address successfully changed.") - else: - messages.warning(request, "E-mail address unchanged.") - except ValueError: - messages.error(request, "That token is not valid.") - - return redirect("account-notifications") - - -@never_cache -def activate_account(request, activation_key): - users = models.User.objects.filter(activation_key=activation_key) - if users.count() != 1: - raise Http404("No such activation key") - user = users[0] - - assert not user.has_usable_password(), "User already has a usable password" - - form = forms.ActivateAccountForm() - if request.method == "POST": - form = forms.ActivateAccountForm(request.POST) - if form.is_valid(): - cd = form.cleaned_data - - # Set the password and revoke the activation key. - user.set_password(cd.get("password")) - user.activation_key = None - user.save() - - # Log the user in. - user = authenticate(username=user.username, password=cd.get("password")) - auth_login(request, user) - if request.session.test_cookie_worked(): - request.session.delete_test_cookie() - - messages.success(request, "Your account has been activated!") - return redirect("kb-account-main") - - context = {} - context["form"] = form - return render(request, "account/activate_account.html", context=context) - - -@login_required -@require_POST -def regenerate_api_key(request): - form = forms.RegenerateApiKeyForm(request.POST) - if form.is_valid(): - key, is_new = models.ApiKey.objects.get_or_create(user=request.user) - key.regenerate() - key.save() - return redirect("kb-account-main") - - -@login_required -def plugin_settings(request, plugin_name): - plugin = request.plugins.get(plugin_name, None) - if not plugin: - raise Http404(f'Plugin "{plugin_name}" not loaded') - - view = plugin.get_user_settings_view() - if not view: - raise Http404("No user settings for this plugin") - - return view(request, plugin) diff --git a/pykeg/web/api/forms.py b/pykeg/web/api/forms.py index 7bcd4244f..9eb77d10f 100644 --- a/pykeg/web/api/forms.py +++ b/pykeg/web/api/forms.py @@ -1,5 +1,6 @@ from django import forms +from pykeg.core import models from pykeg.core.kb_common import USERNAME_REGEX @@ -34,3 +35,19 @@ class ThermoPostForm(forms.Form): class TapCreateForm(forms.Form): name = forms.CharField() + + +class ControllerForm(forms.ModelForm): + """Relocated from the old kegadmin app; used by legacy POSTs.""" + + class Meta: + model = models.Controller + fields = ("name", "model_name", "serial_number") + + +class NewFlowMeterForm(forms.ModelForm): + """Relocated from the old kegadmin app; used by legacy POSTs.""" + + class Meta: + model = models.FlowMeter + fields = ("port_name", "ticks_per_ml", "controller") diff --git a/pykeg/web/api/views.py b/pykeg/web/api/views.py index f851df24f..64f885606 100644 --- a/pykeg/web/api/views.py +++ b/pykeg/web/api/views.py @@ -18,7 +18,7 @@ from pykeg.core import models from pykeg.core import util as core_util from pykeg.web.api import exceptions, forms, serialize, util -from pykeg.web.kegadmin.forms import ControllerForm, NewFlowMeterForm +from pykeg.web.api.forms import ControllerForm, NewFlowMeterForm _LOGGER = logging.getLogger(__name__) diff --git a/pykeg/web/charts/__init__.py b/pykeg/web/charts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/charts/charts.py b/pykeg/web/charts/charts.py deleted file mode 100644 index 4ea01d69c..000000000 --- a/pykeg/web/charts/charts.py +++ /dev/null @@ -1,216 +0,0 @@ -import datetime - -from django.utils import timezone - -from pykeg.core import models -from pykeg.core.util import CtoF -from pykeg.util import units - - -class ChartError(Exception): - """Base chart exception.""" - - -def format_volume(volume_ml, chart_kwargs): - metric_volumes = chart_kwargs.get("metric_volumes", False) - if metric_volumes: - return volume_ml / 1000.0, "L" - else: - return units.Quantity(volume_ml).InPints(), "pints" - - -def format_temperature(temp_c, chart_kwargs): - use_c = chart_kwargs.get("temperature_units", None) == "c" - if use_c: - return temp_c - else: - return CtoF(temp_c) - - -def chart_temp_sensor(sensor, *args, **kwargs): - """Shows a simple line plot of a specific temperature sensor. - - Syntax: - {% chart temp_sensor width height %} - Args: - sensorname - the nice_name of a ThermoSensor - """ - if not isinstance(sensor, models.ThermoSensor): - raise ChartError("Bad sensor given") - - hours = 6 - now = timezone.now() - start = now - (datetime.timedelta(hours=hours)) - start = start - (datetime.timedelta(seconds=start.second)) - - points = sensor.thermolog_set.filter(time__gte=start).order_by("time") - - curr = start - temps = [] - have_temps = False - for point in points: - temp = format_temperature(point.temp, kwargs) - while curr <= point.time: - curr += datetime.timedelta(minutes=1) - if curr < point.time: - temps.append(None) - else: - temps.append(temp) - have_temps = True - - if not have_temps: - raise ChartError("Not enough data") - - res = { - "series": [ - { - "data": temps, - "marker": { - "enabled": False, - }, - }, - ], - "tooltip": { - "enabled": True, - }, - "xAxis": { - "categories": ["Temperature"], - "labels": { - "enabled": False, - }, - "tickInterval": 60, - }, - "yAxis": { - "labels": { - "enabled": True, - }, - "tickInterval": 1, - }, - } - return res - - -def chart_volume_by_weekday(stats, *args, **kwargs): - """Shows a histogram of volume by day of the week. - - Syntax: - {% chart volume_by_weekday width height %} - Args: - stats - a stats object containing volume_by_day_of_week - """ - volmap = [0] * 7 - vols = stats.get("volume_by_day_of_week", {}) - if not volmap: - raise ChartError("Daily volumes unavailable") - - for weekday, volume_ml in list(vols.items()): - volmap[int(weekday)] += format_volume(volume_ml, kwargs)[0] - return _weekday_chart_common(volmap) - - -def chart_sessions_by_weekday(stats, *args, **kwargs): - data = stats.get("volume_by_day_of_week", {}) - weekdays = [0] * 7 - for weekday, volume_ml in list(data.items()): - weekdays[int(weekday)] += format_volume(volume_ml, kwargs)[0] - return _weekday_chart_common(weekdays) - - -def chart_sessions_by_volume(stats, *args, **kwargs): - buckets = [0] * 6 - labels = ["<1", "1.0-1.9", "2.0-2.9", "3.0-3.9", "4.0-4.9", "5+"] - volmap = stats.get("volume_by_session", {}) - for session_volume in list(volmap.values()): - volume = round(format_volume(session_volume, kwargs)[0], 1) - intval = int(volume) - if intval >= len(buckets): - buckets[-1] += 1 - else: - buckets[intval] += 1 - - res = { - "xAxis": { - "categories": labels, - }, - "series": [ - {"data": buckets}, - ], - "yAxis": { - "min": 0, - }, - "chart": { - "defaultSeriesType": "column", - }, - } - return res - - -def chart_users_by_volume(stats, *args, **kwargs): - vols = stats.get("volume_by_drinker") - if not vols: - raise ChartError("no data") - - data = [] - for username, volume in list(vols.items()): - if not username: - username = "Guest" - volume, units = format_volume(volume, kwargs) - label = f"{username} ({volume:.1f} {units})" - data.append((label, volume)) - - other_vol = 0 - data.sort(key=lambda item: item[1]) - for username, pints in data[10:]: - other_vol += pints - data = data[:10] - data.reverse() - - if other_vol: - label = "{} ({:.1f})".format("all others", other_vol) - data.append((label, other_vol)) - - res = { - "series": [ - { - "type": "pie", - "name": "Drinkers by Volume", - "data": data, - } - ], - "yAxis": { - "min": 0, - }, - "chart": { - "defaultSeriesType": "column", - }, - "tooltip": { - "enabled": False, - }, - } - return res - - -def _weekday_chart_common(vals): - labels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] - - # convert from 0=Monday to 0=Sunday - # vals.insert(0, vals.pop(-1)) - - res = { - "xAxis": { - "categories": labels, - }, - "yAxis": { - "min": 0, - }, - "series": [ - {"data": vals}, - ], - "tooltip": { - "enabled": False, - }, - "chart": { - "defaultSeriesType": "column", - }, - } - return res diff --git a/pykeg/web/context_processors.py b/pykeg/web/context_processors.py deleted file mode 100644 index c55fb13c3..000000000 --- a/pykeg/web/context_processors.py +++ /dev/null @@ -1,51 +0,0 @@ -import urllib.error -import urllib.parse -import urllib.request - -from django.conf import settings - -from pykeg.core import models, util -from pykeg.web.kegweb.forms import LoginForm - - -def kbsite(request): - kbsite = getattr(request, "kbsite", None) - - redir = urllib.parse.urlencode({"redir": request.build_absolute_uri(request.path)}) - - sso_login_url = getattr(settings, "SSO_LOGIN_URL", "") - if sso_login_url: - sso_login_url = f"{sso_login_url}?{redir}" - - sso_logout_url = getattr(settings, "SSO_LOGOUT_URL", "") - if sso_logout_url: - sso_logout_url = f"{sso_logout_url}?{redir}" - - ret = { - "DEBUG": settings.DEBUG, - "VERSION": util.get_version(), - "HAVE_SESSIONS": False, - "KEGBOT_ENABLE_ADMIN": settings.KEGBOT_ENABLE_ADMIN, - "ENABLE_SENSING": kbsite.enable_sensing if kbsite else True, - "ENABLE_USERS": kbsite.enable_users if kbsite else True, - "GOOGLE_ANALYTICS_ID": None, - "SSO_LOGIN_URL": sso_login_url, - "SSO_LOGOUT_URL": sso_logout_url, - "CAN_INVITE": kbsite.can_invite(request.user) if kbsite else False, - "kbsite": kbsite, - "request_path": request.path, - "login_form": LoginForm(initial={"next_page": request.path}), - "guest_info": { - "name": "guest", - "image": None, - }, - "PLUGINS": getattr(request, "plugins", {}), - } - - if kbsite: - ret["HAVE_SESSIONS"] = models.DrinkingSession.objects.all().count() > 0 - ret["GOOGLE_ANALYTICS_ID"] = kbsite.google_analytics_id - ret["metric_volumes"] = kbsite.volume_display_units == "metric" - ret["temperature_display_units"] = kbsite.temperature_display_units - - return ret diff --git a/pykeg/web/decorators.py b/pykeg/web/decorators.py deleted file mode 100644 index a563d065e..000000000 --- a/pykeg/web/decorators.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.conf import settings -from django.contrib.auth import REDIRECT_FIELD_NAME -from django.contrib.auth.decorators import user_passes_test - - -def staff_member_required( - view_func, redirect_field_name=REDIRECT_FIELD_NAME, login_url=settings.KEGBOT_ADMIN_LOGIN_URL -): - """ - Clone of django.contrib.admin.views.decorators.staff_member_required that - uses `settings.KEGBOT_ADMIN_LOGIN_URL` as the default login URL. - """ - return user_passes_test( - lambda u: u.is_active and u.is_staff, - login_url=login_url, - redirect_field_name=redirect_field_name, - )(view_func) diff --git a/pykeg/web/kbregistration/__init__.py b/pykeg/web/kbregistration/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/kbregistration/forms.py b/pykeg/web/kbregistration/forms.py deleted file mode 100644 index 5d665819c..000000000 --- a/pykeg/web/kbregistration/forms.py +++ /dev/null @@ -1,89 +0,0 @@ -import urllib.parse - -from django import forms -from django.conf import settings -from django.contrib.auth.tokens import default_token_generator -from django.template import loader -from django.utils.encoding import force_bytes -from django.utils.http import urlsafe_base64_encode -from django.utils.translation import gettext_lazy as _ - -from pykeg.web.util import get_base_url - -try: - from django.contrib.auth import get_user_model - - User = get_user_model() -except ImportError: - from django.contrib.auth.models import User - -from pykeg.core import models - - -class KegbotRegistrationForm(forms.ModelForm): - class Meta: - model = models.User - fields = ("email", "username") - - password1 = forms.CharField(widget=forms.PasswordInput, label=_("Password")) - password2 = forms.CharField(widget=forms.PasswordInput, label=_("Password (again)")) - - def clean(self): - super().clean() - if "password1" in self.cleaned_data and "password2" in self.cleaned_data: - if self.cleaned_data["password1"] != self.cleaned_data["password2"]: - raise forms.ValidationError(_("The two password fields didn't match.")) - return self.cleaned_data - - -class PasswordResetForm(forms.Form): - email = forms.EmailField(label=_("Email"), max_length=254) - - def save( - self, - domain_override=None, - subject_template_name="registration/password_reset_subject.txt", - email_template_name="registration/password_reset_email.html", - use_https=False, - token_generator=default_token_generator, - from_email=None, - request=None, - html_email_template_name=None, - extra_email_context=None, - ): - """ - Generates a one-use only link for resetting password and sends to the - user. - """ - from django.core.mail import send_mail - - email = self.cleaned_data["email"] - active_users = User._default_manager.filter(email__iexact=email, is_active=True) - for user in active_users: - # Make sure that no email is sent to a user that actually has - # a password marked as unusable - if not user.has_usable_password(): - continue - from_email = settings.DEFAULT_FROM_EMAIL or from_email - - base_url = get_base_url() - parsed = urllib.parse.urlparse(base_url) - domain = parsed.netloc - protocol = parsed.scheme - - kbsite = models.KegbotSite.get() - site_name = kbsite.title - c = { - "email": user.email, - "site_name": site_name, - "uid": urlsafe_base64_encode(force_bytes(user.pk)), - "user": user, - "token": token_generator.make_token(user), - "domain": domain, - "protocol": protocol, - } - subject = loader.render_to_string(subject_template_name, c) - # Email subject *must not* contain newlines - subject = "".join(subject.splitlines()) - email = loader.render_to_string(email_template_name, c) - send_mail(subject, email, from_email, [user.email]) diff --git a/pykeg/web/kbregistration/registration_test.py b/pykeg/web/kbregistration/registration_test.py deleted file mode 100644 index f31bf61c1..000000000 --- a/pykeg/web/kbregistration/registration_test.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Unittests for registration functions.""" - -from django.core import mail -from django.test import TestCase -from django.test.utils import override_settings - -from pykeg.core import defaults -from pykeg.core import models as core_models - - -class ForgotPasswordTest(TestCase): - def setUp(self): - defaults.set_defaults(set_is_setup=True) - - self.user = core_models.User.objects.create( - username="notification_user", email="test@example.com" - ) - - # Password reset requires a usable password. - self.user.set_password("1234") - self.user.save() - - @override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend") - @override_settings(DEFAULT_FROM_EMAIL="test-from@example") - def test_notifications(self): - response = self.client.get("/accounts/password/reset/") - self.assertContains(response, "Reset Password", status_code=200) - self.assertEqual(0, len(mail.outbox)) - - response = self.client.post( - "/accounts/password/reset/", data={"email": "test@example.com"}, follow=True - ) - self.assertContains(response, "E-Mail Sent", status_code=200) - self.assertEqual(1, len(mail.outbox)) - - msg = mail.outbox[0] - - # TODO(mikey): Customize subject with `kbsite.title` - self.assertEqual("Password reset", msg.subject) - self.assertEqual(["test@example.com"], msg.to) - self.assertEqual("test-from@example", msg.from_email) diff --git a/pykeg/web/kbregistration/urls.py b/pykeg/web/kbregistration/urls.py deleted file mode 100644 index 56aee4d9b..000000000 --- a/pykeg/web/kbregistration/urls.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.contrib.auth.views import ( - PasswordChangeDoneView, - PasswordChangeView, - PasswordResetCompleteView, - PasswordResetConfirmView, - PasswordResetDoneView, - PasswordResetView, -) -from django.urls import include, path - -from pykeg.web.kbregistration import views -from pykeg.web.kbregistration.forms import PasswordResetForm - -urlpatterns = [ - path("register/", views.register, name="registration_register"), - path("password/change/", PasswordChangeView.as_view(), name="password_change"), - path("password/change/done/", PasswordChangeDoneView.as_view(), name="password_change_done"), - path( - "password/reset/", - PasswordResetView.as_view(), - kwargs={"password_reset_form": PasswordResetForm}, - name="password_reset", - ), - path("password/reset/done/", PasswordResetDoneView.as_view(), name="password_reset_done"), - path( - "password/reset/complete/", - PasswordResetCompleteView.as_view(), - name="password_reset_complete", - ), - path( - "password/reset/confirm/-/", - PasswordResetConfirmView.as_view(), - name="password_reset_confirm", - ), - path("", include("django.contrib.auth.urls")), -] diff --git a/pykeg/web/kbregistration/views.py b/pykeg/web/kbregistration/views.py deleted file mode 100644 index 100d98738..000000000 --- a/pykeg/web/kbregistration/views.py +++ /dev/null @@ -1,61 +0,0 @@ -from django.contrib.auth import authenticate, login -from django.shortcuts import redirect, render - -from pykeg.core import models -from pykeg.web.kbregistration.forms import KegbotRegistrationForm - -"""Kegbot-aware registration views.""" - - -def register(request): - context = {} - form = KegbotRegistrationForm() - - # Check if we need an invitation before processing the request further. - invite = None - if request.kbsite.registration_mode != "public": - invite_code = None - if "invite_code" in request.GET: - invite_code = request.GET["invite_code"] - request.session["invite_code"] = invite_code - else: - invite_code = request.session.get("invite_code", None) - - if not invite_code: - r = render(request, "registration/invitation_required.html", context=context) - r.status_code = 401 - return r - - try: - invite = models.Invitation.objects.get(invite_code=invite_code) - except models.Invitation.DoesNotExist: - pass - - if not invite or invite.is_expired(): - r = render(request, "registration/invitation_expired.html", context=context) - r.status_code = 401 - return r - - if request.method == "POST": - form = KegbotRegistrationForm(request.POST) - if form.is_valid(): - username = form.cleaned_data["username"] - email = form.cleaned_data["email"] - password = form.cleaned_data.get("password1") - - models.User.create_new_user(username=username, email=email, password=password) - - if invite: - invite.delete() - if "invite_code" in request.session: - del request.session["invite_code"] - - if password: - new_user = authenticate(username=username, password=password) - login(request, new_user) - return redirect("kb-account-main") - - return render(request, "registration/registration_complete.html", context=context) - - context["form"] = form - return render(request, "registration/registration_form.html", context=context) diff --git a/pykeg/web/kegweb/kbstorage.py b/pykeg/web/kbstorage.py similarity index 100% rename from pykeg/web/kegweb/kbstorage.py rename to pykeg/web/kbstorage.py diff --git a/pykeg/web/kegadmin/__init__.py b/pykeg/web/kegadmin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/kegadmin/forms.py b/pykeg/web/kegadmin/forms.py deleted file mode 100644 index 11a13fb00..000000000 --- a/pykeg/web/kegadmin/forms.py +++ /dev/null @@ -1,819 +0,0 @@ -from crispy_forms.bootstrap import FormActions -from crispy_forms.helper import FormHelper -from crispy_forms.layout import HTML, Field, Layout, Submit -from django import forms -from django.contrib.humanize.templatetags.humanize import naturaltime - -from pykeg.core import keg_sizes, models -from pykeg.util import units - -ALL_TAPS = models.KegTap.objects.all() -ALL_KEGS = models.Keg.objects.all() -ALL_METERS = models.FlowMeter.objects.all() -ALL_TOGGLES = models.FlowToggle.objects.all() -ALL_THERMOS = models.ThermoSensor.objects.all() - - -class ChangeKegForm(forms.Form): - keg_size = forms.ChoiceField( - choices=keg_sizes.CHOICES, initial=keg_sizes.HALF_BARREL, required=True - ) - - initial_volume = forms.FloatField( - label="Initial Volume", initial=0.0, required=False, help_text="Keg's Initial Volume" - ) - - beer_name = forms.CharField(required=False) # legacy - brewer_name = forms.CharField(required=False) # legacy - - beverage_name = forms.CharField(label="Beer Name", required=False) - beverage_id = forms.CharField(widget=forms.HiddenInput(), required=False) - producer_name = forms.CharField(label="Brewer", required=False) - producer_id = forms.CharField(widget=forms.HiddenInput(), required=False) - style_name = forms.CharField( - required=True, label="Style", help_text="Example: Pale Ale, Stout, etc." - ) - - helper = FormHelper() - helper.form_class = "form-horizontal beer-select" - helper.layout = Layout( - Field("beverage_name", css_class="input-xlarge"), - Field("beverage_id", type="hidden"), - Field("producer_name", css_class="input-xlarge"), - Field("producer_id", type="hidden"), - Field("style_name", css_class="input-xlarge"), - Field("keg_size", css_class="input-xlarge"), - Field("initial_volume", css_class="input-volume"), - FormActions( - Submit("submit_change_keg_form", "Activate Keg", css_class="btn-primary"), - ), - ) - - def clean_beverage_name(self): - beverage_name = self.cleaned_data.get("beverage_name") - if not beverage_name: - beverage_name = self.cleaned_data.get("beer_name") - if not beverage_name: - raise forms.ValidationError("Must specify a beverage name") - self.cleaned_data["beverage_name"] = beverage_name - return beverage_name - - def clean_producer_name(self): - producer_name = self.cleaned_data.get("producer_name") - if not producer_name: - producer_name = self.cleaned_data.get("brewer_name") - if not producer_name: - raise forms.ValidationError("Must specify a producer name") - self.cleaned_data["producer_name"] = producer_name - return producer_name - - def save(self, tap): - if not self.is_valid(): - raise ValueError("Form is not valid.") - - if tap.is_active(): - tap.end_current_keg() - - keg_size = self.cleaned_data.get("keg_size") - full_volume_ml = self.cleaned_data.get("full_volume_ml") - - if keg_size != "other": - full_volume_ml = None - else: - full_volume_ml = self.cleaned_data.get("initial_volume") - - # TODO(mikey): Support non-beer beverage types. - cd = self.cleaned_data - keg = models.Keg.start_keg( - tap, - beverage_name=cd["beverage_name"], - producer_name=cd["producer_name"], - beverage_type="beer", - style_name=cd["style_name"], - keg_type=cd["keg_size"], - full_volume_ml=full_volume_ml, - ) - - if cd.get("description"): - keg.description = cd["description"] - keg.save() - - -class EndKegForm(forms.Form): - keg = forms.ModelChoiceField(queryset=ALL_KEGS, widget=forms.HiddenInput) - - helper = FormHelper() - helper.form_class = "form-horizontal beer-select" - helper.layout = Layout( - Field("keg", type="hidden"), - FormActions( - Submit("submit_end_keg_form", "End Keg", css_class="btn-danger"), - ), - ) - - -class TapForm(forms.ModelForm): - class FlowMeterModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, meter): - if meter.tap: - return f"{meter} (connected to {meter.tap.name})" - else: - return str(meter) - - class FlowToggleModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, toggle): - if toggle.tap: - return f"{toggle} (connected to {toggle.tap.name})" - else: - return str(toggle) - - class ThermoSensorModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, sensor): - last_log = sensor.LastLog() - if last_log: - return f"{sensor} (Last report: {naturaltime(last_log.time)})" - else: - return str(sensor) - - meter = FlowMeterModelChoiceField( - queryset=ALL_METERS, - required=False, - empty_label="Not connected.", - help_text="Tap is routed thorough this flow meter. If unset, reporting is disabled.", - ) - - toggle = FlowToggleModelChoiceField( - queryset=ALL_TOGGLES, - required=False, - empty_label="Not connected.", - help_text="Optional flow toggle (usually a relay/valve) connected to this tap.", - ) - - temperature_sensor = ThermoSensorModelChoiceField( - queryset=ALL_THERMOS, - required=False, - empty_label="No sensor.", - help_text="Optional sensor monitoring the temperature at this tap.", - ) - - class Meta: - model = models.KegTap - fields = ("name", "notes", "temperature_sensor", "sort_order") - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if self.instance: - self.fields["meter"].initial = self.instance.current_meter() - self.fields["toggle"].initial = self.instance.current_toggle() - self.fields["temperature_sensor"].initial = self.instance.temperature_sensor - - def save(self, commit=True): - if not commit: - raise ValueError("TapForm does not support commit=False") - tap = super().save(commit=True) - tap.connect_meter(self.cleaned_data["meter"]) - tap.connect_toggle(self.cleaned_data["toggle"]) - return tap - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("name", css_class="input-xlarge"), - Field("meter", css_class="input-xlarge"), - Field("toggle", css_class="input-xlarge"), - Field("temperature_sensor", css_class="input-xlarge"), - Field("sort_order", css_class="input-xlarge"), - Field("notes", css_class="input-block-level", rows="3"), - FormActions( - Submit("submit_tap_form", "Save Settings", css_class="btn-success"), - ), - ) - - -class DeleteTapForm(forms.Form): - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - FormActions( - Submit("submit_delete_tap_form", "Delete Tap", css_class="btn-danger"), - ) - ) - - -class KegForm(forms.Form): - keg_size = forms.ChoiceField( - choices=keg_sizes.CHOICES, initial=keg_sizes.HALF_BARREL, required=True - ) - - initial_volume = forms.FloatField( - label="Initial Volume", initial=0.0, required=False, help_text="Keg's Initial Volume" - ) - - beer_name = forms.CharField(required=False) # legacy - brewer_name = forms.CharField(required=False) # legacy - - beverage_name = forms.CharField(label="Beer Name", required=False) - beverage_id = forms.CharField(widget=forms.HiddenInput(), required=False) - producer_name = forms.CharField(label="Brewer", required=False) - producer_id = forms.CharField(widget=forms.HiddenInput(), required=False) - style_name = forms.CharField( - required=True, label="Style", help_text="Example: Pale Ale, Stout, etc." - ) - - description = forms.CharField( - max_length=256, - label="Description", - widget=forms.Textarea(), - required=False, - help_text="Optional user-visible description of the keg.", - ) - notes = forms.CharField( - label="Notes", - required=False, - widget=forms.Textarea(), - help_text="Optional private notes about this keg, viewable only by admins.", - ) - connect_to = forms.ModelChoiceField( - queryset=ALL_TAPS, - label="Connect To", - required=False, - help_text="If selected, immediately activates the keg on this tap. " - "(Any existing keg will be ended.)", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal beer-select" - helper.layout = Layout( - Field("beverage_name", css_class="input-xlarge"), - Field("beverage_id", type="hidden"), - Field("producer_name", css_class="input-xlarge"), - Field("producer_id", type="hidden"), - Field("style_name", css_class="input-xlarge"), - Field("keg_size", css_class="input-xlarge"), - Field("initial_volume", css_class="input-volume"), - Field("description", css_class="input-block-level", rows="3"), - Field("notes", css_class="input-block-level", rows="3"), - Field("connect_to", css_class="input-block-level"), - FormActions( - Submit("submit_add_keg", "Save", css_class="btn-primary"), - ), - ) - - def clean_beverage_name(self): - beverage_name = self.cleaned_data.get("beverage_name") - if not beverage_name: - beverage_name = self.cleaned_data.get("beer_name") - if not beverage_name: - raise forms.ValidationError("Must specify a beverage name") - self.cleaned_data["beverage_name"] = beverage_name - return beverage_name - - def clean_producer_name(self): - producer_name = self.cleaned_data.get("producer_name") - if not producer_name: - producer_name = self.cleaned_data.get("brewer_name") - if not producer_name: - raise forms.ValidationError("Must specify a producer name") - self.cleaned_data["producer_name"] = producer_name - return producer_name - - def save(self): - if not self.is_valid(): - raise ValueError("Form is not valid.") - keg_size = self.cleaned_data.get("keg_size") - if keg_size != "other": - full_volume_ml = None - else: - full_volume_ml = self.cleaned_data.get("initial_volume") - - # TODO(mikey): Support non-beer beverage types. - cd = self.cleaned_data - keg = models.Keg.create_keg( - beverage_name=cd["beverage_name"], - producer_name=cd["producer_name"], - beverage_type="beer", - style_name=cd["style_name"], - keg_type=cd["keg_size"], - full_volume_ml=full_volume_ml, - notes=cd["notes"], - description=cd["description"], - ) - - tap = cd["connect_to"] - if tap: - if tap.is_active(): - tap.end_current_keg() - tap.attach_keg(keg) - - return keg - - -class EditKegForm(forms.ModelForm): - class Meta: - model = models.Keg - fields = ( - "type", - "keg_type", - "full_volume_ml", - "spilled_ml", - "description", - "notes", - ) - labels = { - "full_volume_ml": ("Full/Initial Volume"), - "spilled_ml": ("Spilled Volume"), - } - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("type", css_class="input-block-level", rows="3"), - Field("keg_type", css_class="input-block-level", rows="3"), - Field("full_volume_ml", css_class="input-volume"), - Field("spilled_ml", css_class="input-volume"), - Field("description", css_class="input-block-level", rows="3"), - Field("notes", css_class="input-block-level", rows="3"), - FormActions( - Submit("submit_edit_keg", "Save Keg", css_class="btn-primary"), - HTML( - """ - Permanently Delete""" - ), - ), - ) - - -class GeneralSiteSettingsForm(forms.ModelForm): - class Meta: - model = models.KegbotSite - fields = ( - "title", - "enable_sensing", - "enable_users", - "privacy", - "registration_mode", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("title", css_class="input-xlarge"), - Field("enable_sensing", css_class="input-xlarge"), - Field("enable_users", css_class="input-xlarge"), - Field("privacy", css_class="input-xlarge"), - Field("registration_mode", css_class="input-xlarge"), - FormActions( - Submit("submit", "Save Settings", css_class="btn-primary"), - ), - ) - - -class LocationSiteSettingsForm(forms.ModelForm): - guest_image = forms.ImageField(required=False, help_text='Custom image for the "guest" user.') - - class Meta: - model = models.KegbotSite - fields = ( - "volume_display_units", - "temperature_display_units", - "timezone", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("volume_display_units", css_class="input-xlarge"), - Field("temperature_display_units", css_class="input-xlarge"), - Field("timezone"), - FormActions( - Submit("submit", "Save Settings", css_class="btn-primary"), - ), - ) - - -class AdvancedSiteSettingsForm(forms.ModelForm): - class Meta: - model = models.KegbotSite - fields = ( - "session_timeout_minutes", - "google_analytics_id", - "email_config", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("session_timeout_minutes"), - Field("google_analytics_id"), - Field("email_config"), - FormActions( - Submit("submit", "Save Settings", css_class="btn-primary"), - ), - ) - - -class BeverageForm(forms.ModelForm): - class Meta: - model = models.Beverage - fields = ( - "name", - "style", - "producer", - "vintage_year", - "abv_percent", - "original_gravity", - "specific_gravity", - "ibu", - "srm", - "color_hex", - "star_rating", - "untappd_beer_id", - "description", - ) - - new_image = forms.ImageField(required=False, help_text="Set/replace image for this beer type.") - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("name", css_class="input-xlarge"), - Field("style", css_class="input-xlarge"), - Field("producer"), - Field("vintage_year"), - Field("abv_percent"), - Field("original_gravity"), - Field("specific_gravity"), - Field("ibu"), - Field("srm"), - Field("color_hex"), - Field("star_rating"), - Field("untappd_beer_id"), - Field("description"), - Field("new_image"), - FormActions( - Submit("submit", "Save", css_class="btn-primary"), - ), - ) - - -class BeverageProducerForm(forms.ModelForm): - # Django 6.0 changes the default scheme to https; set it explicitly to - # silence the transition warning. - url = forms.URLField(assume_scheme="https", required=False, help_text="Brewer's home page") - - class Meta: - model = models.BeverageProducer - fields = ( - "name", - "country", - "origin_state", - "is_homebrew", - "url", - "description", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("name", css_class="input-xlarge"), - Field("country", css_class="input-xlarge"), - Field("origin_state", css_class="input-xlarge"), - Field("origin_city", css_class="input-xlarge"), - Field("is_homebrew"), - Field("url"), - Field("description"), - FormActions( - Submit("submit", "Save", css_class="btn-primary"), - ), - ) - - -class FindUserForm(forms.Form): - username = forms.CharField() - - -class UserForm(forms.ModelForm): - class Meta: - model = models.User - fields = ( - "username", - "email", - "password", - "is_staff", - ) - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("username", css_class="input-xlarge"), - Field("email", css_class="input-xlarge"), - "password", - "is_staff", - FormActions( - Submit("submit", "Save", css_class="btn-primary"), - ), - ) - - -class UserProfileForm(forms.ModelForm): - class Meta: - model = models.User - fields = ("username", "display_name", "email") - - new_mugshot = forms.ImageField(required=False) - - def save(self, *args, **kwargs): - user = super().save(*args, **kwargs) - image = self.cleaned_data.get("new_mugshot") - if image: - pic = models.Picture.objects.create(user=user) - pic.image.save(image.name, image) - pic.save() - user.mugshot = pic - user.save() - return user - - -class TokenForm(forms.ModelForm): - class Meta: - model = models.AuthenticationToken - fields = ( - "nice_name", - "enabled", - ) - - username = forms.CharField(required=False) - - helper = FormHelper() - helper.form_class = "form-horizontal user-select" - helper.layout = Layout( - Field("username", css_class="input-xlarge"), - Field("nice_name", css_class="input-xlarge"), - "enabled", - FormActions( - Submit("submit", "Save", css_class="btn-primary"), - ), - ) - - def clean_username(self): - username = self.cleaned_data["username"] - if username == "": - self.cleaned_data["user"] = None - return - try: - self.cleaned_data["user"] = models.User.objects.get(username=username) - except models.User.DoesNotExist: - raise forms.ValidationError( - "Invalid username; use a complete user name or leave blank." - ) - return username - - -class DeleteTokenForm(forms.Form): - helper = FormHelper() - helper.form_class = "form-horizontal user-select" - helper.layout = Layout( - FormActions( - Submit("delete_token", "Delete Token", css_class="btn-danger"), - ) - ) - - -class AddTokenForm(forms.ModelForm): - class Meta: - model = models.AuthenticationToken - fields = ( - "auth_device", - "token_value", - "enabled", - ) - - CHOICES = ( - ("core.rfid", "RFID"), - ("core.onewire", "OneWire/iButton"), - ("nfc", "NFC"), - ) - auth_device = forms.ChoiceField(choices=CHOICES) - username = forms.CharField(required=False) - - helper = FormHelper() - helper.form_class = "form-horizontal user-select" - helper.layout = Layout( - Field("auth_device", css_class="input-xlarge"), - Field("token_value", css_class="input-xlarge"), - Field("username", css_class="input-xlarge"), - "enabled", - FormActions( - Submit("submit", "Save", css_class="btn-primary"), - ), - ) - - def clean_username(self): - username = self.cleaned_data["username"] - if username == "": - self.cleaned_data["user"] = None - return - try: - self.cleaned_data["user"] = models.User.objects.get(username=username) - except models.User.DoesNotExist: - raise forms.ValidationError( - "Invalid username; use a complete user name or leave blank." - ) - return username - - -class CancelDrinkForm(forms.Form): - pass - - -class DeleteDrinksForm(forms.Form): - helper = FormHelper() - helper.form_class = "form-horizontal user-select" - helper.layout = Layout( - HTML( - """ - - - - - - - - - - - -{% load kegweblib %} -{% for drink in drinks %} - - - - - - - - -{% endfor %} - -
SelectDrinkDateVolumeUserKeg
-
- Edit   - {{ drink.id }} -
{{ drink.time }}{% volume drink.volume_ml %}{{ drink.user }}{{ drink.keg }}
""" - ), - FormActions( - Submit("delete_drinks", "Delete Drinks", css_class="btn-danger"), - ), - ) - - -class ReassignDrinkForm(forms.Form): - username = forms.CharField(required=True) - - def clean_username(self): - username = self.cleaned_data["username"] - if username == "": - self.cleaned_data["user"] = None - return - try: - self.cleaned_data["user"] = models.User.objects.get(username=username) - except models.User.DoesNotExist: - raise forms.ValidationError( - "Invalid username; use a complete user name or leave blank." - ) - return username - - -class ChangeDrinkVolumeForm(forms.Form): - UNIT_CHOICES = (("mL", "mL"), ("oz", "oz")) - units = forms.ChoiceField(required=True, choices=UNIT_CHOICES) - volume = forms.FloatField(required=True, min_value=0) - - def clean_volume(self): - volume = self.cleaned_data["volume"] - if self.cleaned_data["units"] == "oz": - self.cleaned_data["volume_ml"] = float( - units.Quantity(volume, units.UNITS.Ounce).InMilliliters() - ) - else: - self.cleaned_data["volume_ml"] = volume - return volume - - -class RecordDrinkForm(forms.Form): - units = forms.ChoiceField(required=True, choices=ChangeDrinkVolumeForm.UNIT_CHOICES) - volume = forms.FloatField(required=True, min_value=0) - username = forms.CharField(required=False) - - def clean_username(self): - username = self.cleaned_data["username"] - if username == "": - self.cleaned_data["user"] = None - return - try: - self.cleaned_data["user"] = models.User.objects.get(username=username) - except models.User.DoesNotExist: - raise forms.ValidationError( - "Invalid username; use a complete user name or leave blank." - ) - return username - - def clean_volume(self): - volume = self.cleaned_data["volume"] - if self.cleaned_data["units"] == "oz": - self.cleaned_data["volume_ml"] = float( - units.Quantity(volume, units.UNITS.Ounce).InMilliliters() - ) - else: - self.cleaned_data["volume_ml"] = volume - return volume - - -class TestEmailForm(forms.Form): - address = forms.CharField(required=False) - - def clean_address(self): - address = self.cleaned_data["address"] - if address == "": - self.cleaned_data["address"] = None - return - return address - - -class NewFlowMeterForm(forms.ModelForm): - class Meta: - model = models.FlowMeter - fields = ("port_name", "ticks_per_ml", "controller") - - -class UpdateFlowMeterForm(forms.ModelForm): - class Meta: - model = models.FlowMeter - fields = ("ticks_per_ml",) - - -class AddFlowMeterForm(forms.ModelForm): - class Meta: - model = models.FlowMeter - fields = ("port_name", "ticks_per_ml", "controller") - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("port_name"), - Field("ticks_per_ml"), - Field("controller", type="hidden"), - FormActions( - Submit("add_flow_meter", "Add Flow Meter", css_class="btn-primary"), - ), - ) - - -class FlowToggleForm(forms.ModelForm): - class Meta: - model = models.FlowToggle - fields = ("port_name", "controller") - - -class AddFlowToggleForm(forms.ModelForm): - class Meta: - model = models.FlowToggle - fields = ("port_name", "controller") - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("port_name"), - Field("controller", type="hidden"), - FormActions( - Submit("add_flow_toggle", "Add Flow Toggle", css_class="btn-primary"), - ), - ) - - -class DeleteControllerForm(forms.Form): - helper = FormHelper() - helper.form_class = "form-horizontal user-select" - helper.layout = Layout( - FormActions( - Submit("delete_controller", "Delete Controller", css_class="btn-danger"), - ) - ) - - -class ControllerForm(forms.ModelForm): - class Meta: - model = models.Controller - fields = ("name", "model_name", "serial_number") - - helper = FormHelper() - helper.form_class = "form-horizontal" - helper.layout = Layout( - Field("name", css_class="input-xlarge"), - Field("model_name", css_class="input-xlarge"), - Field("serial_number", css_class="input-xlarge"), - FormActions( - Submit("submit_controller_form", "Save Controller", css_class="btn-success"), - ), - ) diff --git a/pykeg/web/kegadmin/templates/kegadmin/add_controller.html b/pykeg/web/kegadmin/templates/kegadmin/add_controller.html deleted file mode 100644 index 00c17d5ce..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/add_controller.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Controller | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Controller{% endblock %} - -{% block kegadmin-main %} -{% crispy form %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/add_tap.html b/pykeg/web/kegadmin/templates/kegadmin/add_tap.html deleted file mode 100644 index 20e40decc..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/add_tap.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Tap | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Tap{% endblock %} - -{% block kegadmin-main %} -{% crispy form %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/add_token.html b/pykeg/web/kegadmin/templates/kegadmin/add_token.html deleted file mode 100644 index 8618b07b2..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/add_token.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Token | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Token{% endblock %} - -{% block kegadmin-main %} -{% crispy form %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/add_user.html b/pykeg/web/kegadmin/templates/kegadmin/add_user.html deleted file mode 100644 index adc093e6d..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/add_user.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add User | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add User{% endblock %} - -{% block kegadmin-main %} -{% crispy form %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/backup_export.html b/pykeg/web/kegadmin/templates/kegadmin/backup_export.html deleted file mode 100644 index fe2a4abb5..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/backup_export.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Backup/Export | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Backup/Export{% endblock %} - -{% block kegadmin-main %} - -

Create a Backup

-

- Experimental. Click "Export" to generate a zipfile containing all - media and database records. -

-
{% csrf_token %} - -
- -{% if backups %} -

Saved Backups

- - - - - - - - - - - - -{% for backup in backups %} - - - - - - - - -{% endfor %} -
ServerVersionSizeCreatedDownloadDelete
{{ backup.server_name }}{{ backup.server_version }} - {{ backup.size_bytes|filesizeformat }} - ({{ backup.num_tables }} tables, {{ backup.num_media_files }} media files){% timeago backup.created_time %} - - Download - - -
{% csrf_token %} - - -
-
-{% endif %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/base.html b/pykeg/web/kegadmin/templates/kegadmin/base.html deleted file mode 100644 index 004e62408..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/base.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block content %} - -
-
- -
- -
- {% block kegadmin-main %}{% endblock %} -
-
- -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/beer_type_add.html b/pykeg/web/kegadmin/templates/kegadmin/beer_type_add.html deleted file mode 100644 index 9ea5fe88a..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/beer_type_add.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Beer Type | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Beer Type {% endblock %} - -{% block kegadmin-main %} - -

Add Beer Type

- -{% if beer_type.picture %} -
-
-

-
-
-{% endif %} - -{% crispy form %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/beer_type_detail.html b/pykeg/web/kegadmin/templates/kegadmin/beer_type_detail.html deleted file mode 100644 index a38f7655b..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/beer_type_detail.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Beer Type | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Beer Type {% endblock %} - -{% block kegadmin-main %} - -

Edit Beer Type

- -{% if beer_type.picture %} -
-
-

-
-
-{% endif %} - -{% crispy form %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/beer_type_list.html b/pykeg/web/kegadmin/templates/kegadmin/beer_type_list.html deleted file mode 100644 index c12b8fab8..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/beer_type_list.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Beer Types | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Beer Types{% endblock %} - -{% block kegadmin-main %} - - - - - - - - - - -{% for beverage in beverages %} - - - - - -{% endfor %} - - - - -
NameBrewerStyle
-
- Edit   - {{ beverage.name }} -
{{ beverage.producer }}{{ beverage.style }}
- Add New Beer Type -
-{% include "kegweb/_pagination.html" with page=beverages %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/brewer_add.html b/pykeg/web/kegadmin/templates/kegadmin/brewer_add.html deleted file mode 100644 index dab479405..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/brewer_add.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Brewer | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Brewer {% endblock %} - -{% block kegadmin-main %} - -

Add Brewer

- -{% if brewer.picture %} -
-
-

-
-
-{% endif %} - -{% crispy form %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/brewer_detail.html b/pykeg/web/kegadmin/templates/kegadmin/brewer_detail.html deleted file mode 100644 index 77678650b..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/brewer_detail.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Brewer | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Brewer {% endblock %} - -{% block kegadmin-main %} - -

Edit Brewer

- -{% crispy form %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/brewer_list.html b/pykeg/web/kegadmin/templates/kegadmin/brewer_list.html deleted file mode 100644 index ec3d073ea..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/brewer_list.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Brewers | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Brewers{% endblock %} - -{% block kegadmin-main %} - - - - - - - - - - - -{% for brewer in brewers %} - - - - - - -{% endfor %} - - - - -
NameCountryStateCity
-
- Edit   - {{ brewer.name }} -
{{ brewer.country }}{{ brewer.origin_state }}{{ brewer.origin_city }}
- Add New Brewer -
- -{% include "kegweb/_pagination.html" with page=brewers %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/bugreport.html b/pykeg/web/kegadmin/templates/kegadmin/bugreport.html deleted file mode 100644 index 9e5650b1b..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/bugreport.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Bugreport | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Bugreport{% endblock %} - -{% block kegadmin-main %} - -

Bugreport

- -

- Copy and paste the system information below. Warning: Bugreport - output can contain sensitive information. -

- - - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/controller_detail.html b/pykeg/web/kegadmin/templates/kegadmin/controller_detail.html deleted file mode 100644 index 8ec54bb0a..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/controller_detail.html +++ /dev/null @@ -1,223 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Controller: {{ controller.name }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Controller: {{ controller.name }}{% endblock %} - -{% block kegadmin-main %} - - - - - - - - -

{{ controller.name }} {{ controller.type }}

- - -
-
- -
-
- {% if controller.serial_number %} - - - - - - - -
Serial Number{{ controller.serial_number }}
- {% else %} -
This controller does not have a serial number associated
- {% endif %} - -
- -
- - - {% if controller.meters.all %} - - - - {% for meter in controller.meters.all %} - - - - - - {% endfor %} - {% endif %} - - - - -
NameTicks per mL
- Edit   - {{ meter }} - - {{ meter.ticks_per_ml }} -
- Add Flow Meter -
-
- -
- - - {% if controller.toggles.all %} - - - {% for toggle in controller.toggles.all %} - - - - - {% endfor %} - {% endif %} - - - - -
Name
- Edit   - {{ toggle }} -
- Add Flow Toggle -
-
- -
- -
-
- -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/controller_list.html b/pykeg/web/kegadmin/templates/kegadmin/controller_list.html deleted file mode 100644 index 65e522a43..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/controller_list.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Controllers | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Controllers{% endblock %} - -{% block kegadmin-main %} - - - - - - - - - -{% for controller in controllers %} - - - - - -{% endfor %} - - - - -
ControllerMetersToggles
-
- Edit   - {{ controller.name }} -
- {{ controller.meters.count }} - - {{ controller.toggles.count }} -
- Add Controller -
- -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/dashboard.html b/pykeg/web/kegadmin/templates/kegadmin/dashboard.html deleted file mode 100644 index 501873864..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/dashboard.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin{% endblock %} - -{% block kegadmin-main %} -
    -{% badge num_users "User" do_pluralize=True %} -{% badge num_new_users "New User" do_pluralize=True %} -
- -{% if redis_error %} -
-

Redis Connection Error

-

- Connection to the Redis server failed; some features of Kegbot may - not work correctly until it is fixed. -

-

- Error message was: {{ redis_error }} -

-
-{% endif %} - -{% if DEBUG %} -
-

Warning: Debug Mode

- Kegbot Server is running in DEBUG mode. For - performance and security reasons, you should disable DEBUG mode by - setting DEBUG = False in {{ localsettings_path }}. -
-{% endif %} - -{% if not email_configured %} -
-

Warning: E-Mail Configuration Problem

- E-mail is not properly configured; no mails will be sent. Please - read the docs - for more information. -
-{% endif %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/drink_list.html b/pykeg/web/kegadmin/templates/kegadmin/drink_list.html deleted file mode 100644 index 801d7acb6..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/drink_list.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Drinks | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Drinks{% endblock %} - -{% block kegadmin-main %} - -{% crispy delete_drinks_form %} -{% include "kegweb/_pagination.html" with page=drinks %} - -{% endblock %} - diff --git a/pykeg/web/kegadmin/templates/kegadmin/email.html b/pykeg/web/kegadmin/templates/kegadmin/email.html deleted file mode 100644 index 4cd29d513..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/email.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: E-Mail | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: E-Mail{% endblock %} - -{% block kegadmin-main %} -{% if not email_configured %} -
-

Warning!

- E-mail is not properly configured; no mails will be sent. Please - read the docs - for more information. -
-{% else %} -

Test E-mail Configuration

-

- Use this panel to test your server's e-mail configuration. -

-
{% csrf_token %} - - -
-

- Please check your inbox at the address above after sending a test E-mail. - Be sure to check your spam folder, and add the sender to your whitelist if necessary. -

-{% endif %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/includes/extrajs.html b/pykeg/web/kegadmin/templates/kegadmin/includes/extrajs.html deleted file mode 100644 index b0f240f69..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/includes/extrajs.html +++ /dev/null @@ -1,63 +0,0 @@ - \ No newline at end of file diff --git a/pykeg/web/kegadmin/templates/kegadmin/includes/keg-status-label.html b/pykeg/web/kegadmin/templates/kegadmin/includes/keg-status-label.html deleted file mode 100644 index 15a5339f1..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/includes/keg-status-label.html +++ /dev/null @@ -1,9 +0,0 @@ -{% if keg.is_available %} - Untapped -{% elif keg.is_on_tap %} - Online -{% elif keg.is_finished %} - Kicked -{% else %} - Unknown -{% endif %} \ No newline at end of file diff --git a/pykeg/web/kegadmin/templates/kegadmin/includes/keg_nav.html b/pykeg/web/kegadmin/templates/kegadmin/includes/keg_nav.html deleted file mode 100644 index aa07ae6fb..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/includes/keg_nav.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load kegweblib %} - - \ No newline at end of file diff --git a/pykeg/web/kegadmin/templates/kegadmin/includes/user-status-label.html b/pykeg/web/kegadmin/templates/kegadmin/includes/user-status-label.html deleted file mode 100644 index 690140271..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/includes/user-status-label.html +++ /dev/null @@ -1,3 +0,0 @@ -{% if user.is_active %}Active{% else %}Disabled{% endif %} -{% if user.is_staff %}Staff{% endif %} -{% if user.is_superuser %}Superuser{% endif %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/index.html b/pykeg/web/kegadmin/templates/kegadmin/index.html deleted file mode 100644 index aba344ed2..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/index.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: General Settings | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: General Settings{% endblock %} - -{% block kegadmin-main %} - {% crispy settings_form %} -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/keg_add.html b/pykeg/web/kegadmin/templates/kegadmin/keg_add.html deleted file mode 100644 index 369b7566d..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/keg_add.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Add Keg | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Add Keg {% endblock %} - -{% block kegadmin-main %} - -{% include 'kegadmin/includes/keg_nav.html' %} - -{% crispy form %} - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} \ No newline at end of file diff --git a/pykeg/web/kegadmin/templates/kegadmin/keg_detail.html b/pykeg/web/kegadmin/templates/kegadmin/keg_detail.html deleted file mode 100644 index d1246173a..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/keg_detail.html +++ /dev/null @@ -1,139 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Keg | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Keg {% endblock %} - -{% block kegadmin-main %} - -

Keg #{{keg.id}}: {{keg.type.name}} -{% include 'kegadmin/includes/keg-status-label.html' %} -

- -
-
- -
- - -
- {{ keg.percent_full|floatformat:0 }}% full -
- -
- -
- - - - {% if keg.is_on_tap or keg.is_finished %} - - - - - {% endif %} - {% if not keg.is_on_tap and keg.is_finished %} - - - - - {% endif %} - - - - - - - - - - - - - - - - - -
Started{{keg.start_time}}
End Time{{keg.end_time}}
Size{{ keg.keg_type_description }}
Initial Volume{% volume keg.full_volume_ml %}
Served Volume{% volume keg.served_volume_ml %}
Remaining Volume{% volume remaining %}
- -
- -
-
- - -
{% csrf_token %} -
- -{% if keg.is_finished %} - -
- -
- - -
- -
- This keg is marked as finished and cannot be tapped. Click Reactivate to - return the keg to active status. -
- -
- -{% else %} - -
- -
- - -
- -
- Press End Keg to mark the keg as finished. -
- -
- - -{% endif %} - -
-
- - -

Edit Keg

-{% crispy edit_form %} - - - - - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/keg_list.html b/pykeg/web/kegadmin/templates/kegadmin/keg_list.html deleted file mode 100644 index 4242d03c3..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/keg_list.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Kegs | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Kegs{% endblock %} - -{% block kegadmin-main %} - -{% include 'kegadmin/includes/keg_nav.html' %} - -{% for keg in kegs %} - - {% if forloop.counter0|divisibleby:6 %} -
- {% endif %} - -
- -
-
{{ keg.type.name }}
-
-
- - {% if forloop.counter|divisibleby:6 or forloop.last %} -
- {% endif %} - -{% endfor %} - -{% include "kegweb/_pagination.html" with page=kegs %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/logs.html b/pykeg/web/kegadmin/templates/kegadmin/logs.html deleted file mode 100644 index 614300bda..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/logs.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Logs | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Logs{% endblock %} - -{% block kegadmin-main %} -{% if logs %} -

- {{ logs|length }} log{{ logs|length|pluralize }} recorded. -

- -{% for log in logs %} - {% ifchanged %} -
- - {{ log.level.upper.0 }} - {% if log.request_info %} - {{ log.request_info.method }} {{ log.request_info.request_path }}
- {% endif %} - {% endifchanged %} - {{ log.time }} ({{ log.name }}) {{ log.msg }} - - {% if log.traceback %} -
{{ log.traceback }}
- {% endif %} -{% endfor %} - -{% else %} -

- No logs are available. -

-{% endif %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/nav-items.html b/pykeg/web/kegadmin/templates/kegadmin/nav-items.html deleted file mode 100644 index e5ed971ed..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/nav-items.html +++ /dev/null @@ -1,51 +0,0 @@ -{% load kegweblib %} - - -{% if ENABLE_SENSING %} - {% navitem "kegadmin-controllers" "Controllers" %} -{% endif %} - -{% navitem "kegadmin-taps" "Taps" %} -{% navitem "kegadmin-kegs" "Keg Room" %} - -{% if ENABLE_SENSING %} - {% navitem "kegadmin-drinks" "Drinks" %} -{% endif %} -{% if ENABLE_USERS %} - {% navitem "kegadmin-users" "Users" %} - {% navitem "kegadmin-tokens" "Tokens" %} -{% endif %} - - -{% navitem "kegadmin-beverage-producers" "Brewers" %} -{% navitem "kegadmin-beverages" "Beer Types" %} - -{% if PLUGINS %} - -{% for plugin in PLUGINS.values %} - {% if plugin.get_admin_settings_view %} - {% url "kegadmin-plugin-settings" plugin_name=plugin.get_short_name as settings_url %} - {% navitem settings_url plugin.get_name %} - {% endif %} -{% endfor %} -{% endif %} - -
  • - -{% navitem "kegadmin-logs" "Logs" %} -{% navitem "kegadmin-bugreport" "Bugreport" %} -{% navitem "kegadmin-export" "Export Data" %} -{% if KEGBOT_ENABLE_ADMIN %} -
  • Database Admin »
  • -
  • Workers »
  • -{% endif %} -
  • Report a Bug »
  • diff --git a/pykeg/web/kegadmin/templates/kegadmin/tap_detail.html b/pykeg/web/kegadmin/templates/kegadmin/tap_detail.html deleted file mode 100644 index 8286c6426..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/tap_detail.html +++ /dev/null @@ -1,139 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Tap: {{ tap.name }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Tap: {{ tap.name }}{% endblock %} - -{% block kegadmin-main %} - - -
    - {% if not current_keg %} -
    - {% crispy activate_keg_form %} -
    - - {% if available_kegs %} -
    -
    {% csrf_token %} - - - -
    -
    - {% endif %} - - {% else %} -
    - - - - - - - - - - - - - -
    Keg{{ current_keg }}
    Tapped{{ current_keg.start_time }}
    Volume{% volume current_keg.remaining_volume_ml %} remaining ({{ current_keg.percent_full|floatformat:2 }}% full)
    - {% crispy end_keg_form %} -
    - -
    -
    - Manually record a drink using the form below. - You may leave the username field blank to - add an anonymous pour. -
    - -
    {% csrf_token %} -
    - {% if metric_volumes %} - - - mL - {% else %} - - - oz - {% endif %} - - -
    -
    -
    - -
    -
    - Manually record a spill using the form below. Spills are - not attributed to any user and are not saved as an event. - You may also update the keg's total spilled volume on the - keg admin page. -
    - -
    {% csrf_token %} -
    - {% if metric_volumes %} - - - mL - {% else %} - - - oz - {% endif %} - -
    -
    -
    - {% endif %} - -
    - {% crispy tap_settings_form %} -
    - -
    -
    - If you delete a tap with a keg currently attached, the keg - will be taken offline and marked as finished. -
    - {% crispy delete_tap_form %} -
    -
    - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/tap_list.html b/pykeg/web/kegadmin/templates/kegadmin/tap_list.html deleted file mode 100644 index cc9fc1400..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/tap_list.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Taps | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Taps{% endblock %} - -{% block kegadmin-main %} - - - - - - - - - - -{% for tap in taps %} - - - - - -{% endfor %} - - - - -
    TapMeterStatus
    -
    - Edit   - {{ tap.name }} -
    - {% if tap.current_meter %} - {{ tap.current_meter }} - {% else %} - Not connected. - {% endif %} - - {% if tap.current_keg %} - Online: {{ tap.current_keg.type.name }} - {% else %} - Idle - {% endif %} -
    - Add Tap -
    - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/token_detail.html b/pykeg/web/kegadmin/templates/kegadmin/token_detail.html deleted file mode 100644 index da56aa4bd..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/token_detail.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit Token | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit Token {% endblock %} - -{% block kegadmin-main %} - - - -

    {{ token.token_value }} ({{ token.auth_device }})

    - - -
    -
    - -
    -
    - {% crispy form %} -
    -
    - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/token_list.html b/pykeg/web/kegadmin/templates/kegadmin/token_list.html deleted file mode 100644 index caea7de9d..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/token_list.html +++ /dev/null @@ -1,44 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Users | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Users{% endblock %} - -{% block kegadmin-main %} - - - - - - - - - - - -{% for token in tokens %} - - - - - - -{% endfor %} - - - - -
    TokenAliasUserStatus
    -
    - Edit   - {{ token.get_auth_device }} {{ token.token_value }} -
    {% if token.nice_name %}{{ token.nice_name }}{% endif %}{{ token.user }} - {% if token.enabled %}Active{% else %}Disabled{% endif %} -
    - Add Token -
    -{% include "kegweb/_pagination.html" with page=tokens %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/user_detail.html b/pykeg/web/kegadmin/templates/kegadmin/user_detail.html deleted file mode 100644 index 520e512de..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/user_detail.html +++ /dev/null @@ -1,149 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Edit User: {{ edit_user.username }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Edit User: {{ edit_user.username }}{% endblock %} - -{% block kegadmin-main %} - -
    -
    - {% with edit_user as user %} - {% mugshot_box user %} - {% endwith %} -
    -
    - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Username{{ edit_user.username }} (#{{edit_user.id }})
    Display Name{{ edit_user.display_name }}
    E-Mail - {{ edit_user.email }} -
    Date Joined{{ edit_user.date_joined }}
    Last Login{{ edit_user.last_login }}
    Access Level - {% with edit_user as user %} - {% include 'kegadmin/includes/user-status-label.html' %} - {% endwith %} -
    -
    - - {% if tokens %} - - {% endif %} - -
    -
    {% csrf_token %} - - {% if edit_user.is_active %} - - {% if not edit_user.is_superuser %} - - {% endif %} - {% else %} - - - {% endif %} - -
    - - {% if user.is_superuser %} -
    {% csrf_token %} - - {% if edit_user.is_staff %} - - {% if not edit_user.is_superuser %} - - {% endif %} - {% else %} - - - {% endif %} - -
    - {% endif %} -
    - - {% if user.is_superuser %} -
    -
    {% csrf_token %} - {{ profile_form.as_table }} - -
    -
    - {% endif %} -
    -
    - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/templates/kegadmin/user_list.html b/pykeg/web/kegadmin/templates/kegadmin/user_list.html deleted file mode 100644 index b3dd47d6e..000000000 --- a/pykeg/web/kegadmin/templates/kegadmin/user_list.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "kegadmin/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Kegbot Admin: Users | {{ block.super }}{% endblock %} -{% block pagetitle %}Kegbot Admin: Users{% endblock %} - -{% block kegadmin-main %} - -
    -
    {% csrf_token %} -
    - - -
    -
    -
    - - - - - - - - - - -{% for edit_user in users %} - - - - - -{% endfor %} - - - - -
    UserDate JoinedStatus
    -
    - Edit   - {{ edit_user.username }} -
    -
    {{ edit_user.date_joined }} - {% with edit_user as user %} - {% include 'kegadmin/includes/user-status-label.html' %} - {% endwith %} -
    - Add User -
    -{% include "kegweb/_pagination.html" with page=users %} - -{% endblock %} diff --git a/pykeg/web/kegadmin/urls.py b/pykeg/web/kegadmin/urls.py deleted file mode 100644 index 268d6767a..000000000 --- a/pykeg/web/kegadmin/urls.py +++ /dev/null @@ -1,61 +0,0 @@ -from django.urls import path - -from pykeg.plugin import util -from pykeg.web.kegadmin import views - -urlpatterns = [ - # main page - path(r"", views.dashboard, name="kegadmin-dashboard"), - path("settings/general/", views.general_settings, name="kegadmin-main"), - path("settings/location/", views.location_settings, name="kegadmin-location-settings"), - path("settings/advanced/", views.advanced_settings, name="kegadmin-advanced-settings"), - path("bugreport/", views.bugreport, name="kegadmin-bugreport"), - path("export/", views.export, name="kegadmin-export"), - path("beers/", views.beverages_list, name="kegadmin-beverages"), - path("beers/add/", views.beverage_add, name="kegadmin-add-beverage"), - path("beers//", views.beverage_detail, name="kegadmin-edit-beverage"), - path("kegs/", views.keg_list, name="kegadmin-kegs"), - path("kegs/online/", views.keg_list_online, name="kegadmin-kegs-online"), - path("kegs/available/", views.keg_list_available, name="kegadmin-kegs-available"), - path("kegs/kicked/", views.keg_list_kicked, name="kegadmin-kegs-kicked"), - path("kegs/add/", views.keg_add, name="kegadmin-add-keg"), - path("kegs//", views.keg_detail, name="kegadmin-edit-keg"), - path("brewers/", views.beverage_producer_list, name="kegadmin-beverage-producers"), - path("brewers/add/", views.beverage_producer_add, name="kegadmin-add-beverage-producer"), - path( - "brewers//", - views.beverage_producer_detail, - name="kegadmin-edit-beverage-producer", - ), - path("controllers/", views.controller_list, name="kegadmin-controllers"), - path("controllers/create/", views.add_controller, name="kegadmin-add-controller"), - path( - "controllers//", - views.controller_detail, - name="kegadmin-edit-controller", - ), - path("taps/", views.tap_list, name="kegadmin-taps"), - path("taps/create/", views.add_tap, name="kegadmin-add-tap"), - path("taps//", views.tap_detail, name="kegadmin-edit-tap"), - path("users/", views.user_list, name="kegadmin-users"), - path("users//", views.user_detail, name="kegadmin-edit-user"), - path("drinks/", views.drink_list, name="kegadmin-drinks"), - path("drinks//", views.drink_edit, name="kegadmin-edit-drink"), - path("tokens/", views.token_list, name="kegadmin-tokens"), - path("tokens/create/", views.add_token, name="kegadmin-add-token"), - path("tokens//", views.token_detail, name="kegadmin-edit-token"), - path( - "autocomplete/beverage/", - views.autocomplete_beverage, - name="kegadmin-autocomplete-beverage", - ), - path("autocomplete/user/", views.autocomplete_user, name="kegadmin-autocomplete-user"), - path("autocomplete/token/", views.autocomplete_token, name="kegadmin-autocomplete-token"), - path("plugin//", views.plugin_settings, name="kegadmin-plugin-settings"), - path("email/", views.email, name="kegadmin-email"), - path("logs/", views.logs, name="kegadmin-logs"), - path("users/create/", views.add_user, name="kegadmin-add-user"), -] - -if util.get_plugins(): - urlpatterns += util.get_admin_urls() diff --git a/pykeg/web/kegadmin/views.py b/pykeg/web/kegadmin/views.py deleted file mode 100644 index 90e4d4848..000000000 --- a/pykeg/web/kegadmin/views.py +++ /dev/null @@ -1,990 +0,0 @@ -#!/usr/bin/env python -# -import datetime -import logging -import os -import subprocess -import zipfile -from operator import itemgetter - -import redis -from django.conf import settings -from django.contrib import messages -from django.core.files.storage import default_storage -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.db.models import Q -from django.http import Http404, HttpResponse -from django.shortcuts import get_object_or_404, redirect, render -from django.utils import timezone -from django.views.decorators.http import require_http_methods - -from pykeg.backup import backup -from pykeg.core import models, tasks -from pykeg.logging.handlers import RedisListHandler -from pykeg.util import kbjson -from pykeg.util.email import build_message -from pykeg.web.decorators import staff_member_required -from pykeg.web.kegadmin import forms - -logger = logging.getLogger(__name__) - - -@staff_member_required -def dashboard(request): - context = {} - - email_configured = request.kbsite.email_is_configured() - context["email_configured"] = email_configured - - try: - r = redis.StrictRedis.from_url(settings.KEGBOT["REDIS_URL"]) - r.ping() - except redis.RedisError as e: - context["redis_error"] = e.message if e.message else "Unknown error." - - active_users = models.User.objects.filter(is_active=True).exclude(username="guest") - context["num_users"] = len(active_users) - - recent_time = timezone.now() - datetime.timedelta(days=30) - new_users = models.User.objects.filter(date_joined__gte=recent_time).exclude(username="guest") - context["num_new_users"] = len(new_users) - - return render(request, "kegadmin/dashboard.html", context=context) - - -@staff_member_required -def general_settings(request): - context = {} - kbsite = request.kbsite - - form = forms.GeneralSiteSettingsForm(instance=kbsite) - - if request.method == "POST": - form = forms.GeneralSiteSettingsForm(request.POST, instance=kbsite) - if form.is_valid(): - form.save() - messages.success(request, "Settings were updated.") - return redirect("kegadmin-main") - context["settings_form"] = form - - return render(request, "kegadmin/index.html", context=context) - - -@staff_member_required -def location_settings(request): - context = {} - kbsite = request.kbsite - - form = forms.LocationSiteSettingsForm(instance=kbsite) - - if request.method == "POST": - form = forms.LocationSiteSettingsForm(request.POST, instance=kbsite) - if form.is_valid(): - form.save() - messages.success(request, "Settings were updated.") - return redirect("kegadmin-location-settings") - context["settings_form"] = form - - return render(request, "kegadmin/index.html", context=context) - - -@staff_member_required -def advanced_settings(request): - context = {} - kbsite = request.kbsite - - form = forms.AdvancedSiteSettingsForm(instance=kbsite) - - if request.method == "POST": - form = forms.AdvancedSiteSettingsForm(request.POST, instance=kbsite) - if form.is_valid(): - form.save() - guest_image = request.FILES.get("guest_image") - if guest_image: - pic = models.Picture.objects.create() - pic.image.save(guest_image.name, guest_image) - pic.save() - kbsite.guest_image = pic - kbsite.save() - messages.success(request, "Settings were updated.") - return redirect("kegadmin-advanced-settings") - context["settings_form"] = form - - return render(request, "kegadmin/index.html", context=context) - - -@staff_member_required -def email(request): - context = {} - kbsite = request.kbsite - - email_backend = getattr(settings, "EMAIL_BACKEND", None) - email_configured = ( - email_backend and email_backend != "django.core.mail.backends.dummy.EmailBackend" - ) - email_configured = email_configured and settings.DEFAULT_FROM_EMAIL - - if request.method == "POST": - if "send_test_email" in request.POST: - test_email_form = forms.TestEmailForm(request.POST) - if test_email_form.is_valid(): - address = test_email_form.cleaned_data.get("address") - context["site_name"] = kbsite.title - context["site_url"] = kbsite.base_url() - context["settings_url"] = context["site_url"] + "/account" - message = build_message(address, "notification/email_test.html", context) - message.send(fail_silently=True) - messages.success(request, f"E-mail successfully sent to {address}") - - context["email_configured"] = email_configured - - return render(request, "kegadmin/email.html", context=context) - - -@staff_member_required -def export(request): - context = {} - backups = [] - storage = default_storage - - if request.method == "POST": - if "package_backup" in request.POST: - tasks.build_backup.delay() - messages.success(request, "The backup is being generated; please reload in a few.") - - elif "delete_backup" in request.POST: - backup_file = os.path.normpath(os.path.basename(request.POST["backup_name"])) - backup_file = os.path.join(backup.BACKUPS_DIRNAME, backup_file) - if storage.exists(backup_file): - storage.delete(backup_file) - messages.success(request, "Backup deleted.") - else: - messages.warning(request, "Unknown backup file.") - - if storage.exists(backup.BACKUPS_DIRNAME): - subdirs, files = storage.listdir(backup.BACKUPS_DIRNAME) - for filename in files: - if filename[-3:] == "zip": - storage_filename = os.path.join(backup.BACKUPS_DIRNAME, filename) - with storage.open(storage_filename, mode="rb") as backup_file: - archive = zipfile.ZipFile(backup_file) - metadata = backup.read_metadata(archive) - metadata["size_bytes"] = storage.size(storage_filename) - metadata["url"] = storage.url(storage_filename) - metadata["backup_name"] = filename - backups.append(metadata) - - backups.sort(key=itemgetter(backup.META_CREATED_TIME), reverse=True) - context["backups"] = backups - - return render(request, "kegadmin/backup_export.html", context=context) - - -@staff_member_required -def bugreport(request): - context = {} - - error = None - try: - output = subprocess.check_output("kegbot bugreport --noinput", shell=True) - except subprocess.CalledProcessError as e: - logger.exception('Error running "kegbot bugreport" from admin console.') - error = e - - context["bugreport"] = output - context["error"] = error - - return render(request, "kegadmin/bugreport.html", context=context) - - -@staff_member_required -def controller_list(request): - context = {} - context["controllers"] = models.Controller.objects.all() - return render(request, "kegadmin/controller_list.html", context=context) - - -@staff_member_required -def add_controller(request): - context = {} - form = forms.ControllerForm() - if request.method == "POST": - form = forms.ControllerForm(request.POST) - if form.is_valid(): - form.save() - messages.success(request, "Controller created.") - return redirect("kegadmin-controllers") - context["form"] = form - return render(request, "kegadmin/add_controller.html", context=context) - - -@staff_member_required -def controller_detail(request, controller_id): - controller = get_object_or_404(models.Controller, id=controller_id) - delete_controller_form = forms.DeleteControllerForm() - add_flow_meter_form = forms.AddFlowMeterForm() - add_flow_meter_form.fields["controller"].initial = controller_id - add_flow_toggle_form = forms.AddFlowToggleForm() - add_flow_toggle_form.fields["controller"].initial = controller_id - context = {} - - if request.method == "POST": - if "delete_controller" in request.POST: - delete_controller_form = forms.DeleteControllerForm(request.POST) - if delete_controller_form.is_valid(): - controller.delete() - messages.success(request, "The controller was deleted.") - return redirect("kegadmin-controllers") - elif "add_flow_meter" in request.POST: - add_flow_meter_form = forms.AddFlowMeterForm(request.POST) - if add_flow_meter_form.is_valid(): - add_flow_meter_form.save() - messages.success(request, "Flow Meter added successfully.") - return redirect("kegadmin-controllers") - elif "edit_flow_meter" in request.POST: - flowmeter = models.FlowMeter.objects.filter(id=request.POST.get("flowmeter_id")) - flowmeter.update(port_name=request.POST.get("port_name")) - flowmeter.update(ticks_per_ml=request.POST.get("ticks_per_ml")) - messages.success(request, "Flow Meter successfully updated.") - return redirect("kegadmin-controllers") - elif "delete_flow_meter" in request.POST: - flowmeter = models.FlowMeter.objects.filter( - id=request.POST.get("flowmeter_id") - ).delete() - messages.success(request, "Flow Meter removed successfully.") - return redirect("kegadmin-controllers") - elif "add_flow_toggle" in request.POST: - add_flow_toggle_form = forms.AddFlowToggleForm(request.POST) - if add_flow_toggle_form.is_valid(): - add_flow_toggle_form.save() - messages.success(request, "Flow Toggle added successfully.") - return redirect("kegadmin-controllers") - elif "edit_flow_toggle" in request.POST: - flowtoggle = models.FlowToggle.objects.filter(id=request.POST.get("flowtoggle_id")) - flowtoggle.update(port_name=request.POST.get("port_name")) - messages.success(request, "Flow Toggle successfully updated.") - return redirect("kegadmin-controllers") - elif "delete_flow_toggle" in request.POST: - flowmeter = models.FlowToggle.objects.filter( - id=request.POST.get("flowtoggle_id") - ).delete() - messages.success(request, "Flow Toggle removed successfully.") - return redirect("kegadmin-controllers") - - context["controller"] = controller - context["delete_controller_form"] = delete_controller_form - context["add_flow_meter_form"] = add_flow_meter_form - context["add_flow_toggle_form"] = add_flow_toggle_form - return render(request, "kegadmin/controller_detail.html", context=context) - - -@staff_member_required -def tap_list(request): - context = {} - context["taps"] = models.KegTap.objects.all() - return render(request, "kegadmin/tap_list.html", context=context) - - -@staff_member_required -def add_tap(request): - context = {} - form = forms.TapForm() - if request.method == "POST": - form = forms.TapForm(request.POST) - if form.is_valid(): - form.save() - messages.success(request, "Tap created.") - return redirect("kegadmin-taps") - context["form"] = form - return render(request, "kegadmin/add_tap.html", context=context) - - -@staff_member_required -def tap_detail(request, tap_id): - tap = get_object_or_404(models.KegTap, id=tap_id) - available_kegs = models.Keg.objects.filter(status=models.Keg.STATUS_AVAILABLE).order_by("id") - - record_drink_form = forms.RecordDrinkForm() - activate_keg_form = forms.ChangeKegForm() - tap_settings_form = forms.TapForm(instance=tap) - - if request.method == "POST": - if "submit_change_keg_form" in request.POST: - activate_keg_form = forms.ChangeKegForm(request.POST) - if activate_keg_form.is_valid(): - activate_keg_form.save(tap) - messages.success(request, "The new keg was activated. Bottoms up!") - return redirect("kegadmin-taps") - - if "submit_keg_choice" in request.POST: - keg_id = request.POST.get("keg_id") - keg = models.Keg.objects.get(id=keg_id) - d = tap.attach_keg(keg) - messages.success(request, "The new keg was activated. Bottoms up!") - return redirect("kegadmin-taps") - - elif "submit_tap_form" in request.POST: - tap_settings_form = forms.TapForm(request.POST, instance=tap) - if tap_settings_form.is_valid(): - tap_settings_form.save() - messages.success(request, "Tap settings saved.") - tap_settings_form = forms.TapForm(instance=tap) - - elif "submit_delete_tap_form" in request.POST: - delete_form = forms.DeleteTapForm(request.POST) - if delete_form.is_valid(): - if tap.current_keg: - tap.end_current_keg() - tap.delete() - messages.success(request, "Tap deleted.") - return redirect("kegadmin-taps") - - elif "submit_end_keg_form" in request.POST: - end_keg_form = forms.EndKegForm(request.POST) - if end_keg_form.is_valid(): - old_keg = tap.end_current_keg() - messages.success(request, f"Keg {old_keg.id} was ended.") - - elif "submit_record_drink" in request.POST: - record_drink_form = forms.RecordDrinkForm(request.POST) - if record_drink_form.is_valid(): - user = record_drink_form.cleaned_data.get("user") - volume_ml = record_drink_form.cleaned_data.get("volume_ml") - d = models.Drink.record_drink(tap, ticks=0, username=user, volume_ml=volume_ml) - messages.success(request, f"Drink {d.id} recorded.") - else: - messages.error(request, "Please enter a valid volume and user.") - - elif "submit_record_spill" in request.POST: - record_drink_form = forms.RecordDrinkForm(request.POST) - if record_drink_form.is_valid(): - user = record_drink_form.cleaned_data.get("user") - volume_ml = record_drink_form.cleaned_data.get("volume_ml") - d = models.Drink.record_drink( - tap, ticks=0, username=user, volume_ml=volume_ml, spilled=True - ) - messages.success(request, "Spill recorded.") - else: - messages.error(request, "Please enter a valid volume.") - - else: - messages.warning(request, "No form data was found. Bug?") - - end_keg_form = forms.EndKegForm(initial={"keg": tap.current_keg}) - - context = {} - context["tap"] = tap - context["current_keg"] = tap.current_keg - context["available_kegs"] = available_kegs - context["activate_keg_form"] = activate_keg_form - context["record_drink_form"] = record_drink_form - context["end_keg_form"] = end_keg_form - context["tap_settings_form"] = tap_settings_form - context["delete_tap_form"] = forms.DeleteTapForm() - return render(request, "kegadmin/tap_detail.html", context=context) - - -@staff_member_required -def keg_list(request): - qs = models.Keg.objects.all().order_by("-id") - return keg_list_internal(request, qs) - - -@staff_member_required -def keg_list_available(request): - qs = models.Keg.objects.filter(status=models.Keg.STATUS_AVAILABLE).order_by("-id") - return keg_list_internal(request, qs) - - -@staff_member_required -def keg_list_online(request): - qs = models.Keg.objects.filter(status=models.Keg.STATUS_ON_TAP).order_by("-id") - return keg_list_internal(request, qs) - - -@staff_member_required -def keg_list_kicked(request): - qs = models.Keg.objects.filter(status=models.Keg.STATUS_FINISHED).order_by("-id") - return keg_list_internal(request, qs) - - -def keg_list_internal(request, qs): - context = {} - paginator = Paginator(qs, 30) - - page = request.GET.get("page") - try: - kegs = paginator.page(page) - except PageNotAnInteger: - kegs = paginator.page(1) - except EmptyPage: - kegs = paginator.page(paginator.num_pages) - - context["kegs"] = kegs - return render(request, "kegadmin/keg_list.html", context=context) - - -@staff_member_required -def keg_detail(request, keg_id): - keg = get_object_or_404(models.Keg, id=keg_id) - - edit_form = forms.EditKegForm(instance=keg) - - if request.method == "POST": - if "submit_edit_keg" in request.POST: - edit_form = forms.EditKegForm(request.POST, instance=keg) - if edit_form.is_valid(): - edit_form.save() - messages.success(request, "Keg updated.") - return redirect("kegadmin-kegs") - - elif "submit_delete_keg" in request.POST: - keg.cancel() - messages.success(request, "Keg deleted.") - return redirect("kegadmin-kegs") - - elif "submit_reactivate" in request.POST: - keg.reactivate_keg() - messages.success(request, "Keg reactivated.") - return redirect("kegadmin-edit-keg", keg_id=keg.id) - - elif "submit_end" in request.POST: - keg.end_keg() - messages.success(request, "Keg ended.") - return redirect("kegadmin-edit-keg", keg_id=keg.id) - - context = {} - context["keg"] = keg - context["remaining"] = keg.remaining_volume_ml() - context["edit_form"] = edit_form - - return render(request, "kegadmin/keg_detail.html", context=context) - - -@staff_member_required -def keg_add(request): - add_keg_form = forms.KegForm() - if request.method == "POST": - if "submit_add_keg" in request.POST: - add_keg_form = forms.KegForm(request.POST) - if add_keg_form.is_valid(): - keg = add_keg_form.save() - messages.success(request, "New keg added.") - return redirect("kegadmin-edit-keg", keg_id=keg.id) - - context = {} - context["keg"] = "new" - context["form"] = add_keg_form - return render(request, "kegadmin/keg_add.html", context=context) - - -@staff_member_required -def user_list(request): - context = {} - - if request.method == "POST": - form = forms.FindUserForm(request.POST) - if form.is_valid(): - username = form.cleaned_data.get("username") - try: - user = models.User.objects.get(username=username) - return redirect("kegadmin-edit-user", user.id) - except models.User.DoesNotExist: - messages.error(request, f'User "{username}" does not exist.') - - users = models.User.objects.exclude(username="guest").order_by("-id") - paginator = Paginator(users, 25) - - page = request.GET.get("page") - try: - users = paginator.page(page) - except PageNotAnInteger: - users = paginator.page(1) - except EmptyPage: - users = paginator.page(paginator.num_pages) - - context["users"] = users - return render(request, "kegadmin/user_list.html", context=context) - - -@staff_member_required -def add_user(request): - context = {} - form = forms.UserForm() - if request.method == "POST": - form = forms.UserForm(request.POST) - if form.is_valid(): - instance = form.save(commit=False) - instance.set_password(form.cleaned_data.get("password")) - instance.save() - messages.success(request, f'User "{instance.username}" created.') - return redirect("kegadmin-users") - context["form"] = form - return render(request, "kegadmin/add_user.html", context=context) - - -@staff_member_required -def user_detail(request, user_id): - edit_user = get_object_or_404(models.User, id=user_id) - context = {} - profile_form = forms.UserProfileForm(instance=edit_user) - - if request.method == "POST": - if "submit_enable" in request.POST: - if edit_user.is_active: - messages.error(request, "User is already enabled.") - else: - edit_user.is_active = True - edit_user.save() - messages.success(request, f"User {edit_user.username} was enabled.") - - elif "submit_disable" in request.POST: - if edit_user.is_guest(): - messages.error(request, "Cannot disable the guest user.") - elif not edit_user.is_active: - messages.error(request, "User is already disabled.") - else: - edit_user.is_active = False - edit_user.save() - messages.success(request, f"User {edit_user.username} was disabled.") - - elif "submit_add_staff" in request.POST: - if edit_user.is_staff: - messages.error(request, "User is already staff.") - else: - edit_user.is_staff = True - edit_user.save() - messages.success(request, f"User {edit_user.username} staff status enabled.") - - elif "submit_remove_staff" in request.POST: - if edit_user.is_guest(): - messages.error(request, "Cannot change staff status on the guest user.") - elif not edit_user.is_staff: - messages.error(request, "User is not currently staff.") - else: - edit_user.is_staff = False - edit_user.save() - messages.success(request, f"User {edit_user.username} staff status disabled.") - - elif "submit_update_profile" in request.POST: - profile_form = forms.UserProfileForm(request.POST, request.FILES, instance=edit_user) - if profile_form.is_valid(): - profile_form.save() - messages.success(request, f"User {edit_user.username} e-profile updated") - - else: - messages.error(request, "Unknown form submitted.") - - context["profile_form"] = profile_form - context["edit_user"] = edit_user - context["tokens"] = edit_user.tokens.all().order_by("created_time") - - return render(request, "kegadmin/user_detail.html", context=context) - - -@staff_member_required -def drink_list(request): - delete_drinks_form = forms.DeleteDrinksForm() - - if "delete_drinks" in request.POST: - form = forms.DeleteDrinksForm(request.POST) - if form.is_valid(): - delete_ids = request.POST.getlist("delete_ids[]") - drinks = models.Drink.objects.filter(Q(id__in=delete_ids)) - for drink in drinks: - drink.cancel_drink() - delete_ids.reverse() - if len(delete_ids) == 1: - messages.success(request, "Drink " + delete_ids[0] + " has been deleted.") - elif len(delete_ids) == 2: - messages.success( - request, "Drinks " + " and ".join(delete_ids) + " have been deleted." - ) - else: - messages.success( - request, - "Drinks " - + ", ".join(delete_ids[:-1]) - + ", and " - + delete_ids[-1] - + " have been deleted.", - ) - - context = {} - drinks = models.Drink.objects.all().order_by("-time") - paginator = Paginator(drinks, 25) - - page = request.GET.get("page") - try: - drinks = paginator.page(page) - except PageNotAnInteger: - drinks = paginator.page(1) - except EmptyPage: - drinks = paginator.page(paginator.num_pages) - - context["drinks"] = drinks - context["delete_drinks_form"] = delete_drinks_form - return render(request, "kegadmin/drink_list.html", context=context) - - -@staff_member_required -@require_http_methods(["POST"]) -def drink_edit(request, drink_id): - drink = get_object_or_404(models.Drink, id=drink_id) - - if "submit_cancel" in request.POST: - form = forms.CancelDrinkForm(request.POST) - old_keg = drink.keg - if form.is_valid(): - drink.cancel_drink() - messages.success(request, f"Drink {drink_id} was cancelled.") - return redirect(old_keg.get_absolute_url()) - else: - messages.error(request, "Invalid request") - return redirect(drink.get_absolute_url()) - - if "submit_spill" in request.POST: - form = forms.CancelDrinkForm(request.POST) - old_keg = drink.keg - if form.is_valid(): - drink.cancel_drink(spilled=True) - messages.success(request, f"Drink {drink_id} was spilled.") - return redirect(old_keg.get_absolute_url()) - else: - messages.error(request, "Invalid request") - return redirect(drink.get_absolute_url()) - - elif "submit_reassign" in request.POST: - form = forms.ReassignDrinkForm(request.POST) - if form.is_valid(): - new_user = form.cleaned_data["user"] - try: - drink.reassign(new_user) - messages.success(request, f"Drink {drink_id} was reassigned.") - except models.User.DoesNotExist: - messages.error(request, "No such user") - else: - messages.error(request, "Invalid request") - return redirect(drink.get_absolute_url()) - - elif "submit_edit_volume" in request.POST: - form = forms.ChangeDrinkVolumeForm(request.POST) - if form.is_valid(): - volume_ml = form.cleaned_data.get("volume_ml") - if volume_ml == drink.volume_ml: - messages.warning(request, "Drink volume unchanged.") - else: - drink.set_volume(volume_ml) - messages.success(request, f"Drink {drink_id} was updated.") - else: - messages.error(request, "Please provide a valid volume.") - return redirect(drink.get_absolute_url()) - - messages.error(request, "Unknown action.") - return redirect(drink.get_absolute_url()) - - -@staff_member_required -def token_list(request): - context = {} - tokens = models.AuthenticationToken.objects.all().order_by("-created_time") - paginator = Paginator(tokens, 25) - - page = request.GET.get("page") - try: - tokens = paginator.page(page) - except PageNotAnInteger: - tokens = paginator.page(1) - except EmptyPage: - tokens = paginator.page(paginator.num_pages) - - context["tokens"] = tokens - return render(request, "kegadmin/token_list.html", context=context) - - -@staff_member_required -def token_detail(request, token_id): - token = get_object_or_404(models.AuthenticationToken, id=token_id) - delete_token_form = forms.DeleteTokenForm() - context = {} - - username = "" - if token.user: - username = token.user.username - - if request.method == "POST": - if "delete_token" in request.POST: - delete_token_form = forms.DeleteTokenForm(request.POST) - if delete_token_form.is_valid(): - token.delete() - messages.success(request, "The token was deleted.") - return redirect("kegadmin-tokens") - else: - form = forms.TokenForm(request.POST, instance=token) - if form.is_valid(): - instance = form.save(commit=False) - instance.user = form.cleaned_data["user"] - instance.save() - messages.success(request, "Token updated.") - - form = forms.TokenForm(instance=token, initial={"username": username}) - context["token"] = token - context["delete_token_form"] = delete_token_form - context["form"] = form - return render(request, "kegadmin/token_detail.html", context=context) - - -@staff_member_required -def add_token(request): - context = {} - form = forms.AddTokenForm() - if request.method == "POST": - form = forms.AddTokenForm(request.POST) - if form.is_valid(): - instance = form.save(commit=False) - instance.user = form.cleaned_data["user"] - instance.save() - messages.success(request, "Token created.") - return redirect("kegadmin-tokens") - context["form"] = form - return render(request, "kegadmin/add_token.html", context=context) - - -@staff_member_required -def beverages_list(request): - context = {} - beers = models.Beverage.objects.all().order_by("name") - paginator = Paginator(beers, 25) - - page = request.GET.get("page") - try: - beers = paginator.page(page) - except PageNotAnInteger: - beers = paginator.page(1) - except EmptyPage: - beers = paginator.page(paginator.num_pages) - - context["beverages"] = beers - return render(request, "kegadmin/beer_type_list.html", context=context) - - -@staff_member_required -def beverage_detail(request, beer_id): - btype = get_object_or_404(models.Beverage, id=beer_id) - - form = forms.BeverageForm(instance=btype) - if request.method == "POST": - form = forms.BeverageForm(request.POST, instance=btype) - if form.is_valid(): - btype = form.save() - new_image = request.FILES.get("new_image") - if new_image: - pic = models.Picture.objects.create() - pic.image.save(new_image.name, new_image) - pic.save() - btype.picture = pic - btype.save() - - messages.success(request, "Beer type updated.") - return redirect("kegadmin-beverages") - else: - messages.error(request, "Please correct the error(s) below.") - - context = {} - context["beer_type"] = btype - context["form"] = form - return render(request, "kegadmin/beer_type_detail.html", context=context) - - -@staff_member_required -def beverage_add(request): - - form = forms.BeverageForm() - if request.method == "POST": - form = forms.BeverageForm(request.POST) - if form.is_valid(): - btype = form.save() - new_image = request.FILES.get("new_image") - if new_image: - pic = models.Picture.objects.create() - pic.image.save(new_image.name, new_image) - pic.save() - btype.picture = pic - btype.save() - - messages.success(request, "Beer type added.") - return redirect("kegadmin-beverages") - - context = {} - context["beer_type"] = "new" - context["form"] = form - return render(request, "kegadmin/beer_type_add.html", context=context) - - -@staff_member_required -def beverage_producer_list(request): - context = {} - brewers = models.BeverageProducer.objects.all().order_by("name") - paginator = Paginator(brewers, 25) - - page = request.GET.get("page") - try: - brewers = paginator.page(page) - except PageNotAnInteger: - brewers = paginator.page(1) - except EmptyPage: - brewers = paginator.page(paginator.num_pages) - - context["brewers"] = brewers - return render(request, "kegadmin/brewer_list.html", context=context) - - -@staff_member_required -def beverage_producer_detail(request, brewer_id): - brewer = get_object_or_404(models.BeverageProducer, id=brewer_id) - - form = forms.BeverageProducerForm(instance=brewer) - if request.method == "POST": - form = forms.BeverageProducerForm(request.POST, instance=brewer) - if form.is_valid(): - form.save() - messages.success(request, "Brewer updated.") - return redirect("kegadmin-beverage-producers") - - context = {} - context["brewer"] = brewer - context["form"] = form - return render(request, "kegadmin/brewer_detail.html", context=context) - - -@staff_member_required -def beverage_producer_add(request): - form = forms.BeverageProducerForm() - if request.method == "POST": - form = forms.BeverageProducerForm(request.POST) - if form.is_valid(): - btype = form.save() - new_image = request.FILES.get("new_image") - if new_image: - pic = models.Picture.objects.create() - pic.image.save(new_image.name, new_image) - pic.save() - btype.picture = pic - btype.save() - - messages.success(request, "Brewer added.") - return redirect("kegadmin-beverage-producers") - - context = {} - context["brewer"] = "new" - context["form"] = form - return render(request, "kegadmin/brewer_add.html", context=context) - - -@staff_member_required -def autocomplete_beverage(request): - search = request.GET.get("q") - if search: - beverages = models.Beverage.objects.filter( - Q(name__icontains=search) | Q(producer__name__icontains=search) - ) - else: - beverages = models.Beverage.objects.all() - beverages = beverages[:10] # autocomplete widget limited to 10 - values = [] - for beverage in beverages: - values.append( - { - "name": beverage.name, - "id": beverage.id, - "producer_name": beverage.producer.name, - "producer_id": beverage.producer.id, - "style": beverage.style, - } - ) - return HttpResponse( - kbjson.dumps(values, indent=None), content_type="application/json", status=200 - ) - - -@staff_member_required -def autocomplete_user(request): - search = request.GET.get("q") - if search: - users = models.User.objects.filter( - Q(username__icontains=search) - | Q(email__icontains=search) - | Q(display_name__icontains=search) - ) - else: - users = models.User.objects.all() - users = users[:10] # autocomplete widget limited to 10 - values = [] - for user in users: - values.append( - { - "username": user.username, - "id": user.id, - "email": user.email, - "display_name": user.get_full_name(), - "is_active": user.is_active, - } - ) - return HttpResponse( - kbjson.dumps(values, indent=None), content_type="application/json", status=200 - ) - - -@staff_member_required -def autocomplete_token(request): - search = request.GET.get("q") - if search: - tokens = models.AuthenticationToken.objects.filter( - Q(token_value__icontains=search) | Q(nice_name__icontains=search) - ) - else: - tokens = models.AuthenticationToken.objects.all() - tokens = tokens[:10] # autocomplete widget limited to 10 - values = [] - for token in tokens: - values.append( - { - "username": token.user.username, - "id": token.id, - "auth_device": token.auth_device, - "token_value": token.token_value, - "enabled": token.enabled, - } - ) - return HttpResponse( - kbjson.dumps(values, indent=None), content_type="application/json", status=200 - ) - - -@staff_member_required -def plugin_settings(request, plugin_name): - plugin = request.plugins.get(plugin_name, None) - if not plugin: - raise Http404(f'Plugin "{plugin_name}" not loaded') - - view = plugin.get_admin_settings_view() - if not view: - raise Http404("No settings for this plugin") - - return view(request, plugin) - - -@staff_member_required -def logs(request): - context = {} - handlers = logger.parent.handlers - - logs = [] - for h in handlers: - if isinstance(h, RedisListHandler): - logs = list(h.get_logs()) - logs.reverse() - break - - context["logs"] = logs - return render(request, "kegadmin/logs.html", context=context) diff --git a/pykeg/web/kegweb/__init__.py b/pykeg/web/kegweb/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/kegweb/forms.py b/pykeg/web/kegweb/forms.py deleted file mode 100644 index 1ce0d24d0..000000000 --- a/pykeg/web/kegweb/forms.py +++ /dev/null @@ -1,43 +0,0 @@ -from django import forms -from django.contrib.auth.forms import AuthenticationForm - -from pykeg.core import models - -ALL_PICTURES = models.Picture.objects.all() - - -class LoginForm(AuthenticationForm): - next_page = forms.CharField(required=False, widget=forms.HiddenInput) - - -class ActivateAccountForm(forms.Form): - password = forms.CharField(required=True, widget=forms.PasswordInput) - password2 = forms.CharField(required=True, widget=forms.PasswordInput) - - def clean_password2(self): - password1 = self.cleaned_data.get("password") - password2 = self.cleaned_data.get("password2") - if password1 and password2 and password1 != password2: - raise forms.ValidationError("Passwords do not match.") - return password2 - - -class InvitationForm(forms.Form): - email = forms.EmailField(required=True, help_text="E-mail address to invite") - - -class ProfileForm(forms.Form): - new_mugshot = forms.ImageField(required=False) - display_name = forms.CharField(required=True) - - -class RegenerateApiKeyForm(forms.Form): - pass - - -class DeletePictureForm(forms.Form): - picture = forms.ModelChoiceField(queryset=ALL_PICTURES, required=True, widget=forms.HiddenInput) - - -class ChangeEmailForm(forms.Form): - email = forms.CharField(required=True) diff --git a/pykeg/web/kegweb/kegweb_test.py b/pykeg/web/kegweb/kegweb_test.py deleted file mode 100644 index 396403b21..000000000 --- a/pykeg/web/kegweb/kegweb_test.py +++ /dev/null @@ -1,284 +0,0 @@ -"""General tests for the web interface.""" - -from django.core import mail -from django.test import TransactionTestCase -from django.test.utils import override_settings -from django.urls import reverse - -from pykeg.core import defaults, models - - -class KegwebTestCase(TransactionTestCase): - def setUp(self): - self.client.logout() - defaults.set_defaults(set_is_setup=True, create_controller=True) - - def testBasicEndpoints(self): - for endpoint in ("/kegs/", "/stats/", "/drinkers/guest/", "/drinkers/guest/sessions/"): - response = self.client.get(endpoint) - self.assertEqual(200, response.status_code) - - for endpoint in ("/sessions/",): - response = self.client.get(endpoint) - self.assertEqual(404, response.status_code) - - keg = models.Keg.start_keg( - "kegboard.flow0", - beverage_name="Unknown", - producer_name="Unknown", - beverage_type="beer", - style_name="Unknown", - ) - self.assertIsNotNone(keg) - response = self.client.get("/kegs/") - self.assertEqual(200, response.status_code) - - d = models.Drink.record_drink("kegboard.flow0", ticks=100) - drink_id = d.id - - response = self.client.get(f"/d/{drink_id}", follow=True) - self.assertRedirects(response, f"/drinks/{drink_id}/", status_code=301) - - session_id = d.session.id - response = self.client.get(f"/s/{session_id}", follow=True) - self.assertRedirects(response, d.session.get_absolute_url(), status_code=301) - - def testShout(self): - models.Keg.start_keg( - "kegboard.flow0", - beverage_name="Unknown", - producer_name="Unknown", - beverage_type="beer", - style_name="Unknown", - ) - d = models.Drink.record_drink("kegboard.flow0", ticks=123, shout="_UNITTEST_") - response = self.client.get(d.get_absolute_url()) - self.assertContains(response, "

    _UNITTEST_

    ", status_code=200) - - def test_privacy(self): - keg = models.Keg.start_keg( - "kegboard.flow0", - beverage_name="Unknown", - producer_name="Unknown", - beverage_type="beer", - style_name="Unknown", - ) - self.assertIsNotNone(keg) - d = models.Drink.record_drink("kegboard.flow0", ticks=100) - - # URLs to expected contents - urls = { - "/kegs/": "Keg List", - "/stats/": "System Stats", - "/sessions/": "All Sessions", - f"/kegs/{keg.id}/": f"Keg {keg.id}", - f"/drinks/{d.id}/": f"Drink {d.id}", - } - - def test_urls(expect_fail, urls=urls): - for url, expected_content in list(urls.items()): - response = self.client.get(url) - if expect_fail: - self.assertNotContains( - response, expected_content, status_code=401, msg_prefix=url - ) - else: - self.assertContains(response, expected_content, status_code=200, msg_prefix=url) - - user = models.User.create_new_user("testuser", "test@example.com", password="1234") - - kbsite = models.KegbotSite.get() - self.client.logout() - - # Public mode. - test_urls(expect_fail=False) - - # Members-only. - kbsite.privacy = "members" - kbsite.save() - test_urls(expect_fail=True) - logged_in = self.client.login(username="testuser", password="1234") - self.assertTrue(logged_in) - test_urls(expect_fail=False) - - # Staff-only - kbsite.privacy = "staff" - kbsite.save() - - test_urls(expect_fail=True) - user.is_staff = True - user.save() - test_urls(expect_fail=False) - self.client.logout() - test_urls(expect_fail=True) - - def test_whitelisted_urls(self): - """Verify always-accessible URLs.""" - urls = ( - "/accounts/password/reset/", - "/accounts/register/", - "/accounts/login/", - ) - - for url in urls: - response = self.client.get(url) - self.assertNotContains(response, "denied", status_code=200, msg_prefix=url) - - def test_activation(self): - kbsite = models.KegbotSite.get() - self.assertEqual("public", kbsite.privacy) - - user = models.User.create_new_user("testuser", "test@example.com") - self.assertIsNotNone(user.activation_key) - self.assertFalse(user.has_usable_password()) - - activation_key = user.activation_key - self.assertIsNotNone(activation_key) - - activation_url = reverse( - "activate-account", args=(), kwargs={"activation_key": activation_key} - ) - - # Activation works regardless of privacy settings. - self.client.logout() - response = self.client.get(activation_url) - self.assertContains(response, "Choose a Password", status_code=200) - - kbsite.privacy = "staff" - kbsite.save() - response = self.client.get(activation_url) - self.assertContains(response, "Choose a Password", status_code=200) - - kbsite.privacy = "members" - kbsite.save() - response = self.client.get(activation_url) - self.assertContains(response, "Choose a Password", status_code=200) - - # Activate the account. - form_data = { - "password": "123", - "password2": "123", - } - - response = self.client.post(activation_url, data=form_data, follow=True) - self.assertContains(response, "Your account has been activated!", status_code=200) - user = models.User.objects.get(pk=user.id) - self.assertIsNone(user.activation_key) - - @override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend") - @override_settings(DEFAULT_FROM_EMAIL="test-from@example") - def test_registration(self): - kbsite = models.KegbotSite.get() - self.assertEqual("public", kbsite.privacy) - self.assertEqual("public", kbsite.registration_mode) - - response = self.client.get("/accounts/register/") - self.assertContains(response, "Register New Account", status_code=200) - - response = self.client.post( - "/accounts/register/", - data={ - "username": "newuser", - "password1": "1234", - "password2": "1234", - "email": "test2@example.com", - }, - follow=True, - ) - self.assertRedirects(response, "/account/") - self.assertContains(response, "Hello, newuser") - self.assertEqual(1, len(mail.outbox)) - - msg = mail.outbox[0] - self.assertEqual(["test2@example.com"], msg.to) - self.assertTrue("To log in to your account, please click here" in msg.body) - - response = self.client.post( - "/accounts/register/", - data={ - "username": "newuser", - "password1": "1234", - "password2": "1234", - "email": "test2@example.com", - }, - follow=False, - ) - self.assertContains(response, "User with this Username already exists", status_code=200) - - response = self.client.post( - "/accounts/register/", - data={ - "username": "newuser 2", - "password1": "1234", - "password2": "1234", - "email": "test2@example.com", - }, - follow=False, - ) - self.assertContains(response, "Enter a valid username", status_code=200) - - response = self.client.post( - "/accounts/register/", - data={ - "username": "newuser2", - "password1": "1234", - "password2": "1235", - "email": "test2@example.com", - }, - follow=False, - ) - self.assertContains( - response, "The two password fields didn't match.", status_code=200, html=True - ) - - @override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend") - @override_settings(DEFAULT_FROM_EMAIL="test-from@example") - def test_registration_with_invite(self): - kbsite = models.KegbotSite.get() - kbsite.registration_mode = "staff-invite-online" - kbsite.save() - - response = self.client.get("/accounts/register/") - self.assertContains(response, "Invitation Required", status_code=401) - - response = self.client.get("/accounts/register/?invite_code=1234") - self.assertContains(response, "Invitation Expired", status_code=401) - - models.Invitation.objects.create(invite_code="test", for_email="test@example.com") - self.assertEqual(1, models.Invitation.objects.all().count()) - - response = self.client.get("/accounts/register/?invite_code=test") - self.assertContains(response, "Register New Account", status_code=200) - response = self.client.post( - "/accounts/register/", - data={ - "username": "newuser2", - "password1": "1234", - "password2": "1234", - "email": "test2@example.com", - }, - follow=True, - ) - self.assertRedirects(response, "/account/") - self.assertContains(response, "Hello, newuser2") - self.assertEqual(0, models.Invitation.objects.all().count()) - - response = self.client.get("/accounts/register/?invite_code=test") - self.assertContains(response, "Invitation Expired", status_code=401) - - def test_upgrade_bouncer(self): - kbsite = models.KegbotSite.get() - response = self.client.get("/") - self.assertContains(response, "My Kegbot", status_code=200) - - old_version = kbsite.server_version - kbsite.server_version = "0.0.1" - kbsite.save() - response = self.client.get("/") - self.assertContains(response, "Upgrade Required", status_code=403) - - kbsite.server_version = old_version - kbsite.is_setup = False - kbsite.save() - response = self.client.get("/") - self.assertContains(response, "Kegbot Offline", status_code=403) diff --git a/pykeg/web/kegweb/signals.py b/pykeg/web/kegweb/signals.py deleted file mode 100644 index 4132c2085..000000000 --- a/pykeg/web/kegweb/signals.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.contrib import messages -from django.contrib.auth.signals import user_logged_in, user_logged_out - - -def on_logged_in(sender, user, request, **kwargs): - messages.add_message(request, messages.INFO, "You are now logged in!", fail_silently=True) - - -user_logged_in.connect(on_logged_in) - - -def on_logged_out(sender, user, request, **kwargs): - messages.add_message(request, messages.INFO, "You have been logged out.", fail_silently=True) - - -user_logged_out.connect(on_logged_out) diff --git a/pykeg/web/kegweb/templates/kegweb/_pagination.html b/pykeg/web/kegweb/templates/kegweb/_pagination.html deleted file mode 100644 index 66a8abe91..000000000 --- a/pykeg/web/kegweb/templates/kegweb/_pagination.html +++ /dev/null @@ -1,33 +0,0 @@ -{% comment %} -Bootstrap 3 pagination for a Django Paginator Page, passed in as `page`. -Usage: {% include "kegweb/_pagination.html" with page=page_obj %} -{% endcomment %} -{% load paginator_tags %} -{% if page.paginator.num_pages > 1 %} -
      - - {% if page.has_previous %} - « - {% else %} - - {% endif %} - - {% elided_page_range page as page_numbers %} - {% for num in page_numbers %} - {% if num == page.paginator.ELLIPSIS %} -
    • {{ num }}
    • - {% elif num == page.number %} -
    • {{ num }}
    • - {% else %} -
    • {{ num }}
    • - {% endif %} - {% endfor %} - - {% if page.has_next %} - » - {% else %} - - {% endif %} - -
    -{% endif %} diff --git a/pykeg/web/kegweb/templates/kegweb/badge.html b/pykeg/web/kegweb/templates/kegweb/badge.html deleted file mode 100644 index 1bfce53db..000000000 --- a/pykeg/web/kegweb/templates/kegweb/badge.html +++ /dev/null @@ -1,4 +0,0 @@ -
  • -

    {{ badge_amount }}

    - {{ badge_caption }} -
  • diff --git a/pykeg/web/kegweb/templates/kegweb/basic-badges.html b/pykeg/web/kegweb/templates/kegweb/basic-badges.html deleted file mode 100644 index 789d02b84..000000000 --- a/pykeg/web/kegweb/templates/kegweb/basic-badges.html +++ /dev/null @@ -1,27 +0,0 @@ -{% load kegweblib %} -{% if stats %} -
      -
    • -

      {% volume stats.total_volume_ml %}

      - Poured -
    • - {% if keg %} -
    • - {% if keg.remaining_volume_ml > 0 and not keg.is_finished %} -

      {% volume keg.remaining_volume_ml %}

      - {% else %} -

      {% volume 0 %}

      - {% endif %} - Remaining -
    • - {% endif %} -
    • -

      {{ stats.registered_drinkers|length }}

      - Drinkers -
    • -
    • -

      {{ stats.sessions_count }}

      - Sessions -
    • -
    -{% endif %} diff --git a/pykeg/web/kegweb/templates/kegweb/basic-stats.html b/pykeg/web/kegweb/templates/kegweb/basic-stats.html deleted file mode 100644 index ac5f9873d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/basic-stats.html +++ /dev/null @@ -1,41 +0,0 @@ -{% load kegweblib %} -{% if stats %} - - Sessions - - A total of - {{ stats.sessions_count }} drinking session{{ stats.sessions_count|pluralize }} - {% if stats.sessions_count == 1 %}has{% else %}have{% endif %} been recorded. - - - - - Drinks - - A total of - {{ stats.total_pours }} pour{{ stats.total_pours|pluralize }} have been recorded, - totalling {% volume stats.total_volume_ml %}. - - - - - Drinkers - - At least - {{ stats.registered_drinkers|length }} known - drinker{{ stats.registered_drinkers|length|pluralize }} {% if stats.has_guest_pour %}(and untold guests){% endif %} - have poured. - - - -{% if stats.average_volume_ml %} - - Average Pour - - The average pour size is - {% volume stats.average_volume_ml %}. - - -{% endif %} - -{% endif %} diff --git a/pykeg/web/kegweb/templates/kegweb/drink_detail.html b/pykeg/web/kegweb/templates/kegweb/drink_detail.html deleted file mode 100644 index 2ab56be70..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drink_detail.html +++ /dev/null @@ -1,191 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Drink {{ drink.id }} by {% drinker_name drink nolink %} | {{ block.super }}{% endblock %} -{% block pagetitle %}Drink {{ drink.id }} by {% drinker_name drink %}{% endblock %} - -{% block content %} - -{% if user.is_staff %} - - - -{% endif %} - -{% if picture_form %} - -{% endif %} - -
    - -
    - - - - - - {% if user.is_staff %} - - {% else %} - - {% endif %} - - - - - - - - {% if drink.keg %} - - - - - {% endif %} - - - - - - - - - - - - -
    Size -
    {% csrf_token %} -
    - {% if kbsite.volume_display_units == 'metric' %} - - - mL - {% else %} - - - oz - {% endif %} - -
    -
    -
    - {% volume drink.volume_ml %} - {% if drink.duration %} - - (took {{drink.duration}} second{{drink.duration|pluralize}} to pour) - - {% endif %} -
    When - {% timeago drink.time %} -
    Keg - {{drink.keg.type.name}} -
    Session - {{ drink.session.GetTitle }} - ({{ drink.session.summarize_drinkers|safe }}) -
    Permalink - {{ drink.ShortUrl }} -
    - -{% if user.is_staff %} -

    Manager Controls

    -
    {% csrf_token %} -
    - - - - Spill - - Delete -
    -
    -{% endif %} -
    - - -
    - {% if drink.picture %} - - {% if picture_form %} -
    - -
    - {% endif %} - {% endif %} - {% if drink.shout %} - {% include 'kegweb/includes/drink_shout.html' %} - {% endif %} -
    - - -
    - -{% endblock content %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinker-rank.html b/pykeg/web/kegweb/templates/kegweb/drinker-rank.html deleted file mode 100644 index ffba4169d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinker-rank.html +++ /dev/null @@ -1,22 +0,0 @@ -{% load kegweblib %} -{% load humanize %} - -{% for volume_ml,user in ranked_drinkers %} -{% with forloop.counter as rank %} -
    -
    -
    - {% mugshot_box user 64 %} -
    -
    -

    - {{ user.get_full_name }}
    - - #{{ rank }} · {% volume volume_ml %} - -

    -
    -
    -
    -{% endwith %} -{% endfor %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinker_detail.html b/pykeg/web/kegweb/templates/kegweb/drinker_detail.html deleted file mode 100644 index 425f19516..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinker_detail.html +++ /dev/null @@ -1,182 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Drinker Details: {{ drinker.username }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Drinker Details: {{ drinker.username }}{% endblock %} - -{% block content %} -
    -
    - {% mugshot_box drinker %} -
    - See All Drinker Sessions -


    -
    - -
    - - -
    -
    - - {% if stats %} - - - - - - - - - - - - - - - {% if stats.total_pours %} - - - - - - - - - - {% endif %} - {% endif %} - - - - - - - {% with drinker.drinks.latest as last_drink %} - {% if last_drink %} - - - - - {% endif %} - {% endwith %} - -
    Total Volume {% volume stats.total_volume_ml %}
    Total Pours{{ stats.total_pours }}
    Total Sessions - {{ stats.sessions_count }}
    Average Pour{% volume stats.average_volume_ml %}
    Largest Pour{% volume stats.greatest_volume_ml %}
    Member Since - {{ drinker.date_joined|date:"l, F j Y" }}
    - ({% timeago drinker.date_joined %}) -
    Last Drink - {{ last_drink.time|date:"l, F j Y" }}
    - ({% timeago last_drink.time%}) -
    -
    - -
    - {% if not stats.total_pours %} - Looks like {{ drinker }} has never poured a drink. Boo! - {% else %} - - - {% if kbsite.volume_display_units == 'metric' %} - - {% else %} - - {% endif %} - - - - - {% if kbsite.volume_display_units == 'metric' %} - - {% else %} - - {% endif %} - - -
    total liters, by day of weektotal pints, by day of week{% chart sessions_by_weekday stats 350 100 %}
    all sessions, by liter per sessionall sessions, by pints per session{% chart sessions_by_volume stats 350 100 %}
    - {% endif %} -
    - - {% if chunk %} -
    - - {% with chunk.session as session %} - {% include "kegweb/keg-session.html" %} - {% endwith %} - -
    - {% endif %} - - {% if largest_session %} -
    - - {% with largest_session as session %} - {% include "kegweb/keg-session.html" %} - {% endwith %} - -
    - {% endif %} -
    - - {% for drink in drinks %} -
    -
    - {% mugshot_box drink.user 48 %} -
    -
    -
    - - {% drinker_name drink.user %} poured - {% volume drink.volume_ml badge %} - of {{ drink.keg.type.name }} - - - {% timeago drink.time %} - -
    - {% with drink.picture as pic %} - {% if pic %} -

    - - - - - {% endif %} - {% endwith %} - {% if drink.shout %} - {% include 'kegweb/includes/drink_shout.html' %} - {% endif %} -
    -
    -
    - {% endfor %} - -
    - -
    -
    -
    - -{% endblock %} - - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinker_sessions.html b/pykeg/web/kegweb/templates/kegweb/drinker_sessions.html deleted file mode 100644 index 56b6827b0..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinker_sessions.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Drinker Sessions: {{ drinker.username }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Drinker Sessions: {{ drinker.username }}{% endblock %} - -{% block content %} -
    -
    - {% with drinker as user %} - {% mugshot_box user %} - {% endwith %} -
    - Back to Drinker Details -


    -
    - -
    - {% if chunks %} - {% for chunk in chunks %} - {% with chunk.session as session %} - {% include "kegweb/keg-session.html" %} - {% endwith %} - {% endfor %} - {% include "kegweb/_pagination.html" with page=chunks %} - {% endif %} -
    -
    - -{% endblock %} - diff --git a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive.html b/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive.html deleted file mode 100644 index 0e6eb6428..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "kegweb/drinkingsession_archive_base.html" %} - -{% block title %}Sessions | {{ block.super }}{% endblock %} -{% block pagetitle %}Sessions{% endblock %} - -{% block session-right %} -
      - {% for d in date_list %} -
    • - {{d.year}} -
    • - {% endfor %} -
    -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_base.html b/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_base.html deleted file mode 100644 index f9e65978f..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_base.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block content %} -
    -
    -{% for session in sessions %} - {% include "kegweb/keg-session.html" %} -{% endfor %} -
    - -{% block session-right-hide %} -
    -
    -

    All Sessions

    - {% block session-right %} - {% endblock %} -
    -
    -{% endblock %} - -
    - -
    -
    -{% include "kegweb/_pagination.html" with page=page_obj %} -
    -
    - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_day.html b/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_day.html deleted file mode 100644 index 40901fbf1..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_day.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "kegweb/drinkingsession_archive_base.html" %} - -{% block title %}Sessions: {{day|date:"F d, Y"}} | {{ block.super }}{% endblock %} -{% block pagetitle %}Sessions: {{day|date:"F d, Y"}}{% endblock %} - -{% block session-right-hide %}{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_month.html b/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_month.html deleted file mode 100644 index 27e642a07..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_month.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "kegweb/drinkingsession_archive_base.html" %} -{% load tz %} - -{% block title %}Sessions: {{month|date:"F Y"}} | {{ block.super }}{% endblock %} -{% block pagetitle %}Sessions: {{month|date:"F Y"}}{% endblock %} - -{% block session-right %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_year.html b/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_year.html deleted file mode 100644 index 06ac9fb7d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/drinkingsession_archive_year.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "kegweb/drinkingsession_archive_base.html" %} -{% load tz %} -{% block title %}Sessions: {{year.year}} | {{ block.super }}{% endblock %} -{% block pagetitle %}Sessions: {{year.year}}{% endblock %} - -{% block session-right %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/fullscreen.html b/pykeg/web/kegweb/templates/kegweb/fullscreen.html deleted file mode 100644 index d3047cfa4..000000000 --- a/pykeg/web/kegweb/templates/kegweb/fullscreen.html +++ /dev/null @@ -1,167 +0,0 @@ -{% extends "skel.html" %} -{% load static kegweblib %} - -{% block title %}{{ kbsite.title }} (Fullscreen Mode){% endblock %} - -{% block kb-extracss %} - - - - -{% endblock %} - -{% block body %} - -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    - - -
    - -
    -
    - -{% endblock %} - -{% block kb-extrajs %} - - - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/includes/drink_shout.html b/pykeg/web/kegweb/templates/kegweb/includes/drink_shout.html deleted file mode 100644 index ea83da09d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/includes/drink_shout.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load kegweblib %} -
    -

    {{ drink.shout }}

    -{% drinker_name drink %}, during pour -
    -
    -
    \ No newline at end of file diff --git a/pykeg/web/kegweb/templates/kegweb/includes/tap_snapshot.html b/pykeg/web/kegweb/templates/kegweb/includes/tap_snapshot.html deleted file mode 100644 index 2acb80b21..000000000 --- a/pykeg/web/kegweb/templates/kegweb/includes/tap_snapshot.html +++ /dev/null @@ -1,63 +0,0 @@ -{% load kegweblib %} - -{% with tap.current_keg as keg %} - -{% if keg %} -
    -{% elif user.is_staff %} -
    -{% else %} -
    -{% endif %} - -
    - {% if keg.type and keg.type.picture %} -
    - {% else %} -
    - {% endif %} -

    - {% if keg %} - {{ keg.type.name }}
    - {{ tap.name }} - {% else %} - {{ tap.name }} - {% endif %} -

    - - {% if not keg %} -

    - Tap is offline{% if user.is_staff %} (click to manage){% endif %}. -

    - - {% else %} - {% progress_bar keg.percent_full %} -  {% volume keg.remaining_volume_ml %} remaining - - {% if tap.temperature_sensor %} - {% with tap.temperature_sensor.LastLog as temp %} - {% if temp %} - · {% temperature temp.temp %} - {% endif %} - {% endwith %} - {% endif %} - {% endif %} - -
    - - {% if keg.type and keg.type.picture %} - {% url "kb-keg" keg.id as keg_url %} -
    -
    - - - -
    -
    - {% endif %} - -
    -
    -{% endwith %} diff --git a/pykeg/web/kegweb/templates/kegweb/includes/timeline.html b/pykeg/web/kegweb/templates/kegweb/includes/timeline.html deleted file mode 100644 index f3e75095c..000000000 --- a/pykeg/web/kegweb/templates/kegweb/includes/timeline.html +++ /dev/null @@ -1,5 +0,0 @@ -{% with 'timeline' as gallery_id %} -{% for event in events %} -{% include 'kegweb/includes/timeline_event.html' %} -{% endfor %} -{% endwith %} \ No newline at end of file diff --git a/pykeg/web/kegweb/templates/kegweb/includes/timeline_event.html b/pykeg/web/kegweb/templates/kegweb/includes/timeline_event.html deleted file mode 100644 index f40de973d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/includes/timeline_event.html +++ /dev/null @@ -1,80 +0,0 @@ -{% load kegweblib %} -{% load humanize %} - -{% if event.kind == 'drink_poured' %} -{% with event.drink as drink %} -
    -
    - {% mugshot_box event.user 48 %} -
    -
    -
    - - {% drinker_name event.user %} poured - {% volume drink.volume_ml badge %} - of {{ drink.keg.type.name }} - - - {% timeago event.time %} - -
    - {% with drink.picture as pic %} - {% if pic %} -

    - - - - - {% endif %} - {% endwith %} - {% if drink.shout %} - {% include 'kegweb/includes/drink_shout.html' %} - {% endif %} -
    -
    -
    -{% endwith %} - -{% elif event.kind == 'session_joined' %} -
    -
    - - {% drinker_name event.user %} started drinking - - - {% timeago event.time %} - -
    -
    -
    - -{% elif event.kind == 'keg_tapped' %} -
    -
    - - Keg {{ event.keg.id }} ({{ event.keg.type.name }}) was tapped. - - - {% timeago event.time %} - -
    -
    -
    - -{% elif event.kind == 'keg_ended' %} -
    -
    - - Keg {{ event.keg.id }} ({{ event.keg.type.name }}) was finished. - - - {% timeago event.time %} - -
    -
    -
    -{% endif %} - -

    \ No newline at end of file diff --git a/pykeg/web/kegweb/templates/kegweb/keg-session.html b/pykeg/web/kegweb/templates/kegweb/keg-session.html deleted file mode 100644 index 197ddb0e6..000000000 --- a/pykeg/web/kegweb/templates/kegweb/keg-session.html +++ /dev/null @@ -1,56 +0,0 @@ -{% load kegweblib %} - -

    -
    - -

    {{ session.GetTitle }}

    - -{% with session.get_stats as stats %} - -{% with session.get_non_highlighted_pictures as pictures %} -{% with session.get_highlighted_picture as highlight %} - -{% if highlight %} -
    -
    - {% gallery highlight "span12" gallery_id=session.id %} -
    -
    -
    -
    - {% include "kegweb/session-badges.html" %} -
    -
    -
    -
    - {% gallery pictures|slice:":5" "span2" gallery_id=session.id %} -
    -
    -
    -
    -{% else %} -
    -
    - {% include "kegweb/session-badges.html" %} -
    -
    -{% endif %} -{% endwith %} -{% endwith %} - -
    -
    -

    - - {{ session.start_time|date:"M j Y, P" }} - · - {{ session.summarize_drinkers|safe }} -

    -
    -
    - -
    -
    - -{% endwith %} diff --git a/pykeg/web/kegweb/templates/kegweb/keg-snapshot.html b/pykeg/web/kegweb/templates/kegweb/keg-snapshot.html deleted file mode 100644 index 624819e1e..000000000 --- a/pykeg/web/kegweb/templates/kegweb/keg-snapshot.html +++ /dev/null @@ -1,53 +0,0 @@ -{% load static kegweblib %} -{% with keg.current_tap as tap %} -{% url "kb-keg" keg.id as keg_url %} -
    -
    - - -
    -

    {{ keg.type.name }} {% if tap %}{{ tap.name }}{% endif %}

    - -
      -
    • -

      {% volume keg.served_volume %}

      - Poured -
    • -
    • - {% if keg.remaining_volume_ml > 0 and not keg.is_finished %} -

      {% volume keg.remaining_volume_ml %}

      - {% else %} -

      {% volume 0 %}

      - {% endif %} - Remaining -
    • - - {% if tap %} - {% if tap.temperature_sensor %} -
    • - {% if kbsite.temperature_display_units == "f" %} -

      {{ tap.Temperature.TempF|floatformat:1}} °F

      - {% else %} -

      {{ tap.Temperature.TempC|floatformat:1}} °C

      - {% endif %} - Temperature -
    • - {% endif %} - {% endif %} -
    - -
    -
    - -
    - -{% endwith %} diff --git a/pykeg/web/kegweb/templates/kegweb/keg_detail.html b/pykeg/web/kegweb/templates/kegweb/keg_detail.html deleted file mode 100644 index 6c58a4305..000000000 --- a/pykeg/web/kegweb/templates/kegweb/keg_detail.html +++ /dev/null @@ -1,140 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Keg {{ keg.id }} | {{ block.super }}{% endblock %} -{% block pagetitle %}Keg {{ keg.id }}{% endblock %} - -{% block content %} -
    -
    - - -
    -
    -
    - -

    - {{keg.type.name}} - {% if keg.type.producer %} - {{keg.type.producer}} - {% if user.is_staff %} - (edit beer) - {% endif %} - {% endif %} -

    - - {% if keg.current_tap %} - {% progress_bar keg.percent_full %} - {% endif %} - - {% include "kegweb/basic-badges.html" %} - - {% if keg.description %} -
    -

    {{ keg.description}}

    -
    - {% endif %} -
    -
    - -
    - - - - - - - - {% include "kegweb/basic-stats.html" %} - - {% if keg.spilled_volume > 0 %} - - - - - {% endif %} - - -
    Status - {% if keg.current_tap %} - Keg is online, and is {{ keg.keg_age.days }} - day{{keg.keg_age.days|pluralize}} old. - - {% if keg.is_empty %} - (empty) - {% else %} - ({{ keg.percent_full|floatformat:2 }}% full) - {% endif %} - - {% else %} - Keg is offline; it lasted {{ keg.keg_age.days }} - day{{keg.keg_age.days|pluralize}}. - {% endif %} -
    Total Spilled/Lost{% volume keg.spilled_volume %}
    -
    - -
    - - - - - - - - {% with keg.current_tap as tap %} - {% if tap and tap.temperature_sensor %} - - - - - {% endif %} - {% endwith %} - -
    Volume by Weekday{% chart volume_by_weekday stats 340 100 %}
    Temperature - {{ tap.Temperature.TempC|floatformat:1}}°C / - {{ tap.Temperature.TempF|floatformat:1}}°F
    - {% chart temp_sensor tap.temperature_sensor 340 60 %} -
    -
    - - {% if last_session %} -
    - {% for session in last_session %} - {% include "kegweb/keg-session.html" %} - {% endfor %} -
    - {% endif %} - -
    -
    - -
    - {% if stats %} -

    Top Users

    - {% chart users_by_volume stats 280 220 %}
    - - {% with keg.get_top_users as ranked_drinkers %} - {% include "kegweb/drinker-rank.html" %} - {% endwith %} - See All Keg Sessions - {% endif %} -
    -
    - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/keg_list.html b/pykeg/web/kegweb/templates/kegweb/keg_list.html deleted file mode 100644 index 4a92bbdcd..000000000 --- a/pykeg/web/kegweb/templates/kegweb/keg_list.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Keg List | {{ block.super }}{% endblock %} -{% block pagetitle %}Keg List{% endblock %} - -{% block content %} - -{% for keg in kegs %} -
    -
    - {% include "kegweb/keg-snapshot.html" %} -
    -
    -{% endfor %} - -{% include "kegweb/_pagination.html" with page=page_obj %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/keg_sessions.html b/pykeg/web/kegweb/templates/kegweb/keg_sessions.html deleted file mode 100644 index 8fc7b075a..000000000 --- a/pykeg/web/kegweb/templates/kegweb/keg_sessions.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Keg {{ keg.id }}: Sessions | {{ block.super }}{% endblock %} -{% block pagetitle %}Keg {{ keg.id }}: Sessions{% endblock %} - -{% block content %} -
    -
    - {% if sessions %} - {% for session in sessions %} - {% include "kegweb/keg-session.html" %} - {% endfor %} - {% include "kegweb/_pagination.html" with page=sessions %} - {% endif %} -
    - -
    -

    Top Users

    - {% chart users_by_volume stats 280 220 %}
    - - {% with keg.get_top_users as ranked_drinkers %} - {% include "kegweb/drinker-rank.html" %} - {% endwith %} - Back to Keg Detail -
    - -
    - -{% endblock %} - diff --git a/pykeg/web/kegweb/templates/kegweb/members_only.html b/pykeg/web/kegweb/templates/kegweb/members_only.html deleted file mode 100644 index f5bb43039..000000000 --- a/pykeg/web/kegweb/templates/kegweb/members_only.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Access Denied: Members Only | {{ block.super }}{% endblock %} -{% block pagetitle %}Access Denied{% endblock %} - -{% block content %} -

    -You need to log in as a registered drinker in order to see this content. -

    -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/mugshot_box.html b/pykeg/web/kegweb/templates/kegweb/mugshot_box.html deleted file mode 100644 index 43d2127d5..000000000 --- a/pykeg/web/kegweb/templates/kegweb/mugshot_box.html +++ /dev/null @@ -1,24 +0,0 @@ -{% load static %} -{% spaceless %} -
    - -{% if user %} - -{% endif %} - - - - -
    -{% endspaceless %} diff --git a/pykeg/web/kegweb/templates/kegweb/picture-gallery.html b/pykeg/web/kegweb/templates/kegweb/picture-gallery.html deleted file mode 100644 index 382a02261..000000000 --- a/pykeg/web/kegweb/templates/kegweb/picture-gallery.html +++ /dev/null @@ -1,15 +0,0 @@ -
      - -{% for pic in gallery_pictures %} -{% with pic.get_caption as caption %} -
    • - - {{ caption }} - -
    • -{% endwith %} -{% endfor %} - - -
    diff --git a/pykeg/web/kegweb/templates/kegweb/session-badges.html b/pykeg/web/kegweb/templates/kegweb/session-badges.html deleted file mode 100644 index 01a5ef51d..000000000 --- a/pykeg/web/kegweb/templates/kegweb/session-badges.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load kegweblib %} -
      -{% if stats.total_volume_ml %} -{% badge stats.total_pours "Pour" do_pluralize=True style="badge-small" %} -{% badge stats.total_volume_ml "Poured" is_volume=True style="badge-small" %} -{% badge stats.average_volume_ml "Avg Pour" is_volume=True style="badge-small" %} -{% endif %} -
    - diff --git a/pykeg/web/kegweb/templates/kegweb/session_detail.html b/pykeg/web/kegweb/templates/kegweb/session_detail.html deleted file mode 100644 index d32720870..000000000 --- a/pykeg/web/kegweb/templates/kegweb/session_detail.html +++ /dev/null @@ -1,85 +0,0 @@ -{% extends "page-twocol.html" %} -{% load kegweblib %} -{% load humanize %} - -{% block title %}{{ session.GetTitle }} | {{ block.super}}{% endblock %} -{% block pagetitle %} - {{ session.GetTitle }} - {{ session.start_time|date:"l, F jS, P" }} -{% endblock %} - -{% block col-1 %} - -{% with session.events.timeline as events %} -{% include 'kegweb/includes/timeline.html' %} -{% endwith %} - -{% endblock col-1 %} - - - -{% block col-2 %} - - - - - - - - - - - - - - - - - - - - - - -
    - Session Summary -
    Keg{{kegs|pluralize}} - {% for keg in kegs %} - Keg #{{ keg.id }} ({{ keg.type }})
    - {% endfor %} -
    Total - {{ session.drinks.count }} pours, - {% volume session.volume_ml %} -
    Drinkers - {{ stats.registered_drinkers|length }} - drinker{{ stats.registered_drinkers|length|pluralize }} -
    - -{% chart users_by_volume stats 300 250 %} -
    - -{% with session.UserChunksByVolume as chunks %} -{% if chunks %} - - - - - - - - - {% for chunk in session.UserChunksByVolume %} - - - - - {% endfor %} - -
    - Drinker - - Volume -
    {% drinker_name chunk.user %}{% volume chunk.volume_ml %}
    -{% endif %} -{% endwith %} -{% endblock col-2 %} diff --git a/pykeg/web/kegweb/templates/kegweb/staff_only.html b/pykeg/web/kegweb/templates/kegweb/staff_only.html deleted file mode 100644 index be69dbf29..000000000 --- a/pykeg/web/kegweb/templates/kegweb/staff_only.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Kegbot Access Denied: Admins Only{% endblock %} -{% block pagetitle %}Access Denied{% endblock %} - -{% block content %} -

    -You need to log in as a site admin in order to see this content. -

    -{% endblock %} diff --git a/pykeg/web/kegweb/templates/kegweb/system-stats.html b/pykeg/web/kegweb/templates/kegweb/system-stats.html deleted file mode 100644 index 0eb11a1c4..000000000 --- a/pykeg/web/kegweb/templates/kegweb/system-stats.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}System Stats | {{ block.super }}{% endblock %} -{% block pagetitle %}System Stats{% endblock %} - -{% block content%} - -
    - -{% if not stats %} -
    -
    -

    No Stats.. yet.

    -

    - This place will be a lot more interesting once somebody has - poured a drink. -

    -
    -
    - -{% else %} -
    - - -
    -
    - {% include "kegweb/basic-badges.html" %} - - - {% include "kegweb/basic-stats.html" %} - -
    -
    - - {% if largest_session %} -
    - {% with largest_session as session %} - {% include "kegweb/keg-session.html" %} - {% endwith %} -
    - {% endif %} -
    -
    -{% endif %} - -
    - {% if stats %} -

    Top Drinkers

    - {% chart users_by_volume stats 280 220 %}
    - {% with top_drinkers as ranked_drinkers %} - {% include "kegweb/drinker-rank.html" %} - {% endwith %} - {% endif %} -
    - -
    - -{% endblock %} - -{% block kb-extrajs %} - -{% endblock %} diff --git a/pykeg/web/kegweb/templatetags/__init__.py b/pykeg/web/kegweb/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/kegweb/templatetags/kegweblib.py b/pykeg/web/kegweb/templatetags/kegweblib.py deleted file mode 100644 index b1e2210b1..000000000 --- a/pykeg/web/kegweb/templatetags/kegweblib.py +++ /dev/null @@ -1,425 +0,0 @@ -import zoneinfo - -from django.conf import settings -from django.template import ( - Library, - Node, - TemplateSyntaxError, - Variable, - VariableDoesNotExist, -) -from django.template.defaultfilters import pluralize -from django.urls import reverse -from django.utils import timezone -from django.utils.safestring import mark_safe - -from pykeg.core import models -from pykeg.core.util import CtoF -from pykeg.util import kbjson, units -from pykeg.web.charts import charts - -register = Library() - - -@register.inclusion_tag("kegweb/mugshot_box.html", takes_context=True) -def mugshot_box(context, user, boxsize=0): - return { - "user": user, - "boxsize": boxsize, - "guest_info": context.get("guest_info", None), - "STATIC_URL": context.get("STATIC_URL"), - } - - -@register.inclusion_tag("kegweb/picture-gallery.html") -def gallery(picture_or_pictures, thumb_size="span2", gallery_id=""): - c = {} - if not hasattr(picture_or_pictures, "__iter__"): - c["gallery_pictures"] = [picture_or_pictures] - else: - c["gallery_pictures"] = picture_or_pictures - c["thumb_size"] = thumb_size - c["gallery_id"] = gallery_id - return c - - -@register.inclusion_tag("kegweb/badge.html") -def badge(amount, caption, style="", is_volume=False, do_pluralize=False): - if is_volume: - amount = mark_safe(VolumeNode.format(amount, "mL")) - if do_pluralize: - caption += pluralize(amount) - return { - "badge_amount": amount, - "badge_caption": caption, - "badge_style": style, - } - - -@register.inclusion_tag("kegweb/includes/progress_bar.html") -def progress_bar(progress_int, extra_css=""): - c = {} - try: - progress_int = max(int(progress_int), 0) - except ValueError: - progress_int = 0 - progress_int = min(progress_int, 100) - - c["progress_int"] = progress_int - c["extra_css"] = extra_css - if progress_int < 10: - bar_type = "bar-danger" - elif progress_int < 25: - bar_type = "bar-warning" - else: - bar_type = "bar-success" - c["bar_type"] = bar_type - return c - - -# navitem - - -@register.tag("navitem") -def navitem(parser, token): - """{% navitem [exact] %}""" - tokens = token.split_contents() - if len(tokens) < 3: - raise TemplateSyntaxError(f"{tokens[0]} requires at least 3 tokens") - return NavitemNode(*tokens[1:]) - - -class NavitemNode(Node): - def __init__(self, *args): - self._viewname = args[0] - self._title = args[1] - self._exact = "exact" in args[2:] - - def render(self, context): - viewname = Variable(self._viewname).resolve(context) - title = Variable(self._title).resolve(context) - if viewname.startswith("/"): - urlbase = viewname - else: - urlbase = reverse(viewname) - - request_path = context["request_path"] - - if self._exact: - active = request_path == urlbase - else: - active = request_path.startswith(urlbase) - if active: - res = '<li class="active">' - else: - res = "<li>" - res += f'<a href="{urlbase}">{title}</a></li>' - return res - - -# timeago - - -@register.tag("timeago") -def timeago(parser, token): - """{% timeago <timestamp> %}""" - tokens = token.contents.split() - if len(tokens) != 2: - raise TemplateSyntaxError(f"{tokens[0]} requires 2 tokens") - return TimeagoNode(tokens[1]) - - -class TimeagoNode(Node): - def __init__(self, timestamp_varname): - self._timestamp_varname = timestamp_varname - - def render(self, context): - tv = Variable(self._timestamp_varname) - ts = tv.resolve(context) - - # Try to set time zone information. - if settings.TIME_ZONE and not settings.USE_TZ: - try: - tz = zoneinfo.ZoneInfo(settings.TIME_ZONE) - ts = ts.replace(tzinfo=tz) - except zoneinfo.ZoneInfoNotFoundError: - pass - - iso = ts.isoformat() - alt = timezone.localtime(ts).strftime("%A, %B %d, %Y %I:%M%p") - return f'<abbr class="timeago" title="{iso}">{alt}</abbr>' - - -# temperature - - -@register.tag("temperature") -def temperature_tag(parser, token): - """{% temperature <temp_c> %}""" - tokens = token.contents.split() - if len(tokens) < 2: - raise TemplateSyntaxError(f"{tokens[0]} requires at least 2 tokens") - return TemperatureNode(tokens[1]) - - -class TemperatureNode(Node): - TEMPLATE = "%(amount)s° %(unit)s" - - def __init__(self, varname): - self.varname = varname - - def render(self, context): - v = Variable(self.varname) - try: - amount = v.resolve(context) - except VariableDoesNotExist, ValueError: - raise - amount = "unknown" - - unit = "C" - kbsite = models.KegbotSite.get() - if kbsite.temperature_display_units == "f": - unit = "F" - amount = CtoF(amount) - - return self.TEMPLATE % {"amount": amount, "unit": unit} - - -# volume - - -@register.tag("volume") -def volumetag(parser, token): - """{% volume <amount> %}""" - tokens = token.contents.split() - if len(tokens) < 2: - raise TemplateSyntaxError(f"{tokens[0]} requires at least 2 tokens") - return VolumeNode(tokens[1], tokens[2:]) - - -class VolumeNode(Node): - TEMPLATE = """ - <span class="hmeasure %(extra_css)s" title="%(title)s"> - <span class="num">%(amount)s</span> - <span class="unit">%(units)s</span> - </span>""".strip() - - def __init__(self, volume_varname, extra_args): - self._volume_varname = volume_varname - self._extra_args = extra_args - - def render(self, context): - tv = Variable(self._volume_varname) - try: - num = float(tv.resolve(context)) - except VariableDoesNotExist, ValueError: - num = "unknown" - unit = "mL" - make_badge = "badge" in self._extra_args - return self.format(num, unit, make_badge) - - @classmethod - def format(cls, amount, units, make_badge=False): - if amount < 0: - amount = 0 - ctx = { - "units": units, - "amount": amount, - "title": f"{amount} {units}", - "extra_css": "badge " if make_badge else "", - } - return cls.TEMPLATE % ctx - - -# drinker - - -@register.tag("drinker_name") -def drinker_name_tag(parser, token): - """{% drinker_name <drink_or_user_obj> [nolink] %}""" - tokens = token.contents.split() - if len(tokens) < 2: - raise TemplateSyntaxError(f"{tokens[0]} requires at least 2 tokens") - return DrinkerNameNode(tokens[1], tokens[2:]) - - -class DrinkerNameNode(Node): - def __init__(self, drink_varname, extra_args): - self._varname = drink_varname - self._extra_args = extra_args - - def render(self, context): - obj = Variable(self._varname) - try: - obj = obj.resolve(context) - except VariableDoesNotExist, ValueError: - obj = None - - user = None - if obj: - if isinstance(obj, models.Drink) or isinstance(obj, models.SystemEvent): - user = obj.user - elif isinstance(obj, models.User): - user = obj - if user: - if "nolink" in self._extra_args: - return user.get_full_name() - else: - return '<a href="{}">{}</a>'.format( - reverse("kb-drinker", args=[user.username]), - user.get_full_name(), - ) - return context["guest_info"]["name"] - - -# chart - - -@register.tag("chart") -def chart(parser, tokens): - """{% chart <charttype> <obj> width height %}""" - tokens = tokens.contents.split() - if len(tokens) < 4: - raise TemplateSyntaxError("chart requires at least 4 arguments") - charttype = tokens[1] - try: - width = int(tokens[-2]) - height = int(tokens[-1]) - except ValueError: - raise TemplateSyntaxError("invalid width or height") - args = tokens[2:-2] - return ChartNode(charttype, width, height, args) - - -class ChartNode(Node): - CHART_TMPL = """ - <!-- begin chart %(chart_id)s --> - <div id="chart-%(chart_id)s-container" - style="height: %(height)spx; width: %(width)spx;" - class="kb-chartbox"></div> - <script type="text/javascript"> - var chart_%(chart_id)s; - $(document).ready(function() { - var chart_data = %(chart_data)s; - chart_%(chart_id)s = new Highcharts.Chart(chart_data); - }); - </script> - <!-- end chart %(chart_id)s --> - - """ - ERROR_TMPL = """ - <!-- begin chart %(chart_id)s --> - <div id="chart-%(chart_id)s-container" - style="height: %(height)spx; width: %(width)spx;" - class="kb-chartbox-error"> - %(error_str)s - </div> - <!-- end chart %(chart_id)s --> - """ - - def __init__(self, charttype, width, height, args): - self._charttype = charttype - self._width = width - self._height = height - self._args = args - - self._chart_fn = getattr(charts, f"chart_{self._charttype}", None) - - def _get_chart_id(self, context): - # TODO(mikey): Is there a better way to store _CHART_ID? - if not hasattr(context, "_CHART_ID"): - context._CHART_ID = 0 - context._CHART_ID += 1 - return context._CHART_ID - - def show_error(self, error_str): - ctx = { - "error_str": error_str, - "chart_id": 0, - "width": self._width, - "height": self._height, - } - return ChartNode.ERROR_TMPL % ctx - - def render(self, context): - if not self._chart_fn: - return self.show_error(f"Unknown chart type: {self._charttype}") - - chart_id = self._get_chart_id(context) - obj = Variable(self._args[0]).resolve(context) - - metric_volumes = context.get("metric_volumes", False) - temperature_units = context.get("temperature_display_units", "f") - - try: - chart_result = self._chart_fn( - obj, metric_volumes=metric_volumes, temperature_units=temperature_units - ) - except charts.ChartError as e: - return self.show_error(str(e)) - - chart_base = { - "chart": { - "borderColor": "#eeeeff", - "borderWidth": 0, - "renderTo": f"chart-{chart_id}-container", - }, - "credits": { - "enabled": False, - }, - "legend": { - "enabled": False, - }, - "margin": [0, 0, 0, 0], - "title": { - "text": None, - }, - "yAxis": { - "labels": {"align": "left"}, - "title": { - "text": None, - }, - }, - } - - chart_data = chart_base - for k, v in list(chart_result.items()): - if k not in chart_data: - chart_data[k] = v - elif isinstance(v, dict): - chart_data[k].update(v) - else: - chart_data[k] = v - chart_data = kbjson.dumps(chart_data, indent=None) - - ctx = { - "chart_id": chart_id, - "width": self._width, - "height": self._height, - "chart_data": chart_data, - } - - return ChartNode.CHART_TMPL % ctx - - -@register.filter -def volume(text, fmt="pints"): - try: - vol = units.Quantity(float(text)) - except ValueError: - return text - if fmt == "pints": - res = vol.InPints() - elif fmt == "liters": - res = vol.InLiters() - elif fmt == "ounces": - res = vol.InOunces() - elif fmt == "gallons": - res = vol.InUSGallons() - elif fmt == "twelveounces": - res = vol.InTwelveOunceBeers() - elif fmt == "halfbarrels": - res = vol.InHalfBarrelKegs() - else: - raise TemplateSyntaxError(f"Unknown volume format: {fmt}") - return float(res) diff --git a/pykeg/web/kegweb/templatetags/paginator_tags.py b/pykeg/web/kegweb/templatetags/paginator_tags.py deleted file mode 100644 index b7f497f25..000000000 --- a/pykeg/web/kegweb/templatetags/paginator_tags.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Template helpers for rendering Django Paginator pages. - -Replaces the unmaintained django-bootstrap-pagination package with Django's -own Paginator (see kegweb/_pagination.html for the Bootstrap 3 markup). -""" - -from django import template - -register = template.Library() - - -@register.simple_tag -def elided_page_range(page, on_each_side=2, on_ends=1): - """Return a windowed page range (with ELLIPSIS markers) for a Page.""" - return page.paginator.get_elided_page_range( - page.number, on_each_side=on_each_side, on_ends=on_ends - ) diff --git a/pykeg/web/kegweb/urls.py b/pykeg/web/kegweb/urls.py deleted file mode 100644 index d60bdfd7c..000000000 --- a/pykeg/web/kegweb/urls.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.urls import path - -from . import views - -urlpatterns = [ - # main page - path("", views.index, name="kb-home"), - # stats - path("stats/", views.system_stats, name="kb-stats"), - # kegs - path("kegs/", views.KegListView.as_view(), name="kb-kegs"), - path("kegs/<int:keg_id>/", views.keg_detail, name="kb-keg"), - path("kegs/<int:keg_id>/sessions/", views.keg_sessions, name="kb-keg-sessions"), - # fullscreen mode - path("fullscreen/", views.fullscreen, name="kb-fullscreen"), - # drinkers - path("drinkers/<str:username>/", views.user_detail, name="kb-drinker"), - path( - "drinkers/<str:username>/sessions/", - views.drinker_sessions, - name="kb-drinker-sessions", - ), - # drinks - path("drinks/<int:drink_id>/", views.drink_detail, name="kb-drink"), - path("drink/<int:drink_id>/", views.short_drink_detail), - path("d/<int:drink_id>/", views.short_drink_detail, name="kb-drink-short"), - # sessions - path("session/<int:session_id>/", views.short_session_detail), - path("s/<int:session_id>/", views.short_session_detail, name="kb-session-short"), - path("sessions/", views.SessionArchiveIndexView.as_view(), name="kb-sessions"), - path( - "sessions/<int:year>/", - views.SessionYearArchiveView.as_view(), - name="kb-sessions-year", - ), - path( - "sessions/<int:year>/<int:month>/", - views.SessionMonthArchiveView.as_view(month_format="%m"), - name="kb-sessions-month", - ), - path( - "sessions/<int:year>/<int:month>/<int:day>/", - views.SessionDayArchiveView.as_view(month_format="%m"), - name="kb-sessions-day", - ), - path( - "sessions/<int:year>/<int:month>/<int:day>/<int:pk>/", - views.SessionDateDetailView.as_view(month_format="%m"), - name="kb-session-detail", - ), -] diff --git a/pykeg/web/kegweb/views.py b/pykeg/web/kegweb/views.py deleted file mode 100644 index bfb48bf8b..000000000 --- a/pykeg/web/kegweb/views.py +++ /dev/null @@ -1,267 +0,0 @@ -"""Kegweb main views.""" - -from django.contrib import messages -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.shortcuts import get_object_or_404, redirect, render -from django.views.decorators.cache import cache_page -from django.views.generic.dates import ( - ArchiveIndexView, - DateDetailView, - DayArchiveView, - MonthArchiveView, - YearArchiveView, -) -from django.views.generic.list import ListView - -from pykeg.core import models -from pykeg.web.kegweb import forms - -# main views - - -@cache_page(30) -def index(request): - context = {} - - context["taps"] = models.KegTap.objects.all() - context["events"] = models.SystemEvent.objects.timeline()[:20] - sessions = models.DrinkingSession.objects.all().order_by("-id")[:10] - context["sessions"] = sessions - - if sessions: - last_session = sessions[0] - context["most_recent_session"] = last_session - if sessions and last_session.IsActiveNow(): - context["current_session"] = last_session - - return render(request, "index.html", context=context) - - -@cache_page(30) -def system_stats(request): - stats = models.KegbotSite.get().get_stats() - context = { - "stats": stats, - } - - top_drinkers = [] - for username, vol in list(stats.get("volume_by_drinker", {}).items()): - try: - user = models.User.objects.get(username=username) - except models.User.DoesNotExist: - continue # should not happen - top_drinkers.append((vol, user)) - top_drinkers.sort(reverse=True) - - largest_session_id = stats.get("largest_session", {}).get("session_id", None) - if largest_session_id: - try: - context["largest_session"] = models.DrinkingSession.objects.get(pk=largest_session_id) - except models.DrinkingSession.DoesNotExist: - # Stats out of date. - pass - - context["top_drinkers"] = top_drinkers[:10] - - return render(request, "kegweb/system-stats.html", context=context) - - -# object lists and detail (generic views) - - -def user_detail(request, username): - user = get_object_or_404(models.User, username=username) - stats = user.get_stats() - drinks = user.drinks.all() - - context = { - "drinks": drinks, - "stats": stats, - "drinker": user, - } - - largest_session_id = stats.get("largest_session", {}).get("session_id", None) - if largest_session_id: - context["largest_session"] = models.DrinkingSession.objects.get(pk=largest_session_id) - - return render(request, "kegweb/drinker_detail.html", context=context) - - -class KegListView(ListView): - model = models.Keg - template_name = "kegweb/keg_list.html" - context_object_name = "kegs" - paginate_by = 10 - - def get_queryset(self): - return models.Keg.objects.all().order_by("-id") - - -def fullscreen(request): - context = {} - taps = models.KegTap.objects.all() - active_taps = [t for t in taps if t.current_keg] - pages = [active_taps[i : i + 4] for i in range(0, len(active_taps), 4)] - context["pages"] = pages - - return render(request, "kegweb/fullscreen.html", context=context) - - -@cache_page(30) -def keg_detail(request, keg_id): - keg = get_object_or_404(models.Keg, id=keg_id) - sessions = keg.get_sessions() - last_session = sessions[:1] - - context = { - "keg": keg, - "stats": keg.get_stats(), - "sessions": sessions, - "last_session": last_session, - } - return render(request, "kegweb/keg_detail.html", context=context) - - -def short_drink_detail(request, drink_id): - return redirect("kb-drink", drink_id=str(drink_id), permanent=True) - - -def short_session_detail(request, session_id): - session = get_object_or_404(models.DrinkingSession, id=session_id) - url = session.get_absolute_url() - return redirect(url, permanent=True) - - -def drink_detail(request, drink_id): - drink = get_object_or_404(models.Drink, id=drink_id) - context = { - "drink": drink, - } - - can_delete = (request.user == drink.user) or request.user.is_staff - - if can_delete: - picture_form = forms.DeletePictureForm(initial={"picture": drink.picture}) - else: - picture_form = None - - if request.method == "POST": - if can_delete: - picture_form = forms.DeletePictureForm(request.POST) - if picture_form.is_valid(): - drink.picture.erase_and_delete() - picture_form = None - messages.success(request, "Erased image.") - else: - messages.error(request, "request not valid: " + str(picture_form.errors)) - else: - messages.error(request, "No permission to delete picture.") - return redirect("kb-drink", drink_id=str(drink_id)) - - context["picture_form"] = picture_form - return render(request, "kegweb/drink_detail.html", context=context) - - -def drinker_sessions(request, username): - user = get_object_or_404(models.User, username=username) - stats = user.get_stats() - drinks = user.drinks.all() - - chunks = ( - models.Stats.objects.filter( - user=user, keg__isnull=True, session__isnull=False, is_first=True - ) - .order_by("-id") - .select_related("session") - ) - - paginator = Paginator(chunks, 5) - - page = request.GET.get("page") - try: - chunks = paginator.page(page) - except PageNotAnInteger: - chunks = paginator.page(1) - except EmptyPage: - chunks = paginator.page(paginator.num_pages) - - context = { - "drinks": drinks, - "chunks": chunks, - "stats": stats, - "drinker": user, - } - - return render(request, "kegweb/drinker_sessions.html", context=context) - - -def keg_sessions(request, keg_id): - keg = get_object_or_404(models.Keg, id=keg_id) - sessions = keg.get_sessions() - - paginator = Paginator(sessions, 5) - - page = request.GET.get("page") - try: - sessions = paginator.page(page) - except PageNotAnInteger: - sessions = paginator.page(1) - except EmptyPage: - sessions = paginator.page(paginator.num_pages) - - context = { - "keg": keg, - "stats": keg.get_stats(), - "sessions": sessions, - } - return render(request, "kegweb/keg_sessions.html", context=context) - - -class SessionArchiveIndexView(ArchiveIndexView): - model = models.DrinkingSession - date_field = "start_time" - template_name = "kegweb/drinkingsession_archive.html" - context_object_name = "sessions" - paginate_by = 20 - - -class SessionYearArchiveView(YearArchiveView): - model = models.DrinkingSession - date_field = "start_time" - template_name = "kegweb/drinkingsession_archive_year.html" - make_object_list = True - context_object_name = "sessions" - paginate_by = 20 - - -class SessionMonthArchiveView(MonthArchiveView): - model = models.DrinkingSession - date_field = "start_time" - template_name = "kegweb/drinkingsession_archive_month.html" - make_object_list = True - context_object_name = "sessions" - paginate_by = 20 - - -class SessionDayArchiveView(DayArchiveView): - model = models.DrinkingSession - date_field = "start_time" - template_name = "kegweb/drinkingsession_archive_day.html" - make_object_list = True - context_object_name = "sessions" - paginate_by = 20 - - -class SessionDateDetailView(DateDetailView): - model = models.DrinkingSession - date_field = "start_time" - template_name = "kegweb/session_detail.html" - context_object_name = "session" - - def get_context_data(self, **kwargs): - """Adds `stats` to the context.""" - ret = super().get_context_data(**kwargs) - stats = ret[self.context_object_name].get_stats() - ret["stats"] = stats - ret["kegs"] = [models.Keg.objects.get(pk=pk) for pk in stats.get("keg_ids", [])] - return ret diff --git a/pykeg/web/middleware.py b/pykeg/web/middleware.py index 24dd1b4a6..3bfc19cbd 100644 --- a/pykeg/web/middleware.py +++ b/pykeg/web/middleware.py @@ -1,9 +1,7 @@ import logging -from django.conf import settings from django.db import connection -from django.http import HttpResponse -from django.shortcuts import render +from django.http import JsonResponse from django.utils import timezone from pykeg.core import models @@ -14,28 +12,6 @@ logger = logging.getLogger(__name__) -# Requests are always allowed for these path prefixes. -PRIVACY_EXEMPT_PATHS = ( - "/account/activate", - "/accounts/", - "/admin/", - "/media/", - "/setup/", - "/sso/login", - "/sso/logout", - # Both apis enforce their own privacy rules. - "/api/", -) - -PRIVACY_EXEMPT_PATHS += getattr(settings, "KEGBOT_EXTRA_PRIVACY_EXEMPT_PATHS", ()) - - -def _path_allowed(path, kbsite): - for p in PRIVACY_EXEMPT_PATHS: - if path.startswith(p): - return True - return False - class CurrentRequestMiddleware: """Set/clear the current request.""" @@ -102,16 +78,15 @@ def __call__(self, request): request.need_upgrade = False request.kbsite = None - # Skip all checks if we're in the setup wizard. - if request.path.startswith("/setup"): - # On a fresh install the session table doesn't exist yet, so a real - # session read/write would crash; stub it out until migrations (run - # in the wizard's first step) create the table. Once it exists, keep - # the real session so the wizard can log the new admin in. + # On a fresh install the session table doesn't exist yet, so a + # real session read/write would crash; stub it out for the setup + # api until migrations (run in the first setup step) create the + # table. Once it exists, keep the real session so setup can log + # the new admin in. + if request.path.startswith("/api/setup"): if "django_session" not in connection.introspection.table_names(): request.session = {} request.session["_auth_user_backend"] = None - return self.get_response(request) # First confirm the database is working. try: @@ -146,26 +121,37 @@ def __call__(self, request): return self.get_response(request) def process_view(self, request, view_func, view_args, view_kwargs): + """Gates API requests while setup/upgrade is required. + + Only API paths are intercepted (with a JSON 403 the frontend + understands); everything else falls through to the SPA shell, + which reads the same signal from its boot request and renders + the setup flow. + """ if is_api_v1_request(request): - # API endpoints handle "setup required" differently. + # The legacy API handles "setup required" differently. + return None + + if request.path.startswith("/api/setup"): + # The setup API is how the frontend performs setup/upgrade. + return None + + if not request.path.startswith("/api/"): return None if request.need_setup: - return self._setup_required(request) + return JsonResponse({"error": "setup_required"}, status=403) elif request.need_upgrade: - return self._upgrade_required(request) + return JsonResponse( + { + "error": "upgrade_required", + "installed_version": getattr(request, "installed_version_string", None), + }, + status=403, + ) return None - def _setup_required(self, request): - return render(request, "setup_wizard/setup_required.html", status=403) - - def _upgrade_required(self, request): - context = { - "installed_version": getattr(request, "installed_version_string", None), - } - return render(request, "setup_wizard/upgrade_required.html", context=context, status=403) - class KegbotSiteMiddleware: def __init__(self, get_response): @@ -179,41 +165,3 @@ def __call__(self, request): ) return self.get_response(request) - - -class PrivacyMiddleware: - """Enforces site privacy settings. - - Must be installed after ApiRequestMiddleware (in request order) to - access is_api_v1_request attribute. - """ - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - return self.get_response(request) - - def process_view(self, request, view_func, view_args, view_kwargs): - if not hasattr(request, "kbsite"): - return None - elif _path_allowed(request.path, request.kbsite): - return None - elif request.is_api_v1_request: - # api.middleware will enforce access requirements. - return None - - privacy = request.kbsite.privacy - - if privacy == "public": - return None - elif privacy == "staff": - if not request.user.is_staff: - return render(request, "kegweb/staff_only.html", status=401) - return None - elif privacy == "members": - if not request.user.is_authenticated or not request.user.is_active: - return render(request, "kegweb/members_only.html", status=401) - return None - - return HttpResponse(f"Server misconfigured, unknown privacy setting:{privacy}", status=500) diff --git a/pykeg/web/setup_wizard/__init__.py b/pykeg/web/setup_wizard/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pykeg/web/setup_wizard/forms.py b/pykeg/web/setup_wizard/forms.py deleted file mode 100644 index 6968c2f5c..000000000 --- a/pykeg/web/setup_wizard/forms.py +++ /dev/null @@ -1,93 +0,0 @@ -from crispy_forms.bootstrap import FormActions -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Field, Layout, Submit -from django import forms - -from pykeg.core import models - - -class MiniSiteSettingsForm(forms.ModelForm): - class Meta: - model = models.KegbotSite - fields = ( - "title", - "privacy", - "timezone", - "volume_display_units", - "temperature_display_units", - ) - widgets = { - "privacy": forms.RadioSelect(), - "volume_display_units": forms.RadioSelect(), - "temperature_display_units": forms.RadioSelect(), - } - - helper = FormHelper() - helper.form_class = "setup-form span8 offset2" - helper.layout = Layout( - Field("title", css_class="span12"), - Field("privacy", css_class="span12"), - Field("timezone", css_class="span12"), - Field("volume_display_units", css_class="span12"), - Field("temperature_display_units", css_class="span12"), - FormActions( - Submit("save_changes", "Continue", css_class="btn-primary"), - ), - ) - - -class AdminUserForm(forms.Form): - username = forms.RegexField( - label="Username", - max_length=30, - regex=r"^[\w-]+$", - help_text="Your username: 30 characters or fewer. Alphanumeric characters only (letters, digits, hyphens and underscores).", - error_messages={ - "invalid": "Must contain only letters, numbers, hyphens and underscores.", - "required": "Provide a username.", - }, - ) - - password = forms.CharField(widget=forms.PasswordInput()) - confirm_password = forms.CharField(widget=forms.PasswordInput()) - email = forms.EmailField(help_text="In case you lose your password.") - - helper = FormHelper() - helper.form_class = "setup-form span8 offset2" - helper.layout = Layout( - Field("username", css_class="span12"), - Field("email", css_class="span12"), - Field("password", css_class="span12"), - Field("confirm_password", css_class="span12"), - FormActions( - Submit("save_changes", "Continue", css_class="btn-primary"), - ), - ) - - def clean_confirm_password(self): - orig = self.cleaned_data.get("password") - confirm = self.cleaned_data.get("confirm_password") - if orig and confirm and orig != confirm: - raise forms.ValidationError("Passwords must match!") - return confirm - - def clean_username(self): - username = self.cleaned_data.get("username") - if username: - try: - models.User.objects.get(username=username) - raise forms.ValidationError("Sorry, this username is taken already?!") - except models.User.DoesNotExist: - pass # expected - return username - - def save(self): - u = models.User() - u.username = self.cleaned_data.get("username") - u.email = self.cleaned_data.get("email") - u.is_staff = True - u.is_superuser = True - u.save() - u.set_password(self.cleaned_data.get("password")) - u.save() - return u diff --git a/pykeg/web/setup_wizard/setup_wizard_tests.py b/pykeg/web/setup_wizard/setup_wizard_tests.py deleted file mode 100644 index 84017e8c1..000000000 --- a/pykeg/web/setup_wizard/setup_wizard_tests.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Unittests for setup wizard.""" - -from django.core.cache import cache -from django.test import TransactionTestCase -from django.test.utils import override_settings -from django.urls import reverse - -from pykeg.core import defaults, models - - -class SetupWizardTestCase(TransactionTestCase): - def setUp(self): - cache.clear() - - def test_settings_debug_false(self): - """Verify wizard is not offered (DEBUG is False).""" - for path in ("/", "/stats/"): - response = self.client.get(path) - self.assertContains(response, "<h2>Kegbot Offline</h2>", status_code=403) - self.assertNotContains(response, "Start Setup", status_code=403) - - response = self.client.get("/setup/") - self.assertEqual(response.status_code, 404) - - @override_settings(DEBUG=True) - def test_settings_debug_true(self): - """Verify wizard is offered (DEBUG is True).""" - for path in ("/", "/stats/"): - response = self.client.get(path) - self.assertContains(response, "<h2>Setup Required</h2>", status_code=403) - self.assertContains(response, "Start Setup", status_code=403) - - response = self.client.get("/setup/") - self.assertEqual(response.status_code, 200) - - @override_settings(DEBUG=True) - def test_admin_step_logs_in_new_admin(self): - """The admin-user step creates the admin and logs them in.""" - defaults.set_defaults() # site exists but is not yet set up - - response = self.client.post( - reverse("setup_admin"), - { - "username": "wizadmin", - "email": "admin@example.com", - "password": "s3cret-pass", - "confirm_password": "s3cret-pass", - }, - ) - self.assertRedirects(response, reverse("setup_finish"), fetch_redirect_response=False) - - admin = models.User.objects.get(username="wizadmin") - self.assertTrue(admin.is_superuser) - # The freshly-created admin should now be authenticated. - self.assertEqual(str(admin.pk), self.client.session.get("_auth_user_id")) - - def test_setup_not_shown(self): - """Verify wizard is not shown on set-up site.""" - site = defaults.set_defaults() - site.is_setup = True - site.save() - for path in ("/", "/stats/"): - response = self.client.get(path) - self.assertNotContains(response, "<h2>Kegbot Offline</h2>", status_code=200) - self.assertNotContains(response, "<h2>Setup Required</h2>", status_code=200) - self.assertNotContains(response, "Start Setup", status_code=200) - - response = self.client.get("/setup/") - self.assertEqual(response.status_code, 404) diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/accounts.html b/pykeg/web/setup_wizard/templates/setup_wizard/accounts.html deleted file mode 100644 index d1a00aba6..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/accounts.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Sharing your Kegbot?</h2> -<p class="lead"> -Should we enable user tracking, which allows you to link pours to individual -user accounts? -</p> - -<div class="row-fluid"> - <div class="span8 offset2"> - <form action="" method="POST">{% csrf_token %} - - <p> - <input type="submit" name="enable_users" value="Enable User Tracking »" - class="btn btn-primary btn-success btn-block" id="submit-id-enable_users"> - <span class="muted">Classic mode: Enable tracking.</span> - </p> - - <br/> - - <p> - <input type="submit" name="disable_users" value="Anonymous Mode »" - class="btn btn-primary btn-info btn-block" id="submit-id-disable_users"> - <span class="muted">Tracking features are hidden.</span> - </p> - - </form> - </div> -</div> - -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/admin.html b/pykeg/web/setup_wizard/templates/setup_wizard/admin.html deleted file mode 100644 index 3ecbd215b..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/admin.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Kegbot Setup: Create Admin User</h2> -<p class="lead"> - That's you! -</> -</div> - -<div class="row-fluid"> - {% crispy form %} -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/base.html b/pykeg/web/setup_wizard/templates/setup_wizard/base.html deleted file mode 100644 index e7514da55..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/base.html +++ /dev/null @@ -1,87 +0,0 @@ -{% extends "skel.html" %} - -{% block css %} -{{ block.super }} -<style type="text/css"> - body { - padding-top: 20px; - padding-bottom: 40px; - } - - /* Custom container */ - .container-narrow { - margin: 0 auto; - max-width: 700px; - } - .container-narrow > hr { - margin: 30px 0; - } - - .jumbotron { - margin: 60px 0; - text-align: center; - } - .jumbotron h1 { - font-size: 72px; - line-height: 1; - } - .jumbotron .btn { - font-size: 21px; - padding: 14px 24px; - } - - .marketing { - margin: 60px 0; - } - .marketing p + h4 { - margin-top: 28px; - } -</style> - -{% endblock css %} - -{% block body %} -<div class="container-narrow"> - -<div class="masthead"> - {% comment %} - <ul class="nav nav-pills pull-right"> - <li class="active"><a href="#">Home</a></li> - <li><a href="#">About</a></li> - <li><a href="#">Contact</a></li> - </ul> - <h3 class="muted">Kegbot</h3> - {% endcomment %} -</div> - -<hr> - -{% block messages %} -{% for message in messages %} - <div class="alert alert-{{message.tags}}"> - <a class="close" data-dismiss="alert" href="#">×</a> - <p> - {{message}} - </p> - </div> -{% endfor %} -{% endblock messages %} - -{% block content %} -{% endblock %} - -{% if error_stack %} -<div class="container"> - <div class="row-fluid"> - <div class="span10"> - <hr/> - <h3>Error logs</h3> - {% if error_message %}<h5>{{ error_message }}{% endif %} - <pre>{{ error_stack }}</pre> - </div> - </div> -</div> -{% endif %} - -</div> <!-- /container-narrow --> -{% endblock body %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/finish.html b/pykeg/web/setup_wizard/templates/setup_wizard/finish.html deleted file mode 100644 index 7cc377e19..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/finish.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Kegbot Setup: Woot!</h2> -<p class="lead"> - You're done! Press <em>Finish</em> below to finish setup and activate the - your brand spankin' new Kegbot site. -</p> - -<p> - Now would also be a good time to set <code>KEGBOT_ENV=production</code> in your - settings. -</p> - -<p/> - -<form action="" method="POST"> - {% csrf_token %} - <input type="submit" class="btn btn-large btn-success" value="Finish!"/> -</form> - - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/mode.html b/pykeg/web/setup_wizard/templates/setup_wizard/mode.html deleted file mode 100644 index 154ad0309..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/mode.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Kegbot Setup: Pick Mode</h2> -<p class="lead"> - Before you continue, what kind of Kegbot would you like?<br/> - (You can always change settings later.) -</p> - -<div class="row-fluid"> - <div class="span8 offset2"> - <form action="" method="POST">{% csrf_token %} - - <p> - <input type="submit" name="enable_sensing" value="Enable Sensors, Continue »" - class="btn btn-primary btn-success btn-block" id="submit-id-enable_sensing"> - <span class="muted">Classic mode: Enables features that require flow sensing hardware.</span> - </p> - - <br/> - - <p> - <input type="submit" name="disable_sensing" value="No Sensor Hardware, Continue »" - class="btn btn-primary btn-info btn-block" id="submit-id-disable_sensing"> - <span class="muted">Hides features that require flow sensing hardware.</span> - </p> - - </form> - </div> -</div> - -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/setup_required.html b/pykeg/web/setup_wizard/templates/setup_wizard/setup_required.html deleted file mode 100644 index 607658802..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/setup_required.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "setup_wizard/base.html" %} - -{% block content %} -<div class="jumbotron"> - -{% if DEBUG %} -<h2>Setup Required</h2> -<p class="lead"> - This Kegbot is not set up. Click to enter the setup wizard. -</p> -<a class="btn btn-large btn-success" href="{% url "setup_wizard_start" %}"> - Start Setup -</a> -{% else %} -<h2>Kegbot Offline</h2> -<p class="lead"> - This Kegbot is not set up. -</p> -<p class="muted"> - Hint: Set <code>DEBUG = True</code> in local settings and try again. -</p> -{% endif %} - -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/site_settings.html b/pykeg/web/setup_wizard/templates/setup_wizard/site_settings.html deleted file mode 100644 index aa51d6de7..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/site_settings.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Kegbot Setup: General Settings</h2> -<p class="lead"> - Adjust the default site settings below. -</p> -<p class="muted"> - Don't worry, you can change all settings later, too. -</p> -</div> - -<div class="row-fluid"> - <div class="span12"> - {% crispy form %} - </div> -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/start.html b/pykeg/web/setup_wizard/templates/setup_wizard/start.html deleted file mode 100644 index cca0f4677..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/start.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Kegbot Setup: Welcome</h2> -<p class="lead"> - {% if need_upgrade %} - Before you continue, please click below to upgrade your database. - {% elif need_install %} - Before you continue, please click below to perform database setup. - {% else %} - Please click below to continue with setup. - {% endif %} -</p> - -<div class="row-fluid"> - <div class="span8 offset2"> - <form action="" method="POST">{% csrf_token %} - - <p> - <input type="submit" name="db" - value="{% if need_install %}Install Database{% elif need_upgrade %}Upgrade Database{% else %}Continue{% endif %}" - class="btn btn-primary btn-success btn-block" id="submit-id-db"> - </p> - - </form> - </div> -</div> -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/upgrade.html b/pykeg/web/setup_wizard/templates/setup_wizard/upgrade.html deleted file mode 100644 index ec384b0e7..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/upgrade.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "setup_wizard/base.html" %} -{% load crispy_forms_tags %} - -{% block content %} -<div class="jumbotron"> - -<h2>Upgrade Kegbot</h2> -<p class="lead"> - {% if message %} - {{ message }} - {% else %} - Please click below to upgrade your Kegbot. - {% endif %} -</p> - -<div class="row-fluid"> - <div class="span8 offset2"> - <form action="" method="POST">{% csrf_token %} - <p> - <input type="submit" name="update_db" value="Upgrade" - class="btn btn-primary btn-success btn-block" id="submit-id-update_db"> - </p> - </form> - </div> -</div> - -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/templates/setup_wizard/upgrade_required.html b/pykeg/web/setup_wizard/templates/setup_wizard/upgrade_required.html deleted file mode 100644 index 3fefebccb..000000000 --- a/pykeg/web/setup_wizard/templates/setup_wizard/upgrade_required.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "setup_wizard/base.html" %} - -{% block content %} -<div class="jumbotron"> - -<h2>Upgrade Required</h2> -<p class="lead"> - This Kegbot needs to be upgraded. -</p> -<p class="muted"> - Hint: Run <code>kegbot upgrade</code> (see <a - href="https://docs.kegbot.org/projects/kegbot-server/en/latest/upgrade.html">docs</a>). -</p> - -<p class="muted"> -Kegbot version {{ VERSION }} -{% if installed_version %}(installed version: {{ installed_version }}){% endif %} -</p> -</div> - -{% endblock content %} diff --git a/pykeg/web/setup_wizard/urls.py b/pykeg/web/setup_wizard/urls.py deleted file mode 100644 index 03c3d4889..000000000 --- a/pykeg/web/setup_wizard/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.urls import path - -from pykeg.web.setup_wizard import views - -urlpatterns = [ - path("", views.start, name="setup_wizard_start"), - path("upgrade/", views.upgrade, name="setup_upgrade"), - path("mode/", views.mode, name="setup_mode"), - path("setup-accounts/", views.setup_accounts, name="setup_accounts"), - path("settings/", views.site_settings, name="setup_site_settings"), - path("admin-user/", views.admin, name="setup_admin"), - path("finished/", views.finish, name="setup_finish"), -] diff --git a/pykeg/web/setup_wizard/views.py b/pykeg/web/setup_wizard/views.py deleted file mode 100644 index b39fdb4b7..000000000 --- a/pykeg/web/setup_wizard/views.py +++ /dev/null @@ -1,195 +0,0 @@ -import logging -import traceback -from functools import wraps - -from django.conf import settings -from django.contrib import messages -from django.contrib.auth import authenticate, login -from django.core import management -from django.http import Http404 -from django.shortcuts import redirect, render -from django.views.decorators.cache import never_cache - -from pykeg.core import defaults, models -from pykeg.core.util import get_version_object -from pykeg.util import dbstatus - -from .forms import AdminUserForm, MiniSiteSettingsForm - -logger = logging.getLogger(__name__) - - -def setup_view(f): - """Decorator for setup views.""" - - def new_function(*args, **kwargs): - request = args[0] - if not settings.DEBUG: - raise Http404("Site is not in DEBUG mode.") - if request.kbsite and request.kbsite.is_setup: - raise Http404("Site is already setup, wizard disabled.") - return f(*args, **kwargs) - - return wraps(f)(new_function) - - -@setup_view -@never_cache -def start(request): - """Shows database setup button""" - context = {} - - if request.method == "POST": - try: - management.call_command("migrate", no_input=True) - return redirect("setup_mode") - except Exception as e: - logger.exception("Error installing database") - context["error_message"] = str(e) - context["error_stack"] = traceback.format_exc() - else: - try: - logger.info("Checking database status ...") - dbstatus.check_db_status() - logger.info("Database status OK.") - except dbstatus.DatabaseNotInitialized: - context["need_install"] = True - except dbstatus.NeedMigration: - context["need_upgrade"] = True - - return render(request, "setup_wizard/start.html", context=context) - - -@setup_view -@never_cache -def mode(request): - """Shows the enable/disable hardware toggle.""" - context = {} - - if request.method == "POST": - if "enable_sensing" in request.POST: - response = redirect("setup_accounts") - response.set_cookie("kb_setup_enable_sensing", "True") - return response - elif "disable_sensing" in request.POST: - response = redirect("setup_site_settings") - response.set_cookie("kb_setup_enable_sensing", "False") - response.set_cookie("kb_setup_enable_users", "False") - return response - else: - messages.error(request, "Unknown response.") - - return render(request, "setup_wizard/mode.html", context=context) - - -@setup_view -@never_cache -def upgrade(request): - context = {} - if request.method == "POST": - try: - management.call_command("migrate", no_input=True) - site = models.KegbotSite.get() - app_version = get_version_object() - site.server_version = str(app_version) - site.save() - return redirect("kb-home") - except Exception as e: - logger.exception("Error installing database") - context["error_message"] = str(e) - context["error_stack"] = traceback.format_exc() - - try: - logger.info("Checking database status ...") - dbstatus.check_db_status() - logger.info("Database status OK.") - except dbstatus.DatabaseNotInitialized: - context["message"] = "Database not initialized" - except dbstatus.NeedMigration: - context["message"] = "Database upgrade needed" - - return render(request, "setup_wizard/upgrade.html", context=context) - - -@setup_view -@never_cache -def setup_accounts(request): - """Shows the enable/disable accounts toggle.""" - context = {} - - if request.method == "POST": - if "enable_users" in request.POST: - response = redirect("setup_site_settings") - response.set_cookie("kb_setup_enable_users", "True") - return response - elif "disable_users" in request.POST: - response = redirect("setup_site_settings") - response.set_cookie("kb_setup_enable_users", "False") - return response - else: - messages.error(request, "Unknown response.") - - return render(request, "setup_wizard/accounts.html", context=context) - - -@setup_view -@never_cache -def site_settings(request): - context = {} - - if request.method == "POST": - site = models.KegbotSite.get() - form = MiniSiteSettingsForm(request.POST, instance=site) - if form.is_valid(): - form.save() - messages.success(request, "Settings saved!") - return redirect("setup_admin") - else: - try: - defaults.set_defaults() - except defaults.AlreadyInstalledError: - pass - - site = models.KegbotSite.get() - site.enable_sensing = request.COOKIES.get("kb_setup_enable_sensing") == "True" - site.enable_users = request.COOKIES.get("kb_setup_enable_users") == "True" - site.save() - form = MiniSiteSettingsForm(instance=site) - context["form"] = form - return render(request, "setup_wizard/site_settings.html", context=context) - - -@setup_view -@never_cache -def admin(request): - context = {} - form = AdminUserForm() - if request.method == "POST": - form = AdminUserForm(request.POST) - if form.is_valid(): - form.save() - # Log the freshly-created admin in so setup continues as them. By - # this step the session table exists (migrated in the first step), - # so IsSetupMiddleware leaves the real session in place. - user = authenticate( - username=form.cleaned_data.get("username"), - password=form.cleaned_data.get("password"), - ) - if user is not None: - login(request, user) - return redirect("setup_finish") - context["form"] = form - return render(request, "setup_wizard/admin.html", context=context) - - -@setup_view -@never_cache -def finish(request): - context = {} - if request.method == "POST": - site = models.KegbotSite.get() - site.is_setup = True - site.save() - messages.success(request, "Tip: Install a new Keg in Admin: Taps") - return redirect("kb-home") - return render(request, "setup_wizard/finish.html", context=context) diff --git a/pykeg/web/spa.py b/pykeg/web/spa.py new file mode 100644 index 000000000..bcff9859e --- /dev/null +++ b/pykeg/web/spa.py @@ -0,0 +1,55 @@ +"""Serves the frontend single-page app shell.""" + +import functools +import json +import os + +from django.conf import settings +from django.http import HttpResponse +from django.shortcuts import render +from django.views.decorators.csrf import ensure_csrf_cookie + +MANIFEST_PATH = os.path.join(settings.BASE_DIR, "web-ui", "dist", ".vite", "manifest.json") + + +def _read_manifest(): + with open(MANIFEST_PATH) as f: + return json.load(f) + + +@functools.cache +def _cached_manifest(): + return _read_manifest() + + +@ensure_csrf_cookie +def spa_index(request, **kwargs): + """Renders the SPA shell for any non-API route. + + Captured URL kwargs (from the named routes that preserve legacy URL + names for e-mail links and get_absolute_url) are ignored: routing + happens client-side. + + Asset names come from vite's build manifest and are emitted through + {% static %}, so hashed-manifest storage resolves them correctly in + production. Also sets the CSRF cookie so the app can make + authenticated POSTs from its very first render. + """ + try: + # In DEBUG, re-read every request so a fresh `bun run build` is + # picked up without a server restart. + manifest = _read_manifest() if settings.DEBUG else _cached_manifest() + except OSError: + return HttpResponse( + "<h1>Frontend not built</h1>" + "<p>Run <code>bun install && bun run build</code>, or use the vite dev " + "server (<code>bun run dev</code>) during development.</p>", + status=503, + ) + + entry = manifest["index.html"] + context = { + "entry_js": entry["file"], + "entry_css": entry.get("css", []), + } + return render(request, "spa/index.html", context=context) diff --git a/pykeg/web/static/angular/angular-resource.min.js b/pykeg/web/static/angular/angular-resource.min.js deleted file mode 100644 index f37559c7a..000000000 --- a/pykeg/web/static/angular/angular-resource.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - AngularJS v1.0.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(C,d,w){'use strict';d.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function s(b,e){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(e?null:/%20/g,"+")}function t(b,e){this.template=b+="#";this.defaults=e||{};var a=this.urlParams={};h(b.split(/\W/),function(f){f&&RegExp("(^|[^\\\\]):"+f+"\\W").test(b)&&(a[f]=!0)});this.template=b.replace(/\\:/g,":")}function u(b,e,a){function f(m,a){var b= -{},a=o({},e,a);h(a,function(a,z){var c;a.charAt&&a.charAt(0)=="@"?(c=a.substr(1),c=y(c)(m)):c=a;b[z]=c});return b}function g(a){v(a||{},this)}var k=new t(b),a=o({},A,a);h(a,function(a,b){a.method=d.uppercase(a.method);var e=a.method=="POST"||a.method=="PUT"||a.method=="PATCH";g[b]=function(b,c,d,B){var j={},i,l=p,q=null;switch(arguments.length){case 4:q=B,l=d;case 3:case 2:if(r(c)){if(r(b)){l=b;q=c;break}l=c;q=d}else{j=b;i=c;l=d;break}case 1:r(b)?l=b:e?i=b:j=b;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+ -arguments.length+" arguments.";}var n=this instanceof g?this:a.isArray?[]:new g(i);x({method:a.method,url:k.url(o({},f(i,a.params||{}),j)),data:i}).then(function(b){var c=b.data;if(c)a.isArray?(n.length=0,h(c,function(a){n.push(new g(a))})):v(c,n);(l||p)(n,b.headers)},q);return n};g.prototype["$"+b]=function(a,d,h){var m=f(this),j=p,i;switch(arguments.length){case 3:m=a;j=d;i=h;break;case 2:case 1:r(a)?(j=a,i=d):(m=a,j=d||p);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ -arguments.length+" arguments.";}g[b].call(this,m,e?this:w,j,i)}});g.bind=function(d){return u(b,o({},e,d),a)};return g}var A={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},p=d.noop,h=d.forEach,o=d.extend,v=d.copy,r=d.isFunction;t.prototype={url:function(b){var e=this,a=this.template,f,g,b=b||{};h(this.urlParams,function(h,c){f=b.hasOwnProperty(c)?b[c]:e.defaults[c];d.isDefined(f)&&f!==null?(g=s(f,!0).replace(/%26/gi,"&").replace(/%3D/gi, -"=").replace(/%2B/gi,"+"),a=a.replace(RegExp(":"+c+"(\\W)","g"),g+"$1")):a=a.replace(RegExp("(/?):"+c+"(\\W)","g"),function(a,b,c){return c.charAt(0)=="/"?c:b+c})});var a=a.replace(/\/?#$/,""),k=[];h(b,function(a,b){e.urlParams[b]||k.push(s(b)+"="+s(a))});k.sort();a=a.replace(/\/*$/,"");return a+(k.length?"?"+k.join("&"):"")}};return u}])})(window,window.angular); diff --git a/pykeg/web/static/angular/angular-sanitize.min.js b/pykeg/web/static/angular/angular-sanitize.min.js deleted file mode 100644 index 212a90a9d..000000000 --- a/pykeg/web/static/angular/angular-sanitize.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - AngularJS v1.0.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(I,g){'use strict';function i(a){var d={},a=a.split(","),b;for(b=0;b<a.length;b++)d[a[b]]=!0;return d}function z(a,d){function b(a,b,c,h){b=g.lowercase(b);if(m[b])for(;f.last()&&n[f.last()];)e("",f.last());o[b]&&f.last()==b&&e("",b);(h=p[b]||!!h)||f.push(b);var j={};c.replace(A,function(a,b,d,e,c){j[b]=k(d||e||c||"")});d.start&&d.start(b,j,h)}function e(a,b){var e=0,c;if(b=g.lowercase(b))for(e=f.length-1;e>=0;e--)if(f[e]==b)break;if(e>=0){for(c=f.length-1;c>=e;c--)d.end&&d.end(f[c]);f.length= -e}}var c,h,f=[],j=a;for(f.last=function(){return f[f.length-1]};a;){h=!0;if(!f.last()||!q[f.last()]){if(a.indexOf("<\!--")===0)c=a.indexOf("--\>"),c>=0&&(d.comment&&d.comment(a.substring(4,c)),a=a.substring(c+3),h=!1);else if(B.test(a)){if(c=a.match(r))a=a.substring(c[0].length),c[0].replace(r,e),h=!1}else if(C.test(a)&&(c=a.match(s)))a=a.substring(c[0].length),c[0].replace(s,b),h=!1;h&&(c=a.indexOf("<"),h=c<0?a:a.substring(0,c),a=c<0?"":a.substring(c),d.chars&&d.chars(k(h)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+ -f.last()+"[^>]*>","i"),function(b,a){a=a.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(a));return""}),e("",f.last());if(a==j)throw"Parse Error: "+a;j=a}e()}function k(a){l.innerHTML=a.replace(/</g,"<");return l.innerText||l.textContent||""}function t(a){return a.replace(/&/g,"&").replace(F,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function u(a){var d=!1,b=g.bind(a,a.push);return{start:function(a,c,h){a=g.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]== -!0&&(b("<"),b(a),g.forEach(c,function(a,c){var e=g.lowercase(c);if(G[e]==!0&&(w[e]!==!0||a.match(H)))b(" "),b(c),b('="'),b(t(a)),b('"')}),b(h?"/>":">"))},end:function(a){a=g.lowercase(a);!d&&v[a]==!0&&(b("</"),b(a),b(">"));a==d&&(d=!1)},chars:function(a){d||b(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^</,B=/^<\s*\//,D=/<\!--(.*?)--\>/g, -E=/<!\[CDATA\[(.*?)]]\>/g,H=/^((ftp|https?):\/\/|mailto:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=g.extend({},y,x),m=g.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=g.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), -q=i("script,style"),v=g.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=g.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[]; -z(a,u(d));return d.join("")});g.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,b,e){b.addClass("ng-binding").data("$binding",e.ngBindHtml);d.$watch(e.ngBindHtml,function(c){c=a(c);b.html(c||"")})}}]);g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(b){if(!b)return b;for(var e=b,c=[],h=u(c),f,g;b=e.match(a);)f=b[0],b[2]==b[3]&&(f="mailto:"+f),g=b.index, -h.chars(e.substr(0,g)),h.start("a",{href:f}),h.chars(b[0].replace(d,"")),h.end("a"),e=e.substring(g+b[0].length);h.chars(e);return c.join("")}})})(window,window.angular); diff --git a/pykeg/web/static/angular/angular.min.js b/pykeg/web/static/angular/angular.min.js deleted file mode 100644 index 07ea01c58..000000000 --- a/pykeg/web/static/angular/angular.min.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - AngularJS v1.0.5 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(X,Y,q){'use strict';function n(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==n)b.forEach(a,c);else if(!b||typeof b.length!=="number"?0:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"||b instanceof L||ca&&b instanceof ca||xa.call(b)!=="[object Object]"||typeof b.callee==="function")for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d], -d);return b}function mb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function fc(b,a,c){for(var d=mb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function nb(b){return function(a,c){b(c,a)}}function ya(){for(var b=aa.length,a;b;){b--;a=aa[b].charCodeAt(0);if(a==57)return aa[b]="A",aa.join("");if(a==90)aa[b]="0";else return aa[b]=String.fromCharCode(a+1),aa.join("")}aa.unshift("0");return aa.join("")}function v(b){n(arguments,function(a){a!==b&&n(a,function(a,d){b[d]= -a})});return b}function E(b){return parseInt(b,10)}function za(b,a){return v(new (v(function(){},{prototype:b})),a)}function C(){}function na(b){return b}function I(b){return function(){return b}}function w(b){return typeof b=="undefined"}function x(b){return typeof b!="undefined"}function M(b){return b!=null&&typeof b=="object"}function A(b){return typeof b=="string"}function Ra(b){return typeof b=="number"}function oa(b){return xa.apply(b)=="[object Date]"}function B(b){return xa.apply(b)=="[object Array]"} -function H(b){return typeof b=="function"}function pa(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function O(b){return A(b)?b.replace(/^\s*/,"").replace(/\s*$/,""):b}function gc(b){return b&&(b.nodeName||b.bind&&b.find)}function Sa(b,a,c){var d=[];n(b,function(b,g,h){d.push(a.call(c,b,g,h))});return d}function Aa(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ta(b,a){var c=Aa(b,a);c>=0&&b.splice(c,1);return a}function U(b,a){if(pa(b)|| -b&&b.$evalAsync&&b.$watch)throw Error("Can't copy Window or Scope");if(a){if(b===a)throw Error("Can't copy equivalent objects or arrays");if(B(b))for(var c=a.length=0;c<b.length;c++)a.push(U(b[c]));else for(c in n(a,function(b,c){delete a[c]}),b)a[c]=U(b[c])}else(a=b)&&(B(b)?a=U(b,[]):oa(b)?a=new Date(b.getTime()):M(b)&&(a=U(b,{})));return a}function hc(b,a){var a=a||{},c;for(c in b)b.hasOwnProperty(c)&&c.substr(0,2)!=="$$"&&(a[c]=b[c]);return a}function ga(b,a){if(b===a)return!0;if(b===null||a=== -null)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&c=="object")if(B(b)){if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ga(b[d],a[d]))return!1;return!0}}else if(oa(b))return oa(a)&&b.getTime()==a.getTime();else{if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||pa(b)||pa(a))return!1;c={};for(d in b)if(!(d.charAt(0)==="$"||H(b[d]))){if(!ga(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c[d]&&d.charAt(0)!=="$"&&a[d]!==q&&!H(a[d]))return!1;return!0}return!1}function Ua(b,a){var c= -arguments.length>2?ha.call(arguments,2):[];return H(a)&&!(a instanceof RegExp)?c.length?function(){return arguments.length?a.apply(b,c.concat(ha.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function ic(b,a){var c=a;/^\$+/.test(b)?c=q:pa(a)?c="$WINDOW":a&&Y===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b,ic,a?" ":null)}function ob(b){return A(b)?JSON.parse(b):b}function Va(b){b&&b.length!== -0?(b=y(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b}function qa(b){b=u(b).clone();try{b.html("")}catch(a){}var c=u("<div>").append(b).html();try{return b[0].nodeType===3?y(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+y(b)})}catch(d){return y(c)}}function Wa(b){var a={},c,d;n((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=x(c[1])?decodeURIComponent(c[1]):!0)});return a}function pb(b){var a=[];n(b,function(b, -d){a.push(Xa(d,!0)+(b===!0?"":"="+Xa(b,!0)))});return a.length?a.join("&"):""}function Ya(b){return Xa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Xa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;n(h,function(a){h[a]=!0;c(Y.getElementById(a)); -a=a.replace(":","\\:");b.querySelectorAll&&(n(b.querySelectorAll("."+a),c),n(b.querySelectorAll("."+a+"\\:"),c),n(b.querySelectorAll("["+a+"]"),c))});n(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):n(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function qb(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=rb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector", -function(a,b,c,h){a.$apply(function(){b.data("$injector",h);c(b)(a)})}]);return c}function Za(b,a){a=a||"_";return b.replace(kc,function(b,d){return(d?a:"")+b.toLowerCase()})}function $a(b,a,c){if(!b)throw Error("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function ra(b,a,c){c&&B(b)&&(b=b[b.length-1]);$a(H(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function lc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object), -"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw Error("No module: "+d);var b=[],c=[],i=a("$injector","invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider", -"register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:i,run:function(a){c.push(a);return this}};g&&i(g);return k})}})}function sb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,h,f,j,i,k,m;b.length;){h=b.shift();f=0;for(j=h.length;f<j;f++){i=u(h[f]);c?i.triggerHandler("$destroy"):c=!c;k=0;for(e=(m=i.children()).length,i=e;k<i;k++)b.push(ca(m[k]))}}return d.apply(this, -arguments)}var d=ca.fn[b],d=d.$original||d;c.$original=d;ca.fn[b]=c}function L(b){if(b instanceof L)return b;if(!(this instanceof L)){if(A(b)&&b.charAt(0)!="<")throw Error("selectors not implemented");return new L(b)}if(A(b)){var a=Y.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function sa(b){tb(b);for(var a=0,b=b.childNodes||[];a<b.length;a++)sa(b[a])}function ub(b,a,c){var d= -ba(b,"events");ba(b,"handle")&&(w(a)?n(d,function(a,c){db(b,c,a);delete d[c]}):w(c)?(db(b,a,d[a]),delete d[a]):Ta(d[a],c))}function tb(b){var a=b[Ba],c=Ca[a];c&&(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),ub(b)),delete Ca[a],b[Ba]=q)}function ba(b,a,c){var d=b[Ba],d=Ca[d||-1];if(x(c))d||(b[Ba]=d=++oc,d=Ca[d]={}),d[a]=c;else return d&&d[a]}function vb(b,a,c){var d=ba(b,"data"),e=x(c),g=!e&&x(a),h=g&&!M(a);!d&&!h&&ba(b,"data",d={});if(e)d[a]=c;else if(g)if(h)return d&&d[a];else v(d,a);else return d} -function Da(b,a){return(" "+b.className+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" ")>-1}function wb(b,a){a&&n(a.split(" "),function(a){b.className=O((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+O(a)+" "," "))})}function xb(b,a){a&&n(a.split(" "),function(a){if(!Da(b,a))b.className=O(b.className+" "+O(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&x(a.length)&&!pa(a)?a:[a],c=0;c<a.length;c++)b.push(a[c])}function yb(b,a){return Ea(b,"$"+(a||"ngController")+"Controller")}function Ea(b, -a,c){b=u(b);for(b[0].nodeType==9&&(b=b.find("html"));b.length;){if(c=b.data(a))return c;b=b.parent()}}function zb(b,a){var c=Fa[a.toLowerCase()];return c&&Ab[b.nodeName]&&c}function pc(b,a){var c=function(c,e){if(!c.preventDefault)c.preventDefault=function(){c.returnValue=!1};if(!c.stopPropagation)c.stopPropagation=function(){c.cancelBubble=!0};if(!c.target)c.target=c.srcElement||Y;if(w(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented= -!1}c.isDefaultPrevented=function(){return c.defaultPrevented};n(a[e||c.type],function(a){a.call(b,c)});Z<=8?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function fa(b){var a=typeof b,c;if(a=="object"&&b!==null)if(typeof(c=b.$$hashKey)=="function")c=b.$$hashKey();else{if(c===q)c=b.$$hashKey=ya()}else c=b;return a+":"+c}function Ga(b){n(b,this.put,this)}function eb(){}function Bb(b){var a, -c;if(typeof b=="function"){if(!(a=b.$inject))a=[],c=b.toString().replace(qc,""),c=c.match(rc),n(c[1].split(sc),function(b){b.replace(tc,function(b,c,d){a.push(d)})}),b.$inject=a}else B(b)?(c=b.length-1,ra(b[c],"fn"),a=b.slice(0,c)):ra(b,"fn",!0);return a}function rb(b){function a(a){return function(b,c){if(M(b))n(b,nb(a));else return a(b,c)}}function c(a,b){if(H(b)||B(b))b=m.instantiate(b);if(!b.$get)throw Error("Provider "+a+" must define $get factory method.");return k[a+f]=b}function d(a,b){return c(a, -{$get:b})}function e(a){var b=[];n(a,function(a){if(!i.get(a))if(i.put(a,!0),A(a)){var c=ta(a);b=b.concat(e(c.requires)).concat(c._runBlocks);try{for(var d=c._invokeQueue,c=0,f=d.length;c<f;c++){var g=d[c],h=g[0]=="$injector"?m:m.get(g[0]);h[g[1]].apply(h,g[2])}}catch(j){throw j.message&&(j.message+=" from "+a),j;}}else if(H(a))try{b.push(m.invoke(a))}catch(o){throw o.message&&(o.message+=" from "+a),o;}else if(B(a))try{b.push(m.invoke(a))}catch(k){throw k.message&&(k.message+=" from "+String(a[a.length- -1])),k;}else ra(a,"module")});return b}function g(a,b){function c(d){if(typeof d!=="string")throw Error("Service name expected");if(a.hasOwnProperty(d)){if(a[d]===h)throw Error("Circular dependency: "+j.join(" <- "));return a[d]}else try{return j.unshift(d),a[d]=h,a[d]=b(d)}finally{j.shift()}}function d(a,b,e){var f=[],i=Bb(a),g,h,j;h=0;for(g=i.length;h<g;h++)j=i[h],f.push(e&&e.hasOwnProperty(j)?e[j]:c(j));a.$inject||(a=a[g]);switch(b?-1:f.length){case 0:return a();case 1:return a(f[0]);case 2:return a(f[0], -f[1]);case 3:return a(f[0],f[1],f[2]);case 4:return a(f[0],f[1],f[2],f[3]);case 5:return a(f[0],f[1],f[2],f[3],f[4]);case 6:return a(f[0],f[1],f[2],f[3],f[4],f[5]);case 7:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);case 8:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7]);case 9:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]);case 10:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9]);default:return a.apply(b,f)}}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype= -(B(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return M(e)?e:c},get:c,annotate:Bb}}var h={},f="Provider",j=[],i=new Ga,k={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,I(b))}),constant:a(function(a,b){k[a]=b;l[a]=b}),decorator:function(a,b){var c=m.get(a+f),d=c.$get;c.$get=function(){var a=t.invoke(d,c);return t.invoke(b,null,{$delegate:a})}}}},m=g(k,function(){throw Error("Unknown provider: "+ -j.join(" <- "));}),l={},t=l.$injector=g(l,function(a){a=m.get(a+f);return t.invoke(a.$get,a)});n(e(b),function(a){t.invoke(a||C)});return t}function uc(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;n(a,function(a){!b&&y(a.nodeName)==="a"&&(b=a)});return b}function g(){var b=c.hash(),d;b?(d=h.getElementById(b))?d.scrollIntoView():(d=e(h.getElementsByName(b)))?d.scrollIntoView():b==="top"&&a.scrollTo(0,0): -a.scrollTo(0,0)}var h=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(g)});return g}]}function vc(b,a,c,d){function e(a){try{a.apply(null,ha.call(arguments,1))}finally{if(o--,o===0)for(;p.length;)try{p.pop()()}catch(b){c.error(b)}}}function g(a,b){(function R(){n(s,function(a){a()});J=b(R,a)})()}function h(){F!=f.url()&&(F=f.url(),n(V,function(a){a(f.url())}))}var f=this,j=a[0],i=b.location,k=b.history,m=b.setTimeout,l=b.clearTimeout,t={};f.isMock=!1;var o=0,p=[];f.$$completeOutstandingRequest= -e;f.$$incOutstandingRequestCount=function(){o++};f.notifyWhenNoOutstandingRequests=function(a){n(s,function(a){a()});o===0?a():p.push(a)};var s=[],J;f.addPollFn=function(a){w(J)&&g(100,m);s.push(a);return a};var F=i.href,z=a.find("base");f.url=function(a,b){if(a){if(F!=a)return F=a,d.history?b?k.replaceState(null,"",a):(k.pushState(null,"",a),z.attr("href",z.attr("href"))):b?i.replace(a):i.href=a,f}else return i.href.replace(/%27/g,"'")};var V=[],K=!1;f.onUrlChange=function(a){K||(d.history&&u(b).bind("popstate", -h),d.hashchange?u(b).bind("hashchange",h):f.addPollFn(h),K=!0);V.push(a);return a};f.baseHref=function(){var a=z.attr("href");return a?a.replace(/^https?\:\/\/[^\/]*/,""):""};var r={},$="",P=f.baseHref();f.cookies=function(a,b){var d,e,f,i;if(a)if(b===q)j.cookie=escape(a)+"=;path="+P+";expires=Thu, 01 Jan 1970 00:00:00 GMT";else{if(A(b))d=(j.cookie=escape(a)+"="+escape(b)+";path="+P).length+1,d>4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!")}else{if(j.cookie!== -$){$=j.cookie;d=$.split("; ");r={};for(f=0;f<d.length;f++)e=d[f],i=e.indexOf("="),i>0&&(r[unescape(e.substring(0,i))]=unescape(e.substring(i+1)))}return r}};f.defer=function(a,b){var c;o++;c=m(function(){delete t[c];e(a)},b||0);t[c]=!0;return c};f.defer.cancel=function(a){return t[a]?(delete t[a],l(a),e(C),!0):!1}}function wc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new vc(b,d,a,c)}]}function xc(){this.$get=function(){function b(b,d){function e(a){if(a!=m){if(l){if(l== -a)l=a.n}else l=a;g(a.n,a.p);g(a,m);m=a;m.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw Error("cacheId "+b+" taken");var h=0,f=v({},d,{id:b}),j={},i=d&&d.capacity||Number.MAX_VALUE,k={},m=null,l=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);w(b)||(a in j||h++,j[a]=b,h>i&&this.remove(l.key))},get:function(a){var b=k[a];if(b)return e(b),j[a]},remove:function(a){var b=k[a];if(b){if(b==m)m=b.p;if(b==l)l=b.n;g(b.n,b.p);delete k[a];delete j[a];h--}},removeAll:function(){j= -{};h=0;k={};m=l=null},destroy:function(){k=f=j=null;delete a[b]},info:function(){return v({},f,{size:h})}}}var a={};b.info=function(){var b={};n(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Cb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ",h=/^\s*(https?|ftp|mailto):/; -this.directive=function j(d,e){A(d)?($a(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];n(a[d],function(a){try{var g=b.invoke(a);if(H(g))g={compile:I(g)};else if(!g.compile&&g.link)g.compile=I(g.link);g.priority=g.priority||0;g.name=g.name||d;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){c(h)}});return e}])),a[d].push(e)):n(d,nb(j));return this};this.urlSanitizationWhitelist=function(a){return x(a)? -(h=a,this):h};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document",function(b,i,k,m,l,t,o,p,s){function J(a,b,c){a instanceof u||(a=u(a));n(a,function(b,c){b.nodeType==3&&b.nodeValue.match(/\S+/)&&(a[c]=u(b).wrap("<span></span>").parent()[0])});var d=z(a,b,a,c);return function(b,c){$a(b,"scope");for(var e=c?va.clone.call(a):a,g=0,i=e.length;g<i;g++){var h=e[g];(h.nodeType==1||h.nodeType==9)&&e.eq(g).data("$scope",b)}F(e, -"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function F(a,b){try{a.addClass(b)}catch(c){}}function z(a,b,c,d){function e(a,c,d,i){var h,j,k,o,l,m,t,s=[];l=0;for(m=c.length;l<m;l++)s.push(c[l]);t=l=0;for(m=g.length;l<m;t++)j=s[t],c=g[l++],h=g[l++],c?(c.scope?(k=a.$new(M(c.scope)),u(j).data("$scope",k)):k=a,(o=c.transclude)||!i&&b?c(h,k,j,d,function(b){return function(c){var d=a.$new();d.$$transcluded=!0;return b(d,c).bind("$destroy",Ua(d,d.$destroy))}}(o||b)):c(h,k,j,q,i)):h&&h(a,j.childNodes,q,i)} -for(var g=[],i,h,j,k=0;k<a.length;k++)h=new ia,i=V(a[k],[],h,d),h=(i=i.length?K(i,a[k],h,b,c):null)&&i.terminal||!a[k].childNodes.length?null:z(a[k].childNodes,i?i.transclude:b),g.push(i),g.push(h),j=j||i||h;return j?e:null}function V(a,b,c,i){var g=c.$attr,h;switch(a.nodeType){case 1:r(b,ea(fb(a).toLowerCase()),"E",i);var j,k,l;h=a.attributes;for(var o=0,m=h&&h.length;o<m;o++)if(j=h[o],j.specified)k=j.name,l=ea(k.toLowerCase()),g[l]=k,c[l]=j=O(Z&&k=="href"?decodeURIComponent(a.getAttribute(k,2)): -j.value),zb(a,l)&&(c[l]=!0),R(a,b,j,l),r(b,l,"A",i);a=a.className;if(A(a)&&a!=="")for(;h=e.exec(a);)l=ea(h[2]),r(b,l,"C",i)&&(c[l]=O(h[3])),a=a.substr(h.index+h[0].length);break;case 3:x(b,a.nodeValue);break;case 8:try{if(h=d.exec(a.nodeValue))l=ea(h[1]),r(b,l,"M",i)&&(c[l]=O(h[2]))}catch(t){}}b.sort(G);return b}function K(a,b,c,d,e){function i(a,b){if(a)a.require=r.require,m.push(a);if(b)b.require=r.require,s.push(b)}function h(a,b){var c,d="data",e=!1;if(A(a)){for(;(c=a.charAt(0))=="^"||c=="?";)a= -a.substr(1),c=="^"&&(d="inheritedData"),e=e||c=="?";c=b[d]("$"+a+"Controller");if(!c&&!e)throw Error("No controller: "+a);}else B(a)&&(c=[],n(a,function(a){c.push(h(a,b))}));return c}function j(a,d,e,i,g){var l,p,r,D,F;l=b===e?c:hc(c,new ia(u(e),c.$attr));p=l.$$element;if(K){var J=/^\s*([@=&])\s*(\w*)\s*$/,ja=d.$parent||d;n(K.scope,function(a,b){var c=a.match(J)||[],e=c[2]||b,c=c[1],i,g,h;d.$$isolateBindings[b]=c+e;switch(c){case "@":l.$observe(e,function(a){d[b]=a});l.$$observers[e].$$scope=ja;break; -case "=":g=t(l[e]);h=g.assign||function(){i=d[b]=g(ja);throw Error(Db+l[e]+" (directive: "+K.name+")");};i=d[b]=g(ja);d.$watch(function(){var a=g(ja);a!==d[b]&&(a!==i?i=d[b]=a:h(ja,a=i=d[b]));return a});break;case "&":g=t(l[e]);d[b]=function(a){return g(ja,a)};break;default:throw Error("Invalid isolate scope definition for directive "+K.name+": "+a);}})}x&&n(x,function(a){var b={$scope:d,$element:p,$attrs:l,$transclude:g};F=a.controller;F=="@"&&(F=l[a.name]);p.data("$"+a.name+"Controller",o(F,b))}); -i=0;for(r=m.length;i<r;i++)try{D=m[i],D(d,p,l,D.require&&h(D.require,p))}catch(z){k(z,qa(p))}a&&a(d,e.childNodes,q,g);i=0;for(r=s.length;i<r;i++)try{D=s[i],D(d,p,l,D.require&&h(D.require,p))}catch(zc){k(zc,qa(p))}}for(var l=-Number.MAX_VALUE,m=[],s=[],p=null,K=null,z=null,D=c.$$element=u(b),r,G,S,ka,R=d,x,w,W,v=0,y=a.length;v<y;v++){r=a[v];S=q;if(l>r.priority)break;if(W=r.scope)ua("isolated scope",K,r,D),M(W)&&(F(D,"ng-isolate-scope"),K=r),F(D,"ng-scope"),p=p||r;G=r.name;if(W=r.controller)x=x||{}, -ua("'"+G+"' controller",x[G],r,D),x[G]=r;if(W=r.transclude)ua("transclusion",ka,r,D),ka=r,l=r.priority,W=="element"?(S=u(b),D=c.$$element=u(Y.createComment(" "+G+": "+c[G]+" ")),b=D[0],C(e,u(S[0]),b),R=J(S,d,l)):(S=u(cb(b)).contents(),D.html(""),R=J(S,d));if(W=r.template)if(ua("template",z,r,D),z=r,W=Eb(W),r.replace){S=u("<div>"+O(W)+"</div>").contents();b=S[0];if(S.length!=1||b.nodeType!==1)throw Error(g+W);C(e,D,b);G={$attr:{}};a=a.concat(V(b,a.splice(v+1,a.length-(v+1)),G));$(c,G);y=a.length}else D.html(W); -if(r.templateUrl)ua("template",z,r,D),z=r,j=P(a.splice(v,a.length-v),j,D,c,e,r.replace,R),y=a.length;else if(r.compile)try{w=r.compile(D,c,R),H(w)?i(null,w):w&&i(w.pre,w.post)}catch(E){k(E,qa(D))}if(r.terminal)j.terminal=!0,l=Math.max(l,r.priority)}j.scope=p&&p.scope;j.transclude=ka&&R;return j}function r(d,e,i,g){var h=!1;if(a.hasOwnProperty(e))for(var l,e=b.get(e+c),o=0,m=e.length;o<m;o++)try{if(l=e[o],(g===q||g>l.priority)&&l.restrict.indexOf(i)!=-1)d.push(l),h=!0}catch(t){k(t)}return h}function $(a, -b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,i){i=="class"?(F(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):i=="style"?e.attr("style",e.attr("style")+";"+b):i.charAt(0)!="$"&&!a.hasOwnProperty(i)&&(a[i]=b,d[i]=c[i])})}function P(a,b,c,d,e,i,h){var j=[],k,o,t=c[0],s=a.shift(),p=v({},s,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");m.get(s.templateUrl,{cache:l}).success(function(l){var m, -s,l=Eb(l);if(i){s=u("<div>"+O(l)+"</div>").contents();m=s[0];if(s.length!=1||m.nodeType!==1)throw Error(g+l);l={$attr:{}};C(e,c,m);V(m,a,l);$(d,l)}else m=t,c.html(l);a.unshift(p);k=K(a,m,d,h);for(o=z(c.contents(),h);j.length;){var ia=j.pop(),l=j.pop();s=j.pop();var r=j.pop(),D=m;s!==t&&(D=cb(m),C(l,u(s),D));k(function(){b(o,r,D,e,ia)},r,D,e,ia)}j=null}).error(function(a,b,c,d){throw Error("Failed to load template: "+d.url);});return function(a,c,d,e,i){j?(j.push(c),j.push(d),j.push(e),j.push(i)): -k(function(){b(o,c,d,e,i)},c,d,e,i)}}function G(a,b){return b.priority-a.priority}function ua(a,b,c,d){if(b)throw Error("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+qa(d));}function x(a,b){var c=i(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);F(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function R(a,b,c,d){var e=i(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers|| -(c.$$observers={});d==="class"&&(e=i(c[d],!0));c[d]=q;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function C(a,b,c){var d=b[0],e=d.parentNode,i,g;if(a){i=0;for(g=a.length;i<g;i++)if(a[i]==d){a[i]=c;break}}e&&e.replaceChild(c,d);c[u.expando]=d[u.expando];b[0]=c}var ia=function(a,b){this.$$element=a;this.$attr=b||{}};ia.prototype={$normalize:ea,$set:function(a,b,c,d){var e=zb(this.$$element[0],a),i=this.$$observers;e&&(this.$$element.prop(a, -b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=Za(a,"-"));if(fb(this.$$element[0])==="A"&&a==="href")D.setAttribute("href",b),e=D.href,e.match(h)||(this[a]=b="unsafe:"+e);c!==!1&&(b===null||b===q?this.$$element.removeAttr(d):this.$$element.attr(d,b));i&&n(i[a],function(a){try{a(b)}catch(c){k(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);p.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var D=s[0].createElement("a"), -S=i.startSymbol(),ka=i.endSymbol(),Eb=S=="{{"||ka=="}}"?na:function(a){return a.replace(/\{\{/g,S).replace(/}}/g,ka)};return J}]}function ea(b){return sb(b.replace(Ac,""))}function Bc(){var b={};this.register=function(a,c){M(a)?v(b,a):b[a]=c};this.$get=["$injector","$window",function(a,c){return function(d,e){if(A(d)){var g=d,d=b.hasOwnProperty(g)?b[g]:gb(e.$scope,g,!0)||gb(c,g,!0);ra(d,g,!0)}return a.instantiate(d,e)}}]}function Cc(){this.$get=["$window",function(b){return u(b.document)}]}function Dc(){this.$get= -["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Ec(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse",function(c){function d(d,f){for(var j,i,k=0,m=[],l=d.length,t=!1,o=[];k<l;)(j=d.indexOf(b,k))!=-1&&(i=d.indexOf(a,j+e))!=-1?(k!=j&&m.push(d.substring(k,j)),m.push(k=c(t=d.substring(j+e,i))),k.exp=t,k=i+g,t=!0):(k!=l&&m.push(d.substring(k)),k=l);if(!(l=m.length))m.push(""),l=1; -if(!f||t)return o.length=l,k=function(a){for(var b=0,c=l,d;b<c;b++){if(typeof(d=m[b])=="function")d=d(a),d==null||d==q?d="":typeof d!="string"&&(d=da(d));o[b]=d}return o.join("")},k.exp=d,k.parts=m,k}var e=b.length,g=a.length;d.startSymbol=function(){return b};d.endSymbol=function(){return a};return d}]}function Fb(b){for(var b=b.split("/"),a=b.length;a--;)b[a]=Ya(b[a]);return b.join("/")}function wa(b,a){var c=Gb.exec(b),c={protocol:c[1],host:c[3],port:E(c[5])||Hb[c[1]]||null,path:c[6]||"/",search:c[8], -hash:c[10]};if(a)a.$$protocol=c.protocol,a.$$host=c.host,a.$$port=c.port;return c}function la(b,a,c){return b+"://"+a+(c==Hb[b]?"":":"+c)}function Fc(b,a,c){var d=wa(b);return decodeURIComponent(d.path)!=a||w(d.hash)||d.hash.indexOf(c)!==0?b:la(d.protocol,d.host,d.port)+a.substr(0,a.lastIndexOf("/"))+d.hash.substr(c.length)}function Gc(b,a,c){var d=wa(b);if(decodeURIComponent(d.path)==a)return b;else{var e=d.search&&"?"+d.search||"",g=d.hash&&"#"+d.hash||"",h=a.substr(0,a.lastIndexOf("/")),f=d.path.substr(h.length); -if(d.path.indexOf(h)!==0)throw Error('Invalid url "'+b+'", missing path prefix "'+h+'" !');return la(d.protocol,d.host,d.port)+a+"#"+c+f+e+g}}function hb(b,a,c){a=a||"";this.$$parse=function(b){var c=wa(b,this);if(c.path.indexOf(a)!==0)throw Error('Invalid url "'+b+'", missing path prefix "'+a+'" !');this.$$path=decodeURIComponent(c.path.substr(a.length));this.$$search=Wa(c.search);this.$$hash=c.hash&&decodeURIComponent(c.hash)||"";this.$$compose()};this.$$compose=function(){var b=pb(this.$$search), -c=this.$$hash?"#"+Ya(this.$$hash):"";this.$$url=Fb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=la(this.$$protocol,this.$$host,this.$$port)+a+this.$$url};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Ha(b,a,c){var d;this.$$parse=function(b){var c=wa(b,this);if(c.hash&&c.hash.indexOf(a)!==0)throw Error('Invalid url "'+b+'", missing hash prefix "'+a+'" !');d=c.path+(c.search?"?"+c.search:"");c=Hc.exec((c.hash||"").substr(a.length));this.$$path=c[1]?(c[1].charAt(0)== -"/"?"":"/")+decodeURIComponent(c[1]):"";this.$$search=Wa(c[3]);this.$$hash=c[5]&&decodeURIComponent(c[5])||"";this.$$compose()};this.$$compose=function(){var b=pb(this.$$search),c=this.$$hash?"#"+Ya(this.$$hash):"";this.$$url=Fb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=la(this.$$protocol,this.$$host,this.$$port)+d+(this.$$url?"#"+a+this.$$url:"")};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Ib(b,a,c,d){Ha.apply(this,arguments);this.$$rewriteAppUrl=function(b){if(b.indexOf(c)== -0)return c+d+"#"+a+b.substr(c.length)}}function Ia(b){return function(){return this[b]}}function Jb(b,a){return function(c){if(w(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Ic(){var b="",a=!1;this.hashPrefix=function(a){return x(a)?(b=a,this):b};this.html5Mode=function(b){return x(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function h(a){c.$broadcast("$locationChangeSuccess",f.absUrl(),a)}var f,j,i,k=d.url(),m=wa(k);a?(j= -d.baseHref()||"/",i=j.substr(0,j.lastIndexOf("/")),m=la(m.protocol,m.host,m.port)+i+"/",f=e.history?new hb(Fc(k,j,b),i,m):new Ib(Gc(k,j,b),b,m,j.substr(i.length+1))):(m=la(m.protocol,m.host,m.port)+(m.path||"")+(m.search?"?"+m.search:"")+"#"+b+"/",f=new Ha(k,b,m));g.bind("click",function(a){if(!a.ctrlKey&&!(a.metaKey||a.which==2)){for(var b=u(a.target);y(b[0].nodeName)!=="a";)if(b[0]===g[0]||!(b=b.parent())[0])return;var d=b.prop("href"),e=f.$$rewriteAppUrl(d);d&&!b.attr("target")&&e&&(f.$$parse(e), -c.$apply(),a.preventDefault(),X.angular["ff-684208-preventDefault"]=!0)}});f.absUrl()!=k&&d.url(f.absUrl(),!0);d.onUrlChange(function(a){f.absUrl()!=a&&(c.$evalAsync(function(){var b=f.absUrl();f.$$parse(a);h(b)}),c.$$phase||c.$digest())});var l=0;c.$watch(function(){var a=d.url(),b=f.$$replace;if(!l||a!=f.absUrl())l++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),h(a))});f.$$replace=!1;return l});return f}]}function Jc(){this.$get= -["$window",function(b){function a(a){a instanceof Error&&(a.stack?a=a.message&&a.stack.indexOf(a.message)===-1?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function c(c){var e=b.console||{},g=e[c]||e.log||C;return g.apply?function(){var b=[];n(arguments,function(c){b.push(a(c))});return g.apply(e,b)}:function(a,b){g(a,b)}}return{log:c("log"),warn:c("warn"),info:c("info"),error:c("error")}}]}function Kc(b,a){function c(a){return a.indexOf(s)!= --1}function d(){return o+1<b.length?b.charAt(o+1):!1}function e(a){return"0"<=a&&a<="9"}function g(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"||a=="\u00a0"}function h(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"}function f(a){return a=="-"||a=="+"||e(a)}function j(a,c,d){d=d||o;throw Error("Lexer Error: "+a+" at column"+(x(c)?"s "+c+"-"+o+" ["+b.substring(c,d)+"]":" "+d)+" in expression ["+b+"].");}function i(){for(var a="",c=o;o<b.length;){var i=y(b.charAt(o));if(i=="."|| -e(i))a+=i;else{var g=d();if(i=="e"&&f(g))a+=i;else if(f(i)&&g&&e(g)&&a.charAt(a.length-1)=="e")a+=i;else if(f(i)&&(!g||!e(g))&&a.charAt(a.length-1)=="e")j("Invalid exponent");else break}o++}a*=1;l.push({index:c,text:a,json:!0,fn:function(){return a}})}function k(){for(var c="",d=o,f,i,j;o<b.length;){var k=b.charAt(o);if(k=="."||h(k)||e(k))k=="."&&(f=o),c+=k;else break;o++}if(f)for(i=o;i<b.length;){k=b.charAt(i);if(k=="("){j=c.substr(f-d+1);c=c.substr(0,f-d);o=i;break}if(g(k))i++;else break}d={index:d, -text:c};if(Ja.hasOwnProperty(c))d.fn=d.json=Ja[c];else{var m=Kb(c,a);d.fn=v(function(a,b){return m(a,b)},{assign:function(a,b){return Lb(a,c,b)}})}l.push(d);j&&(l.push({index:f,text:".",json:!1}),l.push({index:f+1,text:j,json:!1}))}function m(a){var c=o;o++;for(var d="",e=a,f=!1;o<b.length;){var i=b.charAt(o);e+=i;if(f)i=="u"?(i=b.substring(o+1,o+5),i.match(/[\da-f]{4}/i)||j("Invalid unicode escape [\\u"+i+"]"),o+=4,d+=String.fromCharCode(parseInt(i,16))):(f=Lc[i],d+=f?f:i),f=!1;else if(i=="\\")f= -!0;else if(i==a){o++;l.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}else d+=i;o++}j("Unterminated quote",c)}for(var l=[],t,o=0,p=[],s,J=":";o<b.length;){s=b.charAt(o);if(c("\"'"))m(s);else if(e(s)||c(".")&&e(d()))i();else if(h(s)){if(k(),"{,".indexOf(J)!=-1&&p[0]=="{"&&(t=l[l.length-1]))t.json=t.text.indexOf(".")==-1}else if(c("(){}[].,;:"))l.push({index:o,text:s,json:":[,".indexOf(J)!=-1&&c("{[")||c("}]:,")}),c("{[")&&p.unshift(s),c("}]")&&p.shift(),o++;else if(g(s)){o++; -continue}else{var n=s+d(),z=Ja[s],V=Ja[n];V?(l.push({index:o,text:n,fn:V}),o+=2):z?(l.push({index:o,text:s,fn:z,json:"[,:".indexOf(J)!=-1&&c("+-")}),o+=1):j("Unexpected next character ",o,o+1)}J=s}return l}function Mc(b,a,c,d){function e(a,c){throw Error("Syntax Error: Token '"+c.text+"' "+a+" at column "+(c.index+1)+" of the expression ["+b+"] starting at ["+b.substring(c.index)+"].");}function g(){if(P.length===0)throw Error("Unexpected end of expression: "+b);return P[0]}function h(a,b,c,d){if(P.length> -0){var e=P[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),P.shift(),b):!1}function j(a){f(a)||e("is unexpected, expecting ["+a+"]",h())}function i(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function m(){for(var a=[];;)if(P.length>0&&!h("}",")",";","]")&&a.push(w()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e<a.length;e++){var f= -a[e];f&&(d=f(b,c))}return d}}function l(){for(var a=f(),b=c(a.text),d=[];;)if(a=f(":"))d.push(G());else{var e=function(a,c,e){for(var e=[e],f=0;f<d.length;f++)e.push(d[f](a,c));return b.apply(a,e)};return function(){return e}}}function t(){for(var a=o(),b;;)if(b=f("||"))a=k(a,b.fn,o());else return a}function o(){var a=p(),b;if(b=f("&&"))a=k(a,b.fn,o());return a}function p(){var a=s(),b;if(b=f("==","!="))a=k(a,b.fn,p());return a}function s(){var a;a=J();for(var b;b=f("+","-");)a=k(a,b.fn,J());if(b= -f("<",">","<=",">="))a=k(a,b.fn,s());return a}function J(){for(var a=n(),b;b=f("*","/","%");)a=k(a,b.fn,n());return a}function n(){var a;return f("+")?z():(a=f("-"))?k(r,a.fn,n()):(a=f("!"))?i(a.fn,n()):z()}function z(){var a;if(f("("))a=w(),j(")");else if(f("["))a=V();else if(f("{"))a=K();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=x(a,c),c=null):b.text==="["?(c=a,a=R(a)):b.text==="."?(c=a,a=u(a)):e("IMPOSSIBLE");return a}function V(){var a= -[];if(g().text!="]"){do a.push(G());while(f(","))}j("]");return function(b,c){for(var d=[],e=0;e<a.length;e++)d.push(a[e](b,c));return d}}function K(){var a=[];if(g().text!="}"){do{var b=f(),b=b.string||b.text;j(":");var c=G();a.push({key:b,value:c})}while(f(","))}j("}");return function(b,c){for(var d={},e=0;e<a.length;e++){var f=a[e],i=f.value(b,c);d[f.key]=i}return d}}var r=I(0),$,P=Kc(b,d),G=function(){var a=t(),c,d;return(d=f("="))?(a.assign||e("implies assignment but ["+b.substring(0,d.index)+ -"] can not be assigned to",d),c=t(),function(b,d){return a.assign(b,c(b,d),d)}):a},x=function(a,b){var c=[];if(g().text!=")"){do c.push(G());while(f(","))}j(")");return function(d,e){for(var f=[],i=b?b(d,e):d,g=0;g<c.length;g++)f.push(c[g](d,e));g=a(d,e)||C;return g.apply?g.apply(i,f):g(f[0],f[1],f[2],f[3],f[4])}},u=function(a){var b=f().text,c=Kb(b,d);return v(function(b,d){return c(a(b,d),d)},{assign:function(c,d,e){return Lb(a(c,e),b,d)}})},R=function(a){var b=G();j("]");return v(function(c,d){var e= -a(c,d),f=b(c,d),i;if(!e)return q;if((e=e[f])&&e.then){i=e;if(!("$$v"in e))i.$$v=q,i.then(function(a){i.$$v=a});e=e.$$v}return e},{assign:function(c,d,e){return a(c,e)[b(c,e)]=d}})},w=function(){for(var a=G(),b;;)if(b=f("|"))a=k(a,b.fn,l());else return a};a?(G=t,x=u=R=w=function(){e("is not valid json",{text:b,index:0})},$=z()):$=m();P.length!==0&&e("is an unexpected token",P[0]);return $}function Lb(b,a,c){for(var a=a.split("."),d=0;a.length>1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]= -c}function gb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h<g;h++)d=a[h],b&&(b=(e=b)[d]);return!c&&H(b)?Ua(e,b):b}function Mb(b,a,c,d,e){return function(g,h){var f=h&&h.hasOwnProperty(b)?h:g,j;if(f===null||f===q)return f;if((f=f[b])&&f.then){if(!("$$v"in f))j=f,j.$$v=q,j.then(function(a){j.$$v=a});f=f.$$v}if(!a||f===null||f===q)return f;if((f=f[a])&&f.then){if(!("$$v"in f))j=f,j.$$v=q,j.then(function(a){j.$$v=a});f=f.$$v}if(!c||f===null||f===q)return f;if((f=f[c])&&f.then){if(!("$$v"in -f))j=f,j.$$v=q,j.then(function(a){j.$$v=a});f=f.$$v}if(!d||f===null||f===q)return f;if((f=f[d])&&f.then){if(!("$$v"in f))j=f,j.$$v=q,j.then(function(a){j.$$v=a});f=f.$$v}if(!e||f===null||f===q)return f;if((f=f[e])&&f.then){if(!("$$v"in f))j=f,j.$$v=q,j.then(function(a){j.$$v=a});f=f.$$v}return f}}function Kb(b,a){if(ib.hasOwnProperty(b))return ib[b];var c=b.split("."),d=c.length,e;if(a)e=d<6?Mb(c[0],c[1],c[2],c[3],c[4]):function(a,b){var e=0,i;do i=Mb(c[e++],c[e++],c[e++],c[e++],c[e++])(a,b),b=q, -a=i;while(e<d);return i};else{var g="var l, fn, p;\n";n(c,function(a,b){g+="if(s === null || s === undefined) return s;\nl=s;\ns="+(b?"s":'((k&&k.hasOwnProperty("'+a+'"))?k:s)')+'["'+a+'"];\nif (s && s.then) {\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n'});g+="return s;";e=Function("s","k",g);e.toString=function(){return g}}return ib[b]=e}function Nc(){var b={};this.$get=["$filter","$sniffer",function(a,c){return function(d){switch(typeof d){case "string":return b.hasOwnProperty(d)? -b[d]:b[d]=Mc(d,!1,a,c.csp);case "function":return d;default:return C}}}]}function Oc(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Pc(function(a){b.$evalAsync(a)},a)}]}function Pc(b,a){function c(a){return a}function d(a){return h(a)}var e=function(){var f=[],j,i;return i={resolve:function(a){if(f){var c=f;f=q;j=g(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],j.then(a[0],a[1])})}},reject:function(a){i.resolve(h(a))},promise:{then:function(b,i){var g=e(),h= -function(d){try{g.resolve((b||c)(d))}catch(e){a(e),g.reject(e)}},o=function(b){try{g.resolve((i||d)(b))}catch(c){a(c),g.reject(c)}};f?f.push([h,o]):j.then(h,o);return g.promise}}}},g=function(a){return a&&a.then?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},h=function(a){return{then:function(c,i){var g=e();b(function(){g.resolve((i||d)(a))});return g.promise}}};return{defer:e,reject:h,when:function(f,j,i){var k=e(),m,l=function(b){try{return(j||c)(b)}catch(d){return a(d), -h(d)}},t=function(b){try{return(i||d)(b)}catch(c){return a(c),h(c)}};b(function(){g(f).then(function(a){m||(m=!0,k.resolve(g(a).then(l,t)))},function(a){m||(m=!0,k.resolve(t(a)))})});return k.promise},all:function(a){var b=e(),c=a.length,d=[];c?n(a,function(a,e){g(a).then(function(a){e in d||(d[e]=a,--c||b.resolve(d))},function(a){e in d||b.reject(a)})}):b.resolve(d);return b.promise}}}function Qc(){var b={};this.when=function(a,c){b[a]=v({reloadOnSearch:!0},c);if(a){var d=a[a.length-1]=="/"?a.substr(0, -a.length-1):a+"/";b[d]={redirectTo:a}}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache",function(a,c,d,e,g,h,f){function j(a,b){for(var b="^"+b.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")+"$",c="",d=[],e={},f=/:(\w+)/g,i,g=0;(i=f.exec(b))!==null;)c+=b.slice(g,i.index),c+="([^\\/]*)",d.push(i[1]),g=f.lastIndex;c+=b.substr(g);var h=a.match(RegExp(c));h&&n(d,function(a,b){e[a]=h[b+1]});return h? -e:null}function i(){var b=k(),i=t.current;if(b&&i&&b.$route===i.$route&&ga(b.pathParams,i.pathParams)&&!b.reloadOnSearch&&!l)i.params=b.params,U(i.params,d),a.$broadcast("$routeUpdate",i);else if(b||i)l=!1,a.$broadcast("$routeChangeStart",b,i),(t.current=b)&&b.redirectTo&&(A(b.redirectTo)?c.path(m(b.redirectTo,b.params)).search(b.params).replace():c.url(b.redirectTo(b.pathParams,c.path(),c.search())).replace()),e.when(b).then(function(){if(b){var a=[],c=[],d;n(b.resolve||{},function(b,d){a.push(d); -c.push(A(b)?g.get(b):g.invoke(b))});if(!x(d=b.template))if(x(d=b.templateUrl))d=h.get(d,{cache:f}).then(function(a){return a.data});x(d)&&(a.push("$template"),c.push(d));return e.all(c).then(function(b){var c={};n(b,function(b,d){c[a[d]]=b});return c})}}).then(function(c){if(b==t.current){if(b)b.locals=c,U(b.params,d);a.$broadcast("$routeChangeSuccess",b,i)}},function(c){b==t.current&&a.$broadcast("$routeChangeError",b,i,c)})}function k(){var a,d;n(b,function(b,e){if(!d&&(a=j(c.path(),e)))d=za(b, -{params:v({},c.search(),a),pathParams:a}),d.$route=b});return d||b[null]&&za(b[null],{params:{},pathParams:{}})}function m(a,b){var c=[];n((a||"").split(":"),function(a,d){if(d==0)c.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];c.push(b[f]);c.push(e[2]||"");delete b[f]}});return c.join("")}var l=!1,t={routes:b,reload:function(){l=!0;a.$evalAsync(i)}};a.$on("$locationChangeSuccess",i);return t}]}function Rc(){this.$get=I({})}function Sc(){var b=10;this.digestTtl=function(a){arguments.length&&(b=a); -return b};this.$get=["$injector","$exceptionHandler","$parse",function(a,c,d){function e(){this.$id=ya();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$listeners={};this.$$isolateBindings={}}function g(a){if(j.$$phase)throw Error(j.$$phase+" already in progress");j.$$phase=a}function h(a,b){var c=d(a);ra(c,b);return c}function f(){}e.prototype={$new:function(a){if(H(a))throw Error("API-CHANGE: Use $controller to instantiate controllers."); -a?(a=new e,a.$root=this.$root):(a=function(){},a.prototype=this,a=new a,a.$id=ya());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$asyncQueue=[];a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,c){var d=h(a,"watch"),e=this.$$watchers,g={fn:b,last:f,get:d,exp:a,eq:!!c};if(!H(b)){var j=h(b||C,"listener");g.fn=function(a,b, -c){j(c)}}if(!e)e=this.$$watchers=[];e.unshift(g);return function(){Ta(e,g)}},$digest:function(){var a,d,e,h,t,o,p,s=b,n,F=[],z,q;g("$digest");do{p=!1;n=this;do{for(t=n.$$asyncQueue;t.length;)try{n.$eval(t.shift())}catch(K){c(K)}if(h=n.$$watchers)for(o=h.length;o--;)try{if(a=h[o],(d=a.get(n))!==(e=a.last)&&!(a.eq?ga(d,e):typeof d=="number"&&typeof e=="number"&&isNaN(d)&&isNaN(e)))p=!0,a.last=a.eq?U(d):d,a.fn(d,e===f?d:e,n),s<5&&(z=4-s,F[z]||(F[z]=[]),q=H(a.exp)?"fn: "+(a.exp.name||a.exp.toString()): -a.exp,q+="; newVal: "+da(d)+"; oldVal: "+da(e),F[z].push(q))}catch(r){c(r)}if(!(h=n.$$childHead||n!==this&&n.$$nextSibling))for(;n!==this&&!(h=n.$$nextSibling);)n=n.$parent}while(n=h);if(p&&!s--)throw j.$$phase=null,Error(b+" $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: "+da(F));}while(p||t.length);j.$$phase=null},$destroy:function(){if(!(j==this||this.$$destroyed)){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(a.$$childHead==this)a.$$childHead= -this.$$nextSibling;if(a.$$childTail==this)a.$$childTail=this.$$prevSibling;if(this.$$prevSibling)this.$$prevSibling.$$nextSibling=this.$$nextSibling;if(this.$$nextSibling)this.$$nextSibling.$$prevSibling=this.$$prevSibling;this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null}},$eval:function(a,b){return d(a)(this,b)},$evalAsync:function(a){this.$$asyncQueue.push(a)},$apply:function(a){try{return g("$apply"),this.$eval(a)}catch(b){c(b)}finally{j.$$phase=null;try{j.$digest()}catch(d){throw c(d), -d;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[Aa(c,b)]=null}},$emit:function(a,b){var d=[],e,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},j=[h].concat(ha.call(arguments,1)),n,q;do{e=f.$$listeners[a]||d;h.currentScope=f;n=0;for(q=e.length;n<q;n++)if(e[n])try{if(e[n].apply(null,j),g)return h}catch(z){c(z)}else e.splice(n,1),n--,q--;f=f.$parent}while(f); -return h},$broadcast:function(a,b){var d=this,e=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ha.call(arguments,1)),h,j;do{d=e;f.currentScope=d;e=d.$$listeners[a]||[];h=0;for(j=e.length;h<j;h++)if(e[h])try{e[h].apply(null,g)}catch(n){c(n)}else e.splice(h,1),h--,j--;if(!(e=d.$$childHead||d!==this&&d.$$nextSibling))for(;d!==this&&!(e=d.$$nextSibling);)d=d.$parent}while(d=e);return f}};var j=new e;return j}]}function Tc(){this.$get= -["$window",function(b){var a={},c=E((/android (\d+)/.exec(y(b.navigator.userAgent))||[])[1]);return{history:!(!b.history||!b.history.pushState||c<4),hashchange:"onhashchange"in b&&(!b.document.documentMode||b.document.documentMode>7),hasEvent:function(c){if(c=="input"&&Z==9)return!1;if(w(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Uc(){this.$get=I(X)}function Nb(b){var a={},c,d,e;if(!b)return a;n(b.split("\n"),function(b){e=b.indexOf(":");c=y(O(b.substr(0, -e)));d=O(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Ob(b){var a=M(b)?b:q;return function(c){a||(a=Nb(b));return c?a[y(c)]||null:a}}function Pb(b,a,c){if(H(c))return c(b,a);n(c,function(c){b=c(b,a)});return b}function Vc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){A(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=ob(d,!0)));return d}],transformRequest:[function(a){return M(a)&&xa.apply(a)!=="[object File]"?da(a):a}], -headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,j,i,k){function m(a){function c(a){var b=v({},a,{data:Pb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:i.reject(b)}a.method=ma(a.method);var e=a.transformRequest|| -d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=v({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[y(a.method)],a.headers),e=Pb(a.data,Ob(g),e),j;w(a.data)&&delete g["Content-Type"];j=l(a,e,g);j=j.then(c,c);n(p,function(a){j=a(j)});j.success=function(b){j.then(function(c){b(c.data,c.status,c.headers,a)});return j};j.error=function(b){j.then(null,function(c){b(c.data,c.status,c.headers,a)});return j};return j}function l(b,c,d){function e(a,b,c){n&&(200<=a&&a<300?n.put(q, -[a,b,Nb(c)]):n.remove(q));f(b,a,c);j.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?k.resolve:k.reject)({data:a,status:c,headers:Ob(d),config:b})}function h(){var a=Aa(m.pendingRequests,b);a!==-1&&m.pendingRequests.splice(a,1)}var k=i.defer(),l=k.promise,n,p,q=t(b.url,b.params);m.pendingRequests.push(b);l.then(h,h);b.cache&&b.method=="GET"&&(n=M(b.cache)?b.cache:o);if(n)if(p=n.get(q))if(p.then)return p.then(h,h),p;else B(p)?f(p[1],p[0],U(p[2])):f(p,200,{});else n.put(q,l);p||a(b.method, -q,c,e,d,b.timeout,b.withCredentials);return l}function t(a,b){if(!b)return a;var c=[];fc(b,function(a,b){a==null||a==q||(M(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var o=c("$http"),p=[];n(e,function(a){p.push(A(a)?k.get(a):k.invoke(a))});m.pendingRequests=[];(function(a){n(arguments,function(a){m[a]=function(b,c){return m(v(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){n(arguments,function(a){m[a]= -function(b,c,d){return m(v(d||{},{method:a,url:b,data:c}))}})})("post","put");m.defaults=d;return m}]}function Wc(){this.$get=["$browser","$window","$document",function(b,a,c){return Xc(b,Yc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Xc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= -d;e.body.appendChild(c)}return function(e,j,i,k,m,l,t){function o(a,c,d,e){c=(j.match(Gb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(C)}b.$$incOutstandingRequestCount();j=j||b.url();if(y(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};h(j.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?o(k,200,d[p].data):o(k,-2);delete d[p]})}else{var s=new a;s.open(e,j,!0);n(m,function(a,b){a&&s.setRequestHeader(b,a)}); -var q;s.onreadystatechange=function(){if(s.readyState==4){var a=s.getAllResponseHeaders(),b=["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"];a||(a="",n(b,function(b){var c=s.getResponseHeader(b);c&&(a+=b+": "+c+"\n")}));o(k,q||s.status,s.responseText,a)}};if(t)s.withCredentials=!0;s.send(i||"");l>0&&c(function(){q=-1;s.abort()},l)}}}function Zc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0, -maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","), -AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function $c(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,j){var i=c.defer(),k=i.promise,m=x(j)&&!j,f=a.defer(function(){try{i.resolve(e())}catch(a){i.reject(a),d(a)}m||b.$apply()},f),j=function(){delete g[k.$$timeoutId]}; -k.$$timeoutId=f;g[f]=i;k.then(j,j);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Qb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Rb);a("date",Sb);a("filter",ad);a("json",bd);a("limitTo",cd);a("lowercase",dd);a("number",Tb);a("orderBy",Ub);a("uppercase",ed)}function ad(){return function(b, -a){if(!B(b))return b;var c=[];c.check=function(a){for(var b=0;b<c.length;b++)if(!c[b](a))return!1;return!0};var d=function(a,b){if(b.charAt(0)==="!")return!d(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return(""+a).toLowerCase().indexOf(b)>-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;c<a.length;c++)if(d(a[c],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a= -{$:a};case "object":for(var e in a)e=="$"?function(){var b=(""+a[e]).toLowerCase();b&&c.push(function(a){return d(a,b)})}():function(){var b=e,f=(""+a[e]).toLowerCase();f&&c.push(function(a){return d(gb(a,b),f)})}();break;case "function":c.push(a);break;default:return b}for(var g=[],h=0;h<b.length;h++){var f=b[h];c.check(f)&&g.push(f)}return g}}function Rb(b){var a=b.NUMBER_FORMATS;return function(b,d){if(w(d))d=a.CURRENCY_SYM;return Vb(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g, -d)}}function Tb(b){var a=b.NUMBER_FORMATS;return function(b,d){return Vb(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Vb(b,a,c,d,e){if(isNaN(b)||!isFinite(b))return"";var g=b<0,b=Math.abs(b),h=b+"",f="",j=[],i=!1;if(h.indexOf("e")!==-1){var k=h.match(/([\d\.]+)e(-?)(\d+)/);k&&k[2]=="-"&&k[3]>e+1?h="0":(f=h,i=!0)}if(!i){h=(h.split(Wb)[1]||"").length;w(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));var h=Math.pow(10,e),b=Math.round(b*h)/h,b=(""+b).split(Wb),h=b[0],b=b[1]||"",i=0,k=a.lgSize, -m=a.gSize;if(h.length>=k+m)for(var i=h.length-k,l=0;l<i;l++)(i-l)%m===0&&l!==0&&(f+=c),f+=h.charAt(l);for(l=i;l<h.length;l++)(h.length-l)%k===0&&l!==0&&(f+=c),f+=h.charAt(l);for(;b.length<e;)b+="0";e&&e!=="0"&&(f+=d+b.substr(0,e))}j.push(g?a.negPre:a.posPre);j.push(f);j.push(g?a.negSuf:a.posSuf);return j.join("")}function jb(b,a,c){var d="";b<0&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function N(b,a,c,d){return function(e){e=e["get"+b]();if(c>0||e>-c)e+= -c;e===0&&c==-12&&(e=12);return jb(e,a,d)}}function Ka(b,a){return function(c,d){var e=c["get"+b](),g=ma(a?"SHORT"+b:b);return d[g][e]}}function Sb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=E(b[9]+b[10]),h=E(b[9]+b[11]));a.setUTCFullYear(E(b[1]),E(b[2])-1,E(b[3]));a.setUTCHours(E(b[4]||0)-g,E(b[5]||0)-h,E(b[6]||0),E(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g= -"",h=[],f,j,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;A(c)&&(c=fd.test(c)?E(c):a(c));Ra(c)&&(c=new Date(c));if(!oa(c))return c;for(;e;)(j=gd.exec(e))?(h=h.concat(ha.call(j,1)),e=h.pop()):(h.push(e),e=null);n(h,function(a){f=hd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function bd(){return function(b){return da(b,!0)}}function cd(){return function(b,a){if(!(b instanceof Array))return b;var a=E(a),c=[],d,e;if(!b||!(b instanceof Array))return c;a>b.length? -a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Ub(b){return function(a,c,d){function e(a,b){return Va(b)?function(b,c){return a(c,b)}:a}if(!B(a))return a;if(!c)return a;for(var c=B(c)?c:[c],c=Sa(c,function(a){var c=!1,d=a||na;if(A(a)){if(a.charAt(0)=="+"||a.charAt(0)=="-")c=a.charAt(0)=="-",a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?(f=="string"&&(c=c.toLowerCase()),f== -"string"&&(e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},c)}),g=[],h=0;h<a.length;h++)g.push(a[h]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(e!==0)return e}return 0},d))}}function Q(b){H(b)&&(b={link:b});b.restrict=b.restrict||"AC";return I(b)}function Xb(b,a){function c(a,c){c=c?"-"+Za(c,"-"):"";b.removeClass((a?La:Ma)+c).addClass((a?Ma:La)+c)}var d=this,e=b.parent().controller("form")||Na,g=0,h=d.$error={};d.$name=a.name;d.$dirty=!1;d.$pristine=!0; -d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(Oa);c(!0);d.$addControl=function(a){a.$name&&!d.hasOwnProperty(a.$name)&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&&d[a.$name]===a&&delete d[a.$name];n(h,function(b,c){d.$setValidity(c,!0,a)})};d.$setValidity=function(a,b,i){var k=h[a];if(b){if(k&&(Ta(k,i),!k.length)){g--;if(!g)c(b),d.$valid=!0,d.$invalid=!1;h[a]=!1;c(!0,a);e.$setValidity(a,!0,d)}}else{g||c(b);if(k){if(Aa(k,i)!=-1)return}else h[a]=k=[],g++,c(!1,a),e.$setValidity(a,!1, -d);k.push(i);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(Oa).addClass(Yb);d.$dirty=!0;d.$pristine=!1;e.$setDirty()}}function T(b){return w(b)||b===""||b===null||b!==b}function Pa(b,a,c,d,e,g){var h=function(){var c=O(a.val());d.$viewValue!==c&&b.$apply(function(){d.$setViewValue(c)})};if(e.hasEvent("input"))a.bind("input",h);else{var f;a.bind("keydown",function(a){a=a.keyCode;a===91||15<a&&a<19||37<=a&&a<=40||f||(f=g.defer(function(){h();f=null}))});a.bind("change",h)}d.$render= -function(){a.val(T(d.$viewValue)?"":d.$viewValue)};var j=c.ngPattern,i=function(a,b){return T(b)||a.test(b)?(d.$setValidity("pattern",!0),b):(d.$setValidity("pattern",!1),q)};j&&(j.match(/^\/(.*)\/$/)?(j=RegExp(j.substr(1,j.length-2)),e=function(a){return i(j,a)}):e=function(a){var c=b.$eval(j);if(!c||!c.test)throw Error("Expected "+j+" to be a RegExp but was "+c);return i(c,a)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var k=E(c.ngMinlength),e=function(a){return!T(a)&&a.length< -k?(d.$setValidity("minlength",!1),q):(d.$setValidity("minlength",!0),a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var m=E(c.ngMaxlength),c=function(a){return!T(a)&&a.length>m?(d.$setValidity("maxlength",!1),q):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function kb(b,a){b="ngClass"+b;return Q(function(c,d,e){function g(b){if(a===!0||c.$index%2===a)j&&b!==j&&h(j),f(b);j=b}function h(a){M(a)&&!B(a)&&(a=Sa(a,function(a,b){if(a)return b}));d.removeClass(B(a)? -a.join(" "):a)}function f(a){M(a)&&!B(a)&&(a=Sa(a,function(a,b){if(a)return b}));a&&d.addClass(B(a)?a.join(" "):a)}var j=q;c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",function(d,g){var j=d%2;j!==g%2&&(j==a?f(c.$eval(e[b])):h(c.$eval(e[b])))})})}var y=function(b){return A(b)?b.toLowerCase():b},ma=function(b){return A(b)?b.toUpperCase():b},Z=E((/msie (\d+)/.exec(y(navigator.userAgent))||[])[1]),u,ca,ha=[].slice,Qa=[].push,xa=Object.prototype.toString, -Zb=X.angular||(X.angular={}),ta,fb,aa=["0","0","0"];C.$inject=[];na.$inject=[];fb=Z<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?ma(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var kc=/[A-Z]/g,id={full:"1.0.5",major:1,minor:0,dot:5,codeName:"flatulent-propulsion"},Ca=L.cache={},Ba=L.expando="ng-"+(new Date).getTime(),oc=1,$b=X.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+ -a,c)},db=X.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},mc=/([\:\-\_]+(.))/g,nc=/^moz([A-Z])/,va=L.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);L(X).bind("load",a)},toString:function(){var b=[];n(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+b])},length:0,push:Qa,sort:[].sort,splice:[].splice},Fa={};n("multiple,selected,checked,disabled,readOnly,required".split(","), -function(b){Fa[y(b)]=b});var Ab={};n("input,select,option,textarea,button,form".split(","),function(b){Ab[ma(b)]=!0});n({data:vb,inheritedData:Ea,scope:function(b){return Ea(b,"$scope")},controller:yb,injector:function(b){return Ea(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Da,css:function(b,a,c){a=sb(a);if(x(c))b.style[a]=c;else{var d;Z<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?q:d);return d}},attr:function(b,a,c){var d= -y(a);if(Fa[d])if(x(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:q;else if(x(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?q:b},prop:function(b,a,c){if(x(c))b[a]=c;else return b[a]},text:v(Z<9?function(b,a){if(b.nodeType==1){if(w(a))return b.innerText;b.innerText=a}else{if(w(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(w(a))return b.textContent;b.textContent=a},{$dv:""}), -val:function(b,a){if(w(a))return b.value;b.value=a},html:function(b,a){if(w(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)sa(d[c]);b.innerHTML=a}},function(b,a){L.prototype[a]=function(a,d){var e,g;if((b.length==2&&b!==Da&&b!==yb?a:d)===q)if(M(a)){for(e=0;e<this.length;e++)if(b===vb)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}else{if(this.length)return b(this[0],a,d)}else{for(e=0;e<this.length;e++)b(this[e],a,d);return this}return b.$dv}});n({removeData:tb,dealoc:sa, -bind:function a(c,d,e){var g=ba(c,"events"),h=ba(c,"handle");g||ba(c,"events",g={});h||ba(c,"handle",h=pc(c,g));n(d.split(" "),function(d){var j=g[d];if(!j){if(d=="mouseenter"||d=="mouseleave"){var i=0;g.mouseenter=[];g.mouseleave=[];a(c,"mouseover",function(a){i++;i==1&&h(a,"mouseenter")});a(c,"mouseout",function(a){i--;i==0&&h(a,"mouseleave")})}else $b(c,d,h),g[d]=[];j=g[d]}j.push(e)})},unbind:ub,replaceWith:function(a,c){var d,e=a.parentNode;sa(a);n(new L(c),function(c){d?e.insertBefore(c,d.nextSibling): -e.replaceChild(c,a);d=c})},children:function(a){var c=[];n(a.childNodes,function(a){a.nodeType===1&&c.push(a)});return c},contents:function(a){return a.childNodes||[]},append:function(a,c){n(new L(c),function(c){a.nodeType===1&&a.appendChild(c)})},prepend:function(a,c){if(a.nodeType===1){var d=a.firstChild;n(new L(c),function(c){d?a.insertBefore(c,d):(a.appendChild(c),d=c)})}},wrap:function(a,c){var c=u(c)[0],d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){sa(a);var c=a.parentNode; -c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;n(new L(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:xb,removeClass:wb,toggleClass:function(a,c,d){w(d)&&(d=!Da(a,c));(d?xb:wb)(a,c)},parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;a!=null&&a.nodeType!==1;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName(c)},clone:cb,triggerHandler:function(a, -c){var d=(ba(a,"events")||{})[c];n(d,function(c){c.call(a,null)})}},function(a,c){L.prototype[c]=function(c,e){for(var g,h=0;h<this.length;h++)g==q?(g=a(this[h],c,e),g!==q&&(g=u(g))):bb(g,a(this[h],c,e));return g==q?this:g}});Ga.prototype={put:function(a,c){this[fa(a)]=c},get:function(a){return this[fa(a)]},remove:function(a){var c=this[a=fa(a)];delete this[a];return c}};eb.prototype={push:function(a,c){var d=this[a=fa(a)];d?d.push(c):this[a]=[c]},shift:function(a){var c=this[a=fa(a)];if(c)return c.length== -1?(delete this[a],c[0]):c.shift()},peek:function(a){if(a=this[fa(a)])return a[0]}};var rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,sc=/,/,tc=/^\s*(_?)(\S+?)\1\s*$/,qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Db="Non-assignable model expression: ";Cb.$inject=["$provide"];var Ac=/^(x[\:\-_]|data[\:\-_])/i,Gb=/^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,ac=/^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,Hc=ac,Hb={http:80,https:443,ftp:21};hb.prototype={$$replace:!1,absUrl:Ia("$$absUrl"), -url:function(a,c){if(w(a))return this.$$url;var d=ac.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));if(d[2]||d[1])this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:Ia("$$protocol"),host:Ia("$$host"),port:Ia("$$port"),path:Jb("$$path",function(a){return a.charAt(0)=="/"?a:"/"+a}),search:function(a,c){if(w(a))return this.$$search;x(c)?c===null?delete this.$$search[a]:this.$$search[a]=c:this.$$search=A(a)?Wa(a):a;this.$$compose();return this},hash:Jb("$$hash",na),replace:function(){this.$$replace= -!0;return this}};Ha.prototype=za(hb.prototype);Ib.prototype=za(Ha.prototype);var Ja={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:C,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return x(d)?x(e)?d+e:d:x(e)?e:q},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(x(d)?d:0)-(x(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)}, -"=":C,"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Lc= -{n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},ib={},Yc=X.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw Error("This browser does not support XMLHttpRequest.");};Qb.$inject=["$provide"];Rb.$inject=["$locale"];Tb.$inject=["$locale"];var Wb=".",hd={yyyy:N("FullYear",4),yy:N("FullYear",2,0,!0),y:N("FullYear",1),MMMM:Ka("Month"), -MMM:Ka("Month",!0),MM:N("Month",2,1),M:N("Month",1,1),dd:N("Date",2),d:N("Date",1),HH:N("Hours",2),H:N("Hours",1),hh:N("Hours",2,-12),h:N("Hours",1,-12),mm:N("Minutes",2),m:N("Minutes",1),ss:N("Seconds",2),s:N("Seconds",1),EEEE:Ka("Day"),EEE:Ka("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){var a=-1*a.getTimezoneOffset(),c=a>=0?"+":"";c+=jb(a/60,2)+jb(Math.abs(a%60),2);return c}},gd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, -fd=/^\d+$/;Sb.$inject=["$locale"];var dd=I(y),ed=I(ma);Ub.$inject=["$parse"];var jd=I({restrict:"E",compile:function(a,c){Z<=8&&(!c.href&&!c.name&&c.$set("href",""),a.append(Y.createComment("IE fix")));return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),lb={};n(Fa,function(a,c){var d=ea("ng-"+c);lb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});n(["src","href"],function(a){var c=ea("ng-"+ -a);lb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),Z&&e.prop(a,g[a]))})}}}});var Na={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C};Xb.$inject=["$element","$attrs","$scope"];var Qa=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E",controller:Xb,compile:function(){return{pre:function(a,d,h,f){if(!h.action){var j=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};$b(d[0],"submit",j);d.bind("$destroy", -function(){c(function(){db(d[0],"submit",j)},0,!1)})}var i=d.parent().controller("form"),k=h.name||h.ngForm;k&&(a[k]=f);i&&d.bind("$destroy",function(){i.$removeControl(f);k&&(a[k]=q);v(f,Na)})}}}};return a?v(U(d),{restrict:"EAC"}):d}]},kd=Qa(),ld=Qa(!0),md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,bc={text:Pa,number:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);e.$parsers.push(function(a){var c= -T(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),q)});e.$formatters.push(function(a){return T(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!T(a)&&a<f?(e.$setValidity("min",!1),q):(e.$setValidity("min",!0),a)};e.$parsers.push(a);e.$formatters.push(a)}if(d.max){var j=parseFloat(d.max),d=function(a){return!T(a)&&a>j?(e.$setValidity("max",!1),q):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return T(a)|| -Ra(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),q)})},url:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);a=function(a){return T(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);a=function(a){return T(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){w(d.name)&&c.attr("name",ya());c.bind("click", -function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,h=d.ngFalseValue;A(g)||(g=!0);A(h)||(h=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:C,button:C,submit:C,reset:C}, -cc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,h){h&&(bc[y(g.type)]||bc.text)(d,e,g,h,c,a)}}}],Ma="ng-valid",La="ng-invalid",Oa="ng-pristine",Yb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function h(a,c){c=c?"-"+Za(c,"-"):"";e.removeClass((a?La:Ma)+c).addClass((a?Ma:La)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine= -!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),j=f.assign;if(!j)throw Error(Db+d.ngModel+" ("+qa(e)+")");this.$render=C;var i=e.inheritedData("$formController")||Na,k=0,m=this.$error={};e.addClass(Oa);h(!0);this.$setValidity=function(a,c){if(m[a]!==!c){if(c){if(m[a]&&k--,!k)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,k++;m[a]=!c;h(c,a);i.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty= -!0,this.$pristine=!1,e.removeClass(Oa).addClass(Yb),i.$setDirty();n(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,j(a,d),n(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var l=this;a.$watch(function(){var c=f(a);if(l.$modelValue!==c){var d=l.$formatters,e=d.length;for(l.$modelValue=c;e--;)c=d[e](c);if(l.$viewValue!==c)l.$viewValue=c,l.$render()}})}],qd=function(){return{require:["ngModel","^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],h= -e[1]||Na;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},rd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),dc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(T(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},sd=function(){return{require:"ngModel", -link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&n(a.split(g),function(a){a&&c.push(O(a))});return c});e.$formatters.push(function(a){return B(a)?a.join(", "):q})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},vd=Q(function(a,c,d){c.addClass("ng-binding").data("$binding", -d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==q?"":a)})}),wd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],yd=kb("",!0),zd=kb("Odd",0),Ad=kb("Even",1),Bd=Q({compile:function(a,c){c.$set("ngCloak",q); -a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],ec={};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=ea("ng-"+a);ec[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(y(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Ed=Q(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}), -Fd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,j=h.onload||"",i=h.autoscroll;return function(g,h){var l=0,n,o=function(){n&&(n.$destroy(),n=null);h.html("")};g.$watch(f,function(f){var s=++l;f?a.get(f,{cache:c}).success(function(a){s===l&&(n&&n.$destroy(),n=g.$new(),h.html(a),e(h.contents())(n),x(i)&&(!i||g.$eval(i))&&d(),n.$emit("$includeContentLoaded"),g.$eval(j))}).error(function(){s===l&& -o()}):o()})}}}}],Gd=Q({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Hd=Q({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,j=g.attr(h.$attr.when),i=h.offset||0,k=e.$eval(j),m={},l=c.startSymbol(),t=c.endSymbol();n(k,function(a,e){m[e]=c(a.replace(d,l+f+"-"+i+t))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(k[c]||(c=a.pluralCat(c-i)),m[c](e,g,!0))},function(a){g.text(a)})}}}], -Jd=Q({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),j,i,k;if(!h)throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=h[1];j=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!h)throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");i=h[3]||h[1];k=h[2];var m=new eb;a.$watch(function(a){var e,f,h=a.$eval(j), -n=c,q=new eb,x,z,u,w,r,v;if(B(h))r=h||[];else{r=[];for(u in h)h.hasOwnProperty(u)&&u.charAt(0)!="$"&&r.push(u);r.sort()}x=r.length;e=0;for(f=r.length;e<f;e++){u=h===r?e:r[e];w=h[u];if(v=m.shift(w)){z=v.scope;q.push(w,v);if(e!==v.index)v.index=e,n.after(v.element);n=v.element}else z=a.$new();z[i]=w;k&&(z[k]=u);z.$index=e;z.$first=e===0;z.$last=e===x-1;z.$middle=!(z.$first||z.$last);v||d(z,function(a){n.after(a);v={scope:z,element:n=a,index:e};q.push(w,v)})}for(u in m)if(m.hasOwnProperty(u))for(r=m[u];r.length;)w= -r.pop(),w.element.remove(),w.scope.$destroy();m=q})}}}),Kd=Q(function(a,c,d){a.$watch(d.ngShow,function(a){c.css("display",Va(a)?"":"none")})}),Ld=Q(function(a,c,d){a.$watch(d.ngHide,function(a){c.css("display",Va(a)?"none":"")})}),Md=Q(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&n(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Nd=I({restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(a,c,d,e){var g,h,f;a.$watch(d.ngSwitch||d.on,function(j){h&& -(f.$destroy(),h.remove(),h=f=null);if(g=e.cases["!"+j]||e.cases["?"])a.$eval(d.change),f=a.$new(),g(f,function(a){h=a;c.append(a)})})}}),Od=Q({transclude:"element",priority:500,require:"^ngSwitch",compile:function(a,c,d){return function(a,g,h,f){f.cases["!"+c.ngSwitchWhen]=d}}}),Pd=Q({transclude:"element",priority:500,require:"^ngSwitch",compile:function(a,c,d){return function(a,c,h,f){f.cases["?"]=d}}}),Qd=Q({controller:["$transclude","$element",function(a,c){a(function(a){c.append(a)})}]}),Rd=["$http", -"$templateCache","$route","$anchorScroll","$compile","$controller",function(a,c,d,e,g,h){return{restrict:"ECA",terminal:!0,link:function(a,c,i){function k(){var i=d.current&&d.current.locals,k=i&&i.$template;if(k){c.html(k);m&&(m.$destroy(),m=null);var k=g(c.contents()),n=d.current;m=n.scope=a.$new();if(n.controller)i.$scope=m,i=h(n.controller,i),c.children().data("$ngControllerController",i);k(m);m.$emit("$viewContentLoaded");m.$eval(l);e()}else c.html(""),m&&(m.$destroy(),m=null)}var m,l=i.onload|| -"";a.$on("$routeChangeSuccess",k);k()}}}],Sd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){d.type=="text/ng-template"&&a.put(d.id,c[0].text)}}}],Td=I({terminal:!0}),Ud=["$compile","$parse",function(a,c){var d=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,e={$setViewValue:C};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope", -"$attrs",function(a,c,d){var j=this,i={},k=e,m;j.databound=d.ngModel;j.init=function(a,c,d){k=a;m=d};j.addOption=function(c){i[c]=!0;k.$viewValue==c&&(a.val(c),m.parent()&&m.remove())};j.removeOption=function(a){this.hasOption(a)&&(delete i[a],k.$viewValue==a&&this.renderUnknownOption(a))};j.renderUnknownOption=function(c){c="? "+fa(c)+" ?";m.val(c);a.prepend(m);a.val(c);m.prop("selected",!0)};j.hasOption=function(a){return i.hasOwnProperty(a)};c.$on("$destroy",function(){j.renderUnknownOption=C})}], -link:function(e,h,f,j){function i(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(y.parent()&&y.remove(),c.val(a),a===""&&v.prop("selected",!0)):w(a)&&v?c.val(""):e.renderUnknownOption(a)};c.bind("change",function(){a.$apply(function(){y.parent()&&y.remove();d.$setViewValue(c.val())})})}function k(a,c,d){var e;d.$render=function(){var a=new Ga(d.$viewValue);n(c.find("option"),function(c){c.selected=x(a.get(c.value))})};a.$watch(function(){ga(e,d.$viewValue)||(e=U(d.$viewValue),d.$render())}); -c.bind("change",function(){a.$apply(function(){var a=[];n(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function m(e,f,g){function h(){var a={"":[]},c=[""],d,i,p,u,v;p=g.$modelValue;u=t(e)||[];var w=l?mb(u):u,x,y,A;y={};v=!1;var B,E;if(o)v=new Ga(p);else if(p===null||s)a[""].push({selected:p===null,id:"",label:""}),v=!0;for(A=0;x=w.length,A<x;A++){y[k]=u[l?y[l]=w[A]:A];d=m(e,y)||"";if(!(i=a[d]))i=a[d]=[],c.push(d);o?d=v.remove(n(e,y))!=q:(d=p===n(e,y),v=v||d);B= -j(e,y);B=B===q?"":B;i.push({id:l?w[A]:A,label:B,selected:d})}!o&&!v&&a[""].unshift({id:"?",label:"",selected:!0});y=0;for(w=c.length;y<w;y++){d=c[y];i=a[d];if(r.length<=y)p={element:z.clone().attr("label",d),label:i.label},u=[p],r.push(u),f.append(p.element);else if(u=r[y],p=u[0],p.label!=d)p.element.attr("label",p.label=d);B=null;A=0;for(x=i.length;A<x;A++)if(d=i[A],v=u[A+1]){B=v.element;if(v.label!==d.label)B.text(v.label=d.label);if(v.id!==d.id)B.val(v.id=d.id);if(v.element.selected!==d.selected)B.prop("selected", -v.selected=d.selected)}else d.id===""&&s?E=s:(E=C.clone()).val(d.id).attr("selected",d.selected).text(d.label),u.push({element:E,label:d.label,id:d.id,selected:d.selected}),B?B.after(E):p.element.append(E),B=E;for(A++;u.length>A;)u.pop().element.remove()}for(;r.length>y;)r.pop()[0].element.remove()}var i;if(!(i=p.match(d)))throw Error("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+p+"'.");var j=c(i[2]||i[1]),k=i[4]||i[6],l=i[5],m=c(i[3]||""), -n=c(i[2]?i[1]:k),t=c(i[7]),r=[[{element:f,label:""}]];s&&(a(s)(e),s.removeClass("ng-scope"),s.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=t(e)||[],d={},h,i,j,m,p,s;if(o){i=[];m=0;for(s=r.length;m<s;m++){a=r[m];j=1;for(p=a.length;j<p;j++)if((h=a[j].element)[0].selected)h=h.val(),l&&(d[l]=h),d[k]=c[h],i.push(n(e,d))}}else h=f.val(),h=="?"?i=q:h==""?i=null:(d[k]=c[h],l&&(d[l]=h),i=n(e,d));g.$setViewValue(i)})});g.$render=h;e.$watch(h)}if(j[1]){for(var l=j[0],t=j[1],o= -f.multiple,p=f.ngOptions,s=!1,v,C=u(Y.createElement("option")),z=u(Y.createElement("optgroup")),y=C.clone(),j=0,A=h.children(),r=A.length;j<r;j++)if(A[j].value==""){v=s=A.eq(j);break}l.init(t,s,y);if(o&&(f.required||f.ngRequired)){var B=function(a){t.$setValidity("required",!f.required||a&&a.length);return a};t.$parsers.push(B);t.$formatters.unshift(B);f.$observe("required",function(){B(t.$viewValue)})}p?m(e,h,t):o?k(e,h,t):i(e,h,t,l)}}}}],Vd=["$interpolate",function(a){var c={addOption:C,removeOption:C}; -return{restrict:"E",priority:100,compile:function(d,e){if(w(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var i=d.parent(),k=i.data("$selectController")||i.parent().data("$selectController");k&&k.databound?d.prop("selected",!1):k=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&k.removeOption(c);k.addOption(a)}):k.addOption(e.value);d.bind("$destroy",function(){k.removeOption(e.value)})}}}}],Wd=I({restrict:"E",terminal:!0});(ca=X.jQuery)?(u=ca,v(ca.fn,{scope:va.scope, -controller:va.controller,injector:va.injector,inheritedData:va.inheritedData}),ab("remove",!0),ab("empty"),ab("html")):u=L;Zb.element=u;(function(a){v(a,{bootstrap:qb,copy:U,extend:v,equals:ga,element:u,forEach:n,injector:rb,noop:C,bind:Ua,toJson:da,fromJson:ob,identity:na,isUndefined:w,isDefined:x,isString:A,isFunction:H,isObject:M,isNumber:Ra,isElement:gc,isArray:B,version:id,isDate:oa,lowercase:y,uppercase:ma,callbacks:{counter:0}});ta=lc(X);try{ta("ngLocale")}catch(c){ta("ngLocale",[]).provider("$locale", -Zc)}ta("ng",["ngLocale"],["$provide",function(a){a.provider("$compile",Cb).directive({a:jd,input:cc,textarea:cc,form:kd,script:Sd,select:Ud,style:Wd,option:Vd,ngBind:vd,ngBindHtmlUnsafe:xd,ngBindTemplate:wd,ngClass:yd,ngClassEven:Ad,ngClassOdd:zd,ngCsp:Dd,ngCloak:Bd,ngController:Cd,ngForm:ld,ngHide:Ld,ngInclude:Fd,ngInit:Gd,ngNonBindable:Hd,ngPluralize:Id,ngRepeat:Jd,ngShow:Kd,ngSubmit:Ed,ngStyle:Md,ngSwitch:Nd,ngSwitchWhen:Od,ngSwitchDefault:Pd,ngOptions:Td,ngView:Rd,ngTransclude:Qd,ngModel:qd,ngList:sd, -ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a.provider({$anchorScroll:uc,$browser:wc,$cacheFactory:xc,$controller:Bc,$document:Cc,$exceptionHandler:Dc,$filter:Qb,$interpolate:Ec,$http:Vc,$httpBackend:Wc,$location:Ic,$log:Jc,$parse:Nc,$route:Qc,$routeParams:Rc,$rootScope:Sc,$q:Oc,$sniffer:Tc,$templateCache:yc,$timeout:$c,$window:Uc})}])})(Zb);u(Y).ready(function(){jc(Y,qb)})})(window,document);angular.element(document).find("head").append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>'); diff --git a/pykeg/web/static/bootstrap/LICENSE b/pykeg/web/static/bootstrap/LICENSE deleted file mode 100644 index 2bb9ad240..000000000 --- a/pykeg/web/static/bootstrap/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/img/glyphicons-halflings-white.png b/pykeg/web/static/bootstrap/img/glyphicons-halflings-white.png deleted file mode 100644 index 3bf6484a2..000000000 Binary files a/pykeg/web/static/bootstrap/img/glyphicons-halflings-white.png and /dev/null differ diff --git a/pykeg/web/static/bootstrap/img/glyphicons-halflings.png b/pykeg/web/static/bootstrap/img/glyphicons-halflings.png deleted file mode 100644 index a99699932..000000000 Binary files a/pykeg/web/static/bootstrap/img/glyphicons-halflings.png and /dev/null differ diff --git a/pykeg/web/static/bootstrap/js/bootstrap.js b/pykeg/web/static/bootstrap/js/bootstrap.js deleted file mode 100644 index c298ee42e..000000000 --- a/pykeg/web/static/bootstrap/js/bootstrap.js +++ /dev/null @@ -1,2276 +0,0 @@ -/* =================================================== - * bootstrap-transition.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#transitions - * =================================================== - * Copyright 2012 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.1 - * http://twitter.github.com/bootstrap/javascript.html#alerts - * ========================================================== - * Copyright 2012 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.1 - * http://twitter.github.com/bootstrap/javascript.html#buttons - * ============================================================ - * Copyright 2012 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.1 - * http://twitter.github.com/bootstrap/javascript.html#carousel - * ========================================================== - * Copyright 2012 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.1 - * http://twitter.github.com/bootstrap/javascript.html#collapse - * ============================================================= - * Copyright 2012 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.1 - * http://twitter.github.com/bootstrap/javascript.html#dropdowns - * ============================================================ - * Copyright 2012 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) { - $parent.toggleClass('open') - } - - $this.focus() - - return false - } - - , keydown: function (e) { - var $this - , $items - , $active - , $parent - , isActive - , index - - if (!/(38|40|27)/.test(e.keyCode)) return - - $this = $(this) - - e.preventDefault() - e.stopPropagation() - - if ($this.is('.disabled, :disabled')) return - - $parent = getParent($this) - - isActive = $parent.hasClass('open') - - if (!isActive || (isActive && e.keyCode == 27)) { - if (e.which == 27) $parent.find(toggle).focus() - return $this.click() - } - - $items = $('[role=menu] li:not(.divider):visible a', $parent) - - if (!$items.length) return - - index = $items.index($items.filter(':focus')) - - if (e.keyCode == 38 && index > 0) index-- // up - if (e.keyCode == 40 && index < $items.length - 1) index++ // down - if (!~index) index = 0 - - $items - .eq(index) - .focus() - } - - } - - function clearMenus() { - $(toggle).each(function () { - getParent($(this)).removeClass('open') - }) - } - - function getParent($this) { - var selector = $this.attr('data-target') - , $parent - - if (!selector) { - selector = $this.attr('href') - selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - $parent = selector && $(selector) - - if (!$parent || !$parent.length) $parent = $this.parent() - - return $parent - } - - - /* DROPDOWN PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.dropdown - - $.fn.dropdown = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('dropdown') - if (!data) $this.data('dropdown', (data = new Dropdown(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - $.fn.dropdown.Constructor = Dropdown - - - /* DROPDOWN NO CONFLICT - * ==================== */ - - $.fn.dropdown.noConflict = function () { - $.fn.dropdown = old - return this - } - - - /* APPLY TO STANDARD DROPDOWN ELEMENTS - * =================================== */ - - $(document) - .on('click.dropdown.data-api', clearMenus) - .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) - .on('click.dropdown-menu', function (e) { e.stopPropagation() }) - .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle) - .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) - -}(window.jQuery); -/* ========================================================= - * bootstrap-modal.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#modals - * ========================================================= - * Copyright 2012 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 ;_; - - - /* MODAL CLASS DEFINITION - * ====================== */ - - var Modal = function (element, options) { - this.options = options - this.$element = $(element) - .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) - this.options.remote && this.$element.find('.modal-body').load(this.options.remote) - } - - Modal.prototype = { - - constructor: Modal - - , toggle: function () { - return this[!this.isShown ? 'show' : 'hide']() - } - - , show: function () { - var that = this - , e = $.Event('show') - - this.$element.trigger(e) - - if (this.isShown || e.isDefaultPrevented()) return - - this.isShown = true - - this.escape() - - this.backdrop(function () { - var transition = $.support.transition && that.$element.hasClass('fade') - - if (!that.$element.parent().length) { - that.$element.appendTo(document.body) //don't move modals dom position - } - - that.$element.show() - - if (transition) { - that.$element[0].offsetWidth // force reflow - } - - that.$element - .addClass('in') - .attr('aria-hidden', false) - - that.enforceFocus() - - transition ? - that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : - that.$element.focus().trigger('shown') - - }) - } - - , hide: function (e) { - e && e.preventDefault() - - var that = this - - e = $.Event('hide') - - this.$element.trigger(e) - - if (!this.isShown || e.isDefaultPrevented()) return - - this.isShown = false - - this.escape() - - $(document).off('focusin.modal') - - this.$element - .removeClass('in') - .attr('aria-hidden', true) - - $.support.transition && this.$element.hasClass('fade') ? - this.hideWithTransition() : - this.hideModal() - } - - , enforceFocus: function () { - var that = this - $(document).on('focusin.modal', function (e) { - if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { - that.$element.focus() - } - }) - } - - , escape: function () { - var that = this - if (this.isShown && this.options.keyboard) { - this.$element.on('keyup.dismiss.modal', function ( e ) { - e.which == 27 && that.hide() - }) - } else if (!this.isShown) { - this.$element.off('keyup.dismiss.modal') - } - } - - , hideWithTransition: function () { - var that = this - , timeout = setTimeout(function () { - that.$element.off($.support.transition.end) - that.hideModal() - }, 500) - - this.$element.one($.support.transition.end, function () { - clearTimeout(timeout) - that.hideModal() - }) - } - - , hideModal: function () { - var that = this - this.$element.hide() - this.backdrop(function () { - that.removeBackdrop() - that.$element.trigger('hidden') - }) - } - - , removeBackdrop: function () { - this.$backdrop && this.$backdrop.remove() - this.$backdrop = null - } - - , backdrop: function (callback) { - var that = this - , animate = this.$element.hasClass('fade') ? 'fade' : '' - - if (this.isShown && this.options.backdrop) { - var doAnimate = $.support.transition && animate - - this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') - .appendTo(document.body) - - this.$backdrop.click( - this.options.backdrop == 'static' ? - $.proxy(this.$element[0].focus, this.$element[0]) - : $.proxy(this.hide, this) - ) - - if (doAnimate) this.$backdrop[0].offsetWidth // force reflow - - this.$backdrop.addClass('in') - - if (!callback) return - - doAnimate ? - this.$backdrop.one($.support.transition.end, callback) : - callback() - - } else if (!this.isShown && this.$backdrop) { - this.$backdrop.removeClass('in') - - $.support.transition && this.$element.hasClass('fade')? - this.$backdrop.one($.support.transition.end, callback) : - callback() - - } else if (callback) { - callback() - } - } - } - - - /* MODAL PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.modal - - $.fn.modal = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('modal') - , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option) - if (!data) $this.data('modal', (data = new Modal(this, options))) - if (typeof option == 'string') data[option]() - else if (options.show) data.show() - }) - } - - $.fn.modal.defaults = { - backdrop: true - , keyboard: true - , show: true - } - - $.fn.modal.Constructor = Modal - - - /* MODAL NO CONFLICT - * ================= */ - - $.fn.modal.noConflict = function () { - $.fn.modal = old - return this - } - - - /* MODAL DATA-API - * ============== */ - - $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) { - var $this = $(this) - , href = $this.attr('href') - , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7 - , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data()) - - e.preventDefault() - - $target - .modal(option) - .one('hide', function () { - $this.focus() - }) - }) - -}(window.jQuery); -/* =========================================================== - * bootstrap-tooltip.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#tooltips - * Inspired by the original jQuery.tipsy by Jason Frame - * =========================================================== - * Copyright 2012 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 ;_; - - - /* TOOLTIP PUBLIC CLASS DEFINITION - * =============================== */ - - var Tooltip = function (element, options) { - this.init('tooltip', element, options) - } - - Tooltip.prototype = { - - constructor: Tooltip - - , init: function (type, element, options) { - var eventIn - , eventOut - , triggers - , trigger - , i - - this.type = type - this.$element = $(element) - this.options = this.getOptions(options) - this.enabled = true - - triggers = this.options.trigger.split(' ') - - for (i = triggers.length; i--;) { - trigger = triggers[i] - if (trigger == 'click') { - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) - } else if (trigger != 'manual') { - eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' - eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) - } - } - - this.options.selector ? - (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : - this.fixTitle() - } - - , getOptions: function (options) { - options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options) - - if (options.delay && typeof options.delay == 'number') { - options.delay = { - show: options.delay - , hide: options.delay - } - } - - return options - } - - , enter: function (e) { - var defaults = $.fn[this.type].defaults - , options = {} - , self - - this._options && $.each(this._options, function (key, value) { - if (defaults[key] != value) options[key] = value - }, this) - - self = $(e.currentTarget)[this.type](options).data(this.type) - - if (!self.options.delay || !self.options.delay.show) return self.show() - - clearTimeout(this.timeout) - self.hoverState = 'in' - this.timeout = setTimeout(function() { - if (self.hoverState == 'in') self.show() - }, self.options.delay.show) - } - - , leave: function (e) { - var self = $(e.currentTarget)[this.type](this._options).data(this.type) - - if (this.timeout) clearTimeout(this.timeout) - if (!self.options.delay || !self.options.delay.hide) return self.hide() - - self.hoverState = 'out' - this.timeout = setTimeout(function() { - if (self.hoverState == 'out') self.hide() - }, self.options.delay.hide) - } - - , show: function () { - var $tip - , pos - , actualWidth - , actualHeight - , placement - , tp - , e = $.Event('show') - - if (this.hasContent() && this.enabled) { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $tip = this.tip() - this.setContent() - - if (this.options.animation) { - $tip.addClass('fade') - } - - placement = typeof this.options.placement == 'function' ? - this.options.placement.call(this, $tip[0], this.$element[0]) : - this.options.placement - - $tip - .detach() - .css({ top: 0, left: 0, display: 'block' }) - - this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) - - pos = this.getPosition() - - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - - switch (placement) { - case 'bottom': - tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} - break - case 'top': - tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} - break - case 'left': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} - break - case 'right': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} - break - } - - this.applyPlacement(tp, placement) - this.$element.trigger('shown') - } - } - - , applyPlacement: function(offset, placement){ - var $tip = this.tip() - , width = $tip[0].offsetWidth - , height = $tip[0].offsetHeight - , actualWidth - , actualHeight - , delta - , replace - - $tip - .offset(offset) - .addClass(placement) - .addClass('in') - - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - - if (placement == 'top' && actualHeight != height) { - offset.top = offset.top + height - actualHeight - replace = true - } - - if (placement == 'bottom' || placement == 'top') { - delta = 0 - - if (offset.left < 0){ - delta = offset.left * -2 - offset.left = 0 - $tip.offset(offset) - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - } - - this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') - } else { - this.replaceArrow(actualHeight - height, actualHeight, 'top') - } - - if (replace) $tip.offset(offset) - } - - , replaceArrow: function(delta, dimension, position){ - this - .arrow() - .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '') - } - - , setContent: function () { - var $tip = this.tip() - , title = this.getTitle() - - $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) - $tip.removeClass('fade in top bottom left right') - } - - , hide: function () { - var that = this - , $tip = this.tip() - , e = $.Event('hide') - - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - - $tip.removeClass('in') - - function removeWithAnimation() { - var timeout = setTimeout(function () { - $tip.off($.support.transition.end).detach() - }, 500) - - $tip.one($.support.transition.end, function () { - clearTimeout(timeout) - $tip.detach() - }) - } - - $.support.transition && this.$tip.hasClass('fade') ? - removeWithAnimation() : - $tip.detach() - - this.$element.trigger('hidden') - - return this - } - - , fixTitle: function () { - var $e = this.$element - if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { - $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') - } - } - - , hasContent: function () { - return this.getTitle() - } - - , getPosition: function () { - var el = this.$element[0] - return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { - width: el.offsetWidth - , height: el.offsetHeight - }, this.$element.offset()) - } - - , getTitle: function () { - var title - , $e = this.$element - , o = this.options - - title = $e.attr('data-original-title') - || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) - - return title - } - - , tip: function () { - return this.$tip = this.$tip || $(this.options.template) - } - - , arrow: function(){ - return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") - } - - , validate: function () { - if (!this.$element[0].parentNode) { - this.hide() - this.$element = null - this.options = null - } - } - - , enable: function () { - this.enabled = true - } - - , disable: function () { - this.enabled = false - } - - , toggleEnabled: function () { - this.enabled = !this.enabled - } - - , toggle: function (e) { - var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this - self.tip().hasClass('in') ? self.hide() : self.show() - } - - , destroy: function () { - this.hide().$element.off('.' + this.type).removeData(this.type) - } - - } - - - /* TOOLTIP PLUGIN DEFINITION - * ========================= */ - - var old = $.fn.tooltip - - $.fn.tooltip = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('tooltip') - , options = typeof option == 'object' && option - if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.tooltip.Constructor = Tooltip - - $.fn.tooltip.defaults = { - animation: true - , placement: 'top' - , selector: false - , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' - , trigger: 'hover focus' - , title: '' - , delay: 0 - , html: false - , container: false - } - - - /* TOOLTIP NO CONFLICT - * =================== */ - - $.fn.tooltip.noConflict = function () { - $.fn.tooltip = old - return this - } - -}(window.jQuery); -/* =========================================================== - * bootstrap-popover.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#popovers - * =========================================================== - * Copyright 2012 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 ;_; - - - /* POPOVER PUBLIC CLASS DEFINITION - * =============================== */ - - var Popover = function (element, options) { - this.init('popover', element, options) - } - - - /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js - ========================================== */ - - Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { - - constructor: Popover - - , setContent: function () { - var $tip = this.tip() - , title = this.getTitle() - , content = this.getContent() - - $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) - $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) - - $tip.removeClass('fade top bottom left right in') - } - - , hasContent: function () { - return this.getTitle() || this.getContent() - } - - , getContent: function () { - var content - , $e = this.$element - , o = this.options - - content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) - || $e.attr('data-content') - - return content - } - - , tip: function () { - if (!this.$tip) { - this.$tip = $(this.options.template) - } - return this.$tip - } - - , destroy: function () { - this.hide().$element.off('.' + this.type).removeData(this.type) - } - - }) - - - /* POPOVER PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.popover - - $.fn.popover = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('popover') - , options = typeof option == 'object' && option - if (!data) $this.data('popover', (data = new Popover(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.popover.Constructor = Popover - - $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { - placement: 'right' - , trigger: 'click' - , content: '' - , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' - }) - - - /* POPOVER NO CONFLICT - * =================== */ - - $.fn.popover.noConflict = function () { - $.fn.popover = old - return this - } - -}(window.jQuery); -/* ============================================================= - * bootstrap-scrollspy.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#scrollspy - * ============================================================= - * Copyright 2012 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 ;_; - - - /* SCROLLSPY CLASS DEFINITION - * ========================== */ - - function ScrollSpy(element, options) { - var process = $.proxy(this.process, this) - , $element = $(element).is('body') ? $(window) : $(element) - , href - this.options = $.extend({}, $.fn.scrollspy.defaults, options) - this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process) - this.selector = (this.options.target - || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - || '') + ' .nav li > a' - this.$body = $('body') - this.refresh() - this.process() - } - - ScrollSpy.prototype = { - - constructor: ScrollSpy - - , refresh: function () { - var self = this - , $targets - - this.offsets = $([]) - this.targets = $([]) - - $targets = this.$body - .find(this.selector) - .map(function () { - var $el = $(this) - , href = $el.data('target') || $el.attr('href') - , $href = /^#\w/.test(href) && $(href) - return ( $href - && $href.length - && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null - }) - .sort(function (a, b) { return a[0] - b[0] }) - .each(function () { - self.offsets.push(this[0]) - self.targets.push(this[1]) - }) - } - - , process: function () { - var scrollTop = this.$scrollElement.scrollTop() + this.options.offset - , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight - , maxScroll = scrollHeight - this.$scrollElement.height() - , offsets = this.offsets - , targets = this.targets - , activeTarget = this.activeTarget - , i - - if (scrollTop >= maxScroll) { - return activeTarget != (i = targets.last()[0]) - && this.activate ( i ) - } - - for (i = offsets.length; i--;) { - activeTarget != targets[i] - && scrollTop >= offsets[i] - && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) - && this.activate( targets[i] ) - } - } - - , activate: function (target) { - var active - , selector - - this.activeTarget = target - - $(this.selector) - .parent('.active') - .removeClass('active') - - selector = this.selector - + '[data-target="' + target + '"],' - + this.selector + '[href="' + target + '"]' - - active = $(selector) - .parent('li') - .addClass('active') - - if (active.parent('.dropdown-menu').length) { - active = active.closest('li.dropdown').addClass('active') - } - - active.trigger('activate') - } - - } - - - /* SCROLLSPY PLUGIN DEFINITION - * =========================== */ - - var old = $.fn.scrollspy - - $.fn.scrollspy = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('scrollspy') - , options = typeof option == 'object' && option - if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.scrollspy.Constructor = ScrollSpy - - $.fn.scrollspy.defaults = { - offset: 10 - } - - - /* SCROLLSPY NO CONFLICT - * ===================== */ - - $.fn.scrollspy.noConflict = function () { - $.fn.scrollspy = old - return this - } - - - /* SCROLLSPY DATA-API - * ================== */ - - $(window).on('load', function () { - $('[data-spy="scroll"]').each(function () { - var $spy = $(this) - $spy.scrollspy($spy.data()) - }) - }) - -}(window.jQuery);/* ======================================================== - * bootstrap-tab.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#tabs - * ======================================================== - * Copyright 2012 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 ;_; - - - /* TAB CLASS DEFINITION - * ==================== */ - - var Tab = function (element) { - this.element = $(element) - } - - Tab.prototype = { - - constructor: Tab - - , show: function () { - var $this = this.element - , $ul = $this.closest('ul:not(.dropdown-menu)') - , selector = $this.attr('data-target') - , previous - , $target - , e - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - if ( $this.parent('li').hasClass('active') ) return - - previous = $ul.find('.active:last a')[0] - - e = $.Event('show', { - relatedTarget: previous - }) - - $this.trigger(e) - - if (e.isDefaultPrevented()) return - - $target = $(selector) - - this.activate($this.parent('li'), $ul) - this.activate($target, $target.parent(), function () { - $this.trigger({ - type: 'shown' - , relatedTarget: previous - }) - }) - } - - , activate: function ( element, container, callback) { - var $active = container.find('> .active') - , transition = callback - && $.support.transition - && $active.hasClass('fade') - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active') - - element.addClass('active') - - if (transition) { - element[0].offsetWidth // reflow for transition - element.addClass('in') - } else { - element.removeClass('fade') - } - - if ( element.parent('.dropdown-menu') ) { - element.closest('li.dropdown').addClass('active') - } - - callback && callback() - } - - transition ? - $active.one($.support.transition.end, next) : - next() - - $active.removeClass('in') - } - } - - - /* TAB PLUGIN DEFINITION - * ===================== */ - - var old = $.fn.tab - - $.fn.tab = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('tab') - if (!data) $this.data('tab', (data = new Tab(this))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.tab.Constructor = Tab - - - /* TAB NO CONFLICT - * =============== */ - - $.fn.tab.noConflict = function () { - $.fn.tab = old - return this - } - - - /* TAB DATA-API - * ============ */ - - $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { - e.preventDefault() - $(this).tab('show') - }) - -}(window.jQuery);/* ============================================================= - * bootstrap-typeahead.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#typeahead - * ============================================================= - * Copyright 2012 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 ;_; - - - /* TYPEAHEAD PUBLIC CLASS DEFINITION - * ================================= */ - - var Typeahead = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.typeahead.defaults, options) - this.matcher = this.options.matcher || this.matcher - this.sorter = this.options.sorter || this.sorter - this.highlighter = this.options.highlighter || this.highlighter - this.updater = this.options.updater || this.updater - this.source = this.options.source - this.$menu = $(this.options.menu) - this.shown = false - this.listen() - } - - Typeahead.prototype = { - - constructor: Typeahead - - , select: function () { - var val = this.$menu.find('.active').attr('data-value') - this.$element - .val(this.updater(val)) - .change() - return this.hide() - } - - , updater: function (item) { - return item - } - - , show: function () { - var pos = $.extend({}, this.$element.position(), { - height: this.$element[0].offsetHeight - }) - - this.$menu - .insertAfter(this.$element) - .css({ - top: pos.top + pos.height - , left: pos.left - }) - .show() - - this.shown = true - return this - } - - , hide: function () { - this.$menu.hide() - this.shown = false - return this - } - - , lookup: function (event) { - var items - - this.query = this.$element.val() - - if (!this.query || this.query.length < this.options.minLength) { - return this.shown ? this.hide() : this - } - - items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source - - return items ? this.process(items) : this - } - - , process: function (items) { - var that = this - - items = $.grep(items, function (item) { - return that.matcher(item) - }) - - items = this.sorter(items) - - if (!items.length) { - return this.shown ? this.hide() : this - } - - return this.render(items.slice(0, this.options.items)).show() - } - - , matcher: function (item) { - return ~item.toLowerCase().indexOf(this.query.toLowerCase()) - } - - , sorter: function (items) { - var beginswith = [] - , caseSensitive = [] - , caseInsensitive = [] - , item - - while (item = items.shift()) { - if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) - else if (~item.indexOf(this.query)) caseSensitive.push(item) - else caseInsensitive.push(item) - } - - return beginswith.concat(caseSensitive, caseInsensitive) - } - - , highlighter: function (item) { - var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') - return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { - return '<strong>' + match + '</strong>' - }) - } - - , render: function (items) { - var that = this - - items = $(items).map(function (i, item) { - i = $(that.options.item).attr('data-value', item) - i.find('a').html(that.highlighter(item)) - return i[0] - }) - - items.first().addClass('active') - this.$menu.html(items) - return this - } - - , next: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , next = active.next() - - if (!next.length) { - next = $(this.$menu.find('li')[0]) - } - - next.addClass('active') - } - - , prev: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , prev = active.prev() - - if (!prev.length) { - prev = this.$menu.find('li').last() - } - - prev.addClass('active') - } - - , listen: function () { - this.$element - .on('focus', $.proxy(this.focus, this)) - .on('blur', $.proxy(this.blur, this)) - .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', $.proxy(this.keyup, this)) - - if (this.eventSupported('keydown')) { - this.$element.on('keydown', $.proxy(this.keydown, this)) - } - - this.$menu - .on('click', $.proxy(this.click, this)) - .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) - .on('mouseleave', 'li', $.proxy(this.mouseleave, this)) - } - - , eventSupported: function(eventName) { - var isSupported = eventName in this.$element - if (!isSupported) { - this.$element.setAttribute(eventName, 'return;') - isSupported = typeof this.$element[eventName] === 'function' - } - return isSupported - } - - , move: function (e) { - if (!this.shown) return - - switch(e.keyCode) { - case 9: // tab - case 13: // enter - case 27: // escape - e.preventDefault() - break - - case 38: // up arrow - e.preventDefault() - this.prev() - break - - case 40: // down arrow - e.preventDefault() - this.next() - break - } - - e.stopPropagation() - } - - , keydown: function (e) { - this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]) - this.move(e) - } - - , keypress: function (e) { - if (this.suppressKeyPressRepeat) return - this.move(e) - } - - , keyup: function (e) { - switch(e.keyCode) { - case 40: // down arrow - case 38: // up arrow - case 16: // shift - case 17: // ctrl - case 18: // alt - break - - case 9: // tab - case 13: // enter - if (!this.shown) return - this.select() - break - - case 27: // escape - if (!this.shown) return - this.hide() - break - - default: - this.lookup() - } - - e.stopPropagation() - e.preventDefault() - } - - , focus: function (e) { - this.focused = true - } - - , blur: function (e) { - this.focused = false - if (!this.mousedover && this.shown) this.hide() - } - - , click: function (e) { - e.stopPropagation() - e.preventDefault() - this.select() - this.$element.focus() - } - - , mouseenter: function (e) { - this.mousedover = true - this.$menu.find('.active').removeClass('active') - $(e.currentTarget).addClass('active') - } - - , mouseleave: function (e) { - this.mousedover = false - if (!this.focused && this.shown) this.hide() - } - - } - - - /* TYPEAHEAD PLUGIN DEFINITION - * =========================== */ - - var old = $.fn.typeahead - - $.fn.typeahead = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('typeahead') - , options = typeof option == 'object' && option - if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.typeahead.defaults = { - source: [] - , items: 8 - , menu: '<ul class="typeahead dropdown-menu"></ul>' - , item: '<li><a href="#"></a></li>' - , minLength: 1 - } - - $.fn.typeahead.Constructor = Typeahead - - - /* TYPEAHEAD NO CONFLICT - * =================== */ - - $.fn.typeahead.noConflict = function () { - $.fn.typeahead = old - return this - } - - - /* TYPEAHEAD DATA-API - * ================== */ - - $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { - var $this = $(this) - if ($this.data('typeahead')) return - $this.typeahead($this.data()) - }) - -}(window.jQuery); -/* ========================================================== - * bootstrap-affix.js v2.3.1 - * http://twitter.github.com/bootstrap/javascript.html#affix - * ========================================================== - * Copyright 2012 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 ;_; - - - /* AFFIX CLASS DEFINITION - * ====================== */ - - var Affix = function (element, options) { - this.options = $.extend({}, $.fn.affix.defaults, options) - this.$window = $(window) - .on('scroll.affix.data-api', $.proxy(this.checkPosition, this)) - .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this)) - this.$element = $(element) - this.checkPosition() - } - - Affix.prototype.checkPosition = function () { - if (!this.$element.is(':visible')) return - - var scrollHeight = $(document).height() - , scrollTop = this.$window.scrollTop() - , position = this.$element.offset() - , offset = this.options.offset - , offsetBottom = offset.bottom - , offsetTop = offset.top - , reset = 'affix affix-top affix-bottom' - , affix - - if (typeof offset != 'object') offsetBottom = offsetTop = offset - if (typeof offsetTop == 'function') offsetTop = offset.top() - if (typeof offsetBottom == 'function') offsetBottom = offset.bottom() - - affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? - false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? - 'bottom' : offsetTop != null && scrollTop <= offsetTop ? - 'top' : false - - if (this.affixed === affix) return - - this.affixed = affix - this.unpin = affix == 'bottom' ? position.top - scrollTop : null - - this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : '')) - } - - - /* AFFIX PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.affix - - $.fn.affix = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('affix') - , options = typeof option == 'object' && option - if (!data) $this.data('affix', (data = new Affix(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.affix.Constructor = Affix - - $.fn.affix.defaults = { - offset: 0 - } - - - /* AFFIX NO CONFLICT - * ================= */ - - $.fn.affix.noConflict = function () { - $.fn.affix = old - return this - } - - - /* AFFIX DATA-API - * ============== */ - - $(window).on('load', function () { - $('[data-spy="affix"]').each(function () { - var $spy = $(this) - , data = $spy.data() - - data.offset = data.offset || {} - - data.offsetBottom && (data.offset.bottom = data.offsetBottom) - data.offsetTop && (data.offset.top = data.offsetTop) - - $spy.affix(data) - }) - }) - - -}(window.jQuery); \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/js/bootstrap.min.js b/pykeg/web/static/bootstrap/js/bootstrap.min.js deleted file mode 100644 index 95c5ac5ee..000000000 --- a/pykeg/web/static/bootstrap/js/bootstrap.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! -* Bootstrap.js by @fat & @mdo -* Copyright 2012 Twitter, Inc. -* http://www.apache.org/licenses/LICENSE-2.0.txt -*/ -!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/less/accordion.less b/pykeg/web/static/bootstrap/less/accordion.less deleted file mode 100644 index d63523bc8..000000000 --- a/pykeg/web/static/bootstrap/less/accordion.less +++ /dev/null @@ -1,34 +0,0 @@ -// -// Accordion -// -------------------------------------------------- - - -// Parent container -.accordion { - margin-bottom: @baseLineHeight; -} - -// Group == heading + body -.accordion-group { - margin-bottom: 2px; - border: 1px solid #e5e5e5; - .border-radius(@baseBorderRadius); -} -.accordion-heading { - border-bottom: 0; -} -.accordion-heading .accordion-toggle { - display: block; - padding: 8px 15px; -} - -// General toggle styles -.accordion-toggle { - cursor: pointer; -} - -// Inner needs the styles because you can't animate properly with any styles on the element -.accordion-inner { - padding: 9px 15px; - border-top: 1px solid #e5e5e5; -} diff --git a/pykeg/web/static/bootstrap/less/alerts.less b/pykeg/web/static/bootstrap/less/alerts.less deleted file mode 100644 index 0116b191b..000000000 --- a/pykeg/web/static/bootstrap/less/alerts.less +++ /dev/null @@ -1,79 +0,0 @@ -// -// Alerts -// -------------------------------------------------- - - -// Base styles -// ------------------------- - -.alert { - padding: 8px 35px 8px 14px; - margin-bottom: @baseLineHeight; - text-shadow: 0 1px 0 rgba(255,255,255,.5); - background-color: @warningBackground; - border: 1px solid @warningBorder; - .border-radius(@baseBorderRadius); -} -.alert, -.alert h4 { - // Specified for the h4 to prevent conflicts of changing @headingsColor - color: @warningText; -} -.alert h4 { - margin: 0; -} - -// Adjust close link position -.alert .close { - position: relative; - top: -2px; - right: -21px; - line-height: @baseLineHeight; -} - - -// Alternate styles -// ------------------------- - -.alert-success { - background-color: @successBackground; - border-color: @successBorder; - color: @successText; -} -.alert-success h4 { - color: @successText; -} -.alert-danger, -.alert-error { - background-color: @errorBackground; - border-color: @errorBorder; - color: @errorText; -} -.alert-danger h4, -.alert-error h4 { - color: @errorText; -} -.alert-info { - background-color: @infoBackground; - border-color: @infoBorder; - color: @infoText; -} -.alert-info h4 { - color: @infoText; -} - - -// Block alerts -// ------------------------- - -.alert-block { - padding-top: 14px; - padding-bottom: 14px; -} -.alert-block > p, -.alert-block > ul { - margin-bottom: 0; -} -.alert-block p + p { - margin-top: 5px; -} diff --git a/pykeg/web/static/bootstrap/less/bootstrap.less b/pykeg/web/static/bootstrap/less/bootstrap.less deleted file mode 100644 index b56327adc..000000000 --- a/pykeg/web/static/bootstrap/less/bootstrap.less +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * Bootstrap v2.3.1 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -// Core variables and mixins -@import "variables.less"; // Modify this for custom colors, font-sizes, etc -@import "mixins.less"; - -// CSS Reset -@import "reset.less"; - -// Grid system and page structure -@import "scaffolding.less"; -@import "grid.less"; -@import "layouts.less"; - -// Base CSS -@import "type.less"; -@import "code.less"; -@import "forms.less"; -@import "tables.less"; - -// Components: common -@import "sprites.less"; -@import "dropdowns.less"; -@import "wells.less"; -@import "component-animations.less"; -@import "close.less"; - -// Components: Buttons & Alerts -@import "buttons.less"; -@import "button-groups.less"; -@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less - -// Components: Nav -@import "navs.less"; -@import "navbar.less"; -@import "breadcrumbs.less"; -@import "pagination.less"; -@import "pager.less"; - -// Components: Popovers -@import "modals.less"; -@import "tooltip.less"; -@import "popovers.less"; - -// Components: Misc -@import "thumbnails.less"; -@import "media.less"; -@import "labels-badges.less"; -@import "progress-bars.less"; -@import "accordion.less"; -@import "carousel.less"; -@import "hero-unit.less"; - -// Utility classes -@import "utilities.less"; // Has to be last to override when necessary diff --git a/pykeg/web/static/bootstrap/less/breadcrumbs.less b/pykeg/web/static/bootstrap/less/breadcrumbs.less deleted file mode 100644 index f753df6be..000000000 --- a/pykeg/web/static/bootstrap/less/breadcrumbs.less +++ /dev/null @@ -1,24 +0,0 @@ -// -// Breadcrumbs -// -------------------------------------------------- - - -.breadcrumb { - padding: 8px 15px; - margin: 0 0 @baseLineHeight; - list-style: none; - background-color: #f5f5f5; - .border-radius(@baseBorderRadius); - > li { - display: inline-block; - .ie7-inline-block(); - text-shadow: 0 1px 0 @white; - > .divider { - padding: 0 5px; - color: #ccc; - } - } - > .active { - color: @grayLight; - } -} diff --git a/pykeg/web/static/bootstrap/less/button-groups.less b/pykeg/web/static/bootstrap/less/button-groups.less deleted file mode 100644 index 55cdc6033..000000000 --- a/pykeg/web/static/bootstrap/less/button-groups.less +++ /dev/null @@ -1,229 +0,0 @@ -// -// Button groups -// -------------------------------------------------- - - -// Make the div behave like a button -.btn-group { - position: relative; - display: inline-block; - .ie7-inline-block(); - font-size: 0; // remove as part 1 of font-size inline-block hack - vertical-align: middle; // match .btn alignment given font-size hack above - white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page) - .ie7-restore-left-whitespace(); -} - -// Space out series of button groups -.btn-group + .btn-group { - margin-left: 5px; -} - -// Optional: Group multiple button groups together for a toolbar -.btn-toolbar { - font-size: 0; // Hack to remove whitespace that results from using inline-block - margin-top: @baseLineHeight / 2; - margin-bottom: @baseLineHeight / 2; - > .btn + .btn, - > .btn-group + .btn, - > .btn + .btn-group { - margin-left: 5px; - } -} - -// Float them, remove border radius, then re-add to first and last elements -.btn-group > .btn { - position: relative; - .border-radius(0); -} -.btn-group > .btn + .btn { - margin-left: -1px; -} -.btn-group > .btn, -.btn-group > .dropdown-menu, -.btn-group > .popover { - font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack -} - -// Reset fonts for other sizes -.btn-group > .btn-mini { - font-size: @fontSizeMini; -} -.btn-group > .btn-small { - font-size: @fontSizeSmall; -} -.btn-group > .btn-large { - font-size: @fontSizeLarge; -} - -// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match -.btn-group > .btn:first-child { - margin-left: 0; - .border-top-left-radius(@baseBorderRadius); - .border-bottom-left-radius(@baseBorderRadius); -} -// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it -.btn-group > .btn:last-child, -.btn-group > .dropdown-toggle { - .border-top-right-radius(@baseBorderRadius); - .border-bottom-right-radius(@baseBorderRadius); -} -// Reset corners for large buttons -.btn-group > .btn.large:first-child { - margin-left: 0; - .border-top-left-radius(@borderRadiusLarge); - .border-bottom-left-radius(@borderRadiusLarge); -} -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - .border-top-right-radius(@borderRadiusLarge); - .border-bottom-right-radius(@borderRadiusLarge); -} - -// On hover/focus/active, bring the proper btn to front -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active { - z-index: 2; -} - -// On active and open, don't show outline -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} - - - -// Split button dropdowns -// ---------------------- - -// Give the line between buttons some depth -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; - .box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); - *padding-top: 5px; - *padding-bottom: 5px; -} -.btn-group > .btn-mini + .dropdown-toggle { - padding-left: 5px; - padding-right: 5px; - *padding-top: 2px; - *padding-bottom: 2px; -} -.btn-group > .btn-small + .dropdown-toggle { - *padding-top: 5px; - *padding-bottom: 4px; -} -.btn-group > .btn-large + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; - *padding-top: 7px; - *padding-bottom: 7px; -} - -.btn-group.open { - - // The clickable button for toggling the menu - // Remove the gradient and set the same inset shadow as the :active state - .dropdown-toggle { - background-image: none; - .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); - } - - // Keep the hover's background when dropdown is open - .btn.dropdown-toggle { - background-color: @btnBackgroundHighlight; - } - .btn-primary.dropdown-toggle { - background-color: @btnPrimaryBackgroundHighlight; - } - .btn-warning.dropdown-toggle { - background-color: @btnWarningBackgroundHighlight; - } - .btn-danger.dropdown-toggle { - background-color: @btnDangerBackgroundHighlight; - } - .btn-success.dropdown-toggle { - background-color: @btnSuccessBackgroundHighlight; - } - .btn-info.dropdown-toggle { - background-color: @btnInfoBackgroundHighlight; - } - .btn-inverse.dropdown-toggle { - background-color: @btnInverseBackgroundHighlight; - } -} - - -// Reposition the caret -.btn .caret { - margin-top: 8px; - margin-left: 0; -} -// Carets in other button sizes -.btn-large .caret { - margin-top: 6px; -} -.btn-large .caret { - border-left-width: 5px; - border-right-width: 5px; - border-top-width: 5px; -} -.btn-mini .caret, -.btn-small .caret { - margin-top: 8px; -} -// Upside down carets for .dropup -.dropup .btn-large .caret { - border-bottom-width: 5px; -} - - - -// Account for other colors -.btn-primary, -.btn-warning, -.btn-danger, -.btn-info, -.btn-success, -.btn-inverse { - .caret { - border-top-color: @white; - border-bottom-color: @white; - } -} - - - -// Vertical button groups -// ---------------------- - -.btn-group-vertical { - display: inline-block; // makes buttons only take up the width they need - .ie7-inline-block(); -} -.btn-group-vertical > .btn { - display: block; - float: none; - max-width: 100%; - .border-radius(0); -} -.btn-group-vertical > .btn + .btn { - margin-left: 0; - margin-top: -1px; -} -.btn-group-vertical > .btn:first-child { - .border-radius(@baseBorderRadius @baseBorderRadius 0 0); -} -.btn-group-vertical > .btn:last-child { - .border-radius(0 0 @baseBorderRadius @baseBorderRadius); -} -.btn-group-vertical > .btn-large:first-child { - .border-radius(@borderRadiusLarge @borderRadiusLarge 0 0); -} -.btn-group-vertical > .btn-large:last-child { - .border-radius(0 0 @borderRadiusLarge @borderRadiusLarge); -} diff --git a/pykeg/web/static/bootstrap/less/buttons.less b/pykeg/web/static/bootstrap/less/buttons.less deleted file mode 100644 index 4cd4d862b..000000000 --- a/pykeg/web/static/bootstrap/less/buttons.less +++ /dev/null @@ -1,228 +0,0 @@ -// -// Buttons -// -------------------------------------------------- - - -// Base styles -// -------------------------------------------------- - -// Core -.btn { - display: inline-block; - .ie7-inline-block(); - padding: 4px 12px; - margin-bottom: 0; // For input.btn - font-size: @baseFontSize; - line-height: @baseLineHeight; - text-align: center; - vertical-align: middle; - cursor: pointer; - .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); - border: 1px solid @btnBorder; - *border: 0; // Remove the border to prevent IE7's black border on input:focus - border-bottom-color: darken(@btnBorder, 10%); - .border-radius(@baseBorderRadius); - .ie7-restore-left-whitespace(); // Give IE7 some love - .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); - - // Hover/focus state - &:hover, - &:focus { - color: @grayDark; - text-decoration: none; - background-position: 0 -15px; - - // transition is only when going to hover/focus, otherwise the background - // behind the gradient (there for IE<=9 fallback) gets mismatched - .transition(background-position .1s linear); - } - - // Focus state for keyboard and accessibility - &:focus { - .tab-focus(); - } - - // Active state - &.active, - &:active { - background-image: none; - outline: 0; - .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); - } - - // Disabled state - &.disabled, - &[disabled] { - cursor: default; - background-image: none; - .opacity(65); - .box-shadow(none); - } - -} - - - -// Button Sizes -// -------------------------------------------------- - -// Large -.btn-large { - padding: @paddingLarge; - font-size: @fontSizeLarge; - .border-radius(@borderRadiusLarge); -} -.btn-large [class^="icon-"], -.btn-large [class*=" icon-"] { - margin-top: 4px; -} - -// Small -.btn-small { - padding: @paddingSmall; - font-size: @fontSizeSmall; - .border-radius(@borderRadiusSmall); -} -.btn-small [class^="icon-"], -.btn-small [class*=" icon-"] { - margin-top: 0; -} -.btn-mini [class^="icon-"], -.btn-mini [class*=" icon-"] { - margin-top: -1px; -} - -// Mini -.btn-mini { - padding: @paddingMini; - font-size: @fontSizeMini; - .border-radius(@borderRadiusSmall); -} - - -// Block button -// ------------------------- - -.btn-block { - display: block; - width: 100%; - padding-left: 0; - padding-right: 0; - .box-sizing(border-box); -} - -// Vertically space out multiple block buttons -.btn-block + .btn-block { - margin-top: 5px; -} - -// Specificity overrides -input[type="submit"], -input[type="reset"], -input[type="button"] { - &.btn-block { - width: 100%; - } -} - - - -// Alternate buttons -// -------------------------------------------------- - -// Provide *some* extra contrast for those who can get it -.btn-primary.active, -.btn-warning.active, -.btn-danger.active, -.btn-success.active, -.btn-info.active, -.btn-inverse.active { - color: rgba(255,255,255,.75); -} - -// Set the backgrounds -// ------------------------- -.btn-primary { - .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); -} -// Warning appears are orange -.btn-warning { - .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); -} -// Danger and error appear as red -.btn-danger { - .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); -} -// Success appears as green -.btn-success { - .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); -} -// Info appears as a neutral blue -.btn-info { - .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); -} -// Inverse appears as dark gray -.btn-inverse { - .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); -} - - -// Cross-browser Jank -// -------------------------------------------------- - -button.btn, -input[type="submit"].btn { - - // Firefox 3.6 only I believe - &::-moz-focus-inner { - padding: 0; - border: 0; - } - - // IE7 has some default padding on button controls - *padding-top: 3px; - *padding-bottom: 3px; - - &.btn-large { - *padding-top: 7px; - *padding-bottom: 7px; - } - &.btn-small { - *padding-top: 3px; - *padding-bottom: 3px; - } - &.btn-mini { - *padding-top: 1px; - *padding-bottom: 1px; - } -} - - -// Link buttons -// -------------------------------------------------- - -// Make a button look and behave like a link -.btn-link, -.btn-link:active, -.btn-link[disabled] { - background-color: transparent; - background-image: none; - .box-shadow(none); -} -.btn-link { - border-color: transparent; - cursor: pointer; - color: @linkColor; - .border-radius(0); -} -.btn-link:hover, -.btn-link:focus { - color: @linkColorHover; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -.btn-link[disabled]:focus { - color: @grayDark; - text-decoration: none; -} diff --git a/pykeg/web/static/bootstrap/less/carousel.less b/pykeg/web/static/bootstrap/less/carousel.less deleted file mode 100644 index 55bc05014..000000000 --- a/pykeg/web/static/bootstrap/less/carousel.less +++ /dev/null @@ -1,158 +0,0 @@ -// -// Carousel -// -------------------------------------------------- - - -.carousel { - position: relative; - margin-bottom: @baseLineHeight; - line-height: 1; -} - -.carousel-inner { - overflow: hidden; - width: 100%; - position: relative; -} - -.carousel-inner { - - > .item { - display: none; - position: relative; - .transition(.6s ease-in-out left); - - // Account for jankitude on images - > img, - > a > img { - display: block; - line-height: 1; - } - } - - > .active, - > .next, - > .prev { display: block; } - - > .active { - left: 0; - } - - > .next, - > .prev { - position: absolute; - top: 0; - width: 100%; - } - - > .next { - left: 100%; - } - > .prev { - left: -100%; - } - > .next.left, - > .prev.right { - left: 0; - } - - > .active.left { - left: -100%; - } - > .active.right { - left: 100%; - } - -} - -// Left/right controls for nav -// --------------------------- - -.carousel-control { - position: absolute; - top: 40%; - left: 15px; - width: 40px; - height: 40px; - margin-top: -20px; - font-size: 60px; - font-weight: 100; - line-height: 30px; - color: @white; - text-align: center; - background: @grayDarker; - border: 3px solid @white; - .border-radius(23px); - .opacity(50); - - // we can't have this transition here - // because webkit cancels the carousel - // animation if you trip this while - // in the middle of another animation - // ;_; - // .transition(opacity .2s linear); - - // Reposition the right one - &.right { - left: auto; - right: 15px; - } - - // Hover/focus state - &:hover, - &:focus { - color: @white; - text-decoration: none; - .opacity(90); - } -} - -// Carousel indicator pips -// ----------------------------- -.carousel-indicators { - position: absolute; - top: 15px; - right: 15px; - z-index: 5; - margin: 0; - list-style: none; - - li { - display: block; - float: left; - width: 10px; - height: 10px; - margin-left: 5px; - text-indent: -999px; - background-color: #ccc; - background-color: rgba(255,255,255,.25); - border-radius: 5px; - } - .active { - background-color: #fff; - } -} - -// Caption for text below images -// ----------------------------- - -.carousel-caption { - position: absolute; - left: 0; - right: 0; - bottom: 0; - padding: 15px; - background: @grayDark; - background: rgba(0,0,0,.75); -} -.carousel-caption h4, -.carousel-caption p { - color: @white; - line-height: @baseLineHeight; -} -.carousel-caption h4 { - margin: 0 0 5px; -} -.carousel-caption p { - margin-bottom: 0; -} diff --git a/pykeg/web/static/bootstrap/less/close.less b/pykeg/web/static/bootstrap/less/close.less deleted file mode 100644 index 4c626bda6..000000000 --- a/pykeg/web/static/bootstrap/less/close.less +++ /dev/null @@ -1,32 +0,0 @@ -// -// Close icons -// -------------------------------------------------- - - -.close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: @baseLineHeight; - color: @black; - text-shadow: 0 1px 0 rgba(255,255,255,1); - .opacity(20); - &:hover, - &:focus { - color: @black; - text-decoration: none; - cursor: pointer; - .opacity(40); - } -} - -// Additional properties for button version -// iOS requires the button element instead of an anchor tag. -// If you want the anchor version, it requires `href="#"`. -button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/less/code.less b/pykeg/web/static/bootstrap/less/code.less deleted file mode 100644 index 266a926e7..000000000 --- a/pykeg/web/static/bootstrap/less/code.less +++ /dev/null @@ -1,61 +0,0 @@ -// -// Code (inline and blocK) -// -------------------------------------------------- - - -// Inline and block code styles -code, -pre { - padding: 0 3px 2px; - #font > #family > .monospace; - font-size: @baseFontSize - 2; - color: @grayDark; - .border-radius(3px); -} - -// Inline code -code { - padding: 2px 4px; - color: #d14; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - white-space: nowrap; -} - -// Blocks of code -pre { - display: block; - padding: (@baseLineHeight - 1) / 2; - margin: 0 0 @baseLineHeight / 2; - font-size: @baseFontSize - 1; // 14px to 13px - line-height: @baseLineHeight; - word-break: break-all; - word-wrap: break-word; - white-space: pre; - white-space: pre-wrap; - background-color: #f5f5f5; - border: 1px solid #ccc; // fallback for IE7-8 - border: 1px solid rgba(0,0,0,.15); - .border-radius(@baseBorderRadius); - - // Make prettyprint styles more spaced out for readability - &.prettyprint { - margin-bottom: @baseLineHeight; - } - - // Account for some code outputs that place code tags in pre tags - code { - padding: 0; - color: inherit; - white-space: pre; - white-space: pre-wrap; - background-color: transparent; - border: 0; - } -} - -// Enable scrollable blocks of code -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/less/component-animations.less b/pykeg/web/static/bootstrap/less/component-animations.less deleted file mode 100644 index d614263a7..000000000 --- a/pykeg/web/static/bootstrap/less/component-animations.less +++ /dev/null @@ -1,22 +0,0 @@ -// -// Component animations -// -------------------------------------------------- - - -.fade { - opacity: 0; - .transition(opacity .15s linear); - &.in { - opacity: 1; - } -} - -.collapse { - position: relative; - height: 0; - overflow: hidden; - .transition(height .35s ease); - &.in { - height: auto; - } -} diff --git a/pykeg/web/static/bootstrap/less/dropdowns.less b/pykeg/web/static/bootstrap/less/dropdowns.less deleted file mode 100644 index bbfe3fd3e..000000000 --- a/pykeg/web/static/bootstrap/less/dropdowns.less +++ /dev/null @@ -1,237 +0,0 @@ -// -// Dropdown menus -// -------------------------------------------------- - - -// Use the .menu class on any <li> element within the topbar or ul.tabs and you'll get some superfancy dropdowns -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle { - // The caret makes the toggle a bit too tall in IE7 - *margin-bottom: -3px; -} -.dropdown-toggle:active, -.open .dropdown-toggle { - outline: 0; -} - -// Dropdown arrow/caret -// -------------------- -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid @black; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; -} - -// Place the caret -.dropdown .caret { - margin-top: 8px; - margin-left: 2px; -} - -// The dropdown menu (ul) -// ---------------------- -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: @zindexDropdown; - display: none; // none by default, but block on "open" of the menu - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; // override default ul - list-style: none; - background-color: @dropdownBackground; - border: 1px solid #ccc; // Fallback for IE7-8 - border: 1px solid @dropdownBorder; - *border-right-width: 2px; - *border-bottom-width: 2px; - .border-radius(6px); - .box-shadow(0 5px 10px rgba(0,0,0,.2)); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - - // Aligns the dropdown menu to right - &.pull-right { - right: 0; - left: auto; - } - - // Dividers (basically an hr) within the dropdown - .divider { - .nav-divider(@dropdownDividerTop, @dropdownDividerBottom); - } - - // Links within the dropdown menu - > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: @baseLineHeight; - color: @dropdownLinkColor; - white-space: nowrap; - } -} - -// Hover/Focus state -// ----------- -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - text-decoration: none; - color: @dropdownLinkColorHover; - #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); -} - -// Active state -// ------------ -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: @dropdownLinkColorActive; - text-decoration: none; - outline: 0; - #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%)); -} - -// Disabled state -// -------------- -// Gray out text and ensure the hover/focus state remains gray -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: @grayLight; -} -// Nuke hover/focus effects -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - background-image: none; // Remove CSS gradient - .reset-filter(); - cursor: default; -} - -// Open state for the dropdown -// --------------------------- -.open { - // IE7's z-index only goes to the nearest positioned ancestor, which would - // make the menu appear below buttons that appeared later on the page - *z-index: @zindexDropdown; - - & > .dropdown-menu { - display: block; - } -} - -// Right aligned dropdowns -// --------------------------- -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} - -// Allow for dropdowns to go bottom up (aka, dropup-menu) -// ------------------------------------------------------ -// Just add .dropup after the standard .dropdown class and you're set, bro. -// TODO: abstract this so that the navbar fixed styles are not placed here? -.dropup, -.navbar-fixed-bottom .dropdown { - // Reverse the caret - .caret { - border-top: 0; - border-bottom: 4px solid @black; - content: ""; - } - // Different positioning for bottom up menu - .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; - } -} - -// Sub menus -// --------------------------- -.dropdown-submenu { - position: relative; -} -// Default dropdowns -.dropdown-submenu > .dropdown-menu { - top: 0; - left: 100%; - margin-top: -6px; - margin-left: -1px; - .border-radius(0 6px 6px 6px); -} -.dropdown-submenu:hover > .dropdown-menu { - display: block; -} - -// Dropups -.dropup .dropdown-submenu > .dropdown-menu { - top: auto; - bottom: 0; - margin-top: 0; - margin-bottom: -2px; - .border-radius(5px 5px 5px 0); -} - -// Caret to indicate there is a submenu -.dropdown-submenu > a:after { - display: block; - content: " "; - float: right; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; - border-width: 5px 0 5px 5px; - border-left-color: darken(@dropdownBackground, 20%); - margin-top: 5px; - margin-right: -10px; -} -.dropdown-submenu:hover > a:after { - border-left-color: @dropdownLinkColorHover; -} - -// Left aligned submenus -.dropdown-submenu.pull-left { - // Undo the float - // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere. - float: none; - - // Positioning the submenu - > .dropdown-menu { - left: -100%; - margin-left: 10px; - .border-radius(6px 0 6px 6px); - } -} - -// Tweak nav headers -// ----------------- -// Increase padding from 15px to 20px on sides -.dropdown .dropdown-menu .nav-header { - padding-left: 20px; - padding-right: 20px; -} - -// Typeahead -// --------- -.typeahead { - z-index: 1051; - margin-top: 2px; // give it some space to breathe - .border-radius(@baseBorderRadius); -} diff --git a/pykeg/web/static/bootstrap/less/forms.less b/pykeg/web/static/bootstrap/less/forms.less deleted file mode 100644 index 06767bdd3..000000000 --- a/pykeg/web/static/bootstrap/less/forms.less +++ /dev/null @@ -1,690 +0,0 @@ -// -// Forms -// -------------------------------------------------- - - -// GENERAL STYLES -// -------------- - -// Make all forms have space below them -form { - margin: 0 0 @baseLineHeight; -} - -fieldset { - padding: 0; - margin: 0; - border: 0; -} - -// Groups of fields with labels on top (legends) -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: @baseLineHeight; - font-size: @baseFontSize * 1.5; - line-height: @baseLineHeight * 2; - color: @grayDark; - border: 0; - border-bottom: 1px solid #e5e5e5; - - // Small - small { - font-size: @baseLineHeight * .75; - color: @grayLight; - } -} - -// Set font for forms -label, -input, -button, -select, -textarea { - #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here -} -input, -button, -select, -textarea { - font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element) -} - -// Identify controls by their labels -label { - display: block; - margin-bottom: 5px; -} - -// Form controls -// ------------------------- - -// Shared size and type resets -select, -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - display: inline-block; - height: @baseLineHeight; - padding: 4px 6px; - margin-bottom: @baseLineHeight / 2; - font-size: @baseFontSize; - line-height: @baseLineHeight; - color: @gray; - .border-radius(@inputBorderRadius); - vertical-align: middle; -} - -// Reset appearance properties for textual inputs and textarea -// Declare width for legacy (can't be on input[type=*] selectors or it's too specific) -input, -textarea, -.uneditable-input { - width: 206px; // plus 12px padding and 2px border -} -// Reset height since textareas have rows -textarea { - height: auto; -} -// Everything else -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - background-color: @inputBackground; - border: 1px solid @inputBorder; - .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); - .transition(~"border linear .2s, box-shadow linear .2s"); - - // Focus state - &:focus { - border-color: rgba(82,168,236,.8); - outline: 0; - outline: thin dotted \9; /* IE6-9 */ - .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)"); - } -} - -// Position radios and checkboxes better -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - *margin-top: 0; /* IE7 */ - margin-top: 1px \9; /* IE8-9 */ - line-height: normal; -} - -// Reset width of input images, buttons, radios, checkboxes -input[type="file"], -input[type="image"], -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="radio"], -input[type="checkbox"] { - width: auto; // Override of generic input selector -} - -// Set the height of select and file controls to match text inputs -select, -input[type="file"] { - height: @inputHeight; /* In IE7, the height of the select element cannot be changed by height, only font-size */ - *margin-top: 4px; /* For IE7, add top margin to align select with labels */ - line-height: @inputHeight; -} - -// Make select elements obey height by applying a border -select { - width: 220px; // default input width + 10px of padding that doesn't get applied - border: 1px solid @inputBorder; - background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color -} - -// Make multiple select elements height not fixed -select[multiple], -select[size] { - height: auto; -} - -// Focus for select, file, radio, and checkbox -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - .tab-focus(); -} - - -// Uneditable inputs -// ------------------------- - -// Make uneditable inputs look inactive -.uneditable-input, -.uneditable-textarea { - color: @grayLight; - background-color: darken(@inputBackground, 1%); - border-color: @inputBorder; - .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); - cursor: not-allowed; -} - -// For text that needs to appear as an input but should not be an input -.uneditable-input { - overflow: hidden; // prevent text from wrapping, but still cut it off like an input does - white-space: nowrap; -} - -// Make uneditable textareas behave like a textarea -.uneditable-textarea { - width: auto; - height: auto; -} - - -// Placeholder -// ------------------------- - -// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn't understand a selector -input, -textarea { - .placeholder(); -} - - -// CHECKBOXES & RADIOS -// ------------------- - -// Indent the labels to position radios/checkboxes as hanging -.radio, -.checkbox { - min-height: @baseLineHeight; // clear the floating input if there is no label text - padding-left: 20px; -} -.radio input[type="radio"], -.checkbox input[type="checkbox"] { - float: left; - margin-left: -20px; -} - -// Move the options list down to align with labels -.controls > .radio:first-child, -.controls > .checkbox:first-child { - padding-top: 5px; // has to be padding because margin collaspes -} - -// Radios and checkboxes on same line -// TODO v3: Convert .inline to .control-inline -.radio.inline, -.checkbox.inline { - display: inline-block; - padding-top: 5px; - margin-bottom: 0; - vertical-align: middle; -} -.radio.inline + .radio.inline, -.checkbox.inline + .checkbox.inline { - margin-left: 10px; // space out consecutive inline controls -} - - - -// INPUT SIZES -// ----------- - -// General classes for quick sizes -.input-mini { width: 60px; } -.input-small { width: 90px; } -.input-medium { width: 150px; } -.input-large { width: 210px; } -.input-xlarge { width: 270px; } -.input-xxlarge { width: 530px; } - -// Grid style input sizes -input[class*="span"], -select[class*="span"], -textarea[class*="span"], -.uneditable-input[class*="span"], -// Redeclare since the fluid row class is more specific -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"] { - float: none; - margin-left: 0; -} -// Ensure input-prepend/append never wraps -.input-append input[class*="span"], -.input-append .uneditable-input[class*="span"], -.input-prepend input[class*="span"], -.input-prepend .uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"], -.row-fluid .input-prepend [class*="span"], -.row-fluid .input-append [class*="span"] { - display: inline-block; -} - - - -// GRID SIZING FOR INPUTS -// ---------------------- - -// Grid sizes -#grid > .input(@gridColumnWidth, @gridGutterWidth); - -// Control row for multiple inputs per line -.controls-row { - .clearfix(); // Clear the float from controls -} - -// Float to collapse white-space for proper grid alignment -.controls-row [class*="span"], -// Redeclare the fluid grid collapse since we undo the float for inputs -.row-fluid .controls-row [class*="span"] { - float: left; -} -// Explicity set top padding on all checkboxes/radios, not just first-child -.controls-row .checkbox[class*="span"], -.controls-row .radio[class*="span"] { - padding-top: 5px; -} - - - - -// DISABLED STATE -// -------------- - -// Disabled and read-only inputs -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - cursor: not-allowed; - background-color: @inputDisabledBackground; -} -// Explicitly reset the colors here -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][readonly], -input[type="checkbox"][readonly] { - background-color: transparent; -} - - - - -// FORM FIELD FEEDBACK STATES -// -------------------------- - -// Warning -.control-group.warning { - .formFieldState(@warningText, @warningText, @warningBackground); -} -// Error -.control-group.error { - .formFieldState(@errorText, @errorText, @errorBackground); -} -// Success -.control-group.success { - .formFieldState(@successText, @successText, @successBackground); -} -// Success -.control-group.info { - .formFieldState(@infoText, @infoText, @infoBackground); -} - -// HTML5 invalid states -// Shares styles with the .control-group.error above -input:focus:invalid, -textarea:focus:invalid, -select:focus:invalid { - color: #b94a48; - border-color: #ee5f5b; - &:focus { - border-color: darken(#ee5f5b, 10%); - @shadow: 0 0 6px lighten(#ee5f5b, 20%); - .box-shadow(@shadow); - } -} - - - -// FORM ACTIONS -// ------------ - -.form-actions { - padding: (@baseLineHeight - 1) 20px @baseLineHeight; - margin-top: @baseLineHeight; - margin-bottom: @baseLineHeight; - background-color: @formActionsBackground; - border-top: 1px solid #e5e5e5; - .clearfix(); // Adding clearfix to allow for .pull-right button containers -} - - - -// HELP TEXT -// --------- - -.help-block, -.help-inline { - color: lighten(@textColor, 15%); // lighten the text some for contrast -} - -.help-block { - display: block; // account for any element using help-block - margin-bottom: @baseLineHeight / 2; -} - -.help-inline { - display: inline-block; - .ie7-inline-block(); - vertical-align: middle; - padding-left: 5px; -} - - - -// INPUT GROUPS -// ------------ - -// Allow us to put symbols and text within the input field for a cleaner look -.input-append, -.input-prepend { - display: inline-block; - margin-bottom: @baseLineHeight / 2; - vertical-align: middle; - font-size: 0; // white space collapse hack - white-space: nowrap; // Prevent span and input from separating - - // Reset the white space collapse hack - input, - select, - .uneditable-input, - .dropdown-menu, - .popover { - font-size: @baseFontSize; - } - - input, - select, - .uneditable-input { - position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness - margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms - *margin-left: 0; - vertical-align: top; - .border-radius(0 @inputBorderRadius @inputBorderRadius 0); - // Make input on top when focused so blue border and shadow always show - &:focus { - z-index: 2; - } - } - .add-on { - display: inline-block; - width: auto; - height: @baseLineHeight; - min-width: 16px; - padding: 4px 5px; - font-size: @baseFontSize; - font-weight: normal; - line-height: @baseLineHeight; - text-align: center; - text-shadow: 0 1px 0 @white; - background-color: @grayLighter; - border: 1px solid #ccc; - } - .add-on, - .btn, - .btn-group > .dropdown-toggle { - vertical-align: top; - .border-radius(0); - } - .active { - background-color: lighten(@green, 30); - border-color: @green; - } -} - -.input-prepend { - .add-on, - .btn { - margin-right: -1px; - } - .add-on:first-child, - .btn:first-child { - // FYI, `.btn:first-child` accounts for a button group that's prepended - .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); - } -} - -.input-append { - input, - select, - .uneditable-input { - .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); - + .btn-group .btn:last-child { - .border-radius(0 @inputBorderRadius @inputBorderRadius 0); - } - } - .add-on, - .btn, - .btn-group { - margin-left: -1px; - } - .add-on:last-child, - .btn:last-child, - .btn-group:last-child > .dropdown-toggle { - .border-radius(0 @inputBorderRadius @inputBorderRadius 0); - } -} - -// Remove all border-radius for inputs with both prepend and append -.input-prepend.input-append { - input, - select, - .uneditable-input { - .border-radius(0); - + .btn-group .btn { - .border-radius(0 @inputBorderRadius @inputBorderRadius 0); - } - } - .add-on:first-child, - .btn:first-child { - margin-right: -1px; - .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); - } - .add-on:last-child, - .btn:last-child { - margin-left: -1px; - .border-radius(0 @inputBorderRadius @inputBorderRadius 0); - } - .btn-group:first-child { - margin-left: 0; - } -} - - - - -// SEARCH FORM -// ----------- - -input.search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ - margin-bottom: 0; // Remove the default margin on all inputs - .border-radius(15px); -} - -/* Allow for input prepend/append in search forms */ -.form-search .input-append .search-query, -.form-search .input-prepend .search-query { - .border-radius(0); // Override due to specificity -} -.form-search .input-append .search-query { - .border-radius(14px 0 0 14px); -} -.form-search .input-append .btn { - .border-radius(0 14px 14px 0); -} -.form-search .input-prepend .search-query { - .border-radius(0 14px 14px 0); -} -.form-search .input-prepend .btn { - .border-radius(14px 0 0 14px); -} - - - - -// HORIZONTAL & VERTICAL FORMS -// --------------------------- - -// Common properties -// ----------------- - -.form-search, -.form-inline, -.form-horizontal { - input, - textarea, - select, - .help-inline, - .uneditable-input, - .input-prepend, - .input-append { - display: inline-block; - .ie7-inline-block(); - margin-bottom: 0; - vertical-align: middle; - } - // Re-hide hidden elements due to specifity - .hide { - display: none; - } -} -.form-search label, -.form-inline label, -.form-search .btn-group, -.form-inline .btn-group { - display: inline-block; -} -// Remove margin for input-prepend/-append -.form-search .input-append, -.form-inline .input-append, -.form-search .input-prepend, -.form-inline .input-prepend { - margin-bottom: 0; -} -// Inline checkbox/radio labels (remove padding on left) -.form-search .radio, -.form-search .checkbox, -.form-inline .radio, -.form-inline .checkbox { - padding-left: 0; - margin-bottom: 0; - vertical-align: middle; -} -// Remove float and margin, set to inline-block -.form-search .radio input[type="radio"], -.form-search .checkbox input[type="checkbox"], -.form-inline .radio input[type="radio"], -.form-inline .checkbox input[type="checkbox"] { - float: left; - margin-right: 3px; - margin-left: 0; -} - - -// Margin to space out fieldsets -.control-group { - margin-bottom: @baseLineHeight / 2; -} - -// Legend collapses margin, so next element is responsible for spacing -legend + .control-group { - margin-top: @baseLineHeight; - -webkit-margin-top-collapse: separate; -} - -// Horizontal-specific styles -// -------------------------- - -.form-horizontal { - // Increase spacing between groups - .control-group { - margin-bottom: @baseLineHeight; - .clearfix(); - } - // Float the labels left - .control-label { - float: left; - width: @horizontalComponentOffset - 20; - padding-top: 5px; - text-align: right; - } - // Move over all input controls and content - .controls { - // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend - // don't inherit the margin of the parent, in this case .controls - *display: inline-block; - *padding-left: 20px; - margin-left: @horizontalComponentOffset; - *margin-left: 0; - &:first-child { - *padding-left: @horizontalComponentOffset; - } - } - // Remove bottom margin on block level help text since that's accounted for on .control-group - .help-block { - margin-bottom: 0; - } - // And apply it only to .help-block instances that follow a form control - input, - select, - textarea, - .uneditable-input, - .input-prepend, - .input-append { - + .help-block { - margin-top: @baseLineHeight / 2; - } - } - // Move over buttons in .form-actions to align with .controls - .form-actions { - padding-left: @horizontalComponentOffset; - } -} diff --git a/pykeg/web/static/bootstrap/less/grid.less b/pykeg/web/static/bootstrap/less/grid.less deleted file mode 100644 index 750d20351..000000000 --- a/pykeg/web/static/bootstrap/less/grid.less +++ /dev/null @@ -1,21 +0,0 @@ -// -// Grid system -// -------------------------------------------------- - - -// Fixed (940px) -#grid > .core(@gridColumnWidth, @gridGutterWidth); - -// Fluid (940px) -#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); - -// Reset utility classes due to specificity -[class*="span"].hide, -.row-fluid [class*="span"].hide { - display: none; -} - -[class*="span"].pull-right, -.row-fluid [class*="span"].pull-right { - float: right; -} diff --git a/pykeg/web/static/bootstrap/less/hero-unit.less b/pykeg/web/static/bootstrap/less/hero-unit.less deleted file mode 100644 index 763d86aee..000000000 --- a/pykeg/web/static/bootstrap/less/hero-unit.less +++ /dev/null @@ -1,25 +0,0 @@ -// -// Hero unit -// -------------------------------------------------- - - -.hero-unit { - padding: 60px; - margin-bottom: 30px; - font-size: 18px; - font-weight: 200; - line-height: @baseLineHeight * 1.5; - color: @heroUnitLeadColor; - background-color: @heroUnitBackground; - .border-radius(6px); - h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - color: @heroUnitHeadingColor; - letter-spacing: -1px; - } - li { - line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less - } -} diff --git a/pykeg/web/static/bootstrap/less/labels-badges.less b/pykeg/web/static/bootstrap/less/labels-badges.less deleted file mode 100644 index bc321fe5c..000000000 --- a/pykeg/web/static/bootstrap/less/labels-badges.less +++ /dev/null @@ -1,84 +0,0 @@ -// -// Labels and badges -// -------------------------------------------------- - - -// Base classes -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: @baseFontSize * .846; - font-weight: bold; - line-height: 14px; // ensure proper line-height if floated - color: @white; - vertical-align: baseline; - white-space: nowrap; - text-shadow: 0 -1px 0 rgba(0,0,0,.25); - background-color: @grayLight; -} -// Set unique padding and border-radii -.label { - .border-radius(3px); -} -.badge { - padding-left: 9px; - padding-right: 9px; - .border-radius(9px); -} - -// Empty labels/badges collapse -.label, -.badge { - &:empty { - display: none; - } -} - -// Hover/focus state, but only for links -a { - &.label:hover, - &.label:focus, - &.badge:hover, - &.badge:focus { - color: @white; - text-decoration: none; - cursor: pointer; - } -} - -// Colors -// Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) -.label, -.badge { - // Important (red) - &-important { background-color: @errorText; } - &-important[href] { background-color: darken(@errorText, 10%); } - // Warnings (orange) - &-warning { background-color: @orange; } - &-warning[href] { background-color: darken(@orange, 10%); } - // Success (green) - &-success { background-color: @successText; } - &-success[href] { background-color: darken(@successText, 10%); } - // Info (turquoise) - &-info { background-color: @infoText; } - &-info[href] { background-color: darken(@infoText, 10%); } - // Inverse (black) - &-inverse { background-color: @grayDark; } - &-inverse[href] { background-color: darken(@grayDark, 10%); } -} - -// Quick fix for labels/badges in buttons -.btn { - .label, - .badge { - position: relative; - top: -1px; - } -} -.btn-mini { - .label, - .badge { - top: 0; - } -} diff --git a/pykeg/web/static/bootstrap/less/layouts.less b/pykeg/web/static/bootstrap/less/layouts.less deleted file mode 100644 index 24a206211..000000000 --- a/pykeg/web/static/bootstrap/less/layouts.less +++ /dev/null @@ -1,16 +0,0 @@ -// -// Layouts -// -------------------------------------------------- - - -// Container (centered, fixed-width layouts) -.container { - .container-fixed(); -} - -// Fluid layouts (left aligned, with sidebar, min- & max-width content) -.container-fluid { - padding-right: @gridGutterWidth; - padding-left: @gridGutterWidth; - .clearfix(); -} \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/less/media.less b/pykeg/web/static/bootstrap/less/media.less deleted file mode 100644 index e461e446d..000000000 --- a/pykeg/web/static/bootstrap/less/media.less +++ /dev/null @@ -1,55 +0,0 @@ -// Media objects -// Source: http://stubbornella.org/content/?p=497 -// -------------------------------------------------- - - -// Common styles -// ------------------------- - -// Clear the floats -.media, -.media-body { - overflow: hidden; - *overflow: visible; - zoom: 1; -} - -// Proper spacing between instances of .media -.media, -.media .media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} - -// For images and videos, set to block -.media-object { - display: block; -} - -// Reset margins on headings for tighter default spacing -.media-heading { - margin: 0 0 5px; -} - - -// Media image alignment -// ------------------------- - -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-right { - margin-left: 10px; -} - - -// Media list variation -// ------------------------- - -// Undo default ul/ol styles -.media-list { - margin-left: 0; - list-style: none; -} diff --git a/pykeg/web/static/bootstrap/less/mixins.less b/pykeg/web/static/bootstrap/less/mixins.less deleted file mode 100644 index 79d889219..000000000 --- a/pykeg/web/static/bootstrap/less/mixins.less +++ /dev/null @@ -1,702 +0,0 @@ -// -// Mixins -// -------------------------------------------------- - - -// UTILITY MIXINS -// -------------------------------------------------- - -// Clearfix -// -------- -// For clearing floats like a boss h5bp.com/q -.clearfix { - *zoom: 1; - &:before, - &:after { - display: table; - content: ""; - // Fixes Opera/contenteditable bug: - // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952 - line-height: 0; - } - &:after { - clear: both; - } -} - -// Webkit-style focus -// ------------------ -.tab-focus() { - // Default - outline: thin dotted #333; - // Webkit - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -// Center-align a block level element -// ---------------------------------- -.center-block() { - display: block; - margin-left: auto; - margin-right: auto; -} - -// IE7 inline-block -// ---------------- -.ie7-inline-block() { - *display: inline; /* IE7 inline-block hack */ - *zoom: 1; -} - -// IE7 likes to collapse whitespace on either side of the inline-block elements. -// Ems because we're attempting to match the width of a space character. Left -// version is for form buttons, which typically come after other elements, and -// right version is for icons, which come before. Applying both is ok, but it will -// mean that space between those elements will be .6em (~2 space characters) in IE7, -// instead of the 1 space in other browsers. -.ie7-restore-left-whitespace() { - *margin-left: .3em; - - &:first-child { - *margin-left: 0; - } -} - -.ie7-restore-right-whitespace() { - *margin-right: .3em; -} - -// Sizing shortcuts -// ------------------------- -.size(@height, @width) { - width: @width; - height: @height; -} -.square(@size) { - .size(@size, @size); -} - -// Placeholder text -// ------------------------- -.placeholder(@color: @placeholderText) { - &:-moz-placeholder { - color: @color; - } - &:-ms-input-placeholder { - color: @color; - } - &::-webkit-input-placeholder { - color: @color; - } -} - -// Text overflow -// ------------------------- -// Requires inline-block or block for proper styling -.text-overflow() { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -// CSS image replacement -// ------------------------- -// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - - -// FONTS -// -------------------------------------------------- - -#font { - #family { - .serif() { - font-family: @serifFontFamily; - } - .sans-serif() { - font-family: @sansFontFamily; - } - .monospace() { - font-family: @monoFontFamily; - } - } - .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { - font-size: @size; - font-weight: @weight; - line-height: @lineHeight; - } - .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { - #font > #family > .serif; - #font > .shorthand(@size, @weight, @lineHeight); - } - .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { - #font > #family > .sans-serif; - #font > .shorthand(@size, @weight, @lineHeight); - } - .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { - #font > #family > .monospace; - #font > .shorthand(@size, @weight, @lineHeight); - } -} - - -// FORMS -// -------------------------------------------------- - -// Block level inputs -.input-block-level { - display: block; - width: 100%; - min-height: @inputHeight; // Make inputs at least the height of their button counterpart (base line-height + padding + border) - .box-sizing(border-box); // Makes inputs behave like true block-level elements -} - - - -// Mixin for form field states -.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { - // Set the text color - .control-label, - .help-block, - .help-inline { - color: @textColor; - } - // Style inputs accordingly - .checkbox, - .radio, - input, - select, - textarea { - color: @textColor; - } - input, - select, - textarea { - border-color: @borderColor; - .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work - &:focus { - border-color: darken(@borderColor, 10%); - @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%); - .box-shadow(@shadow); - } - } - // Give a small background color for input-prepend/-append - .input-prepend .add-on, - .input-append .add-on { - color: @textColor; - background-color: @backgroundColor; - border-color: @textColor; - } -} - - - -// CSS3 PROPERTIES -// -------------------------------------------------- - -// Border Radius -.border-radius(@radius) { - -webkit-border-radius: @radius; - -moz-border-radius: @radius; - border-radius: @radius; -} - -// Single Corner Border Radius -.border-top-left-radius(@radius) { - -webkit-border-top-left-radius: @radius; - -moz-border-radius-topleft: @radius; - border-top-left-radius: @radius; -} -.border-top-right-radius(@radius) { - -webkit-border-top-right-radius: @radius; - -moz-border-radius-topright: @radius; - border-top-right-radius: @radius; -} -.border-bottom-right-radius(@radius) { - -webkit-border-bottom-right-radius: @radius; - -moz-border-radius-bottomright: @radius; - border-bottom-right-radius: @radius; -} -.border-bottom-left-radius(@radius) { - -webkit-border-bottom-left-radius: @radius; - -moz-border-radius-bottomleft: @radius; - border-bottom-left-radius: @radius; -} - -// Single Side Border Radius -.border-top-radius(@radius) { - .border-top-right-radius(@radius); - .border-top-left-radius(@radius); -} -.border-right-radius(@radius) { - .border-top-right-radius(@radius); - .border-bottom-right-radius(@radius); -} -.border-bottom-radius(@radius) { - .border-bottom-right-radius(@radius); - .border-bottom-left-radius(@radius); -} -.border-left-radius(@radius) { - .border-top-left-radius(@radius); - .border-bottom-left-radius(@radius); -} - -// Drop shadows -.box-shadow(@shadow) { - -webkit-box-shadow: @shadow; - -moz-box-shadow: @shadow; - box-shadow: @shadow; -} - -// Transitions -.transition(@transition) { - -webkit-transition: @transition; - -moz-transition: @transition; - -o-transition: @transition; - transition: @transition; -} -.transition-delay(@transition-delay) { - -webkit-transition-delay: @transition-delay; - -moz-transition-delay: @transition-delay; - -o-transition-delay: @transition-delay; - transition-delay: @transition-delay; -} -.transition-duration(@transition-duration) { - -webkit-transition-duration: @transition-duration; - -moz-transition-duration: @transition-duration; - -o-transition-duration: @transition-duration; - transition-duration: @transition-duration; -} - -// Transformations -.rotate(@degrees) { - -webkit-transform: rotate(@degrees); - -moz-transform: rotate(@degrees); - -ms-transform: rotate(@degrees); - -o-transform: rotate(@degrees); - transform: rotate(@degrees); -} -.scale(@ratio) { - -webkit-transform: scale(@ratio); - -moz-transform: scale(@ratio); - -ms-transform: scale(@ratio); - -o-transform: scale(@ratio); - transform: scale(@ratio); -} -.translate(@x, @y) { - -webkit-transform: translate(@x, @y); - -moz-transform: translate(@x, @y); - -ms-transform: translate(@x, @y); - -o-transform: translate(@x, @y); - transform: translate(@x, @y); -} -.skew(@x, @y) { - -webkit-transform: skew(@x, @y); - -moz-transform: skew(@x, @y); - -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twitter/bootstrap/issues/4885 - -o-transform: skew(@x, @y); - transform: skew(@x, @y); - -webkit-backface-visibility: hidden; // See https://github.com/twitter/bootstrap/issues/5319 -} -.translate3d(@x, @y, @z) { - -webkit-transform: translate3d(@x, @y, @z); - -moz-transform: translate3d(@x, @y, @z); - -o-transform: translate3d(@x, @y, @z); - transform: translate3d(@x, @y, @z); -} - -// Backface visibility -// Prevent browsers from flickering when using CSS 3D transforms. -// Default value is `visible`, but can be changed to `hidden -// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples -.backface-visibility(@visibility){ - -webkit-backface-visibility: @visibility; - -moz-backface-visibility: @visibility; - backface-visibility: @visibility; -} - -// Background clipping -// Heads up: FF 3.6 and under need "padding" instead of "padding-box" -.background-clip(@clip) { - -webkit-background-clip: @clip; - -moz-background-clip: @clip; - background-clip: @clip; -} - -// Background sizing -.background-size(@size) { - -webkit-background-size: @size; - -moz-background-size: @size; - -o-background-size: @size; - background-size: @size; -} - - -// Box sizing -.box-sizing(@boxmodel) { - -webkit-box-sizing: @boxmodel; - -moz-box-sizing: @boxmodel; - box-sizing: @boxmodel; -} - -// User select -// For selecting text on the page -.user-select(@select) { - -webkit-user-select: @select; - -moz-user-select: @select; - -ms-user-select: @select; - -o-user-select: @select; - user-select: @select; -} - -// Resize anything -.resizable(@direction) { - resize: @direction; // Options: horizontal, vertical, both - overflow: auto; // Safari fix -} - -// CSS3 Content Columns -.content-columns(@columnCount, @columnGap: @gridGutterWidth) { - -webkit-column-count: @columnCount; - -moz-column-count: @columnCount; - column-count: @columnCount; - -webkit-column-gap: @columnGap; - -moz-column-gap: @columnGap; - column-gap: @columnGap; -} - -// Optional hyphenation -.hyphens(@mode: auto) { - word-wrap: break-word; - -webkit-hyphens: @mode; - -moz-hyphens: @mode; - -ms-hyphens: @mode; - -o-hyphens: @mode; - hyphens: @mode; -} - -// Opacity -.opacity(@opacity) { - opacity: @opacity / 100; - filter: ~"alpha(opacity=@{opacity})"; -} - - - -// BACKGROUNDS -// -------------------------------------------------- - -// Add an alphatransparency value to any background or border color (via Elyse Holladay) -#translucent { - .background(@color: @white, @alpha: 1) { - background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); - } - .border(@color: @white, @alpha: 1) { - border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); - .background-clip(padding-box); - } -} - -// Gradient Bar Colors for buttons and alerts -.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { - color: @textColor; - text-shadow: @textShadow; - #gradient > .vertical(@primaryColor, @secondaryColor); - border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); - border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); -} - -// Gradients -#gradient { - .horizontal(@startColor: #555, @endColor: #333) { - background-color: @endColor; - background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ - background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ - background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 - background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 - background-repeat: repeat-x; - filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down - } - .vertical(@startColor: #555, @endColor: #333) { - background-color: mix(@startColor, @endColor, 60%); - background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ - background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ - background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 - background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 - background-repeat: repeat-x; - filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down - } - .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { - background-color: @endColor; - background-repeat: repeat-x; - background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ - background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ - background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 - background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 - } - .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { - background-color: mix(@midColor, @endColor, 80%); - background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); - background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); - background-image: -moz-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); - background-image: -o-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); - background-image: linear-gradient(to right, @startColor, @midColor @colorStop, @endColor); - background-repeat: no-repeat; - filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback - } - - .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { - background-color: mix(@midColor, @endColor, 80%); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); - background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); - background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); - background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); - background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); - background-repeat: no-repeat; - filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback - } - .radial(@innerColor: #555, @outerColor: #333) { - background-color: @outerColor; - background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); - background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); - background-image: -moz-radial-gradient(circle, @innerColor, @outerColor); - background-image: -o-radial-gradient(circle, @innerColor, @outerColor); - background-repeat: no-repeat; - } - .striped(@color: #555, @angle: 45deg) { - background-color: @color; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); - } -} -// Reset filters for IE -.reset-filter() { - filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); -} - - - -// COMPONENT MIXINS -// -------------------------------------------------- - -// Horizontal dividers -// ------------------------- -// Dividers (basically an hr) within dropdowns and nav lists -.nav-divider(@top: #e5e5e5, @bottom: @white) { - // IE7 needs a set width since we gave a height. Restricting just - // to IE7 to keep the 1px left/right space in other browsers. - // It is unclear where IE is getting the extra space that we need - // to negative-margin away, but so it goes. - *width: 100%; - height: 1px; - margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px - *margin: -5px 0 5px; - overflow: hidden; - background-color: @top; - border-bottom: 1px solid @bottom; -} - -// Button backgrounds -// ------------------ -.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { - // gradientBar will set the background to a pleasing blend of these, to support IE<=9 - .gradientBar(@startColor, @endColor, @textColor, @textShadow); - *background-color: @endColor; /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - .reset-filter(); - - // in these cases the gradient won't cover the background, so we override - &:hover, &:focus, &:active, &.active, &.disabled, &[disabled] { - color: @textColor; - background-color: @endColor; - *background-color: darken(@endColor, 5%); - } - - // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves - &:active, - &.active { - background-color: darken(@endColor, 10%) e("\9"); - } -} - -// Navbar vertical align -// ------------------------- -// Vertically center elements in the navbar. -// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin. -.navbarVerticalAlign(@elementHeight) { - margin-top: (@navbarHeight - @elementHeight) / 2; -} - - - -// Grid System -// ----------- - -// Centered container element -.container-fixed() { - margin-right: auto; - margin-left: auto; - .clearfix(); -} - -// Table columns -.tableColumns(@columnSpan: 1) { - float: none; // undo default grid column styles - width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells - margin-left: 0; // undo default grid column styles -} - -// Make a Grid -// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior -.makeRow() { - margin-left: @gridGutterWidth * -1; - .clearfix(); -} -.makeColumn(@columns: 1, @offset: 0) { - float: left; - margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2); - width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); -} - -// The Grid -#grid { - - .core (@gridColumnWidth, @gridGutterWidth) { - - .spanX (@index) when (@index > 0) { - .span@{index} { .span(@index); } - .spanX(@index - 1); - } - .spanX (0) {} - - .offsetX (@index) when (@index > 0) { - .offset@{index} { .offset(@index); } - .offsetX(@index - 1); - } - .offsetX (0) {} - - .offset (@columns) { - margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1)); - } - - .span (@columns) { - width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); - } - - .row { - margin-left: @gridGutterWidth * -1; - .clearfix(); - } - - [class*="span"] { - float: left; - min-height: 1px; // prevent collapsing columns - margin-left: @gridGutterWidth; - } - - // Set the container width, and override it for fixed navbars in media queries - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { .span(@gridColumns); } - - // generate .spanX and .offsetX - .spanX (@gridColumns); - .offsetX (@gridColumns); - - } - - .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) { - - .spanX (@index) when (@index > 0) { - .span@{index} { .span(@index); } - .spanX(@index - 1); - } - .spanX (0) {} - - .offsetX (@index) when (@index > 0) { - .offset@{index} { .offset(@index); } - .offset@{index}:first-child { .offsetFirstChild(@index); } - .offsetX(@index - 1); - } - .offsetX (0) {} - - .offset (@columns) { - margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2); - *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%); - } - - .offsetFirstChild (@columns) { - margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth); - *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); - } - - .span (@columns) { - width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)); - *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%); - } - - .row-fluid { - width: 100%; - .clearfix(); - [class*="span"] { - .input-block-level(); - float: left; - margin-left: @fluidGridGutterWidth; - *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); - } - [class*="span"]:first-child { - margin-left: 0; - } - - // Space grid-sized controls properly if multiple per line - .controls-row [class*="span"] + [class*="span"] { - margin-left: @fluidGridGutterWidth; - } - - // generate .spanX and .offsetX - .spanX (@gridColumns); - .offsetX (@gridColumns); - } - - } - - .input(@gridColumnWidth, @gridGutterWidth) { - - .spanX (@index) when (@index > 0) { - input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index} { .span(@index); } - .spanX(@index - 1); - } - .spanX (0) {} - - .span(@columns) { - width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14; - } - - input, - textarea, - .uneditable-input { - margin-left: 0; // override margin-left from core grid system - } - - // Space grid-sized controls properly if multiple per line - .controls-row [class*="span"] + [class*="span"] { - margin-left: @gridGutterWidth; - } - - // generate .spanX - .spanX (@gridColumns); - - } -} diff --git a/pykeg/web/static/bootstrap/less/modals.less b/pykeg/web/static/bootstrap/less/modals.less deleted file mode 100644 index 8e272d409..000000000 --- a/pykeg/web/static/bootstrap/less/modals.less +++ /dev/null @@ -1,95 +0,0 @@ -// -// Modals -// -------------------------------------------------- - -// Background -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: @zindexModalBackdrop; - background-color: @black; - // Fade for backdrop - &.fade { opacity: 0; } -} - -.modal-backdrop, -.modal-backdrop.fade.in { - .opacity(80); -} - -// Base modal -.modal { - position: fixed; - top: 10%; - left: 50%; - z-index: @zindexModal; - width: 560px; - margin-left: -280px; - background-color: @white; - border: 1px solid #999; - border: 1px solid rgba(0,0,0,.3); - *border: 1px solid #999; /* IE6-7 */ - .border-radius(6px); - .box-shadow(0 3px 7px rgba(0,0,0,0.3)); - .background-clip(padding-box); - // Remove focus outline from opened modal - outline: none; - - &.fade { - .transition(e('opacity .3s linear, top .3s ease-out')); - top: -25%; - } - &.fade.in { top: 10%; } -} -.modal-header { - padding: 9px 15px; - border-bottom: 1px solid #eee; - // Close icon - .close { margin-top: 2px; } - // Heading - h3 { - margin: 0; - line-height: 30px; - } -} - -// Body (where all modal content resides) -.modal-body { - position: relative; - overflow-y: auto; - max-height: 400px; - padding: 15px; -} -// Remove bottom margin if need be -.modal-form { - margin-bottom: 0; -} - -// Footer (for actions) -.modal-footer { - padding: 14px 15px 15px; - margin-bottom: 0; - text-align: right; // right align buttons - background-color: #f5f5f5; - border-top: 1px solid #ddd; - .border-radius(0 0 6px 6px); - .box-shadow(inset 0 1px 0 @white); - .clearfix(); // clear it in case folks use .pull-* classes on buttons - - // Properly space out buttons - .btn + .btn { - margin-left: 5px; - margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs - } - // but override that for button groups - .btn-group .btn + .btn { - margin-left: -1px; - } - // and override it for block buttons as well - .btn-block + .btn-block { - margin-left: 0; - } -} diff --git a/pykeg/web/static/bootstrap/less/navbar.less b/pykeg/web/static/bootstrap/less/navbar.less deleted file mode 100644 index 93d09bcad..000000000 --- a/pykeg/web/static/bootstrap/less/navbar.less +++ /dev/null @@ -1,497 +0,0 @@ -// -// Navbars (Redux) -// -------------------------------------------------- - - -// COMMON STYLES -// ------------- - -// Base class and wrapper -.navbar { - overflow: visible; - margin-bottom: @baseLineHeight; - - // Fix for IE7's bad z-indexing so dropdowns don't appear below content that follows the navbar - *position: relative; - *z-index: 2; -} - -// Inner for background effects -// Gradient is applied to its own element because overflow visible is not honored by IE when filter is present -.navbar-inner { - min-height: @navbarHeight; - padding-left: 20px; - padding-right: 20px; - #gradient > .vertical(@navbarBackgroundHighlight, @navbarBackground); - border: 1px solid @navbarBorder; - .border-radius(@baseBorderRadius); - .box-shadow(0 1px 4px rgba(0,0,0,.065)); - - // Prevent floats from breaking the navbar - .clearfix(); -} - -// Set width to auto for default container -// We then reset it for fixed navbars in the #gridSystem mixin -.navbar .container { - width: auto; -} - -// Override the default collapsed state -.nav-collapse.collapse { - height: auto; - overflow: visible; -} - - -// Brand: website or project name -// ------------------------- -.navbar .brand { - float: left; - display: block; - // Vertically center the text given @navbarHeight - padding: ((@navbarHeight - @baseLineHeight) / 2) 20px ((@navbarHeight - @baseLineHeight) / 2); - margin-left: -20px; // negative indent to left-align the text down the page - font-size: 20px; - font-weight: 200; - color: @navbarBrandColor; - text-shadow: 0 1px 0 @navbarBackgroundHighlight; - &:hover, - &:focus { - text-decoration: none; - } -} - -// Plain text in topbar -// ------------------------- -.navbar-text { - margin-bottom: 0; - line-height: @navbarHeight; - color: @navbarText; -} - -// Janky solution for now to account for links outside the .nav -// ------------------------- -.navbar-link { - color: @navbarLinkColor; - &:hover, - &:focus { - color: @navbarLinkColorHover; - } -} - -// Dividers in navbar -// ------------------------- -.navbar .divider-vertical { - height: @navbarHeight; - margin: 0 9px; - border-left: 1px solid @navbarBackground; - border-right: 1px solid @navbarBackgroundHighlight; -} - -// Buttons in navbar -// ------------------------- -.navbar .btn, -.navbar .btn-group { - .navbarVerticalAlign(30px); // Vertically center in navbar -} -.navbar .btn-group .btn, -.navbar .input-prepend .btn, -.navbar .input-append .btn, -.navbar .input-prepend .btn-group, -.navbar .input-append .btn-group { - margin-top: 0; // then undo the margin here so we don't accidentally double it -} - -// Navbar forms -// ------------------------- -.navbar-form { - margin-bottom: 0; // remove default bottom margin - .clearfix(); - input, - select, - .radio, - .checkbox { - .navbarVerticalAlign(30px); // Vertically center in navbar - } - input, - select, - .btn { - display: inline-block; - margin-bottom: 0; - } - input[type="image"], - input[type="checkbox"], - input[type="radio"] { - margin-top: 3px; - } - .input-append, - .input-prepend { - margin-top: 5px; - white-space: nowrap; // preven two items from separating within a .navbar-form that has .pull-left - input { - margin-top: 0; // remove the margin on top since it's on the parent - } - } -} - -// Navbar search -// ------------------------- -.navbar-search { - position: relative; - float: left; - .navbarVerticalAlign(30px); // Vertically center in navbar - margin-bottom: 0; - .search-query { - margin-bottom: 0; - padding: 4px 14px; - #font > .sans-serif(13px, normal, 1); - .border-radius(15px); // redeclare because of specificity of the type attribute - } -} - - - -// Static navbar -// ------------------------- - -.navbar-static-top { - position: static; - margin-bottom: 0; // remove 18px margin for default navbar - .navbar-inner { - .border-radius(0); - } -} - - - -// Fixed navbar -// ------------------------- - -// Shared (top/bottom) styles -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: @zindexFixedNavbar; - margin-bottom: 0; // remove 18px margin for default navbar -} -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - border-width: 0 0 1px; -} -.navbar-fixed-bottom .navbar-inner { - border-width: 1px 0 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-fixed-bottom .navbar-inner { - padding-left: 0; - padding-right: 0; - .border-radius(0); -} - -// Reset container width -// Required here as we reset the width earlier on and the grid mixins don't override early enough -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - #grid > .core > .span(@gridColumns); -} - -// Fixed to top -.navbar-fixed-top { - top: 0; -} -.navbar-fixed-top, -.navbar-static-top { - .navbar-inner { - .box-shadow(~"0 1px 10px rgba(0,0,0,.1)"); - } -} - -// Fixed to bottom -.navbar-fixed-bottom { - bottom: 0; - .navbar-inner { - .box-shadow(~"0 -1px 10px rgba(0,0,0,.1)"); - } -} - - - -// NAVIGATION -// ---------- - -.navbar .nav { - position: relative; - left: 0; - display: block; - float: left; - margin: 0 10px 0 0; -} -.navbar .nav.pull-right { - float: right; // redeclare due to specificity - margin-right: 0; // remove margin on float right nav -} -.navbar .nav > li { - float: left; -} - -// Links -.navbar .nav > li > a { - float: none; - // Vertically center the text given @navbarHeight - padding: ((@navbarHeight - @baseLineHeight) / 2) 15px ((@navbarHeight - @baseLineHeight) / 2); - color: @navbarLinkColor; - text-decoration: none; - text-shadow: 0 1px 0 @navbarBackgroundHighlight; -} -.navbar .nav .dropdown-toggle .caret { - margin-top: 8px; -} - -// Hover/focus -.navbar .nav > li > a:focus, -.navbar .nav > li > a:hover { - background-color: @navbarLinkBackgroundHover; // "transparent" is default to differentiate :hover/:focus from .active - color: @navbarLinkColorHover; - text-decoration: none; -} - -// Active nav items -.navbar .nav > .active > a, -.navbar .nav > .active > a:hover, -.navbar .nav > .active > a:focus { - color: @navbarLinkColorActive; - text-decoration: none; - background-color: @navbarLinkBackgroundActive; - .box-shadow(inset 0 3px 8px rgba(0,0,0,.125)); -} - -// Navbar button for toggling navbar items in responsive layouts -// These definitions need to come after '.navbar .btn' -.navbar .btn-navbar { - display: none; - float: right; - padding: 7px 10px; - margin-left: 5px; - margin-right: 5px; - .buttonBackground(darken(@navbarBackgroundHighlight, 5%), darken(@navbarBackground, 5%)); - .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075)"); -} -.navbar .btn-navbar .icon-bar { - display: block; - width: 18px; - height: 2px; - background-color: #f5f5f5; - .border-radius(1px); - .box-shadow(0 1px 0 rgba(0,0,0,.25)); -} -.btn-navbar .icon-bar + .icon-bar { - margin-top: 3px; -} - - - -// Dropdown menus -// -------------- - -// Menu position and menu carets -.navbar .nav > li > .dropdown-menu { - &:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-bottom-color: @dropdownBorder; - position: absolute; - top: -7px; - left: 9px; - } - &:after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid @dropdownBackground; - position: absolute; - top: -6px; - left: 10px; - } -} -// Menu position and menu caret support for dropups via extra dropup class -.navbar-fixed-bottom .nav > li > .dropdown-menu { - &:before { - border-top: 7px solid #ccc; - border-top-color: @dropdownBorder; - border-bottom: 0; - bottom: -7px; - top: auto; - } - &:after { - border-top: 6px solid @dropdownBackground; - border-bottom: 0; - bottom: -6px; - top: auto; - } -} - -// Caret should match text color on hover/focus -.navbar .nav li.dropdown > a:hover .caret, -.navbar .nav li.dropdown > a:focus .caret { - border-top-color: @navbarLinkColorHover; - border-bottom-color: @navbarLinkColorHover; -} - -// Remove background color from open dropdown -.navbar .nav li.dropdown.open > .dropdown-toggle, -.navbar .nav li.dropdown.active > .dropdown-toggle, -.navbar .nav li.dropdown.open.active > .dropdown-toggle { - background-color: @navbarLinkBackgroundActive; - color: @navbarLinkColorActive; -} -.navbar .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: @navbarLinkColor; - border-bottom-color: @navbarLinkColor; -} -.navbar .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: @navbarLinkColorActive; - border-bottom-color: @navbarLinkColorActive; -} - -// Right aligned menus need alt position -.navbar .pull-right > li > .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right { - left: auto; - right: 0; - &:before { - left: auto; - right: 12px; - } - &:after { - left: auto; - right: 13px; - } - .dropdown-menu { - left: auto; - right: 100%; - margin-left: 0; - margin-right: -1px; - .border-radius(6px 0 6px 6px); - } -} - - -// Inverted navbar -// ------------------------- - -.navbar-inverse { - - .navbar-inner { - #gradient > .vertical(@navbarInverseBackgroundHighlight, @navbarInverseBackground); - border-color: @navbarInverseBorder; - } - - .brand, - .nav > li > a { - color: @navbarInverseLinkColor; - text-shadow: 0 -1px 0 rgba(0,0,0,.25); - &:hover, - &:focus { - color: @navbarInverseLinkColorHover; - } - } - - .brand { - color: @navbarInverseBrandColor; - } - - .navbar-text { - color: @navbarInverseText; - } - - .nav > li > a:focus, - .nav > li > a:hover { - background-color: @navbarInverseLinkBackgroundHover; - color: @navbarInverseLinkColorHover; - } - - .nav .active > a, - .nav .active > a:hover, - .nav .active > a:focus { - color: @navbarInverseLinkColorActive; - background-color: @navbarInverseLinkBackgroundActive; - } - - // Inline text links - .navbar-link { - color: @navbarInverseLinkColor; - &:hover, - &:focus { - color: @navbarInverseLinkColorHover; - } - } - - // Dividers in navbar - .divider-vertical { - border-left-color: @navbarInverseBackground; - border-right-color: @navbarInverseBackgroundHighlight; - } - - // Dropdowns - .nav li.dropdown.open > .dropdown-toggle, - .nav li.dropdown.active > .dropdown-toggle, - .nav li.dropdown.open.active > .dropdown-toggle { - background-color: @navbarInverseLinkBackgroundActive; - color: @navbarInverseLinkColorActive; - } - .nav li.dropdown > a:hover .caret, - .nav li.dropdown > a:focus .caret { - border-top-color: @navbarInverseLinkColorActive; - border-bottom-color: @navbarInverseLinkColorActive; - } - .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: @navbarInverseLinkColor; - border-bottom-color: @navbarInverseLinkColor; - } - .nav li.dropdown.open > .dropdown-toggle .caret, - .nav li.dropdown.active > .dropdown-toggle .caret, - .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: @navbarInverseLinkColorActive; - border-bottom-color: @navbarInverseLinkColorActive; - } - - // Navbar search - .navbar-search { - .search-query { - color: @white; - background-color: @navbarInverseSearchBackground; - border-color: @navbarInverseSearchBorder; - .box-shadow(~"inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15)"); - .transition(none); - .placeholder(@navbarInverseSearchPlaceholderColor); - - // Focus states (we use .focused since IE7-8 and down doesn't support :focus) - &:focus, - &.focused { - padding: 5px 15px; - color: @grayDark; - text-shadow: 0 1px 0 @white; - background-color: @navbarInverseSearchBackgroundFocus; - border: 0; - .box-shadow(0 0 3px rgba(0,0,0,.15)); - outline: 0; - } - } - } - - // Navbar collapse button - .btn-navbar { - .buttonBackground(darken(@navbarInverseBackgroundHighlight, 5%), darken(@navbarInverseBackground, 5%)); - } - -} diff --git a/pykeg/web/static/bootstrap/less/navs.less b/pykeg/web/static/bootstrap/less/navs.less deleted file mode 100644 index 01cd805bd..000000000 --- a/pykeg/web/static/bootstrap/less/navs.less +++ /dev/null @@ -1,409 +0,0 @@ -// -// Navs -// -------------------------------------------------- - - -// BASE CLASS -// ---------- - -.nav { - margin-left: 0; - margin-bottom: @baseLineHeight; - list-style: none; -} - -// Make links block level -.nav > li > a { - display: block; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: @grayLighter; -} - -// Prevent IE8 from misplacing imgs -// See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 -.nav > li > a > img { - max-width: none; -} - -// Redeclare pull classes because of specifity -.nav > .pull-right { - float: right; -} - -// Nav headers (for dropdowns and lists) -.nav-header { - display: block; - padding: 3px 15px; - font-size: 11px; - font-weight: bold; - line-height: @baseLineHeight; - color: @grayLight; - text-shadow: 0 1px 0 rgba(255,255,255,.5); - text-transform: uppercase; -} -// Space them out when they follow another list item (link) -.nav li + .nav-header { - margin-top: 9px; -} - - - -// NAV LIST -// -------- - -.nav-list { - padding-left: 15px; - padding-right: 15px; - margin-bottom: 0; -} -.nav-list > li > a, -.nav-list .nav-header { - margin-left: -15px; - margin-right: -15px; - text-shadow: 0 1px 0 rgba(255,255,255,.5); -} -.nav-list > li > a { - padding: 3px 15px; -} -.nav-list > .active > a, -.nav-list > .active > a:hover, -.nav-list > .active > a:focus { - color: @white; - text-shadow: 0 -1px 0 rgba(0,0,0,.2); - background-color: @linkColor; -} -.nav-list [class^="icon-"], -.nav-list [class*=" icon-"] { - margin-right: 2px; -} -// Dividers (basically an hr) within the dropdown -.nav-list .divider { - .nav-divider(); -} - - - -// TABS AND PILLS -// ------------- - -// Common styles -.nav-tabs, -.nav-pills { - .clearfix(); -} -.nav-tabs > li, -.nav-pills > li { - float: left; -} -.nav-tabs > li > a, -.nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; - margin-right: 2px; - line-height: 14px; // keeps the overall height an even number -} - -// TABS -// ---- - -// Give the tabs something to sit on -.nav-tabs { - border-bottom: 1px solid #ddd; -} -// Make the list-items overlay the bottom border -.nav-tabs > li { - margin-bottom: -1px; -} -// Actual tabs (as links) -.nav-tabs > li > a { - padding-top: 8px; - padding-bottom: 8px; - line-height: @baseLineHeight; - border: 1px solid transparent; - .border-radius(4px 4px 0 0); - &:hover, - &:focus { - border-color: @grayLighter @grayLighter #ddd; - } -} -// Active state, and it's :hover/:focus to override normal :hover/:focus -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover, -.nav-tabs > .active > a:focus { - color: @gray; - background-color: @bodyBackground; - border: 1px solid #ddd; - border-bottom-color: transparent; - cursor: default; -} - - -// PILLS -// ----- - -// Links rendered as pills -.nav-pills > li > a { - padding-top: 8px; - padding-bottom: 8px; - margin-top: 2px; - margin-bottom: 2px; - .border-radius(5px); -} - -// Active state -.nav-pills > .active > a, -.nav-pills > .active > a:hover, -.nav-pills > .active > a:focus { - color: @white; - background-color: @linkColor; -} - - - -// STACKED NAV -// ----------- - -// Stacked tabs and pills -.nav-stacked > li { - float: none; -} -.nav-stacked > li > a { - margin-right: 0; // no need for the gap between nav items -} - -// Tabs -.nav-tabs.nav-stacked { - border-bottom: 0; -} -.nav-tabs.nav-stacked > li > a { - border: 1px solid #ddd; - .border-radius(0); -} -.nav-tabs.nav-stacked > li:first-child > a { - .border-top-radius(4px); -} -.nav-tabs.nav-stacked > li:last-child > a { - .border-bottom-radius(4px); -} -.nav-tabs.nav-stacked > li > a:hover, -.nav-tabs.nav-stacked > li > a:focus { - border-color: #ddd; - z-index: 2; -} - -// Pills -.nav-pills.nav-stacked > li > a { - margin-bottom: 3px; -} -.nav-pills.nav-stacked > li:last-child > a { - margin-bottom: 1px; // decrease margin to match sizing of stacked tabs -} - - - -// DROPDOWNS -// --------- - -.nav-tabs .dropdown-menu { - .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu -} -.nav-pills .dropdown-menu { - .border-radius(6px); // make rounded corners match the pills -} - -// Default dropdown links -// ------------------------- -// Make carets use linkColor to start -.nav .dropdown-toggle .caret { - border-top-color: @linkColor; - border-bottom-color: @linkColor; - margin-top: 6px; -} -.nav .dropdown-toggle:hover .caret, -.nav .dropdown-toggle:focus .caret { - border-top-color: @linkColorHover; - border-bottom-color: @linkColorHover; -} -/* move down carets for tabs */ -.nav-tabs .dropdown-toggle .caret { - margin-top: 8px; -} - -// Active dropdown links -// ------------------------- -.nav .active .dropdown-toggle .caret { - border-top-color: #fff; - border-bottom-color: #fff; -} -.nav-tabs .active .dropdown-toggle .caret { - border-top-color: @gray; - border-bottom-color: @gray; -} - -// Active:hover/:focus dropdown links -// ------------------------- -.nav > .dropdown.active > a:hover, -.nav > .dropdown.active > a:focus { - cursor: pointer; -} - -// Open dropdowns -// ------------------------- -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover, -.nav > li.dropdown.open.active > a:focus { - color: @white; - background-color: @grayLight; - border-color: @grayLight; -} -.nav li.dropdown.open .caret, -.nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret, -.nav li.dropdown.open a:focus .caret { - border-top-color: @white; - border-bottom-color: @white; - .opacity(100); -} - -// Dropdowns in stacked tabs -.tabs-stacked .open > a:hover, -.tabs-stacked .open > a:focus { - border-color: @grayLight; -} - - - -// TABBABLE -// -------- - - -// COMMON STYLES -// ------------- - -// Clear any floats -.tabbable { - .clearfix(); -} -.tab-content { - overflow: auto; // prevent content from running below tabs -} - -// Remove border on bottom, left, right -.tabs-below > .nav-tabs, -.tabs-right > .nav-tabs, -.tabs-left > .nav-tabs { - border-bottom: 0; -} - -// Show/hide tabbable areas -.tab-content > .tab-pane, -.pill-content > .pill-pane { - display: none; -} -.tab-content > .active, -.pill-content > .active { - display: block; -} - - -// BOTTOM -// ------ - -.tabs-below > .nav-tabs { - border-top: 1px solid #ddd; -} -.tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} -.tabs-below > .nav-tabs > li > a { - .border-radius(0 0 4px 4px); - &:hover, - &:focus { - border-bottom-color: transparent; - border-top-color: #ddd; - } -} -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover, -.tabs-below > .nav-tabs > .active > a:focus { - border-color: transparent #ddd #ddd #ddd; -} - -// LEFT & RIGHT -// ------------ - -// Common styles -.tabs-left > .nav-tabs > li, -.tabs-right > .nav-tabs > li { - float: none; -} -.tabs-left > .nav-tabs > li > a, -.tabs-right > .nav-tabs > li > a { - min-width: 74px; - margin-right: 0; - margin-bottom: 3px; -} - -// Tabs on the left -.tabs-left > .nav-tabs { - float: left; - margin-right: 19px; - border-right: 1px solid #ddd; -} -.tabs-left > .nav-tabs > li > a { - margin-right: -1px; - .border-radius(4px 0 0 4px); -} -.tabs-left > .nav-tabs > li > a:hover, -.tabs-left > .nav-tabs > li > a:focus { - border-color: @grayLighter #ddd @grayLighter @grayLighter; -} -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover, -.tabs-left > .nav-tabs .active > a:focus { - border-color: #ddd transparent #ddd #ddd; - *border-right-color: @white; -} - -// Tabs on the right -.tabs-right > .nav-tabs { - float: right; - margin-left: 19px; - border-left: 1px solid #ddd; -} -.tabs-right > .nav-tabs > li > a { - margin-left: -1px; - .border-radius(0 4px 4px 0); -} -.tabs-right > .nav-tabs > li > a:hover, -.tabs-right > .nav-tabs > li > a:focus { - border-color: @grayLighter @grayLighter @grayLighter #ddd; -} -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover, -.tabs-right > .nav-tabs .active > a:focus { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: @white; -} - - - -// DISABLED STATES -// --------------- - -// Gray out text -.nav > .disabled > a { - color: @grayLight; -} -// Nuke hover/focus effects -.nav > .disabled > a:hover, -.nav > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - cursor: default; -} diff --git a/pykeg/web/static/bootstrap/less/pager.less b/pykeg/web/static/bootstrap/less/pager.less deleted file mode 100644 index 147618829..000000000 --- a/pykeg/web/static/bootstrap/less/pager.less +++ /dev/null @@ -1,43 +0,0 @@ -// -// Pager pagination -// -------------------------------------------------- - - -.pager { - margin: @baseLineHeight 0; - list-style: none; - text-align: center; - .clearfix(); -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - .border-radius(15px); -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #f5f5f5; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: @grayLight; - background-color: #fff; - cursor: default; -} \ No newline at end of file diff --git a/pykeg/web/static/bootstrap/less/pagination.less b/pykeg/web/static/bootstrap/less/pagination.less deleted file mode 100644 index a789db2d2..000000000 --- a/pykeg/web/static/bootstrap/less/pagination.less +++ /dev/null @@ -1,123 +0,0 @@ -// -// Pagination (multiple pages) -// -------------------------------------------------- - -// Space out pagination from surrounding content -.pagination { - margin: @baseLineHeight 0; -} - -.pagination ul { - // Allow for text-based alignment - display: inline-block; - .ie7-inline-block(); - // Reset default ul styles - margin-left: 0; - margin-bottom: 0; - // Visuals - .border-radius(@baseBorderRadius); - .box-shadow(0 1px 2px rgba(0,0,0,.05)); -} -.pagination ul > li { - display: inline; // Remove list-style and block-level defaults -} -.pagination ul > li > a, -.pagination ul > li > span { - float: left; // Collapse white-space - padding: 4px 12px; - line-height: @baseLineHeight; - text-decoration: none; - background-color: @paginationBackground; - border: 1px solid @paginationBorder; - border-left-width: 0; -} -.pagination ul > li > a:hover, -.pagination ul > li > a:focus, -.pagination ul > .active > a, -.pagination ul > .active > span { - background-color: @paginationActiveBackground; -} -.pagination ul > .active > a, -.pagination ul > .active > span { - color: @grayLight; - cursor: default; -} -.pagination ul > .disabled > span, -.pagination ul > .disabled > a, -.pagination ul > .disabled > a:hover, -.pagination ul > .disabled > a:focus { - color: @grayLight; - background-color: transparent; - cursor: default; -} -.pagination ul > li:first-child > a, -.pagination ul > li:first-child > span { - border-left-width: 1px; - .border-left-radius(@baseBorderRadius); -} -.pagination ul > li:last-child > a, -.pagination ul > li:last-child > span { - .border-right-radius(@baseBorderRadius); -} - - -// Alignment -// -------------------------------------------------- - -.pagination-centered { - text-align: center; -} -.pagination-right { - text-align: right; -} - - -// Sizing -// -------------------------------------------------- - -// Large -.pagination-large { - ul > li > a, - ul > li > span { - padding: @paddingLarge; - font-size: @fontSizeLarge; - } - ul > li:first-child > a, - ul > li:first-child > span { - .border-left-radius(@borderRadiusLarge); - } - ul > li:last-child > a, - ul > li:last-child > span { - .border-right-radius(@borderRadiusLarge); - } -} - -// Small and mini -.pagination-mini, -.pagination-small { - ul > li:first-child > a, - ul > li:first-child > span { - .border-left-radius(@borderRadiusSmall); - } - ul > li:last-child > a, - ul > li:last-child > span { - .border-right-radius(@borderRadiusSmall); - } -} - -// Small -.pagination-small { - ul > li > a, - ul > li > span { - padding: @paddingSmall; - font-size: @fontSizeSmall; - } -} -// Mini -.pagination-mini { - ul > li > a, - ul > li > span { - padding: @paddingMini; - font-size: @fontSizeMini; - } -} diff --git a/pykeg/web/static/bootstrap/less/popovers.less b/pykeg/web/static/bootstrap/less/popovers.less deleted file mode 100644 index aae35c8cd..000000000 --- a/pykeg/web/static/bootstrap/less/popovers.less +++ /dev/null @@ -1,133 +0,0 @@ -// -// Popovers -// -------------------------------------------------- - - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: @zindexPopover; - display: none; - max-width: 276px; - padding: 1px; - text-align: left; // Reset given new insertion method - background-color: @popoverBackground; - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0,0,0,.2); - .border-radius(6px); - .box-shadow(0 5px 10px rgba(0,0,0,.2)); - - // Overrides for proper insertion - white-space: normal; - - // Offset the popover to account for the popover arrow - &.top { margin-top: -10px; } - &.right { margin-left: 10px; } - &.bottom { margin-top: 10px; } - &.left { margin-left: -10px; } -} - -.popover-title { - margin: 0; // reset heading margin - padding: 8px 14px; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: @popoverTitleBackground; - border-bottom: 1px solid darken(@popoverTitleBackground, 5%); - .border-radius(5px 5px 0 0); - - &:empty { - display: none; - } -} - -.popover-content { - padding: 9px 14px; -} - -// Arrows -// -// .arrow is outer, .arrow:after is inner - -.popover .arrow, -.popover .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover .arrow { - border-width: @popoverArrowOuterWidth; -} -.popover .arrow:after { - border-width: @popoverArrowWidth; - content: ""; -} - -.popover { - &.top .arrow { - left: 50%; - margin-left: -@popoverArrowOuterWidth; - border-bottom-width: 0; - border-top-color: #999; // IE8 fallback - border-top-color: @popoverArrowOuterColor; - bottom: -@popoverArrowOuterWidth; - &:after { - bottom: 1px; - margin-left: -@popoverArrowWidth; - border-bottom-width: 0; - border-top-color: @popoverArrowColor; - } - } - &.right .arrow { - top: 50%; - left: -@popoverArrowOuterWidth; - margin-top: -@popoverArrowOuterWidth; - border-left-width: 0; - border-right-color: #999; // IE8 fallback - border-right-color: @popoverArrowOuterColor; - &:after { - left: 1px; - bottom: -@popoverArrowWidth; - border-left-width: 0; - border-right-color: @popoverArrowColor; - } - } - &.bottom .arrow { - left: 50%; - margin-left: -@popoverArrowOuterWidth; - border-top-width: 0; - border-bottom-color: #999; // IE8 fallback - border-bottom-color: @popoverArrowOuterColor; - top: -@popoverArrowOuterWidth; - &:after { - top: 1px; - margin-left: -@popoverArrowWidth; - border-top-width: 0; - border-bottom-color: @popoverArrowColor; - } - } - - &.left .arrow { - top: 50%; - right: -@popoverArrowOuterWidth; - margin-top: -@popoverArrowOuterWidth; - border-right-width: 0; - border-left-color: #999; // IE8 fallback - border-left-color: @popoverArrowOuterColor; - &:after { - right: 1px; - border-right-width: 0; - border-left-color: @popoverArrowColor; - bottom: -@popoverArrowWidth; - } - } - -} diff --git a/pykeg/web/static/bootstrap/less/progress-bars.less b/pykeg/web/static/bootstrap/less/progress-bars.less deleted file mode 100644 index 5e0c3dda0..000000000 --- a/pykeg/web/static/bootstrap/less/progress-bars.less +++ /dev/null @@ -1,122 +0,0 @@ -// -// Progress bars -// -------------------------------------------------- - - -// ANIMATIONS -// ---------- - -// Webkit -@-webkit-keyframes progress-bar-stripes { - from { background-position: 40px 0; } - to { background-position: 0 0; } -} - -// Firefox -@-moz-keyframes progress-bar-stripes { - from { background-position: 40px 0; } - to { background-position: 0 0; } -} - -// IE9 -@-ms-keyframes progress-bar-stripes { - from { background-position: 40px 0; } - to { background-position: 0 0; } -} - -// Opera -@-o-keyframes progress-bar-stripes { - from { background-position: 0 0; } - to { background-position: 40px 0; } -} - -// Spec -@keyframes progress-bar-stripes { - from { background-position: 40px 0; } - to { background-position: 0 0; } -} - - - -// THE BARS -// -------- - -// Outer container -.progress { - overflow: hidden; - height: @baseLineHeight; - margin-bottom: @baseLineHeight; - #gradient > .vertical(#f5f5f5, #f9f9f9); - .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); - .border-radius(@baseBorderRadius); -} - -// Bar of progress -.progress .bar { - width: 0%; - height: 100%; - color: @white; - float: left; - font-size: 12px; - text-align: center; - text-shadow: 0 -1px 0 rgba(0,0,0,.25); - #gradient > .vertical(#149bdf, #0480be); - .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); - .box-sizing(border-box); - .transition(width .6s ease); -} -.progress .bar + .bar { - .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); -} - -// Striped bars -.progress-striped .bar { - #gradient > .striped(#149bdf); - .background-size(40px 40px); -} - -// Call animation for the active one -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} - - - -// COLORS -// ------ - -// Danger (red) -.progress-danger .bar, .progress .bar-danger { - #gradient > .vertical(#ee5f5b, #c43c35); -} -.progress-danger.progress-striped .bar, .progress-striped .bar-danger { - #gradient > .striped(#ee5f5b); -} - -// Success (green) -.progress-success .bar, .progress .bar-success { - #gradient > .vertical(#62c462, #57a957); -} -.progress-success.progress-striped .bar, .progress-striped .bar-success { - #gradient > .striped(#62c462); -} - -// Info (teal) -.progress-info .bar, .progress .bar-info { - #gradient > .vertical(#5bc0de, #339bb9); -} -.progress-info.progress-striped .bar, .progress-striped .bar-info { - #gradient > .striped(#5bc0de); -} - -// Warning (orange) -.progress-warning .bar, .progress .bar-warning { - #gradient > .vertical(lighten(@orange, 15%), @orange); -} -.progress-warning.progress-striped .bar, .progress-striped .bar-warning { - #gradient > .striped(lighten(@orange, 15%)); -} diff --git a/pykeg/web/static/bootstrap/less/reset.less b/pykeg/web/static/bootstrap/less/reset.less deleted file mode 100644 index 4806bd5e5..000000000 --- a/pykeg/web/static/bootstrap/less/reset.less +++ /dev/null @@ -1,216 +0,0 @@ -// -// Reset CSS -// Adapted from http://github.com/necolas/normalize.css -// -------------------------------------------------- - - -// Display in IE6-9 and FF3 -// ------------------------- - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} - -// Display block in IE6-9 and FF3 -// ------------------------- - -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} - -// Prevents modern browsers from displaying 'audio' without controls -// ------------------------- - -audio:not([controls]) { - display: none; -} - -// Base settings -// ------------------------- - -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -// Focus states -a:focus { - .tab-focus(); -} -// Hover & Active -a:hover, -a:active { - outline: 0; -} - -// Prevents sub and sup affecting line-height in all browsers -// ------------------------- - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} - -// Img border in a's and image quality -// ------------------------- - -img { - /* Responsive images (ensure images don't scale beyond their parents) */ - max-width: 100%; /* Part 1: Set a maxium relative to the parent */ - width: auto\9; /* IE7-8 need help adjusting responsive images */ - height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ - - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; -} - -// Prevent max-width from affecting Google Maps -#map_canvas img, -.google-maps img { - max-width: none; -} - -// Forms -// ------------------------- - -// Font size in all browsers, margin changes, misc consistency -button, -input, -select, -textarea { - margin: 0; - font-size: 100%; - vertical-align: middle; -} -button, -input { - *overflow: visible; // Inner spacing ie IE6/7 - line-height: normal; // FF3/4 have !important on line-height in UA stylesheet -} -button::-moz-focus-inner, -input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 - padding: 0; - border: 0; -} -button, -html input[type="button"], // Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; // Corrects inability to style clickable `input` types in iOS. - cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. -} -label, -select, -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -input[type="radio"], -input[type="checkbox"] { - cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. -} -input[type="search"] { // Appearance in Safari/Chrome - .box-sizing(content-box); - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 -} -textarea { - overflow: auto; // Remove vertical scrollbar in IE6-9 - vertical-align: top; // Readability and alignment cross-browser -} - - -// Printing -// ------------------------- -// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css - -@media print { - - * { - text-shadow: none !important; - color: #000 !important; // Black prints faster: h5bp.com/s - background: transparent !important; - box-shadow: none !important; - } - - a, - a:visited { - text-decoration: underline; - } - - a[href]:after { - content: " (" attr(href) ")"; - } - - abbr[title]:after { - content: " (" attr(title) ")"; - } - - // Don't show links for images, or javascript/internal links - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - - thead { - display: table-header-group; // h5bp.com/t - } - - tr, - img { - page-break-inside: avoid; - } - - img { - max-width: 100% !important; - } - - @page { - margin: 0.5cm; - } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { - page-break-after: avoid; - } -} diff --git a/pykeg/web/static/bootstrap/less/responsive-1200px-min.less b/pykeg/web/static/bootstrap/less/responsive-1200px-min.less deleted file mode 100644 index 4f35ba6ca..000000000 --- a/pykeg/web/static/bootstrap/less/responsive-1200px-min.less +++ /dev/null @@ -1,28 +0,0 @@ -// -// Responsive: Large desktop and up -// -------------------------------------------------- - - -@media (min-width: 1200px) { - - // Fixed grid - #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); - - // Fluid grid - #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); - - // Input grid - #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); - - // Thumbnails - .thumbnails { - margin-left: -@gridGutterWidth1200; - } - .thumbnails > li { - margin-left: @gridGutterWidth1200; - } - .row-fluid .thumbnails { - margin-left: 0; - } - -} diff --git a/pykeg/web/static/bootstrap/less/responsive-767px-max.less b/pykeg/web/static/bootstrap/less/responsive-767px-max.less deleted file mode 100644 index 128f4ce30..000000000 --- a/pykeg/web/static/bootstrap/less/responsive-767px-max.less +++ /dev/null @@ -1,193 +0,0 @@ -// -// Responsive: Landscape phone to desktop/tablet -// -------------------------------------------------- - - -@media (max-width: 767px) { - - // Padding to set content in a bit - body { - padding-left: 20px; - padding-right: 20px; - } - // Negative indent the now static "fixed" navbar - .navbar-fixed-top, - .navbar-fixed-bottom, - .navbar-static-top { - margin-left: -20px; - margin-right: -20px; - } - // Remove padding on container given explicit padding set on body - .container-fluid { - padding: 0; - } - - // TYPOGRAPHY - // ---------- - // Reset horizontal dl - .dl-horizontal { - dt { - float: none; - clear: none; - width: auto; - text-align: left; - } - dd { - margin-left: 0; - } - } - - // GRID & CONTAINERS - // ----------------- - // Remove width from containers - .container { - width: auto; - } - // Fluid rows - .row-fluid { - width: 100%; - } - // Undo negative margin on rows and thumbnails - .row, - .thumbnails { - margin-left: 0; - } - .thumbnails > li { - float: none; - margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present - } - // Make all grid-sized elements block level again - [class*="span"], - .uneditable-input[class*="span"], // Makes uneditable inputs full-width when using grid sizing - .row-fluid [class*="span"] { - float: none; - display: block; - width: 100%; - margin-left: 0; - .box-sizing(border-box); - } - .span12, - .row-fluid .span12 { - width: 100%; - .box-sizing(border-box); - } - .row-fluid [class*="offset"]:first-child { - margin-left: 0; - } - - // FORM FIELDS - // ----------- - // Make span* classes full width - .input-large, - .input-xlarge, - .input-xxlarge, - input[class*="span"], - select[class*="span"], - textarea[class*="span"], - .uneditable-input { - .input-block-level(); - } - // But don't let it screw up prepend/append inputs - .input-prepend input, - .input-append input, - .input-prepend input[class*="span"], - .input-append input[class*="span"] { - display: inline-block; // redeclare so they don't wrap to new lines - width: auto; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 0; - } - - // Modals - .modal { - position: fixed; - top: 20px; - left: 20px; - right: 20px; - width: auto; - margin: 0; - &.fade { top: -100px; } - &.fade.in { top: 20px; } - } - -} - - - -// UP TO LANDSCAPE PHONE -// --------------------- - -@media (max-width: 480px) { - - // Smooth out the collapsing/expanding nav - .nav-collapse { - -webkit-transform: translate3d(0, 0, 0); // activate the GPU - } - - // Block level the page header small tag for readability - .page-header h1 small { - display: block; - line-height: @baseLineHeight; - } - - // Update checkboxes for iOS - input[type="checkbox"], - input[type="radio"] { - border: 1px solid #ccc; - } - - // Remove the horizontal form styles - .form-horizontal { - .control-label { - float: none; - width: auto; - padding-top: 0; - text-align: left; - } - // Move over all input controls and content - .controls { - margin-left: 0; - } - // Move the options list down to align with labels - .control-list { - padding-top: 0; // has to be padding because margin collaspes - } - // Move over buttons in .form-actions to align with .controls - .form-actions { - padding-left: 10px; - padding-right: 10px; - } - } - - // Medias - // Reset float and spacing to stack - .media .pull-left, - .media .pull-right { - float: none; - display: block; - margin-bottom: 10px; - } - // Remove side margins since we stack instead of indent - .media-object { - margin-right: 0; - margin-left: 0; - } - - // Modals - .modal { - top: 10px; - left: 10px; - right: 10px; - } - .modal-header .close { - padding: 10px; - margin: -10px; - } - - // Carousel - .carousel-caption { - position: static; - } - -} diff --git a/pykeg/web/static/bootstrap/less/responsive-768px-979px.less b/pykeg/web/static/bootstrap/less/responsive-768px-979px.less deleted file mode 100644 index 8e8c486a0..000000000 --- a/pykeg/web/static/bootstrap/less/responsive-768px-979px.less +++ /dev/null @@ -1,19 +0,0 @@ -// -// Responsive: Tablet to desktop -// -------------------------------------------------- - - -@media (min-width: 768px) and (max-width: 979px) { - - // Fixed grid - #grid > .core(@gridColumnWidth768, @gridGutterWidth768); - - // Fluid grid - #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); - - // Input grid - #grid > .input(@gridColumnWidth768, @gridGutterWidth768); - - // No need to reset .thumbnails here since it's the same @gridGutterWidth - -} diff --git a/pykeg/web/static/bootstrap/less/responsive-navbar.less b/pykeg/web/static/bootstrap/less/responsive-navbar.less deleted file mode 100644 index 21cd3ba67..000000000 --- a/pykeg/web/static/bootstrap/less/responsive-navbar.less +++ /dev/null @@ -1,189 +0,0 @@ -// -// Responsive: Navbar -// -------------------------------------------------- - - -// TABLETS AND BELOW -// ----------------- -@media (max-width: @navbarCollapseWidth) { - - // UNFIX THE TOPBAR - // ---------------- - // Remove any padding from the body - body { - padding-top: 0; - } - // Unfix the navbars - .navbar-fixed-top, - .navbar-fixed-bottom { - position: static; - } - .navbar-fixed-top { - margin-bottom: @baseLineHeight; - } - .navbar-fixed-bottom { - margin-top: @baseLineHeight; - } - .navbar-fixed-top .navbar-inner, - .navbar-fixed-bottom .navbar-inner { - padding: 5px; - } - .navbar .container { - width: auto; - padding: 0; - } - // Account for brand name - .navbar .brand { - padding-left: 10px; - padding-right: 10px; - margin: 0 0 0 -5px; - } - - // COLLAPSIBLE NAVBAR - // ------------------ - // Nav collapse clears brand - .nav-collapse { - clear: both; - } - // Block-level the nav - .nav-collapse .nav { - float: none; - margin: 0 0 (@baseLineHeight / 2); - } - .nav-collapse .nav > li { - float: none; - } - .nav-collapse .nav > li > a { - margin-bottom: 2px; - } - .nav-collapse .nav > .divider-vertical { - display: none; - } - .nav-collapse .nav .nav-header { - color: @navbarText; - text-shadow: none; - } - // Nav and dropdown links in navbar - .nav-collapse .nav > li > a, - .nav-collapse .dropdown-menu a { - padding: 9px 15px; - font-weight: bold; - color: @navbarLinkColor; - .border-radius(3px); - } - // Buttons - .nav-collapse .btn { - padding: 4px 10px 4px; - font-weight: normal; - .border-radius(@baseBorderRadius); - } - .nav-collapse .dropdown-menu li + li a { - margin-bottom: 2px; - } - .nav-collapse .nav > li > a:hover, - .nav-collapse .nav > li > a:focus, - .nav-collapse .dropdown-menu a:hover, - .nav-collapse .dropdown-menu a:focus { - background-color: @navbarBackground; - } - .navbar-inverse .nav-collapse .nav > li > a, - .navbar-inverse .nav-collapse .dropdown-menu a { - color: @navbarInverseLinkColor; - } - .navbar-inverse .nav-collapse .nav > li > a:hover, - .navbar-inverse .nav-collapse .nav > li > a:focus, - .navbar-inverse .nav-collapse .dropdown-menu a:hover, - .navbar-inverse .nav-collapse .dropdown-menu a:focus { - background-color: @navbarInverseBackground; - } - // Buttons in the navbar - .nav-collapse.in .btn-group { - margin-top: 5px; - padding: 0; - } - // Dropdowns in the navbar - .nav-collapse .dropdown-menu { - position: static; - top: auto; - left: auto; - float: none; - display: none; - max-width: none; - margin: 0 15px; - padding: 0; - background-color: transparent; - border: none; - .border-radius(0); - .box-shadow(none); - } - .nav-collapse .open > .dropdown-menu { - display: block; - } - - .nav-collapse .dropdown-menu:before, - .nav-collapse .dropdown-menu:after { - display: none; - } - .nav-collapse .dropdown-menu .divider { - display: none; - } - .nav-collapse .nav > li > .dropdown-menu { - &:before, - &:after { - display: none; - } - } - // Forms in navbar - .nav-collapse .navbar-form, - .nav-collapse .navbar-search { - float: none; - padding: (@baseLineHeight / 2) 15px; - margin: (@baseLineHeight / 2) 0; - border-top: 1px solid @navbarBackground; - border-bottom: 1px solid @navbarBackground; - .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)"); - } - .navbar-inverse .nav-collapse .navbar-form, - .navbar-inverse .nav-collapse .navbar-search { - border-top-color: @navbarInverseBackground; - border-bottom-color: @navbarInverseBackground; - } - // Pull right (secondary) nav content - .navbar .nav-collapse .nav.pull-right { - float: none; - margin-left: 0; - } - // Hide everything in the navbar save .brand and toggle button */ - .nav-collapse, - .nav-collapse.collapse { - overflow: hidden; - height: 0; - } - // Navbar button - .navbar .btn-navbar { - display: block; - } - - // STATIC NAVBAR - // ------------- - .navbar-static .navbar-inner { - padding-left: 10px; - padding-right: 10px; - } - - -} - - -// DEFAULT DESKTOP -// --------------- - -@media (min-width: @navbarCollapseDesktopWidth) { - - // Required to make the collapsing navbar work on regular desktops - .nav-collapse.collapse { - height: auto !important; - overflow: visible !important; - } - -} diff --git a/pykeg/web/static/bootstrap/less/responsive-utilities.less b/pykeg/web/static/bootstrap/less/responsive-utilities.less deleted file mode 100644 index bf43e8ef7..000000000 --- a/pykeg/web/static/bootstrap/less/responsive-utilities.less +++ /dev/null @@ -1,59 +0,0 @@ -// -// Responsive: Utility classes -// -------------------------------------------------- - - -// IE10 Metro responsive -// Required for Windows 8 Metro split-screen snapping with IE10 -// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ -@-ms-viewport{ - width: device-width; -} - -// Hide from screenreaders and browsers -// Credit: HTML5 Boilerplate -.hidden { - display: none; - visibility: hidden; -} - -// Visibility utilities - -// For desktops -.visible-phone { display: none !important; } -.visible-tablet { display: none !important; } -.hidden-phone { } -.hidden-tablet { } -.hidden-desktop { display: none !important; } -.visible-desktop { display: inherit !important; } - -// Tablets & small desktops only -@media (min-width: 768px) and (max-width: 979px) { - // Hide everything else - .hidden-desktop { display: inherit !important; } - .visible-desktop { display: none !important ; } - // Show - .visible-tablet { display: inherit !important; } - // Hide - .hidden-tablet { display: none !important; } -} - -// Phones only -@media (max-width: 767px) { - // Hide everything else - .hidden-desktop { display: inherit !important; } - .visible-desktop { display: none !important; } - // Show - .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior - // Hide - .hidden-phone { display: none !important; } -} - -// Print utilities -.visible-print { display: none !important; } -.hidden-print { } - -@media print { - .visible-print { display: inherit !important; } - .hidden-print { display: none !important; } -} diff --git a/pykeg/web/static/bootstrap/less/responsive.less b/pykeg/web/static/bootstrap/less/responsive.less deleted file mode 100644 index b8366defb..000000000 --- a/pykeg/web/static/bootstrap/less/responsive.less +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * Bootstrap Responsive v2.3.1 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - - -// Responsive.less -// For phone and tablet devices -// ------------------------------------------------------------- - - -// REPEAT VARIABLES & MIXINS -// ------------------------- -// Required since we compile the responsive stuff separately - -@import "variables.less"; // Modify this for custom colors, font-sizes, etc -@import "mixins.less"; - - -// RESPONSIVE CLASSES -// ------------------ - -@import "responsive-utilities.less"; - - -// MEDIA QUERIES -// ------------------ - -// Large desktops -@import "responsive-1200px-min.less"; - -// Tablets to regular desktops -@import "responsive-768px-979px.less"; - -// Phones to portrait tablets and narrow desktops -@import "responsive-767px-max.less"; - - -// RESPONSIVE NAVBAR -// ------------------ - -// From 979px and below, show a button to toggle navbar contents -@import "responsive-navbar.less"; diff --git a/pykeg/web/static/bootstrap/less/scaffolding.less b/pykeg/web/static/bootstrap/less/scaffolding.less deleted file mode 100644 index f17e8cadb..000000000 --- a/pykeg/web/static/bootstrap/less/scaffolding.less +++ /dev/null @@ -1,53 +0,0 @@ -// -// Scaffolding -// -------------------------------------------------- - - -// Body reset -// ------------------------- - -body { - margin: 0; - font-family: @baseFontFamily; - font-size: @baseFontSize; - line-height: @baseLineHeight; - color: @textColor; - background-color: @bodyBackground; -} - - -// Links -// ------------------------- - -a { - color: @linkColor; - text-decoration: none; -} -a:hover, -a:focus { - color: @linkColorHover; - text-decoration: underline; -} - - -// Images -// ------------------------- - -// Rounded corners -.img-rounded { - .border-radius(6px); -} - -// Add polaroid-esque trim -.img-polaroid { - padding: 4px; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0,0,0,.2); - .box-shadow(0 1px 3px rgba(0,0,0,.1)); -} - -// Perfect circle -.img-circle { - .border-radius(500px); // crank the border-radius so it works with most reasonably sized images -} diff --git a/pykeg/web/static/bootstrap/less/sprites.less b/pykeg/web/static/bootstrap/less/sprites.less deleted file mode 100644 index 1812bf71a..000000000 --- a/pykeg/web/static/bootstrap/less/sprites.less +++ /dev/null @@ -1,197 +0,0 @@ -// -// Sprites -// -------------------------------------------------- - - -// ICONS -// ----- - -// All icons receive the styles of the <i> tag with a base class -// of .i and are then given a unique class to add width, height, -// and background-position. Your resulting HTML will look like -// <i class="icon-inbox"></i>. - -// For the white version of the icons, just add the .icon-white class: -// <i class="icon-inbox icon-white"></i> - -[class^="icon-"], -[class*=" icon-"] { - display: inline-block; - width: 14px; - height: 14px; - .ie7-restore-right-whitespace(); - line-height: 14px; - vertical-align: text-top; - background-image: url("@{iconSpritePath}"); - background-position: 14px 14px; - background-repeat: no-repeat; - margin-top: 1px; -} - -/* White icons with optional class, or on hover/focus/active states of certain elements */ -.icon-white, -.nav-pills > .active > a > [class^="icon-"], -.nav-pills > .active > a > [class*=" icon-"], -.nav-list > .active > a > [class^="icon-"], -.nav-list > .active > a > [class*=" icon-"], -.navbar-inverse .nav > .active > a > [class^="icon-"], -.navbar-inverse .nav > .active > a > [class*=" icon-"], -.dropdown-menu > li > a:hover > [class^="icon-"], -.dropdown-menu > li > a:focus > [class^="icon-"], -.dropdown-menu > li > a:hover > [class*=" icon-"], -.dropdown-menu > li > a:focus > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:focus > a > [class^="icon-"], -.dropdown-submenu:hover > a > [class*=" icon-"], -.dropdown-submenu:focus > a > [class*=" icon-"] { - background-image: url("@{iconWhiteSpritePath}"); -} - -.icon-glass { background-position: 0 0; } -.icon-music { background-position: -24px 0; } -.icon-search { background-position: -48px 0; } -.icon-envelope { background-position: -72px 0; } -.icon-heart { background-position: -96px 0; } -.icon-star { background-position: -120px 0; } -.icon-star-empty { background-position: -144px 0; } -.icon-user { background-position: -168px 0; } -.icon-film { background-position: -192px 0; } -.icon-th-large { background-position: -216px 0; } -.icon-th { background-position: -240px 0; } -.icon-th-list { background-position: -264px 0; } -.icon-ok { background-position: -288px 0; } -.icon-remove { background-position: -312px 0; } -.icon-zoom-in { background-position: -336px 0; } -.icon-zoom-out { background-position: -360px 0; } -.icon-off { background-position: -384px 0; } -.icon-signal { background-position: -408px 0; } -.icon-cog { background-position: -432px 0; } -.icon-trash { background-position: -456px 0; } - -.icon-home { background-position: 0 -24px; } -.icon-file { background-position: -24px -24px; } -.icon-time { background-position: -48px -24px; } -.icon-road { background-position: -72px -24px; } -.icon-download-alt { background-position: -96px -24px; } -.icon-download { background-position: -120px -24px; } -.icon-upload { background-position: -144px -24px; } -.icon-inbox { background-position: -168px -24px; } -.icon-play-circle { background-position: -192px -24px; } -.icon-repeat { background-position: -216px -24px; } -.icon-refresh { background-position: -240px -24px; } -.icon-list-alt { background-position: -264px -24px; } -.icon-lock { background-position: -287px -24px; } // 1px off -.icon-flag { background-position: -312px -24px; } -.icon-headphones { background-position: -336px -24px; } -.icon-volume-off { background-position: -360px -24px; } -.icon-volume-down { background-position: -384px -24px; } -.icon-volume-up { background-position: -408px -24px; } -.icon-qrcode { background-position: -432px -24px; } -.icon-barcode { background-position: -456px -24px; } - -.icon-tag { background-position: 0 -48px; } -.icon-tags { background-position: -25px -48px; } // 1px off -.icon-book { background-position: -48px -48px; } -.icon-bookmark { background-position: -72px -48px; } -.icon-print { background-position: -96px -48px; } -.icon-camera { background-position: -120px -48px; } -.icon-font { background-position: -144px -48px; } -.icon-bold { background-position: -167px -48px; } // 1px off -.icon-italic { background-position: -192px -48px; } -.icon-text-height { background-position: -216px -48px; } -.icon-text-width { background-position: -240px -48px; } -.icon-align-left { background-position: -264px -48px; } -.icon-align-center { background-position: -288px -48px; } -.icon-align-right { background-position: -312px -48px; } -.icon-align-justify { background-position: -336px -48px; } -.icon-list { background-position: -360px -48px; } -.icon-indent-left { background-position: -384px -48px; } -.icon-indent-right { background-position: -408px -48px; } -.icon-facetime-video { background-position: -432px -48px; } -.icon-picture { background-position: -456px -48px; } - -.icon-pencil { background-position: 0 -72px; } -.icon-map-marker { background-position: -24px -72px; } -.icon-adjust { background-position: -48px -72px; } -.icon-tint { background-position: -72px -72px; } -.icon-edit { background-position: -96px -72px; } -.icon-share { background-position: -120px -72px; } -.icon-check { background-position: -144px -72px; } -.icon-move { background-position: -168px -72px; } -.icon-step-backward { background-position: -192px -72px; } -.icon-fast-backward { background-position: -216px -72px; } -.icon-backward { background-position: -240px -72px; } -.icon-play { background-position: -264px -72px; } -.icon-pause { background-position: -288px -72px; } -.icon-stop { background-position: -312px -72px; } -.icon-forward { background-position: -336px -72px; } -.icon-fast-forward { background-position: -360px -72px; } -.icon-step-forward { background-position: -384px -72px; } -.icon-eject { background-position: -408px -72px; } -.icon-chevron-left { background-position: -432px -72px; } -.icon-chevron-right { background-position: -456px -72px; } - -.icon-plus-sign { background-position: 0 -96px; } -.icon-minus-sign { background-position: -24px -96px; } -.icon-remove-sign { background-position: -48px -96px; } -.icon-ok-sign { background-position: -72px -96px; } -.icon-question-sign { background-position: -96px -96px; } -.icon-info-sign { background-position: -120px -96px; } -.icon-screenshot { background-position: -144px -96px; } -.icon-remove-circle { background-position: -168px -96px; } -.icon-ok-circle { background-position: -192px -96px; } -.icon-ban-circle { background-position: -216px -96px; } -.icon-arrow-left { background-position: -240px -96px; } -.icon-arrow-right { background-position: -264px -96px; } -.icon-arrow-up { background-position: -289px -96px; } // 1px off -.icon-arrow-down { background-position: -312px -96px; } -.icon-share-alt { background-position: -336px -96px; } -.icon-resize-full { background-position: -360px -96px; } -.icon-resize-small { background-position: -384px -96px; } -.icon-plus { background-position: -408px -96px; } -.icon-minus { background-position: -433px -96px; } -.icon-asterisk { background-position: -456px -96px; } - -.icon-exclamation-sign { background-position: 0 -120px; } -.icon-gift { background-position: -24px -120px; } -.icon-leaf { background-position: -48px -120px; } -.icon-fire { background-position: -72px -120px; } -.icon-eye-open { background-position: -96px -120px; } -.icon-eye-close { background-position: -120px -120px; } -.icon-warning-sign { background-position: -144px -120px; } -.icon-plane { background-position: -168px -120px; } -.icon-calendar { background-position: -192px -120px; } -.icon-random { background-position: -216px -120px; width: 16px; } -.icon-comment { background-position: -240px -120px; } -.icon-magnet { background-position: -264px -120px; } -.icon-chevron-up { background-position: -288px -120px; } -.icon-chevron-down { background-position: -313px -119px; } // 1px, 1px off -.icon-retweet { background-position: -336px -120px; } -.icon-shopping-cart { background-position: -360px -120px; } -.icon-folder-close { background-position: -384px -120px; width: 16px; } -.icon-folder-open { background-position: -408px -120px; width: 16px; } -.icon-resize-vertical { background-position: -432px -119px; } // 1px, 1px off -.icon-resize-horizontal { background-position: -456px -118px; } // 1px, 2px off - -.icon-hdd { background-position: 0 -144px; } -.icon-bullhorn { background-position: -24px -144px; } -.icon-bell { background-position: -48px -144px; } -.icon-certificate { background-position: -72px -144px; } -.icon-thumbs-up { background-position: -96px -144px; } -.icon-thumbs-down { background-position: -120px -144px; } -.icon-hand-right { background-position: -144px -144px; } -.icon-hand-left { background-position: -168px -144px; } -.icon-hand-up { background-position: -192px -144px; } -.icon-hand-down { background-position: -216px -144px; } -.icon-circle-arrow-right { background-position: -240px -144px; } -.icon-circle-arrow-left { background-position: -264px -144px; } -.icon-circle-arrow-up { background-position: -288px -144px; } -.icon-circle-arrow-down { background-position: -312px -144px; } -.icon-globe { background-position: -336px -144px; } -.icon-wrench { background-position: -360px -144px; } -.icon-tasks { background-position: -384px -144px; } -.icon-filter { background-position: -408px -144px; } -.icon-briefcase { background-position: -432px -144px; } -.icon-fullscreen { background-position: -456px -144px; } diff --git a/pykeg/web/static/bootstrap/less/tables.less b/pykeg/web/static/bootstrap/less/tables.less deleted file mode 100644 index 0e35271e1..000000000 --- a/pykeg/web/static/bootstrap/less/tables.less +++ /dev/null @@ -1,244 +0,0 @@ -// -// Tables -// -------------------------------------------------- - - -// BASE TABLES -// ----------------- - -table { - max-width: 100%; - background-color: @tableBackground; - border-collapse: collapse; - border-spacing: 0; -} - -// BASELINE STYLES -// --------------- - -.table { - width: 100%; - margin-bottom: @baseLineHeight; - // Cells - th, - td { - padding: 8px; - line-height: @baseLineHeight; - text-align: left; - vertical-align: top; - border-top: 1px solid @tableBorder; - } - th { - font-weight: bold; - } - // Bottom align for column headings - thead th { - vertical-align: bottom; - } - // Remove top border from thead by default - caption + thead tr:first-child th, - caption + thead tr:first-child td, - colgroup + thead tr:first-child th, - colgroup + thead tr:first-child td, - thead:first-child tr:first-child th, - thead:first-child tr:first-child td { - border-top: 0; - } - // Account for multiple tbody instances - tbody + tbody { - border-top: 2px solid @tableBorder; - } - - // Nesting - .table { - background-color: @bodyBackground; - } -} - - - -// CONDENSED TABLE W/ HALF PADDING -// ------------------------------- - -.table-condensed { - th, - td { - padding: 4px 5px; - } -} - - -// BORDERED VERSION -// ---------------- - -.table-bordered { - border: 1px solid @tableBorder; - border-collapse: separate; // Done so we can round those corners! - *border-collapse: collapse; // IE7 can't round corners anyway - border-left: 0; - .border-radius(@baseBorderRadius); - th, - td { - border-left: 1px solid @tableBorder; - } - // Prevent a double border - caption + thead tr:first-child th, - caption + tbody tr:first-child th, - caption + tbody tr:first-child td, - colgroup + thead tr:first-child th, - colgroup + tbody tr:first-child th, - colgroup + tbody tr:first-child td, - thead:first-child tr:first-child th, - tbody:first-child tr:first-child th, - tbody:first-child tr:first-child td { - border-top: 0; - } - // For first th/td in the first row in the first thead or tbody - thead:first-child tr:first-child > th:first-child, - tbody:first-child tr:first-child > td:first-child, - tbody:first-child tr:first-child > th:first-child { - .border-top-left-radius(@baseBorderRadius); - } - // For last th/td in the first row in the first thead or tbody - thead:first-child tr:first-child > th:last-child, - tbody:first-child tr:first-child > td:last-child, - tbody:first-child tr:first-child > th:last-child { - .border-top-right-radius(@baseBorderRadius); - } - // For first th/td (can be either) in the last row in the last thead, tbody, and tfoot - thead:last-child tr:last-child > th:first-child, - tbody:last-child tr:last-child > td:first-child, - tbody:last-child tr:last-child > th:first-child, - tfoot:last-child tr:last-child > td:first-child, - tfoot:last-child tr:last-child > th:first-child { - .border-bottom-left-radius(@baseBorderRadius); - } - // For last th/td (can be either) in the last row in the last thead, tbody, and tfoot - thead:last-child tr:last-child > th:last-child, - tbody:last-child tr:last-child > td:last-child, - tbody:last-child tr:last-child > th:last-child, - tfoot:last-child tr:last-child > td:last-child, - tfoot:last-child tr:last-child > th:last-child { - .border-bottom-right-radius(@baseBorderRadius); - } - - // Clear border-radius for first and last td in the last row in the last tbody for table with tfoot - tfoot + tbody:last-child tr:last-child td:first-child { - .border-bottom-left-radius(0); - } - tfoot + tbody:last-child tr:last-child td:last-child { - .border-bottom-right-radius(0); - } - - // Special fixes to round the left border on the first td/th - caption + thead tr:first-child th:first-child, - caption + tbody tr:first-child td:first-child, - colgroup + thead tr:first-child th:first-child, - colgroup + tbody tr:first-child td:first-child { - .border-top-left-radius(@baseBorderRadius); - } - caption + thead tr:first-child th:last-child, - caption + tbody tr:first-child td:last-child, - colgroup + thead tr:first-child th:last-child, - colgroup + tbody tr:first-child td:last-child { - .border-top-right-radius(@baseBorderRadius); - } - -} - - - - -// ZEBRA-STRIPING -// -------------- - -// Default zebra-stripe styles (alternating gray and transparent backgrounds) -.table-striped { - tbody { - > tr:nth-child(odd) > td, - > tr:nth-child(odd) > th { - background-color: @tableBackgroundAccent; - } - } -} - - -// HOVER EFFECT -// ------------ -// Placed here since it has to come after the potential zebra striping -.table-hover { - tbody { - tr:hover > td, - tr:hover > th { - background-color: @tableBackgroundHover; - } - } -} - - -// TABLE CELL SIZING -// ----------------- - -// Reset default grid behavior -table td[class*="span"], -table th[class*="span"], -.row-fluid table td[class*="span"], -.row-fluid table th[class*="span"] { - display: table-cell; - float: none; // undo default grid column styles - margin-left: 0; // undo default grid column styles -} - -// Change the column widths to account for td/th padding -.table td, -.table th { - &.span1 { .tableColumns(1); } - &.span2 { .tableColumns(2); } - &.span3 { .tableColumns(3); } - &.span4 { .tableColumns(4); } - &.span5 { .tableColumns(5); } - &.span6 { .tableColumns(6); } - &.span7 { .tableColumns(7); } - &.span8 { .tableColumns(8); } - &.span9 { .tableColumns(9); } - &.span10 { .tableColumns(10); } - &.span11 { .tableColumns(11); } - &.span12 { .tableColumns(12); } -} - - - -// TABLE BACKGROUNDS -// ----------------- -// Exact selectors below required to override .table-striped - -.table tbody tr { - &.success > td { - background-color: @successBackground; - } - &.error > td { - background-color: @errorBackground; - } - &.warning > td { - background-color: @warningBackground; - } - &.info > td { - background-color: @infoBackground; - } -} - -// Hover states for .table-hover -.table-hover tbody tr { - &.success:hover > td { - background-color: darken(@successBackground, 5%); - } - &.error:hover > td { - background-color: darken(@errorBackground, 5%); - } - &.warning:hover > td { - background-color: darken(@warningBackground, 5%); - } - &.info:hover > td { - background-color: darken(@infoBackground, 5%); - } -} diff --git a/pykeg/web/static/bootstrap/less/thumbnails.less b/pykeg/web/static/bootstrap/less/thumbnails.less deleted file mode 100644 index 4fd07d253..000000000 --- a/pykeg/web/static/bootstrap/less/thumbnails.less +++ /dev/null @@ -1,53 +0,0 @@ -// -// Thumbnails -// -------------------------------------------------- - - -// Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files - -// Make wrapper ul behave like the grid -.thumbnails { - margin-left: -@gridGutterWidth; - list-style: none; - .clearfix(); -} -// Fluid rows have no left margin -.row-fluid .thumbnails { - margin-left: 0; -} - -// Float li to make thumbnails appear in a row -.thumbnails > li { - float: left; // Explicity set the float since we don't require .span* classes - margin-bottom: @baseLineHeight; - margin-left: @gridGutterWidth; -} - -// The actual thumbnail (can be `a` or `div`) -.thumbnail { - display: block; - padding: 4px; - line-height: @baseLineHeight; - border: 1px solid #ddd; - .border-radius(@baseBorderRadius); - .box-shadow(0 1px 3px rgba(0,0,0,.055)); - .transition(all .2s ease-in-out); -} -// Add a hover/focus state for linked versions only -a.thumbnail:hover, -a.thumbnail:focus { - border-color: @linkColor; - .box-shadow(0 1px 4px rgba(0,105,214,.25)); -} - -// Images and captions -.thumbnail > img { - display: block; - max-width: 100%; - margin-left: auto; - margin-right: auto; -} -.thumbnail .caption { - padding: 9px; - color: @gray; -} diff --git a/pykeg/web/static/bootstrap/less/tooltip.less b/pykeg/web/static/bootstrap/less/tooltip.less deleted file mode 100644 index 83d5f2bd7..000000000 --- a/pykeg/web/static/bootstrap/less/tooltip.less +++ /dev/null @@ -1,70 +0,0 @@ -// -// Tooltips -// -------------------------------------------------- - - -// Base class -.tooltip { - position: absolute; - z-index: @zindexTooltip; - display: block; - visibility: visible; - font-size: 11px; - line-height: 1.4; - .opacity(0); - &.in { .opacity(80); } - &.top { margin-top: -3px; padding: 5px 0; } - &.right { margin-left: 3px; padding: 0 5px; } - &.bottom { margin-top: 3px; padding: 5px 0; } - &.left { margin-left: -3px; padding: 0 5px; } -} - -// Wrapper for the tooltip content -.tooltip-inner { - max-width: 200px; - padding: 8px; - color: @tooltipColor; - text-align: center; - text-decoration: none; - background-color: @tooltipBackground; - .border-radius(@baseBorderRadius); -} - -// Arrows -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip { - &.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -@tooltipArrowWidth; - border-width: @tooltipArrowWidth @tooltipArrowWidth 0; - border-top-color: @tooltipArrowColor; - } - &.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -@tooltipArrowWidth; - border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; - border-right-color: @tooltipArrowColor; - } - &.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -@tooltipArrowWidth; - border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; - border-left-color: @tooltipArrowColor; - } - &.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -@tooltipArrowWidth; - border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; - border-bottom-color: @tooltipArrowColor; - } -} diff --git a/pykeg/web/static/bootstrap/less/type.less b/pykeg/web/static/bootstrap/less/type.less deleted file mode 100644 index 337138ac8..000000000 --- a/pykeg/web/static/bootstrap/less/type.less +++ /dev/null @@ -1,247 +0,0 @@ -// -// Typography -// -------------------------------------------------- - - -// Body text -// ------------------------- - -p { - margin: 0 0 @baseLineHeight / 2; -} -.lead { - margin-bottom: @baseLineHeight; - font-size: @baseFontSize * 1.5; - font-weight: 200; - line-height: @baseLineHeight * 1.5; -} - - -// Emphasis & misc -// ------------------------- - -// Ex: 14px base font * 85% = about 12px -small { font-size: 85%; } - -strong { font-weight: bold; } -em { font-style: italic; } -cite { font-style: normal; } - -// Utility classes -.muted { color: @grayLight; } -a.muted:hover, -a.muted:focus { color: darken(@grayLight, 10%); } - -.text-warning { color: @warningText; } -a.text-warning:hover, -a.text-warning:focus { color: darken(@warningText, 10%); } - -.text-error { color: @errorText; } -a.text-error:hover, -a.text-error:focus { color: darken(@errorText, 10%); } - -.text-info { color: @infoText; } -a.text-info:hover, -a.text-info:focus { color: darken(@infoText, 10%); } - -.text-success { color: @successText; } -a.text-success:hover, -a.text-success:focus { color: darken(@successText, 10%); } - -.text-left { text-align: left; } -.text-right { text-align: right; } -.text-center { text-align: center; } - - -// Headings -// ------------------------- - -h1, h2, h3, h4, h5, h6 { - margin: (@baseLineHeight / 2) 0; - font-family: @headingsFontFamily; - font-weight: @headingsFontWeight; - line-height: @baseLineHeight; - color: @headingsColor; - text-rendering: optimizelegibility; // Fix the character spacing for headings - small { - font-weight: normal; - line-height: 1; - color: @grayLight; - } -} - -h1, -h2, -h3 { line-height: @baseLineHeight * 2; } - -h1 { font-size: @baseFontSize * 2.75; } // ~38px -h2 { font-size: @baseFontSize * 2.25; } // ~32px -h3 { font-size: @baseFontSize * 1.75; } // ~24px -h4 { font-size: @baseFontSize * 1.25; } // ~18px -h5 { font-size: @baseFontSize; } -h6 { font-size: @baseFontSize * 0.85; } // ~12px - -h1 small { font-size: @baseFontSize * 1.75; } // ~24px -h2 small { font-size: @baseFontSize * 1.25; } // ~18px -h3 small { font-size: @baseFontSize; } -h4 small { font-size: @baseFontSize; } - - -// Page header -// ------------------------- - -.page-header { - padding-bottom: (@baseLineHeight / 2) - 1; - margin: @baseLineHeight 0 (@baseLineHeight * 1.5); - border-bottom: 1px solid @grayLighter; -} - - - -// Lists -// -------------------------------------------------- - -// Unordered and Ordered lists -ul, ol { - padding: 0; - margin: 0 0 @baseLineHeight / 2 25px; -} -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} -li { - line-height: @baseLineHeight; -} - -// Remove default list styles -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} - -// Single-line list items -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; - > li { - display: inline-block; - .ie7-inline-block(); - padding-left: 5px; - padding-right: 5px; - } -} - -// Description Lists -dl { - margin-bottom: @baseLineHeight; -} -dt, -dd { - line-height: @baseLineHeight; -} -dt { - font-weight: bold; -} -dd { - margin-left: @baseLineHeight / 2; -} -// Horizontal layout (like forms) -.dl-horizontal { - .clearfix(); // Ensure dl clears floats if empty dd elements present - dt { - float: left; - width: @horizontalComponentOffset - 20; - clear: left; - text-align: right; - .text-overflow(); - } - dd { - margin-left: @horizontalComponentOffset; - } -} - -// MISC -// ---- - -// Horizontal rules -hr { - margin: @baseLineHeight 0; - border: 0; - border-top: 1px solid @hrBorder; - border-bottom: 1px solid @white; -} - -// Abbreviations and acronyms -abbr[title], -// Added data-* attribute to help out our tooltip plugin, per https://github.com/twitter/bootstrap/issues/5257 -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted @grayLight; -} -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} - -// Blockquotes -blockquote { - padding: 0 0 0 15px; - margin: 0 0 @baseLineHeight; - border-left: 5px solid @grayLighter; - p { - margin-bottom: 0; - font-size: @baseFontSize * 1.25; - font-weight: 300; - line-height: 1.25; - } - small { - display: block; - line-height: @baseLineHeight; - color: @grayLight; - &:before { - content: '\2014 \00A0'; - } - } - - // Float right with text-align: right - &.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid @grayLighter; - border-left: 0; - p, - small { - text-align: right; - } - small { - &:before { - content: ''; - } - &:after { - content: '\00A0 \2014'; - } - } - } -} - -// Quotes -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} - -// Addresses -address { - display: block; - margin-bottom: @baseLineHeight; - font-style: normal; - line-height: @baseLineHeight; -} diff --git a/pykeg/web/static/bootstrap/less/utilities.less b/pykeg/web/static/bootstrap/less/utilities.less deleted file mode 100644 index 314b4ffdb..000000000 --- a/pykeg/web/static/bootstrap/less/utilities.less +++ /dev/null @@ -1,30 +0,0 @@ -// -// Utility classes -// -------------------------------------------------- - - -// Quick floats -.pull-right { - float: right; -} -.pull-left { - float: left; -} - -// Toggling content -.hide { - display: none; -} -.show { - display: block; -} - -// Visibility -.invisible { - visibility: hidden; -} - -// For Affix plugin -.affix { - position: fixed; -} diff --git a/pykeg/web/static/bootstrap/less/variables.less b/pykeg/web/static/bootstrap/less/variables.less deleted file mode 100644 index 31c131b1e..000000000 --- a/pykeg/web/static/bootstrap/less/variables.less +++ /dev/null @@ -1,301 +0,0 @@ -// -// Variables -// -------------------------------------------------- - - -// Global values -// -------------------------------------------------- - - -// Grays -// ------------------------- -@black: #000; -@grayDarker: #222; -@grayDark: #333; -@gray: #555; -@grayLight: #999; -@grayLighter: #eee; -@white: #fff; - - -// Accent colors -// ------------------------- -@blue: #049cdb; -@blueDark: #0064cd; -@green: #46a546; -@red: #9d261d; -@yellow: #ffc40d; -@orange: #f89406; -@pink: #c3325f; -@purple: #7a43b6; - - -// Scaffolding -// ------------------------- -@bodyBackground: @white; -@textColor: @grayDark; - - -// Links -// ------------------------- -@linkColor: #08c; -@linkColorHover: darken(@linkColor, 15%); - - -// Typography -// ------------------------- -@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; -@serifFontFamily: Georgia, "Times New Roman", Times, serif; -@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace; - -@baseFontSize: 14px; -@baseFontFamily: @sansFontFamily; -@baseLineHeight: 20px; -@altFontFamily: @serifFontFamily; - -@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily -@headingsFontWeight: bold; // instead of browser default, bold -@headingsColor: inherit; // empty to use BS default, @textColor - - -// Component sizing -// ------------------------- -// Based on 14px font-size and 20px line-height - -@fontSizeLarge: @baseFontSize * 1.25; // ~18px -@fontSizeSmall: @baseFontSize * 0.85; // ~12px -@fontSizeMini: @baseFontSize * 0.75; // ~11px - -@paddingLarge: 11px 19px; // 44px -@paddingSmall: 2px 10px; // 26px -@paddingMini: 0 6px; // 22px - -@baseBorderRadius: 4px; -@borderRadiusLarge: 6px; -@borderRadiusSmall: 3px; - - -// Tables -// ------------------------- -@tableBackground: transparent; // overall background-color -@tableBackgroundAccent: #f9f9f9; // for striping -@tableBackgroundHover: #f5f5f5; // for hover -@tableBorder: #ddd; // table and cell border - -// Buttons -// ------------------------- -@btnBackground: @white; -@btnBackgroundHighlight: darken(@white, 10%); -@btnBorder: #ccc; - -@btnPrimaryBackground: @linkColor; -@btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%); - -@btnInfoBackground: #5bc0de; -@btnInfoBackgroundHighlight: #2f96b4; - -@btnSuccessBackground: #62c462; -@btnSuccessBackgroundHighlight: #51a351; - -@btnWarningBackground: lighten(@orange, 15%); -@btnWarningBackgroundHighlight: @orange; - -@btnDangerBackground: #ee5f5b; -@btnDangerBackgroundHighlight: #bd362f; - -@btnInverseBackground: #444; -@btnInverseBackgroundHighlight: @grayDarker; - - -// Forms -// ------------------------- -@inputBackground: @white; -@inputBorder: #ccc; -@inputBorderRadius: @baseBorderRadius; -@inputDisabledBackground: @grayLighter; -@formActionsBackground: #f5f5f5; -@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border - - -// Dropdowns -// ------------------------- -@dropdownBackground: @white; -@dropdownBorder: rgba(0,0,0,.2); -@dropdownDividerTop: #e5e5e5; -@dropdownDividerBottom: @white; - -@dropdownLinkColor: @grayDark; -@dropdownLinkColorHover: @white; -@dropdownLinkColorActive: @white; - -@dropdownLinkBackgroundActive: @linkColor; -@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive; - - - -// COMPONENT VARIABLES -// -------------------------------------------------- - - -// Z-index master list -// ------------------------- -// Used for a bird's eye view of components dependent on the z-axis -// Try to avoid customizing these :) -@zindexDropdown: 1000; -@zindexPopover: 1010; -@zindexTooltip: 1030; -@zindexFixedNavbar: 1030; -@zindexModalBackdrop: 1040; -@zindexModal: 1050; - - -// Sprite icons path -// ------------------------- -@iconSpritePath: "../img/glyphicons-halflings.png"; -@iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; - - -// Input placeholder text color -// ------------------------- -@placeholderText: @grayLight; - - -// Hr border color -// ------------------------- -@hrBorder: @grayLighter; - - -// Horizontal forms & lists -// ------------------------- -@horizontalComponentOffset: 180px; - - -// Wells -// ------------------------- -@wellBackground: #f5f5f5; - - -// Navbar -// ------------------------- -@navbarCollapseWidth: 979px; -@navbarCollapseDesktopWidth: @navbarCollapseWidth + 1; - -@navbarHeight: 40px; -@navbarBackgroundHighlight: #ffffff; -@navbarBackground: darken(@navbarBackgroundHighlight, 5%); -@navbarBorder: darken(@navbarBackground, 12%); - -@navbarText: #777; -@navbarLinkColor: #777; -@navbarLinkColorHover: @grayDark; -@navbarLinkColorActive: @gray; -@navbarLinkBackgroundHover: transparent; -@navbarLinkBackgroundActive: darken(@navbarBackground, 5%); - -@navbarBrandColor: @navbarLinkColor; - -// Inverted navbar -@navbarInverseBackground: #111111; -@navbarInverseBackgroundHighlight: #222222; -@navbarInverseBorder: #252525; - -@navbarInverseText: @grayLight; -@navbarInverseLinkColor: @grayLight; -@navbarInverseLinkColorHover: @white; -@navbarInverseLinkColorActive: @navbarInverseLinkColorHover; -@navbarInverseLinkBackgroundHover: transparent; -@navbarInverseLinkBackgroundActive: @navbarInverseBackground; - -@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%); -@navbarInverseSearchBackgroundFocus: @white; -@navbarInverseSearchBorder: @navbarInverseBackground; -@navbarInverseSearchPlaceholderColor: #ccc; - -@navbarInverseBrandColor: @navbarInverseLinkColor; - - -// Pagination -// ------------------------- -@paginationBackground: #fff; -@paginationBorder: #ddd; -@paginationActiveBackground: #f5f5f5; - - -// Hero unit -// ------------------------- -@heroUnitBackground: @grayLighter; -@heroUnitHeadingColor: inherit; -@heroUnitLeadColor: inherit; - - -// Form states and alerts -// ------------------------- -@warningText: #c09853; -@warningBackground: #fcf8e3; -@warningBorder: darken(spin(@warningBackground, -10), 3%); - -@errorText: #b94a48; -@errorBackground: #f2dede; -@errorBorder: darken(spin(@errorBackground, -10), 3%); - -@successText: #468847; -@successBackground: #dff0d8; -@successBorder: darken(spin(@successBackground, -10), 5%); - -@infoText: #3a87ad; -@infoBackground: #d9edf7; -@infoBorder: darken(spin(@infoBackground, -10), 7%); - - -// Tooltips and popovers -// ------------------------- -@tooltipColor: #fff; -@tooltipBackground: #000; -@tooltipArrowWidth: 5px; -@tooltipArrowColor: @tooltipBackground; - -@popoverBackground: #fff; -@popoverArrowWidth: 10px; -@popoverArrowColor: #fff; -@popoverTitleBackground: darken(@popoverBackground, 3%); - -// Special enhancement for popovers -@popoverArrowOuterWidth: @popoverArrowWidth + 1; -@popoverArrowOuterColor: rgba(0,0,0,.25); - - - -// GRID -// -------------------------------------------------- - - -// Default 940px grid -// ------------------------- -@gridColumns: 12; -@gridColumnWidth: 60px; -@gridGutterWidth: 20px; -@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); - -// 1200px min -@gridColumnWidth1200: 70px; -@gridGutterWidth1200: 30px; -@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1)); - -// 768px-979px -@gridColumnWidth768: 42px; -@gridGutterWidth768: 20px; -@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1)); - - -// Fluid grid -// ------------------------- -@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth); -@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth); - -// 1200px min -@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200); -@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200); - -// 768px-979px -@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768); -@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768); diff --git a/pykeg/web/static/bootstrap/less/wells.less b/pykeg/web/static/bootstrap/less/wells.less deleted file mode 100644 index 84a744b1c..000000000 --- a/pykeg/web/static/bootstrap/less/wells.less +++ /dev/null @@ -1,29 +0,0 @@ -// -// Wells -// -------------------------------------------------- - - -// Base class -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: @wellBackground; - border: 1px solid darken(@wellBackground, 7%); - .border-radius(@baseBorderRadius); - .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); - blockquote { - border-color: #ddd; - border-color: rgba(0,0,0,.15); - } -} - -// Sizes -.well-large { - padding: 24px; - .border-radius(@borderRadiusLarge); -} -.well-small { - padding: 9px; - .border-radius(@borderRadiusSmall); -} diff --git a/pykeg/web/static/css/bootstrap-cyborg.css b/pykeg/web/static/css/bootstrap-cyborg.css deleted file mode 100644 index 04e8686c2..000000000 --- a/pykeg/web/static/css/bootstrap-cyborg.css +++ /dev/null @@ -1,9 +0,0 @@ -@import url('//fonts.googleapis.com/css?family=Droid+Sans:400,700');/*! - * Bootstrap v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:'Droid Sans',sans-serif;font-size:14px;line-height:20px;color:#999;background-color:#060606}a{color:#33b5e5;text-decoration:none}a:hover,a:focus{color:#fff;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#adafae}a.muted:hover,a.muted:focus{color:#939695}.text-warning{color:#a47e3c}a.text-warning:hover,a.text-warning:focus{color:#7f612e}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#09c}a.text-info:hover,a.text-info:focus{color:#007399}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:normal;line-height:20px;color:#fff;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#adafae}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #222;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #adafae}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#adafae}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#222;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#222;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#adafae}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:'Droid Sans',sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#999;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ccc;border:1px solid #bbb;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#ccc;border:1px solid #bbb}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#adafae;cursor:not-allowed;background-color:#c9c9c9;border-color:#bbb;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#adafae}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#adafae}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#adafae}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#555}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#a47e3c}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#a47e3c}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#7f612e;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ceae78;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ceae78;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ceae78}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#a47e3c;background-color:#eee;border-color:#a47e3c}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#eee;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#eee;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#09c}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#09c}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#09c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#007399;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #3cf;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #3cf;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #3cf}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#09c;background-color:#eee;border-color:#09c}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:transparent;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#bfbfbf}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#bf3;border-color:#690}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #222}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #222}.table .table{background-color:#060606}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #222;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #222}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:rgba(100,100,100,0.1)}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#222}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#eee}.table tbody tr.error>td{background-color:#eee}.table tbody tr.warning>td{background-color:#eee}.table tbody tr.info>td{background-color:#eee}.table-hover tbody tr.success:hover>td{background-color:#e1e1e1}.table-hover tbody tr.error:hover>td{background-color:#e1e1e1}.table-hover tbody tr.warning:hover>td{background-color:#e1e1e1}.table-hover tbody tr.info:hover>td{background-color:#e1e1e1}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../bootstrap/img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../bootstrap/img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#131517;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:transparent;border-bottom:1px solid #222}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#999;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#2ab2e4;background-image:-moz-linear-gradient(top,#33b5e5,#1dade2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#33b5e5),to(#1dade2));background-image:-webkit-linear-gradient(top,#33b5e5,#1dade2);background-image:-o-linear-gradient(top,#33b5e5,#1dade2);background-image:linear-gradient(to bottom,#33b5e5,#1dade2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff33b5e5',endColorstr='#ff1dade2',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#2ab2e4;background-image:-moz-linear-gradient(top,#33b5e5,#1dade2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#33b5e5),to(#1dade2));background-image:-webkit-linear-gradient(top,#33b5e5,#1dade2);background-image:-o-linear-gradient(top,#33b5e5,#1dade2);background-image:linear-gradient(to bottom,#33b5e5,#1dade2);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff33b5e5',endColorstr='#ff1dade2',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#adafae}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#000;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#131517;border:1px solid #030303;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#222;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#616161;*background-color:#595959;background-image:-moz-linear-gradient(top,#666,#595959);background-image:-webkit-gradient(linear,0 0,0 100%,from(#666),to(#595959));background-image:-webkit-linear-gradient(top,#666,#595959);background-image:-o-linear-gradient(top,#666,#595959);background-image:linear-gradient(to bottom,#666,#595959);background-repeat:repeat-x;border:1px solid rgba(0,0,0,0);*border:0;border-color:#595959 #595959 #333;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:rgba(0,0,0,0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff666666',endColorstr='#ff595959',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#222;background-color:#595959;*background-color:#4d4d4d}.btn:active,.btn.active{background-color:#404040 \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#222;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#008ab8;*background-color:#007399;background-image:-moz-linear-gradient(top,#09c,#007399);background-image:-webkit-gradient(linear,0 0,0 100%,from(#09c),to(#007399));background-image:-webkit-linear-gradient(top,#09c,#007399);background-image:-o-linear-gradient(top,#09c,#007399);background-image:linear-gradient(to bottom,#09c,#007399);background-repeat:repeat-x;border-color:#007399 #007399 #00394d;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0099cc',endColorstr='#ff007399',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#007399;*background-color:#006080}.btn-primary:active,.btn-primary.active{background-color:#004d66 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ff9d2e;*background-color:#f80;background-image:-moz-linear-gradient(top,#ffac4d,#f80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffac4d),to(#f80));background-image:-webkit-linear-gradient(top,#ffac4d,#f80);background-image:-o-linear-gradient(top,#ffac4d,#f80);background-image:linear-gradient(to bottom,#ffac4d,#f80);background-repeat:repeat-x;border-color:#f80 #f80 #b35f00;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffac4d',endColorstr='#ffff8800',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f80;*background-color:#e67a00}.btn-warning:active,.btn-warning.active{background-color:#cc6d00 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#eb0000;*background-color:#c00;background-image:-moz-linear-gradient(top,#f00,#c00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f00),to(#c00));background-image:-webkit-linear-gradient(top,#f00,#c00);background-image:-o-linear-gradient(top,#f00,#c00);background-image:linear-gradient(to bottom,#f00,#c00);background-repeat:repeat-x;border-color:#c00 #c00 #800000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff0000',endColorstr='#ffcc0000',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#c00;*background-color:#b30000}.btn-danger:active,.btn-danger.active{background-color:#900 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#7ab800;*background-color:#690;background-image:-moz-linear-gradient(top,#8c0,#690);background-image:-webkit-gradient(linear,0 0,0 100%,from(#8c0),to(#690));background-image:-webkit-linear-gradient(top,#8c0,#690);background-image:-o-linear-gradient(top,#8c0,#690);background-image:linear-gradient(to bottom,#8c0,#690);background-repeat:repeat-x;border-color:#690 #690 #334d00;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff88cc00',endColorstr='#ff669900',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#690;*background-color:#558000}.btn-success:active,.btn-success.active{background-color:#460 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#292929;*background-color:#191919;background-image:-moz-linear-gradient(top,#333,#191919);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#191919));background-image:-webkit-linear-gradient(top,#333,#191919);background-image:-o-linear-gradient(top,#333,#191919);background-image:linear-gradient(to bottom,#333,#191919);background-repeat:repeat-x;border-color:#191919 #191919 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff333333',endColorstr='#ff191919',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#191919;*background-color:#0d0d0d}.btn-info:active,.btn-info.active{background-color:#000 \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#9f3fcf;*background-color:#93c;background-image:-moz-linear-gradient(top,#a347d1,#93c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#a347d1),to(#93c));background-image:-webkit-linear-gradient(top,#a347d1,#93c);background-image:-o-linear-gradient(top,#a347d1,#93c);background-image:linear-gradient(to bottom,#a347d1,#93c);background-repeat:repeat-x;border-color:#93c #93c #6b248f;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffa347d1',endColorstr='#ff9933cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#93c;*background-color:#8a2eb8}.btn-inverse:active,.btn-inverse.active{background-color:#7a29a3 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#33b5e5;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#fff;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#222;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#595959}.btn-group.open .btn-primary.dropdown-toggle{background-color:#007399}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f80}.btn-group.open .btn-danger.dropdown-toggle{background-color:#c00}.btn-group.open .btn-success.dropdown-toggle{background-color:#690}.btn-group.open .btn-info.dropdown-toggle{background-color:#191919}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#93c}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#eee;border:1px solid transparent;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#a47e3c}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#eee;border-color:#e1e1e1}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#eee;border-color:#e6e6e6}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#09c;background-color:#eee;border-color:#dcdcdc}.alert-info h4{color:#09c}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#adafae;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#33b5e5}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#999;cursor:default;background-color:#060606;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#33b5e5}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#33b5e5;border-bottom-color:#33b5e5}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#adafae;border-color:#adafae}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#adafae}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#adafae}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:50px;padding-right:20px;padding-left:20px;background-color:#020202;background-image:-moz-linear-gradient(top,#020202,#020202);background-image:-webkit-gradient(linear,0 0,0 100%,from(#020202),to(#020202));background-image:-webkit-linear-gradient(top,#020202,#020202);background-image:-o-linear-gradient(top,#020202,#020202);background-image:linear-gradient(to bottom,#020202,#020202);background-repeat:repeat-x;border:1px solid #000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202',endColorstr='#ff020202',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:15px 20px 15px;margin-left:-20px;font-size:20px;font-weight:200;color:#adafae;text-shadow:0 1px 0 #020202}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:50px;color:#adafae}.navbar-link{color:#adafae}.navbar-link:hover,.navbar-link:focus{color:#fff}.navbar .divider-vertical{height:50px;margin:0 9px;border-right:1px solid #020202;border-left:1px solid #020202}.navbar .btn,.navbar .btn-group{margin-top:10px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:10px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:10px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:'Droid Sans',sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:15px 15px 15px;color:#adafae;text-decoration:none;text-shadow:0 1px 0 #020202}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#fff;text-decoration:none;background-color:#020202;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#000;*background-color:#000;background-image:-moz-linear-gradient(top,#000,#000);background-image:-webkit-gradient(linear,0 0,0 100%,from(#000),to(#000));background-image:-webkit-linear-gradient(top,#000,#000);background-image:-o-linear-gradient(top,#000,#000);background-image:linear-gradient(to bottom,#000,#000);background-repeat:repeat-x;border-color:#000 #000 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000',endColorstr='#ff000000',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#000;*background-color:#000}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#000 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #131517;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #131517;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#020202}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#adafae;border-bottom-color:#adafae}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#252a30;background-image:-moz-linear-gradient(top,#252a30,#252a30);background-image:-webkit-gradient(linear,0 0,0 100%,from(#252a30),to(#252a30));background-image:-webkit-linear-gradient(top,#252a30,#252a30);background-image:-o-linear-gradient(top,#252a30,#252a30);background-image:linear-gradient(to bottom,#252a30,#252a30);background-repeat:repeat-x;border-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff252a30',endColorstr='#ff252a30',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#adafae;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#adafae}.navbar-inverse .navbar-text{color:#adafae}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:#242a31}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#242a31}.navbar-inverse .navbar-link{color:#adafae}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#252a30;border-left-color:#252a30}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#242a31}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#adafae;border-bottom-color:#adafae}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#5d6978;border-color:#252a30;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#fff}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#fff}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#fff}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#222;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#1a1d22;*background-color:#1a1d22;background-image:-moz-linear-gradient(top,#1a1d22,#1a1d22);background-image:-webkit-gradient(linear,0 0,0 100%,from(#1a1d22),to(#1a1d22));background-image:-webkit-linear-gradient(top,#1a1d22,#1a1d22);background-image:-o-linear-gradient(top,#1a1d22,#1a1d22);background-image:linear-gradient(to bottom,#1a1d22,#1a1d22);background-repeat:repeat-x;border-color:#1a1d22 #1a1d22 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1a1d22',endColorstr='#ff1a1d22',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#1a1d22;*background-color:#0f1113}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#040405 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#adafae}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#060606;border:1px solid transparent;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#33b5e5}.pagination ul>.active>a,.pagination ul>.active>span{color:#adafae;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#adafae;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#adafae;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1020;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#131517;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#131517;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#131517;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#131517;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#131517;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#131517;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#131517;border-bottom:1px solid #070809;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#131517;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#131517;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#131517;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#131517;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#33b5e5;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#999}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#adafae}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f80}.label-warning[href],.badge-warning[href]{background-color:#cc6d00}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#09c}.label-info[href],.badge-info[href]{background-color:#007399}.label-inverse,.badge-inverse{background-color:#222}.label-inverse[href],.badge-inverse[href]{background-color:#080808}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#ff9d2e;background-image:-moz-linear-gradient(top,#ffac4d,#f80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffac4d),to(#f80));background-image:-webkit-linear-gradient(top,#ffac4d,#f80);background-image:-o-linear-gradient(top,#ffac4d,#f80);background-image:linear-gradient(to bottom,#ffac4d,#f80);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffac4d',endColorstr='#ffff8800',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#ffac4d;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#020202;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#222;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#131517;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}label,input,button,select,textarea,.navbar .search-query:-moz-placeholder,.navbar .search-query::-webkit-input-placeholder{font-family:'Droid Sans',sans-serif;color:#999}code,pre{background-color:#eee}blockquote{border-left:5px solid #222}blockquote.pull-right{border-right:5px solid #222}html{min-height:100%}body{min-height:100%;background-color:#121417;background-image:-moz-linear-gradient(top,#060606,#252a30);background-image:-webkit-gradient(linear,0 0,0 100%,from(#060606),to(#252a30));background-image:-webkit-linear-gradient(top,#060606,#252a30);background-image:-o-linear-gradient(top,#060606,#252a30);background-image:linear-gradient(to bottom,#060606,#252a30);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff060606',endColorstr='#ff252a30',GradientType=0)}.page-header{border-bottom:1px solid #222}hr{border-bottom:0}.navbar .navbar-inner{border-bottom:1px solid #222;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.navbar .brand{padding:15px 20px 15px;font-weight:normal;color:#eee;text-shadow:none}.navbar .nav>li>a{padding:15px 15px 14px;border-bottom:1px solid transparent}.navbar .nav>li>a:hover,.navbar .nav>.active>a,.navbar .nav>.active>a:hover{border-bottom:1px solid #33b5e5}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.navbar .navbar-text{padding:15px 15px 14px;margin-bottom:1px;line-height:inherit}.navbar .divider-vertical{margin:0;border-left:1px solid #222;border-right-width:0}.navbar .search-query,.navbar .search-query:focus,.navbar .search-query.focused{line-height:normal;color:#adafae;text-shadow:none;background-color:#222;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.navbar .search-query:-moz-placeholder,.navbar .search-query:focus:-moz-placeholder,.navbar .search-query.focused:-moz-placeholder{color:#999}.navbar .search-query:-ms-input-placeholder,.navbar .search-query:focus:-ms-input-placeholder,.navbar .search-query.focused:-ms-input-placeholder{color:#999}.navbar .search-query::-webkit-input-placeholder,.navbar .search-query:focus::-webkit-input-placeholder,.navbar .search-query.focused::-webkit-input-placeholder{color:#999}@media(max-width:979px){.navbar .nav-collapse .nav li>a{font-weight:normal;color:#eee;text-shadow:none;border:0}.navbar .nav-collapse .nav li>a:hover{background-color:#33b5e5;border:0}.navbar .nav-collapse .nav .active>a{background-color:#33b5e5;border:0}.navbar .nav-collapse .dropdown-menu a:hover{background-color:#33b5e5}.navbar .nav-collapse .navbar-form,.navbar .nav-collapse .navbar-search{border-top:0;border-bottom:0}.navbar .nav-collapse .nav-header{color:rgba(128,128,128,0.6)}.navbar-inverse .nav-collapse .nav li>a:hover{background-color:#111}.navbar-inverse .nav-collapse .nav .active>a{background-color:#111}.navbar-inverse .nav-collapse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav-collapse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav-collapse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#111}}div.subnav{margin:0 1px;background-color:#020202;background-image:none;border:0;border-bottom:1px solid #222}div.subnav .nav>li>a,div.subnav .nav>li:first-child>a,div.subnav .nav>li:first-child>a:hover{padding:11px 12px;color:#adafae;background-color:#020202;border:0}div.subnav .nav>li>a:hover,div.subnav .nav>li.active>a,div.subnav .nav>li.active>a:hover,div.subnav .nav>li:first-child>a:hover{padding:11px 12px;color:#fff;background:transparent;border:0;border-bottom:1px solid #33b5e5}div.subnav .nav li.nav-header{text-shadow:none}div.subnav-fixed{top:50px;margin:0}.nav-tabs{border-bottom:1px solid #222}.nav-tabs li>a:hover,.nav-tabs li.active>a,.nav-tabs li.active>a:hover{color:#fff;background-color:#33b5e5;border-color:transparent}.nav-tabs li.disabled>a{color:#999}.nav-tabs .open .dropdown-toggle{background-color:#060606;border-color:transparent}.nav-pills li>a:hover{color:#fff;background-color:#33b5e5}.nav-pills li.disabled>a{color:#999}.nav-pills .open .dropdown-toggle{background-color:#060606}.nav-pills .dropdown-menu li>a:hover{border:0}.nav-list li>a{text-shadow:none}.nav-list li>a:hover{color:#fff;background-color:#33b5e5}.nav-list .nav-header{text-shadow:none}.nav-list .divider{background-color:transparent;border-bottom:1px solid #222}.nav-stacked li>a{border:1px solid #222!important}.nav-stacked li>a:hover,.nav-stacked li.active>a{color:#fff;background-color:#33b5e5}.tabbable .nav-tabs,.tabbable .nav-tabs li.active>a{border-color:#222}.breadcrumb{font-size:14px;background-color:transparent;background-image:none;border-width:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.breadcrumb li{text-shadow:none}.breadcrumb li>a{color:#33b5e5;text-shadow:none}.pagination ul{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>span,.pagination ul>.disabled>span:hover{background-color:rgba(0,0,0,0.2)}.pager li>a,.pager li>span{background-color:#060606;border:0}.pager li>a:hover,.pager li>span:hover{background-color:#33b5e5}.pager .disabled a,.pager .disabled a:hover{background-color:#060606}.btn{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);text-shadow:none;background-color:#5c5c5c;*background-color:#4d4d4d;background-image:-moz-linear-gradient(top,#666,#4d4d4d);background-image:-webkit-gradient(linear,0 0,0 100%,from(#666),to(#4d4d4d));background-image:-webkit-linear-gradient(top,#666,#4d4d4d);background-image:-o-linear-gradient(top,#666,#4d4d4d);background-image:linear-gradient(to bottom,#666,#4d4d4d);background-repeat:repeat-x;border-color:#4d4d4d #4d4d4d #262626;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff666666',endColorstr='#ff4d4d4d',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:1px 1px 2px #111;-moz-box-shadow:1px 1px 2px #111;box-shadow:1px 1px 2px #111}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#fff;background-color:#4d4d4d;*background-color:#404040}.btn:active,.btn.active{background-color:#333 \9}.btn:hover{color:#fff;text-shadow:none}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#008ab8;*background-color:#007399;background-image:-moz-linear-gradient(top,#09c,#007399);background-image:-webkit-gradient(linear,0 0,0 100%,from(#09c),to(#007399));background-image:-webkit-linear-gradient(top,#09c,#007399);background-image:-o-linear-gradient(top,#09c,#007399);background-image:linear-gradient(to bottom,#09c,#007399);background-repeat:repeat-x;border-color:#007399 #007399 #00394d;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0099cc',endColorstr='#ff007399',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#007399;*background-color:#006080}.btn-primary:active,.btn-primary.active{background-color:#004d66 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ff961f;*background-color:#f80;background-image:-moz-linear-gradient(top,#ffa033,#f80);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffa033),to(#f80));background-image:-webkit-linear-gradient(top,#ffa033,#f80);background-image:-o-linear-gradient(top,#ffa033,#f80);background-image:linear-gradient(to bottom,#ffa033,#f80);background-repeat:repeat-x;border-color:#f80 #f80 #b35f00;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffa033',endColorstr='#ffff8800',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f80;*background-color:#e67a00}.btn-warning:active,.btn-warning.active{background-color:#cc6d00 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#eb0000;*background-color:#c00;background-image:-moz-linear-gradient(top,#f00,#c00);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f00),to(#c00));background-image:-webkit-linear-gradient(top,#f00,#c00);background-image:-o-linear-gradient(top,#f00,#c00);background-image:linear-gradient(to bottom,#f00,#c00);background-repeat:repeat-x;border-color:#c00 #c00 #800000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff0000',endColorstr='#ffcc0000',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#c00;*background-color:#b30000}.btn-danger:active,.btn-danger.active{background-color:#900 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#7ab800;*background-color:#690;background-image:-moz-linear-gradient(top,#8c0,#690);background-image:-webkit-gradient(linear,0 0,0 100%,from(#8c0),to(#690));background-image:-webkit-linear-gradient(top,#8c0,#690);background-image:-o-linear-gradient(top,#8c0,#690);background-image:linear-gradient(to bottom,#8c0,#690);background-repeat:repeat-x;border-color:#690 #690 #334d00;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff88cc00',endColorstr='#ff669900',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#690;*background-color:#558000}.btn-success:active,.btn-success.active{background-color:#460 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#292929;*background-color:#191919;background-image:-moz-linear-gradient(top,#333,#191919);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#191919));background-image:-webkit-linear-gradient(top,#333,#191919);background-image:-o-linear-gradient(top,#333,#191919);background-image:linear-gradient(to bottom,#333,#191919);background-repeat:repeat-x;border-color:#191919 #191919 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff333333',endColorstr='#ff191919',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#191919;*background-color:#0d0d0d}.btn-info:active,.btn-info.active{background-color:#000 \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#9f3fcf;*background-color:#93c;background-image:-moz-linear-gradient(top,#a347d1,#93c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#a347d1),to(#93c));background-image:-webkit-linear-gradient(top,#a347d1,#93c);background-image:-o-linear-gradient(top,#a347d1,#93c);background-image:linear-gradient(to bottom,#a347d1,#93c);background-repeat:repeat-x;border-color:#93c #93c #6b248f;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffa347d1',endColorstr='#ff9933cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#93c;*background-color:#8a2eb8}.btn-inverse:active,.btn-inverse.active{background-color:#7a29a3 \9}.btn .caret{border-top:4px solid black;opacity:.3}.btn-group>.dropdown-menu>li>a:hover{border-bottom:0}.btn.disabled,.btn[disabled]{background-color:#adafae}input,textarea,select{border-width:2px;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{color:#222}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly],.uneditable-input{border-color:#444}input:focus,textarea:focus,input.focused,textarea.focused{border-color:#52a8ec;outline:0;outline:thin dotted \9}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}legend,label{color:#999;border-bottom:0 solid #222}.form-actions{border-top:1px solid #222}.table{-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.table tbody tr.success td{color:#fff;background-color:#690}.table tbody tr.error td{color:#fff;background-color:#c00}.table tbody tr.info td{color:#fff;background-color:#33b5e5}.table tbody tr.warning td{color:#fff;background-color:#f80}.dropdown-menu{-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.8);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.8);box-shadow:0 2px 4px rgba(0,0,0,0.8)}.alert,.alert .alert-heading,.alert-success,.alert-success .alert-heading,.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading,.alert-info,.alert-info .alert-heading{color:#eee;text-shadow:none;border:0}.alert h1,.alert h2,.alert h3,.alert h4,.alert h5,.alert h6{color:#eee}.label{color:#eee}.label,.alert{background-color:#666}.label:hover{background-color:#4d4d4d}.label-important,.alert-danger,.alert-error{background-color:#c00}.label-important:hover{background-color:#900}.label-warning,.alert-warning{background-color:#cc6d00}.label-warning:hover{background-color:#995200}.label-success,.alert-success{background-color:#5c8a00}.label-success:hover{background-color:#3a5700}.label-info,.alert-info{background-color:#007399}.label-info:hover{background-color:#004d66}.badge-inverse,.label-inverse,.alert-inverse{background-color:#7a29a3}.label-inverse:hover{background-color:#5c1f7a}.well,.hero-unit{-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.well,.hero-unit{border-top:solid 1px #2f2f2f;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.8);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.8);box-shadow:0 2px 4px rgba(0,0,0,0.8)}.thumbnail{border-color:#222}.progress{background-color:#060606;background-image:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.modal{background-color:#222;border-top:solid 1px #2f2f2f;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.modal-header{border-bottom:1px solid #222}.modal-footer{background-color:#222;border-top:1px solid #222;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.footer{border-top:1px solid #222}@media(max-width:768px){div.subnav .nav>li+li>a,div.subnav .nav>li:first-child>a{border-top:1px solid #222;border-left:1px solid #222}.subnav .nav>li+li>a:hover,.subnav .nav>li:first-child>a:hover{background-color:#33b5e5;border-bottom:0}}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/pykeg/web/static/css/bootstrap-variables.less b/pykeg/web/static/css/bootstrap-variables.less deleted file mode 100644 index 4e14c65ce..000000000 --- a/pykeg/web/static/css/bootstrap-variables.less +++ /dev/null @@ -1,98 +0,0 @@ -/** - * bootstrap-variables.less - * Copy of bootstrap/lib/variables.less, for kegweb specific modifications. - */ - -// GLOBAL VALUES -// -------------------------------------------------- - -// Links -@linkColor: #08c; -@linkColorHover: darken(@linkColor, 15%); - -// Grays -@black: #000; -@grayDarker: #222; -@grayDark: #333; -@gray: #555; -@grayLight: #999; -@grayLighter: #eee; -@white: #fff; - -// Accent colors -@blue: #049cdb; -@blueDark: #0064cd; -@green: #46a546; -@red: #9d261d; -@yellow: #ffc40d; -@orange: #f89406; -@pink: #c3325f; -@purple: #7a43b6; - -// Typography -@baseFontSize: 13px; -@baseFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; -@baseLineHeight: 18px; -@textColor: @grayDark; - -// Buttons -@primaryButtonBackground: @linkColor; - - - -// COMPONENT VARIABLES -// -------------------------------------------------- - -// Z-index master list -// Used for a bird's eye view of components dependent on the z-axis -// Try to avoid customizing these :) -@zindexDropdown: 1000; -@zindexPopover: 1010; -@zindexTooltip: 1020; -@zindexFixedNavbar: 1030; -@zindexModalBackdrop: 1040; -@zindexModal: 1050; - -// Input placeholder text color -@placeholderText: @grayLight; - -// Navbar -@navbarHeight: 40px; -@navbarBackground: @grayDarker; -@navbarBackgroundHighlight: @grayDark; - -@navbarText: @grayLight; -@navbarLinkColor: @grayLight; -@navbarLinkColorHover: @white; - -// Form states and alerts -@warningText: #c09853; -@warningBackground: #fcf8e3; -@warningBorder: darken(spin(@warningBackground, -10), 3%); - -@errorText: #b94a48; -@errorBackground: #f2dede; -@errorBorder: darken(spin(@errorBackground, -10), 3%); - -@successText: #468847; -@successBackground: #dff0d8; -@successBorder: darken(spin(@successBackground, -10), 5%); - -@infoText: #3a87ad; -@infoBackground: #d9edf7; -@infoBorder: darken(spin(@infoBackground, -10), 7%); - - - -// GRID -// -------------------------------------------------- - -// Default 940px grid -@gridColumns: 12; -@gridColumnWidth: 60px; -@gridGutterWidth: 20px; -@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); - -// Fluid grid -@fluidGridColumnWidth: 6.382978723%; -@fluidGridGutterWidth: 2.127659574%; diff --git a/pykeg/web/static/css/kegweb.css b/pykeg/web/static/css/kegweb.css deleted file mode 100644 index 7f5757d16..000000000 --- a/pykeg/web/static/css/kegweb.css +++ /dev/null @@ -1,5631 +0,0 @@ -/*! - * Kegweb main css. - */ -/*! - * Bootstrap v2.3.1 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ -.clearfix { - *zoom: 1; -} -.clearfix:before, -.clearfix:after { - display: table; - content: ""; - line-height: 0; -} -.clearfix:after { - clear: both; -} -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.input-block-level { - display: block; - width: 100%; - min-height: 28px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} -audio:not([controls]) { - display: none; -} -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -a:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -a:hover, -a:active { - outline: 0; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} -img { - /* Responsive images (ensure images don't scale beyond their parents) */ - - max-width: 100%; - /* Part 1: Set a maxium relative to the parent */ - - width: auto\9; - /* IE7-8 need help adjusting responsive images */ - - height: auto; - /* Part 2: Scale the height according to the width, otherwise you get stretching */ - - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; -} -#map_canvas img, -.google-maps img { - max-width: none; -} -button, -input, -select, -textarea { - margin: 0; - font-size: 100%; - vertical-align: middle; -} -button, -input { - *overflow: visible; - line-height: normal; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -label, -select, -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -input[type="radio"], -input[type="checkbox"] { - cursor: pointer; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; -} -textarea { - overflow: auto; - vertical-align: top; -} -@media print { - * { - text-shadow: none !important; - color: #000 !important; - background: transparent !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - @page { - margin: 0.5cm; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } -} -body { - margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 18px; - color: #333333; - background-color: #ffffff; -} -a { - color: #0088cc; - text-decoration: none; -} -a:hover, -a:focus { - color: #005580; - text-decoration: underline; -} -.img-rounded { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.img-polaroid { - padding: 4px; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} -.img-circle { - -webkit-border-radius: 500px; - -moz-border-radius: 500px; - border-radius: 500px; -} -.row { - margin-left: -20px; - *zoom: 1; -} -.row:before, -.row:after { - display: table; - content: ""; - line-height: 0; -} -.row:after { - clear: both; -} -[class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; -} -.container, -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} -.span12 { - width: 940px; -} -.span11 { - width: 860px; -} -.span10 { - width: 780px; -} -.span9 { - width: 700px; -} -.span8 { - width: 620px; -} -.span7 { - width: 540px; -} -.span6 { - width: 460px; -} -.span5 { - width: 380px; -} -.span4 { - width: 300px; -} -.span3 { - width: 220px; -} -.span2 { - width: 140px; -} -.span1 { - width: 60px; -} -.offset12 { - margin-left: 980px; -} -.offset11 { - margin-left: 900px; -} -.offset10 { - margin-left: 820px; -} -.offset9 { - margin-left: 740px; -} -.offset8 { - margin-left: 660px; -} -.offset7 { - margin-left: 580px; -} -.offset6 { - margin-left: 500px; -} -.offset5 { - margin-left: 420px; -} -.offset4 { - margin-left: 340px; -} -.offset3 { - margin-left: 260px; -} -.offset2 { - margin-left: 180px; -} -.offset1 { - margin-left: 100px; -} -.row-fluid { - width: 100%; - *zoom: 1; -} -.row-fluid:before, -.row-fluid:after { - display: table; - content: ""; - line-height: 0; -} -.row-fluid:after { - clear: both; -} -.row-fluid [class*="span"] { - display: block; - width: 100%; - min-height: 28px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - float: left; - margin-left: 2.127659574%; - *margin-left: 2.0744680846382977%; -} -.row-fluid [class*="span"]:first-child { - margin-left: 0; -} -.row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.127659574%; -} -.row-fluid .span12 { - width: 99.99999998999999%; - *width: 99.94680850063828%; -} -.row-fluid .span11 { - width: 91.489361693%; - *width: 91.4361702036383%; -} -.row-fluid .span10 { - width: 82.97872339599999%; - *width: 82.92553190663828%; -} -.row-fluid .span9 { - width: 74.468085099%; - *width: 74.4148936096383%; -} -.row-fluid .span8 { - width: 65.95744680199999%; - *width: 65.90425531263828%; -} -.row-fluid .span7 { - width: 57.446808505%; - *width: 57.3936170156383%; -} -.row-fluid .span6 { - width: 48.93617020799999%; - *width: 48.88297871863829%; -} -.row-fluid .span5 { - width: 40.425531911%; - *width: 40.3723404216383%; -} -.row-fluid .span4 { - width: 31.914893614%; - *width: 31.8617021246383%; -} -.row-fluid .span3 { - width: 23.404255317%; - *width: 23.3510638276383%; -} -.row-fluid .span2 { - width: 14.89361702%; - *width: 14.8404255306383%; -} -.row-fluid .span1 { - width: 6.382978723%; - *width: 6.329787233638298%; -} -.row-fluid .offset12 { - margin-left: 104.25531913799999%; - *margin-left: 104.14893615927657%; -} -.row-fluid .offset12:first-child { - margin-left: 102.127659564%; - *margin-left: 102.02127658527658%; -} -.row-fluid .offset11 { - margin-left: 95.744680841%; - *margin-left: 95.63829786227659%; -} -.row-fluid .offset11:first-child { - margin-left: 93.61702126700001%; - *margin-left: 93.5106382882766%; -} -.row-fluid .offset10 { - margin-left: 87.23404254399999%; - *margin-left: 87.12765956527657%; -} -.row-fluid .offset10:first-child { - margin-left: 85.10638297%; - *margin-left: 84.99999999127658%; -} -.row-fluid .offset9 { - margin-left: 78.723404247%; - *margin-left: 78.61702126827659%; -} -.row-fluid .offset9:first-child { - margin-left: 76.59574467300001%; - *margin-left: 76.4893616942766%; -} -.row-fluid .offset8 { - margin-left: 70.21276594999999%; - *margin-left: 70.10638297127657%; -} -.row-fluid .offset8:first-child { - margin-left: 68.085106376%; - *margin-left: 67.97872339727658%; -} -.row-fluid .offset7 { - margin-left: 61.702127653%; - *margin-left: 61.595744674276595%; -} -.row-fluid .offset7:first-child { - margin-left: 59.574468079%; - *margin-left: 59.468085100276596%; -} -.row-fluid .offset6 { - margin-left: 53.19148935599999%; - *margin-left: 53.08510637727659%; -} -.row-fluid .offset6:first-child { - margin-left: 51.06382978199999%; - *margin-left: 50.95744680327659%; -} -.row-fluid .offset5 { - margin-left: 44.680851059%; - *margin-left: 44.574468080276596%; -} -.row-fluid .offset5:first-child { - margin-left: 42.553191485%; - *margin-left: 42.4468085062766%; -} -.row-fluid .offset4 { - margin-left: 36.170212762%; - *margin-left: 36.063829783276596%; -} -.row-fluid .offset4:first-child { - margin-left: 34.042553188%; - *margin-left: 33.9361702092766%; -} -.row-fluid .offset3 { - margin-left: 27.659574465%; - *margin-left: 27.553191486276596%; -} -.row-fluid .offset3:first-child { - margin-left: 25.531914891%; - *margin-left: 25.425531912276597%; -} -.row-fluid .offset2 { - margin-left: 19.148936168%; - *margin-left: 19.042553189276596%; -} -.row-fluid .offset2:first-child { - margin-left: 17.021276594%; - *margin-left: 16.914893615276597%; -} -.row-fluid .offset1 { - margin-left: 10.638297870999999%; - *margin-left: 10.531914892276596%; -} -.row-fluid .offset1:first-child { - margin-left: 8.510638297%; - *margin-left: 8.404255318276597%; -} -[class*="span"].hide, -.row-fluid [class*="span"].hide { - display: none; -} -[class*="span"].pull-right, -.row-fluid [class*="span"].pull-right { - float: right; -} -.container { - margin-right: auto; - margin-left: auto; - *zoom: 1; -} -.container:before, -.container:after { - display: table; - content: ""; - line-height: 0; -} -.container:after { - clear: both; -} -.container-fluid { - padding-right: 20px; - padding-left: 20px; - *zoom: 1; -} -.container-fluid:before, -.container-fluid:after { - display: table; - content: ""; - line-height: 0; -} -.container-fluid:after { - clear: both; -} -p { - margin: 0 0 9px; -} -.lead { - margin-bottom: 18px; - font-size: 19.5px; - font-weight: 200; - line-height: 27px; -} -small { - font-size: 85%; -} -strong { - font-weight: bold; -} -em { - font-style: italic; -} -cite { - font-style: normal; -} -.muted { - color: #999999; -} -a.muted:hover, -a.muted:focus { - color: #808080; -} -.text-warning { - color: #c09853; -} -a.text-warning:hover, -a.text-warning:focus { - color: #a47e3c; -} -.text-error { - color: #b94a48; -} -a.text-error:hover, -a.text-error:focus { - color: #953b39; -} -.text-info { - color: #3a87ad; -} -a.text-info:hover, -a.text-info:focus { - color: #2d6987; -} -.text-success { - color: #468847; -} -a.text-success:hover, -a.text-success:focus { - color: #356635; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 9px 0; - font-family: inherit; - font-weight: bold; - line-height: 18px; - color: inherit; - text-rendering: optimizelegibility; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - font-weight: normal; - line-height: 1; - color: #999999; -} -h1, -h2, -h3 { - line-height: 36px; -} -h1 { - font-size: 35.75px; -} -h2 { - font-size: 29.25px; -} -h3 { - font-size: 22.75px; -} -h4 { - font-size: 16.25px; -} -h5 { - font-size: 13px; -} -h6 { - font-size: 11.049999999999999px; -} -h1 small { - font-size: 22.75px; -} -h2 small { - font-size: 16.25px; -} -h3 small { - font-size: 13px; -} -h4 small { - font-size: 13px; -} -.page-header { - padding-bottom: 8px; - margin: 18px 0 27px; - border-bottom: 1px solid #eeeeee; -} -ul, -ol { - padding: 0; - margin: 0 0 9px 25px; -} -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} -li { - line-height: 18px; -} -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; -} -ul.inline > li, -ol.inline > li { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - padding-left: 5px; - padding-right: 5px; -} -dl { - margin-bottom: 18px; -} -dt, -dd { - line-height: 18px; -} -dt { - font-weight: bold; -} -dd { - margin-left: 9px; -} -.dl-horizontal { - *zoom: 1; -} -.dl-horizontal:before, -.dl-horizontal:after { - display: table; - content: ""; - line-height: 0; -} -.dl-horizontal:after { - clear: both; -} -.dl-horizontal dt { - float: left; - width: 160px; - clear: left; - text-align: right; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.dl-horizontal dd { - margin-left: 180px; -} -hr { - margin: 18px 0; - border: 0; - border-top: 1px solid #eeeeee; - border-bottom: 1px solid #ffffff; -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 0 0 0 15px; - margin: 0 0 18px; - border-left: 5px solid #eeeeee; -} -blockquote p { - margin-bottom: 0; - font-size: 16.25px; - font-weight: 300; - line-height: 1.25; -} -blockquote small { - display: block; - line-height: 18px; - color: #999999; -} -blockquote small:before { - content: '\2014 \00A0'; -} -blockquote.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; -} -blockquote.pull-right p, -blockquote.pull-right small { - text-align: right; -} -blockquote.pull-right small:before { - content: ''; -} -blockquote.pull-right small:after { - content: '\00A0 \2014'; -} -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} -address { - display: block; - margin-bottom: 18px; - font-style: normal; - line-height: 18px; -} -code, -pre { - padding: 0 3px 2px; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - font-size: 11px; - color: #333333; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -code { - padding: 2px 4px; - color: #d14; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - white-space: nowrap; -} -pre { - display: block; - padding: 8.5px; - margin: 0 0 9px; - font-size: 12px; - line-height: 18px; - word-break: break-all; - word-wrap: break-word; - white-space: pre; - white-space: pre-wrap; - background-color: #f5f5f5; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -pre.prettyprint { - margin-bottom: 18px; -} -pre code { - padding: 0; - color: inherit; - white-space: pre; - white-space: pre-wrap; - background-color: transparent; - border: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -form { - margin: 0 0 18px; -} -fieldset { - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 18px; - font-size: 19.5px; - line-height: 36px; - color: #333333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -legend small { - font-size: 13.5px; - color: #999999; -} -label, -input, -button, -select, -textarea { - font-size: 13px; - font-weight: normal; - line-height: 18px; -} -input, -button, -select, -textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} -label { - display: block; - margin-bottom: 5px; -} -select, -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - display: inline-block; - height: 18px; - padding: 4px 6px; - margin-bottom: 9px; - font-size: 13px; - line-height: 18px; - color: #555555; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - vertical-align: middle; -} -input, -textarea, -.uneditable-input { - width: 206px; -} -textarea { - height: auto; -} -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - background-color: #ffffff; - border: 1px solid #cccccc; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -webkit-transition: border linear .2s, box-shadow linear .2s; - -moz-transition: border linear .2s, box-shadow linear .2s; - -o-transition: border linear .2s, box-shadow linear .2s; - transition: border linear .2s, box-shadow linear .2s; -} -textarea:focus, -input[type="text"]:focus, -input[type="password"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="date"]:focus, -input[type="month"]:focus, -input[type="time"]:focus, -input[type="week"]:focus, -input[type="number"]:focus, -input[type="email"]:focus, -input[type="url"]:focus, -input[type="search"]:focus, -input[type="tel"]:focus, -input[type="color"]:focus, -.uneditable-input:focus { - border-color: rgba(82, 168, 236, 0.8); - outline: 0; - outline: thin dotted \9; - /* IE6-9 */ - - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); - -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - *margin-top: 0; - /* IE7 */ - - margin-top: 1px \9; - /* IE8-9 */ - - line-height: normal; -} -input[type="file"], -input[type="image"], -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="radio"], -input[type="checkbox"] { - width: auto; -} -select, -input[type="file"] { - height: 28px; - /* In IE7, the height of the select element cannot be changed by height, only font-size */ - - *margin-top: 4px; - /* For IE7, add top margin to align select with labels */ - - line-height: 28px; -} -select { - width: 220px; - border: 1px solid #cccccc; - background-color: #ffffff; -} -select[multiple], -select[size] { - height: auto; -} -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.uneditable-input, -.uneditable-textarea { - color: #999999; - background-color: #fcfcfc; - border-color: #cccccc; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - cursor: not-allowed; -} -.uneditable-input { - overflow: hidden; - white-space: nowrap; -} -.uneditable-textarea { - width: auto; - height: auto; -} -input:-moz-placeholder, -textarea:-moz-placeholder { - color: #999999; -} -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #999999; -} -input::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #999999; -} -.radio, -.checkbox { - min-height: 18px; - padding-left: 20px; -} -.radio input[type="radio"], -.checkbox input[type="checkbox"] { - float: left; - margin-left: -20px; -} -.controls > .radio:first-child, -.controls > .checkbox:first-child { - padding-top: 5px; -} -.radio.inline, -.checkbox.inline { - display: inline-block; - padding-top: 5px; - margin-bottom: 0; - vertical-align: middle; -} -.radio.inline + .radio.inline, -.checkbox.inline + .checkbox.inline { - margin-left: 10px; -} -.input-mini { - width: 60px; -} -.input-small { - width: 90px; -} -.input-medium { - width: 150px; -} -.input-large { - width: 210px; -} -.input-xlarge { - width: 270px; -} -.input-xxlarge { - width: 530px; -} -input[class*="span"], -select[class*="span"], -textarea[class*="span"], -.uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"] { - float: none; - margin-left: 0; -} -.input-append input[class*="span"], -.input-append .uneditable-input[class*="span"], -.input-prepend input[class*="span"], -.input-prepend .uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"], -.row-fluid .input-prepend [class*="span"], -.row-fluid .input-append [class*="span"] { - display: inline-block; -} -input, -textarea, -.uneditable-input { - margin-left: 0; -} -.controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; -} -input.span12, -textarea.span12, -.uneditable-input.span12 { - width: 926px; -} -input.span11, -textarea.span11, -.uneditable-input.span11 { - width: 846px; -} -input.span10, -textarea.span10, -.uneditable-input.span10 { - width: 766px; -} -input.span9, -textarea.span9, -.uneditable-input.span9 { - width: 686px; -} -input.span8, -textarea.span8, -.uneditable-input.span8 { - width: 606px; -} -input.span7, -textarea.span7, -.uneditable-input.span7 { - width: 526px; -} -input.span6, -textarea.span6, -.uneditable-input.span6 { - width: 446px; -} -input.span5, -textarea.span5, -.uneditable-input.span5 { - width: 366px; -} -input.span4, -textarea.span4, -.uneditable-input.span4 { - width: 286px; -} -input.span3, -textarea.span3, -.uneditable-input.span3 { - width: 206px; -} -input.span2, -textarea.span2, -.uneditable-input.span2 { - width: 126px; -} -input.span1, -textarea.span1, -.uneditable-input.span1 { - width: 46px; -} -.controls-row { - *zoom: 1; -} -.controls-row:before, -.controls-row:after { - display: table; - content: ""; - line-height: 0; -} -.controls-row:after { - clear: both; -} -.controls-row [class*="span"], -.row-fluid .controls-row [class*="span"] { - float: left; -} -.controls-row .checkbox[class*="span"], -.controls-row .radio[class*="span"] { - padding-top: 5px; -} -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - cursor: not-allowed; - background-color: #eeeeee; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][readonly], -input[type="checkbox"][readonly] { - background-color: transparent; -} -.control-group.warning .control-label, -.control-group.warning .help-block, -.control-group.warning .help-inline { - color: #c09853; -} -.control-group.warning .checkbox, -.control-group.warning .radio, -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - color: #c09853; -} -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - border-color: #c09853; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.warning input:focus, -.control-group.warning select:focus, -.control-group.warning textarea:focus { - border-color: #a47e3c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; -} -.control-group.warning .input-prepend .add-on, -.control-group.warning .input-append .add-on { - color: #c09853; - background-color: #fcf8e3; - border-color: #c09853; -} -.control-group.error .control-label, -.control-group.error .help-block, -.control-group.error .help-inline { - color: #b94a48; -} -.control-group.error .checkbox, -.control-group.error .radio, -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - color: #b94a48; -} -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - border-color: #b94a48; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.error input:focus, -.control-group.error select:focus, -.control-group.error textarea:focus { - border-color: #953b39; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; -} -.control-group.error .input-prepend .add-on, -.control-group.error .input-append .add-on { - color: #b94a48; - background-color: #f2dede; - border-color: #b94a48; -} -.control-group.success .control-label, -.control-group.success .help-block, -.control-group.success .help-inline { - color: #468847; -} -.control-group.success .checkbox, -.control-group.success .radio, -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - color: #468847; -} -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - border-color: #468847; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.success input:focus, -.control-group.success select:focus, -.control-group.success textarea:focus { - border-color: #356635; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; -} -.control-group.success .input-prepend .add-on, -.control-group.success .input-append .add-on { - color: #468847; - background-color: #dff0d8; - border-color: #468847; -} -.control-group.info .control-label, -.control-group.info .help-block, -.control-group.info .help-inline { - color: #3a87ad; -} -.control-group.info .checkbox, -.control-group.info .radio, -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - color: #3a87ad; -} -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - border-color: #3a87ad; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.info input:focus, -.control-group.info select:focus, -.control-group.info textarea:focus { - border-color: #2d6987; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; -} -.control-group.info .input-prepend .add-on, -.control-group.info .input-append .add-on { - color: #3a87ad; - background-color: #d9edf7; - border-color: #3a87ad; -} -input:focus:invalid, -textarea:focus:invalid, -select:focus:invalid { - color: #b94a48; - border-color: #ee5f5b; -} -input:focus:invalid:focus, -textarea:focus:invalid:focus, -select:focus:invalid:focus { - border-color: #e9322d; - -webkit-box-shadow: 0 0 6px #f8b9b7; - -moz-box-shadow: 0 0 6px #f8b9b7; - box-shadow: 0 0 6px #f8b9b7; -} -.form-actions { - padding: 17px 20px 18px; - margin-top: 18px; - margin-bottom: 18px; - background-color: #f5f5f5; - border-top: 1px solid #e5e5e5; - *zoom: 1; -} -.form-actions:before, -.form-actions:after { - display: table; - content: ""; - line-height: 0; -} -.form-actions:after { - clear: both; -} -.help-block, -.help-inline { - color: #595959; -} -.help-block { - display: block; - margin-bottom: 9px; -} -.help-inline { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - vertical-align: middle; - padding-left: 5px; -} -.input-append, -.input-prepend { - display: inline-block; - margin-bottom: 9px; - vertical-align: middle; - font-size: 0; - white-space: nowrap; -} -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input, -.input-append .dropdown-menu, -.input-prepend .dropdown-menu, -.input-append .popover, -.input-prepend .popover { - font-size: 13px; -} -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input { - position: relative; - margin-bottom: 0; - *margin-left: 0; - vertical-align: top; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-append input:focus, -.input-prepend input:focus, -.input-append select:focus, -.input-prepend select:focus, -.input-append .uneditable-input:focus, -.input-prepend .uneditable-input:focus { - z-index: 2; -} -.input-append .add-on, -.input-prepend .add-on { - display: inline-block; - width: auto; - height: 18px; - min-width: 16px; - padding: 4px 5px; - font-size: 13px; - font-weight: normal; - line-height: 18px; - text-align: center; - text-shadow: 0 1px 0 #ffffff; - background-color: #eeeeee; - border: 1px solid #ccc; -} -.input-append .add-on, -.input-prepend .add-on, -.input-append .btn, -.input-prepend .btn, -.input-append .btn-group > .dropdown-toggle, -.input-prepend .btn-group > .dropdown-toggle { - vertical-align: top; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.input-append .active, -.input-prepend .active { - background-color: #a9dba9; - border-color: #46a546; -} -.input-prepend .add-on, -.input-prepend .btn { - margin-right: -1px; -} -.input-prepend .add-on:first-child, -.input-prepend .btn:first-child { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-append input, -.input-append select, -.input-append .uneditable-input { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-append input + .btn-group .btn:last-child, -.input-append select + .btn-group .btn:last-child, -.input-append .uneditable-input + .btn-group .btn:last-child { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-append .add-on, -.input-append .btn, -.input-append .btn-group { - margin-left: -1px; -} -.input-append .add-on:last-child, -.input-append .btn:last-child, -.input-append .btn-group:last-child > .dropdown-toggle { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append input, -.input-prepend.input-append select, -.input-prepend.input-append .uneditable-input { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.input-prepend.input-append input + .btn-group .btn, -.input-prepend.input-append select + .btn-group .btn, -.input-prepend.input-append .uneditable-input + .btn-group .btn { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append .add-on:first-child, -.input-prepend.input-append .btn:first-child { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-prepend.input-append .add-on:last-child, -.input-prepend.input-append .btn:last-child { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append .btn-group:first-child { - margin-left: 0; -} -input.search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; - /* IE7-8 doesn't have border-radius, so don't indent the padding */ - - margin-bottom: 0; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -/* Allow for input prepend/append in search forms */ -.form-search .input-append .search-query, -.form-search .input-prepend .search-query { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.form-search .input-append .search-query { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} -.form-search .input-append .btn { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} -.form-search .input-prepend .search-query { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} -.form-search .input-prepend .btn { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} -.form-search input, -.form-inline input, -.form-horizontal input, -.form-search textarea, -.form-inline textarea, -.form-horizontal textarea, -.form-search select, -.form-inline select, -.form-horizontal select, -.form-search .help-inline, -.form-inline .help-inline, -.form-horizontal .help-inline, -.form-search .uneditable-input, -.form-inline .uneditable-input, -.form-horizontal .uneditable-input, -.form-search .input-prepend, -.form-inline .input-prepend, -.form-horizontal .input-prepend, -.form-search .input-append, -.form-inline .input-append, -.form-horizontal .input-append { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - margin-bottom: 0; - vertical-align: middle; -} -.form-search .hide, -.form-inline .hide, -.form-horizontal .hide { - display: none; -} -.form-search label, -.form-inline label, -.form-search .btn-group, -.form-inline .btn-group { - display: inline-block; -} -.form-search .input-append, -.form-inline .input-append, -.form-search .input-prepend, -.form-inline .input-prepend { - margin-bottom: 0; -} -.form-search .radio, -.form-search .checkbox, -.form-inline .radio, -.form-inline .checkbox { - padding-left: 0; - margin-bottom: 0; - vertical-align: middle; -} -.form-search .radio input[type="radio"], -.form-search .checkbox input[type="checkbox"], -.form-inline .radio input[type="radio"], -.form-inline .checkbox input[type="checkbox"] { - float: left; - margin-right: 3px; - margin-left: 0; -} -.control-group { - margin-bottom: 9px; -} -legend + .control-group { - margin-top: 18px; - -webkit-margin-top-collapse: separate; -} -.form-horizontal .control-group { - margin-bottom: 18px; - *zoom: 1; -} -.form-horizontal .control-group:before, -.form-horizontal .control-group:after { - display: table; - content: ""; - line-height: 0; -} -.form-horizontal .control-group:after { - clear: both; -} -.form-horizontal .control-label { - float: left; - width: 160px; - padding-top: 5px; - text-align: right; -} -.form-horizontal .controls { - *display: inline-block; - *padding-left: 20px; - margin-left: 180px; - *margin-left: 0; -} -.form-horizontal .controls:first-child { - *padding-left: 180px; -} -.form-horizontal .help-block { - margin-bottom: 0; -} -.form-horizontal input + .help-block, -.form-horizontal select + .help-block, -.form-horizontal textarea + .help-block, -.form-horizontal .uneditable-input + .help-block, -.form-horizontal .input-prepend + .help-block, -.form-horizontal .input-append + .help-block { - margin-top: 9px; -} -.form-horizontal .form-actions { - padding-left: 180px; -} -table { - max-width: 100%; - background-color: transparent; - border-collapse: collapse; - border-spacing: 0; -} -.table { - width: 100%; - margin-bottom: 18px; -} -.table th, -.table td { - padding: 8px; - line-height: 18px; - text-align: left; - vertical-align: top; - border-top: 1px solid #dddddd; -} -.table th { - font-weight: bold; -} -.table thead th { - vertical-align: bottom; -} -.table caption + thead tr:first-child th, -.table caption + thead tr:first-child td, -.table colgroup + thead tr:first-child th, -.table colgroup + thead tr:first-child td, -.table thead:first-child tr:first-child th, -.table thead:first-child tr:first-child td { - border-top: 0; -} -.table tbody + tbody { - border-top: 2px solid #dddddd; -} -.table .table { - background-color: #ffffff; -} -.table-condensed th, -.table-condensed td { - padding: 4px 5px; -} -.table-bordered { - border: 1px solid #dddddd; - border-collapse: separate; - *border-collapse: collapse; - border-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.table-bordered th, -.table-bordered td { - border-left: 1px solid #dddddd; -} -.table-bordered caption + thead tr:first-child th, -.table-bordered caption + tbody tr:first-child th, -.table-bordered caption + tbody tr:first-child td, -.table-bordered colgroup + thead tr:first-child th, -.table-bordered colgroup + tbody tr:first-child th, -.table-bordered colgroup + tbody tr:first-child td, -.table-bordered thead:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child td { - border-top: 0; -} -.table-bordered thead:first-child tr:first-child > th:first-child, -.table-bordered tbody:first-child tr:first-child > td:first-child, -.table-bordered tbody:first-child tr:first-child > th:first-child { - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.table-bordered thead:first-child tr:first-child > th:last-child, -.table-bordered tbody:first-child tr:first-child > td:last-child, -.table-bordered tbody:first-child tr:first-child > th:last-child { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; -} -.table-bordered thead:last-child tr:last-child > th:first-child, -.table-bordered tbody:last-child tr:last-child > td:first-child, -.table-bordered tbody:last-child tr:last-child > th:first-child, -.table-bordered tfoot:last-child tr:last-child > td:first-child, -.table-bordered tfoot:last-child tr:last-child > th:first-child { - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.table-bordered thead:last-child tr:last-child > th:last-child, -.table-bordered tbody:last-child tr:last-child > td:last-child, -.table-bordered tbody:last-child tr:last-child > th:last-child, -.table-bordered tfoot:last-child tr:last-child > td:last-child, -.table-bordered tfoot:last-child tr:last-child > th:last-child { - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { - -webkit-border-bottom-left-radius: 0; - -moz-border-radius-bottomleft: 0; - border-bottom-left-radius: 0; -} -.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomright: 0; - border-bottom-right-radius: 0; -} -.table-bordered caption + thead tr:first-child th:first-child, -.table-bordered caption + tbody tr:first-child td:first-child, -.table-bordered colgroup + thead tr:first-child th:first-child, -.table-bordered colgroup + tbody tr:first-child td:first-child { - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.table-bordered caption + thead tr:first-child th:last-child, -.table-bordered caption + tbody tr:first-child td:last-child, -.table-bordered colgroup + thead tr:first-child th:last-child, -.table-bordered colgroup + tbody tr:first-child td:last-child { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; -} -.table-striped tbody > tr:nth-child(odd) > td, -.table-striped tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; -} -.table-hover tbody tr:hover > td, -.table-hover tbody tr:hover > th { - background-color: #f5f5f5; -} -table td[class*="span"], -table th[class*="span"], -.row-fluid table td[class*="span"], -.row-fluid table th[class*="span"] { - display: table-cell; - float: none; - margin-left: 0; -} -.table td.span1, -.table th.span1 { - float: none; - width: 44px; - margin-left: 0; -} -.table td.span2, -.table th.span2 { - float: none; - width: 124px; - margin-left: 0; -} -.table td.span3, -.table th.span3 { - float: none; - width: 204px; - margin-left: 0; -} -.table td.span4, -.table th.span4 { - float: none; - width: 284px; - margin-left: 0; -} -.table td.span5, -.table th.span5 { - float: none; - width: 364px; - margin-left: 0; -} -.table td.span6, -.table th.span6 { - float: none; - width: 444px; - margin-left: 0; -} -.table td.span7, -.table th.span7 { - float: none; - width: 524px; - margin-left: 0; -} -.table td.span8, -.table th.span8 { - float: none; - width: 604px; - margin-left: 0; -} -.table td.span9, -.table th.span9 { - float: none; - width: 684px; - margin-left: 0; -} -.table td.span10, -.table th.span10 { - float: none; - width: 764px; - margin-left: 0; -} -.table td.span11, -.table th.span11 { - float: none; - width: 844px; - margin-left: 0; -} -.table td.span12, -.table th.span12 { - float: none; - width: 924px; - margin-left: 0; -} -.table tbody tr.success > td { - background-color: #dff0d8; -} -.table tbody tr.error > td { - background-color: #f2dede; -} -.table tbody tr.warning > td { - background-color: #fcf8e3; -} -.table tbody tr.info > td { - background-color: #d9edf7; -} -.table-hover tbody tr.success:hover > td { - background-color: #d0e9c6; -} -.table-hover tbody tr.error:hover > td { - background-color: #ebcccc; -} -.table-hover tbody tr.warning:hover > td { - background-color: #faf2cc; -} -.table-hover tbody tr.info:hover > td { - background-color: #c4e3f3; -} -[class^="icon-"], -[class*=" icon-"] { - display: inline-block; - width: 14px; - height: 14px; - *margin-right: .3em; - line-height: 14px; - vertical-align: text-top; - background-image: url("../bootstrap/img/glyphicons-halflings.png"); - background-position: 14px 14px; - background-repeat: no-repeat; - margin-top: 1px; -} -/* White icons with optional class, or on hover/focus/active states of certain elements */ -.icon-white, -.nav-pills > .active > a > [class^="icon-"], -.nav-pills > .active > a > [class*=" icon-"], -.nav-list > .active > a > [class^="icon-"], -.nav-list > .active > a > [class*=" icon-"], -.navbar-inverse .nav > .active > a > [class^="icon-"], -.navbar-inverse .nav > .active > a > [class*=" icon-"], -.dropdown-menu > li > a:hover > [class^="icon-"], -.dropdown-menu > li > a:focus > [class^="icon-"], -.dropdown-menu > li > a:hover > [class*=" icon-"], -.dropdown-menu > li > a:focus > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:focus > a > [class^="icon-"], -.dropdown-submenu:hover > a > [class*=" icon-"], -.dropdown-submenu:focus > a > [class*=" icon-"] { - background-image: url("../bootstrap/img/glyphicons-halflings-white.png"); -} -.icon-glass { - background-position: 0 0; -} -.icon-music { - background-position: -24px 0; -} -.icon-search { - background-position: -48px 0; -} -.icon-envelope { - background-position: -72px 0; -} -.icon-heart { - background-position: -96px 0; -} -.icon-star { - background-position: -120px 0; -} -.icon-star-empty { - background-position: -144px 0; -} -.icon-user { - background-position: -168px 0; -} -.icon-film { - background-position: -192px 0; -} -.icon-th-large { - background-position: -216px 0; -} -.icon-th { - background-position: -240px 0; -} -.icon-th-list { - background-position: -264px 0; -} -.icon-ok { - background-position: -288px 0; -} -.icon-remove { - background-position: -312px 0; -} -.icon-zoom-in { - background-position: -336px 0; -} -.icon-zoom-out { - background-position: -360px 0; -} -.icon-off { - background-position: -384px 0; -} -.icon-signal { - background-position: -408px 0; -} -.icon-cog { - background-position: -432px 0; -} -.icon-trash { - background-position: -456px 0; -} -.icon-home { - background-position: 0 -24px; -} -.icon-file { - background-position: -24px -24px; -} -.icon-time { - background-position: -48px -24px; -} -.icon-road { - background-position: -72px -24px; -} -.icon-download-alt { - background-position: -96px -24px; -} -.icon-download { - background-position: -120px -24px; -} -.icon-upload { - background-position: -144px -24px; -} -.icon-inbox { - background-position: -168px -24px; -} -.icon-play-circle { - background-position: -192px -24px; -} -.icon-repeat { - background-position: -216px -24px; -} -.icon-refresh { - background-position: -240px -24px; -} -.icon-list-alt { - background-position: -264px -24px; -} -.icon-lock { - background-position: -287px -24px; -} -.icon-flag { - background-position: -312px -24px; -} -.icon-headphones { - background-position: -336px -24px; -} -.icon-volume-off { - background-position: -360px -24px; -} -.icon-volume-down { - background-position: -384px -24px; -} -.icon-volume-up { - background-position: -408px -24px; -} -.icon-qrcode { - background-position: -432px -24px; -} -.icon-barcode { - background-position: -456px -24px; -} -.icon-tag { - background-position: 0 -48px; -} -.icon-tags { - background-position: -25px -48px; -} -.icon-book { - background-position: -48px -48px; -} -.icon-bookmark { - background-position: -72px -48px; -} -.icon-print { - background-position: -96px -48px; -} -.icon-camera { - background-position: -120px -48px; -} -.icon-font { - background-position: -144px -48px; -} -.icon-bold { - background-position: -167px -48px; -} -.icon-italic { - background-position: -192px -48px; -} -.icon-text-height { - background-position: -216px -48px; -} -.icon-text-width { - background-position: -240px -48px; -} -.icon-align-left { - background-position: -264px -48px; -} -.icon-align-center { - background-position: -288px -48px; -} -.icon-align-right { - background-position: -312px -48px; -} -.icon-align-justify { - background-position: -336px -48px; -} -.icon-list { - background-position: -360px -48px; -} -.icon-indent-left { - background-position: -384px -48px; -} -.icon-indent-right { - background-position: -408px -48px; -} -.icon-facetime-video { - background-position: -432px -48px; -} -.icon-picture { - background-position: -456px -48px; -} -.icon-pencil { - background-position: 0 -72px; -} -.icon-map-marker { - background-position: -24px -72px; -} -.icon-adjust { - background-position: -48px -72px; -} -.icon-tint { - background-position: -72px -72px; -} -.icon-edit { - background-position: -96px -72px; -} -.icon-share { - background-position: -120px -72px; -} -.icon-check { - background-position: -144px -72px; -} -.icon-move { - background-position: -168px -72px; -} -.icon-step-backward { - background-position: -192px -72px; -} -.icon-fast-backward { - background-position: -216px -72px; -} -.icon-backward { - background-position: -240px -72px; -} -.icon-play { - background-position: -264px -72px; -} -.icon-pause { - background-position: -288px -72px; -} -.icon-stop { - background-position: -312px -72px; -} -.icon-forward { - background-position: -336px -72px; -} -.icon-fast-forward { - background-position: -360px -72px; -} -.icon-step-forward { - background-position: -384px -72px; -} -.icon-eject { - background-position: -408px -72px; -} -.icon-chevron-left { - background-position: -432px -72px; -} -.icon-chevron-right { - background-position: -456px -72px; -} -.icon-plus-sign { - background-position: 0 -96px; -} -.icon-minus-sign { - background-position: -24px -96px; -} -.icon-remove-sign { - background-position: -48px -96px; -} -.icon-ok-sign { - background-position: -72px -96px; -} -.icon-question-sign { - background-position: -96px -96px; -} -.icon-info-sign { - background-position: -120px -96px; -} -.icon-screenshot { - background-position: -144px -96px; -} -.icon-remove-circle { - background-position: -168px -96px; -} -.icon-ok-circle { - background-position: -192px -96px; -} -.icon-ban-circle { - background-position: -216px -96px; -} -.icon-arrow-left { - background-position: -240px -96px; -} -.icon-arrow-right { - background-position: -264px -96px; -} -.icon-arrow-up { - background-position: -289px -96px; -} -.icon-arrow-down { - background-position: -312px -96px; -} -.icon-share-alt { - background-position: -336px -96px; -} -.icon-resize-full { - background-position: -360px -96px; -} -.icon-resize-small { - background-position: -384px -96px; -} -.icon-plus { - background-position: -408px -96px; -} -.icon-minus { - background-position: -433px -96px; -} -.icon-asterisk { - background-position: -456px -96px; -} -.icon-exclamation-sign { - background-position: 0 -120px; -} -.icon-gift { - background-position: -24px -120px; -} -.icon-leaf { - background-position: -48px -120px; -} -.icon-fire { - background-position: -72px -120px; -} -.icon-eye-open { - background-position: -96px -120px; -} -.icon-eye-close { - background-position: -120px -120px; -} -.icon-warning-sign { - background-position: -144px -120px; -} -.icon-plane { - background-position: -168px -120px; -} -.icon-calendar { - background-position: -192px -120px; -} -.icon-random { - background-position: -216px -120px; - width: 16px; -} -.icon-comment { - background-position: -240px -120px; -} -.icon-magnet { - background-position: -264px -120px; -} -.icon-chevron-up { - background-position: -288px -120px; -} -.icon-chevron-down { - background-position: -313px -119px; -} -.icon-retweet { - background-position: -336px -120px; -} -.icon-shopping-cart { - background-position: -360px -120px; -} -.icon-folder-close { - background-position: -384px -120px; - width: 16px; -} -.icon-folder-open { - background-position: -408px -120px; - width: 16px; -} -.icon-resize-vertical { - background-position: -432px -119px; -} -.icon-resize-horizontal { - background-position: -456px -118px; -} -.icon-hdd { - background-position: 0 -144px; -} -.icon-bullhorn { - background-position: -24px -144px; -} -.icon-bell { - background-position: -48px -144px; -} -.icon-certificate { - background-position: -72px -144px; -} -.icon-thumbs-up { - background-position: -96px -144px; -} -.icon-thumbs-down { - background-position: -120px -144px; -} -.icon-hand-right { - background-position: -144px -144px; -} -.icon-hand-left { - background-position: -168px -144px; -} -.icon-hand-up { - background-position: -192px -144px; -} -.icon-hand-down { - background-position: -216px -144px; -} -.icon-circle-arrow-right { - background-position: -240px -144px; -} -.icon-circle-arrow-left { - background-position: -264px -144px; -} -.icon-circle-arrow-up { - background-position: -288px -144px; -} -.icon-circle-arrow-down { - background-position: -312px -144px; -} -.icon-globe { - background-position: -336px -144px; -} -.icon-wrench { - background-position: -360px -144px; -} -.icon-tasks { - background-position: -384px -144px; -} -.icon-filter { - background-position: -408px -144px; -} -.icon-briefcase { - background-position: -432px -144px; -} -.icon-fullscreen { - background-position: -456px -144px; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle { - *margin-bottom: -3px; -} -.dropdown-toggle:active, -.open .dropdown-toggle { - outline: 0; -} -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid #000000; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; -} -.dropdown .caret { - margin-top: 8px; - margin-left: 2px; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - *width: 100%; - height: 1px; - margin: 8px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 18px; - color: #333333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - text-decoration: none; - color: #ffffff; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #ffffff; - text-decoration: none; - outline: 0; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #999999; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - cursor: default; -} -.open { - *z-index: 1000; -} -.open > .dropdown-menu { - display: block; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - border-top: 0; - border-bottom: 4px solid #000000; - content: ""; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; -} -.dropdown-submenu { - position: relative; -} -.dropdown-submenu > .dropdown-menu { - top: 0; - left: 100%; - margin-top: -6px; - margin-left: -1px; - -webkit-border-radius: 0 6px 6px 6px; - -moz-border-radius: 0 6px 6px 6px; - border-radius: 0 6px 6px 6px; -} -.dropdown-submenu:hover > .dropdown-menu { - display: block; -} -.dropup .dropdown-submenu > .dropdown-menu { - top: auto; - bottom: 0; - margin-top: 0; - margin-bottom: -2px; - -webkit-border-radius: 5px 5px 5px 0; - -moz-border-radius: 5px 5px 5px 0; - border-radius: 5px 5px 5px 0; -} -.dropdown-submenu > a:after { - display: block; - content: " "; - float: right; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; - border-width: 5px 0 5px 5px; - border-left-color: #cccccc; - margin-top: 5px; - margin-right: -10px; -} -.dropdown-submenu:hover > a:after { - border-left-color: #ffffff; -} -.dropdown-submenu.pull-left { - float: none; -} -.dropdown-submenu.pull-left > .dropdown-menu { - left: -100%; - margin-left: 10px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} -.dropdown .dropdown-menu .nav-header { - padding-left: 20px; - padding-right: 20px; -} -.typeahead { - z-index: 1051; - margin-top: 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, 0.15); -} -.well-large { - padding: 24px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.well-small { - padding: 9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - -moz-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - -moz-transition: height 0.35s ease; - -o-transition: height 0.35s ease; - transition: height 0.35s ease; -} -.collapse.in { - height: auto; -} -.close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 18px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); -} -.close:hover, -.close:focus { - color: #000000; - text-decoration: none; - cursor: pointer; - opacity: 0.4; - filter: alpha(opacity=40); -} -button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} -.btn { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - padding: 4px 12px; - margin-bottom: 0; - font-size: 13px; - line-height: 18px; - text-align: center; - vertical-align: middle; - cursor: pointer; - color: #333333; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - background-color: #f5f5f5; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #e6e6e6; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - border: 1px solid #cccccc; - *border: 0; - border-bottom-color: #b3b3b3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - *margin-left: .3em; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); -} -.btn:hover, -.btn:focus, -.btn:active, -.btn.active, -.btn.disabled, -.btn[disabled] { - color: #333333; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} -.btn:active, -.btn.active { - background-color: #cccccc \9; -} -.btn:first-child { - *margin-left: 0; -} -.btn:hover, -.btn:focus { - color: #333333; - text-decoration: none; - background-position: 0 -15px; - -webkit-transition: background-position 0.1s linear; - -moz-transition: background-position 0.1s linear; - -o-transition: background-position 0.1s linear; - transition: background-position 0.1s linear; -} -.btn:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn.active, -.btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); -} -.btn.disabled, -.btn[disabled] { - cursor: default; - background-image: none; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn-large { - padding: 11px 19px; - font-size: 16.25px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.btn-large [class^="icon-"], -.btn-large [class*=" icon-"] { - margin-top: 4px; -} -.btn-small { - padding: 2px 10px; - font-size: 11.049999999999999px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.btn-small [class^="icon-"], -.btn-small [class*=" icon-"] { - margin-top: 0; -} -.btn-mini [class^="icon-"], -.btn-mini [class*=" icon-"] { - margin-top: -1px; -} -.btn-mini { - padding: 0 6px; - font-size: 9.75px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; - padding-left: 0; - padding-right: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.btn-primary.active, -.btn-warning.active, -.btn-danger.active, -.btn-success.active, -.btn-info.active, -.btn-inverse.active { - color: rgba(255, 255, 255, 0.75); -} -.btn-primary { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #006dcc; - background-image: -moz-linear-gradient(top, #0088cc, #0044cc); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); - background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); - background-image: -o-linear-gradient(top, #0088cc, #0044cc); - background-image: linear-gradient(to bottom, #0088cc, #0044cc); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); - border-color: #0044cc #0044cc #002a80; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #0044cc; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-primary:hover, -.btn-primary:focus, -.btn-primary:active, -.btn-primary.active, -.btn-primary.disabled, -.btn-primary[disabled] { - color: #ffffff; - background-color: #0044cc; - *background-color: #003bb3; -} -.btn-primary:active, -.btn-primary.active { - background-color: #003399 \9; -} -.btn-warning { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); - border-color: #f89406 #f89406 #ad6704; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #f89406; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-warning:hover, -.btn-warning:focus, -.btn-warning:active, -.btn-warning.active, -.btn-warning.disabled, -.btn-warning[disabled] { - color: #ffffff; - background-color: #f89406; - *background-color: #df8505; -} -.btn-warning:active, -.btn-warning.active { - background-color: #c67605 \9; -} -.btn-danger { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #da4f49; - background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); - background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); - border-color: #bd362f #bd362f #802420; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #bd362f; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-danger:hover, -.btn-danger:focus, -.btn-danger:active, -.btn-danger.active, -.btn-danger.disabled, -.btn-danger[disabled] { - color: #ffffff; - background-color: #bd362f; - *background-color: #a9302a; -} -.btn-danger:active, -.btn-danger.active { - background-color: #942a25 \9; -} -.btn-success { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #5bb75b; - background-image: -moz-linear-gradient(top, #62c462, #51a351); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); - background-image: -webkit-linear-gradient(top, #62c462, #51a351); - background-image: -o-linear-gradient(top, #62c462, #51a351); - background-image: linear-gradient(to bottom, #62c462, #51a351); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); - border-color: #51a351 #51a351 #387038; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #51a351; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-success:hover, -.btn-success:focus, -.btn-success:active, -.btn-success.active, -.btn-success.disabled, -.btn-success[disabled] { - color: #ffffff; - background-color: #51a351; - *background-color: #499249; -} -.btn-success:active, -.btn-success.active { - background-color: #408140 \9; -} -.btn-info { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #49afcd; - background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); - background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); - background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); - border-color: #2f96b4 #2f96b4 #1f6377; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #2f96b4; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-info:hover, -.btn-info:focus, -.btn-info:active, -.btn-info.active, -.btn-info.disabled, -.btn-info[disabled] { - color: #ffffff; - background-color: #2f96b4; - *background-color: #2a85a0; -} -.btn-info:active, -.btn-info.active { - background-color: #24748c \9; -} -.btn-inverse { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #363636; - background-image: -moz-linear-gradient(top, #444444, #222222); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); - background-image: -webkit-linear-gradient(top, #444444, #222222); - background-image: -o-linear-gradient(top, #444444, #222222); - background-image: linear-gradient(to bottom, #444444, #222222); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); - border-color: #222222 #222222 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #222222; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-inverse:hover, -.btn-inverse:focus, -.btn-inverse:active, -.btn-inverse.active, -.btn-inverse.disabled, -.btn-inverse[disabled] { - color: #ffffff; - background-color: #222222; - *background-color: #151515; -} -.btn-inverse:active, -.btn-inverse.active { - background-color: #080808 \9; -} -button.btn, -input[type="submit"].btn { - *padding-top: 3px; - *padding-bottom: 3px; -} -button.btn::-moz-focus-inner, -input[type="submit"].btn::-moz-focus-inner { - padding: 0; - border: 0; -} -button.btn.btn-large, -input[type="submit"].btn.btn-large { - *padding-top: 7px; - *padding-bottom: 7px; -} -button.btn.btn-small, -input[type="submit"].btn.btn-small { - *padding-top: 3px; - *padding-bottom: 3px; -} -button.btn.btn-mini, -input[type="submit"].btn.btn-mini { - *padding-top: 1px; - *padding-bottom: 1px; -} -.btn-link, -.btn-link:active, -.btn-link[disabled] { - background-color: transparent; - background-image: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn-link { - border-color: transparent; - cursor: pointer; - color: #0088cc; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-link:hover, -.btn-link:focus { - color: #005580; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -.btn-link[disabled]:focus { - color: #333333; - text-decoration: none; -} -.btn-group { - position: relative; - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - font-size: 0; - vertical-align: middle; - white-space: nowrap; - *margin-left: .3em; -} -.btn-group:first-child { - *margin-left: 0; -} -.btn-group + .btn-group { - margin-left: 5px; -} -.btn-toolbar { - font-size: 0; - margin-top: 9px; - margin-bottom: 9px; -} -.btn-toolbar > .btn + .btn, -.btn-toolbar > .btn-group + .btn, -.btn-toolbar > .btn + .btn-group { - margin-left: 5px; -} -.btn-group > .btn { - position: relative; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-group > .btn + .btn { - margin-left: -1px; -} -.btn-group > .btn, -.btn-group > .dropdown-menu, -.btn-group > .popover { - font-size: 13px; -} -.btn-group > .btn-mini { - font-size: 9.75px; -} -.btn-group > .btn-small { - font-size: 11.049999999999999px; -} -.btn-group > .btn-large { - font-size: 16.25px; -} -.btn-group > .btn:first-child { - margin-left: 0; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.btn-group > .btn:last-child, -.btn-group > .dropdown-toggle { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.btn-group > .btn.large:first-child { - margin-left: 0; - -webkit-border-top-left-radius: 6px; - -moz-border-radius-topleft: 6px; - border-top-left-radius: 6px; - -webkit-border-bottom-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - border-bottom-left-radius: 6px; -} -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - -webkit-border-top-right-radius: 6px; - -moz-border-radius-topright: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - -moz-border-radius-bottomright: 6px; - border-bottom-right-radius: 6px; -} -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active { - z-index: 2; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; - -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - *padding-top: 5px; - *padding-bottom: 5px; -} -.btn-group > .btn-mini + .dropdown-toggle { - padding-left: 5px; - padding-right: 5px; - *padding-top: 2px; - *padding-bottom: 2px; -} -.btn-group > .btn-small + .dropdown-toggle { - *padding-top: 5px; - *padding-bottom: 4px; -} -.btn-group > .btn-large + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; - *padding-top: 7px; - *padding-bottom: 7px; -} -.btn-group.open .dropdown-toggle { - background-image: none; - -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); -} -.btn-group.open .btn.dropdown-toggle { - background-color: #e6e6e6; -} -.btn-group.open .btn-primary.dropdown-toggle { - background-color: #0044cc; -} -.btn-group.open .btn-warning.dropdown-toggle { - background-color: #f89406; -} -.btn-group.open .btn-danger.dropdown-toggle { - background-color: #bd362f; -} -.btn-group.open .btn-success.dropdown-toggle { - background-color: #51a351; -} -.btn-group.open .btn-info.dropdown-toggle { - background-color: #2f96b4; -} -.btn-group.open .btn-inverse.dropdown-toggle { - background-color: #222222; -} -.btn .caret { - margin-top: 8px; - margin-left: 0; -} -.btn-large .caret { - margin-top: 6px; -} -.btn-large .caret { - border-left-width: 5px; - border-right-width: 5px; - border-top-width: 5px; -} -.btn-mini .caret, -.btn-small .caret { - margin-top: 8px; -} -.dropup .btn-large .caret { - border-bottom-width: 5px; -} -.btn-primary .caret, -.btn-warning .caret, -.btn-danger .caret, -.btn-info .caret, -.btn-success .caret, -.btn-inverse .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.btn-group-vertical { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; -} -.btn-group-vertical > .btn { - display: block; - float: none; - max-width: 100%; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-group-vertical > .btn + .btn { - margin-left: 0; - margin-top: -1px; -} -.btn-group-vertical > .btn:first-child { - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} -.btn-group-vertical > .btn:last-child { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} -.btn-group-vertical > .btn-large:first-child { - -webkit-border-radius: 6px 6px 0 0; - -moz-border-radius: 6px 6px 0 0; - border-radius: 6px 6px 0 0; -} -.btn-group-vertical > .btn-large:last-child { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} -.alert { - padding: 8px 35px 8px 14px; - margin-bottom: 18px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - background-color: #fcf8e3; - border: 1px solid #fbeed5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.alert, -.alert h4 { - color: #c09853; -} -.alert h4 { - margin: 0; -} -.alert .close { - position: relative; - top: -2px; - right: -21px; - line-height: 18px; -} -.alert-success { - background-color: #dff0d8; - border-color: #d6e9c6; - color: #468847; -} -.alert-success h4 { - color: #468847; -} -.alert-danger, -.alert-error { - background-color: #f2dede; - border-color: #eed3d7; - color: #b94a48; -} -.alert-danger h4, -.alert-error h4 { - color: #b94a48; -} -.alert-info { - background-color: #d9edf7; - border-color: #bce8f1; - color: #3a87ad; -} -.alert-info h4 { - color: #3a87ad; -} -.alert-block { - padding-top: 14px; - padding-bottom: 14px; -} -.alert-block > p, -.alert-block > ul { - margin-bottom: 0; -} -.alert-block p + p { - margin-top: 5px; -} -.nav { - margin-left: 0; - margin-bottom: 18px; - list-style: none; -} -.nav > li > a { - display: block; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eeeeee; -} -.nav > li > a > img { - max-width: none; -} -.nav > .pull-right { - float: right; -} -.nav-header { - display: block; - padding: 3px 15px; - font-size: 11px; - font-weight: bold; - line-height: 18px; - color: #999999; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-transform: uppercase; -} -.nav li + .nav-header { - margin-top: 9px; -} -.nav-list { - padding-left: 15px; - padding-right: 15px; - margin-bottom: 0; -} -.nav-list > li > a, -.nav-list .nav-header { - margin-left: -15px; - margin-right: -15px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.nav-list > li > a { - padding: 3px 15px; -} -.nav-list > .active > a, -.nav-list > .active > a:hover, -.nav-list > .active > a:focus { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); - background-color: #0088cc; -} -.nav-list [class^="icon-"], -.nav-list [class*=" icon-"] { - margin-right: 2px; -} -.nav-list .divider { - *width: 100%; - height: 1px; - margin: 8px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} -.nav-tabs, -.nav-pills { - *zoom: 1; -} -.nav-tabs:before, -.nav-pills:before, -.nav-tabs:after, -.nav-pills:after { - display: table; - content: ""; - line-height: 0; -} -.nav-tabs:after, -.nav-pills:after { - clear: both; -} -.nav-tabs > li, -.nav-pills > li { - float: left; -} -.nav-tabs > li > a, -.nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; - margin-right: 2px; - line-height: 14px; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - margin-bottom: -1px; -} -.nav-tabs > li > a { - padding-top: 8px; - padding-bottom: 8px; - line-height: 18px; - border: 1px solid transparent; - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover, -.nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #dddddd; -} -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover, -.nav-tabs > .active > a:focus { - color: #555555; - background-color: #ffffff; - border: 1px solid #ddd; - border-bottom-color: transparent; - cursor: default; -} -.nav-pills > li > a { - padding-top: 8px; - padding-bottom: 8px; - margin-top: 2px; - margin-bottom: 2px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} -.nav-pills > .active > a, -.nav-pills > .active > a:hover, -.nav-pills > .active > a:focus { - color: #ffffff; - background-color: #0088cc; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li > a { - margin-right: 0; -} -.nav-tabs.nav-stacked { - border-bottom: 0; -} -.nav-tabs.nav-stacked > li > a { - border: 1px solid #ddd; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.nav-tabs.nav-stacked > li:first-child > a { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.nav-tabs.nav-stacked > li:last-child > a { - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.nav-tabs.nav-stacked > li > a:hover, -.nav-tabs.nav-stacked > li > a:focus { - border-color: #ddd; - z-index: 2; -} -.nav-pills.nav-stacked > li > a { - margin-bottom: 3px; -} -.nav-pills.nav-stacked > li:last-child > a { - margin-bottom: 1px; -} -.nav-tabs .dropdown-menu { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} -.nav-pills .dropdown-menu { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.nav .dropdown-toggle .caret { - border-top-color: #0088cc; - border-bottom-color: #0088cc; - margin-top: 6px; -} -.nav .dropdown-toggle:hover .caret, -.nav .dropdown-toggle:focus .caret { - border-top-color: #005580; - border-bottom-color: #005580; -} -/* move down carets for tabs */ -.nav-tabs .dropdown-toggle .caret { - margin-top: 8px; -} -.nav .active .dropdown-toggle .caret { - border-top-color: #fff; - border-bottom-color: #fff; -} -.nav-tabs .active .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} -.nav > .dropdown.active > a:hover, -.nav > .dropdown.active > a:focus { - cursor: pointer; -} -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover, -.nav > li.dropdown.open.active > a:focus { - color: #ffffff; - background-color: #999999; - border-color: #999999; -} -.nav li.dropdown.open .caret, -.nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret, -.nav li.dropdown.open a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} -.tabs-stacked .open > a:hover, -.tabs-stacked .open > a:focus { - border-color: #999999; -} -.tabbable { - *zoom: 1; -} -.tabbable:before, -.tabbable:after { - display: table; - content: ""; - line-height: 0; -} -.tabbable:after { - clear: both; -} -.tab-content { - overflow: auto; -} -.tabs-below > .nav-tabs, -.tabs-right > .nav-tabs, -.tabs-left > .nav-tabs { - border-bottom: 0; -} -.tab-content > .tab-pane, -.pill-content > .pill-pane { - display: none; -} -.tab-content > .active, -.pill-content > .active { - display: block; -} -.tabs-below > .nav-tabs { - border-top: 1px solid #ddd; -} -.tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} -.tabs-below > .nav-tabs > li > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} -.tabs-below > .nav-tabs > li > a:hover, -.tabs-below > .nav-tabs > li > a:focus { - border-bottom-color: transparent; - border-top-color: #ddd; -} -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover, -.tabs-below > .nav-tabs > .active > a:focus { - border-color: transparent #ddd #ddd #ddd; -} -.tabs-left > .nav-tabs > li, -.tabs-right > .nav-tabs > li { - float: none; -} -.tabs-left > .nav-tabs > li > a, -.tabs-right > .nav-tabs > li > a { - min-width: 74px; - margin-right: 0; - margin-bottom: 3px; -} -.tabs-left > .nav-tabs { - float: left; - margin-right: 19px; - border-right: 1px solid #ddd; -} -.tabs-left > .nav-tabs > li > a { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.tabs-left > .nav-tabs > li > a:hover, -.tabs-left > .nav-tabs > li > a:focus { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover, -.tabs-left > .nav-tabs .active > a:focus { - border-color: #ddd transparent #ddd #ddd; - *border-right-color: #ffffff; -} -.tabs-right > .nav-tabs { - float: right; - margin-left: 19px; - border-left: 1px solid #ddd; -} -.tabs-right > .nav-tabs > li > a { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.tabs-right > .nav-tabs > li > a:hover, -.tabs-right > .nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover, -.tabs-right > .nav-tabs .active > a:focus { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} -.nav > .disabled > a { - color: #999999; -} -.nav > .disabled > a:hover, -.nav > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - cursor: default; -} -.navbar { - overflow: visible; - margin-bottom: 18px; - *position: relative; - *z-index: 2; -} -.navbar-inner { - min-height: 40px; - padding-left: 20px; - padding-right: 20px; - background-color: #2c2c2c; - background-image: -moz-linear-gradient(top, #333333, #222222); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); - background-image: -webkit-linear-gradient(top, #333333, #222222); - background-image: -o-linear-gradient(top, #333333, #222222); - background-image: linear-gradient(to bottom, #333333, #222222); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff333333', endColorstr='#ff222222', GradientType=0); - border: 1px solid #030303; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - *zoom: 1; -} -.navbar-inner:before, -.navbar-inner:after { - display: table; - content: ""; - line-height: 0; -} -.navbar-inner:after { - clear: both; -} -.navbar .container { - width: auto; -} -.nav-collapse.collapse { - height: auto; - overflow: visible; -} -.navbar .brand { - float: left; - display: block; - padding: 11px 20px 11px; - margin-left: -20px; - font-size: 20px; - font-weight: 200; - color: #999999; - text-shadow: 0 1px 0 #333333; -} -.navbar .brand:hover, -.navbar .brand:focus { - text-decoration: none; -} -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #999999; -} -.navbar-link { - color: #999999; -} -.navbar-link:hover, -.navbar-link:focus { - color: #ffffff; -} -.navbar .divider-vertical { - height: 40px; - margin: 0 9px; - border-left: 1px solid #222222; - border-right: 1px solid #333333; -} -.navbar .btn, -.navbar .btn-group { - margin-top: 5px; -} -.navbar .btn-group .btn, -.navbar .input-prepend .btn, -.navbar .input-append .btn, -.navbar .input-prepend .btn-group, -.navbar .input-append .btn-group { - margin-top: 0; -} -.navbar-form { - margin-bottom: 0; - *zoom: 1; -} -.navbar-form:before, -.navbar-form:after { - display: table; - content: ""; - line-height: 0; -} -.navbar-form:after { - clear: both; -} -.navbar-form input, -.navbar-form select, -.navbar-form .radio, -.navbar-form .checkbox { - margin-top: 5px; -} -.navbar-form input, -.navbar-form select, -.navbar-form .btn { - display: inline-block; - margin-bottom: 0; -} -.navbar-form input[type="image"], -.navbar-form input[type="checkbox"], -.navbar-form input[type="radio"] { - margin-top: 3px; -} -.navbar-form .input-append, -.navbar-form .input-prepend { - margin-top: 5px; - white-space: nowrap; -} -.navbar-form .input-append input, -.navbar-form .input-prepend input { - margin-top: 0; -} -.navbar-search { - position: relative; - float: left; - margin-top: 5px; - margin-bottom: 0; -} -.navbar-search .search-query { - margin-bottom: 0; - padding: 4px 14px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: 1; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -.navbar-static-top { - position: static; - margin-bottom: 0; -} -.navbar-static-top .navbar-inner { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; - margin-bottom: 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - border-width: 0 0 1px; -} -.navbar-fixed-bottom .navbar-inner { - border-width: 1px 0 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-fixed-bottom .navbar-inner { - padding-left: 0; - padding-right: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} -.navbar-fixed-top { - top: 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1); - -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1); - box-shadow: 0 1px 10px rgba(0,0,0,.1); -} -.navbar-fixed-bottom { - bottom: 0; -} -.navbar-fixed-bottom .navbar-inner { - -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1); - -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1); - box-shadow: 0 -1px 10px rgba(0,0,0,.1); -} -.navbar .nav { - position: relative; - left: 0; - display: block; - float: left; - margin: 0 10px 0 0; -} -.navbar .nav.pull-right { - float: right; - margin-right: 0; -} -.navbar .nav > li { - float: left; -} -.navbar .nav > li > a { - float: none; - padding: 11px 15px 11px; - color: #999999; - text-decoration: none; - text-shadow: 0 1px 0 #333333; -} -.navbar .nav .dropdown-toggle .caret { - margin-top: 8px; -} -.navbar .nav > li > a:focus, -.navbar .nav > li > a:hover { - background-color: transparent; - color: #ffffff; - text-decoration: none; -} -.navbar .nav > .active > a, -.navbar .nav > .active > a:hover, -.navbar .nav > .active > a:focus { - color: #555555; - text-decoration: none; - background-color: #151515; - -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); -} -.navbar .btn-navbar { - display: none; - float: right; - padding: 7px 10px; - margin-left: 5px; - margin-right: 5px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #1f1f1f; - background-image: -moz-linear-gradient(top, #262626, #151515); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#262626), to(#151515)); - background-image: -webkit-linear-gradient(top, #262626, #151515); - background-image: -o-linear-gradient(top, #262626, #151515); - background-image: linear-gradient(to bottom, #262626, #151515); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff262626', endColorstr='#ff151515', GradientType=0); - border-color: #151515 #151515 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #151515; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); - box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); -} -.navbar .btn-navbar:hover, -.navbar .btn-navbar:focus, -.navbar .btn-navbar:active, -.navbar .btn-navbar.active, -.navbar .btn-navbar.disabled, -.navbar .btn-navbar[disabled] { - color: #ffffff; - background-color: #151515; - *background-color: #080808; -} -.navbar .btn-navbar:active, -.navbar .btn-navbar.active { - background-color: #000000 \9; -} -.navbar .btn-navbar .icon-bar { - display: block; - width: 18px; - height: 2px; - background-color: #f5f5f5; - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; - -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -} -.btn-navbar .icon-bar + .icon-bar { - margin-top: 3px; -} -.navbar .nav > li > .dropdown-menu:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-bottom-color: rgba(0, 0, 0, 0.2); - position: absolute; - top: -7px; - left: 9px; -} -.navbar .nav > li > .dropdown-menu:after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #ffffff; - position: absolute; - top: -6px; - left: 10px; -} -.navbar-fixed-bottom .nav > li > .dropdown-menu:before { - border-top: 7px solid #ccc; - border-top-color: rgba(0, 0, 0, 0.2); - border-bottom: 0; - bottom: -7px; - top: auto; -} -.navbar-fixed-bottom .nav > li > .dropdown-menu:after { - border-top: 6px solid #ffffff; - border-bottom: 0; - bottom: -6px; - top: auto; -} -.navbar .nav li.dropdown > a:hover .caret, -.navbar .nav li.dropdown > a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.navbar .nav li.dropdown.open > .dropdown-toggle, -.navbar .nav li.dropdown.active > .dropdown-toggle, -.navbar .nav li.dropdown.open.active > .dropdown-toggle { - background-color: #151515; - color: #555555; -} -.navbar .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} -.navbar .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} -.navbar .pull-right > li > .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right { - left: auto; - right: 0; -} -.navbar .pull-right > li > .dropdown-menu:before, -.navbar .nav > li > .dropdown-menu.pull-right:before { - left: auto; - right: 12px; -} -.navbar .pull-right > li > .dropdown-menu:after, -.navbar .nav > li > .dropdown-menu.pull-right:after { - left: auto; - right: 13px; -} -.navbar .pull-right > li > .dropdown-menu .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { - left: auto; - right: 100%; - margin-left: 0; - margin-right: -1px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} -.navbar-inverse .navbar-inner { - background-color: #1b1b1b; - background-image: -moz-linear-gradient(top, #222222, #111111); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); - background-image: -webkit-linear-gradient(top, #222222, #111111); - background-image: -o-linear-gradient(top, #222222, #111111); - background-image: linear-gradient(to bottom, #222222, #111111); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); - border-color: #252525; -} -.navbar-inverse .brand, -.navbar-inverse .nav > li > a { - color: #999999; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} -.navbar-inverse .brand:hover, -.navbar-inverse .nav > li > a:hover, -.navbar-inverse .brand:focus, -.navbar-inverse .nav > li > a:focus { - color: #ffffff; -} -.navbar-inverse .brand { - color: #999999; -} -.navbar-inverse .navbar-text { - color: #999999; -} -.navbar-inverse .nav > li > a:focus, -.navbar-inverse .nav > li > a:hover { - background-color: transparent; - color: #ffffff; -} -.navbar-inverse .nav .active > a, -.navbar-inverse .nav .active > a:hover, -.navbar-inverse .nav .active > a:focus { - color: #ffffff; - background-color: #111111; -} -.navbar-inverse .navbar-link { - color: #999999; -} -.navbar-inverse .navbar-link:hover, -.navbar-inverse .navbar-link:focus { - color: #ffffff; -} -.navbar-inverse .divider-vertical { - border-left-color: #111111; - border-right-color: #222222; -} -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { - background-color: #111111; - color: #ffffff; -} -.navbar-inverse .nav li.dropdown > a:hover .caret, -.navbar-inverse .nav li.dropdown > a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.navbar-inverse .navbar-search .search-query { - color: #ffffff; - background-color: #515151; - border-color: #111111; - -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; -} -.navbar-inverse .navbar-search .search-query:-moz-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query:focus, -.navbar-inverse .navbar-search .search-query.focused { - padding: 5px 15px; - color: #333333; - text-shadow: 0 1px 0 #ffffff; - background-color: #ffffff; - border: 0; - -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - outline: 0; -} -.navbar-inverse .btn-navbar { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e0e0e; - background-image: -moz-linear-gradient(top, #151515, #040404); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); - background-image: -webkit-linear-gradient(top, #151515, #040404); - background-image: -o-linear-gradient(top, #151515, #040404); - background-image: linear-gradient(to bottom, #151515, #040404); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); - border-color: #040404 #040404 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #040404; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.navbar-inverse .btn-navbar:hover, -.navbar-inverse .btn-navbar:focus, -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active, -.navbar-inverse .btn-navbar.disabled, -.navbar-inverse .btn-navbar[disabled] { - color: #ffffff; - background-color: #040404; - *background-color: #000000; -} -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active { - background-color: #000000 \9; -} -.breadcrumb { - padding: 8px 15px; - margin: 0 0 18px; - list-style: none; - background-color: #f5f5f5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - text-shadow: 0 1px 0 #ffffff; -} -.breadcrumb > li > .divider { - padding: 0 5px; - color: #ccc; -} -.breadcrumb > .active { - color: #999999; -} -.pagination { - margin: 18px 0; -} -.pagination ul { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; - margin-left: 0; - margin-bottom: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} -.pagination ul > li { - display: inline; -} -.pagination ul > li > a, -.pagination ul > li > span { - float: left; - padding: 4px 12px; - line-height: 18px; - text-decoration: none; - background-color: #ffffff; - border: 1px solid #dddddd; - border-left-width: 0; -} -.pagination ul > li > a:hover, -.pagination ul > li > a:focus, -.pagination ul > .active > a, -.pagination ul > .active > span { - background-color: #f5f5f5; -} -.pagination ul > .active > a, -.pagination ul > .active > span { - color: #999999; - cursor: default; -} -.pagination ul > .disabled > span, -.pagination ul > .disabled > a, -.pagination ul > .disabled > a:hover, -.pagination ul > .disabled > a:focus { - color: #999999; - background-color: transparent; - cursor: default; -} -.pagination ul > li:first-child > a, -.pagination ul > li:first-child > span { - border-left-width: 1px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.pagination ul > li:last-child > a, -.pagination ul > li:last-child > span { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.pagination-centered { - text-align: center; -} -.pagination-right { - text-align: right; -} -.pagination-large ul > li > a, -.pagination-large ul > li > span { - padding: 11px 19px; - font-size: 16.25px; -} -.pagination-large ul > li:first-child > a, -.pagination-large ul > li:first-child > span { - -webkit-border-top-left-radius: 6px; - -moz-border-radius-topleft: 6px; - border-top-left-radius: 6px; - -webkit-border-bottom-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - border-bottom-left-radius: 6px; -} -.pagination-large ul > li:last-child > a, -.pagination-large ul > li:last-child > span { - -webkit-border-top-right-radius: 6px; - -moz-border-radius-topright: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - -moz-border-radius-bottomright: 6px; - border-bottom-right-radius: 6px; -} -.pagination-mini ul > li:first-child > a, -.pagination-small ul > li:first-child > a, -.pagination-mini ul > li:first-child > span, -.pagination-small ul > li:first-child > span { - -webkit-border-top-left-radius: 3px; - -moz-border-radius-topleft: 3px; - border-top-left-radius: 3px; - -webkit-border-bottom-left-radius: 3px; - -moz-border-radius-bottomleft: 3px; - border-bottom-left-radius: 3px; -} -.pagination-mini ul > li:last-child > a, -.pagination-small ul > li:last-child > a, -.pagination-mini ul > li:last-child > span, -.pagination-small ul > li:last-child > span { - -webkit-border-top-right-radius: 3px; - -moz-border-radius-topright: 3px; - border-top-right-radius: 3px; - -webkit-border-bottom-right-radius: 3px; - -moz-border-radius-bottomright: 3px; - border-bottom-right-radius: 3px; -} -.pagination-small ul > li > a, -.pagination-small ul > li > span { - padding: 2px 10px; - font-size: 11.049999999999999px; -} -.pagination-mini ul > li > a, -.pagination-mini ul > li > span { - padding: 0 6px; - font-size: 9.75px; -} -.pager { - margin: 18px 0; - list-style: none; - text-align: center; - *zoom: 1; -} -.pager:before, -.pager:after { - display: table; - content: ""; - line-height: 0; -} -.pager:after { - clear: both; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #f5f5f5; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #999999; - background-color: #fff; - cursor: default; -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000000; -} -.modal-backdrop.fade { - opacity: 0; -} -.modal-backdrop, -.modal-backdrop.fade.in { - opacity: 0.8; - filter: alpha(opacity=80); -} -.modal { - position: fixed; - top: 10%; - left: 50%; - z-index: 1050; - width: 560px; - margin-left: -280px; - background-color: #ffffff; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, 0.3); - *border: 1px solid #999; - /* IE6-7 */ - - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; - outline: none; -} -.modal.fade { - -webkit-transition: opacity .3s linear, top .3s ease-out; - -moz-transition: opacity .3s linear, top .3s ease-out; - -o-transition: opacity .3s linear, top .3s ease-out; - transition: opacity .3s linear, top .3s ease-out; - top: -25%; -} -.modal.fade.in { - top: 10%; -} -.modal-header { - padding: 9px 15px; - border-bottom: 1px solid #eee; -} -.modal-header .close { - margin-top: 2px; -} -.modal-header h3 { - margin: 0; - line-height: 30px; -} -.modal-body { - position: relative; - overflow-y: auto; - max-height: 400px; - padding: 15px; -} -.modal-form { - margin-bottom: 0; -} -.modal-footer { - padding: 14px 15px 15px; - margin-bottom: 0; - text-align: right; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; - *zoom: 1; -} -.modal-footer:before, -.modal-footer:after { - display: table; - content: ""; - line-height: 0; -} -.modal-footer:after { - clear: both; -} -.modal-footer .btn + .btn { - margin-left: 5px; - margin-bottom: 0; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.tooltip { - position: absolute; - z-index: 1020; - display: block; - visibility: visible; - font-size: 11px; - line-height: 1.4; - opacity: 0; - filter: alpha(opacity=0); -} -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} -.tooltip.top { - margin-top: -3px; - padding: 5px 0; -} -.tooltip.right { - margin-left: 3px; - padding: 0 5px; -} -.tooltip.bottom { - margin-top: 3px; - padding: 5px 0; -} -.tooltip.left { - margin-left: -3px; - padding: 0 5px; -} -.tooltip-inner { - max-width: 200px; - padding: 8px; - color: #ffffff; - text-align: center; - text-decoration: none; - background-color: #000000; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1010; - display: none; - max-width: 276px; - padding: 1px; - text-align: left; - background-color: #ffffff; - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - white-space: normal; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - margin: 0; - padding: 8px 14px; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; -} -.popover-title:empty { - display: none; -} -.popover-content { - padding: 9px 14px; -} -.popover .arrow, -.popover .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover .arrow { - border-width: 11px; -} -.popover .arrow:after { - border-width: 10px; - content: ""; -} -.popover.top .arrow { - left: 50%; - margin-left: -11px; - border-bottom-width: 0; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, 0.25); - bottom: -11px; -} -.popover.top .arrow:after { - bottom: 1px; - margin-left: -10px; - border-bottom-width: 0; - border-top-color: #ffffff; -} -.popover.right .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-left-width: 0; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, 0.25); -} -.popover.right .arrow:after { - left: 1px; - bottom: -10px; - border-left-width: 0; - border-right-color: #ffffff; -} -.popover.bottom .arrow { - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, 0.25); - top: -11px; -} -.popover.bottom .arrow:after { - top: 1px; - margin-left: -10px; - border-top-width: 0; - border-bottom-color: #ffffff; -} -.popover.left .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, 0.25); -} -.popover.left .arrow:after { - right: 1px; - border-right-width: 0; - border-left-color: #ffffff; - bottom: -10px; -} -.thumbnails { - margin-left: -20px; - list-style: none; - *zoom: 1; -} -.thumbnails:before, -.thumbnails:after { - display: table; - content: ""; - line-height: 0; -} -.thumbnails:after { - clear: both; -} -.row-fluid .thumbnails { - margin-left: 0; -} -.thumbnails > li { - float: left; - margin-bottom: 18px; - margin-left: 20px; -} -.thumbnail { - display: block; - padding: 4px; - line-height: 18px; - border: 1px solid #ddd; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} -a.thumbnail:hover, -a.thumbnail:focus { - border-color: #0088cc; - -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -} -.thumbnail > img { - display: block; - max-width: 100%; - margin-left: auto; - margin-right: auto; -} -.thumbnail .caption { - padding: 9px; - color: #555555; -} -.media, -.media-body { - overflow: hidden; - *overflow: visible; - zoom: 1; -} -.media, -.media .media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media-object { - display: block; -} -.media-heading { - margin: 0 0 5px; -} -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-right { - margin-left: 10px; -} -.media-list { - margin-left: 0; - list-style: none; -} -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: 10.998px; - font-weight: bold; - line-height: 14px; - color: #ffffff; - vertical-align: baseline; - white-space: nowrap; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #999999; -} -.label { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.badge { - padding-left: 9px; - padding-right: 9px; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; -} -.label:empty, -.badge:empty { - display: none; -} -a.label:hover, -a.label:focus, -a.badge:hover, -a.badge:focus { - color: #ffffff; - text-decoration: none; - cursor: pointer; -} -.label-important, -.badge-important { - background-color: #b94a48; -} -.label-important[href], -.badge-important[href] { - background-color: #953b39; -} -.label-warning, -.badge-warning { - background-color: #f89406; -} -.label-warning[href], -.badge-warning[href] { - background-color: #c67605; -} -.label-success, -.badge-success { - background-color: #468847; -} -.label-success[href], -.badge-success[href] { - background-color: #356635; -} -.label-info, -.badge-info { - background-color: #3a87ad; -} -.label-info[href], -.badge-info[href] { - background-color: #2d6987; -} -.label-inverse, -.badge-inverse { - background-color: #333333; -} -.label-inverse[href], -.badge-inverse[href] { - background-color: #1a1a1a; -} -.btn .label, -.btn .badge { - position: relative; - top: -1px; -} -.btn-mini .label, -.btn-mini .badge { - top: 0; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-moz-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-ms-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - overflow: hidden; - height: 18px; - margin-bottom: 18px; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.progress .bar { - width: 0%; - height: 100%; - color: #ffffff; - float: left; - font-size: 12px; - text-align: center; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: width 0.6s ease; - -moz-transition: width 0.6s ease; - -o-transition: width 0.6s ease; - transition: width 0.6s ease; -} -.progress .bar + .bar { - -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); - -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); - box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); -} -.progress-striped .bar { - background-color: #149bdf; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - -moz-background-size: 40px 40px; - -o-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-danger .bar, -.progress .bar-danger { - background-color: #dd514c; - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); -} -.progress-danger.progress-striped .bar, -.progress-striped .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-success .bar, -.progress .bar-success { - background-color: #5eb95e; - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(to bottom, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); -} -.progress-success.progress-striped .bar, -.progress-striped .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-info .bar, -.progress .bar-info { - background-color: #4bb1cf; - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(to bottom, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); -} -.progress-info.progress-striped .bar, -.progress-striped .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-warning .bar, -.progress .bar-warning { - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); -} -.progress-warning.progress-striped .bar, -.progress-striped .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.accordion { - margin-bottom: 18px; -} -.accordion-group { - margin-bottom: 2px; - border: 1px solid #e5e5e5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.accordion-heading { - border-bottom: 0; -} -.accordion-heading .accordion-toggle { - display: block; - padding: 8px 15px; -} -.accordion-toggle { - cursor: pointer; -} -.accordion-inner { - padding: 9px 15px; - border-top: 1px solid #e5e5e5; -} -.carousel { - position: relative; - margin-bottom: 18px; - line-height: 1; -} -.carousel-inner { - overflow: hidden; - width: 100%; - position: relative; -} -.carousel-inner > .item { - display: none; - position: relative; - -webkit-transition: 0.6s ease-in-out left; - -moz-transition: 0.6s ease-in-out left; - -o-transition: 0.6s ease-in-out left; - transition: 0.6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - line-height: 1; -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 40%; - left: 15px; - width: 40px; - height: 40px; - margin-top: -20px; - font-size: 60px; - font-weight: 100; - line-height: 30px; - color: #ffffff; - text-align: center; - background: #222222; - border: 3px solid #ffffff; - -webkit-border-radius: 23px; - -moz-border-radius: 23px; - border-radius: 23px; - opacity: 0.5; - filter: alpha(opacity=50); -} -.carousel-control.right { - left: auto; - right: 15px; -} -.carousel-control:hover, -.carousel-control:focus { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} -.carousel-indicators { - position: absolute; - top: 15px; - right: 15px; - z-index: 5; - margin: 0; - list-style: none; -} -.carousel-indicators li { - display: block; - float: left; - width: 10px; - height: 10px; - margin-left: 5px; - text-indent: -999px; - background-color: #ccc; - background-color: rgba(255, 255, 255, 0.25); - border-radius: 5px; -} -.carousel-indicators .active { - background-color: #fff; -} -.carousel-caption { - position: absolute; - left: 0; - right: 0; - bottom: 0; - padding: 15px; - background: #333333; - background: rgba(0, 0, 0, 0.75); -} -.carousel-caption h4, -.carousel-caption p { - color: #ffffff; - line-height: 18px; -} -.carousel-caption h4 { - margin: 0 0 5px; -} -.carousel-caption p { - margin-bottom: 0; -} -.hero-unit { - padding: 60px; - margin-bottom: 30px; - font-size: 18px; - font-weight: 200; - line-height: 27px; - color: inherit; - background-color: #eeeeee; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - color: inherit; - letter-spacing: -1px; -} -.hero-unit li { - line-height: 27px; -} -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.hide { - display: none; -} -.show { - display: block; -} -.invisible { - visibility: hidden; -} -.affix { - position: fixed; -} -/** - * bootstrap-variables.less - * Copy of bootstrap/lib/variables.less, for kegweb specific modifications. - */ -body { - padding-top: 40px; - /* 40px to make the container go all the way to the bottom of the topbar */ - -} -.sidebar { - max-width: 200px; -} -.container-fluid > footer p { - text-align: center; - /* center align it with the container */ - -} -[class^="icon-"] { - background-image: url("../bootstrap/img/glyphicons-halflings.png"); -} -[class*="icon-white"] { - background-image: url("../bootstrap/img/glyphicons-halflings-white.png"); -} -.page-header { - background-color: #f5f5f5; - padding: 20px 20px 10px; - margin: 0 -20px; -} -div#content { - background-color: #fff; - padding: 20px; - margin: 0 -20px; - /* negative indent the amount of the padding to maintain the grid system */ - - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); -} -.helptext { - color: #595959; - display: block; - margin-bottom: 9px; -} -.fill-parent-width { - width: 100%; -} -.center-block { - display: block; - margin: 0 auto; - text-align: center; -} -.badge-list { - *zoom: 1; - margin-left: 0; - list-style: none; -} -.badge-list:before, -.badge-list:after { - display: table; - content: ""; - line-height: 0; -} -.badge-list:after { - clear: both; -} -.badge-list > li { - float: left; - width: 120px; - background-color: #555555; - color: #eeeeee; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - border: 1px solid #eeeeee; - text-align: center; - padding: 4px; - margin-right: 4px; -} -.badge-list > li a { - color: #eeeeee; -} -.badge-list > li h2 { - color: #eeeeee; - text-shadow: #000000 2px 2px 2px; -} -.badge-list > li small { - color: #999999; - text-transform: uppercase; - text-weight: bold; -} -.badge-list .badge-small h2 { - margin: 0px; - text-shadow: none; - font-size: 22.75px; - line-height: 31.5px; -} -.avatar-list { - *zoom: 1; - margin-left: 0; - margin-bottom: 0; - list-style: none; -} -.avatar-list:before, -.avatar-list:after { - display: table; - content: ""; - line-height: 0; -} -.avatar-list:after { - clear: both; -} -.avatar-list > li { - margin: 0; - float: left; - padding: 1px; -} -.tap-snapshot-image { - width: 120px; - height: auto; - padding: 4px; -} -.right-col { - background-color: #eeeeee; -} -.badge-header { - font-size: 15px; - color: #333333; - font-weight: bold; - padding: 2px; - -webkit-border-radius: 6px 6px 0px 0px; - -moz-border-radius: 6px 6px 0px 0px; - border-radius: 6px 6px 0px 0px; - text-align: center; - border-bottom: 1px solid #eee; - font: bold; -} -.kb-tapbox { - *zoom: 1; - border-bottom: 1px solid #eeeeee; - margin-bottom: 16px; -} -.kb-tapbox:before, -.kb-tapbox:after { - display: table; - content: ""; - line-height: 0; -} -.kb-tapbox:after { - clear: both; -} -.session-snapshot { - border-bottom: 1px solid #eeeeee; - padding: 8px; -} -.clickable { - cursor: pointer; -} -.clickable:hover { - background-color: #eeeeee; -} -.kb-keg-description { - color: #333333; - margin-bottom: 8px; -} -.smalltext { - font-size: 11px; - color: #999999; - font-family: inherit; - font-weight: bold; -} -.arrow_box { - position: relative; - background: #ebebeb; - border: 3px solid #9fabc2; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; -} -.arrow_box:after, -.arrow_box:before { - right: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; -} -.arrow_box:after { - border-right-color: #ebebeb; - border-width: 20px; - top: 50%; - margin-top: -20px; -} -.arrow_box:before { - border-right-color: #9fabc2; - border-width: 24px; - top: 50%; - margin-top: -24px; -} -/* @end */ -/* @group Charts */ -.kb-chartbox-error { - color: #ccc; - margin-left: auto; - margin-right: auto; - display: block; - margin: 0 auto; - text-align: center; -} -/* @group Events */ -.kb-event-box-info { - font-size: 0.7em; - color: #999; -} -.kb-event-box-info a:link { - color: #999; - text-decoration: underline; - font-weight: normal; -} -.kb-event-box-info a:visited { - color: #999; - text-decoration: underline; - font-weight: normal; -} -.kb-event-box-dateline { - font-size: 0.7em; - color: #ccc; - margin-top: 4px; - font-style: italic; -} -.kb-event-box { - width: 300px; - margin-bottom: 8px; - padding: 8px; - border: 1px solid #eee; - display: inline-block; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - *zoom: 1; -} -.kb-event-box:before, -.kb-event-box:after { - display: table; - content: ""; - line-height: 0; -} -.kb-event-box:after { - clear: both; -} -.kb-event-box-session-start { - width: 300px; - text-align: center; - margin-bottom: 32px; - font-size: 0.7em; - background-color: #ddd; - color: #777; - padding: 5px; -} -.kb-event-box { - width: 300px; - margin-bottom: 8px; - padding: 8px; - border: 1px solid #eee; - vertical-align: middle; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} -.kb-event-box-details { - vertical-align: middle; -} -.kb-event-box-image { - vertical-align: middle; - float: left; - margin-right: 8px; -} -/* @end */ -/* @group Kegadmin Nav */ -#kb-admin-nav { - float: left; - width: 160px; - margin-right: 32px; - margin-bottom: 16px; - font-size: 0.9em; -} -.kb-admin-nav-tab { - width: 160px; - height: 32px; - line-height: 32px; - background-color: #eee; - margin-bottom: 2px; - padding-left: 4px; -} -.kb-admin-nav-tab-hover { - background-color: #aaa; - cursor: pointer; -} -.kb-admin-subnav { - font-size: 0.8em; -} -.kb-admin-subnav p { - margin: 0px; - margin-left: 10px; -} -.kb-admin-subnav-tap { - margin-bottom: 10px; -} -.setup-form .control-group { - margin-bottom: 32px; -} -.setup-form .control-group label.control-label { - font-size: 26px; - height: 200%; -} -.setup-form .control-group label.checkbox { - font-size: 26px; - height: 36px; -} -.setup-form .control-group .requiredField { - text-weight: bold; -} -.setup-form .control-group .asteriskField { - display: none; -} -.setup-form .help-block { - font-size: 15.6px; - color: #555555; -} - -/* BS3 pagination classes - needed by `django_boostrap_pagination` */ -.pagination{height:36px;margin:0;padding: 0;} -.pager,.pagination ul{margin-left:0;*zoom:1} -.pagination ul{padding:0;display:inline-block;*display:inline;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)} -.pagination li{display:inline} -.pagination a{float:left;padding:0 12px;line-height:30px;text-decoration:none;border:1px solid #ddd;border-left-width:0} -.pagination .active a,.pagination a:hover{background-color:#f5f5f5;color:#94999E} -.pagination .active a{color:#94999E;cursor:default} -.pagination .disabled a,.pagination .disabled a:hover,.pagination .disabled span{color:#94999E;background-color:transparent;cursor:default} -.pagination li:first-child a,.pagination li:first-child span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px} -.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0} -.pagination-centered{text-align:center} -.pagination-right{text-align:right} -.pager{margin-bottom:18px;text-align:center} -.pager:after,.pager:before{display:table;content:""} -.pager li{display:inline} -.pager a{display:inline-block;padding:5px 12px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px} -.pager a:hover{text-decoration:none;background-color:#f5f5f5} -.pager .next a{float:right} -.pager .previous a{float:left} -.pager .disabled a,.pager .disabled a:hover{color:#999;background-color:#fff;cursor:default} -.pagination .prev.disabled span{float:left;padding:0 12px;line-height:30px;text-decoration:none;border:1px solid #ddd;border-left-width:1} -.pagination .next.disabled span{float:left;padding:0 12px;line-height:30px;text-decoration:none;border:1px solid #ddd;border-left-width:0} -.pagination li.active, .pagination li.disabled { - float:left;padding:0 12px;line-height:30px;text-decoration:none;border:1px solid #ddd;border-left-width:0 -} -.pagination li.active { - background: #364E63; - color: #fff; -} -.pagination li:first-child { - border-left-width: 1px; -} -/* @end */ -/* vim sw=2 ts=2 noet */ diff --git a/pykeg/web/static/css/kegweb.less b/pykeg/web/static/css/kegweb.less deleted file mode 100644 index f5f16e071..000000000 --- a/pykeg/web/static/css/kegweb.less +++ /dev/null @@ -1,346 +0,0 @@ -/*! - * Kegweb main css. - */ - -@import "../bootstrap/less/bootstrap.less"; -@import "bootstrap-variables.less"; - -body { - padding-top: 40px; /* 40px to make the container go all the way to the bottom of the topbar */ -} - -.sidebar { - // TODO(mikey): conditional on media min/max width. - max-width: 200px; -} - -.container-fluid > footer p { - text-align: center; /* center align it with the container */ -} - -[class^="icon-"] { - background-image: url("/static/bootstrap/img/glyphicons-halflings.png"); -} -[class*="icon-white"] { - background-image: url("/static/bootstrap/img/glyphicons-halflings-white.png"); -} - -.page-header { - background-color: #f5f5f5; - padding: 20px 20px 10px; - margin: 0 -20px; -} - -// Override container.content. -// Add white background. -div#content { - background-color: #fff; - padding: 20px; - margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */ - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15); - -moz-box-shadow: 0 1px 2px rgba(0,0,0,.15); - box-shadow: 0 1px 2px rgba(0,0,0,.15); -} - -.helptext { - .help-block(); -} - -.fill-parent-width { - width: 100%; -} - -.center-block { - display: block; - margin: 0 auto; - text-align: center; -} - -.badge-list { - .clearfix(); - margin-left: 0; - list-style: none; - - > li { - float: left; - width: 2 * @gridColumnWidth; - background-color: @gray; - color: @grayLighter; - .border-radius(6px); - border: 1px solid @grayLighter; - text-align: center; - padding: 4px; - margin-right: 4px; - - a { - color: @grayLighter; - } - h2 { - color: @grayLighter; - text-shadow: @black 2px 2px 2px; - } - small { - color: @grayLight; - text-transform: uppercase; - text-weight: bold; - } - } - - .badge-small { - h2 { - margin: 0px; - text-shadow: none; - font-size: @baseFontSize * 1.75; - line-height: @baseLineHeight * 1.75; - } - } -} - -.avatar-list { - .clearfix(); - margin-left: 0; - margin-bottom: 0; - list-style: none; - - > li { - margin: 0; - float: left; - padding: 1px; - } -} - -.tap-snapshot-image { - width: 2 * @gridColumnWidth; - height: auto; - padding: 4px; -} - -.right-col { - background-color: @grayLighter; -} - -.badge-header { - font-size: @baseFontSize + 2px; - color: @grayDark; - font-weight: bold; - padding: 2px; - .border-radius(6px 6px 0px 0px); - - text-align: center; - border-bottom: 1px solid #eee; - font: bold; -} - -.kb-tapbox { - .clearfix(); - border-bottom: 1px solid @grayLighter; - margin-bottom: 16px; -} - -.session-snapshot { - border-bottom: 1px solid @grayLighter; - padding: 8px; -} - -.clickable { - cursor: pointer; -} -.clickable:hover { - background-color: @grayLighter; -} - -.kb-keg-description { - color: @grayDark; - margin-bottom: 8px; -} - -.smalltext { - font-size: 11px; - color: @grayLight; - font-family: @headingsFontFamily; - font-weight: @headingsFontWeight; -} - -.arrow_box { - position: relative; - background: #ebebeb; - border: 3px solid #9fabc2; - .border-radius(3px); - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; -} - -.arrow_box:after, .arrow_box:before { - right: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; -} - -.arrow_box:after { - border-right-color: #ebebeb; - border-width: 20px; - top: 50%; - margin-top: -20px; -} - -.arrow_box:before { - border-right-color: #9fabc2; - border-width: 24px; - top: 50%; - margin-top: -24px; -} - -/* @end */ - -/* @group Charts */ - -.kb-chartbox-error { - color: #ccc; - .center-block(); -} - -/* @group Events */ - -.kb-event-box-info { - font-size: 0.7em; - color: #999; -} - -.kb-event-box-info a:link { - color: #999; - text-decoration: underline; - font-weight: normal; -} - -.kb-event-box-info a:visited { - color: #999; - text-decoration: underline; - font-weight: normal; -} - -.kb-event-box-dateline { - font-size: 0.7em; - color: #ccc; - margin-top: 4px; - font-style: italic; -} - -.kb-event-box { - width: 300px; - margin-bottom: 8px; - padding: 8px; - border: 1px solid #eee; - display: inline-block; - .border-radius(5px); - .clearfix(); -} - -.kb-event-box-session-start { - width: 300px; - text-align: center; - margin-bottom: 32px; - font-size: 0.7em; - background-color: #ddd; - color: #777; - padding: 5px; -} - -.kb-event-box { - width: 300px; - margin-bottom: 8px; - padding: 8px; - border: 1px solid #eee; - vertical-align: middle; - .border-radius(5px); -} - -.kb-event-box-details { - vertical-align: middle; -} - -.kb-event-box-image { - vertical-align: middle; - float: left; - margin-right: 8px; -} - -.kb-drink-box-details-headline { - -} - -/* @end */ - -/* @group Kegadmin Nav */ - -#kb-admin-nav { - float: left; - width: 160px; - margin-right: 32px; - margin-bottom: 16px; - font-size: 0.9em; -} - -.kb-admin-nav-tab { - width: 160px; - height: 32px; - line-height: 32px; - background-color: #eee; - margin-bottom: 2px; - padding-left: 4px; -} - -.kb-admin-nav-tab-hover { - background-color: #aaa; - cursor: pointer; -} - -.kb-admin-subnav { - font-size: 0.8em; -} - -.kb-admin-subnav p { - margin: 0px; - margin-left: 10px; -} - -.kb-admin-subnav-tap { - margin-bottom: 10px; -} - -.setup-form { - .control-group { - margin-bottom: 32px; - - label.control-label { - font-size: @baseFontSize * 2.0; - height:200%; - } - label.checkbox { - font-size: @baseFontSize * 2.0; - height: @baseLineHeight * 2.0; - } - .requiredField { - text-weight: bold; - } - .asteriskField { - display: none; - } - } - .help-block { - font-size: @baseFontSize * 1.2; - color: @gray; - } -} - -/* @end */ - -/* vim sw=2 ts=2 noet */ diff --git a/pykeg/web/static/fancybox/blank.gif b/pykeg/web/static/fancybox/blank.gif deleted file mode 100644 index 35d42e808..000000000 Binary files a/pykeg/web/static/fancybox/blank.gif and /dev/null differ diff --git a/pykeg/web/static/fancybox/fancybox_loading.gif b/pykeg/web/static/fancybox/fancybox_loading.gif deleted file mode 100644 index 01586176d..000000000 Binary files a/pykeg/web/static/fancybox/fancybox_loading.gif and /dev/null differ diff --git a/pykeg/web/static/fancybox/fancybox_overlay.png b/pykeg/web/static/fancybox/fancybox_overlay.png deleted file mode 100644 index a4391396a..000000000 Binary files a/pykeg/web/static/fancybox/fancybox_overlay.png and /dev/null differ diff --git a/pykeg/web/static/fancybox/fancybox_sprite.png b/pykeg/web/static/fancybox/fancybox_sprite.png deleted file mode 100644 index fd8d5ca56..000000000 Binary files a/pykeg/web/static/fancybox/fancybox_sprite.png and /dev/null differ diff --git a/pykeg/web/static/fancybox/helpers/fancybox_buttons.png b/pykeg/web/static/fancybox/helpers/fancybox_buttons.png deleted file mode 100644 index 078720727..000000000 Binary files a/pykeg/web/static/fancybox/helpers/fancybox_buttons.png and /dev/null differ diff --git a/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.css b/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.css deleted file mode 100644 index 9453b464d..000000000 --- a/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.css +++ /dev/null @@ -1,96 +0,0 @@ -#fancybox-buttons { - position: fixed; - left: 0; - width: 100%; - z-index: 8050; -} - -#fancybox-buttons.top { - top: 10px; -} - -#fancybox-buttons.bottom { - bottom: 10px; -} - -#fancybox-buttons ul { - display: block; - width: 166px; - height: 30px; - margin: 0 auto; - padding: 0; - list-style: none; - border: 1px solid #111; - border-radius: 3px; - -webkit-box-shadow: inset 0 0 0 1px rgba(255,255,255,.05); - -moz-box-shadow: inset 0 0 0 1px rgba(255,255,255,.05); - box-shadow: inset 0 0 0 1px rgba(255,255,255,.05); - background: rgb(50,50,50); - background: -moz-linear-gradient(top, rgb(68,68,68) 0%, rgb(52,52,52) 50%, rgb(41,41,41) 50%, rgb(51,51,51) 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(68,68,68)), color-stop(50%,rgb(52,52,52)), color-stop(50%,rgb(41,41,41)), color-stop(100%,rgb(51,51,51))); - background: -webkit-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%); - background: -o-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%); - background: -ms-linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%); - background: linear-gradient(top, rgb(68,68,68) 0%,rgb(52,52,52) 50%,rgb(41,41,41) 50%,rgb(51,51,51) 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#444444', endColorstr='#222222',GradientType=0 ); -} - -#fancybox-buttons ul li { - float: left; - margin: 0; - padding: 0; -} - -#fancybox-buttons a { - display: block; - width: 30px; - height: 30px; - text-indent: -9999px; - background-image: url('fancybox_buttons.png'); - background-repeat: no-repeat; - outline: none; - opacity: 0.8; -} - -#fancybox-buttons a:hover { - opacity: 1; -} - -#fancybox-buttons a.btnPrev { - background-position: 5px 0; -} - -#fancybox-buttons a.btnNext { - background-position: -33px 0; - border-right: 1px solid #3e3e3e; -} - -#fancybox-buttons a.btnPlay { - background-position: 0 -30px; -} - -#fancybox-buttons a.btnPlayOn { - background-position: -30px -30px; -} - -#fancybox-buttons a.btnToggle { - background-position: 3px -60px; - border-left: 1px solid #111; - border-right: 1px solid #3e3e3e; - width: 35px -} - -#fancybox-buttons a.btnToggleOn { - background-position: -27px -60px; -} - -#fancybox-buttons a.btnClose { - border-left: 1px solid #111; - width: 35px; - background-position: -56px 0px; -} - -#fancybox-buttons a.btnDisabled { - opacity : 0.4; - cursor: default; -} \ No newline at end of file diff --git a/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.js b/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.js deleted file mode 100644 index 50baeca42..000000000 --- a/pykeg/web/static/fancybox/helpers/jquery.fancybox-buttons.js +++ /dev/null @@ -1,121 +0,0 @@ - /*! - * Buttons helper for fancyBox - * version: 1.0.5 (Mon, 15 Oct 2012) - * @requires fancyBox v2.0 or later - * - * Usage: - * $(".fancybox").fancybox({ - * helpers : { - * buttons: { - * position : 'top' - * } - * } - * }); - * - */ -(function ($) { - //Shortcut for fancyBox object - var F = $.fancybox; - - //Add helper object - F.helpers.buttons = { - defaults : { - skipSingle : false, // disables if gallery contains single image - position : 'top', // 'top' or 'bottom' - tpl : '<div id="fancybox-buttons"><ul><li><a class="btnPrev" title="Previous" href="javascript:;"></a></li><li><a class="btnPlay" title="Start slideshow" href="javascript:;"></a></li><li><a class="btnNext" title="Next" href="javascript:;"></a></li><li><a class="btnToggle" title="Toggle size" href="javascript:;"></a></li><li><a class="btnClose" title="Close" href="javascript:jQuery.fancybox.close();"></a></li></ul></div>' - }, - - list : null, - buttons: null, - - beforeLoad: function (opts, obj) { - //Remove self if gallery do not have at least two items - - if (opts.skipSingle && obj.group.length < 2) { - obj.helpers.buttons = false; - obj.closeBtn = true; - - return; - } - - //Increase top margin to give space for buttons - obj.margin[ opts.position === 'bottom' ? 2 : 0 ] += 30; - }, - - onPlayStart: function () { - if (this.buttons) { - this.buttons.play.attr('title', 'Pause slideshow').addClass('btnPlayOn'); - } - }, - - onPlayEnd: function () { - if (this.buttons) { - this.buttons.play.attr('title', 'Start slideshow').removeClass('btnPlayOn'); - } - }, - - afterShow: function (opts, obj) { - var buttons = this.buttons; - - if (!buttons) { - this.list = $(opts.tpl).addClass(opts.position).appendTo('body'); - - buttons = { - prev : this.list.find('.btnPrev').click( F.prev ), - next : this.list.find('.btnNext').click( F.next ), - play : this.list.find('.btnPlay').click( F.play ), - toggle : this.list.find('.btnToggle').click( F.toggle ) - } - } - - //Prev - if (obj.index > 0 || obj.loop) { - buttons.prev.removeClass('btnDisabled'); - } else { - buttons.prev.addClass('btnDisabled'); - } - - //Next / Play - if (obj.loop || obj.index < obj.group.length - 1) { - buttons.next.removeClass('btnDisabled'); - buttons.play.removeClass('btnDisabled'); - - } else { - buttons.next.addClass('btnDisabled'); - buttons.play.addClass('btnDisabled'); - } - - this.buttons = buttons; - - this.onUpdate(opts, obj); - }, - - onUpdate: function (opts, obj) { - var toggle; - - if (!this.buttons) { - return; - } - - toggle = this.buttons.toggle.removeClass('btnDisabled btnToggleOn'); - - //Size toggle button - if (obj.canShrink) { - toggle.addClass('btnToggleOn'); - - } else if (!obj.canExpand) { - toggle.addClass('btnDisabled'); - } - }, - - beforeClose: function () { - if (this.list) { - this.list.remove(); - } - - this.list = null; - this.buttons = null; - } - }; - -}(jQuery)); \ No newline at end of file diff --git a/pykeg/web/static/fancybox/helpers/jquery.fancybox-media.js b/pykeg/web/static/fancybox/helpers/jquery.fancybox-media.js deleted file mode 100644 index 4b5e78356..000000000 --- a/pykeg/web/static/fancybox/helpers/jquery.fancybox-media.js +++ /dev/null @@ -1,196 +0,0 @@ -/*! - * Media helper for fancyBox - * version: 1.0.5 (Tue, 23 Oct 2012) - * @requires fancyBox v2.0 or later - * - * Usage: - * $(".fancybox").fancybox({ - * helpers : { - * media: true - * } - * }); - * - * Set custom URL parameters: - * $(".fancybox").fancybox({ - * helpers : { - * media: { - * youtube : { - * params : { - * autoplay : 0 - * } - * } - * } - * } - * }); - * - * Or: - * $(".fancybox").fancybox({, - * helpers : { - * media: true - * }, - * youtube : { - * autoplay: 0 - * } - * }); - * - * Supports: - * - * Youtube - * http://www.youtube.com/watch?v=opj24KnzrWo - * http://www.youtube.com/embed/opj24KnzrWo - * http://youtu.be/opj24KnzrWo - * Vimeo - * http://vimeo.com/40648169 - * http://vimeo.com/channels/staffpicks/38843628 - * http://vimeo.com/groups/surrealism/videos/36516384 - * http://player.vimeo.com/video/45074303 - * Metacafe - * http://www.metacafe.com/watch/7635964/dr_seuss_the_lorax_movie_trailer/ - * http://www.metacafe.com/watch/7635964/ - * Dailymotion - * http://www.dailymotion.com/video/xoytqh_dr-seuss-the-lorax-premiere_people - * Twitvid - * http://twitvid.com/QY7MD - * Twitpic - * http://twitpic.com/7p93st - * Instagram - * http://instagr.am/p/IejkuUGxQn/ - * http://instagram.com/p/IejkuUGxQn/ - * Google maps - * http://maps.google.com/maps?q=Eiffel+Tower,+Avenue+Gustave+Eiffel,+Paris,+France&t=h&z=17 - * http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 - * http://maps.google.com/?ll=48.859463,2.292626&spn=0.000965,0.002642&t=m&z=19&layer=c&cbll=48.859524,2.292532&panoid=YJ0lq28OOy3VT2IqIuVY0g&cbp=12,151.58,,0,-15.56 - */ -(function ($) { - "use strict"; - - //Shortcut for fancyBox object - var F = $.fancybox, - format = function( url, rez, params ) { - params = params || ''; - - if ( $.type( params ) === "object" ) { - params = $.param(params, true); - } - - $.each(rez, function(key, value) { - url = url.replace( '$' + key, value || '' ); - }); - - if (params.length) { - url += ( url.indexOf('?') > 0 ? '&' : '?' ) + params; - } - - return url; - }; - - //Add helper object - F.helpers.media = { - defaults : { - youtube : { - matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i, - params : { - autoplay : 1, - autohide : 1, - fs : 1, - rel : 0, - hd : 1, - wmode : 'opaque', - enablejsapi : 1 - }, - type : 'iframe', - url : '//www.youtube.com/embed/$3' - }, - vimeo : { - matcher : /(?:vimeo(?:pro)?.com)\/(?:[^\d]+)?(\d+)(?:.*)/, - params : { - autoplay : 1, - hd : 1, - show_title : 1, - show_byline : 1, - show_portrait : 0, - fullscreen : 1 - }, - type : 'iframe', - url : '//player.vimeo.com/video/$1' - }, - metacafe : { - matcher : /metacafe.com\/(?:watch|fplayer)\/([\w\-]{1,10})/, - params : { - autoPlay : 'yes' - }, - type : 'swf', - url : function( rez, params, obj ) { - obj.swf.flashVars = 'playerVars=' + $.param( params, true ); - - return '//www.metacafe.com/fplayer/' + rez[1] + '/.swf'; - } - }, - dailymotion : { - matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, - params : { - additionalInfos : 0, - autoStart : 1 - }, - type : 'swf', - url : '//www.dailymotion.com/swf/video/$1' - }, - twitvid : { - matcher : /twitvid\.com\/([a-zA-Z0-9_\-\?\=]+)/i, - params : { - autoplay : 0 - }, - type : 'iframe', - url : '//www.twitvid.com/embed.php?guid=$1' - }, - twitpic : { - matcher : /twitpic\.com\/(?!(?:place|photos|events)\/)([a-zA-Z0-9\?\=\-]+)/i, - type : 'image', - url : '//twitpic.com/show/full/$1/' - }, - instagram : { - matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, - type : 'image', - url : '//$1/p/$2/media/' - }, - google_maps : { - matcher : /maps\.google\.([a-z]{2,3}(\.[a-z]{2})?)\/(\?ll=|maps\?)(.*)/i, - type : 'iframe', - url : function( rez ) { - return '//maps.google.' + rez[1] + '/' + rez[3] + '' + rez[4] + '&output=' + (rez[4].indexOf('layer=c') > 0 ? 'svembed' : 'embed'); - } - } - }, - - beforeLoad : function(opts, obj) { - var url = obj.href || '', - type = false, - what, - item, - rez, - params; - - for (what in opts) { - item = opts[ what ]; - rez = url.match( item.matcher ); - - if (rez) { - type = item.type; - params = $.extend(true, {}, item.params, obj[ what ] || ($.isPlainObject(opts[ what ]) ? opts[ what ].params : null)); - - url = $.type( item.url ) === "function" ? item.url.call( this, rez, params, obj ) : format( item.url, rez, params ); - - break; - } - } - - if (type) { - obj.href = url; - obj.type = type; - - obj.autoHeight = false; - } - } - }; - -}(jQuery)); \ No newline at end of file diff --git a/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.css b/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.css deleted file mode 100644 index e40ae820b..000000000 --- a/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.css +++ /dev/null @@ -1,54 +0,0 @@ -#fancybox-thumbs { - position: fixed; - left: 0; - width: 100%; - overflow: hidden; - z-index: 8050; -} - -#fancybox-thumbs.bottom { - bottom: 2px; -} - -#fancybox-thumbs.top { - top: 2px; -} - -#fancybox-thumbs ul { - position: relative; - list-style: none; - margin: 0; - padding: 0; -} - -#fancybox-thumbs ul li { - float: left; - padding: 1px; - opacity: 0.5; -} - -#fancybox-thumbs ul li.active { - opacity: 0.75; - padding: 0; - border: 1px solid #fff; -} - -#fancybox-thumbs ul li:hover { - opacity: 1; -} - -#fancybox-thumbs ul li a { - display: block; - position: relative; - overflow: hidden; - border: 1px solid #222; - background: #111; - outline: none; -} - -#fancybox-thumbs ul li img { - display: block; - position: relative; - border: 0; - padding: 0; -} \ No newline at end of file diff --git a/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.js b/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.js deleted file mode 100644 index 5db3d4ac2..000000000 --- a/pykeg/web/static/fancybox/helpers/jquery.fancybox-thumbs.js +++ /dev/null @@ -1,162 +0,0 @@ - /*! - * Thumbnail helper for fancyBox - * version: 1.0.7 (Mon, 01 Oct 2012) - * @requires fancyBox v2.0 or later - * - * Usage: - * $(".fancybox").fancybox({ - * helpers : { - * thumbs: { - * width : 50, - * height : 50 - * } - * } - * }); - * - */ -(function ($) { - //Shortcut for fancyBox object - var F = $.fancybox; - - //Add helper object - F.helpers.thumbs = { - defaults : { - width : 50, // thumbnail width - height : 50, // thumbnail height - position : 'bottom', // 'top' or 'bottom' - source : function ( item ) { // function to obtain the URL of the thumbnail image - var href; - - if (item.element) { - href = $(item.element).find('img').attr('src'); - } - - if (!href && item.type === 'image' && item.href) { - href = item.href; - } - - return href; - } - }, - - wrap : null, - list : null, - width : 0, - - init: function (opts, obj) { - var that = this, - list, - thumbWidth = opts.width, - thumbHeight = opts.height, - thumbSource = opts.source; - - //Build list structure - list = ''; - - for (var n = 0; n < obj.group.length; n++) { - list += '<li><a style="width:' + thumbWidth + 'px;height:' + thumbHeight + 'px;" href="javascript:jQuery.fancybox.jumpto(' + n + ');"></a></li>'; - } - - this.wrap = $('<div id="fancybox-thumbs"></div>').addClass(opts.position).appendTo('body'); - this.list = $('<ul>' + list + '</ul>').appendTo(this.wrap); - - //Load each thumbnail - $.each(obj.group, function (i) { - var href = thumbSource( obj.group[ i ] ); - - if (!href) { - return; - } - - $("<img />").load(function () { - var width = this.width, - height = this.height, - widthRatio, heightRatio, parent; - - if (!that.list || !width || !height) { - return; - } - - //Calculate thumbnail width/height and center it - widthRatio = width / thumbWidth; - heightRatio = height / thumbHeight; - - parent = that.list.children().eq(i).find('a'); - - if (widthRatio >= 1 && heightRatio >= 1) { - if (widthRatio > heightRatio) { - width = Math.floor(width / heightRatio); - height = thumbHeight; - - } else { - width = thumbWidth; - height = Math.floor(height / widthRatio); - } - } - - $(this).css({ - width : width, - height : height, - top : Math.floor(thumbHeight / 2 - height / 2), - left : Math.floor(thumbWidth / 2 - width / 2) - }); - - parent.width(thumbWidth).height(thumbHeight); - - $(this).hide().appendTo(parent).fadeIn(300); - - }).attr('src', href); - }); - - //Set initial width - this.width = this.list.children().eq(0).outerWidth(true); - - this.list.width(this.width * (obj.group.length + 1)).css('left', Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5))); - }, - - beforeLoad: function (opts, obj) { - //Remove self if gallery do not have at least two items - if (obj.group.length < 2) { - obj.helpers.thumbs = false; - - return; - } - - //Increase bottom margin to give space for thumbs - obj.margin[ opts.position === 'top' ? 0 : 2 ] += ((opts.height) + 15); - }, - - afterShow: function (opts, obj) { - //Check if exists and create or update list - if (this.list) { - this.onUpdate(opts, obj); - - } else { - this.init(opts, obj); - } - - //Set active element - this.list.children().removeClass('active').eq(obj.index).addClass('active'); - }, - - //Center list - onUpdate: function (opts, obj) { - if (this.list) { - this.list.stop(true).animate({ - 'left': Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5)) - }, 150); - } - }, - - beforeClose: function () { - if (this.wrap) { - this.wrap.remove(); - } - - this.wrap = null; - this.list = null; - this.width = 0; - } - } - -}(jQuery)); \ No newline at end of file diff --git a/pykeg/web/static/fancybox/jquery.fancybox.css b/pykeg/web/static/fancybox/jquery.fancybox.css deleted file mode 100644 index bd3289b2d..000000000 --- a/pykeg/web/static/fancybox/jquery.fancybox.css +++ /dev/null @@ -1,249 +0,0 @@ -/*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */ -.fancybox-wrap, -.fancybox-skin, -.fancybox-outer, -.fancybox-inner, -.fancybox-image, -.fancybox-wrap iframe, -.fancybox-wrap object, -.fancybox-nav, -.fancybox-nav span, -.fancybox-tmp -{ - padding: 0; - margin: 0; - border: 0; - outline: none; - vertical-align: top; -} - -.fancybox-wrap { - position: absolute; - top: 0; - left: 0; - z-index: 8020; -} - -.fancybox-skin { - position: relative; - background: #f9f9f9; - color: #444; - text-shadow: none; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.fancybox-opened { - z-index: 8030; -} - -.fancybox-opened .fancybox-skin { - -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); -} - -.fancybox-outer, .fancybox-inner { - position: relative; -} - -.fancybox-inner { - overflow: hidden; -} - -.fancybox-type-iframe .fancybox-inner { - -webkit-overflow-scrolling: touch; -} - -.fancybox-error { - color: #444; - font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; - margin: 0; - padding: 15px; - white-space: nowrap; -} - -.fancybox-image, .fancybox-iframe { - display: block; - width: 100%; - height: 100%; -} - -.fancybox-image { - max-width: 100%; - max-height: 100%; -} - -#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { - background-image: url('fancybox_sprite.png'); -} - -#fancybox-loading { - position: fixed; - top: 50%; - left: 50%; - margin-top: -22px; - margin-left: -22px; - background-position: 0 -108px; - opacity: 0.8; - cursor: pointer; - z-index: 8060; -} - -#fancybox-loading div { - width: 44px; - height: 44px; - background: url('fancybox_loading.gif') center center no-repeat; -} - -.fancybox-close { - position: absolute; - top: -18px; - right: -18px; - width: 36px; - height: 36px; - cursor: pointer; - z-index: 8040; -} - -.fancybox-nav { - position: absolute; - top: 0; - width: 40%; - height: 100%; - cursor: pointer; - text-decoration: none; - background: transparent url('blank.gif'); /* helps IE */ - -webkit-tap-highlight-color: rgba(0,0,0,0); - z-index: 8040; -} - -.fancybox-prev { - left: 0; -} - -.fancybox-next { - right: 0; -} - -.fancybox-nav span { - position: absolute; - top: 50%; - width: 36px; - height: 34px; - margin-top: -18px; - cursor: pointer; - z-index: 8040; - visibility: hidden; -} - -.fancybox-prev span { - left: 10px; - background-position: 0 -36px; -} - -.fancybox-next span { - right: 10px; - background-position: 0 -72px; -} - -.fancybox-nav:hover span { - visibility: visible; -} - -.fancybox-tmp { - position: absolute; - top: -99999px; - left: -99999px; - visibility: hidden; - max-width: 99999px; - max-height: 99999px; - overflow: visible !important; -} - -/* Overlay helper */ - -.fancybox-lock { - overflow: hidden; -} - -.fancybox-overlay { - position: absolute; - top: 0; - left: 0; - overflow: hidden; - display: none; - z-index: 8010; - background: url('fancybox_overlay.png'); -} - -.fancybox-overlay-fixed { - position: fixed; - bottom: 0; - right: 0; -} - -.fancybox-lock .fancybox-overlay { - overflow: auto; - overflow-y: scroll; -} - -/* Title helper */ - -.fancybox-title { - visibility: hidden; - font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; - position: relative; - text-shadow: none; - z-index: 8050; -} - -.fancybox-opened .fancybox-title { - visibility: visible; -} - -.fancybox-title-float-wrap { - position: absolute; - bottom: 0; - right: 50%; - margin-bottom: -35px; - z-index: 8050; - text-align: center; -} - -.fancybox-title-float-wrap .child { - display: inline-block; - margin-right: -100%; - padding: 2px 20px; - background: transparent; /* Fallback for web browsers that doesn't support RGBa */ - background: rgba(0, 0, 0, 0.8); - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; - text-shadow: 0 1px 2px #222; - color: #FFF; - font-weight: bold; - line-height: 24px; - white-space: nowrap; -} - -.fancybox-title-outside-wrap { - position: relative; - margin-top: 10px; - color: #fff; -} - -.fancybox-title-inside-wrap { - padding-top: 10px; -} - -.fancybox-title-over-wrap { - position: absolute; - bottom: 0; - left: 0; - color: #fff; - padding: 10px; - background: #000; - background: rgba(0, 0, 0, .8); -} \ No newline at end of file diff --git a/pykeg/web/static/fancybox/jquery.fancybox.js b/pykeg/web/static/fancybox/jquery.fancybox.js deleted file mode 100644 index bd153b013..000000000 --- a/pykeg/web/static/fancybox/jquery.fancybox.js +++ /dev/null @@ -1,1983 +0,0 @@ -/*! - * fancyBox - jQuery Plugin - * version: 2.1.4 (Thu, 10 Jan 2013) - * @requires jQuery v1.6 or later - * - * Examples at http://fancyapps.com/fancybox/ - * License: www.fancyapps.com/fancybox/#license - * - * Copyright 2012 Janis Skarnelis - janis@fancyapps.com - * - */ - -(function (window, document, $, undefined) { - "use strict"; - - var W = $(window), - D = $(document), - F = $.fancybox = function () { - F.open.apply( this, arguments ); - }, - IE = navigator.userAgent.match(/msie/), - didUpdate = null, - isTouch = document.createTouch !== undefined, - - isQuery = function(obj) { - return obj && obj.hasOwnProperty && obj instanceof $; - }, - isString = function(str) { - return str && $.type(str) === "string"; - }, - isPercentage = function(str) { - return isString(str) && str.indexOf('%') > 0; - }, - isScrollable = function(el) { - return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight))); - }, - getScalar = function(orig, dim) { - var value = parseInt(orig, 10) || 0; - - if (dim && isPercentage(orig)) { - value = F.getViewport()[ dim ] / 100 * value; - } - - return Math.ceil(value); - }, - getValue = function(value, dim) { - return getScalar(value, dim) + 'px'; - }; - - $.extend(F, { - // The current version of fancyBox - version: '2.1.4', - - defaults: { - padding : 15, - margin : 20, - - width : 800, - height : 600, - minWidth : 100, - minHeight : 100, - maxWidth : 9999, - maxHeight : 9999, - - autoSize : true, - autoHeight : false, - autoWidth : false, - - autoResize : true, - autoCenter : !isTouch, - fitToView : true, - aspectRatio : false, - topRatio : 0.5, - leftRatio : 0.5, - - scrolling : 'auto', // 'auto', 'yes' or 'no' - wrapCSS : '', - - arrows : true, - closeBtn : true, - closeClick : false, - nextClick : false, - mouseWheel : true, - autoPlay : false, - playSpeed : 3000, - preload : 3, - modal : false, - loop : true, - - ajax : { - dataType : 'html', - headers : { 'X-fancyBox': true } - }, - iframe : { - scrolling : 'auto', - preload : true - }, - swf : { - wmode: 'transparent', - allowfullscreen : 'true', - allowscriptaccess : 'always' - }, - - keys : { - next : { - 13 : 'left', // enter - 34 : 'up', // page down - 39 : 'left', // right arrow - 40 : 'up' // down arrow - }, - prev : { - 8 : 'right', // backspace - 33 : 'down', // page up - 37 : 'right', // left arrow - 38 : 'down' // up arrow - }, - close : [27], // escape key - play : [32], // space - start/stop slideshow - toggle : [70] // letter "f" - toggle fullscreen - }, - - direction : { - next : 'left', - prev : 'right' - }, - - scrollOutside : true, - - // Override some properties - index : 0, - type : null, - href : null, - content : null, - title : null, - - // HTML templates - tpl: { - wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>', - image : '<img class="fancybox-image" src="{href}" alt="" />', - iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + (IE ? ' allowtransparency="true"' : '') + '></iframe>', - error : '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>', - closeBtn : '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>', - next : '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>', - prev : '<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>' - }, - - // Properties for each animation type - // Opening fancyBox - openEffect : 'fade', // 'elastic', 'fade' or 'none' - openSpeed : 250, - openEasing : 'swing', - openOpacity : true, - openMethod : 'zoomIn', - - // Closing fancyBox - closeEffect : 'fade', // 'elastic', 'fade' or 'none' - closeSpeed : 250, - closeEasing : 'swing', - closeOpacity : true, - closeMethod : 'zoomOut', - - // Changing next gallery item - nextEffect : 'elastic', // 'elastic', 'fade' or 'none' - nextSpeed : 250, - nextEasing : 'swing', - nextMethod : 'changeIn', - - // Changing previous gallery item - prevEffect : 'elastic', // 'elastic', 'fade' or 'none' - prevSpeed : 250, - prevEasing : 'swing', - prevMethod : 'changeOut', - - // Enable default helpers - helpers : { - overlay : true, - title : true - }, - - // Callbacks - onCancel : $.noop, // If canceling - beforeLoad : $.noop, // Before loading - afterLoad : $.noop, // After loading - beforeShow : $.noop, // Before changing in current item - afterShow : $.noop, // After opening - beforeChange : $.noop, // Before changing gallery item - beforeClose : $.noop, // Before closing - afterClose : $.noop // After closing - }, - - //Current state - group : {}, // Selected group - opts : {}, // Group options - previous : null, // Previous element - coming : null, // Element being loaded - current : null, // Currently loaded element - isActive : false, // Is activated - isOpen : false, // Is currently open - isOpened : false, // Have been fully opened at least once - - wrap : null, - skin : null, - outer : null, - inner : null, - - player : { - timer : null, - isActive : false - }, - - // Loaders - ajaxLoad : null, - imgPreload : null, - - // Some collections - transitions : {}, - helpers : {}, - - /* - * Static methods - */ - - open: function (group, opts) { - if (!group) { - return; - } - - if (!$.isPlainObject(opts)) { - opts = {}; - } - - // Close if already active - if (false === F.close(true)) { - return; - } - - // Normalize group - if (!$.isArray(group)) { - group = isQuery(group) ? $(group).get() : [group]; - } - - // Recheck if the type of each element is `object` and set content type (image, ajax, etc) - $.each(group, function(i, element) { - var obj = {}, - href, - title, - content, - type, - rez, - hrefParts, - selector; - - if ($.type(element) === "object") { - // Check if is DOM element - if (element.nodeType) { - element = $(element); - } - - if (isQuery(element)) { - obj = { - href : element.data('fancybox-href') || element.attr('href'), - title : element.data('fancybox-title') || element.attr('title'), - isDom : true, - element : element - }; - - if ($.metadata) { - $.extend(true, obj, element.metadata()); - } - - } else { - obj = element; - } - } - - href = opts.href || obj.href || (isString(element) ? element : null); - title = opts.title !== undefined ? opts.title : obj.title || ''; - - content = opts.content || obj.content; - type = content ? 'html' : (opts.type || obj.type); - - if (!type && obj.isDom) { - type = element.data('fancybox-type'); - - if (!type) { - rez = element.prop('class').match(/fancybox\.(\w+)/); - type = rez ? rez[1] : null; - } - } - - if (isString(href)) { - // Try to guess the content type - if (!type) { - if (F.isImage(href)) { - type = 'image'; - - } else if (F.isSWF(href)) { - type = 'swf'; - - } else if (href.charAt(0) === '#') { - type = 'inline'; - - } else if (isString(element)) { - type = 'html'; - content = element; - } - } - - // Split url into two pieces with source url and content selector, e.g, - // "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id" - if (type === 'ajax') { - hrefParts = href.split(/\s+/, 2); - href = hrefParts.shift(); - selector = hrefParts.shift(); - } - } - - if (!content) { - if (type === 'inline') { - if (href) { - content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7 - - } else if (obj.isDom) { - content = element; - } - - } else if (type === 'html') { - content = href; - - } else if (!type && !href && obj.isDom) { - type = 'inline'; - content = element; - } - } - - $.extend(obj, { - href : href, - type : type, - content : content, - title : title, - selector : selector - }); - - group[ i ] = obj; - }); - - // Extend the defaults - F.opts = $.extend(true, {}, F.defaults, opts); - - // All options are merged recursive except keys - if (opts.keys !== undefined) { - F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false; - } - - F.group = group; - - return F._start(F.opts.index); - }, - - // Cancel image loading or abort ajax request - cancel: function () { - var coming = F.coming; - - if (!coming || false === F.trigger('onCancel')) { - return; - } - - F.hideLoading(); - - if (F.ajaxLoad) { - F.ajaxLoad.abort(); - } - - F.ajaxLoad = null; - - if (F.imgPreload) { - F.imgPreload.onload = F.imgPreload.onerror = null; - } - - if (coming.wrap) { - coming.wrap.stop(true, true).trigger('onReset').remove(); - } - - F.coming = null; - - // If the first item has been canceled, then clear everything - if (!F.current) { - F._afterZoomOut( coming ); - } - }, - - // Start closing animation if is open; remove immediately if opening/closing - close: function (event) { - F.cancel(); - - if (false === F.trigger('beforeClose')) { - return; - } - - F.unbindEvents(); - - if (!F.isActive) { - return; - } - - if (!F.isOpen || event === true) { - $('.fancybox-wrap').stop(true).trigger('onReset').remove(); - - F._afterZoomOut(); - - } else { - F.isOpen = F.isOpened = false; - F.isClosing = true; - - $('.fancybox-item, .fancybox-nav').remove(); - - F.wrap.stop(true, true).removeClass('fancybox-opened'); - - F.transitions[ F.current.closeMethod ](); - } - }, - - // Manage slideshow: - // $.fancybox.play(); - toggle slideshow - // $.fancybox.play( true ); - start - // $.fancybox.play( false ); - stop - play: function ( action ) { - var clear = function () { - clearTimeout(F.player.timer); - }, - set = function () { - clear(); - - if (F.current && F.player.isActive) { - F.player.timer = setTimeout(F.next, F.current.playSpeed); - } - }, - stop = function () { - clear(); - - $('body').unbind('.player'); - - F.player.isActive = false; - - F.trigger('onPlayEnd'); - }, - start = function () { - if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) { - F.player.isActive = true; - - $('body').bind({ - 'afterShow.player onUpdate.player' : set, - 'onCancel.player beforeClose.player' : stop, - 'beforeLoad.player' : clear - }); - - set(); - - F.trigger('onPlayStart'); - } - }; - - if (action === true || (!F.player.isActive && action !== false)) { - start(); - } else { - stop(); - } - }, - - // Navigate to next gallery item - next: function ( direction ) { - var current = F.current; - - if (current) { - if (!isString(direction)) { - direction = current.direction.next; - } - - F.jumpto(current.index + 1, direction, 'next'); - } - }, - - // Navigate to previous gallery item - prev: function ( direction ) { - var current = F.current; - - if (current) { - if (!isString(direction)) { - direction = current.direction.prev; - } - - F.jumpto(current.index - 1, direction, 'prev'); - } - }, - - // Navigate to gallery item by index - jumpto: function ( index, direction, router ) { - var current = F.current; - - if (!current) { - return; - } - - index = getScalar(index); - - F.direction = direction || current.direction[ (index >= current.index ? 'next' : 'prev') ]; - F.router = router || 'jumpto'; - - if (current.loop) { - if (index < 0) { - index = current.group.length + (index % current.group.length); - } - - index = index % current.group.length; - } - - if (current.group[ index ] !== undefined) { - F.cancel(); - - F._start(index); - } - }, - - // Center inside viewport and toggle position type to fixed or absolute if needed - reposition: function (e, onlyAbsolute) { - var current = F.current, - wrap = current ? current.wrap : null, - pos; - - if (wrap) { - pos = F._getPosition(onlyAbsolute); - - if (e && e.type === 'scroll') { - delete pos.position; - - wrap.stop(true, true).animate(pos, 200); - - } else { - wrap.css(pos); - - current.pos = $.extend({}, current.dim, pos); - } - } - }, - - update: function (e) { - var type = (e && e.type), - anyway = !type || type === 'orientationchange'; - - if (anyway) { - clearTimeout(didUpdate); - - didUpdate = null; - } - - if (!F.isOpen || didUpdate) { - return; - } - - didUpdate = setTimeout(function() { - var current = F.current; - - if (!current || F.isClosing) { - return; - } - - F.wrap.removeClass('fancybox-tmp'); - - if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) { - F._setDimension(); - } - - if (!(type === 'scroll' && current.canShrink)) { - F.reposition(e); - } - - F.trigger('onUpdate'); - - didUpdate = null; - - }, (anyway && !isTouch ? 0 : 300)); - }, - - // Shrink content to fit inside viewport or restore if resized - toggle: function ( action ) { - if (F.isOpen) { - F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView; - - // Help browser to restore document dimensions - if (isTouch) { - F.wrap.removeAttr('style').addClass('fancybox-tmp'); - - F.trigger('onUpdate'); - } - - F.update(); - } - }, - - hideLoading: function () { - D.unbind('.loading'); - - $('#fancybox-loading').remove(); - }, - - showLoading: function () { - var el, viewport; - - F.hideLoading(); - - el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body'); - - // If user will press the escape-button, the request will be canceled - D.bind('keydown.loading', function(e) { - if ((e.which || e.keyCode) === 27) { - e.preventDefault(); - - F.cancel(); - } - }); - - if (!F.defaults.fixed) { - viewport = F.getViewport(); - - el.css({ - position : 'absolute', - top : (viewport.h * 0.5) + viewport.y, - left : (viewport.w * 0.5) + viewport.x - }); - } - }, - - getViewport: function () { - var locked = (F.current && F.current.locked) || false, - rez = { - x: W.scrollLeft(), - y: W.scrollTop() - }; - - if (locked) { - rez.w = locked[0].clientWidth; - rez.h = locked[0].clientHeight; - - } else { - // See http://bugs.jquery.com/ticket/6724 - rez.w = isTouch && window.innerWidth ? window.innerWidth : W.width(); - rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height(); - } - - return rez; - }, - - // Unbind the keyboard / clicking actions - unbindEvents: function () { - if (F.wrap && isQuery(F.wrap)) { - F.wrap.unbind('.fb'); - } - - D.unbind('.fb'); - W.unbind('.fb'); - }, - - bindEvents: function () { - var current = F.current, - keys; - - if (!current) { - return; - } - - // Changing document height on iOS devices triggers a 'resize' event, - // that can change document height... repeating infinitely - W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); - - keys = current.keys; - - if (keys) { - D.bind('keydown.fb', function (e) { - var code = e.which || e.keyCode, - target = e.target || e.srcElement; - - // Skip esc key if loading, because showLoading will cancel preloading - if (code === 27 && F.coming) { - return false; - } - - // Ignore key combinations and key events within form elements - if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) { - $.each(keys, function(i, val) { - if (current.group.length > 1 && val[ code ] !== undefined) { - F[ i ]( val[ code ] ); - - e.preventDefault(); - return false; - } - - if ($.inArray(code, val) > -1) { - F[ i ] (); - - e.preventDefault(); - return false; - } - }); - } - }); - } - - if ($.fn.mousewheel && current.mouseWheel) { - F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) { - var target = e.target || null, - parent = $(target), - canScroll = false; - - while (parent.length) { - if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) { - break; - } - - canScroll = isScrollable( parent[0] ); - parent = $(parent).parent(); - } - - if (delta !== 0 && !canScroll) { - if (F.group.length > 1 && !current.canShrink) { - if (deltaY > 0 || deltaX > 0) { - F.prev( deltaY > 0 ? 'down' : 'left' ); - - } else if (deltaY < 0 || deltaX < 0) { - F.next( deltaY < 0 ? 'up' : 'right' ); - } - - e.preventDefault(); - } - } - }); - } - }, - - trigger: function (event, o) { - var ret, obj = o || F.coming || F.current; - - if (!obj) { - return; - } - - if ($.isFunction( obj[event] )) { - ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1)); - } - - if (ret === false) { - return false; - } - - if (obj.helpers) { - $.each(obj.helpers, function (helper, opts) { - if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) { - opts = $.extend(true, {}, F.helpers[helper].defaults, opts); - - F.helpers[helper][event](opts, obj); - } - }); - } - - $.event.trigger(event + '.fb'); - }, - - isImage: function (str) { - return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i); - }, - - isSWF: function (str) { - return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i); - }, - - _start: function (index) { - var coming = {}, - obj, - href, - type, - margin, - padding; - - index = getScalar( index ); - obj = F.group[ index ] || null; - - if (!obj) { - return false; - } - - coming = $.extend(true, {}, F.opts, obj); - - // Convert margin and padding properties to array - top, right, bottom, left - margin = coming.margin; - padding = coming.padding; - - if ($.type(margin) === 'number') { - coming.margin = [margin, margin, margin, margin]; - } - - if ($.type(padding) === 'number') { - coming.padding = [padding, padding, padding, padding]; - } - - // 'modal' propery is just a shortcut - if (coming.modal) { - $.extend(true, coming, { - closeBtn : false, - closeClick : false, - nextClick : false, - arrows : false, - mouseWheel : false, - keys : null, - helpers: { - overlay : { - closeClick : false - } - } - }); - } - - // 'autoSize' property is a shortcut, too - if (coming.autoSize) { - coming.autoWidth = coming.autoHeight = true; - } - - if (coming.width === 'auto') { - coming.autoWidth = true; - } - - if (coming.height === 'auto') { - coming.autoHeight = true; - } - - /* - * Add reference to the group, so it`s possible to access from callbacks, example: - * afterLoad : function() { - * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : ''); - * } - */ - - coming.group = F.group; - coming.index = index; - - // Give a chance for callback or helpers to update coming item (type, title, etc) - F.coming = coming; - - if (false === F.trigger('beforeLoad')) { - F.coming = null; - - return; - } - - type = coming.type; - href = coming.href; - - if (!type) { - F.coming = null; - - //If we can not determine content type then drop silently or display next/prev item if looping through gallery - if (F.current && F.router && F.router !== 'jumpto') { - F.current.index = index; - - return F[ F.router ]( F.direction ); - } - - return false; - } - - F.isActive = true; - - if (type === 'image' || type === 'swf') { - coming.autoHeight = coming.autoWidth = false; - coming.scrolling = 'visible'; - } - - if (type === 'image') { - coming.aspectRatio = true; - } - - if (type === 'iframe' && isTouch) { - coming.scrolling = 'scroll'; - } - - // Build the neccessary markup - coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' ); - - $.extend(coming, { - skin : $('.fancybox-skin', coming.wrap), - outer : $('.fancybox-outer', coming.wrap), - inner : $('.fancybox-inner', coming.wrap) - }); - - $.each(["Top", "Right", "Bottom", "Left"], function(i, v) { - coming.skin.css('padding' + v, getValue(coming.padding[ i ])); - }); - - F.trigger('onReady'); - - // Check before try to load; 'inline' and 'html' types need content, others - href - if (type === 'inline' || type === 'html') { - if (!coming.content || !coming.content.length) { - return F._error( 'content' ); - } - - } else if (!href) { - return F._error( 'href' ); - } - - if (type === 'image') { - F._loadImage(); - - } else if (type === 'ajax') { - F._loadAjax(); - - } else if (type === 'iframe') { - F._loadIframe(); - - } else { - F._afterLoad(); - } - }, - - _error: function ( type ) { - $.extend(F.coming, { - type : 'html', - autoWidth : true, - autoHeight : true, - minWidth : 0, - minHeight : 0, - scrolling : 'no', - hasError : type, - content : F.coming.tpl.error - }); - - F._afterLoad(); - }, - - _loadImage: function () { - // Reset preload image so it is later possible to check "complete" property - var img = F.imgPreload = new Image(); - - img.onload = function () { - this.onload = this.onerror = null; - - F.coming.width = this.width; - F.coming.height = this.height; - - F._afterLoad(); - }; - - img.onerror = function () { - this.onload = this.onerror = null; - - F._error( 'image' ); - }; - - img.src = F.coming.href; - - if (img.complete !== true) { - F.showLoading(); - } - }, - - _loadAjax: function () { - var coming = F.coming; - - F.showLoading(); - - F.ajaxLoad = $.ajax($.extend({}, coming.ajax, { - url: coming.href, - error: function (jqXHR, textStatus) { - if (F.coming && textStatus !== 'abort') { - F._error( 'ajax', jqXHR ); - - } else { - F.hideLoading(); - } - }, - success: function (data, textStatus) { - if (textStatus === 'success') { - coming.content = data; - - F._afterLoad(); - } - } - })); - }, - - _loadIframe: function() { - var coming = F.coming, - iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime())) - .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling) - .attr('src', coming.href); - - // This helps IE - $(coming.wrap).bind('onReset', function () { - try { - $(this).find('iframe').hide().attr('src', '//about:blank').end().empty(); - } catch (e) {} - }); - - if (coming.iframe.preload) { - F.showLoading(); - - iframe.one('load', function() { - $(this).data('ready', 1); - - // iOS will lose scrolling if we resize - if (!isTouch) { - $(this).bind('load.fb', F.update); - } - - // Without this trick: - // - iframe won't scroll on iOS devices - // - IE7 sometimes displays empty iframe - $(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show(); - - F._afterLoad(); - }); - } - - coming.content = iframe.appendTo( coming.inner ); - - if (!coming.iframe.preload) { - F._afterLoad(); - } - }, - - _preloadImages: function() { - var group = F.group, - current = F.current, - len = group.length, - cnt = current.preload ? Math.min(current.preload, len - 1) : 0, - item, - i; - - for (i = 1; i <= cnt; i += 1) { - item = group[ (current.index + i ) % len ]; - - if (item.type === 'image' && item.href) { - new Image().src = item.href; - } - } - }, - - _afterLoad: function () { - var coming = F.coming, - previous = F.current, - placeholder = 'fancybox-placeholder', - current, - content, - type, - scrolling, - href, - embed; - - F.hideLoading(); - - if (!coming || F.isActive === false) { - return; - } - - if (false === F.trigger('afterLoad', coming, previous)) { - coming.wrap.stop(true).trigger('onReset').remove(); - - F.coming = null; - - return; - } - - if (previous) { - F.trigger('beforeChange', previous); - - previous.wrap.stop(true).removeClass('fancybox-opened') - .find('.fancybox-item, .fancybox-nav') - .remove(); - } - - F.unbindEvents(); - - current = coming; - content = coming.content; - type = coming.type; - scrolling = coming.scrolling; - - $.extend(F, { - wrap : current.wrap, - skin : current.skin, - outer : current.outer, - inner : current.inner, - current : current, - previous : previous - }); - - href = current.href; - - switch (type) { - case 'inline': - case 'ajax': - case 'html': - if (current.selector) { - content = $('<div>').html(content).find(current.selector); - - } else if (isQuery(content)) { - if (!content.data(placeholder)) { - content.data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() ); - } - - content = content.show().detach(); - - current.wrap.bind('onReset', function () { - if ($(this).find(content).length) { - content.hide().replaceAll( content.data(placeholder) ).data(placeholder, false); - } - }); - } - break; - - case 'image': - content = current.tpl.image.replace('{href}', href); - break; - - case 'swf': - content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>'; - embed = ''; - - $.each(current.swf, function(name, val) { - content += '<param name="' + name + '" value="' + val + '"></param>'; - embed += ' ' + name + '="' + val + '"'; - }); - - content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>'; - break; - } - - if (!(isQuery(content) && content.parent().is(current.inner))) { - current.inner.append( content ); - } - - // Give a chance for helpers or callbacks to update elements - F.trigger('beforeShow'); - - // Set scrolling before calculating dimensions - current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling)); - - // Set initial dimensions and start position - F._setDimension(); - - F.reposition(); - - F.isOpen = false; - F.coming = null; - - F.bindEvents(); - - if (!F.isOpened) { - $('.fancybox-wrap').not( current.wrap ).stop(true).trigger('onReset').remove(); - - } else if (previous.prevMethod) { - F.transitions[ previous.prevMethod ](); - } - - F.transitions[ F.isOpened ? current.nextMethod : current.openMethod ](); - - F._preloadImages(); - }, - - _setDimension: function () { - var viewport = F.getViewport(), - steps = 0, - canShrink = false, - canExpand = false, - wrap = F.wrap, - skin = F.skin, - inner = F.inner, - current = F.current, - width = current.width, - height = current.height, - minWidth = current.minWidth, - minHeight = current.minHeight, - maxWidth = current.maxWidth, - maxHeight = current.maxHeight, - scrolling = current.scrolling, - scrollOut = current.scrollOutside ? current.scrollbarWidth : 0, - margin = current.margin, - wMargin = getScalar(margin[1] + margin[3]), - hMargin = getScalar(margin[0] + margin[2]), - wPadding, - hPadding, - wSpace, - hSpace, - origWidth, - origHeight, - origMaxWidth, - origMaxHeight, - ratio, - width_, - height_, - maxWidth_, - maxHeight_, - iframe, - body; - - // Reset dimensions so we could re-check actual size - wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp'); - - wPadding = getScalar(skin.outerWidth(true) - skin.width()); - hPadding = getScalar(skin.outerHeight(true) - skin.height()); - - // Any space between content and viewport (margin, padding, border, title) - wSpace = wMargin + wPadding; - hSpace = hMargin + hPadding; - - origWidth = isPercentage(width) ? (viewport.w - wSpace) * getScalar(width) / 100 : width; - origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height; - - if (current.type === 'iframe') { - iframe = current.content; - - if (current.autoHeight && iframe.data('ready') === 1) { - try { - if (iframe[0].contentWindow.document.location) { - inner.width( origWidth ).height(9999); - - body = iframe.contents().find('body'); - - if (scrollOut) { - body.css('overflow-x', 'hidden'); - } - - origHeight = body.height(); - } - - } catch (e) {} - } - - } else if (current.autoWidth || current.autoHeight) { - inner.addClass( 'fancybox-tmp' ); - - // Set width or height in case we need to calculate only one dimension - if (!current.autoWidth) { - inner.width( origWidth ); - } - - if (!current.autoHeight) { - inner.height( origHeight ); - } - - if (current.autoWidth) { - origWidth = inner.width(); - } - - if (current.autoHeight) { - origHeight = inner.height(); - } - - inner.removeClass( 'fancybox-tmp' ); - } - - width = getScalar( origWidth ); - height = getScalar( origHeight ); - - ratio = origWidth / origHeight; - - // Calculations for the content - minWidth = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth); - maxWidth = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth); - - minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight); - maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight); - - // These will be used to determine if wrap can fit in the viewport - origMaxWidth = maxWidth; - origMaxHeight = maxHeight; - - if (current.fitToView) { - maxWidth = Math.min(viewport.w - wSpace, maxWidth); - maxHeight = Math.min(viewport.h - hSpace, maxHeight); - } - - maxWidth_ = viewport.w - wMargin; - maxHeight_ = viewport.h - hMargin; - - if (current.aspectRatio) { - if (width > maxWidth) { - width = maxWidth; - height = getScalar(width / ratio); - } - - if (height > maxHeight) { - height = maxHeight; - width = getScalar(height * ratio); - } - - if (width < minWidth) { - width = minWidth; - height = getScalar(width / ratio); - } - - if (height < minHeight) { - height = minHeight; - width = getScalar(height * ratio); - } - - } else { - width = Math.max(minWidth, Math.min(width, maxWidth)); - - if (current.autoHeight && current.type !== 'iframe') { - inner.width( width ); - - height = inner.height(); - } - - height = Math.max(minHeight, Math.min(height, maxHeight)); - } - - // Try to fit inside viewport (including the title) - if (current.fitToView) { - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - // Real wrap dimensions - width_ = wrap.width(); - height_ = wrap.height(); - - if (current.aspectRatio) { - while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) { - if (steps++ > 19) { - break; - } - - height = Math.max(minHeight, Math.min(maxHeight, height - 10)); - width = getScalar(height * ratio); - - if (width < minWidth) { - width = minWidth; - height = getScalar(width / ratio); - } - - if (width > maxWidth) { - width = maxWidth; - height = getScalar(width / ratio); - } - - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - width_ = wrap.width(); - height_ = wrap.height(); - } - - } else { - width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_))); - height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_))); - } - } - - if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) { - width += scrollOut; - } - - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - width_ = wrap.width(); - height_ = wrap.height(); - - canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight; - canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight)); - - $.extend(current, { - dim : { - width : getValue( width_ ), - height : getValue( height_ ) - }, - origWidth : origWidth, - origHeight : origHeight, - canShrink : canShrink, - canExpand : canExpand, - wPadding : wPadding, - hPadding : hPadding, - wrapSpace : height_ - skin.outerHeight(true), - skinSpace : skin.height() - height - }); - - if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) { - inner.height('auto'); - } - }, - - _getPosition: function (onlyAbsolute) { - var current = F.current, - viewport = F.getViewport(), - margin = current.margin, - width = F.wrap.width() + margin[1] + margin[3], - height = F.wrap.height() + margin[0] + margin[2], - rez = { - position: 'absolute', - top : margin[0], - left : margin[3] - }; - - if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) { - rez.position = 'fixed'; - - } else if (!current.locked) { - rez.top += viewport.y; - rez.left += viewport.x; - } - - rez.top = getValue(Math.max(rez.top, rez.top + ((viewport.h - height) * current.topRatio))); - rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width) * current.leftRatio))); - - return rez; - }, - - _afterZoomIn: function () { - var current = F.current; - - if (!current) { - return; - } - - F.isOpen = F.isOpened = true; - - F.wrap.css('overflow', 'visible').addClass('fancybox-opened'); - - F.update(); - - // Assign a click event - if ( current.closeClick || (current.nextClick && F.group.length > 1) ) { - F.inner.css('cursor', 'pointer').bind('click.fb', function(e) { - if (!$(e.target).is('a') && !$(e.target).parent().is('a')) { - e.preventDefault(); - - F[ current.closeClick ? 'close' : 'next' ](); - } - }); - } - - // Create a close button - if (current.closeBtn) { - $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) { - e.preventDefault(); - - F.close(); - }); - } - - // Create navigation arrows - if (current.arrows && F.group.length > 1) { - if (current.loop || current.index > 0) { - $(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev); - } - - if (current.loop || current.index < F.group.length - 1) { - $(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next); - } - } - - F.trigger('afterShow'); - - // Stop the slideshow if this is the last item - if (!current.loop && current.index === current.group.length - 1) { - F.play( false ); - - } else if (F.opts.autoPlay && !F.player.isActive) { - F.opts.autoPlay = false; - - F.play(); - } - }, - - _afterZoomOut: function ( obj ) { - obj = obj || F.current; - - $('.fancybox-wrap').trigger('onReset').remove(); - - $.extend(F, { - group : {}, - opts : {}, - router : false, - current : null, - isActive : false, - isOpened : false, - isOpen : false, - isClosing : false, - wrap : null, - skin : null, - outer : null, - inner : null - }); - - F.trigger('afterClose', obj); - } - }); - - /* - * Default transitions - */ - - F.transitions = { - getOrigPosition: function () { - var current = F.current, - element = current.element, - orig = current.orig, - pos = {}, - width = 50, - height = 50, - hPadding = current.hPadding, - wPadding = current.wPadding, - viewport = F.getViewport(); - - if (!orig && current.isDom && element.is(':visible')) { - orig = element.find('img:first'); - - if (!orig.length) { - orig = element; - } - } - - if (isQuery(orig)) { - pos = orig.offset(); - - if (orig.is('img')) { - width = orig.outerWidth(); - height = orig.outerHeight(); - } - - } else { - pos.top = viewport.y + (viewport.h - height) * current.topRatio; - pos.left = viewport.x + (viewport.w - width) * current.leftRatio; - } - - if (F.wrap.css('position') === 'fixed' || current.locked) { - pos.top -= viewport.y; - pos.left -= viewport.x; - } - - pos = { - top : getValue(pos.top - hPadding * current.topRatio), - left : getValue(pos.left - wPadding * current.leftRatio), - width : getValue(width + wPadding), - height : getValue(height + hPadding) - }; - - return pos; - }, - - step: function (now, fx) { - var ratio, - padding, - value, - prop = fx.prop, - current = F.current, - wrapSpace = current.wrapSpace, - skinSpace = current.skinSpace; - - if (prop === 'width' || prop === 'height') { - ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start); - - if (F.isClosing) { - ratio = 1 - ratio; - } - - padding = prop === 'width' ? current.wPadding : current.hPadding; - value = now - padding; - - F.skin[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) ) ); - F.inner[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) - (skinSpace * ratio) ) ); - } - }, - - zoomIn: function () { - var current = F.current, - startPos = current.pos, - effect = current.openEffect, - elastic = effect === 'elastic', - endPos = $.extend({opacity : 1}, startPos); - - // Remove "position" property that breaks older IE - delete endPos.position; - - if (elastic) { - startPos = this.getOrigPosition(); - - if (current.openOpacity) { - startPos.opacity = 0.1; - } - - } else if (effect === 'fade') { - startPos.opacity = 0.1; - } - - F.wrap.css(startPos).animate(endPos, { - duration : effect === 'none' ? 0 : current.openSpeed, - easing : current.openEasing, - step : elastic ? this.step : null, - complete : F._afterZoomIn - }); - }, - - zoomOut: function () { - var current = F.current, - effect = current.closeEffect, - elastic = effect === 'elastic', - endPos = {opacity : 0.1}; - - if (elastic) { - endPos = this.getOrigPosition(); - - if (current.closeOpacity) { - endPos.opacity = 0.1; - } - } - - F.wrap.animate(endPos, { - duration : effect === 'none' ? 0 : current.closeSpeed, - easing : current.closeEasing, - step : elastic ? this.step : null, - complete : F._afterZoomOut - }); - }, - - changeIn: function () { - var current = F.current, - effect = current.nextEffect, - startPos = current.pos, - endPos = { opacity : 1 }, - direction = F.direction, - distance = 200, - field; - - startPos.opacity = 0.1; - - if (effect === 'elastic') { - field = direction === 'down' || direction === 'up' ? 'top' : 'left'; - - if (direction === 'down' || direction === 'right') { - startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance); - endPos[ field ] = '+=' + distance + 'px'; - - } else { - startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance); - endPos[ field ] = '-=' + distance + 'px'; - } - } - - // Workaround for http://bugs.jquery.com/ticket/12273 - if (effect === 'none') { - F._afterZoomIn(); - - } else { - F.wrap.css(startPos).animate(endPos, { - duration : current.nextSpeed, - easing : current.nextEasing, - complete : F._afterZoomIn - }); - } - }, - - changeOut: function () { - var previous = F.previous, - effect = previous.prevEffect, - endPos = { opacity : 0.1 }, - direction = F.direction, - distance = 200; - - if (effect === 'elastic') { - endPos[ direction === 'down' || direction === 'up' ? 'top' : 'left' ] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px'; - } - - previous.wrap.animate(endPos, { - duration : effect === 'none' ? 0 : previous.prevSpeed, - easing : previous.prevEasing, - complete : function () { - $(this).trigger('onReset').remove(); - } - }); - } - }; - - /* - * Overlay helper - */ - - F.helpers.overlay = { - defaults : { - closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay - speedOut : 200, // duration of fadeOut animation - showEarly : true, // indicates if should be opened immediately or wait until the content is ready - css : {}, // custom CSS properties - locked : !isTouch, // if true, the content will be locked into overlay - fixed : true // if false, the overlay CSS position property will not be set to "fixed" - }, - - overlay : null, // current handle - fixed : false, // indicates if the overlay has position "fixed" - - // Public methods - create : function(opts) { - opts = $.extend({}, this.defaults, opts); - - if (this.overlay) { - this.close(); - } - - this.overlay = $('<div class="fancybox-overlay"></div>').appendTo( 'body' ); - this.fixed = false; - - if (opts.fixed && F.defaults.fixed) { - this.overlay.addClass('fancybox-overlay-fixed'); - - this.fixed = true; - } - }, - - open : function(opts) { - var that = this; - - opts = $.extend({}, this.defaults, opts); - - if (this.overlay) { - this.overlay.unbind('.overlay').width('auto').height('auto'); - - } else { - this.create(opts); - } - - if (!this.fixed) { - W.bind('resize.overlay', $.proxy( this.update, this) ); - - this.update(); - } - - if (opts.closeClick) { - this.overlay.bind('click.overlay', function(e) { - if ($(e.target).hasClass('fancybox-overlay')) { - if (F.isActive) { - F.close(); - } else { - that.close(); - } - } - }); - } - - this.overlay.css( opts.css ).show(); - }, - - close : function() { - $('.fancybox-overlay').remove(); - - W.unbind('resize.overlay'); - - this.overlay = null; - - if (this.margin !== false) { - $('body').css('margin-right', this.margin); - - this.margin = false; - } - - if (this.el) { - this.el.removeClass('fancybox-lock'); - } - }, - - // Private, callbacks - - update : function () { - var width = '100%', offsetWidth; - - // Reset width/height so it will not mess - this.overlay.width(width).height('100%'); - - // jQuery does not return reliable result for IE - if (IE) { - offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth); - - if (D.width() > offsetWidth) { - width = D.width(); - } - - } else if (D.width() > W.width()) { - width = D.width(); - } - - this.overlay.width(width).height(D.height()); - }, - - // This is where we can manipulate DOM, because later it would cause iframes to reload - onReady : function (opts, obj) { - $('.fancybox-overlay').stop(true, true); - - if (!this.overlay) { - this.margin = D.height() > W.height() || $('body').css('overflow-y') === 'scroll' ? $('body').css('margin-right') : false; - this.el = document.all && !document.querySelector ? $('html') : $('body'); - - this.create(opts); - } - - if (opts.locked && this.fixed) { - obj.locked = this.overlay.append( obj.wrap ); - obj.fixed = false; - } - - if (opts.showEarly === true) { - this.beforeShow.apply(this, arguments); - } - }, - - beforeShow : function(opts, obj) { - if (obj.locked) { - this.el.addClass('fancybox-lock'); - - if (this.margin !== false) { - $('body').css('margin-right', getScalar( this.margin ) + obj.scrollbarWidth); - } - } - - this.open(opts); - }, - - onUpdate : function() { - if (!this.fixed) { - this.update(); - } - }, - - afterClose: function (opts) { - // Remove overlay if exists and fancyBox is not opening - // (e.g., it is not being open using afterClose callback) - if (this.overlay && !F.isActive) { - this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this )); - } - } - }; - - /* - * Title helper - */ - - F.helpers.title = { - defaults : { - type : 'float', // 'float', 'inside', 'outside' or 'over', - position : 'bottom' // 'top' or 'bottom' - }, - - beforeShow: function (opts) { - var current = F.current, - text = current.title, - type = opts.type, - title, - target; - - if ($.isFunction(text)) { - text = text.call(current.element, current); - } - - if (!isString(text) || $.trim(text) === '') { - return; - } - - title = $('<div class="fancybox-title fancybox-title-' + type + '-wrap">' + text + '</div>'); - - switch (type) { - case 'inside': - target = F.skin; - break; - - case 'outside': - target = F.wrap; - break; - - case 'over': - target = F.inner; - break; - - default: // 'float' - target = F.skin; - - title.appendTo('body'); - - if (IE) { - title.width( title.width() ); - } - - title.wrapInner('<span class="child"></span>'); - - //Increase bottom margin so this title will also fit into viewport - F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) ); - break; - } - - title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target); - } - }; - - // jQuery plugin initialization - $.fn.fancybox = function (options) { - var index, - that = $(this), - selector = this.selector || '', - run = function(e) { - var what = $(this).blur(), idx = index, relType, relVal; - - if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) { - relType = options.groupAttr || 'data-fancybox-group'; - relVal = what.attr(relType); - - if (!relVal) { - relType = 'rel'; - relVal = what.get(0)[ relType ]; - } - - if (relVal && relVal !== '' && relVal !== 'nofollow') { - what = selector.length ? $(selector) : that; - what = what.filter('[' + relType + '="' + relVal + '"]'); - idx = what.index(this); - } - - options.index = idx; - - // Stop an event from bubbling if everything is fine - if (F.open(what, options) !== false) { - e.preventDefault(); - } - } - }; - - options = options || {}; - index = options.index || 0; - - if (!selector || options.live === false) { - that.unbind('click.fb-start').bind('click.fb-start', run); - - } else { - D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run); - } - - this.filter('[data-fancybox-start=1]').trigger('click'); - - return this; - }; - - // Tests that need a body at doc ready - D.ready(function() { - if ( $.scrollbarWidth === undefined ) { - // http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth - $.scrollbarWidth = function() { - var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'), - child = parent.children(), - width = child.innerWidth() - child.height( 99 ).innerWidth(); - - parent.remove(); - - return width; - }; - } - - if ( $.support.fixedPosition === undefined ) { - $.support.fixedPosition = (function() { - var elem = $('<div style="position:fixed;top:20px;"></div>').appendTo('body'), - fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 ); - - elem.remove(); - - return fixed; - }()); - } - - $.extend(F.defaults, { - scrollbarWidth : $.scrollbarWidth(), - fixed : $.support.fixedPosition, - parent : $('body') - }); - }); - -}(window, document, jQuery)); \ No newline at end of file diff --git a/pykeg/web/static/fancybox/jquery.fancybox.pack.js b/pykeg/web/static/fancybox/jquery.fancybox.pack.js deleted file mode 100644 index 9f6a628eb..000000000 --- a/pykeg/web/static/fancybox/jquery.fancybox.pack.js +++ /dev/null @@ -1,45 +0,0 @@ -/*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */ -(function(C,z,f,r){var q=f(C),n=f(z),b=f.fancybox=function(){b.open.apply(this,arguments)},H=navigator.userAgent.match(/msie/),w=null,s=z.createTouch!==r,t=function(a){return a&&a.hasOwnProperty&&a instanceof f},p=function(a){return a&&"string"===f.type(a)},F=function(a){return p(a)&&0<a.indexOf("%")},l=function(a,d){var e=parseInt(a,10)||0;d&&F(a)&&(e*=b.getViewport()[d]/100);return Math.ceil(e)},x=function(a,b){return l(a,b)+"px"};f.extend(b,{version:"2.1.4",defaults:{padding:15,margin:20,width:800, -height:600,minWidth:100,minHeight:100,maxWidth:9999,maxHeight:9999,autoSize:!0,autoHeight:!1,autoWidth:!1,autoResize:!0,autoCenter:!s,fitToView:!0,aspectRatio:!1,topRatio:0.5,leftRatio:0.5,scrolling:"auto",wrapCSS:"",arrows:!0,closeBtn:!0,closeClick:!1,nextClick:!1,mouseWheel:!0,autoPlay:!1,playSpeed:3E3,preload:3,modal:!1,loop:!0,ajax:{dataType:"html",headers:{"X-fancyBox":!0}},iframe:{scrolling:"auto",preload:!0},swf:{wmode:"transparent",allowfullscreen:"true",allowscriptaccess:"always"},keys:{next:{13:"left", -34:"up",39:"left",40:"up"},prev:{8:"right",33:"down",37:"right",38:"down"},close:[27],play:[32],toggle:[70]},direction:{next:"left",prev:"right"},scrollOutside:!0,index:0,type:null,href:null,content:null,title:null,tpl:{wrap:'<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',image:'<img class="fancybox-image" src="{href}" alt="" />',iframe:'<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen'+ -(H?' allowtransparency="true"':"")+"></iframe>",error:'<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',closeBtn:'<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',next:'<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',prev:'<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, -openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, -isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=t(a)?f(a).get():[a]),f.each(a,function(e,c){var k={},g,h,j,m,l;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),t(c)?(k={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,k, -c.metadata())):k=c);g=d.href||k.href||(p(c)?c:null);h=d.title!==r?d.title:k.title||"";m=(j=d.content||k.content)?"html":d.type||k.type;!m&&k.isDom&&(m=c.data("fancybox-type"),m||(m=(m=c.prop("class").match(/fancybox\.(\w+)/))?m[1]:null));p(g)&&(m||(b.isImage(g)?m="image":b.isSWF(g)?m="swf":"#"===g.charAt(0)?m="inline":p(c)&&(m="html",j=c)),"ajax"===m&&(l=g.split(/\s+/,2),g=l.shift(),l=l.shift()));j||("inline"===m?g?j=f(p(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):k.isDom&&(j=c):"html"===m?j=g:!m&&(!g&& -k.isDom)&&(m="inline",j=c));f.extend(k,{href:g,type:m,content:j,title:h,selector:l});a[e]=k}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==r&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current|| -b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer= -setTimeout(b.next,b.current.playSpeed))},c=function(){d();f("body").unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index<b.group.length-1))b.player.isActive=!0,f("body").bind({"afterShow.player onUpdate.player":e,"onCancel.player beforeClose.player":c,"beforeLoad.player":d}),e(),b.trigger("onPlayStart")}else c()},next:function(a){var d=b.current;d&&(p(a)||(a=d.direction.next),b.jumpto(d.index+1,a,"next"))}, -prev:function(a){var d=b.current;d&&(p(a)||(a=d.direction.prev),b.jumpto(d.index-1,a,"prev"))},jumpto:function(a,d,e){var c=b.current;c&&(a=l(a),b.direction=d||c.direction[a>=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==r&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,k;c&&(k=b._getPosition(d),a&&"scroll"===a.type?(delete k.position,c.stop(!0,!0).animate(k,200)):(c.css(k),e.pos=f.extend({}, -e.dim,k)))},update:function(a){var d=a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(w),w=null);b.isOpen&&!w&&(w=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),w=null)},e&&!s?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,s&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"), -b.trigger("onUpdate")),b.update())},hideLoading:function(){n.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('<div id="fancybox-loading"><div></div></div>').click(b.cancel).appendTo("body");n.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked|| -!1,d={x:q.scrollLeft(),y:q.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=s&&C.innerWidth?C.innerWidth:q.width(),d.h=s&&C.innerHeight?C.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&t(b.wrap)&&b.wrap.unbind(".fb");n.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(s?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&n.bind("keydown.fb",function(e){var c=e.which||e.keyCode,k= -e.target||e.srcElement;if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!k||!k.type&&!f(k).is("[contenteditable]")))&&f.each(d,function(d,k){if(1<a.group.length&&k[c]!==r)return b[d](k[c]),e.preventDefault(),!1;if(-1<f.inArray(c,k))return b[d](),e.preventDefault(),!1})}),f.fn.mousewheel&&a.mouseWheel&&b.wrap.bind("mousewheel.fb",function(d,c,k,g){for(var h=f(d.target||null),j=!1;h.length&&!j&&!h.is(".fancybox-skin")&&!h.is(".fancybox-wrap");)j=h[0]&&!(h[0].style.overflow&& -"hidden"===h[0].style.overflow)&&(h[0].clientWidth&&h[0].scrollWidth>h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!j&&1<b.group.length&&!a.canShrink){if(0<g||0<k)b.prev(0<g?"down":"left");else if(0>g||0>k)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d, -e){e&&(b.helpers[d]&&f.isFunction(b.helpers[d][a]))&&(e=f.extend(!0,{},b.helpers[d].defaults,e),b.helpers[d][a](e,c))});f.event.trigger(a+".fb")}},isImage:function(a){return p(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)},isSWF:function(a){return p(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=l(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&& -(d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive= -!0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&s&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(s?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady"); -if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width= -this.width;b.coming.height=this.height;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g, -(new Date).getTime())).attr("scrolling",s?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);s||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a= -b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,k,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents(); -e=a.content;c=a.type;k=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("<div>").html(e).find(a.selector):t(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('<div class="fancybox-placeholder"></div>').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder", -!1)}));break;case "image":e=a.tpl.image.replace("{href}",g);break;case "swf":e='<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="'+g+'"></param>',h="",f.each(a.swf,function(a,b){e+='<param name="'+a+'" value="'+b+'"></param>';h+=" "+a+'="'+b+'"'}),e+='<embed src="'+g+'" type="application/x-shockwave-flash" width="100%" height="100%"'+h+"></embed></object>"}(!t(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow"); -a.inner.css("overflow","yes"===k?"scroll":"no"===k?"hidden":k);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,k=b.skin,g=b.inner,h=b.current,c=h.width,j=h.height,m=h.minWidth,u=h.minHeight,n=h.maxWidth, -v=h.maxHeight,s=h.scrolling,q=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,p=l(y[1]+y[3]),r=l(y[0]+y[2]),z,A,t,D,B,G,C,E,w;e.add(k).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=l(k.outerWidth(!0)-k.width());z=l(k.outerHeight(!0)-k.height());A=p+y;t=r+z;D=F(c)?(a.w-A)*l(c)/100:c;B=F(j)?(a.h-t)*l(j)/100:j;if("iframe"===h.type){if(w=h.content,h.autoHeight&&1===w.data("ready"))try{w[0].contentWindow.document.location&&(g.width(D).height(9999),G=w.contents().find("body"),q&&G.css("overflow-x", -"hidden"),B=G.height())}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=l(D);j=l(B);E=D/B;m=l(F(m)?l(m,"w")-A:m);n=l(F(n)?l(n,"w")-A:n);u=l(F(u)?l(u,"h")-t:u);v=l(F(v)?l(v,"h")-t:v);G=n;C=v;h.fitToView&&(n=Math.min(a.w-A,n),v=Math.min(a.h-t,v));A=a.w-p;r=a.h-r;h.aspectRatio?(c>n&&(c=n,j=l(c/E)),j>v&&(j=v,c=l(j*E)),c<m&&(c=m,j=l(c/E)),j<u&& -(j=u,c=l(j*E))):(c=Math.max(m,Math.min(c,n)),h.autoHeight&&"iframe"!==h.type&&(g.width(c),j=g.height()),j=Math.max(u,Math.min(j,v)));if(h.fitToView)if(g.width(c).height(j),e.width(c+y),a=e.width(),p=e.height(),h.aspectRatio)for(;(a>A||p>r)&&(c>m&&j>u)&&!(19<d++);)j=Math.max(u,Math.min(v,j-10)),c=l(j*E),c<m&&(c=m,j=l(c/E)),c>n&&(c=n,j=l(c/E)),g.width(c).height(j),e.width(c+y),a=e.width(),p=e.height();else c=Math.max(m,Math.min(c,c-(a-A))),j=Math.max(u,Math.min(j,j-(p-r)));q&&("auto"===s&&j<B&&c+y+ -q<A)&&(c+=q);g.width(c).height(j);e.width(c+y);a=e.width();p=e.height();e=(a>A||p>r)&&c>m&&j>u;c=h.aspectRatio?c<G&&j<C&&c<D&&j<B:(c<G||j<C)&&(c<D||j<B);f.extend(h,{dim:{width:x(a),height:x(p)},origWidth:D,origHeight:B,canShrink:e,canExpand:c,wPadding:y,hPadding:z,wrapSpace:p-k.outerHeight(!0),skinSpace:k.height()-j});!w&&(h.autoHeight&&j>u&&j<v&&!c)&&g.height("auto")},_getPosition:function(a){var d=b.current,e=b.getViewport(),c=d.margin,f=b.wrap.width()+c[1]+c[3],g=b.wrap.height()+c[0]+c[2],c={position:"absolute", -top:c[0],left:c[3]};d.autoCenter&&d.fixed&&!a&&g<=e.h&&f<=e.w?c.position="fixed":d.locked||(c.top+=e.y,c.left+=e.x);c.top=x(Math.max(c.top,c.top+(e.h-g)*d.topRatio));c.left=x(Math.max(c.left,c.left+(e.w-f)*d.leftRatio));return c},_afterZoomIn:function(){var a=b.current;a&&(b.isOpen=b.isOpened=!0,b.wrap.css("overflow","visible").addClass("fancybox-opened"),b.update(),(a.closeClick||a.nextClick&&1<b.group.length)&&b.inner.css("cursor","pointer").bind("click.fb",function(d){!f(d.target).is("a")&&!f(d.target).parent().is("a")&& -(d.preventDefault(),b[a.closeClick?"close":"next"]())}),a.closeBtn&&f(a.tpl.closeBtn).appendTo(b.skin).bind("click.fb",function(a){a.preventDefault();b.close()}),a.arrows&&1<b.group.length&&((a.loop||0<a.index)&&f(a.tpl.prev).appendTo(b.outer).bind("click.fb",b.prev),(a.loop||a.index<b.group.length-1)&&f(a.tpl.next).appendTo(b.outer).bind("click.fb",b.next)),b.trigger("afterShow"),!a.loop&&a.index===a.group.length-1?b.play(!1):b.opts.autoPlay&&!b.player.isActive&&(b.opts.autoPlay=!1,b.play()))},_afterZoomOut:function(a){a= -a||b.current;f(".fancybox-wrap").trigger("onReset").remove();f.extend(b,{group:{},opts:{},router:!1,current:null,isActive:!1,isOpened:!1,isOpen:!1,isClosing:!1,wrap:null,skin:null,outer:null,inner:null});b.trigger("afterClose",a)}});b.transitions={getOrigPosition:function(){var a=b.current,d=a.element,e=a.orig,c={},f=50,g=50,h=a.hPadding,j=a.wPadding,m=b.getViewport();!e&&(a.isDom&&d.is(":visible"))&&(e=d.find("img:first"),e.length||(e=d));t(e)?(c=e.offset(),e.is("img")&&(f=e.outerWidth(),g=e.outerHeight())): -(c.top=m.y+(m.h-g)*a.topRatio,c.left=m.x+(m.w-f)*a.leftRatio);if("fixed"===b.wrap.css("position")||a.locked)c.top-=m.y,c.left-=m.x;return c={top:x(c.top-h*a.topRatio),left:x(c.left-j*a.leftRatio),width:x(f+j),height:x(g+h)}},step:function(a,d){var e,c,f=d.prop;c=b.current;var g=c.wrapSpace,h=c.skinSpace;if("width"===f||"height"===f)e=d.end===d.start?1:(a-d.start)/(d.end-d.start),b.isClosing&&(e=1-e),c="width"===f?c.wPadding:c.hPadding,c=a-c,b.skin[f](l("width"===f?c:c-g*e)),b.inner[f](l("width"=== -f?c:c-g*e-h*e))},zoomIn:function(){var a=b.current,d=a.pos,e=a.openEffect,c="elastic"===e,k=f.extend({opacity:1},d);delete k.position;c?(d=this.getOrigPosition(),a.openOpacity&&(d.opacity=0.1)):"fade"===e&&(d.opacity=0.1);b.wrap.css(d).animate(k,{duration:"none"===e?0:a.openSpeed,easing:a.openEasing,step:c?this.step:null,complete:b._afterZoomIn})},zoomOut:function(){var a=b.current,d=a.closeEffect,e="elastic"===d,c={opacity:0.1};e&&(c=this.getOrigPosition(),a.closeOpacity&&(c.opacity=0.1));b.wrap.animate(c, -{duration:"none"===d?0:a.closeSpeed,easing:a.closeEasing,step:e?this.step:null,complete:b._afterZoomOut})},changeIn:function(){var a=b.current,d=a.nextEffect,e=a.pos,c={opacity:1},f=b.direction,g;e.opacity=0.1;"elastic"===d&&(g="down"===f||"up"===f?"top":"left","down"===f||"right"===f?(e[g]=x(l(e[g])-200),c[g]="+=200px"):(e[g]=x(l(e[g])+200),c[g]="-=200px"));"none"===d?b._afterZoomIn():b.wrap.css(e).animate(c,{duration:a.nextSpeed,easing:a.nextEasing,complete:b._afterZoomIn})},changeOut:function(){var a= -b.previous,d=a.prevEffect,e={opacity:0.1},c=b.direction;"elastic"===d&&(e["down"===c||"up"===c?"top":"left"]=("up"===c||"left"===c?"-":"+")+"=200px");a.wrap.animate(e,{duration:"none"===d?0:a.prevSpeed,easing:a.prevEasing,complete:function(){f(this).trigger("onReset").remove()}})}};b.helpers.overlay={defaults:{closeClick:!0,speedOut:200,showEarly:!0,css:{},locked:!s,fixed:!0},overlay:null,fixed:!1,create:function(a){a=f.extend({},this.defaults,a);this.overlay&&this.close();this.overlay=f('<div class="fancybox-overlay"></div>').appendTo("body"); -this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){f(a.target).hasClass("fancybox-overlay")&&(b.isActive?b.close():d.close())});this.overlay.css(a.css).show()}, -close:function(){f(".fancybox-overlay").remove();q.unbind("resize.overlay");this.overlay=null;!1!==this.margin&&(f("body").css("margin-right",this.margin),this.margin=!1);this.el&&this.el.removeClass("fancybox-lock")},update:function(){var a="100%",b;this.overlay.width(a).height("100%");H?(b=Math.max(z.documentElement.offsetWidth,z.body.offsetWidth),n.width()>b&&(a=n.width())):n.width()>q.width()&&(a=n.width());this.overlay.width(a).height(n.height())},onReady:function(a,b){f(".fancybox-overlay").stop(!0, -!0);this.overlay||(this.margin=n.height()>q.height()||"scroll"===f("body").css("overflow-y")?f("body").css("margin-right"):!1,this.el=z.all&&!z.querySelector?f("html"):f("body"),this.create(a));a.locked&&this.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&(this.el.addClass("fancybox-lock"),!1!==this.margin&&f("body").css("margin-right",l(this.margin)+b.scrollbarWidth));this.open(a)},onUpdate:function(){this.fixed|| -this.update()},afterClose:function(a){this.overlay&&!b.isActive&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(p(e)&&""!==f.trim(e)){d=f('<div class="fancybox-title fancybox-title-'+c+'-wrap">'+e+"</div>");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"), -H&&d.width(d.width()),d.wrapInner('<span class="child"></span>'),b.current.margin[2]+=Math.abs(l(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",k=function(g){var h=f(this).blur(),j=d,k,l;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(k=a.groupAttr||"data-fancybox-group",l=h.attr(k),l||(k="rel",l=h.get(0)[k]),l&&(""!==l&&"nofollow"!==l)&&(h=c.length?f(c):e,h=h.filter("["+k+'="'+l+ -'"]'),j=h.index(this)),a.index=j,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",k):n.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')","click.fb-start",k);this.filter("[data-fancybox-start=1]").trigger("click");return this};n.ready(function(){f.scrollbarWidth===r&&(f.scrollbarWidth=function(){var a=f('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo("body"),b=a.children(), -b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===r){var a=f.support,d=f('<div style="position:fixed;top:20px;"></div>').appendTo("body"),e=20===d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")})})})(window,document,jQuery); \ No newline at end of file diff --git a/pykeg/web/static/highcharts/gfx/vml-radial-gradient.png b/pykeg/web/static/highcharts/gfx/vml-radial-gradient.png deleted file mode 100644 index 504ef6e09..000000000 Binary files a/pykeg/web/static/highcharts/gfx/vml-radial-gradient.png and /dev/null differ diff --git a/pykeg/web/static/highcharts/graphics/skies.jpg b/pykeg/web/static/highcharts/graphics/skies.jpg deleted file mode 100644 index 259f46a2c..000000000 Binary files a/pykeg/web/static/highcharts/graphics/skies.jpg and /dev/null differ diff --git a/pykeg/web/static/highcharts/graphics/snow.png b/pykeg/web/static/highcharts/graphics/snow.png deleted file mode 100644 index 0171485b0..000000000 Binary files a/pykeg/web/static/highcharts/graphics/snow.png and /dev/null differ diff --git a/pykeg/web/static/highcharts/graphics/sun.png b/pykeg/web/static/highcharts/graphics/sun.png deleted file mode 100644 index 2c89e40bd..000000000 Binary files a/pykeg/web/static/highcharts/graphics/sun.png and /dev/null differ diff --git a/pykeg/web/static/highcharts/js/adapters/mootools-adapter.js b/pykeg/web/static/highcharts/js/adapters/mootools-adapter.js deleted file mode 100644 index d3bb92e33..000000000 --- a/pykeg/web/static/highcharts/js/adapters/mootools-adapter.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - MooTools adapter - - (c) 2010-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this, -arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt(document.id(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0},a.getComputedStyle=function(){return a.element.getComputedStyle.apply(a.element, -arguments)};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:document.id(a),g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName? -document.id(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b==="unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d= -null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX=a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})(); diff --git a/pykeg/web/static/highcharts/js/adapters/mootools-adapter.src.js b/pykeg/web/static/highcharts/js/adapters/mootools-adapter.src.js deleted file mode 100644 index f93554d0a..000000000 --- a/pykeg/web/static/highcharts/js/adapters/mootools-adapter.src.js +++ /dev/null @@ -1,316 +0,0 @@ -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * MooTools adapter - * - * (c) 2010-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -// JSLint options: -/*global Fx, $, $extend, $each, $merge, Events, Event, DOMEvent */ - -(function () { - -var win = window, - doc = document, - mooVersion = win.MooTools.version.substring(0, 3), // Get the first three characters of the version number - legacy = mooVersion === '1.2' || mooVersion === '1.1', // 1.1 && 1.2 considered legacy, 1.3 is not. - legacyEvent = legacy || mooVersion === '1.3', // In versions 1.1 - 1.3 the event class is named Event, in newer versions it is named DOMEvent. - $extend = win.$extend || function () { - return Object.append.apply(Object, arguments); - }; - -win.HighchartsAdapter = { - /** - * Initialize the adapter. This is run once as Highcharts is first run. - * @param {Object} pathAnim The helper object to do animations across adapters. - */ - init: function (pathAnim) { - var fxProto = Fx.prototype, - fxStart = fxProto.start, - morphProto = Fx.Morph.prototype, - morphCompute = morphProto.compute; - - // override Fx.start to allow animation of SVG element wrappers - /*jslint unparam: true*//* allow unused parameters in fx functions */ - fxProto.start = function (from, to) { - var fx = this, - elem = fx.element; - - // special for animating paths - if (from.d) { - //this.fromD = this.element.d.split(' '); - fx.paths = pathAnim.init( - elem, - elem.d, - fx.toD - ); - } - fxStart.apply(fx, arguments); - - return this; // chainable - }; - - // override Fx.step to allow animation of SVG element wrappers - morphProto.compute = function (from, to, delta) { - var fx = this, - paths = fx.paths; - - if (paths) { - fx.element.attr( - 'd', - pathAnim.step(paths[0], paths[1], delta, fx.toD) - ); - } else { - return morphCompute.apply(fx, arguments); - } - }; - /*jslint unparam: false*/ - }, - - /** - * Run a general method on the framework, following jQuery syntax - * @param {Object} el The HTML element - * @param {String} method Which method to run on the wrapped element - */ - adapterRun: function (el, method) { - - // This currently works for getting inner width and height. If adding - // more methods later, we need a conditional implementation for each. - if (method === 'width' || method === 'height') { - return parseInt(document.id(el).getStyle(method), 10); - } - }, - - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript: function (scriptLocation, callback) { - // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script. - var head = doc.getElementsByTagName('head')[0]; - var script = doc.createElement('script'); - - script.type = 'text/javascript'; - script.src = scriptLocation; - script.onload = callback; - - head.appendChild(script); - }, - - /** - * Animate a HTML element or SVG element wrapper - * @param {Object} el - * @param {Object} params - * @param {Object} options jQuery-like animation options: duration, easing, callback - */ - animate: function (el, params, options) { - var isSVGElement = el.attr, - effect, - complete = options && options.complete; - - if (isSVGElement && !el.setStyle) { - // add setStyle and getStyle methods for internal use in Moo - el.getStyle = el.attr; - el.setStyle = function () { // property value is given as array in Moo - break it down - var args = arguments; - this.attr.call(this, args[0], args[1][0]); - }; - // dirty hack to trick Moo into handling el as an element wrapper - el.$family = function () { return true; }; - el.getComputedStyle = function () { - return el.element.getComputedStyle.apply(el.element, arguments); - }; - } - - // stop running animations - win.HighchartsAdapter.stop(el); - - // define and run the effect - effect = new Fx.Morph( - isSVGElement ? el : document.id(el), - $extend({ - transition: Fx.Transitions.Quad.easeInOut - }, options) - ); - - // Make sure that the element reference is set when animating svg elements - if (isSVGElement) { - effect.element = el; - } - - // special treatment for paths - if (params.d) { - effect.toD = params.d; - } - - // jQuery-like events - if (complete) { - effect.addEvent('complete', complete); - } - - // run - effect.start(params); - - // record for use in stop method - el.fx = effect; - }, - - /** - * MooTool's each function - * - */ - each: function (arr, fn) { - return legacy ? - $each(arr, fn) : - Array.each(arr, fn); - }, - - /** - * Map an array - * @param {Array} arr - * @param {Function} fn - */ - map: function (arr, fn) { - return arr.map(fn); - }, - - /** - * Grep or filter an array - * @param {Array} arr - * @param {Function} fn - */ - grep: function (arr, fn) { - return arr.filter(fn); - }, - - /** - * Return the index of an item in an array, or -1 if not matched - */ - inArray: function (item, arr, from) { - return arr ? arr.indexOf(item, from) : -1; - }, - - /** - * Get the offset of an element relative to the top left corner of the web page - */ - offset: function (el) { - var offsets = el.getPosition(); // #1496 - return { - left: offsets.x, - top: offsets.y - }; - }, - - /** - * Extends an object with Events, if its not done - */ - extendWithEvents: function (el) { - // if the addEvent method is not defined, el is a custom Highcharts object - // like series or point - if (!el.addEvent) { - if (el.nodeName) { - el = document.id(el); // a dynamically generated node - } else { - $extend(el, new Events()); // a custom object - } - } - }, - - /** - * Add an event listener - * @param {Object} el HTML element or custom object - * @param {String} type Event type - * @param {Function} fn Event handler - */ - addEvent: function (el, type, fn) { - if (typeof type === 'string') { // chart broke due to el being string, type function - - if (type === 'unload') { // Moo self destructs before custom unload events - type = 'beforeunload'; - } - - win.HighchartsAdapter.extendWithEvents(el); - - el.addEvent(type, fn); - } - }, - - removeEvent: function (el, type, fn) { - if (typeof el === 'string') { - // el.removeEvents below apperantly calls this method again. Do not quite understand why, so for now just bail out. - return; - } - - if (el.addEvent) { // If el doesn't have an addEvent method, there are no events to remove - if (type) { - if (type === 'unload') { // Moo self destructs before custom unload events - type = 'beforeunload'; - } - - if (fn) { - el.removeEvent(type, fn); - } else if (el.removeEvents) { // #958 - el.removeEvents(type); - } - } else { - el.removeEvents(); - } - } - }, - - fireEvent: function (el, event, eventArguments, defaultFunction) { - var eventArgs = { - type: event, - target: el - }; - // create an event object that keeps all functions - event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs); - event = $extend(event, eventArguments); - - // When running an event on the Chart.prototype, MooTools nests the target in event.event - if (!event.target && event.event) { - event.target = event.event.target; - } - - // override the preventDefault function to be able to use - // this for custom events - event.preventDefault = function () { - defaultFunction = null; - }; - // if fireEvent is not available on the object, there hasn't been added - // any events to it above - if (el.fireEvent) { - el.fireEvent(event.type, event); - } - - // fire the default if it is passed and it is not prevented above - if (defaultFunction) { - defaultFunction(event); - } - }, - - /** - * Set back e.pageX and e.pageY that MooTools has abstracted away. #1165, #1346. - */ - washMouseEvent: function (e) { - if (e.page) { - e.pageX = e.page.x; - e.pageY = e.page.y; - } - return e; - }, - - /** - * Stop running animations on the object - */ - stop: function (el) { - if (el.fx) { - el.fx.cancel(); - } - } -}; - -}()); diff --git a/pykeg/web/static/highcharts/js/adapters/prototype-adapter.js b/pykeg/web/static/highcharts/js/adapters/prototype-adapter.js deleted file mode 100644 index 22bb50b58..000000000 --- a/pykeg/web/static/highcharts/js/adapters/prototype-adapter.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - Prototype adapter - - @author Michael Nelson, Torstein Honsi. - - Feel free to use and modify this script. - Highcharts license: www.highcharts.com/license. -*/ -var HighchartsAdapter=function(){var f=typeof Effect!=="undefined";return{init:function(a){if(f)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(b,c,d,g){var e;this.element=b;this.key=c;e=b.attr?b.attr(c):$(b).getStyle(c);if(c==="d")this.paths=a.init(b,b.d,d),this.toD=d,e=0,d=1;this.start(Object.extend(g||{},{from:e,to:d,attribute:c}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]= -this},update:function(b){var c=this.paths,d=this.element;c&&(b=a.step(c[0],c[1],b,this.toD));d.attr?d.element&&d.attr(this.options.attribute,b):(c={},c[this.options.attribute]=b,$(d).setStyle(c))},finish:function(){this.element&&this.element._highchart_animation&&delete this.element._highchart_animation[this.key]}})},adapterRun:function(a,b){return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=$$("head")[0];c&&c.appendChild((new Element("script",{type:"text/javascript",src:a})).observe("load", -b))},addNS:function(a){var b=/^(?:click|mouse(?:down|up|over|move|out))$/;return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(a)||b.test(a)?a:"h:"+a},addEvent:function(a,b,c){a.addEventListener||a.attachEvent?Event.observe($(a),HighchartsAdapter.addNS(b),c):(HighchartsAdapter._extend(a),a._highcharts_observe(b,c))},animate:function(a,b,c){var d,c=c||{};c.delay=0;c.duration=(c.duration||500)/1E3;c.afterFinish=c.complete;if(f)for(d in b)new Effect.HighchartsTransition($(a), -d,b[d],c);else{if(a.attr)for(d in b)a.attr(d,b[d]);c.complete&&c.complete()}a.attr||$(a).setStyle(b)},stop:function(a){var b;if(a._highcharts_extended&&a._highchart_animation)for(b in a._highchart_animation)a._highchart_animation[b].cancel()},each:function(a,b){$A(a).each(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){return $(a).cumulativeOffset()},fireEvent:function(a,b,c,d){a.fire?a.fire(HighchartsAdapter.addNS(b),c):a._highcharts_extended&&(c=c||{},a._highcharts_fire(b, -c));c&&c.defaultPrevented&&(d=null);d&&d(c)},removeEvent:function(a,b,c){$(a).stopObserving&&(b&&(b=HighchartsAdapter.addNS(b)),$(a).stopObserving(b,c));window===a?Event.stopObserving(a,b,c):(HighchartsAdapter._extend(a),a._highcharts_stop_observing(b,c))},washMouseEvent:function(a){return a},grep:function(a,b){return a.findAll(b)},map:function(a,b){return a.map(b)},_extend:function(a){a._highcharts_extended||Object.extend(a,{_highchart_events:{},_highchart_animation:null,_highcharts_extended:!0, -_highcharts_observe:function(b,a){this._highchart_events[b]=[this._highchart_events[b],a].compact().flatten()},_highcharts_stop_observing:function(b,a){b?a?this._highchart_events[b]=[this._highchart_events[b]].compact().flatten().without(a):delete this._highchart_events[b]:this._highchart_events={}},_highcharts_fire:function(a,c){var d=this;(this._highchart_events[a]||[]).each(function(a){if(!c.stopped)c.preventDefault=function(){c.defaultPrevented=!0},c.target=d,a.bind(this)(c)===!1&&c.preventDefault()}.bind(this))}})}}}(); diff --git a/pykeg/web/static/highcharts/js/adapters/prototype-adapter.src.js b/pykeg/web/static/highcharts/js/adapters/prototype-adapter.src.js deleted file mode 100644 index 39cf39392..000000000 --- a/pykeg/web/static/highcharts/js/adapters/prototype-adapter.src.js +++ /dev/null @@ -1,316 +0,0 @@ -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * Prototype adapter - * - * @author Michael Nelson, Torstein Honsi. - * - * Feel free to use and modify this script. - * Highcharts license: www.highcharts.com/license. - */ - -// JSLint options: -/*global Effect, Class, Event, Element, $, $$, $A */ - -// Adapter interface between prototype and the Highcharts charting library -var HighchartsAdapter = (function () { - -var hasEffect = typeof Effect !== 'undefined'; - -return { - - /** - * Initialize the adapter. This is run once as Highcharts is first run. - * @param {Object} pathAnim The helper object to do animations across adapters. - */ - init: function (pathAnim) { - if (hasEffect) { - /** - * Animation for Highcharts SVG element wrappers only - * @param {Object} element - * @param {Object} attribute - * @param {Object} to - * @param {Object} options - */ - Effect.HighchartsTransition = Class.create(Effect.Base, { - initialize: function (element, attr, to, options) { - var from, - opts; - - this.element = element; - this.key = attr; - from = element.attr ? element.attr(attr) : $(element).getStyle(attr); - - // special treatment for paths - if (attr === 'd') { - this.paths = pathAnim.init( - element, - element.d, - to - ); - this.toD = to; - - - // fake values in order to read relative position as a float in update - from = 0; - to = 1; - } - - opts = Object.extend((options || {}), { - from: from, - to: to, - attribute: attr - }); - this.start(opts); - }, - setup: function () { - HighchartsAdapter._extend(this.element); - // If this is the first animation on this object, create the _highcharts_animation helper that - // contain pointers to the animation objects. - if (!this.element._highchart_animation) { - this.element._highchart_animation = {}; - } - - // Store a reference to this animation instance. - this.element._highchart_animation[this.key] = this; - }, - update: function (position) { - var paths = this.paths, - element = this.element, - obj; - - if (paths) { - position = pathAnim.step(paths[0], paths[1], position, this.toD); - } - - if (element.attr) { // SVGElement - - if (element.element) { // If not, it has been destroyed (#1405) - element.attr(this.options.attribute, position); - } - - } else { // HTML, #409 - obj = {}; - obj[this.options.attribute] = position; - $(element).setStyle(obj); - } - - }, - finish: function () { - // Delete the property that holds this animation now that it is finished. - // Both canceled animations and complete ones gets a 'finish' call. - if (this.element && this.element._highchart_animation) { // #1405 - delete this.element._highchart_animation[this.key]; - } - } - }); - } - }, - - /** - * Run a general method on the framework, following jQuery syntax - * @param {Object} el The HTML element - * @param {String} method Which method to run on the wrapped element - */ - adapterRun: function (el, method) { - - // This currently works for getting inner width and height. If adding - // more methods later, we need a conditional implementation for each. - return parseInt($(el).getStyle(method), 10); - - }, - - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript: function (scriptLocation, callback) { - var head = $$('head')[0]; // Returns an array, so pick the first element. - if (head) { - // Append a new 'script' element, set its type and src attributes, add a 'load' handler that calls the callback - head.appendChild(new Element('script', { type: 'text/javascript', src: scriptLocation}).observe('load', callback)); - } - }, - - /** - * Custom events in prototype needs to be namespaced. This method adds a namespace 'h:' in front of - * events that are not recognized as native. - */ - addNS: function (eventName) { - var HTMLEvents = /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, - MouseEvents = /^(?:click|mouse(?:down|up|over|move|out))$/; - return (HTMLEvents.test(eventName) || MouseEvents.test(eventName)) ? - eventName : - 'h:' + eventName; - }, - - // el needs an event to be attached. el is not necessarily a dom element - addEvent: function (el, event, fn) { - if (el.addEventListener || el.attachEvent) { - Event.observe($(el), HighchartsAdapter.addNS(event), fn); - - } else { - HighchartsAdapter._extend(el); - el._highcharts_observe(event, fn); - } - }, - - // motion makes things pretty. use it if effects is loaded, if not... still get to the end result. - animate: function (el, params, options) { - var key, - fx; - - // default options - options = options || {}; - options.delay = 0; - options.duration = (options.duration || 500) / 1000; - options.afterFinish = options.complete; - - // animate wrappers and DOM elements - if (hasEffect) { - for (key in params) { - // The fx variable is seemingly thrown away here, but the Effect.setup will add itself to the _highcharts_animation object - // on the element itself so its not really lost. - fx = new Effect.HighchartsTransition($(el), key, params[key], options); - } - } else { - if (el.attr) { // #409 without effects - for (key in params) { - el.attr(key, params[key]); - } - } - if (options.complete) { - options.complete(); - } - } - - if (!el.attr) { // HTML element, #409 - $(el).setStyle(params); - } - }, - - // this only occurs in higcharts 2.0+ - stop: function (el) { - var key; - if (el._highcharts_extended && el._highchart_animation) { - for (key in el._highchart_animation) { - // Cancel the animation - // The 'finish' function in the Effect object will remove the reference - el._highchart_animation[key].cancel(); - } - } - }, - - // um.. each - each: function (arr, fn) { - $A(arr).each(fn); - }, - - inArray: function (item, arr, from) { - return arr ? arr.indexOf(item, from) : -1; - }, - - /** - * Get the cumulative offset relative to the top left of the page. This method, unlike its - * jQuery and MooTools counterpart, still suffers from issue #208 regarding the position - * of a chart within a fixed container. - */ - offset: function (el) { - return $(el).cumulativeOffset(); - }, - - // fire an event based on an event name (event) and an object (el). - // again, el may not be a dom element - fireEvent: function (el, event, eventArguments, defaultFunction) { - if (el.fire) { - el.fire(HighchartsAdapter.addNS(event), eventArguments); - } else if (el._highcharts_extended) { - eventArguments = eventArguments || {}; - el._highcharts_fire(event, eventArguments); - } - - if (eventArguments && eventArguments.defaultPrevented) { - defaultFunction = null; - } - - if (defaultFunction) { - defaultFunction(eventArguments); - } - }, - - removeEvent: function (el, event, handler) { - if ($(el).stopObserving) { - if (event) { - event = HighchartsAdapter.addNS(event); - } - $(el).stopObserving(event, handler); - } if (window === el) { - Event.stopObserving(el, event, handler); - } else { - HighchartsAdapter._extend(el); - el._highcharts_stop_observing(event, handler); - } - }, - - washMouseEvent: function (e) { - return e; - }, - - // um, grep - grep: function (arr, fn) { - return arr.findAll(fn); - }, - - // um, map - map: function (arr, fn) { - return arr.map(fn); - }, - - // extend an object to handle highchart events (highchart objects, not svg elements). - // this is a very simple way of handling events but whatever, it works (i think) - _extend: function (object) { - if (!object._highcharts_extended) { - Object.extend(object, { - _highchart_events: {}, - _highchart_animation: null, - _highcharts_extended: true, - _highcharts_observe: function (name, fn) { - this._highchart_events[name] = [this._highchart_events[name], fn].compact().flatten(); - }, - _highcharts_stop_observing: function (name, fn) { - if (name) { - if (fn) { - this._highchart_events[name] = [this._highchart_events[name]].compact().flatten().without(fn); - } else { - delete this._highchart_events[name]; - } - } else { - this._highchart_events = {}; - } - }, - _highcharts_fire: function (name, args) { - var target = this; - (this._highchart_events[name] || []).each(function (fn) { - // args is never null here - if (args.stopped) { - return; // "throw $break" wasn't working. i think because of the scope of 'this'. - } - - // Attach a simple preventDefault function to skip default handler if called - args.preventDefault = function () { - args.defaultPrevented = true; - }; - args.target = target; - - // If the event handler return false, prevent the default handler from executing - if (fn.bind(this)(args) === false) { - args.preventDefault(); - } - } -.bind(this)); - } - }); - } - } -}; -}()); diff --git a/pykeg/web/static/highcharts/js/adapters/standalone-framework.js b/pykeg/web/static/highcharts/js/adapters/standalone-framework.js deleted file mode 100644 index 30a75c516..000000000 --- a/pykeg/web/static/highcharts/js/adapters/standalone-framework.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - - Standalone Highcharts Framework - - License: MIT License -*/ -var HighchartsAdapter=function(){function o(c){function b(b,a,d){b.removeEventListener(a,d,!1)}function d(b,a,d){d=b.HCProxiedMethods[d.toString()];b.detachEvent("on"+a,d)}function a(a,c){var f=a.HCEvents,i,g,k,j;if(a.removeEventListener)i=b;else if(a.attachEvent)i=d;else return;c?(g={},g[c]=!0):g=f;for(j in g)if(f[j])for(k=f[j].length;k--;)i(a,j,f[j][k])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(a,b){var d=this,c=this.HCEvents,g;if(d.addEventListener)d.addEventListener(a, -b,!1);else if(d.attachEvent){g=function(a){b.call(d,a)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[b.toString()]=g;d.attachEvent("on"+a,g)}c[a]===r&&(c[a]=[]);c[a].push(b)},unbind:function(c,h){var f,i;c?(f=this.HCEvents[c]||[],h?(i=HighchartsAdapter.inArray(h,f),i>-1&&(f.splice(i,1),this.HCEvents[c]=f),this.removeEventListener?b(this,c,h):this.attachEvent&&d(this,c,h)):(a(this,c),this.HCEvents[c]=[])):(a(this),this.HCEvents={})},trigger:function(a,b){var d=this.HCEvents[a]|| -[],c=d.length,g,k,j;k=function(){b.defaultPrevented=!0};for(g=0;g<c;g++){j=d[g];if(b.stopped)break;b.preventDefault=k;b.target=this;if(!b.type)b.type=a;j.call(this,b)===!1&&b.preventDefault()}}});return c}var r,l=document,p=[],m=[],q,n;Math.easeInOutSine=function(c,b,d,a){return-d/2*(Math.cos(Math.PI*c/a)-1)+b};return{init:function(c){if(!l.defaultView)this._getStyle=function(b,d){var a;return b.style[d]?b.style[d]:(d==="opacity"&&(d="filter"),a=b.currentStyle[d.replace(/\-(\w)/g,function(b,a){return a.toUpperCase()})], -d==="filter"&&(a=a.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),a===""?1:a)},this.adapterRun=function(b,d){var a={width:"clientWidth",height:"clientHeight"}[d];if(a)return b.style.zoom=1,b[a]-2*parseInt(HighchartsAdapter._getStyle(b,"padding"),10)};if(!Array.prototype.forEach)this.each=function(b,d){for(var a=0,c=b.length;a<c;a++)if(d.call(b[a],b[a],a,b)===!1)return a};if(!Array.prototype.indexOf)this.inArray=function(b,d){var a,c=0;if(d)for(a=d.length;c<a;c++)if(d[c]===b)return c; -return-1};if(!Array.prototype.filter)this.grep=function(b,d){for(var a=[],c=0,h=b.length;c<h;c++)d(b[c],c)&&a.push(b[c]);return a};n=function(b,c,a){this.options=c;this.elem=b;this.prop=a};n.prototype={update:function(){var b;b=this.paths;var d=this.elem,a=d.element;b&&a?d.attr("d",c.step(b[0],b[1],this.now,this.toD)):d.attr?a&&d.attr(this.prop,this.now):(b={},b[d]=this.now+this.unit,Highcharts.css(d,b));this.options.step&&this.options.step.call(this.elem,this.now,this)},custom:function(b,c,a){var e= -this,h=function(a){return e.step(a)},f;this.startTime=+new Date;this.start=b;this.end=c;this.unit=a;this.now=this.start;this.pos=this.state=0;h.elem=this.elem;h()&&m.push(h)===1&&(q=setInterval(function(){for(f=0;f<m.length;f++)m[f]()||m.splice(f--,1);m.length||clearInterval(q)},13))},step:function(b){var c=+new Date,a;a=this.options;var e;if(this.elem.stopAnimation)a=!1;else if(b||c>=a.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();b=this.options.curAnim[this.prop]= -!0;for(e in a.curAnim)a.curAnim[e]!==!0&&(b=!1);b&&a.complete&&a.complete.call(this.elem);a=!1}else e=c-this.startTime,this.state=e/a.duration,this.pos=a.easing(e,0,1,a.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),a=!0;return a}};this.animate=function(b,d,a){var e,h="",f,i,g;b.stopAnimation=!1;if(typeof a!=="object"||a===null)e=arguments,a={duration:e[2],easing:e[3],complete:e[4]};if(typeof a.duration!=="number")a.duration=400;a.easing=Math[a.easing]||Math.easeInOutSine; -a.curAnim=Highcharts.extend({},d);for(g in d)i=new n(b,a,g),f=null,g==="d"?(i.paths=c.init(b,b.d,d.d),i.toD=d.d,e=0,f=1):b.attr?e=b.attr(g):(e=parseFloat(HighchartsAdapter._getStyle(b,g))||0,g!=="opacity"&&(h="px")),f||(f=parseFloat(d[g])),i.custom(e,f,h)}},_getStyle:function(c,b){return window.getComputedStyle(c).getPropertyValue(b)},getScript:function(c,b){var d=l.getElementsByTagName("head")[0],a=l.createElement("script");a.type="text/javascript";a.src=c;a.onload=b;d.appendChild(a)},inArray:function(c, -b){return b.indexOf?b.indexOf(c):p.indexOf.call(b,c)},adapterRun:function(c,b){return parseInt(HighchartsAdapter._getStyle(c,b),10)},grep:function(c,b){return p.filter.call(c,b)},map:function(c,b){for(var d=[],a=0,e=c.length;a<e;a++)d[a]=b.call(c[a],c[a],a,c);return d},offset:function(c){for(var b=0,d=0;c;)b+=c.offsetLeft,d+=c.offsetTop,c=c.offsetParent;return{left:b,top:d}},addEvent:function(c,b,d){o(c).bind(b,d)},removeEvent:function(c,b,d){o(c).unbind(b,d)},fireEvent:function(c,b,d,a){var e;l.createEvent&& -(c.dispatchEvent||c.fireEvent)?(e=l.createEvent("Events"),e.initEvent(b,!0,!0),e.target=c,Highcharts.extend(e,d),c.dispatchEvent?c.dispatchEvent(e):c.fireEvent(b,e)):c.HCExtended===!0&&(d=d||{},c.trigger(b,d));d&&d.defaultPrevented&&(a=null);a&&a(d)},washMouseEvent:function(c){return c},stop:function(c){c.stopAnimation=!0},each:function(c,b){return Array.prototype.forEach.call(c,b)}}}(); diff --git a/pykeg/web/static/highcharts/js/adapters/standalone-framework.src.js b/pykeg/web/static/highcharts/js/adapters/standalone-framework.src.js deleted file mode 100644 index 9461d564b..000000000 --- a/pykeg/web/static/highcharts/js/adapters/standalone-framework.src.js +++ /dev/null @@ -1,591 +0,0 @@ -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * - * Standalone Highcharts Framework - * - * License: MIT License - */ - - -/*global Highcharts */ -var HighchartsAdapter = (function () { - -var UNDEFINED, - doc = document, - emptyArray = [], - timers = [], - timerId, - Fx; - -Math.easeInOutSine = function (t, b, c, d) { - return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; -}; - - - -/** - * Extend given object with custom events - */ -function augment(obj) { - function removeOneEvent(el, type, fn) { - el.removeEventListener(type, fn, false); - } - - function IERemoveOneEvent(el, type, fn) { - fn = el.HCProxiedMethods[fn.toString()]; - el.detachEvent('on' + type, fn); - } - - function removeAllEvents(el, type) { - var events = el.HCEvents, - remove, - types, - len, - n; - - if (el.removeEventListener) { - remove = removeOneEvent; - } else if (el.attachEvent) { - remove = IERemoveOneEvent; - } else { - return; // break on non-DOM events - } - - - if (type) { - types = {}; - types[type] = true; - } else { - types = events; - } - - for (n in types) { - if (events[n]) { - len = events[n].length; - while (len--) { - remove(el, n, events[n][len]); - } - } - } - } - - if (!obj.HCExtended) { - Highcharts.extend(obj, { - HCExtended: true, - - HCEvents: {}, - - bind: function (name, fn) { - var el = this, - events = this.HCEvents, - wrappedFn; - - // handle DOM events in modern browsers - if (el.addEventListener) { - el.addEventListener(name, fn, false); - - // handle old IE implementation - } else if (el.attachEvent) { - - wrappedFn = function (e) { - fn.call(el, e); - }; - - if (!el.HCProxiedMethods) { - el.HCProxiedMethods = {}; - } - - // link wrapped fn with original fn, so we can get this in removeEvent - el.HCProxiedMethods[fn.toString()] = wrappedFn; - - el.attachEvent('on' + name, wrappedFn); - } - - - if (events[name] === UNDEFINED) { - events[name] = []; - } - - events[name].push(fn); - }, - - unbind: function (name, fn) { - var events, - index; - - if (name) { - events = this.HCEvents[name] || []; - if (fn) { - index = HighchartsAdapter.inArray(fn, events); - if (index > -1) { - events.splice(index, 1); - this.HCEvents[name] = events; - } - if (this.removeEventListener) { - removeOneEvent(this, name, fn); - } else if (this.attachEvent) { - IERemoveOneEvent(this, name, fn); - } - } else { - removeAllEvents(this, name); - this.HCEvents[name] = []; - } - } else { - removeAllEvents(this); - this.HCEvents = {}; - } - }, - - trigger: function (name, args) { - var events = this.HCEvents[name] || [], - target = this, - len = events.length, - i, - preventDefault, - fn; - - // Attach a simple preventDefault function to skip default handler if called - preventDefault = function () { - args.defaultPrevented = true; - }; - - for (i = 0; i < len; i++) { - fn = events[i]; - - // args is never null here - if (args.stopped) { - return; - } - - args.preventDefault = preventDefault; - args.target = target; - - // If the type is not set, we're running a custom event (#2297). If it is set, - // we're running a browser event, and setting it will cause en error in - // IE8 (#2465). - if (!args.type) { - args.type = name; - } - - - - // If the event handler return false, prevent the default handler from executing - if (fn.call(this, args) === false) { - args.preventDefault(); - } - } - } - }); - } - - return obj; -} - - -return { - /** - * Initialize the adapter. This is run once as Highcharts is first run. - */ - init: function (pathAnim) { - - /** - * Compatibility section to add support for legacy IE. This can be removed if old IE - * support is not needed. - */ - if (!doc.defaultView) { - this._getStyle = function (el, prop) { - var val; - if (el.style[prop]) { - return el.style[prop]; - } else { - if (prop === 'opacity') { - prop = 'filter'; - } - /*jslint unparam: true*/ - val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b) { return b.toUpperCase(); })]; - if (prop === 'filter') { - val = val.replace( - /alpha\(opacity=([0-9]+)\)/, - function (a, b) { - return b / 100; - } - ); - } - /*jslint unparam: false*/ - return val === '' ? 1 : val; - } - }; - this.adapterRun = function (elem, method) { - var alias = { width: 'clientWidth', height: 'clientHeight' }[method]; - - if (alias) { - elem.style.zoom = 1; - return elem[alias] - 2 * parseInt(HighchartsAdapter._getStyle(elem, 'padding'), 10); - } - }; - } - - if (!Array.prototype.forEach) { - this.each = function (arr, fn) { // legacy - var i = 0, - len = arr.length; - for (; i < len; i++) { - if (fn.call(arr[i], arr[i], i, arr) === false) { - return i; - } - } - }; - } - - if (!Array.prototype.indexOf) { - this.inArray = function (item, arr) { - var len, - i = 0; - - if (arr) { - len = arr.length; - - for (; i < len; i++) { - if (arr[i] === item) { - return i; - } - } - } - - return -1; - }; - } - - if (!Array.prototype.filter) { - this.grep = function (elements, callback) { - var ret = [], - i = 0, - length = elements.length; - - for (; i < length; i++) { - if (!!callback(elements[i], i)) { - ret.push(elements[i]); - } - } - - return ret; - }; - } - - //--- End compatibility section --- - - - /** - * Start of animation specific code - */ - Fx = function (elem, options, prop) { - this.options = options; - this.elem = elem; - this.prop = prop; - }; - Fx.prototype = { - - update: function () { - var styles, - paths = this.paths, - elem = this.elem, - elemelem = elem.element; // if destroyed, it is null - - // Animating a path definition on SVGElement - if (paths && elemelem) { - elem.attr('d', pathAnim.step(paths[0], paths[1], this.now, this.toD)); - - // Other animations on SVGElement - } else if (elem.attr) { - if (elemelem) { - elem.attr(this.prop, this.now); - } - - // HTML styles - } else { - styles = {}; - styles[elem] = this.now + this.unit; - Highcharts.css(elem, styles); - } - - if (this.options.step) { - this.options.step.call(this.elem, this.now, this); - } - - }, - custom: function (from, to, unit) { - var self = this, - t = function (gotoEnd) { - return self.step(gotoEnd); - }, - i; - - this.startTime = +new Date(); - this.start = from; - this.end = to; - this.unit = unit; - this.now = this.start; - this.pos = this.state = 0; - - t.elem = this.elem; - - if (t() && timers.push(t) === 1) { - timerId = setInterval(function () { - - for (i = 0; i < timers.length; i++) { - if (!timers[i]()) { - timers.splice(i--, 1); - } - } - - if (!timers.length) { - clearInterval(timerId); - } - }, 13); - } - }, - - step: function (gotoEnd) { - var t = +new Date(), - ret, - done, - options = this.options, - i; - - if (this.elem.stopAnimation) { - ret = false; - - } else if (gotoEnd || t >= options.duration + this.startTime) { - this.now = this.end; - this.pos = this.state = 1; - this.update(); - - this.options.curAnim[this.prop] = true; - - done = true; - for (i in options.curAnim) { - if (options.curAnim[i] !== true) { - done = false; - } - } - - if (done) { - if (options.complete) { - options.complete.call(this.elem); - } - } - ret = false; - - } else { - var n = t - this.startTime; - this.state = n / options.duration; - this.pos = options.easing(n, 0, 1, options.duration); - this.now = this.start + ((this.end - this.start) * this.pos); - this.update(); - ret = true; - } - return ret; - } - }; - - /** - * The adapter animate method - */ - this.animate = function (el, prop, opt) { - var start, - unit = '', - end, - fx, - args, - name; - - el.stopAnimation = false; // ready for new - - if (typeof opt !== 'object' || opt === null) { - args = arguments; - opt = { - duration: args[2], - easing: args[3], - complete: args[4] - }; - } - if (typeof opt.duration !== 'number') { - opt.duration = 400; - } - opt.easing = Math[opt.easing] || Math.easeInOutSine; - opt.curAnim = Highcharts.extend({}, prop); - - for (name in prop) { - fx = new Fx(el, opt, name); - end = null; - - if (name === 'd') { - fx.paths = pathAnim.init( - el, - el.d, - prop.d - ); - fx.toD = prop.d; - start = 0; - end = 1; - } else if (el.attr) { - start = el.attr(name); - } else { - start = parseFloat(HighchartsAdapter._getStyle(el, name)) || 0; - if (name !== 'opacity') { - unit = 'px'; - } - } - - if (!end) { - end = parseFloat(prop[name]); - } - fx.custom(start, end, unit); - } - }; - }, - - /** - * Internal method to return CSS value for given element and property - */ - _getStyle: function (el, prop) { - return window.getComputedStyle(el).getPropertyValue(prop); - }, - - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript: function (scriptLocation, callback) { - // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script. - var head = doc.getElementsByTagName('head')[0], - script = doc.createElement('script'); - - script.type = 'text/javascript'; - script.src = scriptLocation; - script.onload = callback; - - head.appendChild(script); - }, - - /** - * Return the index of an item in an array, or -1 if not found - */ - inArray: function (item, arr) { - return arr.indexOf ? arr.indexOf(item) : emptyArray.indexOf.call(arr, item); - }, - - - /** - * A direct link to adapter methods - */ - adapterRun: function (elem, method) { - return parseInt(HighchartsAdapter._getStyle(elem, method), 10); - }, - - /** - * Filter an array - */ - grep: function (elements, callback) { - return emptyArray.filter.call(elements, callback); - }, - - /** - * Map an array - */ - map: function (arr, fn) { - var results = [], i = 0, len = arr.length; - - for (; i < len; i++) { - results[i] = fn.call(arr[i], arr[i], i, arr); - } - - return results; - }, - - offset: function (el) { - var left = 0, - top = 0; - - while (el) { - left += el.offsetLeft; - top += el.offsetTop; - el = el.offsetParent; - } - - return { - left: left, - top: top - }; - }, - - /** - * Add an event listener - */ - addEvent: function (el, type, fn) { - augment(el).bind(type, fn); - }, - - /** - * Remove event added with addEvent - */ - removeEvent: function (el, type, fn) { - augment(el).unbind(type, fn); - }, - - /** - * Fire an event on a custom object - */ - fireEvent: function (el, type, eventArguments, defaultFunction) { - var e; - - if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) { - e = doc.createEvent('Events'); - e.initEvent(type, true, true); - e.target = el; - - Highcharts.extend(e, eventArguments); - - if (el.dispatchEvent) { - el.dispatchEvent(e); - } else { - el.fireEvent(type, e); - } - - } else if (el.HCExtended === true) { - eventArguments = eventArguments || {}; - el.trigger(type, eventArguments); - } - - if (eventArguments && eventArguments.defaultPrevented) { - defaultFunction = null; - } - - if (defaultFunction) { - defaultFunction(eventArguments); - } - }, - - washMouseEvent: function (e) { - return e; - }, - - - /** - * Stop running animation - */ - stop: function (el) { - el.stopAnimation = true; - }, - - /** - * Utility for iterating over an array. Parameters are reversed compared to jQuery. - * @param {Array} arr - * @param {Function} fn - */ - each: function (arr, fn) { // modern browsers - return Array.prototype.forEach.call(arr, fn); - } -}; -}()); diff --git a/pykeg/web/static/highcharts/js/highcharts-all.js b/pykeg/web/static/highcharts/js/highcharts-all.js deleted file mode 100644 index 95bf1142b..000000000 --- a/pykeg/web/static/highcharts/js/highcharts-all.js +++ /dev/null @@ -1,400 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - - Standalone Highcharts Framework - - License: MIT License -*/ -var HighchartsAdapter=function(){function o(c){function b(b,a,d){b.removeEventListener(a,d,!1)}function d(b,a,d){d=b.HCProxiedMethods[d.toString()];b.detachEvent("on"+a,d)}function a(a,c){var f=a.HCEvents,i,g,k,j;if(a.removeEventListener)i=b;else if(a.attachEvent)i=d;else return;c?(g={},g[c]=!0):g=f;for(j in g)if(f[j])for(k=f[j].length;k--;)i(a,j,f[j][k])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(a,b){var d=this,c=this.HCEvents,g;if(d.addEventListener)d.addEventListener(a, -b,!1);else if(d.attachEvent){g=function(a){b.call(d,a)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[b.toString()]=g;d.attachEvent("on"+a,g)}c[a]===r&&(c[a]=[]);c[a].push(b)},unbind:function(c,h){var f,i;c?(f=this.HCEvents[c]||[],h?(i=HighchartsAdapter.inArray(h,f),i>-1&&(f.splice(i,1),this.HCEvents[c]=f),this.removeEventListener?b(this,c,h):this.attachEvent&&d(this,c,h)):(a(this,c),this.HCEvents[c]=[])):(a(this),this.HCEvents={})},trigger:function(a,b){var d=this.HCEvents[a]|| -[],c=d.length,g,k,j;k=function(){b.defaultPrevented=!0};for(g=0;g<c;g++){j=d[g];if(b.stopped)break;b.preventDefault=k;b.target=this;if(!b.type)b.type=a;j.call(this,b)===!1&&b.preventDefault()}}});return c}var r,l=document,p=[],m=[],q,n;Math.easeInOutSine=function(c,b,d,a){return-d/2*(Math.cos(Math.PI*c/a)-1)+b};return{init:function(c){if(!l.defaultView)this._getStyle=function(b,d){var a;return b.style[d]?b.style[d]:(d==="opacity"&&(d="filter"),a=b.currentStyle[d.replace(/\-(\w)/g,function(b,a){return a.toUpperCase()})], -d==="filter"&&(a=a.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),a===""?1:a)},this.adapterRun=function(b,d){var a={width:"clientWidth",height:"clientHeight"}[d];if(a)return b.style.zoom=1,b[a]-2*parseInt(HighchartsAdapter._getStyle(b,"padding"),10)};if(!Array.prototype.forEach)this.each=function(b,d){for(var a=0,c=b.length;a<c;a++)if(d.call(b[a],b[a],a,b)===!1)return a};if(!Array.prototype.indexOf)this.inArray=function(b,d){var a,c=0;if(d)for(a=d.length;c<a;c++)if(d[c]===b)return c; -return-1};if(!Array.prototype.filter)this.grep=function(b,d){for(var a=[],c=0,h=b.length;c<h;c++)d(b[c],c)&&a.push(b[c]);return a};n=function(b,c,a){this.options=c;this.elem=b;this.prop=a};n.prototype={update:function(){var b;b=this.paths;var d=this.elem,a=d.element;b&&a?d.attr("d",c.step(b[0],b[1],this.now,this.toD)):d.attr?a&&d.attr(this.prop,this.now):(b={},b[d]=this.now+this.unit,Highcharts.css(d,b));this.options.step&&this.options.step.call(this.elem,this.now,this)},custom:function(b,c,a){var e= -this,h=function(a){return e.step(a)},f;this.startTime=+new Date;this.start=b;this.end=c;this.unit=a;this.now=this.start;this.pos=this.state=0;h.elem=this.elem;h()&&m.push(h)===1&&(q=setInterval(function(){for(f=0;f<m.length;f++)m[f]()||m.splice(f--,1);m.length||clearInterval(q)},13))},step:function(b){var c=+new Date,a;a=this.options;var e;if(this.elem.stopAnimation)a=!1;else if(b||c>=a.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();b=this.options.curAnim[this.prop]= -!0;for(e in a.curAnim)a.curAnim[e]!==!0&&(b=!1);b&&a.complete&&a.complete.call(this.elem);a=!1}else e=c-this.startTime,this.state=e/a.duration,this.pos=a.easing(e,0,1,a.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),a=!0;return a}};this.animate=function(b,d,a){var e,h="",f,i,g;b.stopAnimation=!1;if(typeof a!=="object"||a===null)e=arguments,a={duration:e[2],easing:e[3],complete:e[4]};if(typeof a.duration!=="number")a.duration=400;a.easing=Math[a.easing]||Math.easeInOutSine; -a.curAnim=Highcharts.extend({},d);for(g in d)i=new n(b,a,g),f=null,g==="d"?(i.paths=c.init(b,b.d,d.d),i.toD=d.d,e=0,f=1):b.attr?e=b.attr(g):(e=parseFloat(HighchartsAdapter._getStyle(b,g))||0,g!=="opacity"&&(h="px")),f||(f=parseFloat(d[g])),i.custom(e,f,h)}},_getStyle:function(c,b){return window.getComputedStyle(c).getPropertyValue(b)},getScript:function(c,b){var d=l.getElementsByTagName("head")[0],a=l.createElement("script");a.type="text/javascript";a.src=c;a.onload=b;d.appendChild(a)},inArray:function(c, -b){return b.indexOf?b.indexOf(c):p.indexOf.call(b,c)},adapterRun:function(c,b){return parseInt(HighchartsAdapter._getStyle(c,b),10)},grep:function(c,b){return p.filter.call(c,b)},map:function(c,b){for(var d=[],a=0,e=c.length;a<e;a++)d[a]=b.call(c[a],c[a],a,c);return d},offset:function(c){for(var b=0,d=0;c;)b+=c.offsetLeft,d+=c.offsetTop,c=c.offsetParent;return{left:b,top:d}},addEvent:function(c,b,d){o(c).bind(b,d)},removeEvent:function(c,b,d){o(c).unbind(b,d)},fireEvent:function(c,b,d,a){var e;l.createEvent&& -(c.dispatchEvent||c.fireEvent)?(e=l.createEvent("Events"),e.initEvent(b,!0,!0),e.target=c,Highcharts.extend(e,d),c.dispatchEvent?c.dispatchEvent(e):c.fireEvent(b,e)):c.HCExtended===!0&&(d=d||{},c.trigger(b,d));d&&d.defaultPrevented&&(a=null);a&&a(d)},washMouseEvent:function(c){return c},stop:function(c){c.stopAnimation=!0},each:function(c,b){return Array.prototype.forEach.call(c,b)}}}(); -/* - Highcharts JS v3.0.9 (2014-01-15) - - (c) 2009-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(){function r(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function x(){var a,b=arguments,c,d={},e=function(a,b){var c,d;typeof a!=="object"&&(a={});for(d in b)b.hasOwnProperty(d)&&(c=b[d],a[d]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?e(a[d]||{},c):b[d]);return a};b[0]===!0&&(d=b[1],b=Array.prototype.slice.call(b,2));c=b.length;for(a=0;a<c;a++)d=e(d,b[a]);return d}function z(a,b){return parseInt(a,b||10)}function fa(a){return typeof a=== -"string"}function S(a){return typeof a==="object"}function Ka(a){return Object.prototype.toString.call(a)==="[object Array]"}function wa(a){return typeof a==="number"}function xa(a){return P.log(a)/P.LN10}function ga(a){return P.pow(10,a)}function ha(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function t(a){return a!==u&&a!==null}function v(a,b,c){var d,e;if(fa(b))t(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(t(b)&&S(b))for(d in b)a.setAttribute(d,b[d]); -return e}function ja(a){return Ka(a)?a:[a]}function n(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function D(a,b){if(ya&&b&&b.opacity!==u)b.filter="alpha(opacity="+b.opacity*100+")";r(a.style,b)}function T(a,b,c,d,e){a=y.createElement(a);b&&r(a,b);e&&D(a,{padding:0,border:Q,margin:0});c&&D(a,c);d&&d.appendChild(a);return a}function ia(a,b){var c=function(){};c.prototype=new a;r(c.prototype,b);return c}function Da(a,b,c,d){var e=G.lang,a=+a|| -0,f=b===-1?(a.toString().split(".")[1]||"").length:isNaN(b=M(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(z(a=M(a).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+M(a-c).toFixed(f).slice(2):"")}function Ea(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function Va(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);a.unshift(d);return c.apply(this, -a)}}function Fa(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=G.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e=Da(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:"")):e=ab(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function mb(a){return P.pow(10,N(P.log(a)/P.LN10))}function nb(a,b,c,d){var e,c=n(c, -1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Ab(){this.symbol=this.color=0}function ob(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}function La(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function za(a){for(var b=a.length,c=a[0];b--;)a[b]>c&&(c= -a[b]);return c}function Ma(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Na(a){bb||(bb=T(Ga));a&&bb.appendChild(a);bb.innerHTML=""}function ka(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else C.console&&console.log(c)}function aa(a){return parseFloat(a.toPrecision(14))}function Oa(a,b){oa=n(a,b.animation)}function Bb(){var a=G.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Pa=(a&&G.global.timezoneOffset||0)*6E4;cb=a? -Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,n(c,1),n(g,0),n(h,0),n(i,0))).getTime()};pb=b+"Minutes";qb=b+"Hours";rb=b+"Day";Wa=b+"Date";db=b+"Month";eb=b+"FullYear";Cb=c+"Minutes";Db=c+"Hours";sb=c+"Date";Eb=c+"Month";Fb=c+"FullYear"}function pa(){}function Qa(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function qa(){this.init.apply(this,arguments)}function Gb(a,b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d; -this.total=null;this.points={};this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:n(b.y,g?4:c?14:-6),x:n(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function tb(){this.init.apply(this,arguments)}function fb(){this.init.apply(this,arguments)}var u,y=document,C=window,P=Math,w=P.round,N=P.floor,Ha=P.ceil,s=P.max,I=P.min,M=P.abs,U=P.cos,ba=P.sin,Aa=P.PI,Ba= -Aa*2/360,ra=navigator.userAgent,Hb=C.opera,ya=/msie/i.test(ra)&&!Hb,gb=y.documentMode===8,hb=/AppleWebKit/.test(ra),Xa=/Firefox/.test(ra),Ib=/(Mobile|Android|Windows Phone)/.test(ra),Ca="http://www.w3.org/2000/svg",V=!!y.createElementNS&&!!y.createElementNS(Ca,"svg").createSVGRect,Nb=Xa&&parseInt(ra.split("Firefox/")[1],10)<4,da=!V&&!ya&&!!y.createElement("canvas").getContext,Ya,ib=y.documentElement.ontouchstart!==u,Jb={},ub=0,bb,G,ab,oa,vb,E,la=function(){},Ia=[],Ga="div",Q="none",Ob=/^[0-9]+$/, -Kb="rgba(192,192,192,"+(V?1.0E-4:0.002)+")",Lb="stroke-width",cb,Pa,pb,qb,rb,Wa,db,eb,Cb,Db,sb,Eb,Fb,L={};C.Highcharts=C.Highcharts?ka(16,!0):{};ab=function(a,b,c){if(!t(b)||isNaN(b))return"Invalid date";var a=n(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b-Pa),e,f=d[qb](),g=d[rb](),h=d[Wa](),i=d[db](),j=d[eb](),k=G.lang,l=k.weekdays,d=r({a:l[g].substr(0,3),A:l[g],d:Ea(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ea(i+1),y:j.toString().substr(2,2),Y:j,H:Ea(f),I:Ea(f%12||12),l:f%12||12,M:Ea(d[pb]()),p:f<12?"AM": -"PM",P:f<12?"am":"pm",S:Ea(d.getSeconds()),L:Ea(w(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Ab.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};E=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}("millisecond",1,"second",1E3,"minute",6E4,"hour",36E5,"day", -864E5,"week",6048E5,"month",26784E5,"year",31556952E3);vb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f&&b.length===c.length)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f, -f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){C.HighchartsAdapter=C.HighchartsAdapter||a&&{init:function(b){var c=a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur", -"_default","width","height","opacity"],function(a,b){var e=d,k;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){var d,c=a?c:this;if(c.prop!=="align")return d=c.elem,d.attr?d.attr(c.prop,b==="cur"?u:c.now):k.apply(this,arguments)})});Va(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}: -d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==u)c.chart=c.chart||{},c.chart.renderTo=this[0],new Highcharts[a](c,b[1]),d=this;c===u&&(d=Ia[v(this[0],"data-highcharts-chart")]);return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b, -c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=y.removeEventListener?"removeEventListener":"detachEvent";y[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,c,d,e){var f=a.Event(c),g="detached"+c,h;!ya&&d&&(delete d.layerX,delete d.layerY);r(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault", -"stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===u)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style={};if(c.d)b.toD=c.d,c.d=1;e.stop();c.opacity!==u&&b.attr&&(c.opacity+="px");e.animate(c,d)},stop:function(b){a(b).stop()}}})(C.jQuery);var W= -C.HighchartsAdapter,J=W||{};W&&W.init.call(W,vb);var jb=J.adapterRun,Pb=J.getScript,sa=J.inArray,p=J.each,wb=J.grep,Qb=J.offset,Ra=J.map,F=J.addEvent,X=J.removeEvent,A=J.fireEvent,Rb=J.washMouseEvent,kb=J.animate,Za=J.stop,J={enabled:!0,x:0,y:15,style:{color:"#666",cursor:"default",fontSize:"11px"}};G={colors:"#2f7ed8,#0d233a,#8bbc21,#910000,#1aadce,#492970,#f28f43,#77a1e5,#c42525,#a6c96a".split(","),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","), -shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,canvasToolsURL:"http://code.highcharts.com/3.0.9/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/3.0.9/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:5, -defaultSeriesType:"line",ignoreHiddenSeries:!0,spacing:[10,10,15,10],style:{fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif',fontSize:"12px"},backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",margin:15,style:{color:"#274b6d",fontSize:"16px"}},subtitle:{text:"",align:"center",style:{color:"#4d759e"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1, -animation:{duration:1E3},events:{},lineWidth:2,marker:{enabled:!0,lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:x(J,{align:"center",enabled:!1,formatter:function(){return this.y===null?"":Da(this.y,-1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,states:{hover:{marker:{}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3}},labels:{style:{position:"absolute",color:"#3E576F"}}, -legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderWidth:1,borderColor:"#909090",borderRadius:5,navigation:{activeColor:"#274b6d",inactiveColor:"#CCC"},shadow:!1,itemStyle:{cursor:"pointer",color:"#274b6d",fontSize:"12px"},itemHoverStyle:{color:"#000"},itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold", -position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:V,backgroundColor:"rgba(255, 255, 255, .85)",borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>', -pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',shadow:!0,snap:Ib?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var Y=G.plotOptions,W=Y.line;Bb();var Sb=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/, -Tb=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,Ub=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,ta=function(a){var b=[],c,d;(function(a){a&&a.stops?d=Ra(a.stops,function(a){return ta(a[1])}):(c=Sb.exec(a))?b=[z(c[1]),z(c[2]),z(c[3]),parseFloat(c[4],10)]:(c=Tb.exec(a))?b=[z(c[1],16),z(c[2],16),z(c[3],16),1]:(c=Ub.exec(a))&&(b=[z(c[1]),z(c[2]),z(c[3]),1])})(a);return{get:function(c){var f;d?(f=x(a),f.stops=[].concat(f.stops),p(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})): -f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)p(d,function(b){b.brighten(a)});else if(wa(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=z(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};pa.prototype={init:function(a,b){this.element=b==="span"?T(b):y.createElementNS(Ca,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=n(b,oa,!0); -Za(this);if(b){b=x(b);if(c)b.complete=c;kb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,l=this.shadows,m,q,o=this;fa(a)&&t(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),o=v(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&c!=="fill"&&(o=parseFloat(o));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==u&&(d=e);if(c==="d")d&& -d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;e<g.childNodes.length;e++)f=g.childNodes[e],v(f,"x")===v(g,"x")&&v(f,"x",d);else if(this.rotation&&(c==="x"||c==="y"))q=!0;else if(c==="fill")d=i.color(d,g,c);else if(h==="circle"&&(c==="x"||c==="y"))c={x:"cx",y:"cy"}[c]||c;else if(h==="rect"&&c==="r")v(g,{rx:d,ry:d}),j=!0;else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="verticalAlign"||c==="scaleX"||c==="scaleY")j=q=!0;else if(c==="stroke")d= -i.color(d,g,c);else if(c==="dashstyle")if(c="stroke-dasharray",d=d&&d.toLowerCase(),d==="solid")d=Q;else{if(d){d=d.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=d.length;e--;)d[e]=z(d[e])*n(a["stroke-width"],this["stroke-width"]);d=d.join(",")}}else if(c==="width")d=z(d);else if(c==="align")c="text-anchor",d= -{left:"start",center:"middle",right:"end"}[d];else if(c==="title")e=g.getElementsByTagName("title")[0],e||(e=y.createElementNS(Ca,"title"),g.appendChild(e)),e.textContent=d;c==="strokeWidth"&&(c="stroke-width");if(c==="stroke-width"||c==="stroke"){this[c]=d;if(this.stroke&&this["stroke-width"])v(g,"stroke",this.stroke),v(g,"stroke-width",this["stroke-width"]),this.hasStroke=!0;else if(c==="stroke-width"&&d===0&&this.hasStroke)g.removeAttribute("stroke"),this.hasStroke=!1;j=!0}this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&& -(m||(this.symbolAttr(a),m=!0),j=!0);if(l&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(c))for(e=l.length;e--;)v(l[e],c,c==="height"?s(d-(l[e].cutHeight||0),0):d);if((c==="width"||c==="height")&&h==="rect"&&d<0)d=0;this[c]=d;c==="text"?(d!==this.textStr&&delete this.bBox,this.textStr=d,this.added&&i.buildText(this)):j||v(g,c,d)}q&&this.updateTransform()}return o},addClass:function(a){var b=this.element,c=v(b,"class")||"";c.indexOf(a)===-1&&v(b,"class",c+" "+a);return this},symbolAttr:function(a){var b= -this;p("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=n(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+a.id+")":Q)},crisp:function(a,b,c,d,e){var f,g={},h={},i,a=a||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;i=w(a)%2/2;h.x=N(b||this.x||0)+i;h.y=N(c||this.y||0)+i;h.width=N((d||this.width||0)-2*i);h.height=N((e||this.height||0)-2*i);h.strokeWidth= -a;for(f in h)this[f]!==h[f]&&(this[f]=g[f]=h[f]);return g},css:function(a){var b=this.element,c=this.textWidth=a&&a.width&&b.nodeName.toLowerCase()==="text"&&z(a.width),d,e="",f=function(a,b){return"-"+b.toLowerCase()};if(a&&a.color)a.fill=a.color;this.styles=a=r(this.styles,a);c&&delete a.width;if(ya&&!V)D(this.element,a);else{for(d in a)e+=d.replace(/([A-Z])/g,f)+":"+a[d]+";";v(b,"style",e)}c&&this.added&&this.renderer.buildText(this);return this},on:function(a,b){var c=this,d=c.element;ib&&a=== -"click"?(d.ontouchstart=function(a){c.touchEventFired=Date.now();a.preventDefault();b.call(d,a)},d.onclick=function(a){(ra.indexOf("Android")===-1||Date.now()-(c.touchEventFired||0)>1100)&&b.call(d,a)}):d["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY|| -0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")");(t(c)||t(d))&&a.push("scale("+n(c,1)+" "+n(d,1)+")");a.length&&v(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions= -a,this.alignByTranslate=b,!c||fa(c))this.alignTo=d=c||"renderer",ha(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=n(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=w(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=w(g);this[this.placed?"animate":"attr"](h);this.placed= -!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d,e=this.rotation;c=this.element;var f=this.styles,g=e*Ba;d=this.textStr;var h;if(d===""||Ob.test(d))h=d.length+"|"+f.fontSize+"|"+f.fontFamily,a=b.cache[h];if(!a){if(c.namespaceURI===Ca||b.forExport){try{a=c.getBBox?r({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(i){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){c=a.width;d=a.height;if(ya&&f&&f.fontSize==="11px"&& -d.toPrecision(3)==="16.9")a.height=d=14;if(e)a.width=M(d*ba(g))+M(c*U(g)),a.height=M(d*U(g))+M(c*ba(g))}this.bBox=a;h&&(b.cache[h]=a)}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=v(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted= -a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=z(g);if(c.handleZ)for(c=0;c<e.length;c++)if(a=e[c],b=v(a,"zIndex"),a!==f&&(z(b)>g||!t(g)&&t(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;A(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.renderer.isSVG&&b.nodeName==="SPAN"&&a.parentGroup,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point= -null;Za(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f<a.stops.length;f++)a.stops[f]=a.stops[f].destroy();a.stops=null}a.safeRemoveChild(b);for(c&&p(c,function(b){a.safeRemoveChild(b)});d&&d.div.childNodes.length===0;)b=d.parentGroup,a.safeRemoveChild(d.div),delete d.div,d=b;a.alignTo&&ha(a.renderer.alignedObjects,a);for(e in a)delete a[e];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=n(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted? -"(-1,-1)":"("+n(a.offsetX,1)+", "+n(a.offsetY,1)+")";for(e=1;e<=i;e++){f=g.cloneNode(0);h=i*2+1-2*e;v(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:Q});if(c)v(f,"height",s(v(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this}};var ua=function(){this.init.apply(this,arguments)};ua.prototype={Element:pa,init:function(a,b,c,d){var e=location,f,g;f=this.createElement("svg").attr({version:"1.1"}); -g=f.element;a.appendChild(g);a.innerHTML.indexOf("xmlns")===-1&&v(g,"xmlns",Ca);this.isSVG=!0;this.box=g;this.boxWrapper=f;this.alignedObjects=[];this.url=(Xa||hb)&&y.getElementsByTagName("base").length?e.href.replace(/#.*?$/,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(y.createTextNode("Created with Highcharts 3.0.9"));this.defs=this.createElement("defs").add();this.forExport=d;this.gradients={};this.cache={};this.setSize(b,c,!1);var h; -if(Xa&&a.getBoundingClientRect)this.subPixelFix=b=function(){D(a,{left:0,top:0});h=a.getBoundingClientRect();D(a,{left:Ha(h.left)-h.left+"px",top:Ha(h.top)-h.top+"px"})},b(),F(C,"resize",b)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Ma(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&X(C,"resize",this.subPixelFix);return this.alignedObjects=null},createElement:function(a){var b= -new this.Element;b.init(this,a);return b},draw:function(){},buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=n(a.textStr,"").toString().replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="(http[^"]+)"/,i=v(b,"x"),j=a.styles,k=a.textWidth,l=j&&j.lineHeight,m=f.length,q=function(a){return l?z(l): -c.fontMetrics(/px$/.test(a&&a.style.fontSize)?a.style.fontSize:j.fontSize||11).h};m--;)b.removeChild(f[m]);k&&!a.added&&this.box.appendChild(b);e[e.length-1]===""&&e.pop();p(e,function(e,f){var l,m=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");l=e.split("|||");p(l,function(e){if(e!==""||l.length===1){var o={},n=y.createElementNS(Ca,"tspan"),p;g.test(e)&&(p=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),v(n,"style",p));h.test(e)&&!d&&(v(n,"onclick",'location.href="'+ -e.match(h)[1]+'"'),D(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");if(e!==" "&&(n.appendChild(y.createTextNode(e)),m?o.dx=0:o.x=i,v(n,o),!m&&f&&(!V&&d&&D(n,{display:"block"}),v(n,"dy",q(n),hb&&n.offsetHeight)),b.appendChild(n),m++,k))for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),o=e.length>1&&j.whiteSpace!=="nowrap",t,s,w=a._clipHeight,u=[],r=q(),$=1;o&&(e.length||u.length);)delete a.bBox,t=a.getBBox(),s=t.width,!V&&c.forExport&&(s=c.measureSpanWidth(n.firstChild.data, -a.styles)),t=s>k,!t||e.length===1?(e=u,u=[],e.length&&($++,w&&$*r>w?(e=["..."],a.attr("title",a.textStr)):(n=y.createElementNS(Ca,"tspan"),v(n,{dy:r,x:i}),p&&v(n,"style",p),b.appendChild(n),s>k&&(k=s)))):(n.removeChild(n.firstChild),u.unshift(e.pop())),e.length&&n.appendChild(y.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g,h,i){var j=this.label(a,b,c,i,null,null,null,null,"button"),k=0,l,m,q,o,n,p,a={x1:0,y1:0,x2:0,y2:1},e=x({"stroke-width":1,stroke:"#CCCCCC", -fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);q=e.style;delete e.style;f=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);o=f.style;delete f.style;g=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);n=g.style;delete g.style;h=x(e,{style:{color:"#CCC"}},h);p=h.style;delete h.style;F(j.element,ya?"mouseover":"mouseenter",function(){k!==3&&j.attr(f).css(o)});F(j.element,ya?"mouseout":"mouseleave", -function(){k!==3&&(l=[e,f,g][k],m=[q,o,n][k],j.attr(l).css(m))});j.setState=function(a){(j.state=k=a)?a===2?j.attr(g).css(n):a===3&&j.attr(h).css(p):j.attr(e).css(q)};return j.on("click",function(){k!==3&&d.call(j)}).attr(e).css(r({cursor:"default"},q))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=w(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=w(a[2])+b%2/2);return a},path:function(a){var b={fill:Q};Ka(a)?b.d=a:S(a)&&r(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=S(a)?a:{x:a, -y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(S(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;a=this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0});a.r=c;return a},rect:function(a,b,c,d,e,f){e=S(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:Q});return e.attr(S(a)?a:e.crisp(f,a,b,s(c,0),s(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[n(c,!0)?"animate":"attr"]({width:a, -height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return t(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:Q};arguments.length>1&&r(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(w(b),w(c),d,e,f),i=/^url\((.*?)\)$/, -j,k;if(h)g=this.path(h),r(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&r(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(w((d-b[0])/2),w((e-b[1])/2)))},j=a.match(i)[1],a=Jb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),T("img",{onload:function(){k(g,Jb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/ -2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=U(f),j=ba(f),k=U(g),g=ba(g),e=e.end-f<Aa?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e, -1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]}},clipRect:function(a,b,c,d){var e="highcharts-"+ub++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},color:function(a,b,c){var d=this,e,f=/^rgba/,g,h,i,j,k,l,m,q=[];a&&a.linearGradient?g="linearGradient":a&&a.radialGradient&&(g="radialGradient");if(g){c=a[g];h=d.gradients;j=a.stops;b=b.radialReference;Ka(c)&&(a[g]=c={x1:c[0],y1:c[1],x2:c[2],y2:c[3],gradientUnits:"userSpaceOnUse"}); -g==="radialGradient"&&b&&!t(c.gradientUnits)&&(c=x(c,{cx:b[0]-b[2]/2+c.cx*b[2],cy:b[1]-b[2]/2+c.cy*b[2],r:c.r*b[2],gradientUnits:"userSpaceOnUse"}));for(m in c)m!=="id"&&q.push(m,c[m]);for(m in j)q.push(j[m]);q=q.join(",");h[q]?a=h[q].id:(c.id=a="highcharts-"+ub++,h[q]=i=d.createElement(g).attr(c).add(d.defs),i.stops=[],p(j,function(a){f.test(a[1])?(e=ta(a[1]),k=e.get("rgb"),l=e.get("a")):(k=a[1],l=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":l}).add(i);i.stops.push(a)})); -return"url("+d.url+"#"+a+")"}else return f.test(a)?(e=ta(a),v(b,c+"-opacity",e.get("a")),e.get("rgb")):(b.removeAttribute(c+"-opacity"),a)},text:function(a,b,c,d){var e=G.chart.style,f=da||!V&&this.forExport;if(d&&!this.forExport)return this.html(a,b,c);b=w(n(b,0));c=w(n(c,0));a=this.createElement("text").attr({x:b,y:c,text:a}).css({fontFamily:e.fontFamily,fontSize:e.fontSize});f&&a.css({position:"absolute"});a.x=b;a.y=c;return a},fontMetrics:function(a){var a=z(a||11),a=a<24?a+4:w(a*1.2),b=w(a*0.8); -return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=n.element.style;va=(Z===void 0||Sa===void 0||o.styles.textAlign)&&n.getBBox();o.width=(Z||va.width||0)+2*ea+lb;o.height=(Sa||va.height||0)+2*ea;$=ea+q.fontMetrics(a&&a.fontSize).b;if(y){if(!H)a=w(-s*ea),b=h?-$:0,o.box=H=d?q.symbol(d,a,b,o.width,o.height,v):q.rect(a,b,o.width,o.height,0,v[Lb]),H.add(o);H.isImg||H.attr(x({width:o.width,height:o.height},v));v=null}}function k(){var a=o.styles,a=a&&a.textAlign,b=lb+ea*(1-s),c;c= -h?0:$;if(t(Z)&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Z-va.width);(b!==n.x||c!==n.y)&&n.attr({x:b,y:c});n.x=b;n.y=c}function l(a,b){H?H.attr(a,b):v[a]=b}function m(){n.add(o);o.attr({text:a,x:b,y:c});H&&t(e)&&o.attr({anchorX:e,anchorY:f})}var q=this,o=q.g(i),n=q.text("",0,0,g).attr({zIndex:1}),H,va,s=0,ea=3,lb=0,Z,Sa,Ta,K,B=0,v={},$,g=o.attrSetters,y;F(o,"add",m);g.width=function(a){Z=a;return!1};g.height=function(a){Sa=a;return!1};g.padding=function(a){t(a)&&a!==ea&&(ea=a,k());return!1}; -g.paddingLeft=function(a){t(a)&&a!==lb&&(lb=a,k());return!1};g.align=function(a){s={left:0,center:0.5,right:1}[a];return!1};g.text=function(a,b){n.attr(b,a);j();k();return!1};g[Lb]=function(a,b){y=!0;B=a%2/2;l(b,a);return!1};g.stroke=g.fill=g.r=function(a,b){b==="fill"&&(y=!0);l(b,a);return!1};g.anchorX=function(a,b){e=a;l(b,a+B-Ta);return!1};g.anchorY=function(a,b){f=a;l(b,a-K);return!1};g.x=function(a){o.x=a;a-=s*((Z||va.width)+ea);Ta=w(a);o.attr("translateX",Ta);return!1};g.y=function(a){K=o.y= -w(a);o.attr("translateY",K);return!1};var z=o.css;return r(o,{css:function(a){if(a){var b={},a=x(a);p("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration,textShadow".split(","),function(c){a[c]!==u&&(b[c]=a[c],delete a[c])});n.css(b)}return z.call(o,a)},getBBox:function(){return{width:va.width+2*ea,height:va.height+2*ea,x:va.x-ea,y:va.y-ea}},shadow:function(a){H&&H.shadow(a);return o},destroy:function(){X(o,"add",m);X(o.element,"mouseenter");X(o.element,"mouseleave");n&&(n=n.destroy()); -H&&(H=H.destroy());pa.prototype.destroy.call(o);o=q=j=k=l=m=null}})}};Ya=ua;r(pa.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=r(this.styles,a);D(this.element,a);return this},htmlGetBBox:function(){var a=this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a= -this.renderer,b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=this.shadows;D(b,{marginLeft:c,marginTop:d});i&&p(i,function(a){D(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&p(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var j=this.rotation,k,l=z(this.textWidth),m=[j,g,b.innerHTML,this.textWidth].join(",");if(m!==this.cTT){k=a.fontMetrics(b.style.fontSize).b;t(j)&&this.setSpanRotation(j, -h,k);i=n(this.elemWidth,b.offsetWidth);if(i>l&&/[ \-]/.test(b.textContent||b.innerText))D(b,{width:l+"px",display:"block",whiteSpace:"normal"}),i=l;this.getSpanCorrection(i,k,h,j,g)}D(b,{left:e+(this.xCorr||0)+"px",top:f+(this.yCorr||0)+"px"});if(hb)k=b.offsetHeight;this.cTT=m}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,c){var d={},e=ya?"-ms-transform":hb?"-webkit-transform":Xa?"MozTransform":Hb?"-o-transform":"";d[e]=d.transform="rotate("+a+"deg)";d[e+(Xa?"Origin":"-origin")]=b*100+"% "+ -c+"px";D(this.element,d)},getSpanCorrection:function(a,b,c){this.xCorr=-a*c;this.yCorr=-b}});r(ua.prototype,{html:function(a,b,c){var d=G.chart.style,e=this.createElement("span"),f=e.attrSetters,g=e.element,h=e.renderer;f.text=function(a){a!==g.innerHTML&&delete this.bBox;g.innerHTML=a;return!1};f.x=f.y=f.align=f.rotation=function(a,b){b==="align"&&(b="textAlign");e[b]=a;e.htmlUpdateTransform();return!1};e.attr({text:a,x:w(b),y:w(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:d.fontFamily, -fontSize:d.fontSize});e.css=e.htmlCss;if(h.isSVG)e.add=function(a){var b,c=h.box.parentNode,d=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)d.push(a),a=a.parentGroup;p(d.reverse(),function(a){var d;b=a.div=a.div||T(Ga,{className:v(a.element,"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;r(a.attrSetters,{translateX:function(a){d.left=a+"px"},translateY:function(a){d.top=a+"px"},visibility:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(g); -e.added=!0;e.alignOnAdd&&e.htmlUpdateTransform();return e};return e}});var R;if(!V&&!da){Highcharts.VMLElement=R={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",";"],e=b===Ga;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=T(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer, -c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();A(this,"add");return this},updateTransform:pa.prototype.htmlUpdateTransform,setSpanRotation:function(){var a=this.rotation,b=U(a*Ba),c=ba(a*Ba);D(this.element,{filter:a?["progid:DXImageTransform.Microsoft.Matrix(M11=",b,", M12=",-c,", M21=",c,", M22=",b,", sizingMethod='auto expand')"].join(""):Q})},getSpanCorrection:function(a, -b,c,d,e){var f=d?U(d*Ba):1,g=d?ba(d*Ba):0,h=n(this.elemHeight,this.element.offsetHeight),i;this.xCorr=f<0&&-a;this.yCorr=g<0&&-h;i=f*g<0;this.xCorr+=g*b*(i?1-c:c);this.yCorr-=f*b*(d?i?c:1-c:1);e&&e!=="left"&&(this.xCorr-=a*c*(f<0?-1:1),d&&(this.yCorr-=h*c*(g<0?-1:1)),D(this.element,{textAlign:e}))},pathToVML:function(a){for(var b=a.length,c=[];b--;)if(wa(a[b]))c[b]=w(a[b]*10)-5;else if(a[b]==="Z")c[b]="x";else if(c[b]=a[b],a.isArc&&(a[b]==="wa"||a[b]==="at"))c[b+5]===c[b+7]&&(c[b+7]+=a[b+7]>a[b+5]? -1:-1),c[b+6]===c[b+8]&&(c[b+8]+=a[b+8]>a[b+6]?1:-1);return c.join(" ")||"x"},attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,l=this.shadows,m,q=this.attrSetters,o=this;fa(a)&&t(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,o=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],m=!1,e=q[c]&&q[c].call(this,d,c),e!==!1&&d!==null){e!==u&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k|| -(this.symbolAttr(a),k=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");f.path=d=this.pathToVML(d);if(l)for(e=l.length;e--;)l[e].path=l[e].cutOff?this.cutOffPath(d,l[e].cutOff):d;m=!0}else if(c==="visibility"){if(l)for(e=l.length;e--;)l[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,gb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;m=!0}else if(c==="zIndex")d&&(g[c]=d),m=!0;else if(sa(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=s(0,d),this.updateClipping? -(this[c]=d,this.updateClipping()):g[c]=d,m=!0;else if(c==="class"&&h==="DIV")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,wa(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||T(i.prepVML(["<stroke/>"]),null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==Q?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c=== -"opacity")m=!0;else if(h==="shape"&&c==="rotation")this[c]=f.style[c]=d,f.style.left=-w(ba(d*Ba)+1)+"px",f.style.top=w(U(d*Ba))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),m=!0;m||(gb?f[c]=d:v(f,c,d))}return o},clip:function(a){var b=this,c;a?(c=a.members,ha(c,b),c.push(b),b.destroyClip=function(){ha(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:gb?"inherit":"rect(auto)"});return b.css(a)},css:pa.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&& -Na(a)},destroy:function(){this.destroyClip&&this.destroyClip();return pa.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=C.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=z(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,l,m,q,o;k&&typeof k.value!=="string"&&(k="x");m=k;if(a){q=n(a.width,3);o=(a.opacity|| -0.15)/q;for(e=1;e<=3;e++){l=q*2+1-2*e;c&&(m=this.cutOffPath(k.value,l+0.5));j=['<shape isShadow="true" strokeweight="',l,'" filled="false" path="',m,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=T(g.prepVML(j),null,{left:z(i.left)+n(a.offsetX,1),top:z(i.top)+n(a.offsetY,1)});if(c)h.cutOff=l+1;j=['<stroke color="',a.color||"black",'" opacity="',o*e,'"/>'];T(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};R=ia(pa,R); -var xb={Element:R,isIE8:ra.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(Ga);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.cache={};this.setSize(b,c,!1);if(!y.namespaces.hcv){y.namespaces.add("hcv","urn:schemas-microsoft-com:vml");try{y.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}catch(f){y.styleSheets[0].cssText+= -"hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}}},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=S(a);return r(e,{members:[],left:(f?a.x:a)+1,top:(f?a.y:b)+1,width:(f?a.width:c)-1,height:(f?a.height:d)-1,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+w(a?e:d)+"px,"+w(a?f: -b)+"px,"+w(a?b:f)+"px,"+w(a?d:e)+"px)"};!a&&gb&&c==="DIV"&&r(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){p(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=Q;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,l,m=a.linearGradient||a.radialGradient,q,o,n,H,s,t="",a=a.stops,u,w=[],r=function(){h=['<fill colors="'+w.join(",")+'" opacity="',n,'" o:opacity2="',o,'" type="',i,'" ',t,'focus="100%" method="any" />']; -T(e.prepVML(h),null,null,b)};q=a[0];u=a[a.length-1];q[0]>0&&a.unshift([0,q[1]]);u[0]<1&&a.push([1,u[1]]);p(a,function(a,b){g.test(a[1])?(f=ta(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);w.push(a[0]*100+"% "+k);b?(n=l,H=k):(o=l,s=k)});if(c==="fill")if(i==="gradient")c=m.x1||m[0]||0,a=m.y1||m[1]||0,q=m.x2||m[2]||0,m=m.y2||m[3]||0,t='angle="'+(90-P.atan((m-a)/(q-c))*180/Aa)+'"',r();else{var j=m.r,Sa=j*2,Ta=j*2,v=m.cx,B=m.cy,x=b.radialReference,$,j=function(){x&&($=d.getBBox(),v+=(x[0]-$.x)/$.width- -0.5,B+=(x[1]-$.y)/$.height-0.5,Sa*=x[2]/$.width,Ta*=x[2]/$.height);t='src="'+G.global.VMLRadialGradientURL+'" size="'+Sa+","+Ta+'" origin="0.5,0.5" position="'+v+","+B+'" color2="'+s+'" ';r()};d.added?j():F(d,"add",j);j=H}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ta(a),h=["<",c,' opacity="',f.get("a"),'"/>'],T(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join(""); -b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:ua.prototype.html,path:function(a){var b={coordsize:"10 10"};Ka(a)?b.d=a:S(a)&&r(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(S(a))c=a.r,b=a.y,a=a.x;d.isCircle= -!0;d.r=c;return d.attr({x:a,y:b})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(Ga).attr(b)},image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){var g=this.symbol("rect");g.r=S(a)?a.r:e;return g.attr(S(a)?a:g.crisp(f,a,b,s(c,0),s(d,0)))},invertChild:function(a,b){var c=b.style;D(a,{flip:"x",left:z(c.width)-1,top:z(c.height)-1,rotation:-90})}, -symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=U(f),i=ba(f),j=U(g),k=ba(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&(c=d=2*e.r);e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!t(e)||!e.r?f=ua.prototype.symbols.square.apply(0, -arguments):(h=I(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*h,f,g,f,g-h,f-h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}};Highcharts.VMLRenderer=R=function(){this.init.apply(this,arguments)};R.prototype=x(ua.prototype,xb);Ya=R}ua.prototype.measureSpanWidth=function(a,b){var c=y.createElement("span"),d;d=y.createTextNode(a);c.appendChild(d);D(c,b);this.box.appendChild(c);d=c.offsetWidth;Na(c); -return d};var Mb;if(da)Highcharts.CanVGRenderer=R=function(){Ca="http://www.w3.org/1999/xhtml"},R.prototype.symbols={},Mb=function(){function a(){var a=b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Pb(d,a);b.push(c)}}}(),Ya=R;Qa.prototype={addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.margin[3]|| -c.chartWidth*0.33),j=g===i[0],k=g===i[i.length-1],l,f=e?n(e[g],f[g],g):g,e=this.label,m=i.info;a.isDatetimeAxis&&m&&(l=b.dateTimeLabelFormats[m.higherRanks[g]||m.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,isLast:k,dateTimeLabelFormat:l,value:a.isLog?aa(ga(f)):f});g=d&&{width:s(1,w(d-2*(h.padding||10)))+"px"};g=r(g,h.style);if(t(e))e&&e.attr({text:b}).css(g);else{l={align:a.labelAlign};if(wa(h.rotation))l.rotation=h.rotation;if(d&&h.ellipsis)l._clipHeight= -a.len/i.length;this.label=t(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(l).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,b=this.axis;return a?a.getBBox()[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.label.getBBox(),b=this.axis,c=b.horiz,d=b.options.labels,a=c?a.width:a.height,b=c?a*{left:0,center:0.5,right:1}[b.labelAlign]-d.x:a;return[-b,a-b]},handleOverflow:function(a,b){var B;var c=!0,d=this.axis,e=this.isFirst,f=this.isLast,g=d.horiz?b.x: -b.y,h=d.reversed,i=d.tickPositions,j=this.getLabelSides(),k=j[0],j=j[1],l=d.pos,m=l+d.len,q=this.label.line||0,o=d.labelEdge,n=d.justifyLabels&&(e||f);o[q]===u||g+k>o[q]?o[q]=g+j:n||(c=!1);if(n)B=(d=d.ticks[i[a+(e?1:-1)]])&&d.label.xy&&d.label.xy.x+d.getLabelSides()[e?0:1],i=B,e&&!h||f&&h?g+k<l&&(g=l-k,d&&g+j>i&&(c=!1)):g+j>m&&(g=m-j,d&&g+k<i&&(c=!1)),b.x=g;return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+ -e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,l=i.staggerLines,m=i.chart.renderer.fontMetrics(e.style.fontSize).b,q=e.rotation,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);q&&i.side===2&&(b-=m-m*U(q*Ba));!t(e.y)&&!q&&(b+=m-c.getBBox().height/2);if(l)c.line=g/(h||1)%l, -b+=c.line*(i.labelOffset/l);return{x:a,y:b}},getMarkPath:function(a,b,c,d,e,f){return f.crispLine(["M",a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,l=this.gridLine,m=h?h+"Grid":"grid",q=h?h+"Tick":"tick",o=e[m+"LineWidth"],p=e[m+"LineColor"],H=e[m+"LineDashStyle"],s=e[q+"Length"],m=e[q+"Width"]||0,t=e[q+"Color"],w=e[q+"Position"],q=this.mark,r=k.step,Z=!0,x=d.tickmarkOffset,v=this.getPosition(g, -j,x,b),y=v.x,v=v.y,B=g&&y===d.pos+d.len||!g&&v===d.pos?-1:1;this.isActive=!0;if(o){j=d.getPlotLinePath(j+x,o*B,b,!0);if(l===u){l={stroke:p,"stroke-width":o};if(H)l.dashstyle=H;if(!h)l.zIndex=1;if(b)l.opacity=0;this.gridLine=l=o?f.path(j).attr(l).add(d.gridGroup):null}if(!b&&l&&j)l[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(m&&s)w==="inside"&&(s=-s),d.opposite&&(s=-s),h=this.getMarkPath(y,v,s,m*B,g,f),q?q.animate({d:h,opacity:c}):this.mark=f.path(h).attr({stroke:t,"stroke-width":m,opacity:c}).add(d.axisGroup); -if(i&&!isNaN(y))i.xy=v=this.getLabelPosition(y,v,i,g,k,x,a,r),this.isFirst&&!this.isLast&&!n(e.showFirstLabel,1)||this.isLast&&!this.isFirst&&!n(e.showLastLabel,1)?Z=!1:!d.isRadial&&!k.step&&!k.rotation&&!b&&c!==0&&(Z=this.handleOverflow(a,v)),r&&a%r&&(Z=!1),Z&&!isNaN(v.y)?(v.opacity=c,i[this.isNew?"attr":"animate"](v),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Ma(this,this.axis)}};var yb=function(a,b){this.axis=a;if(b)this.options=b,this.id=b.id};yb.prototype={render:function(){var a=this, -b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,g=a.label,h=e.width,i=e.to,j=e.from,k=t(j)&&t(i),l=e.value,m=e.dashStyle,q=a.svgElem,o=[],p,H=e.color,w=e.zIndex,r=e.events,u=b.chart.renderer;b.isLog&&(j=xa(j),i=xa(i),l=xa(l));if(h){if(o=b.getPlotLinePath(l,h),d={stroke:H,"stroke-width":h},m)d.dashstyle=m}else if(k){if(j=s(j,b.min-d),i=I(i,b.max+d),o=b.getPlotBandPath(j,i,e),d={fill:H},e.borderWidth)d.stroke=e.borderColor,d["stroke-width"]=e.borderWidth}else return;if(t(w))d.zIndex= -w;if(q)if(o)q.animate({d:o},null,q.onGetPath);else{if(q.hide(),q.onGetPath=function(){q.show()},g)a.label=g=g.destroy()}else if(o&&o.length&&(a.svgElem=q=u.path(o).attr(d).add(),r))for(p in e=function(b){q.on(b,function(c){r[b].apply(a,[c])})},r)e(p);if(f&&t(f.text)&&o&&o.length&&b.width>0&&b.height>0){f=x({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=u.text(f.text,0,0,f.useHTML).attr({align:f.textAlign||f.align,rotation:f.rotation, -zIndex:w}).css(f.style).add();b=[o[1],o[4],n(o[6],o[1])];o=[o[2],o[5],n(o[7],o[2])];c=La(b);k=La(o);g.align(f,!1,{x:c,y:k,width:za(b)-c,height:za(o)-k});g.show()}else g&&g.hide();return a},destroy:function(){ha(this.axis.plotLinesAndBands,this);delete this.axis;Ma(this)}};qa.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:J,lineColor:"#C0D0E0", -lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e",fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8,y:3},lineWidth:0, -maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return Da(this.total,-1)},style:J.style}},defaultLeftAxisOptions:{labels:{x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:8,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.coll= -(this.isXAxis=c)?"xAxis":"yAxis";this.opposite=b.opposite;this.side=b.side||(this.horiz?this.opposite?0:2:this.opposite?1:3);this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.names=[];this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=t(d.linkedTo); -this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.labelEdge=[];this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.oldStacks={};this.stackExtremes={};this.min=this.max=null;this.crosshair=n(d.crosshair,ja(a.options.tooltip.crosshairs)[c?0:1],!1);var f,d=this.options.events;sa(this,a.axes)===-1&&(a.axes.push(this), -a[this.coll].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===u)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)F(this,f,d[f]);if(this.isLog)this.val2lin=xa,this.lin2val=ga},setOptions:function(a){this.options=x(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],x(G[this.coll],a))},defaultLabelFormatter:function(){var a= -this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=G.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=Fa(h,this);else if(c)g=b;else if(d)g=ab(d,b);else if(f&&a>=1E3)for(;f--&&g===u;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=Da(b/c,-1)+e[f]);g===u&&(g=b>=1E4?Da(b,0):Da(b,-1,u,""));return g},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;a.stackExtremes={};a.buildStacks();p(a.series,function(c){if(c.visible|| -!b.options.chart.ignoreHiddenSeries){var d;d=c.options.threshold;var e;a.hasVisibleSeries=!0;a.isLog&&d<=0&&(d=null);if(a.isXAxis){if(d=c.xData,d.length)a.dataMin=I(n(a.dataMin,d[0]),La(d)),a.dataMax=s(n(a.dataMax,d[0]),za(d))}else{c.getExtremes();e=c.dataMax;c=c.dataMin;if(t(c)&&t(e))a.dataMin=I(n(a.dataMin,c),c),a.dataMax=s(n(a.dataMax,e),e);if(t(d))if(a.dataMin>=d)a.dataMin=d,a.ignoreMinPadding=!0;else if(a.dataMax<d)a.dataMax=d,a.ignoreMaxPadding=!0}}})},translate:function(a,b,c,d,e,f){var g= -this.len,h=1,i=0,j=d?this.oldTransA:this.transA,d=d?this.oldMin:this.min,k=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!j)j=this.transA;c&&(h*=-1,i=g);this.reversed&&(h*=-1,i-=h*g);b?(a=a*h+i,a-=k,a=a/j+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),f==="between"&&(f=0.5),a=h*(a-d)*j+i+h*k+(wa(f)?j*f*this.pointRange:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a- -(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,e){var f=this.chart,g=this.left,h=this.top,i,j,k=c&&f.oldChartHeight||f.chartHeight,l=c&&f.oldChartWidth||f.chartWidth,m;i=this.transB;e=n(e,this.translate(a,null,null,c));a=c=w(e+i);i=j=w(k-e-i);if(isNaN(e))m=!0;else if(this.horiz){if(i=h,j=k-this.bottom,a<g||a>g+this.width)m=!0}else if(a=g,c=l-this.right,i<h||i>h+this.height)m=!0;return m&&!d?null:f.renderer.crispLine(["M",a,i,"L",c,j],b||1)},getLinearTickPositions:function(a, -b,c){for(var d,b=aa(N(b/a)*a),c=aa(Ha(c/a)*a),e=[];b<=c;){e.push(b);b=aa(b+a);if(b===d)break;d=b}return e},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(this.getTimeTicks(this.normalizeTimeTickInterval(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&d.shift();else for(b=this.min+ -(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===u&&!this.isLog)t(a.min)||t(a.max)?this.minRange=null:(p(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===u||h<f)f=h}),this.minRange=I(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=(k-c+b)/2;d=[b-d,n(a.min,b-d)]; -if(e)d[2]=this.dataMin;b=za(d);c=[b+k,n(a.max,b+k)];if(e)c[2]=this.dataMax;c=La(c);c-b<k&&(d[0]=c-k,d[1]=n(a.min,c-k),b=za(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this.max-this.min,c=0,d,e=0,f=0,g=this.linkedParent,h=!!this.categories,i=this.transA;if(this.isXAxis||h)g?(e=g.minPointOffset,f=g.pointRangePadding):p(this.series,function(a){var g=s(a.pointRange,+h),i=a.options.pointPlacement,m=a.closestPointRange;g>b&&(g=0);c=s(c,g);e=s(e,fa(i)?0:g/2);f=s(f,i==="on"?0:g);!a.noSharedTooltip&& -t(m)&&(d=t(d)?I(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=I(c,b),this.closestPointRange=d;if(a)this.oldTransA=i;this.translationSlope=this.transA=i=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding=i*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,l=d.tickInterval, -m=d.minTickInterval,q=d.tickPixelInterval,o,ma=b.categories;h?(b.linkedParent=c[b.coll][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=n(c.min,c.dataMin),b.max=n(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&ka(11,1)):(b.min=n(b.userMin,d.min,b.dataMin),b.max=n(b.userMax,d.max,b.dataMax));if(e)!a&&I(b.min,n(b.dataMin,b.min))<=0&&ka(10,1),b.min=aa(xa(b.min)),b.max=aa(xa(b.max));if(b.range&&t(b.max))b.userMin=b.min=s(b.min,b.max-b.range),b.userMax=b.max,b.range=null;b.beforePadding&&b.beforePadding(); -b.adjustForMinRange();if(!ma&&!b.usePercentage&&!h&&t(b.min)&&t(b.max)&&(c=b.max-b.min)){if(!t(d.min)&&!t(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!t(d.max)&&!t(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.min===b.max||b.min===void 0||b.max===void 0?b.tickInterval=1:h&&!l&&q===b.linkedParent.options.tickPixelInterval?b.tickInterval=b.linkedParent.tickInterval:(b.tickInterval=n(l,ma?1:(b.max-b.min)*q/s(b.len,q)),!t(l)&&b.len<q&&!this.isRadial&&!ma&&d.startOnTick&& -d.endOnTick&&(o=!0,b.tickInterval/=4));g&&!a&&p(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(b.pointRange)b.tickInterval=s(b.pointRange,b.tickInterval);if(!l&&b.tickInterval<m)b.tickInterval=m;if(!f&&!e&&!l)b.tickInterval=nb(b.tickInterval,null,mb(b.tickInterval),d);b.minorTickInterval=d.minorTickInterval=== -"auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=a=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!a)!b.ordinalPositions&&(b.max-b.min)/b.tickInterval>s(2*b.len,200)&&ka(19,!0),a=f?b.getTimeTicks(b.normalizeTimeTickInterval(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),o&&a.splice(1,a.length-2), -b.tickPositions=a;if(!h)e=a[0],f=a[a.length-1],h=b.minPointOffset||0,d.startOnTick?b.min=e:b.min-h>e&&a.shift(),d.endOnTick?b.max=f:b.max+h<f&&a.pop(),a.length===1&&(b.min-=0.001,b.max+=0.001)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.coll,this.pos,this.len].join("-");if(!this.isLinked&&!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey, -b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1&&this.min!==u){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(aa(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(t(d)&&a!==d)this.isDirty=!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!== -this.oldAxisLength;p(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=!0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax){if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].total=null,a[b][c].cum=0;this.forceRedraw=!1;this.getSeriesExtremes();this.setTickPositions();this.oldUserMin=this.userMin;this.oldUserMax=this.userMax;if(!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax}else if(!this.isXAxis){if(this.oldStacks)a= -this.stacks=this.oldStacks;for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total}this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=n(c,!0),e=r(e,{min:a,max:b});A(f,"setExtremes",e,function(){f.userMin=a;f.userMax=b;f.eventArgs=e;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){this.allowZoomOutside||(t(this.dataMin)&&a<=this.dataMin&&(a=u),t(this.dataMax)&&b>=this.dataMax&&(b=u));this.displayBtn=a!==u||b!==u;this.setExtremes(a,b,!1,u,{trigger:"zoom"});return!0},setAxisSize:function(){var a= -this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=n(b.left,a.plotLeft+c);this.top=f=n(b.top,a.plotTop);this.width=c=n(b.width,a.plotWidth-c+d);this.height=b=n(b.height,a.plotHeight);this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=s(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?aa(ga(this.min)):this.min,max:a?aa(ga(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}}, -getThreshold:function(a){var b=this.isLog,c=b?ga(this.min):this.min,b=b?ga(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},autoLabelAlign:function(a){a=(n(a,0)-this.side*90+720)%360;return a>15&&a<165?"right":a>195&&a<345?"left":"center"},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,l,m=0,q=d.title,o=d.labels,ma=0,H=b.axisOffset,w=b.clipOffset,r=[-1,1,1,-1][h],v, -x=1,Z=n(o.maxStaggerLines,5),y,z,K,B;a.hasData=j=a.hasVisibleSeries||t(a.min)&&t(a.max)&&!!e;a.showAxis=b=j||n(d.showEmpty,!0);a.staggerLines=a.horiz&&o.staggerLines;if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:o.zIndex||7}).add();if(j||a.isLinked){a.labelAlign=n(o.align||a.autoLabelAlign(o.rotation));p(e,function(b){f[b]?f[b].addLabel():f[b]=new Qa(a,b)});if(a.horiz&& -!a.staggerLines&&Z&&!o.rotation){for(v=a.reversed?[].concat(e).reverse():e;x<Z;){j=[];y=!1;for(o=0;o<v.length;o++)z=v[o],K=(K=f[z].label&&f[z].label.getBBox())?K.width:0,B=o%x,K&&(z=a.translate(z),j[B]!==u&&z<j[B]&&(y=!0),j[B]=z+K);if(y)x++;else break}if(x>1)a.staggerLines=x}p(e,function(b){if(h===0||h===2||{1:"left",3:"right"}[h]===a.labelAlign)ma=s(f[b].getLabelSize(),ma)});if(a.staggerLines)ma*=a.staggerLines,a.labelOffset=ma}else for(v in f)f[v].destroy(),delete f[v];if(q&&q.text&&q.enabled!== -!1){if(!a.axisTitle)a.axisTitle=c.text(q.text,0,0,q.useHTML).attr({zIndex:7,rotation:q.rotation||0,align:q.textAlign||{low:"left",middle:"center",high:"right"}[q.align]}).css(q.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(b)k=a.axisTitle.getBBox()[g?"height":"width"],m=n(q.margin,g?5:10),l=q.offset;a.axisTitle[b?"show":"hide"]()}a.offset=r*n(d.offset,H[h]);a.axisTitleMargin=n(l,ma+m+(h!==2&&ma&&r*d.labels[g?"y":"x"]));H[h]=s(H[h],a.axisTitleMargin+k+r*a.offset);w[i]=s(w[i],N(d.lineWidth/2)*2)}, -getLinePath:function(a){var b=this.chart,c=this.opposite,d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d,d=b.chartHeight-this.bottom-(c?this.height:0)+d;c&&(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=z(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+d/2,high:f+ -(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.horiz,c=a.reversed,d=a.chart,e=d.renderer,f=a.options,g=a.isLog,h=a.isLinked,i=a.tickPositions,j,k=a.axisTitle,l=a.stacks,m=a.ticks,q=a.minorTicks,o=a.alternateBands,n=f.stackLabels,H=f.alternateGridColor,s=a.tickmarkOffset,r=f.lineWidth,w=d.hasRendered&&t(a.oldMin)&&!isNaN(a.oldMin),v= -a.hasData,x=a.showAxis,y,z=a.justifyLabels=!a.staggerLines&&b&&f.labels.overflow==="justify",K;a.labelEdge.length=0;p([m,q,o],function(a){for(var b in a)a[b].isActive=!1});if(v||h)if(a.minorTickInterval&&!a.categories&&p(a.getMinorTickPositions(),function(b){q[b]||(q[b]=new Qa(a,b,"minor"));w&&q[b].isNew&&q[b].render(null,!0);q[b].render(null,!1,1)}),i.length&&(j=i.slice(),(b&&c||!b&&!c)&&j.reverse(),z&&(j=j.slice(1).concat([j[0]])),p(j,function(b,c){z&&(c=c===j.length-1?0:c+1);if(!h||b>=a.min&&b<= -a.max)m[b]||(m[b]=new Qa(a,b)),w&&m[b].isNew&&m[b].render(c,!0,0.1),m[b].render(c,!1,1)}),s&&a.min===0&&(m[-1]||(m[-1]=new Qa(a,-1,null,!0)),m[-1].render(-1))),H&&p(i,function(b,c){if(c%2===0&&b<a.max)o[b]||(o[b]=new yb(a)),y=b+s,K=i[c+1]!==u?i[c+1]+s:a.max,o[b].options={from:g?ga(y):y,to:g?ga(K):K,color:H},o[b].render(),o[b].isActive=!0}),!a._addedPlotLB)p((f.plotLines||[]).concat(f.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;p([m,q,o],function(a){var b,c,e=[],f=oa?oa.duration|| -500:0,g=function(){for(c=e.length;c--;)a[e[c]]&&!a[e[c]].isActive&&(a[e[c]].destroy(),delete a[e[c]])};for(b in a)if(!a[b].isActive)a[b].render(b,!1,0),a[b].isActive=!1,e.push(b);a===o||!d.hasRendered||!f?g():f&&setTimeout(g,f)});if(r)b=a.getLinePath(r),a.axisLine?a.axisLine.animate({d:b}):a.axisLine=e.path(b).attr({stroke:f.lineColor,"stroke-width":r,zIndex:7}).add(a.axisGroup),a.axisLine[x?"show":"hide"]();if(k&&x)k[k.isNew?"attr":"animate"](a.getTitlePosition()),k.isNew=!1;if(n&&n.enabled){var B, -A,f=a.stackTotalGroup;if(!f)a.stackTotalGroup=f=e.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();f.translate(d.plotLeft,d.plotTop);for(B in l)for(A in e=l[B],e)e[A].render(f)}a.isDirty=!1},redraw:function(){var a=this.chart.pointer;a.reset&&a.reset(!0);this.render();p(this.plotLinesAndBands,function(a){a.render()});p(this.series,function(a){a.isDirty=!0})},buildStacks:function(){var a=this.series,b=a.length;if(!this.isXAxis){for(;b--;)a[b].setStackedPoints();if(this.usePercentage)for(b= -0;b<a.length;b++)a[b].setPercentStacks()}},destroy:function(a){var b=this,c=b.stacks,d,e=b.plotLinesAndBands;a||X(b);for(d in c)Ma(c[d]),c[d]=null;p([b.ticks,b.minorTicks,b.alternateBands],function(a){Ma(a)});for(a=e.length;a--;)e[a].destroy();p("stackTotalGroup,axisLine,axisTitle,axisGroup,cross,gridGroup,labelGroup".split(","),function(a){b[a]&&(b[a]=b[a].destroy())});this.cross&&this.cross.destroy()},drawCrosshair:function(a,b){if(this.crosshair)if((t(b)||!n(this.crosshair.snap,!0))===!1)this.hideCrosshair(); -else{var c,d=this.crosshair,e=d.animation;n(d.snap,!0)?t(b)&&(c=this.chart.inverted!=this.horiz?b.plotX:this.len-b.plotY):c=this.horiz?a.chartX-this.pos:this.len-a.chartY+this.pos;c=this.isRadial?this.getPlotLinePath(this.isXAxis?b.x:n(b.stackY,b.y)):this.getPlotLinePath(null,null,null,null,c);if(c===null)this.hideCrosshair();else if(this.cross)this.cross.attr({visibility:"visible"})[e?"animate":"attr"]({d:c},e);else{e={"stroke-width":d.width||1,stroke:d.color||"#C0C0C0",zIndex:d.zIndex||2};if(d.dashStyle)e.dashstyle= -d.dashStyle;this.cross=this.chart.renderer.path(c).attr(e).add()}}},hideCrosshair:function(){this.cross&&this.cross.hide()}};r(qa.prototype,{getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},addPlotLine:function(a){this.addPlotBandOrLine(a,"plotLines")},addPlotBandOrLine:function(a,b){var c=(new yb(this,a)).render(),d=this.userOptions;c&&(b&&(d[b]=d[b]|| -[],d[b].push(a)),this.plotLinesAndBands.push(c));return c},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=this.options,d=this.userOptions,e=b.length;e--;)b[e].id===a&&b[e].destroy();p([c.plotLines||[],d.plotLines||[],c.plotBands||[],d.plotBands||[]],function(b){for(e=b.length;e--;)b[e].id===a&&ha(b,b[e])})}});qa.prototype.getLogTickPositions=function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=w(a),g=this.getLinearTickPositions(a, -b,c);else if(a>=0.08)for(var f=N(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!l;f++){i=e.length;for(h=0;h<i&&!l;h++)j=xa(ga(f)*e[h]),j>b&&(!d||k<=c)&&g.push(k),k>c&&(l=!0),k=j}else if(b=ga(b),c=ga(c),a=e[d?"minorTickInterval":"tickInterval"],a=n(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=nb(a,null,mb(a)),g=Ra(this.getLinearTickPositions(a,b,c),xa),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval= -a;return g};qa.prototype.getTimeTicks=function(a,b,c,d){var e=[],f={},g=G.global.useUTC,h,i=new Date(b-Pa),j=a.unitRange,k=a.count;if(t(b)){j>=E.second&&(i.setMilliseconds(0),i.setSeconds(j>=E.minute?0:k*N(i.getSeconds()/k)));if(j>=E.minute)i[Cb](j>=E.hour?0:k*N(i[pb]()/k));if(j>=E.hour)i[Db](j>=E.day?0:k*N(i[qb]()/k));if(j>=E.day)i[sb](j>=E.month?1:k*N(i[Wa]()/k));j>=E.month&&(i[Eb](j>=E.year?0:k*N(i[db]()/k)),h=i[eb]());j>=E.year&&(h-=h%k,i[Fb](h));if(j===E.week)i[sb](i[Wa]()-i[rb]()+n(d,1));b= -1;Pa&&(i=new Date(i.getTime()+Pa));h=i[eb]();for(var d=i.getTime(),l=i[db](),m=i[Wa](),q=g?Pa:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===E.year?d=cb(h+b*k,0):j===E.month?d=cb(h,l+b*k):!g&&(j===E.day||j===E.week)?d=cb(h,l,m+b*k*(j===E.day?1:7)):d+=j*k,b++;e.push(d);p(wb(e,function(a){return j<=E.hour&&a%E.day===q}),function(a){f[a]="day"})}e.info=r(a,{higherRanks:f,totalRange:j*k});return e};qa.prototype.normalizeTimeTickInterval=function(a,b){var c=b||[["millisecond",[1,2,5,10,20, -25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]],d=c[c.length-1],e=E[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=E[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+E[c[g+1][0]])/2)break;e===E.year&&a<5*e&&(f=[1,2,5]);c=nb(a/e,f,d[0]==="year"?s(mb(a/e),1):1);return{unitRange:e,count:c,unitName:d[0]}};Gb.prototype={destroy:function(){Ma(this,this.axis)},render:function(a){var b=this.options, -c=b.format,c=c?Fa(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=M(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e? -c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?V?"inherit":"visible":"hidden"})}};tb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=z(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).add().attr({y:-999}); -da||this.label.shadow(b.shadow);this.shared=b.shared},destroy:function(){if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden;r(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:g?(2*f.anchorX+c)/3:c,anchorY:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(M(a-f.x)>1||M(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b, -c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},n(this.options.hideDelay,500)),b&&p(b,function(a){a.setState()}),this.chart.hoverPoints=null},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=d.plotTop,g=0,h=0,i,a=ja(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===u&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(p(a,function(a){i= -a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Ra(c,w)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=n(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,l;d<7&&(d=e+s(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,l=!0);k<f+5&&(k=f+5,l&&c>=k&&c<=k+b&&(k=c+ -f+i));k+b>f+h&&(k=s(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ja(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];p(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat||"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h={},i,j=[];i=e.formatter||this.defaultFormatter;var h=c.hoverPoints,k,l=this.shared; -clearTimeout(this.hideTimer);this.followPointer=ja(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];l&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,h&&p(h,function(a){a.setState()}),p(a,function(a){a.setState("hover");j.push(a.getLabelConfig())}),h={x:a[0].category,y:a[0].y},h.points=j,a=a[0]):h=a.getLabelConfig();i=i.call(h,this);h=a.series;i===!1?this.hide():(this.isHidden&&(Za(d),d.attr("opacity",1).show()),d.attr({text:i}),k=e.borderColor||a.color||h.color|| -"#606060",d.attr({stroke:k}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);A(c,"tooltipRefresh",{text:i,x:f+c.plotLeft,y:g+c.plotTop,borderColor:k})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(w(c.x),w(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};var $a=Highcharts.Pointer=function(a,b){this.init(a,b)};$a.prototype={init:function(a,b){var c=b.chart,d=c.events,e=da?"":c.zoomType,c=a.inverted, -f;this.options=b;this.chart=a;this.zoomX=f=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=f&&!c||e&&c;this.zoomVert=e&&!c||f&&c;this.runChartClick=d&&!!d.click;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new tb(a,b.tooltip);this.setDOMEvents()},normalize:function(a,b){var c,d,a=a||C.event;if(!a.target)a.target=a.srcElement;a=Rb(a);d=a.touches?a.touches.item(0):a;if(!b)this.chartPosition=b=Qb(this.chart.container);d.pageX===u?(c=s(a.x,a.clientX-b.left),d=a.y):(c=d.pageX- -b.left,d=d.pageY-b.top);return r(a,{chartX:w(c),chartY:w(d)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};p(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this,c=b.chart,d=c.series,e=c.tooltip,f,g,h=c.hoverPoint,i=c.hoverSeries,j,k,l=c.chartWidth,m=b.getIndex(a);if(e&& -b.options.tooltip.shared&&(!i||!i.noSharedTooltip)){g=[];j=d.length;for(k=0;k<j;k++)if(d[k].visible&&d[k].options.enableMouseTracking!==!1&&!d[k].noSharedTooltip&&d[k].tooltipPoints.length&&(f=d[k].tooltipPoints[m])&&f.series)f._dist=M(m-f.clientX),l=I(l,f._dist),g.push(f);for(j=g.length;j--;)g[j]._dist>l&&g.splice(j,1);if(g.length&&g[0].clientX!==b.hoverX)e.refresh(g,a),b.hoverX=g[0].clientX}if(i&&i.tracker){if((f=i.tooltipPoints[m])&&f!==h)f.onMouseOver(a)}else e&&e.followPointer&&!e.isHidden&& -(d=e.getAnchor([{}],a),e.updatePosition({plotX:d[0],plotY:d[1]}));if(e&&!b._onDocumentMouseMove)b._onDocumentMouseMove=function(a){b.onDocumentMouseMove(a)},F(y,"mousemove",b._onDocumentMouseMove);p(c.axes,function(b){b.drawCrosshair(a,n(f,h))})},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,f=e&&e.shared?b.hoverPoints:d;(a=a&&e&&f)&&ja(f)[0].plotX===u&&(a=!1);if(a)e.refresh(f),d&&d.setState(d.state,!0);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&e.hide();if(this._onDocumentMouseMove)X(y, -"mousemove",this._onDocumentMouseMove),this._onDocumentMouseMove=null;p(b.axes,function(a){a.hideCrosshair()});this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart,d;p(c.series,function(e){d=a||e.getPlotBox();e.xAxis&&e.xAxis.zoomEnabled&&(e.group.attr(d),e.markerGroup&&(e.markerGroup.attr(d),e.markerGroup.clip(b?c.clipRect:null)),e.dataLabelsGroup&&e.dataLabelsGroup.attr(d))});c.clipRect.attr(b||c.clipBox)},pinchTranslate:function(a,b,c,d,e,f,g,h){a&&this.pinchTranslateDirection(!0,c,d, -e,f,g,h);b&&this.pinchTranslateDirection(!1,c,d,e,f,g,h)},pinchTranslateDirection:function(a,b,c,d,e,f,g,h){var i=this.chart,j=a?"x":"y",k=a?"X":"Y",l="chart"+k,m=a?"width":"height",q=i["plot"+(a?"Left":"Top")],o,n,p=h||1,s=i.inverted,t=i.bounds[a?"h":"v"],r=b.length===1,w=b[0][l],u=c[0][l],v=!r&&b[1][l],x=!r&&c[1][l],y,c=function(){!r&&M(w-v)>20&&(p=h||M(u-x)/M(w-v));n=(q-u)/p+w;o=i["plot"+(a?"Width":"Height")]/p};c();b=n;b<t.min?(b=t.min,y=!0):b+o>t.max&&(b=t.max-o,y=!0);y?(u-=0.8*(u-g[j][0]),r|| -(x-=0.8*(x-g[j][1])),c()):g[j]=[u,x];s||(f[j]=n-q,f[m]=o);f=s?1/p:p;e[m]=o;e[j]=b;d[s?a?"scaleY":"scaleX":"scale"+k]=p;d["translate"+k]=f*q+(u-f*w)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,l=b.selectionMarker,m={},q=g===1&&(b.inClass(a.target,"highcharts-tracker")&&c.runTrackerClick||c.runChartClick),o={};(k||e)&&!q&&a.preventDefault();Ra(f, -function(a){return b.normalize(a)});if(a.type==="touchstart")p(f,function(a,b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],p(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=I(e,f),e=s(e,f);b.min=I(a.pos,g-d);b.max=s(a.pos+a.len,e+d)}});else if(d.length){if(!l)b.selectionMarker=l=r({destroy:la},c.plotBox);b.pinchTranslate(i,j,d,f,m,l,o,h);b.hasPinched= -k;b.scaleGroups(m,o);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,f=this.zoomHor,g=this.zoomVert,h=b.plotLeft,i=b.plotTop,j=b.plotWidth,k=b.plotHeight,l,m=this.mouseDownX,q=this.mouseDownY;d<h?d=h:d>h+j&&(d=h+j);e<i?e=i:e>i+k&&(e=i+k);this.hasDragged=Math.sqrt(Math.pow(m- -d,2)+Math.pow(q-e,2));if(this.hasDragged>10){l=b.isInsidePlot(m-h,q-i);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(h,i,f?1:j,g?1:k,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&f&&(d-=m,this.selectionMarker.attr({width:M(d),x:(d>0?0:d)+m}));this.selectionMarker&&g&&(d=e-q,this.selectionMarker.attr({height:M(d),y:(d>0?0:d)+q}));l&&!this.selectionMarker&&c.panning&&b.pan(a,c.panning)}}, -drop:function(a){var b=this.chart,c=this.hasPinched;if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)p(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.coll].push({axis:a,min:I(c,b),max:s(c,b)}),h=!0)}}),h&&A(b,"selection",d,function(a){b.zoom(r(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy(); -c&&this.scaleGroups()}if(b)D(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=this.normalize(a,c);c&&d&&!this.inClass(a.target,"highcharts-tracker")&&!b.isInsidePlot(a.chartX-b.plotLeft, -a.chartY-b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);b.mouseIsDown==="mousedown"&&this.drag(a);(this.inClass(a.target,"highcharts-tracker")||b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop))&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=v(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a= -a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries,c=(a=a.relatedTarget||a.toElement)&&a.point&&a.point.series;if(b&&!b.options.stickyTracking&&!this.inClass(a,"highcharts-tooltip")&&c!==b)b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,r(c,{pageX:g.left+d+(f? -b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),A(c.series,"click",r(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(r(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&A(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)?(this.runPointActions(a),this.pinch(a)):this.reset()):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length=== -1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a=this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[y,"mouseup","onDocumentMouseUp"]];ib&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[y,"touchend","onDocumentTouchEnd"]);p(c,function(b){a["_"+ -b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]:F(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;p(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:X(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};J=Highcharts.TrackerMixin={drawTrackerPoint:function(){var a=this,b=a.chart,c=b.pointer,d=a.options.cursor,e=d&&{cursor:d},f=function(c){var d=c.target,e;if(b.hoverSeries!==a)a.onMouseOver();for(;d&&!e;)e=d.point,d=d.parentNode; -if(e!==u&&e!==b.hoverPoint)e.onMouseOver(c)};p(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});if(!a._hasTracking)p(a.trackerGroups,function(b){if(a[b]&&(a[b].addClass("highcharts-tracker").on("mouseover",f).on("mouseout",function(a){c.onTrackerMouseOut(a)}).css(e),ib))a[b].on("touchstart",f)}),a._hasTracking=!0},drawTrackerGraph:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer, -h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,l=k&&{cursor:k},k=a.singlePoints,m,q=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-i,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+i,d[m-1]);for(m=0;m<k.length;m++)e=k[m],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);j?j.attr({d:d}):(a.tracker=h.path(d).attr({"stroke-linejoin":"round",visibility:a.visible?"visible":"hidden",stroke:Kb,fill:c?Kb:Q,"stroke-width":b.lineWidth+ -(c?0:2*i),zIndex:2}).add(a.group),p([a.tracker,a.markerGroup],function(a){a.addClass("highcharts-tracker").on("mouseover",q).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(l);if(ib)a.on("touchstart",q)}))}};if(C.PointerEvent||C.MSPointerEvent){var na={};$a.prototype.getWebkitTouches=function(){var a,b=[];b.item=function(a){return this[a]};for(a in na)na.hasOwnProperty(a)&&b.push({pageX:na[a].pageX,pageY:na[a].pageY,target:na[a].target});return b};Va($a.prototype,"init",function(a,b,c){b.container.style["-ms-touch-action"]= -b.container.style["touch-action"]="none";a.call(this,b,c)});Va($a.prototype,"setDOMEvents",function(a){var b=this;a.apply(this,Array.prototype.slice.call(arguments,1));p([[this.chart.container,"PointerDown","touchstart","onContainerTouchStart",function(a){na[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}}],[this.chart.container,"PointerMove","touchmove","onContainerTouchMove",function(a){na[a.pointerId]={pageX:a.pageX,pageY:a.pageY};if(!na[a.pointerId].target)na[a.pointerId].target= -a.currentTarget}],[document,"PointerUp","touchend","onDocumentTouchEnd",function(a){delete na[a.pointerId]}]],function(a){F(a[0],window.PointerEvent?a[1].toLowerCase():"MS"+a[1],function(d){d=d.originalEvent;if(d.pointerType==="touch"||d.pointerType===d.MSPOINTER_TYPE_TOUCH)a[4](d),b[a[3]]({type:a[2],target:d.currentTarget,preventDefault:la,touches:b.getWebkitTouches()})})})})}var zb=Highcharts.Legend=function(a,b){this.init(a,b)};zb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=n(b.padding, -8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=z(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=x(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight=0,c.lastLineHeight=0,c.symbolWidth=n(b.symbolWidth,16),c.pages=[],c.render(),F(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c= -b?c.itemStyle.color:g,h=b?a.legendColor||a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g&&f.isMarker)for(j in g=a.convertAttribs(g),g)d=g[j],d!==u&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;p(["legendItem", -"legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&Na(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY,p(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,D(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":Q}))})},renderTitle:function(){var a= -this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);a=this.title.getBBox();c=a.height;this.offsetWidth=a.width;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var B;var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=b.symbolWidth,h=e.symbolPadding,i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding, -l=f?n(e.itemDistance,8):0,m=!e.rtl,q=e.width,o=e.itemMarginBottom||0,p=b.itemMarginTop,t=b.initialItemX,r=a.legendItem,u=a.series&&a.series.drawLegendSymbol?a.series:a,v=u.options,v=v&&v.showCheckbox,y=e.useHTML;if(!r&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),u.drawLegendSymbol(b,a),a.legendItem=r=d.text(e.labelFormat?Fa(e.labelFormat,a):e.labelFormatter.call(a),m?g+h:-h,b.baseline,y).css(x(a.visible?i:j)).attr({align:m?"left":"right",zIndex:2}).add(a.legendGroup),(y? -r:a.legendGroup).on("mouseover",function(){a.setState("hover");r.css(b.options.itemHoverStyle)}).on("mouseout",function(){r.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):A(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),v))a.checkbox=T("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},e.itemCheckboxStyle,c.container),F(a.checkbox,"click",function(b){A(a, -"checkboxClick",{checked:b.target.checked},function(){a.select()})});d=r.getBBox();B=a.legendItemWidth=e.itemWidth||a.legendItemWidth||g+h+d.width+l+(v?20:0),e=B;b.itemHeight=g=w(a.legendItemHeight||d.height);if(f&&b.itemX-t+e>(q||c.chartWidth-2*k-t))b.itemX=t,b.itemY+=p+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=s(b.maxItemWidth,e);b.lastItemY=p+b.itemY+o;b.lastLineHeight=s(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=p+g+o,b.lastLineHeight=g);b.offsetWidth= -q||s((f?b.itemX-t-l:e)+k,b.offsetWidth)},getAllItems:function(){var a=[];p(this.chart.series,function(b){var c=b.options;if(n(c.showInLegend,!t(c.linkedTo)?u:!1,!0))a=a.concat(b.legendItems||(c.legendType==="point"?b.data:b))});return a},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,l=j.borderWidth,m=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(), -a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=a.getAllItems();ob(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;p(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(l||m){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)), -i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,"stroke-width":l||0,fill:m||Q}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;p(e,function(b){a.positionItem(b)});f&&d.align(r({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight, -h,i=this.clipRect,j=e.navigation,k=n(j.animation,!0),l=j.arrowSize||12,m=this.nav,q=this.pages,o,s=this.allItems;e.layout==="horizontal"&&(f/=2);g&&(f=I(f,g));q.length=0;if(a>f&&!e.useHTML){this.clipHeight=h=f-20-this.titleHeight-this.padding;this.currentPage=n(this.currentPage,1);this.fullHeight=a;p(s,function(a,b){var c=a._legendItemPos[1],d=w(a.legendItem.bBox.height),e=q.length;if(!e||c-q[e-1]>h)q.push(o||c);b===s.length-1&&c+d-q[e-1]>h&&q.push(c);c!==o&&(o=c)});if(!i)i=b.clipRect=d.clipRect(0, -this.padding,9999,0),b.contentGroup.clip(i);i.attr({height:h});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,l,l).on("click",function(){b.scroll(-1,k)}).add(m),this.pager=d.text("",15,10).css(j.style).add(m),this.down=d.symbol("triangle-down",0,0,l,l).on("click",function(){b.scroll(1,k)}).add(m);b.scroll(0);a=f}else if(m)i.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pages, -d=c.length,e=this.currentPage+a,f=this.clipHeight,g=this.options.navigation,h=g.activeColor,g=g.inactiveColor,i=this.pager,j=this.padding;e>d&&(e=d);if(e>0)b!==u&&Oa(b,this.chart),this.nav.attr({translateX:j,translateY:f+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:e===1?g:h}).css({cursor:e===1?"default":"pointer"}),i.attr({text:e+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,fill:e===d?g:h}).css({cursor:e===d?"default":"pointer"}),c=-c[e-1]+this.initialItemY, -this.scrollGroup.animate({translateY:c}),this.currentPage=e,this.positionCheckboxes(c)}};R=Highcharts.LegendSymbolMixin={drawRectangle:function(a,b){var c=a.options.symbolHeight||12;b.legendSymbol=this.chart.renderer.rect(0,a.baseline-5-c/2,a.symbolWidth,c,n(a.options.symbolRadius,2)).attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b=this.options,c=b.marker,d;d=a.symbolWidth;var e=this.chart.renderer,f=this.legendGroup,a=a.baseline-w(e.fontMetrics(a.options.itemStyle.fontSize).b* -0.3),g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a,"L",d,a]).attr(g).add(f)}if(c&&c.enabled)b=c.radius,this.legendSymbol=d=e.symbol(this.symbol,d/2-b,a-b,2*b,2*b).add(f),d.isMarker=!0}};/Trident\/7\.0/.test(ra)&&Va(zb.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c,b)};c.chart.renderer.forExport?d():setTimeout(d)});fb.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=x(G, -a);c.series=a.series=d;this.userOptions=a;d=c.chart;this.margin=this.splashArray("margin",d);this.spacing=this.splashArray("spacing",d);var e=d.events;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ia.length;Ia.push(f);d.reflow!==!1&&F(f,"load",function(){f.initReflow()});if(e)for(g in e)F(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=da?!1:n(d.animation,!0);f.pointCount=0;f.counters=new Ab; -f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=L[a.type||b.type||b.defaultSeriesType])||ka(17,!0);b=new b;b.init(this,a);return b},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&p(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h,i=this.isDirtyBox, -j=c.length,k=j,l=this.renderer,m=l.isHidden(),q=[];Oa(a,this);m&&this.cloneRenderTo();for(this.layOutTitles();k--;)if(a=c[k],a.options.stacking&&(g=!0,a.isDirty)){h=!0;break}if(h)for(k=j;k--;)if(a=c[k],a.options.stacking)a.isDirty=!0;p(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;g&&this.getStacks();if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,p(b,function(a){a.setScale()});this.adjustTickAmounts(); -this.getMargins();p(b,function(a){a.isDirty&&(i=!0)});p(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,q.push(function(){A(a,"afterSetExtremes",r(a.eventArgs,a.getExtremes()));delete a.eventArgs});(i||g)&&a.redraw()})}i&&this.drawChartBox();p(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);l.draw();A(this,"redraw");m&&this.cloneRenderTo(!0);p(q,function(a){a.call()})},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id=== -a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||[];for(b=0;b<e.length;b++)if(e[b].id===a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=ja(b.xAxis||{}),b=b.yAxis=ja(b.yAxis||{});p(c,function(a,b){a.index=b;a.isX=!0});p(b,function(a,b){a.index=b});c=c.concat(b);p(c,function(b){new qa(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];p(this.series,function(b){a=a.concat(wb(b.points||[], -function(a){return a.selected}))});return a},getSelectedSeries:function(){return wb(this.series,function(a){return a.selected})},getStacks:function(){var a=this;p(a.yAxis,function(a){if(a.stacks&&a.hasVisibleSeries)a.oldStacks=a.stacks});p(a.series,function(b){if(b.options.stacking&&(b.visible===!0||a.options.chart.ignoreHiddenSeries===!1))b.stackKey=b.type+n(b.options.stack,"")})},showResetZoom:function(){var a=this,b=G.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo=== -"chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=this;A(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?p(this.axes,function(a){b=a.zoom()}):p(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b= -e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&S(e))this.resetZoomButton=e.destroy();b&&this.redraw(n(this.options.chart.animation,a&&a.animation,this.pointCount<100))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&p(d,function(a){a.setState()});p(b==="xy"?[1,0]:[1],function(b){var d=a[b?"chartX":"chartY"],h=c[b?"xAxis":"yAxis"][0],i=c[b?"mouseDownX":"mouseDownY"],j=(h.pointRange||0)/2,k=h.getExtremes(),l=h.toValue(i-d,!0)+j,i=h.toValue(i+ -c[b?"plotWidth":"plotHeight"]-d,!0)-j;h.series.length&&l>I(k.dataMin,k.min)&&i<s(k.dataMax,k.max)&&(h.setExtremes(l,i,!1,!1,{trigger:"pan"}),e=!0);c[b?"mouseDownX":"mouseDownY"]=d});e&&c.redraw(!1);D(c.container,{cursor:"move"})},setTitle:function(a,b){var f;var c=this,d=c.options,e;e=d.title=x(d.title,a);f=d.subtitle=x(d.subtitle,b),d=f;p([["title",a,e],["subtitle",b,d]],function(a){var b=a[0],d=c[b],e=a[1],a=a[2];d&&e&&(c[b]=d=d.destroy());a&&a.text&&!d&&(c[b]=c.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align, -"class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add())});c.layOutTitles()},layOutTitles:function(){var a=0,b=this.title,c=this.subtitle,d=this.options,e=d.title,d=d.subtitle,f=this.spacingBox.width-44;if(b&&(b.css({width:(e.width||f)+"px"}).align(r({y:15},e),!1,"spacingBox"),!e.floating&&!e.verticalAlign))a=b.getBBox().height,a>=18&&a<=25&&(a=15);c&&(c.css({width:(d.width||f)+"px"}).align(r({y:a+e.margin},d),!1,"spacingBox"),!d.floating&&!d.verticalAlign&&(a=Ha(a+c.getBBox().height)));this.titleOffset= -a},getChartSize:function(){var a=this.options.chart,b=this.renderToClone||this.renderTo;this.containerWidth=jb(b,"width");this.containerHeight=jb(b,"height");this.chartWidth=s(0,a.width||this.containerWidth||600);this.chartHeight=s(0,n(a.height,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Na(b),delete this.renderToClone):(c&&c.parentNode===this.renderTo&&this.renderTo.removeChild(c),this.renderToClone= -b=this.renderTo.cloneNode(0),D(b,{position:"absolute",top:"-9999px",display:"block"}),y.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+ub++;if(fa(a))this.renderTo=a=y.getElementById(a);a||ka(13,!0);c=z(v(a,"data-highcharts-chart"));!isNaN(c)&&Ia[c]&&Ia[c].destroy();v(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight; -this.container=a=T(Ga,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},r({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new ua(a,c,d,!0):new Ya(a,c,d);da&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.spacing,b,c=this.legend,d=this.margin,e=this.options.legend, -f=n(e.margin,10),g=e.x,h=e.y,i=e.align,j=e.verticalAlign,k=this.titleOffset;this.resetMargins();b=this.axisOffset;if(k&&!t(d[0]))this.plotTop=s(this.plotTop,k+this.options.title.margin+a[0]);if(c.display&&!e.floating)if(i==="right"){if(!t(d[1]))this.marginRight=s(this.marginRight,c.legendWidth-g+f+a[1])}else if(i==="left"){if(!t(d[3]))this.plotLeft=s(this.plotLeft,c.legendWidth+g+f+a[3])}else if(j==="top"){if(!t(d[0]))this.plotTop=s(this.plotTop,c.legendHeight+h+f+a[0])}else if(j==="bottom"&&!t(d[2]))this.marginBottom= -s(this.marginBottom,c.legendHeight-h+f+a[2]);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&p(this.axes,function(a){a.getOffset()});t(d[3])||(this.plotLeft+=b[3]);t(d[0])||(this.plotTop+=b[0]);t(d[2])||(this.marginBottom+=b[2]);t(d[1])||(this.marginRight+=b[1]);this.setChartSize()},reflow:function(a){var b=this,c=b.options.chart,d=b.renderTo,e=c.width||jb(d,"width"),f=c.height||jb(d,"height"),c= -a?a.target:C,d=function(){if(b.container)b.setSize(e,f,!1),b.hasUserSize=null};if(!b.hasUserSize&&e&&f&&(c===C||c===y)){if(e!==b.containerWidth||f!==b.containerHeight)clearTimeout(b.reflowTimeout),a?b.reflowTimeout=setTimeout(d,100):d();b.containerWidth=e;b.containerHeight=f}},initReflow:function(){var a=this,b=function(b){a.reflow(b)};F(C,"resize",b);F(a,"destroy",function(){X(C,"resize",b)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&A(d,"endResize",null,function(){d.isResizing-= -1})};Oa(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(t(a))d.chartWidth=e=s(0,w(a)),d.hasUserSize=!!e;if(t(b))d.chartHeight=f=s(0,w(b));(oa?kb:D)(d.container,{width:e+"px",height:f+"px"},oa);d.setChartSize(!0);d.renderer.setSize(e,f,c);d.maxTicks=null;p(d.axes,function(a){a.isDirty=!0;a.setScale()});p(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;A(d,"resize");oa===!1?g():setTimeout(g,oa&&oa.duration|| -500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=this.spacing,h=this.clipOffset,i,j,k,l;this.plotLeft=i=w(this.plotLeft);this.plotTop=j=w(this.plotTop);this.plotWidth=k=s(0,w(d-i-this.marginRight));this.plotHeight=l=s(0,w(e-j-this.marginBottom));this.plotSizeX=b?l:k;this.plotSizeY=b?k:l;this.plotBorderWidth=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:e-g[0]-g[2]};this.plotBox= -c.plotBox={x:i,y:j,width:k,height:l};d=2*N(this.plotBorderWidth/2);b=Ha(s(d,h[3])/2);c=Ha(s(d,h[0])/2);this.clipBox={x:b,y:c,width:N(this.plotSizeX-s(d,h[1])/2-b),height:N(this.plotSizeY-s(d,h[2])/2-c)};a||p(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this.spacing,b=this.margin;this.plotTop=n(b[0],a[0]);this.marginRight=n(b[1],a[1]);this.marginBottom=n(b[2],a[2]);this.plotLeft=n(b[3],a[3]);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a= -this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,m=a.plotBorderWidth||0,q,o=this.plotLeft,n=this.plotTop,p=this.plotWidth,s=this.plotHeight,r=this.plotBox,t=this.clipRect,w=this.clipBox;q=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-q,d-q));else{e={fill:j||Q};if(i)e.stroke=a.borderColor,e["stroke-width"]= -i;this.chartBackground=b.rect(q/2,q/2,c-q,d-q,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?f.animate(r):this.plotBackground=b.rect(o,n,p,s,0).attr({fill:k}).add().shadow(a.plotShadow);if(l)h?h.animate(r):this.plotBGImage=b.image(l,o,n,p,s).add();t?t.animate({width:w.width,height:w.height}):this.clipRect=b.clipRect(w);if(m)g?g.animate(g.crisp(null,o,n,p,s)):this.plotBorder=b.rect(o,n,p,s,0,-m).attr({stroke:a.plotBorderColor,"stroke-width":m,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a= -this,b=a.options.chart,c,d=a.options.series,e,f;p(["inverted","angular","polar"],function(g){c=L[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=L[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;p(b,function(a){a.linkedSeries.length=0});p(b,function(b){var d=b.options.linkedTo;if(fa(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),b.linkedParent=d})},render:function(){var a=this,b=a.axes, -c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new zb(a,d.legend);a.getStacks();p(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;p(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&p(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();p(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&p(e.items,function(b){var d= -r(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;A(a,"destroy");Ia[a.index]=u;a.renderTo.removeAttribute("data-highcharts-chart"); -X(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();p("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",X(d),f&&Na(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!V&&C==C.top&&y.readyState!=="complete"||da&&!C.canvg?(da?Mb.push(function(){a.firstRender()}, -a.options.global.canvasToolsURL):y.attachEvent("onreadystatechange",function(){y.detachEvent("onreadystatechange",a.firstRender);y.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),A(a,"init"),a.resetMargins(),a.setChartSize(),a.propFromSeries(),a.getAxes(),p(b.series||[],function(b){a.initSeries(b)}),a.linkSeries(),A(a,"beforeRender"),a.pointer=new $a(a,b),a.render(),a.renderer.draw(),c&&c.apply(a, -[a]),p(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),A(a,"load")},splashArray:function(a,b){var c=b[a],c=S(c)?c:[c,c,c,c];return[n(b[a+"Top"],c[0]),n(b[a+"Right"],c[1]),n(b[a+"Bottom"],c[2]),n(b[a+"Left"],c[3])]}};fb.prototype.callbacks=[];var xb=Highcharts.CenteredSeriesMixin={getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-2*c,b=a.center,a=[n(b[0],"50%"),n(b[1],"50%"),a.size||"100%",a.innerSize||0],g=I(e,f),h;return Ra(a, -function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*z(a)/100:a)+(d?c:0)})}},Ja=function(){};Ja.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ja.prototype.optionsToObject.call(this,a);r(this, -a);this.options=this.options?r(this.options,a):a;if(d)this.y=this[d];if(this.x===u&&c)this.x=b===u?c.autoIncrement():b;return this},optionsToObject:function(a){var b={},c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b[d[0]]=a;else if(Ka(a)){if(a.length>e){c=typeof a[0];if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers= -!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),ha(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)X(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category, -y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series,e=d.chart,a=n(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=c.options.selected=a;d.options.data[sa(c,d.data)]=c.options;c.setState(a&&"select");b||p(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[sa(a,d.data)]=a.options,a.setState(""), -a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||sa(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=n(c.valueDecimals, -""),e=c.valuePrefix||"",f=c.valueSuffix||"";p(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return Fa(a,{point:this,series:this.series})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});A(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a= -x(this.series.options.point,this.options).events,b;this.events=a;for(b in a)F(this,b,a[b]);this.hasImportedEvents=!0}},setState:function(a,b){var c=this.plotX,d=this.plotY,e=this.series,f=e.options.states,g=Y[e.type].marker&&e.options.marker,h=g&&!g.enabled,i=g&&g.states[a],j=i&&i.enabled===!1,k=e.stateMarkerGraphic,l=this.marker||{},m=e.chart,q=this.pointAttr,a=a||"",b=b&&k;if(!(a===this.state&&!b||this.selected&&a!=="select"||f[a]&&f[a].enabled===!1||a&&(j||h&&!i.enabled)||a&&l.states&&l.states[a]&& -l.states[a].enabled===!1)){if(this.graphic)f=g&&this.graphic.symbolName&&q[a].r,this.graphic.attr(x(q[a],f?{x:c-f,y:d-f,width:2*f,height:2*f}:{}));else{if(a&&i)if(f=i.radius,l=l.symbol||e.symbol,k&&k.currentSymbol!==l&&(k=k.destroy()),k)k[b?"animate":"attr"]({x:c-f,y:d-f});else e.stateMarkerGraphic=k=m.renderer.symbol(l,c-f,d-f,2*f,2*f).attr(q[a]).add(e.markerGroup),k.currentSymbol=l;if(k)k[a&&m.isInsidePlot(c,d,m.inverted)?"show":"hide"]()}this.state=a}}};var O=function(){};O.prototype={isCartesian:!0, -type:"line",pointClass:Ja,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],init:function(a,b){var c=this,d,e,f=a.series,g=function(a,b){return n(a.options.index,a._i)-n(b.options.index,b._i)};c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();r(c,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if(da)b.animation= -!1;e=b.events;for(d in e)F(c,d,e[d]);if(e&&e.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor();c.getSymbol();p(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);if(c.isCartesian)a.hasCartesianSeries=!0;f.push(c);c._i=f.length-1;ob(f,g);this.yAxis&&ob(this.yAxis.series,g);p(f,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)})},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;p(a.axisTypes||[],function(e){p(c[e],function(c){d= -c.options;if(b[e]===d.index||b[e]!==u&&b[e]===d.id||b[e]===u&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});!a[e]&&a.optionalAxis!==e&&ka(18,!0)})},updateParallelArrays:function(a,b){var c=a.series,d=arguments;p(c.parallelArrays,typeof b==="number"?function(d){var f=d==="y"&&c.toYData?c.toYData(a):a[d];c[d+"Data"][b]=f}:function(a){Array.prototype[b].apply(c[a+"Data"],Array.prototype.slice.call(d,2))})},autoIncrement:function(){var a=this.options,b=this.xIncrement,b=n(b,a.pointStart,0);this.pointInterval= -n(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else p(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart,c=b.options.plotOptions,b=b.userOptions||{},d=b.plotOptions||{},e=c[this.type];this.userOptions= -a;c=x(e,c.series,a);this.tooltipOptions=x(G.tooltip,G.plotOptions[this.type].tooltip,b.tooltip,d.series&&d.series.tooltip,d[this.type]&&d[this.type].tooltip,a.tooltip);e.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||Y[this.type].color;if(!e&&!a.colorByPoint)t(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a= -this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,c=c.counters;this.symbol=b.symbol;if(!this.symbol)t(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:R.drawLineMarker,setData:function(a,b){var c=this,d=c.points,e=c.options,f=c.chart,g=null,h=c.xAxis,i=h&&!!h.categories,j;c.xIncrement=null;c.pointRange=i?1:e.pointRange;c.colorCounter=0;var a=a||[],k=a.length; -j=e.turboThreshold;var l=this.xData,m=this.yData,q=c.pointArrayMap,q=q&&q.length;p(this.parallelArrays,function(a){c[a+"Data"].length=0});if(j&&k>j){for(j=0;g===null&&j<k;)g=a[j],j++;if(wa(g)){i=n(e.pointStart,0);e=n(e.pointInterval,1);for(j=0;j<k;j++)l[j]=i,m[j]=a[j],i+=e;c.xIncrement=i}else if(Ka(g))if(q)for(j=0;j<k;j++)e=a[j],l[j]=e[0],m[j]=e.slice(1,q+1);else for(j=0;j<k;j++)e=a[j],l[j]=e[0],m[j]=e[1];else ka(12)}else for(j=0;j<k;j++)if(a[j]!==u&&(e={series:c},c.pointClass.prototype.applyOptions.apply(e, -[a[j]]),c.updateParallelArrays(e,j),i&&e.name))h.names[e.x]=e.name;fa(m[0])&&ka(14,!0);c.data=[];c.options.data=a;for(j=d&&d.length||0;j--;)d[j]&&d[j].destroy&&d[j].destroy();if(h)h.minRange=h.userMinRange;c.isDirty=c.isDirtyData=f.isDirtyBox=!0;n(b,!0)&&f.redraw(!1)},processData:function(a){var b=this.xData,c=this.yData,d=b.length,e;e=0;var f,g,h=this.xAxis,i=this.options,j=i.cropThreshold,k=this.isCartesian;if(k&&!this.isDirty&&!h.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(k&&this.sorted&&(!j|| -d>j||this.forceCrop))if(a=h.min,h=h.max,b[d-1]<a||b[0]>h)b=[],c=[];else if(b[0]<a||b[d-1]>h)e=this.cropData(this.xData,this.yData,a,h),b=e.xData,c=e.yData,e=e.start,f=!0;for(h=b.length-1;h>=0;h--)d=b[h]-b[h-1],d>0&&(g===u||d<g)?g=d:d<0&&this.requireSorting&&ka(15);this.cropped=f;this.cropStart=e;this.processedXData=b;this.processedYData=c;if(i.pointRange===null)this.pointRange=g||1;this.closestPointRange=g},cropData:function(a,b,c,d){var e=a.length,f=0,g=e,h=n(this.cropShoulder,1),i;for(i=0;i<e;i++)if(a[i]>= -c){f=s(0,i-h);break}for(;i<e;i++)if(a[i]>d){g=i+h;break}return{xData:a.slice(f,g),yData:b.slice(f,g),start:f,end:g}},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,l=[],m;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(m=0;m<g;m++)i=h+m,j?l[m]=(new f).init(this,[d[m]].concat(ja(e[m]))):(b[i]?k=b[i]:a[i]!==u&&(b[i]=k=(new f).init(this,a[i],d[m])),l[m]=k);if(b&& -(g!==(c=b.length)||j))for(m=0;m<c;m++)if(m===h&&!j&&(m+=g),b[m])b[m].destroyElements(),b[m].plotX=u;this.data=b;this.points=l},setStackedPoints:function(){if(this.options.stacking&&!(this.visible!==!0&&this.chart.options.chart.ignoreHiddenSeries!==!1)){var a=this.processedXData,b=this.processedYData,c=[],d=b.length,e=this.options,f=e.threshold,g=e.stack,e=e.stacking,h=this.stackKey,i="-"+h,j=this.negStacks,k=this.yAxis,l=k.stacks,m=k.oldStacks,q,o,n,p,r;for(n=0;n<d;n++){p=a[n];r=b[n];o=(q=j&&r<f)? -i:h;l[o]||(l[o]={});if(!l[o][p])m[o]&&m[o][p]?(l[o][p]=m[o][p],l[o][p].total=null):l[o][p]=new Gb(k,k.options.stackLabels,q,p,g,e);o=l[o][p];o.points[this.index]=[o.cum||0];e==="percent"?(q=q?h:i,j&&l[q]&&l[q][p]?(q=l[q][p],o.total=q.total=s(q.total,o.total)+M(r)||0):o.total=aa(o.total+(M(r)||0))):o.total=aa(o.total+(r||0));o.cum=(o.cum||0)+(r||0);o.points[this.index].push(o.cum);c[n]=o.cum}if(e==="percent")k.usePercentage=!0;this.stackedYData=c;k.oldStacks={}}},setPercentStacks:function(){var a= -this,b=a.stackKey,c=a.yAxis.stacks;p([b,"-"+b],function(b){var d;for(var e=a.xData.length,f,g;e--;)if(f=a.xData[e],d=(g=c[b]&&c[b][f])&&g.points[a.index],f=d)g=g.total?100/g.total:0,f[0]=aa(f[0]*g),f[1]=aa(f[1]*g),a.stackedYData[e]=f[1]})},getExtremes:function(a){var b=this.yAxis,c=this.processedXData,d,e=[],f=0;d=this.xAxis.getExtremes();var g=d.min,h=d.max,i,j,k,l,a=a||this.stackedYData||this.processedYData;d=a.length;for(l=0;l<d;l++)if(j=c[l],k=a[l],i=k!==null&&k!==u&&(!b.isLog||k.length||k>0), -j=this.getExtremesFromAll||this.cropped||(c[l+1]||j)>=g&&(c[l-1]||j)<=h,i&&j)if(i=k.length)for(;i--;)k[i]!==null&&(e[f++]=k[i]);else e[f++]=k;this.dataMin=n(void 0,La(e));this.dataMax=n(void 0,za(e))},translate:function(){this.processedXData||this.processData();this.generatePoints();for(var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i=a.pointPlacement,j=i==="between"||wa(i),k=a.threshold,a=0;a<g;a++){var l=f[a],m=l.x,q=l.y,o= -l.low,p=b&&e.stacks[(this.negStacks&&q<k?"-":"")+this.stackKey];if(e.isLog&&q<=0)l.y=q=null;l.plotX=c.translate(m,0,0,0,1,i,this.type==="flags");if(b&&this.visible&&p&&p[m])p=p[m],q=p.points[this.index],o=q[0],q=q[1],o===0&&(o=n(k,e.min)),e.isLog&&o<=0&&(o=null),l.total=l.stackTotal=p.total,l.percentage=b==="percent"&&l.y/p.total*100,l.stackY=q,p.setOffset(this.pointXOffset||0,this.barW||0);l.yBottom=t(o)?e.translate(o,0,1,0,1):null;h&&(q=this.modifyValue(q,l));l.plotY=typeof q==="number"&&q!==Infinity? -e.translate(q,0,1,0,1):u;l.clientX=j?c.translate(m,0,0,0,1):l.plotX;l.negative=l.y<(k||0);l.category=d&&d[l.x]!==u?d[l.x]:l.x}this.getSegments()},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||e.len:this.chart.plotSizeX,h,i,j=[];if(this.options.enableMouseTracking!==!1){if(a)this.tooltipPoints=null;p(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(i= -0;i<a;i++)if(e=b[i],c=e.x,c>=f.min&&c<=f.max){h=b[i+1];c=d===u?0:d+1;for(d=b[i+1]?I(s(0,N((e.clientX+(h?h.wrappedClientX||h.clientX:g))/2)),g):g;c>=0&&c<=d;)j[c++]=e}this.tooltipPoints=j}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.dateTimeLabelFormats,d=b.xDateFormat,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!d){if(e)for(g in E){if(E[g]>=e){d=c[g];break}}else d=c.day;d=d||c.year}f&&d&&wa(a.key)&&(b=b.replace("{point.key}", -"{point.key:"+d+"}"));return Fa(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&A(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&A(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b= -this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!S(e))e=Y[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],e=c[h+"m"],a||(c[h]=a=d.clipRect(r(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()}, -e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,l=this.options.marker,m,q=this.markerGroup;if(l.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=N(g.plotX),e=g.plotY,k=g.graphic,i=g.marker||{},a=l.enabled&&i.enabled=== -u||i.enabled,m=c.isInsidePlot(w(d),e,c.inverted),a&&e!==u&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=n(i.symbol,this.symbol),j=i.indexOf("url")===0,k)k.attr({visibility:m?V?"inherit":"visible":"hidden"}).animate(r({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(m&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(q)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{}, -c=c||{},d=d||{};for(f in e)g=e[f],h[f]=n(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=Y[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color,h={stroke:g,fill:g},i=a.points||[],j=[],k,l=a.pointAttrToOptions,m=b.negativeColor,n=c.lineColor,o=c.fillColor,s;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||ta(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);p(["hover","select"],function(b){j[b]=a.convertAttribs(d[b], -j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&m)h.color=h.fillColor=m;k=b.colorByPoint||h.color;if(h.options)for(s in l)t(c[l[s]])&&(k=!0);if(k){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=ta(f.color||h.color).brighten(f.brightness||e.brightness).get();f={color:h.color};if(!o)f.fillColor=h.color;if(!n)f.lineColor=h.color;k[""]=a.convertAttribs(r(f,c),j[""]);k.hover=a.convertAttribs(d.hover, -j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""])}else k=j;h.pointAttr=k}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(ra),d,e,f=a.data||[],g,h,i;A(a,"destroy");X(a);p(a.axisTypes||[],function(b){if(i=a[b])ha(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);p("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","), -function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ha(b.series,a);for(h in a)delete a[h]},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;p(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},getGraphPath:function(){var a= -this,b=[],c,d=[];p(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=b.linecap!=="square",g=this.getGraphPath(),h=b.negativeColor;h&&c.push(["graphNeg",h]);p(c,function(c,h){var k=c[0],l=a[k];if(l)Za(l),l.animate({d:g});else if(d&&g.length)l={stroke:c[1],"stroke-width":d,zIndex:1},e?l.dashstyle=e:f&&(l["stroke-linecap"]= -l["stroke-linejoin"]="round"),a[k]=a.chart.renderer.path(g).attr(l).add(a.group).shadow(!h&&b.shadow)})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor||a.negativeFillColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=s(e,j),l=this.yAxis;if(d&&(f||g)){d=w(l.toPixels(a.threshold||0,!0));d<0&&(k-=d);a={x:0,y:0,width:k,height:d};k={x:0,y:d,width:k,height:k};if(b.inverted)a.height=k.y=b.plotWidth-d,c.isVML&&(a={x:b.plotWidth- -d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,height:e});l.reversed?(b=k,e=a):(b=a,e=k);h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&this.graphNeg&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))}},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};p(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)F(c,"resize",a),F(b, -"destroy",function(){X(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=!f;g&&(this[a]=f=this.chart.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"](this.getPlotBox());return f},getPlotBox:function(){return{translateX:this.xAxis?this.xAxis.left:this.chart.plotLeft,translateY:this.yAxis?this.yAxis.top:this.chart.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG, -e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup","markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels&&this.drawDataLabels();this.visible&&this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&& -!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:n(d&&d.left,a.plotLeft),translateY:n(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&A(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg, -e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a,b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===u?!h:a)?"show":"hide";p(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c, -a);c.isDirty=!0;c.options.stacking&&p(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});p(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();A(c,f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===u?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;A(this,a?"select":"unselect")},drawTracker:J.drawTrackerGraph};r(fb.prototype,{addSeries:function(a,b,c){var d,e=this;a&&(b= -n(b,!0),A(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;e.linkSeries();b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var e=b?"xAxis":"yAxis",f=this.options;new qa(this,x(a,{index:this[e].length,isX:b}));f[e]=ja(f[e]||{});f[e].push(a);n(c,!0)&&this.redraw(d)},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=T(Ga,{className:"highcharts-loading"},r(d.style,{zIndex:10,display:Q}),this.container),this.loadingSpan=T("span", -null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)D(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),kb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&kb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){D(b,{display:Q})}});this.loadingShown=!1}});r(Ja.prototype,{update:function(a, -b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=e.chart,j=e.options,b=n(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);if(S(a)){e.getAttribs();if(f)a&&a.marker&&a.marker.symbol?d.graphic=f.destroy():f.attr(d.pointAttr[d.state||""]);if(a&&a.dataLabels&&d.dataLabel)d.dataLabel=d.dataLabel.destroy()}g=sa(d,h);e.updateParallelArrays(d,g);j.data[g]=d.options;e.isDirty=e.isDirtyData=!0;if(!e.fixedBox&&e.hasCartesianSeries)i.isDirtyBox=!0;j.legendType==="point"&&i.legend.destroyItem(d); -b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.points,f=d.chart,g,h=d.data;Oa(b,f);a=n(a,!0);c.firePointEvent("remove",null,function(){g=sa(c,h);h.length===e.length&&e.splice(g,1);h.splice(g,1);d.options.data.splice(g,1);d.updateParallelArrays(c,"splice",g,1);c.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&f.redraw()})}});r(O.prototype,{addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xAxis&&this.xAxis.names,k=g&&g.shift||0,l=e.data, -m,q=this.xData;Oa(d,i);c&&p([g,h,this.graphNeg,this.areaNeg],function(a){if(a)a.shift=k+1});if(h)h.isArea=!0;b=n(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);g=d.x;h=q.length;if(this.requireSorting&&g<q[h-1])for(m=!0;h&&q[h-1]>g;)h--;this.updateParallelArrays(d,"splice",h,0,0);this.updateParallelArrays(d,h);if(j)j[g]=d.name;l.splice(h,0,a);m&&(this.data.splice(h,0,null),this.processData());e.legendType==="point"&&this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1): -(f.shift(),this.updateParallelArrays(d,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&(this.getAttribs(),i.redraw())},remove:function(a,b){var c=this,d=c.chart,a=n(a,!0);if(!c.isRemoving)c.isRemoving=!0,A(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;d.linkSeries();a&&d.redraw(b)});c.isRemoving=!1},update:function(a,b){var c=this.chart,d=this.type,e=L[d].prototype,f,a=x(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},{data:this.options.data}, -a);this.remove(!1);for(f in e)e.hasOwnProperty(f)&&(this[f]=u);r(this,L[a.type||d].prototype);this.init(c,a);n(b,!0)&&c.redraw(!1)}});r(qa.prototype,{update:function(a,b){var c=this.chart,a=c.options[this.coll][this.options.index]=x(this.userOptions,a);this.destroy(!0);this._addedPlotLB=this.userMin=this.userMax=u;this.init(c,r(a,{events:u}));c.isDirtyBox=!0;n(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.coll;p(this.series,function(a){a.remove(!1)});ha(b.axes,this);ha(b[c],this); -b.options[c].splice(this.options.index,1);p(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;n(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}});var ca=ia(O);L.line=ca;Y.area=x(W,{threshold:0});var Ua=ia(O,{type:"area",getSegments:function(){var a=[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k=this.options.connectNulls,l,m,n;if(this.options.stacking&&!this.cropped){for(m= -0;m<j.length;m++)g[j[m].x]=j[m];for(n in f)f[n].total!==null&&c.push(+n);c.sort(function(a,b){return a-b});p(c,function(a){if(!k||g[a]&&g[a].y!==null)g[a]?b.push(g[a]):(h=d.translate(a),l=f[a].percent?f[a].total?f[a].cum*100/f[a].total:0:f[a].cum,i=e.toPixels(l,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:la}))});b.length&&a.push(b)}else O.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=O.prototype.getSegmentPath.call(this,a), -c=[].concat(b),d,e=this.options;d=b.length;var f=this.yAxis.getThreshold(e.threshold),g;d===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)g=n(a[d].yBottom,f),d<a.length-1&&e.step&&c.push(a[d+1].plotX,g),c.push(a[d].plotX,g);else this.closeSegment(c,a,f);this.areaPath=this.areaPath.concat(c);return b},closeSegment:function(a,b,c){a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];O.prototype.drawGraph.apply(this);var a=this, -b=this.areaPath,c=this.options,d=c.negativeColor,e=c.negativeFillColor,f=[["area",this.color,c.fillColor]];(d||e)&&f.push(["areaNeg",d,e]);p(f,function(d){var e=d[0],f=a[e];f?f.animate({d:b}):a[e]=a.chart.renderer.path(b).attr({fill:n(d[2],ta(d[1]).setOpacity(n(c.fillOpacity,0.75)).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:R.drawRectangle});L.area=Ua;Y.spline=x(W);ca=ia(O,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j= -g.plotX;var g=g.plotY,l;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;l=(k-i)*(j-d)/(j-h)+e-k;i+=l;k+=l;i>a&&i>e?(i=s(a,e),k=2*e-i):i<a&&i<e&&(i=I(a,e),k=2*e-i);k>g&&k>e?(k=s(g,e),i=2*e-k):k<g&&k<e&&(k=I(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?(b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=f.rightContY=null):b=["M",d,e];return b}});L.spline=ca;Y.areaspline=x(Y.area);Ua=Ua.prototype;ca=ia(ca,{type:"areaspline",closedStacks:!0,getSegmentPath:Ua.getSegmentPath, -closeSegment:Ua.closeSegment,drawGraph:Ua.drawGraph,drawLegendSymbol:R.drawRectangle});L.areaspline=ca;Y.column=x(W,{borderColor:"#FFFFFF",borderWidth:1,borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,shadow:!1},select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,threshold:0});ca=ia(O,{type:"column",pointAttrToOptions:{stroke:"borderColor", -"stroke-width":"borderWidth",fill:"color",r:"borderRadius"},cropShoulder:0,trackerGroups:["group","dataLabelsGroup"],negStacks:!0,init:function(){O.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},getColumnMetrics:function(){var a=this,b=a.options,c=a.xAxis,d=a.yAxis,e=c.reversed,f,g={},h,i=0;b.grouping===!1?i=1:p(a.chart.series,function(b){var c=b.options,e=b.yAxis;if(b.type===a.type&&b.visible&&d.len===e.len&&d.pos=== -e.pos)c.stacking?(f=b.stackKey,g[f]===u&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var c=I(M(c.transA)*(c.ordinalSlope||b.pointRange||c.closestPointRange||1),c.len),j=c*b.groupPadding,k=(c-2*j)/i,l=b.pointWidth,b=t(l)?(k-l)/2:k*b.pointPadding,l=n(l,k-2*b);return a.columnMetrics={width:l,offset:b+(j+((e?i-(a.columnIndex||0):a.columnIndex)||0)*k-c/2)*(e?-1:1)}},translate:function(){var a=this.chart,b=this.options,c=b.borderWidth,d=this.yAxis,e=this.translatedThreshold=d.getThreshold(b.threshold), -f=n(b.minPointLength,5),b=this.getColumnMetrics(),g=b.width,h=this.barW=Ha(s(g,1+2*c)),i=this.pointXOffset=b.offset,j=-(c%2?0.5:0),k=c%2?0.5:1;a.renderer.isVML&&a.inverted&&(k+=1);O.prototype.translate.apply(this);p(this.points,function(a){var b=n(a.yBottom,e),c=I(s(-999-b,a.plotY),d.len+999+b),o=a.plotX+i,p=h,r=I(c,b),t,c=s(c,b)-r;M(c)<f&&f&&(c=f,r=w(M(r-e)>f?b-f:e-(d.translate(a.y,0,1,0,1)<=e?f:0)));a.barX=o;a.pointWidth=g;b=M(o)<0.5;p=w(o+p)+j;o=w(o)+j;p-=o;t=M(r)<0.5;c=w(r+c)+k;r=w(r)+k;c-=r; -b&&(o+=1,p-=1);t&&(r-=1,c+=1);a.shapeType="rect";a.shapeArgs={x:o,y:r,width:p,height:c}})},getSymbol:la,drawLegendSymbol:R.drawRectangle,drawGraph:la,drawPoints:function(){var a=this,b=this.chart,c=a.options,d=b.renderer,e=b.options.animationLimit||250,f;p(a.points,function(g){var h=g.plotY,i=g.graphic;if(h!==u&&!isNaN(h)&&g.y!==null)f=g.shapeArgs,i?(Za(i),i[b.pointCount<e?"animate":"attr"](x(f))):g.graphic=d[g.shapeType](f).attr(g.pointAttr[g.selected?"select":""]).add(a.group).shadow(c.shadow,null, -c.stacking&&!c.borderRadius);else if(i)g.graphic=i.destroy()})},drawTracker:J.drawTrackerPoint,animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(V)a?(e.scaleY=0.001,a=I(b.pos+b.len,s(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},remove:function(){var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type=== -a.type)b.isDirty=!0});O.prototype.remove.apply(a,arguments)}});L.column=ca;Y.bar=x(Y.column);ca=ia(ca,{type:"bar",inverted:!0});L.bar=ca;Y.scatter=x(W,{lineWidth:0,tooltip:{headerFormat:'<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>",followPointer:!0},stickyTracking:!1});ca=ia(O,{type:"scatter",sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["markerGroup"],takeOrdinalPosition:!1,drawTracker:J.drawTrackerPoint, -drawGraph:function(){this.options.lineWidth&&O.prototype.drawGraph.call(this)},setTooltipPoints:la});L.scatter=ca;Y.pie=x(W,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});W={type:"pie",isCartesian:!1,pointClass:ia(Ja, -{init:function(){Ja.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;r(a,{visible:a.visible!==!1,name:n(a.name,"Slice")});b=function(b){a.slice(b.type==="select")};F(a,"select",b);F(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===u?!b.visible:a;c.options.data[sa(b,c.data)]=b.options;e=a?"show":"hide";p(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b, -a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Oa(c,d.chart);n(b,!0);this.sliced=this.options.sliced=a=t(a)?a:!this.sliced;d.options.data[sa(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth", -fill:"color"},getColor:la,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)p(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){O.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();n(b,!0)&&this.chart.redraw()},generatePoints:function(){var a,b=0,c,d,e,f=this.options.ignoreHiddenPoint;O.prototype.generatePoints.call(this); -c=this.points;d=c.length;for(a=0;a<d;a++)e=c[a],b+=f&&!e.visible?0:e.y;this.total=b;for(a=0;a<d;a++)e=c[a],e.percentage=b>0?e.y/b*100:0,e.total=b},translate:function(a){this.generatePoints();var b=0,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g,h,i=c.startAngle||0,j=this.startAngleRad=Aa/180*(i-90),i=(this.endAngleRad=Aa/180*((c.endAngle||i+360)-90))-j,k=this.points,l=c.dataLabels.distance,c=c.ignoreHiddenPoint,m,n=k.length,o;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){h= -P.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*U(h)*(a[2]/2+l)};for(m=0;m<n;m++){o=k[m];f=j+b*i;if(!c||o.visible)b+=o.percentage/100;g=j+b*i;o.shapeType="arc";o.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:w(f*1E3)/1E3,end:w(g*1E3)/1E3};h=(g+f)/2;h>0.75*i&&(h-=2*Aa);o.slicedTranslation={translateX:w(U(h)*d),translateY:w(ba(h)*d)};f=U(h)*a[2]/2;g=ba(h)*a[2]/2;o.tooltipPos=[a[0]+f*0.7,a[1]+g*0.7];o.half=h<-Aa/2||h>Aa/2?1:0;o.angle=h;e=I(e,l/2);o.labelPos=[a[0]+f+U(h)*l,a[1]+g+ba(h)*l,a[0]+ -f+U(h)*e,a[1]+g+ba(h)*e,a[0]+f,a[1]+g,l<0?"center":o.half?"right":"left",h]}},setTooltipPoints:la,drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);p(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};f&&f.attr(c);d?d.animate(r(g,c)):h.graphic=d=b.arc(g).setRadialReference(a.center).attr(h.pointAttr[h.selected? -"select":""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible!==void 0&&h.setVisible(h.visible)})},sortByAngle:function(a,b){a.sort(function(a,d){return a.angle!==void 0&&(d.angle-a.angle)*b})},drawTracker:J.drawTrackerPoint,drawLegendSymbol:R.drawRectangle,getCenter:xb.getCenter,getSymbol:la};W=ia(O,W);L.pie=W;O.prototype.drawDataLabels=function(){var a=this,b=a.options,c=b.cursor,d=b.dataLabels,b=a.points,e,f,g,h;if(d.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(d), -h=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",d.zIndex||6),f=d,p(b,function(b){var j,k=b.dataLabel,l,m,p=b.connector,o=!0;e=b.options&&b.options.dataLabels;j=n(e&&e.enabled,f.enabled);if(k&&!j)b.dataLabel=k.destroy();else if(j){d=x(f,e);j=d.rotation;l=b.getLabelConfig();g=d.format?Fa(d.format,l):d.formatter.call(l,d);d.style.color=n(d.color,d.style.color,a.color,"black");if(k)if(t(g))k.attr({text:g}),o=!1;else{if(b.dataLabel=k=k.destroy(),p)b.connector=p.destroy()}else if(t(g)){k= -{fill:d.backgroundColor,stroke:d.borderColor,"stroke-width":d.borderWidth,r:d.borderRadius||0,rotation:j,padding:d.padding,zIndex:1};for(m in k)k[m]===u&&delete k[m];k=b.dataLabel=a.chart.renderer[j?"text":"label"](g,0,-999,null,null,null,d.useHTML).attr(k).css(r(d.style,c&&{cursor:c})).add(h).shadow(d.shadow)}k&&a.alignDataLabel(b,k,d,null,o)}})};O.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=n(a.plotX,-999),i=n(a.plotY,-999),j=b.getBBox();if(a=this.visible&&(a.series.forceDL|| -f.isInsidePlot(a.plotX,a.plotY,g)))d=r({x:g?f.plotWidth-i:h,y:w(g?f.plotHeight-h:i),width:0,height:0},d),r(c,{width:j.width,height:j.height}),c.rotation?(g={align:c.align,x:d.x+c.x+d.width/2,y:d.y+c.y+d.height/2},b[e?"attr":"animate"](g)):(b.align(c,null,d),g=b.alignAttr,n(c.overflow,"justify")==="justify"?this.justifyDataLabel(b,c,g,j,d,e):n(c.crop,!0)&&(a=f.isInsidePlot(g.x,g.y)&&f.isInsidePlot(g.x+j.width,g.y+j.height)));if(!a)b.attr({y:-999}),b.placed=!1};O.prototype.justifyDataLabel=function(a, -b,c,d,e,f){var g=this.chart,h=b.align,i=b.verticalAlign,j,k;j=c.x;if(j<0)h==="right"?b.align="left":b.x=-j,k=!0;j=c.x+d.width;if(j>g.plotWidth)h==="left"?b.align="right":b.x=g.plotWidth-j,k=!0;j=c.y;if(j<0)i==="bottom"?b.verticalAlign="top":b.y=-j,k=!0;j=c.y+d.height;if(j>g.plotHeight)i==="top"?b.verticalAlign="bottom":b.y=g.plotHeight-j,k=!0;if(k)a.placed=!f,a.align(b,null,e)};if(L.pie)L.pie.prototype.drawDataLabels=function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=n(e.connectorPadding, -10),g=n(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=n(e.softConnector,!0),l=e.distance,m=a.center,q=m[2]/2,o=m[1],r=l>0,t,u,v,x,y=[[],[]],z,A,E,K,B,D=[0,0,0,0],I=function(a,b){return b.y-a.y};if(a.visible&&(e.enabled||a._hasPointLabels)){O.prototype.drawDataLabels.apply(a);p(b,function(a){a.dataLabel&&a.visible&&y[a.half].push(a)});for(K=0;!x&&b[K];)x=b[K]&&b[K].dataLabel&&(b[K].dataLabel.getBBox().height||21),K++;for(K=2;K--;){var b=[],J=[],F=y[K],G=F.length,C;a.sortByAngle(F,K-0.5);if(l> -0){for(B=o-q-l;B<=o+q+l;B+=x)b.push(B);u=b.length;if(G>u){c=[].concat(F);c.sort(I);for(B=G;B--;)c[B].rank=B;for(B=G;B--;)F[B].rank>=u&&F.splice(B,1);G=F.length}for(B=0;B<G;B++){c=F[B];v=c.labelPos;c=9999;var L,N;for(N=0;N<u;N++)L=M(b[N]-v[1]),L<c&&(c=L,C=N);if(C<B&&b[B]!==null)C=B;else for(u<G-B+C&&b[B]!==null&&(C=u-G+B);b[C]===null;)C++;J.push({i:C,y:b[C]});b[C]=null}J.sort(I)}for(B=0;B<G;B++){c=F[B];v=c.labelPos;t=c.dataLabel;E=c.visible===!1?"hidden":"visible";c=v[1];if(l>0){if(u=J.pop(),C=u.i, -A=u.y,c>A&&b[C+1]!==null||c<A&&b[C-1]!==null)A=c}else A=c;z=e.justify?m[0]+(K?-1:1)*(q+l):a.getX(C===0||C===b.length-1?c:A,K);t._attr={visibility:E,align:v[6]};t._pos={x:z+e.x+({left:f,right:-f}[v[6]]||0),y:A+e.y-10};t.connX=z;t.connY=A;if(this.options.size===null)u=t.width,z-u<f?D[3]=s(w(u-z+f),D[3]):z+u>h-f&&(D[1]=s(w(z+u-h+f),D[1])),A-x/2<0?D[0]=s(w(-A+x/2),D[0]):A+x/2>d&&(D[2]=s(w(A+x/2-d),D[2]))}}if(za(D)===0||this.verifyDataLabelOverflow(D))this.placeDataLabels(),r&&g&&p(this.points,function(b){i= -b.connector;v=b.labelPos;if((t=b.dataLabel)&&t._pos)E=t._attr.visibility,z=t.connX,A=t.connY,j=k?["M",z+(v[6]==="left"?5:-5),A,"C",z,A,2*v[2]-v[4],2*v[3]-v[5],v[2],v[3],"L",v[4],v[5]]:["M",z+(v[6]==="left"?5:-5),A,"L",v[2],v[3],"L",v[4],v[5]],i?(i.animate({d:j}),i.attr("visibility",E)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:E}).add(a.group);else if(i)b.connector=i.destroy()})}},L.pie.prototype.placeDataLabels=function(){p(this.points, -function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},L.pie.prototype.alignDataLabel=la,L.pie.prototype.verifyDataLabelOverflow=function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=s(b[2]-s(a[1],a[3]),c):(e=s(b[2]-a[1]-a[3],c),b[0]+=(a[3]-a[1])/2);d[1]!==null?e=s(I(e,b[2]-s(a[0],a[2])),c):(e=s(I(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),p(this.points,function(a){if(a.dataLabel)a.dataLabel._pos= -null}),this.drawDataLabels&&this.drawDataLabels()):f=!0;return f};if(L.column)L.column.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>n(this.translatedThreshold,f.plotSizeY),j=n(c.inside,!!this.options.stacking);if(h&&(d=x(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:0,d.height=0);c.align=n(c.align,!g||j?"center":i?"right":"left"); -c.verticalAlign=n(c.verticalAlign,g||j?"middle":i?"top":"bottom");O.prototype.alignDataLabel.call(this,a,b,c,d,e)};r(Highcharts,{Axis:qa,Chart:fb,Color:ta,Point:Ja,Tick:Qa,Tooltip:tb,Renderer:Ya,Series:O,SVGElement:pa,SVGRenderer:ua,arrayMin:La,arrayMax:za,charts:Ia,dateFormat:ab,format:Fa,pathAnim:vb,getOptions:function(){return G},hasBidiBug:Nb,isTouchDevice:Ib,numberFormat:Da,seriesTypes:L,setOptions:function(a){G=x(!0,G,a);Bb();return G},addEvent:F,removeEvent:X,createElement:T,discardElement:Na, -css:D,each:p,extend:r,map:Ra,merge:x,pick:n,splat:ja,extendClass:ia,pInt:z,wrap:Va,svg:V,canvas:da,vml:!V&&!da,product:"Highcharts",version:"3.0.9"})})(); -/* - Highcharts JS v3.0.9 (2014-01-15) - - (c) 2009-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(k,D){function J(a,b,c){this.init.call(this,a,b,c)}var N=k.arrayMin,O=k.arrayMax,t=k.each,z=k.extend,q=k.merge,P=k.map,r=k.pick,w=k.pInt,o=k.getOptions().plotOptions,h=k.seriesTypes,u=k.extendClass,K=k.splat,p=k.wrap,L=k.Axis,A=k.Tick,H=k.Point,Q=k.Pointer,R=k.TrackerMixin,S=k.CenteredSeriesMixin,x=k.Series,v=Math,E=v.round,B=v.floor,T=v.max,U=k.Color,s=function(){};z(J.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background={};d.options=a=q(e,a); -(a=a.background)&&t([].concat(K(a)).reverse(),function(a){var f=a.backgroundColor,a=q(d.defaultBackgroundOptions,a);if(f)a.backgroundColor=f;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"}}); -var G=L.prototype,A=A.prototype,V={getOffset:s,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:s,setCategories:s,setTitle:s},M={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,distance:15, -x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=q(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=S.getCenter.call(this.pane)},getLinePath:function(a,b){var c=this.center, -b=r(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center)this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/4:0):0},beforeSetTickPositions:function(){this.autoConnect&& -(this.max+=this.categories&&1||this.pointRange||this.closestPointRange||0)},setAxisSize:function(){G.setAxisSize.call(this);if(this.isRadial)this.center=this.pane.center=k.CenteredSeriesMixin.getCenter.call(this.pane),this.len=this.width=this.height=this.isCircular?this.center[2]*(this.endAngleRad-this.startAngleRad)/2:this.center[2]/2},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),r(b,this.center[2]/2)-this.offset)},postTranslate:function(a, -b){var c=this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,g=d[2]/2,f=[r(c.outerRadius,"100%"),c.innerRadius,r(c.thickness,10)],j=/%$/,n,l=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(l||(f[0]=this.translate(a),f[1]=this.translate(b)),f=P(f,function(a){j.test(a)&&(a=w(a,10)*g/100); -return a}),c.shape==="circle"||!l?(a=-Math.PI/2,b=Math.PI*1.5,n=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],f[0],f[0],{start:a,end:b,innerR:r(f[1],f[0]-f[2]),open:n}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),g,f,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(g= -d.xAxis[0],j=[],a=this.translate(a),c=g.tickPositions,g.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),t(c,function(c,b){f=g.getPosition(c,a);j.push(b?"L":"M",f.x,f.y)}));return j},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};p(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,g=c.isX,f=d&&g,j,n;n=b.options;var l=c.pane||0;if(d){if(z(this, -f?V:M),j=!g)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)z(this,M),this.defaultRadialOptions=(j=g)?this.defaultRadialXOptions:q(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!f&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[l]=b.panes[l]||new J(K(n.pane)[l],b,this),l=i);l=l.options;b.inverted=!1;n.chart.zoomType=null;this.startAngleRad=b=(l.startAngle-90)*Math.PI/180;this.endAngleRad=n=(r(l.endAngle,l.startAngle+360)-90)*Math.PI/ -180;this.offset=a.offset||0;if((this.isCircular=j)&&c.max===D&&n-b===2*Math.PI)this.autoConnect=!0}});p(A,"getPosition",function(a,b,c,d,e){var g=this.axis;return g.getPosition?g.getPosition(c):a.call(this,b,c,d,e)});p(A,"getLabelPosition",function(a,b,c,d,e,g,f,j,n){var l=this.axis,i=g.y,m=g.align,y=(l.translate(this.pos)+l.startAngleRad+Math.PI/2)/Math.PI*180%360;l.isRadial?(a=l.getPosition(this.pos,l.center[2]/2+r(g.distance,-25)),g.rotation==="auto"?d.attr({rotation:y}):i===null&&(i=l.chart.renderer.fontMetrics(d.styles.fontSize).b- -d.getBBox().height/2),m===null&&(m=l.isCircular?y>20&&y<160?"left":y>200&&y<340?"right":"center":"center",d.attr({align:m})),a.x+=g.x,a.y+=i):a=a.call(this,b,c,d,e,g,f,j,n);return a});p(A,"getMarkPath",function(a,b,c,d,e,g,f){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,g,f);return b});o.arearange=q(o.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'}, -trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=u(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;t(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});x.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);t(this.points, -function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.translate(d,0,1,0,1)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.translate(d,0,1,0,1))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=x.prototype.getSegmentPath,g,f;f=this.options;var j=f.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)g=a[d],g.plotHigh!==null&&c.push({plotX:g.plotX,plotY:g.plotHigh});a=e.call(this,b);if(j)j=== -!0&&(j="left"),f.step={left:"right",center:"center",right:"left"}[j];c=e.call(this,c);f.step=j;f=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return f},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=x.prototype,g=this.options.dataLabels,f,j=this.chart.inverted;if(g.enabled||this._hasPointLabels){for(c=b;c--;)f=a[c],f.y=f.high,f.plotY=f.plotHigh,d[c]=f.dataLabel,f.dataLabel=f.dataLabelUpper,f.below=!1,j?(g.align="left",g.x=g.xHigh):g.y=g.yHigh;e.drawDataLabels&&e.drawDataLabels.apply(this, -arguments);for(c=b;c--;)f=a[c],f.dataLabelUpper=f.dataLabel,f.dataLabel=d[c],f.y=f.low,f.plotY=f.plotLow,f.below=!0,j?(g.align="right",g.x=g.xLow):g.y=g.yLow;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments)}},alignDataLabel:function(){h.column.prototype.alignDataLabel.apply(this,arguments)},getSymbol:h.column.prototype.getSymbol,drawPoints:s});o.areasplinerange=q(o.arearange);h.areasplinerange=u(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});(function(){var a= -h.column.prototype;o.columnrange=q(o.column,o.arearange,{lineWidth:1,pointRange:null});h.columnrange=u(h.arearange,{type:"columnrange",translate:function(){var b=this,c=b.yAxis,d;a.translate.apply(b);t(b.points,function(a){var g=a.shapeArgs,f=b.options.minPointLength,j;a.plotHigh=d=c.translate(a.high,0,1,0,1);a.plotLow=a.plotY;j=d;a=a.plotY-d;a<f&&(f-=a,a+=f,j-=f/2);g.height=a;g.y=j})},trackerGroups:["group","dataLabels"],drawGraph:s,pointAttrToOptions:a.pointAttrToOptions,drawPoints:a.drawPoints, -drawTracker:a.drawTracker,animate:a.animate,getColumnMetrics:a.getColumnMetrics})})();o.gauge=q(o.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",borderRadius:3,crop:!1,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});H={type:"gauge",pointClass:u(H,{setState:function(a){this.state=a}}),angular:!0,drawGraph:s,fixedBox:!0,forceDL:!0,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options, -c=a.center;this.generatePoints();t(this.points,function(d){var e=q(b.dial,d.dial),g=w(r(e.radius,80))*c[2]/200,f=w(r(e.baseLength,70))*g/100,j=w(r(e.rearLength,10))*g/100,n=e.baseWidth||3,l=e.topWidth||1,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs={d:e.path||["M",-j,-n/2,"L",f,-n/2,g,-l/2,g,l/2,f,n/2,-j,n/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY= -c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,g=a.chart.renderer;t(a.points,function(f){var c=f.graphic,b=f.shapeArgs,e=b.d,i=q(d.dial,f.dial);c?(c.animate(b),b.d=e):f.graphic=g[f.shapeType](b).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:b.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=g.circle(0,0,r(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor|| -"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)t(b.points,function(a){var d=a.graphic;d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);x.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:function(a, -b){x.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();r(b,!0)&&this.chart.redraw()},drawTracker:R.drawTrackerPoint};h.gauge=u(h.line,H);o.boxplot=q(o.column,{fillColor:"#FFFFFF",lineWidth:1,medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>Maximum: {point.high}<br/>Upper quartile: {point.q3}<br/>Median: {point.median}<br/>Lower quartile: {point.q1}<br/>Minimum: {point.low}<br/>'}, -whiskerLength:"50%",whiskerWidth:2});h.boxplot=u(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:s,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);t(this.points,function(c){t(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a= -this,b=a.points,c=a.options,d=a.chart.renderer,e,g,f,j,n,l,i,m,y,h,k,I,o,p,q,u,x,s,v,w,A,z,F=a.doQuartiles!==!1,C=parseInt(a.options.whiskerLength,10)/100;t(b,function(b){y=b.graphic;A=b.shapeArgs;k={};p={};u={};z=b.color||a.color;if(b.plotY!==D)if(e=b.pointAttr[b.selected?"selected":""],x=A.width,s=B(A.x),v=s+x,w=E(x/2),g=B(F?b.q1Plot:b.lowPlot),f=B(F?b.q3Plot:b.lowPlot),j=B(b.highPlot),n=B(b.lowPlot),k.stroke=b.stemColor||c.stemColor||z,k["stroke-width"]=r(b.stemWidth,c.stemWidth,c.lineWidth),k.dashstyle= -b.stemDashStyle||c.stemDashStyle,p.stroke=b.whiskerColor||c.whiskerColor||z,p["stroke-width"]=r(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||z,u["stroke-width"]=r(b.medianWidth,c.medianWidth,c.lineWidth),u["stroke-linecap"]="round",i=k["stroke-width"]%2/2,m=s+w+i,h=["M",m,f,"L",m,j,"M",m,g,"L",m,n,"z"],F&&(i=e["stroke-width"]%2/2,m=B(m)+i,g=B(g)+i,f=B(f)+i,s+=i,v+=i,I=["M",s,f,"L",s,g,"L",v,g,"L",v,f,"L",s,f,"z"]),C&&(i=p["stroke-width"]%2/2,j+=i,n+=i,o=["M",m- -w*C,j,"L",m+w*C,j,"M",m-w*C,n,"L",m+w*C,n]),i=u["stroke-width"]%2/2,l=E(b.medianPlot)+i,q=["M",s,l,"L",v,l,"z"],y)b.stem.animate({d:h}),C&&b.whiskers.animate({d:o}),F&&b.box.animate({d:I}),b.medianShape.animate({d:q});else{b.graphic=y=d.g().add(a.group);b.stem=d.path(h).attr(k).add(y);if(C)b.whiskers=d.path(o).attr(p).add(y);if(F)b.box=d.path(I).attr(e).add(y);b.medianShape=d.path(q).attr(u).add(y)}})}});o.errorbar=q(o.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'}, -whiskerWidth:null});h.errorbar=u(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});o.waterfall=q(o.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=u(h.column,{type:"waterfall",upColorProp:"fill",pointArrayMap:["low","y"],pointValKey:"y",init:function(a, -b){b.stacking=!0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.options,b=this.yAxis,c,d,e,g,f,j,n,l,i;c=a.threshold;a=a.borderWidth%2/2;h.column.prototype.translate.apply(this);l=c;e=this.points;for(d=0,c=e.length;d<c;d++){g=e[d];f=g.shapeArgs;j=this.getStack(d);i=j.points[this.index];if(isNaN(g.y))g.y=this.yData[d];n=T(l,l+g.y)+i[0];f.y=b.translate(n,0,1);g.isSum||g.isIntermediateSum?(f.y=b.translate(i[1],0,1),f.height=b.translate(i[0],0,1)-f.y):l+=j.total;f.height<0&&(f.y+= -f.height,f.height*=-1);g.plotY=f.y=E(f.y)-a;f.height=E(f.height);g.yBottom=f.y+f.height}},processData:function(a){var b=this.yData,c=this.points,d,e=b.length,g=this.options.threshold||0,f,j,n,l,i,m;j=f=n=l=g;for(m=0;m<e;m++)i=b[m],d=c&&c[m]?c[m]:{},i==="sum"||d.isSum?b[m]=j:i==="intermediateSum"||d.isIntermediateSum?(b[m]=f,f=g):(j+=i,f+=i),n=Math.min(j,n),l=Math.max(j,l);x.prototype.processData.call(this,a);this.dataMin=n;this.dataMax=l},toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum"; -return a.y},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=k.Color(c).brighten(0.1).get(),d=q(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;t(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=E(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],e,g,f;for(f=1;f<b;f++)g=a[f].shapeArgs, -e=a[f-1].shapeArgs,g=["M",e.x+e.width,e.y+c,"L",g.x,e.y+c],a[f-1].y<0&&(g[2]+=e.height,g[5]+=e.height),d=d.concat(g);return d},getExtremes:s,getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},drawGraph:x.prototype.drawGraph});o.bubble=q(o.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"}, -turboThreshold:0,zThreshold:0});h.bubble=u(h.scatter,{type:"bubble",pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],bubblePadding:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},applyOpacity:function(a){var b=this.options.marker,c=r(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=U(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=x.prototype.convertAttribs.apply(this,arguments);a.fill= -this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,g,f,j=this.zData,n=[],l=this.options.sizeBy!=="width";for(g=0,e=j.length;g<e;g++)f=b-a,f=f>0?(j[g]-a)/(b-a):0.5,l&&f>=0&&(f=Math.sqrt(f)),n.push(v.ceil(c+f*(d-c))/2);this.radii=n},animate:function(a){var b=this.options.animation;if(!a)t(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this); -for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=D},drawLegendSymbol:function(a,b){var c=w(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel}); -L.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,g=this.isXAxis,f=g?"xData":"yData",j=this.min,n={},l=v.min(c.plotWidth,c.plotHeight),i=Number.MAX_VALUE,m=-Number.MAX_VALUE,h=this.max-j,k=b/h,p=[];this.tickPositions&&(t(this.series,function(b){var f=b.options;if(b.bubblePadding&&b.visible&&(a.allowZoomOutside=!0,p.push(b),g))t(["minSize","maxSize"],function(a){var b=f[a],g=/%$/.test(b),b=w(b);n[a]=g?l*b/100:b}),b.minPxSize=n.minSize,b=b.zData,b.length&&(i=v.min(i,v.max(N(b), -f.displayNegative===!1?f.zThreshold:-Number.MAX_VALUE)),m=v.max(m,O(b)))}),t(p,function(a){var b=a[f],c=b.length,l;g&&a.getRadii(i,m,n.minSize,n.maxSize);if(h>0)for(;c--;)typeof b[c]==="number"&&(l=a.radii[c],d=Math.min((b[c]-j)*k-l,d),e=Math.max((b[c]-j)*k+l,e))}),p.length&&h>0&&r(this.options.min,this.userMin)===D&&r(this.options.max,this.userMax)===D&&(e-=b,k*=(b+d-e)/b,this.min+=d/k,this.max+=e/k))};(function(){function a(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b= -this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function b(a,b){var c=this.chart,d=this.options.animation,e=this.group,i=this.markerGroup,m=this.xAxis.center,h=c.plotLeft,k=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:m[0]+h,translateY:m[1]+k,scaleX:0.001,scaleY:0.001},e.attr(c),i)i.attrSetters=e.attrSetters,i.attr(c)}else c={translateX:h,translateY:k,scaleX:1,scaleY:1},e.animate(c,d),i&&i.animate(c,d),this.animate=null}else a.call(this,b)}var c=x.prototype, -d=Q.prototype,e;c.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=(b/Math.PI*180+this.xAxis.pane.options.startAngle)%360;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};c.orderTooltipPoints=function(a){if(this.chart.polar&&(a.sort(function(a,b){return a.clientX-b.clientX}),a[0]))a[0].wrappedClientX=a[0].clientX+360,a.push(a[0])};h.area&&p(h.area.prototype,"init",a);h.areaspline&& -p(h.areaspline.prototype,"init",a);h.spline&&p(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,i,m,h,k,p,o;if(this.chart.polar){e=c.plotX;i=c.plotY;a=b[d-1];m=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),m||(m=b[1]));if(a&&m)h=a.plotX,k=a.plotY,b=m.plotX,p=m.plotY,h=(1.5*e+h)/2.5,k=(1.5*i+k)/2.5,m=(1.5*e+b)/2.5,o=(1.5*i+p)/2.5,b=Math.sqrt(Math.pow(h-e,2)+Math.pow(k-i,2)),p=Math.sqrt(Math.pow(m-e,2)+Math.pow(o-i,2)),h=Math.atan2(k-i,h-e),k=Math.atan2(o-i,m-e),o=Math.PI/2+(h+k)/2,Math.abs(h- -o)>Math.PI/2&&(o-=Math.PI),h=e+Math.cos(o)*b,k=i+Math.sin(o)*b,m=e+Math.cos(Math.PI+o)*p,o=i+Math.sin(Math.PI+o)*p,c.rightContX=m,c.rightContY=o;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,h||e,k||i,e,i],a.rightContX=a.rightContY=null):c=["M",e,i]}else c=a.call(this,b,c,d);return c});p(c,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});p(c,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&& -this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,b)});p(c,"animate",b);p(c,"setTooltipPoints",function(a,b){this.chart.polar&&z(this.xAxis,{tooltipLen:360});return a.call(this,b)});if(h.column)e=h.column.prototype,p(e,"animate",b),p(e,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,i=this.chart.renderer,h,k;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points; -for(k=b.length;k--;)h=b[k],a=h.barX+e,h.shapeType="path",h.shapeArgs={d:i.symbols.arc(d[0],d[1],c-h.plotY,null,{start:a,end:a+h.pointWidth,innerR:c-r(h.yBottom,c)})},this.toXY(h)}}),p(e,"alignDataLabel",function(a,b,d,e,h,i){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(e.align===null)e.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(e.verticalAlign===null)e.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";c.alignDataLabel.call(this,b,d,e,h,i)}else a.call(this,b,d,e,h, -i)});p(d,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});p(d,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?t(c.axes,function(a){var e=a.isXAxis,g=a.center,h=b.chartX-g[0]-c.plotLeft,g=b.chartY-g[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(h,g):Math.sqrt(Math.pow(h,2)+Math.pow(g, -2)),!0)})}):d=a.call(this,b);return d})})()})(Highcharts); -/* - Highcharts JS v3.0.9 (2014-01-15) - Exporting module - - (c) 2010-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(f){var A=f.Chart,t=f.addEvent,B=f.removeEvent,l=f.createElement,o=f.discardElement,v=f.css,k=f.merge,r=f.each,p=f.extend,D=Math.max,j=document,C=window,E=f.isTouchDevice,F=f.Renderer.prototype.symbols,s=f.getOptions(),y;p(s.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});s.navigation={menuStyle:{border:"1px solid #A0A0A0", -background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:E?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};s.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{menuClassName:"highcharts-contextmenu", -symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(b,a,d){var c,b=l("form",k({method:"post", -action:b,enctype:"multipart/form-data"},d),{display:"none"},j.body);for(c in a)l("input",{type:"hidden",name:c,value:a[c]},null,b);b.submit();o(b)};p(A.prototype,{getSVG:function(b){var a=this,d,c,z,h,g=k(a.options,b);if(!j.createElementNS)j.createElementNS=function(a,b){return j.createElement(b)};b=l("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},j.body);c=a.renderTo.style.width;h=a.renderTo.style.height;c=g.exporting.sourceWidth||g.chart.width|| -/px$/.test(c)&&parseInt(c,10)||600;h=g.exporting.sourceHeight||g.chart.height||/px$/.test(h)&&parseInt(h,10)||400;p(g.chart,{animation:!1,renderTo:b,forExport:!0,width:c,height:h});g.exporting.enabled=!1;g.series=[];r(a.series,function(a){z=k(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});z.isInternal||g.series.push(z)});d=new f.Chart(g,a.callback);r(["xAxis","yAxis"],function(b){r(a[b],function(a,c){var g=d[b][c],f=a.getExtremes(),h=f.userMin,f=f.userMax;g&&(h!==void 0||f!==void 0)&& -g.setExtremes(h,f,!0,!1)})});c=d.container.innerHTML;g=null;d.destroy();o(b);c=c.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/ /g," ").replace(/­/g,"­").replace(/<IMG /g,"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g, -'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});return c=c.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},exportChart:function(b,a){var b=b||{},d=this.options.exporting,d=this.getSVG(k({chart:{borderRadius:0}},d.chartOptions,a,{exporting:{sourceWidth:b.sourceWidth|| -d.sourceWidth,sourceHeight:b.sourceHeight||d.sourceHeight}})),b=k(this.options.exporting,b);f.post(b.url,{filename:b.filename||"chart",type:b.type,width:b.width||0,scale:b.scale||2,svg:d},b.formAttributes)},print:function(){var b=this,a=b.container,d=[],c=a.parentNode,f=j.body,h=f.childNodes;if(!b.isPrinting)b.isPrinting=!0,r(h,function(a,b){if(a.nodeType===1)d[b]=a.style.display,a.style.display="none"}),f.appendChild(a),C.focus(),C.print(),setTimeout(function(){c.appendChild(a);r(h,function(a,b){if(a.nodeType=== -1)a.style.display=d[b]});b.isPrinting=!1},1E3)},contextMenu:function(b,a,d,c,f,h,g){var e=this,k=e.options.navigation,q=k.menuItemStyle,m=e.chartWidth,n=e.chartHeight,j="cache-"+b,i=e[j],u=D(f,h),w,x,o,s=function(a){e.pointer.inClass(a.target,b)||x()};if(!i)e[j]=i=l("div",{className:b},{position:"absolute",zIndex:1E3,padding:u+"px"},e.container),w=l("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},k.menuStyle),i),x=function(){v(i,{display:"none"}); -g&&g.setState(0);e.openMenu=!1},t(i,"mouseleave",function(){o=setTimeout(x,500)}),t(i,"mouseenter",function(){clearTimeout(o)}),t(document,"mouseup",s),t(e,"destroy",function(){B(document,"mouseup",s)}),r(a,function(a){if(a){var b=a.separator?l("hr",null,null,w):l("div",{onmouseover:function(){v(this,k.menuItemHoverStyle)},onmouseout:function(){v(this,q)},onclick:function(){x();a.onclick.apply(e,arguments)},innerHTML:a.text||e.options.lang[a.textKey]},p({cursor:"pointer"},q),w);e.exportDivElements.push(b)}}), -e.exportDivElements.push(w,i),e.exportMenuWidth=i.offsetWidth,e.exportMenuHeight=i.offsetHeight;a={display:"block"};d+e.exportMenuWidth>m?a.right=m-d-f-u+"px":a.left=d-u+"px";c+h+e.exportMenuHeight>n&&g.alignOptions.verticalAlign!=="top"?a.bottom=n-c-u+"px":a.top=c+h-u+"px";v(i,a);e.openMenu=!0},addButton:function(b){var a=this,d=a.renderer,c=k(a.options.navigation.buttonOptions,b),j=c.onclick,h=c.menuItems,g,e,l={stroke:c.symbolStroke,fill:c.symbolFill},q=c.symbolSize||12;if(!a.btnCount)a.btnCount= -0;if(!a.exportDivElements)a.exportDivElements=[],a.exportSVGElements=[];if(c.enabled!==!1){var m=c.theme,n=m.states,o=n&&n.hover,n=n&&n.select,i;delete m.states;j?i=function(){j.apply(a,arguments)}:h&&(i=function(){a.contextMenu(e.menuClassName,h,e.translateX,e.translateY,e.width,e.height,e);e.setState(2)});c.text&&c.symbol?m.paddingLeft=f.pick(m.paddingLeft,25):c.text||p(m,{width:c.width,height:c.height,padding:0});e=d.button(c.text,0,0,i,m,o,n).attr({title:a.options.lang[c._titleKey],"stroke-linecap":"round"}); -e.menuClassName=b.menuClassName||"highcharts-menu-"+a.btnCount++;c.symbol&&(g=d.symbol(c.symbol,c.symbolX-q/2,c.symbolY-q/2,q,q).attr(p(l,{"stroke-width":c.symbolStrokeWidth||1,zIndex:1})).add(e));e.add().align(p(c,{width:e.width,x:f.pick(c.x,y)}),!0,"spacingBox");y+=(e.width+c.buttonSpacing)*(c.align==="right"?-1:1);a.exportSVGElements.push(e,g)}},destroyExport:function(b){var b=b.target,a,d;for(a=0;a<b.exportSVGElements.length;a++)if(d=b.exportSVGElements[a])d.onclick=d.ontouchstart=null,b.exportSVGElements[a]= -d.destroy();for(a=0;a<b.exportDivElements.length;a++)d=b.exportDivElements[a],B(d,"mouseleave"),b.exportDivElements[a]=d.onmouseout=d.onmouseover=d.ontouchstart=d.onclick=null,o(d)}});F.menu=function(b,a,d,c){return["M",b,a+2.5,"L",b+d,a+2.5,"M",b,a+c/2+0.5,"L",b+d,a+c/2+0.5,"M",b,a+c-1.5,"L",b+d,a+c-1.5]};A.prototype.callbacks.push(function(b){var a,d=b.options.exporting,c=d.buttons;y=0;if(d.enabled!==!1){for(a in c)b.addButton(c[a]);t(b,"destroy",b.destroyExport)}})})(Highcharts); -/* - Data plugin for Highcharts - - (c) 2012-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(j){var m=j.each,o=function(b,a){this.init(b,a)};j.extend(o.prototype,{init:function(b,a){this.options=b;this.chartOptions=a;this.columns=b.columns||this.rowsToColumns(b.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},getColumnDistribution:function(){var b=this.chartOptions,a=b&&b.chart&&b.chart.type,c=[];m(b&&b.series||[],function(b){c.push((j.seriesTypes[b.type||a||"line"].prototype.pointArrayMap||[0]).length)});this.valueCount= -{global:(j.seriesTypes[a||"line"].prototype.pointArrayMap||[0]).length,individual:c}},dataFound:function(){if(this.options.switchRowsAndColumns)this.columns=this.rowsToColumns(this.columns);this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var b=this,a=this.options,c=a.csv,d=this.columns,f=a.startRow||0,h=a.endRow||Number.MAX_VALUE,i=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g,k,j=0;c&&(k=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(a.lineDelimiter|| -"\n"),g=a.itemDelimiter||(c.indexOf("\t")!==-1?"\t":","),m(k,function(a,c){var k=b.trim(a),n=k.indexOf("#")===0;c>=f&&c<=h&&!n&&k!==""&&(k=a.split(g),m(k,function(b,a){a>=i&&a<=e&&(d[a-i]||(d[a-i]=[]),d[a-i][j]=b)}),j+=1)}),this.dataFound())},parseTable:function(){var b=this.options,a=b.table,c=this.columns,d=b.startRow||0,f=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,i=b.endColumn||Number.MAX_VALUE,e;a&&(typeof a==="string"&&(a=document.getElementById(a)),m(a.getElementsByTagName("tr"),function(a, -b){e=0;b>=d&&b<=f&&m(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&e>=h&&e<=i)c[e]||(c[e]=[]),c[e][b-d]=a.innerHTML,e+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var b=this,a=this.options,c=a.googleSpreadsheetKey,d=this.columns,f=a.startRow||0,h=a.endRow||Number.MAX_VALUE,i=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g,k;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(a.googleSpreadsheetWorksheet||"od6")+"/public/values?alt=json-in-script&callback=?", -function(a){var a=a.feed.entry,c,j=a.length,m=0,n=0,l;for(l=0;l<j;l++)c=a[l],m=Math.max(m,c.gs$cell.col),n=Math.max(n,c.gs$cell.row);for(l=0;l<m;l++)if(l>=i&&l<=e)d[l-i]=[],d[l-i].length=Math.min(n,h-f);for(l=0;l<j;l++)if(c=a[l],g=c.gs$cell.row-1,k=c.gs$cell.col-1,k>=i&&k<=e&&g>=f&&g<=h)d[k-i][g-f]=c.content.$t;b.dataFound()})},findHeaderRow:function(){m(this.columns,function(){});this.headerRow=0},trim:function(b){return typeof b==="string"?b.replace(/^\s+|\s+$/g,""):b},parseTypes:function(){for(var b= -this.columns,a=b.length,c,d,f,h;a--;)for(c=b[a].length;c--;)d=b[a][c],f=parseFloat(d),h=this.trim(d),h==f?(b[a][c]=f,f>31536E6?b[a].isDatetime=!0:b[a].isNumeric=!0):(d=this.parseDate(d),a===0&&typeof d==="number"&&!isNaN(d)?(b[a][c]=d,b[a].isDatetime=!0):b[a][c]=h===""?null:h)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(b){return Date.UTC(+b[1],b[2]-1,+b[3])}}},parseDate:function(b){var a=this.options.parseDate,c,d,f;a&&(c=a(b));if(typeof b==="string")for(d in this.dateFormats)a= -this.dateFormats[d],(f=b.match(a.regex))&&(c=a.parser(f));return c},rowsToColumns:function(b){var a,c,d,f,h;if(b){h=[];c=b.length;for(a=0;a<c;a++){f=b[a].length;for(d=0;d<f;d++)h[d]||(h[d]=[]),h[d][a]=b[a][d]}}return h},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var b=this.columns,a,c,d=this.options,f,h,i,e,g,k;if(d.complete){this.getColumnDistribution();b.length>1&&(a=b.shift(),this.headerRow===0&&a.shift(),a.isDatetime?c="datetime":a.isNumeric|| -(c="category"));for(e=0;e<b.length;e++)if(this.headerRow===0)b[e].name=b[e].shift();h=[];for(e=0,k=0;e<b.length;k++){f=j.pick(this.valueCount.individual[k],this.valueCount.global);i=[];for(g=0;g<b[e].length;g++)i[g]=[a[g],b[e][g]!==void 0?b[e][g]:null],f>1&&i[g].push(b[e+1][g]!==void 0?b[e+1][g]:null),f>2&&i[g].push(b[e+2][g]!==void 0?b[e+2][g]:null),f>3&&i[g].push(b[e+3][g]!==void 0?b[e+3][g]:null),f>4&&i[g].push(b[e+4][g]!==void 0?b[e+4][g]:null);h[k]={name:b[e].name,data:i};e+=f}d.complete({xAxis:{type:c}, -series:h})}}});j.Data=o;j.data=function(b,a){return new o(b,a)};j.wrap(j.Chart.prototype,"init",function(b,a,c){var d=this;a&&a.data?j.data(j.extend(a.data,{complete:function(f){a.series&&m(a.series,function(b,c){a.series[c]=j.merge(b,f.series[c])});a=j.merge(f,a);b.call(d,a,c)}}),a):b.call(d,a,c)})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/highcharts-more.js b/pykeg/web/static/highcharts/js/highcharts-more.js deleted file mode 100644 index d8d9b4e4b..000000000 --- a/pykeg/web/static/highcharts/js/highcharts-more.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - - (c) 2009-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(k,D){function J(a,b,c){this.init.call(this,a,b,c)}var N=k.arrayMin,O=k.arrayMax,t=k.each,z=k.extend,q=k.merge,P=k.map,r=k.pick,w=k.pInt,o=k.getOptions().plotOptions,h=k.seriesTypes,u=k.extendClass,K=k.splat,p=k.wrap,L=k.Axis,A=k.Tick,H=k.Point,Q=k.Pointer,R=k.TrackerMixin,S=k.CenteredSeriesMixin,x=k.Series,v=Math,E=v.round,B=v.floor,T=v.max,U=k.Color,s=function(){};z(J.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background={};d.options=a=q(e,a); -(a=a.background)&&t([].concat(K(a)).reverse(),function(a){var f=a.backgroundColor,a=q(d.defaultBackgroundOptions,a);if(f)a.backgroundColor=f;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"}}); -var G=L.prototype,A=A.prototype,V={getOffset:s,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:s,setCategories:s,setTitle:s},M={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,distance:15, -x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=q(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=S.getCenter.call(this.pane)},getLinePath:function(a,b){var c=this.center, -b=r(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center)this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/4:0):0},beforeSetTickPositions:function(){this.autoConnect&& -(this.max+=this.categories&&1||this.pointRange||this.closestPointRange||0)},setAxisSize:function(){G.setAxisSize.call(this);if(this.isRadial)this.center=this.pane.center=k.CenteredSeriesMixin.getCenter.call(this.pane),this.len=this.width=this.height=this.isCircular?this.center[2]*(this.endAngleRad-this.startAngleRad)/2:this.center[2]/2},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),r(b,this.center[2]/2)-this.offset)},postTranslate:function(a, -b){var c=this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,g=d[2]/2,f=[r(c.outerRadius,"100%"),c.innerRadius,r(c.thickness,10)],j=/%$/,n,l=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(l||(f[0]=this.translate(a),f[1]=this.translate(b)),f=P(f,function(a){j.test(a)&&(a=w(a,10)*g/100); -return a}),c.shape==="circle"||!l?(a=-Math.PI/2,b=Math.PI*1.5,n=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],f[0],f[0],{start:a,end:b,innerR:r(f[1],f[0]-f[2]),open:n}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),g,f,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(g= -d.xAxis[0],j=[],a=this.translate(a),c=g.tickPositions,g.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),t(c,function(c,b){f=g.getPosition(c,a);j.push(b?"L":"M",f.x,f.y)}));return j},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};p(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,g=c.isX,f=d&&g,j,n;n=b.options;var l=c.pane||0;if(d){if(z(this, -f?V:M),j=!g)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)z(this,M),this.defaultRadialOptions=(j=g)?this.defaultRadialXOptions:q(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!f&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[l]=b.panes[l]||new J(K(n.pane)[l],b,this),l=i);l=l.options;b.inverted=!1;n.chart.zoomType=null;this.startAngleRad=b=(l.startAngle-90)*Math.PI/180;this.endAngleRad=n=(r(l.endAngle,l.startAngle+360)-90)*Math.PI/ -180;this.offset=a.offset||0;if((this.isCircular=j)&&c.max===D&&n-b===2*Math.PI)this.autoConnect=!0}});p(A,"getPosition",function(a,b,c,d,e){var g=this.axis;return g.getPosition?g.getPosition(c):a.call(this,b,c,d,e)});p(A,"getLabelPosition",function(a,b,c,d,e,g,f,j,n){var l=this.axis,i=g.y,m=g.align,y=(l.translate(this.pos)+l.startAngleRad+Math.PI/2)/Math.PI*180%360;l.isRadial?(a=l.getPosition(this.pos,l.center[2]/2+r(g.distance,-25)),g.rotation==="auto"?d.attr({rotation:y}):i===null&&(i=l.chart.renderer.fontMetrics(d.styles.fontSize).b- -d.getBBox().height/2),m===null&&(m=l.isCircular?y>20&&y<160?"left":y>200&&y<340?"right":"center":"center",d.attr({align:m})),a.x+=g.x,a.y+=i):a=a.call(this,b,c,d,e,g,f,j,n);return a});p(A,"getMarkPath",function(a,b,c,d,e,g,f){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,g,f);return b});o.arearange=q(o.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'}, -trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=u(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;t(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});x.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);t(this.points, -function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.translate(d,0,1,0,1)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.translate(d,0,1,0,1))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=x.prototype.getSegmentPath,g,f;f=this.options;var j=f.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)g=a[d],g.plotHigh!==null&&c.push({plotX:g.plotX,plotY:g.plotHigh});a=e.call(this,b);if(j)j=== -!0&&(j="left"),f.step={left:"right",center:"center",right:"left"}[j];c=e.call(this,c);f.step=j;f=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return f},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=x.prototype,g=this.options.dataLabels,f,j=this.chart.inverted;if(g.enabled||this._hasPointLabels){for(c=b;c--;)f=a[c],f.y=f.high,f.plotY=f.plotHigh,d[c]=f.dataLabel,f.dataLabel=f.dataLabelUpper,f.below=!1,j?(g.align="left",g.x=g.xHigh):g.y=g.yHigh;e.drawDataLabels&&e.drawDataLabels.apply(this, -arguments);for(c=b;c--;)f=a[c],f.dataLabelUpper=f.dataLabel,f.dataLabel=d[c],f.y=f.low,f.plotY=f.plotLow,f.below=!0,j?(g.align="right",g.x=g.xLow):g.y=g.yLow;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments)}},alignDataLabel:function(){h.column.prototype.alignDataLabel.apply(this,arguments)},getSymbol:h.column.prototype.getSymbol,drawPoints:s});o.areasplinerange=q(o.arearange);h.areasplinerange=u(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});(function(){var a= -h.column.prototype;o.columnrange=q(o.column,o.arearange,{lineWidth:1,pointRange:null});h.columnrange=u(h.arearange,{type:"columnrange",translate:function(){var b=this,c=b.yAxis,d;a.translate.apply(b);t(b.points,function(a){var g=a.shapeArgs,f=b.options.minPointLength,j;a.plotHigh=d=c.translate(a.high,0,1,0,1);a.plotLow=a.plotY;j=d;a=a.plotY-d;a<f&&(f-=a,a+=f,j-=f/2);g.height=a;g.y=j})},trackerGroups:["group","dataLabels"],drawGraph:s,pointAttrToOptions:a.pointAttrToOptions,drawPoints:a.drawPoints, -drawTracker:a.drawTracker,animate:a.animate,getColumnMetrics:a.getColumnMetrics})})();o.gauge=q(o.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver",borderRadius:3,crop:!1,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});H={type:"gauge",pointClass:u(H,{setState:function(a){this.state=a}}),angular:!0,drawGraph:s,fixedBox:!0,forceDL:!0,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options, -c=a.center;this.generatePoints();t(this.points,function(d){var e=q(b.dial,d.dial),g=w(r(e.radius,80))*c[2]/200,f=w(r(e.baseLength,70))*g/100,j=w(r(e.rearLength,10))*g/100,n=e.baseWidth||3,l=e.topWidth||1,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs={d:e.path||["M",-j,-n/2,"L",f,-n/2,g,-l/2,g,l/2,f,n/2,-j,n/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY= -c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,g=a.chart.renderer;t(a.points,function(f){var c=f.graphic,b=f.shapeArgs,e=b.d,i=q(d.dial,f.dial);c?(c.animate(b),b.d=e):f.graphic=g[f.shapeType](b).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:b.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=g.circle(0,0,r(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor|| -"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)t(b.points,function(a){var d=a.graphic;d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);x.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:function(a, -b){x.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();r(b,!0)&&this.chart.redraw()},drawTracker:R.drawTrackerPoint};h.gauge=u(h.line,H);o.boxplot=q(o.column,{fillColor:"#FFFFFF",lineWidth:1,medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>Maximum: {point.high}<br/>Upper quartile: {point.q3}<br/>Median: {point.median}<br/>Lower quartile: {point.q1}<br/>Minimum: {point.low}<br/>'}, -whiskerLength:"50%",whiskerWidth:2});h.boxplot=u(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:s,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);t(this.points,function(c){t(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a= -this,b=a.points,c=a.options,d=a.chart.renderer,e,g,f,j,n,l,i,m,y,h,k,I,o,p,q,u,x,s,v,w,A,z,F=a.doQuartiles!==!1,C=parseInt(a.options.whiskerLength,10)/100;t(b,function(b){y=b.graphic;A=b.shapeArgs;k={};p={};u={};z=b.color||a.color;if(b.plotY!==D)if(e=b.pointAttr[b.selected?"selected":""],x=A.width,s=B(A.x),v=s+x,w=E(x/2),g=B(F?b.q1Plot:b.lowPlot),f=B(F?b.q3Plot:b.lowPlot),j=B(b.highPlot),n=B(b.lowPlot),k.stroke=b.stemColor||c.stemColor||z,k["stroke-width"]=r(b.stemWidth,c.stemWidth,c.lineWidth),k.dashstyle= -b.stemDashStyle||c.stemDashStyle,p.stroke=b.whiskerColor||c.whiskerColor||z,p["stroke-width"]=r(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||z,u["stroke-width"]=r(b.medianWidth,c.medianWidth,c.lineWidth),u["stroke-linecap"]="round",i=k["stroke-width"]%2/2,m=s+w+i,h=["M",m,f,"L",m,j,"M",m,g,"L",m,n,"z"],F&&(i=e["stroke-width"]%2/2,m=B(m)+i,g=B(g)+i,f=B(f)+i,s+=i,v+=i,I=["M",s,f,"L",s,g,"L",v,g,"L",v,f,"L",s,f,"z"]),C&&(i=p["stroke-width"]%2/2,j+=i,n+=i,o=["M",m- -w*C,j,"L",m+w*C,j,"M",m-w*C,n,"L",m+w*C,n]),i=u["stroke-width"]%2/2,l=E(b.medianPlot)+i,q=["M",s,l,"L",v,l,"z"],y)b.stem.animate({d:h}),C&&b.whiskers.animate({d:o}),F&&b.box.animate({d:I}),b.medianShape.animate({d:q});else{b.graphic=y=d.g().add(a.group);b.stem=d.path(h).attr(k).add(y);if(C)b.whiskers=d.path(o).attr(p).add(y);if(F)b.box=d.path(I).attr(e).add(y);b.medianShape=d.path(q).attr(u).add(y)}})}});o.errorbar=q(o.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'}, -whiskerWidth:null});h.errorbar=u(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});o.waterfall=q(o.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=u(h.column,{type:"waterfall",upColorProp:"fill",pointArrayMap:["low","y"],pointValKey:"y",init:function(a, -b){b.stacking=!0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.options,b=this.yAxis,c,d,e,g,f,j,n,l,i;c=a.threshold;a=a.borderWidth%2/2;h.column.prototype.translate.apply(this);l=c;e=this.points;for(d=0,c=e.length;d<c;d++){g=e[d];f=g.shapeArgs;j=this.getStack(d);i=j.points[this.index];if(isNaN(g.y))g.y=this.yData[d];n=T(l,l+g.y)+i[0];f.y=b.translate(n,0,1);g.isSum||g.isIntermediateSum?(f.y=b.translate(i[1],0,1),f.height=b.translate(i[0],0,1)-f.y):l+=j.total;f.height<0&&(f.y+= -f.height,f.height*=-1);g.plotY=f.y=E(f.y)-a;f.height=E(f.height);g.yBottom=f.y+f.height}},processData:function(a){var b=this.yData,c=this.points,d,e=b.length,g=this.options.threshold||0,f,j,n,l,i,m;j=f=n=l=g;for(m=0;m<e;m++)i=b[m],d=c&&c[m]?c[m]:{},i==="sum"||d.isSum?b[m]=j:i==="intermediateSum"||d.isIntermediateSum?(b[m]=f,f=g):(j+=i,f+=i),n=Math.min(j,n),l=Math.max(j,l);x.prototype.processData.call(this,a);this.dataMin=n;this.dataMax=l},toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum"; -return a.y},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=k.Color(c).brighten(0.1).get(),d=q(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;t(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=E(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],e,g,f;for(f=1;f<b;f++)g=a[f].shapeArgs, -e=a[f-1].shapeArgs,g=["M",e.x+e.width,e.y+c,"L",g.x,e.y+c],a[f-1].y<0&&(g[2]+=e.height,g[5]+=e.height),d=d.concat(g);return d},getExtremes:s,getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},drawGraph:x.prototype.drawGraph});o.bubble=q(o.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"}, -turboThreshold:0,zThreshold:0});h.bubble=u(h.scatter,{type:"bubble",pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],bubblePadding:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},applyOpacity:function(a){var b=this.options.marker,c=r(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=U(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=x.prototype.convertAttribs.apply(this,arguments);a.fill= -this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,g,f,j=this.zData,n=[],l=this.options.sizeBy!=="width";for(g=0,e=j.length;g<e;g++)f=b-a,f=f>0?(j[g]-a)/(b-a):0.5,l&&f>=0&&(f=Math.sqrt(f)),n.push(v.ceil(c+f*(d-c))/2);this.radii=n},animate:function(a){var b=this.options.animation;if(!a)t(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this); -for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=D},drawLegendSymbol:function(a,b){var c=w(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel}); -L.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,g=this.isXAxis,f=g?"xData":"yData",j=this.min,n={},l=v.min(c.plotWidth,c.plotHeight),i=Number.MAX_VALUE,m=-Number.MAX_VALUE,h=this.max-j,k=b/h,p=[];this.tickPositions&&(t(this.series,function(b){var f=b.options;if(b.bubblePadding&&b.visible&&(a.allowZoomOutside=!0,p.push(b),g))t(["minSize","maxSize"],function(a){var b=f[a],g=/%$/.test(b),b=w(b);n[a]=g?l*b/100:b}),b.minPxSize=n.minSize,b=b.zData,b.length&&(i=v.min(i,v.max(N(b), -f.displayNegative===!1?f.zThreshold:-Number.MAX_VALUE)),m=v.max(m,O(b)))}),t(p,function(a){var b=a[f],c=b.length,l;g&&a.getRadii(i,m,n.minSize,n.maxSize);if(h>0)for(;c--;)typeof b[c]==="number"&&(l=a.radii[c],d=Math.min((b[c]-j)*k-l,d),e=Math.max((b[c]-j)*k+l,e))}),p.length&&h>0&&r(this.options.min,this.userMin)===D&&r(this.options.max,this.userMax)===D&&(e-=b,k*=(b+d-e)/b,this.min+=d/k,this.max+=e/k))};(function(){function a(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b= -this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function b(a,b){var c=this.chart,d=this.options.animation,e=this.group,i=this.markerGroup,m=this.xAxis.center,h=c.plotLeft,k=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:m[0]+h,translateY:m[1]+k,scaleX:0.001,scaleY:0.001},e.attr(c),i)i.attrSetters=e.attrSetters,i.attr(c)}else c={translateX:h,translateY:k,scaleX:1,scaleY:1},e.animate(c,d),i&&i.animate(c,d),this.animate=null}else a.call(this,b)}var c=x.prototype, -d=Q.prototype,e;c.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=(b/Math.PI*180+this.xAxis.pane.options.startAngle)%360;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};c.orderTooltipPoints=function(a){if(this.chart.polar&&(a.sort(function(a,b){return a.clientX-b.clientX}),a[0]))a[0].wrappedClientX=a[0].clientX+360,a.push(a[0])};h.area&&p(h.area.prototype,"init",a);h.areaspline&& -p(h.areaspline.prototype,"init",a);h.spline&&p(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,i,m,h,k,p,o;if(this.chart.polar){e=c.plotX;i=c.plotY;a=b[d-1];m=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),m||(m=b[1]));if(a&&m)h=a.plotX,k=a.plotY,b=m.plotX,p=m.plotY,h=(1.5*e+h)/2.5,k=(1.5*i+k)/2.5,m=(1.5*e+b)/2.5,o=(1.5*i+p)/2.5,b=Math.sqrt(Math.pow(h-e,2)+Math.pow(k-i,2)),p=Math.sqrt(Math.pow(m-e,2)+Math.pow(o-i,2)),h=Math.atan2(k-i,h-e),k=Math.atan2(o-i,m-e),o=Math.PI/2+(h+k)/2,Math.abs(h- -o)>Math.PI/2&&(o-=Math.PI),h=e+Math.cos(o)*b,k=i+Math.sin(o)*b,m=e+Math.cos(Math.PI+o)*p,o=i+Math.sin(Math.PI+o)*p,c.rightContX=m,c.rightContY=o;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,h||e,k||i,e,i],a.rightContX=a.rightContY=null):c=["M",e,i]}else c=a.call(this,b,c,d);return c});p(c,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});p(c,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&& -this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,b)});p(c,"animate",b);p(c,"setTooltipPoints",function(a,b){this.chart.polar&&z(this.xAxis,{tooltipLen:360});return a.call(this,b)});if(h.column)e=h.column.prototype,p(e,"animate",b),p(e,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,i=this.chart.renderer,h,k;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points; -for(k=b.length;k--;)h=b[k],a=h.barX+e,h.shapeType="path",h.shapeArgs={d:i.symbols.arc(d[0],d[1],c-h.plotY,null,{start:a,end:a+h.pointWidth,innerR:c-r(h.yBottom,c)})},this.toXY(h)}}),p(e,"alignDataLabel",function(a,b,d,e,h,i){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(e.align===null)e.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(e.verticalAlign===null)e.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";c.alignDataLabel.call(this,b,d,e,h,i)}else a.call(this,b,d,e,h, -i)});p(d,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});p(d,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?t(c.axes,function(a){var e=a.isXAxis,g=a.center,h=b.chartX-g[0]-c.plotLeft,g=b.chartY-g[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(h,g):Math.sqrt(Math.pow(h,2)+Math.pow(g, -2)),!0)})}):d=a.call(this,b);return d})})()})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/highcharts-more.src.js b/pykeg/web/static/highcharts/js/highcharts-more.src.js deleted file mode 100644 index 501f3b0b0..000000000 --- a/pykeg/web/static/highcharts/js/highcharts-more.src.js +++ /dev/null @@ -1,2500 +0,0 @@ -// ==ClosureCompiler== -// @compilation_level SIMPLE_OPTIMIZATIONS - -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * - * (c) 2009-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -// JSLint options: -/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */ - -(function (Highcharts, UNDEFINED) { -var arrayMin = Highcharts.arrayMin, - arrayMax = Highcharts.arrayMax, - each = Highcharts.each, - extend = Highcharts.extend, - merge = Highcharts.merge, - map = Highcharts.map, - pick = Highcharts.pick, - pInt = Highcharts.pInt, - defaultPlotOptions = Highcharts.getOptions().plotOptions, - seriesTypes = Highcharts.seriesTypes, - extendClass = Highcharts.extendClass, - splat = Highcharts.splat, - wrap = Highcharts.wrap, - Axis = Highcharts.Axis, - Tick = Highcharts.Tick, - Point = Highcharts.Point, - Pointer = Highcharts.Pointer, - TrackerMixin = Highcharts.TrackerMixin, - CenteredSeriesMixin = Highcharts.CenteredSeriesMixin, - Series = Highcharts.Series, - math = Math, - mathRound = math.round, - mathFloor = math.floor, - mathMax = math.max, - Color = Highcharts.Color, - noop = function () {};/** - * The Pane object allows options that are common to a set of X and Y axes. - * - * In the future, this can be extended to basic Highcharts and Highstock. - */ -function Pane(options, chart, firstAxis) { - this.init.call(this, options, chart, firstAxis); -} - -// Extend the Pane prototype -extend(Pane.prototype, { - - /** - * Initiate the Pane object - */ - init: function (options, chart, firstAxis) { - var pane = this, - backgroundOption, - defaultOptions = pane.defaultOptions; - - pane.chart = chart; - - // Set options - if (chart.angular) { // gauges - defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions - } - pane.options = options = merge(defaultOptions, options); - - backgroundOption = options.background; - - // To avoid having weighty logic to place, update and remove the backgrounds, - // push them to the first axis' plot bands and borrow the existing logic there. - if (backgroundOption) { - each([].concat(splat(backgroundOption)).reverse(), function (config) { - var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients) - config = merge(pane.defaultBackgroundOptions, config); - if (backgroundColor) { - config.backgroundColor = backgroundColor; - } - config.color = config.backgroundColor; // due to naming in plotBands - firstAxis.options.plotBands.unshift(config); - }); - } - }, - - /** - * The default options object - */ - defaultOptions: { - // background: {conditional}, - center: ['50%', '50%'], - size: '85%', - startAngle: 0 - //endAngle: startAngle + 360 - }, - - /** - * The default background options - */ - defaultBackgroundOptions: { - shape: 'circle', - borderWidth: 1, - borderColor: 'silver', - backgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, '#FFF'], - [1, '#DDD'] - ] - }, - from: Number.MIN_VALUE, // corrected to axis min - innerRadius: 0, - to: Number.MAX_VALUE, // corrected to axis max - outerRadius: '105%' - } - -}); -var axisProto = Axis.prototype, - tickProto = Tick.prototype; - -/** - * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges - */ -var hiddenAxisMixin = { - getOffset: noop, - redraw: function () { - this.isDirty = false; // prevent setting Y axis dirty - }, - render: function () { - this.isDirty = false; // prevent setting Y axis dirty - }, - setScale: noop, - setCategories: noop, - setTitle: noop -}; - -/** - * Augmented methods for the value axis - */ -/*jslint unparam: true*/ -var radialAxisMixin = { - isRadial: true, - - /** - * The default options extend defaultYAxisOptions - */ - defaultRadialGaugeOptions: { - labels: { - align: 'center', - x: 0, - y: null // auto - }, - minorGridLineWidth: 0, - minorTickInterval: 'auto', - minorTickLength: 10, - minorTickPosition: 'inside', - minorTickWidth: 1, - plotBands: [], - tickLength: 10, - tickPosition: 'inside', - tickWidth: 2, - title: { - rotation: 0 - }, - zIndex: 2 // behind dials, points in the series group - }, - - // Circular axis around the perimeter of a polar chart - defaultRadialXOptions: { - gridLineWidth: 1, // spokes - labels: { - align: null, // auto - distance: 15, - x: 0, - y: null // auto - }, - maxPadding: 0, - minPadding: 0, - plotBands: [], - showLastLabel: false, - tickLength: 0 - }, - - // Radial axis, like a spoke in a polar chart - defaultRadialYOptions: { - gridLineInterpolation: 'circle', - labels: { - align: 'right', - x: -3, - y: -2 - }, - plotBands: [], - showLastLabel: false, - title: { - x: 4, - text: null, - rotation: 90 - } - }, - - /** - * Merge and set options - */ - setOptions: function (userOptions) { - - this.options = merge( - this.defaultOptions, - this.defaultRadialOptions, - userOptions - ); - - }, - - /** - * Wrap the getOffset method to return zero offset for title or labels in a radial - * axis - */ - getOffset: function () { - // Call the Axis prototype method (the method we're in now is on the instance) - axisProto.getOffset.call(this); - - // Title or label offsets are not counted - this.chart.axisOffset[this.side] = 0; - - // Set the center array - this.center = this.pane.center = CenteredSeriesMixin.getCenter.call(this.pane); - }, - - - /** - * Get the path for the axis line. This method is also referenced in the getPlotLinePath - * method. - */ - getLinePath: function (lineWidth, radius) { - var center = this.center; - radius = pick(radius, center[2] / 2 - this.offset); - - return this.chart.renderer.symbols.arc( - this.left + center[0], - this.top + center[1], - radius, - radius, - { - start: this.startAngleRad, - end: this.endAngleRad, - open: true, - innerR: 0 - } - ); - }, - - /** - * Override setAxisTranslation by setting the translation to the difference - * in rotation. This allows the translate method to return angle for - * any given value. - */ - setAxisTranslation: function () { - - // Call uber method - axisProto.setAxisTranslation.call(this); - - // Set transA and minPixelPadding - if (this.center) { // it's not defined the first time - if (this.isCircular) { - - this.transA = (this.endAngleRad - this.startAngleRad) / - ((this.max - this.min) || 1); - - - } else { - this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1); - } - - if (this.isXAxis) { - this.minPixelPadding = this.transA * this.minPointOffset + - (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ??? - } else { - // This is a workaround for regression #2593, but categories still don't position correctly. - // TODO: Implement true handling of Y axis categories on gauges. - this.minPixelPadding = 0; - } - } - }, - - /** - * In case of auto connect, add one closestPointRange to the max value right before - * tickPositions are computed, so that ticks will extend passed the real max. - */ - beforeSetTickPositions: function () { - if (this.autoConnect) { - this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260 - } - }, - - /** - * Override the setAxisSize method to use the arc's circumference as length. This - * allows tickPixelInterval to apply to pixel lengths along the perimeter - */ - setAxisSize: function () { - - axisProto.setAxisSize.call(this); - - if (this.isRadial) { - - // Set the center array - this.center = this.pane.center = Highcharts.CenteredSeriesMixin.getCenter.call(this.pane); - - this.len = this.width = this.height = this.isCircular ? - this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 : - this.center[2] / 2; - } - }, - - /** - * Returns the x, y coordinate of a point given by a value and a pixel distance - * from center - */ - getPosition: function (value, length) { - if (!this.isCircular) { - length = this.translate(value); - value = this.min; - } - - return this.postTranslate( - this.translate(value), - pick(length, this.center[2] / 2) - this.offset - ); - }, - - /** - * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates. - */ - postTranslate: function (angle, radius) { - - var chart = this.chart, - center = this.center; - - angle = this.startAngleRad + angle; - - return { - x: chart.plotLeft + center[0] + Math.cos(angle) * radius, - y: chart.plotTop + center[1] + Math.sin(angle) * radius - }; - - }, - - /** - * Find the path for plot bands along the radial axis - */ - getPlotBandPath: function (from, to, options) { - var center = this.center, - startAngleRad = this.startAngleRad, - fullRadius = center[2] / 2, - radii = [ - pick(options.outerRadius, '100%'), - options.innerRadius, - pick(options.thickness, 10) - ], - percentRegex = /%$/, - start, - end, - open, - isCircular = this.isCircular, // X axis in a polar chart - ret; - - // Polygonal plot bands - if (this.options.gridLineInterpolation === 'polygon') { - ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true)); - - // Circular grid bands - } else { - - // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from - if (!isCircular) { - radii[0] = this.translate(from); - radii[1] = this.translate(to); - } - - // Convert percentages to pixel values - radii = map(radii, function (radius) { - if (percentRegex.test(radius)) { - radius = (pInt(radius, 10) * fullRadius) / 100; - } - return radius; - }); - - // Handle full circle - if (options.shape === 'circle' || !isCircular) { - start = -Math.PI / 2; - end = Math.PI * 1.5; - open = true; - } else { - start = startAngleRad + this.translate(from); - end = startAngleRad + this.translate(to); - } - - - ret = this.chart.renderer.symbols.arc( - this.left + center[0], - this.top + center[1], - radii[0], - radii[0], - { - start: start, - end: end, - innerR: pick(radii[1], radii[0] - radii[2]), - open: open - } - ); - } - - return ret; - }, - - /** - * Find the path for plot lines perpendicular to the radial axis. - */ - getPlotLinePath: function (value, reverse) { - var axis = this, - center = axis.center, - chart = axis.chart, - end = axis.getPosition(value), - xAxis, - xy, - tickPositions, - ret; - - // Spokes - if (axis.isCircular) { - ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y]; - - // Concentric circles - } else if (axis.options.gridLineInterpolation === 'circle') { - value = axis.translate(value); - if (value) { // a value of 0 is in the center - ret = axis.getLinePath(0, value); - } - // Concentric polygons - } else { - xAxis = chart.xAxis[0]; - ret = []; - value = axis.translate(value); - tickPositions = xAxis.tickPositions; - if (xAxis.autoConnect) { - tickPositions = tickPositions.concat([tickPositions[0]]); - } - // Reverse the positions for concatenation of polygonal plot bands - if (reverse) { - tickPositions = [].concat(tickPositions).reverse(); - } - - each(tickPositions, function (pos, i) { - xy = xAxis.getPosition(pos, value); - ret.push(i ? 'L' : 'M', xy.x, xy.y); - }); - - } - return ret; - }, - - /** - * Find the position for the axis title, by default inside the gauge - */ - getTitlePosition: function () { - var center = this.center, - chart = this.chart, - titleOptions = this.options.title; - - return { - x: chart.plotLeft + center[0] + (titleOptions.x || 0), - y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] * - center[2]) + (titleOptions.y || 0) - }; - } - -}; -/*jslint unparam: false*/ - -/** - * Override axisProto.init to mix in special axis instance functions and function overrides - */ -wrap(axisProto, 'init', function (proceed, chart, userOptions) { - var axis = this, - angular = chart.angular, - polar = chart.polar, - isX = userOptions.isX, - isHidden = angular && isX, - isCircular, - startAngleRad, - endAngleRad, - options, - chartOptions = chart.options, - paneIndex = userOptions.pane || 0, - pane, - paneOptions; - - // Before prototype.init - if (angular) { - extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin); - isCircular = !isX; - if (isCircular) { - this.defaultRadialOptions = this.defaultRadialGaugeOptions; - } - - } else if (polar) { - //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin); - extend(this, radialAxisMixin); - isCircular = isX; - this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions); - - } - - // Run prototype.init - proceed.call(this, chart, userOptions); - - if (!isHidden && (angular || polar)) { - options = this.options; - - // Create the pane and set the pane options. - if (!chart.panes) { - chart.panes = []; - } - this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane( - splat(chartOptions.pane)[paneIndex], - chart, - axis - ); - paneOptions = pane.options; - - - // Disable certain features on angular and polar axes - chart.inverted = false; - chartOptions.chart.zoomType = null; - - // Start and end angle options are - // given in degrees relative to top, while internal computations are - // in radians relative to right (like SVG). - this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180; - this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180; - this.offset = options.offset || 0; - - this.isCircular = isCircular; - - // Automatically connect grid lines? - if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) { - this.autoConnect = true; - } - } - -}); - -/** - * Add special cases within the Tick class' methods for radial axes. - */ -wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) { - var axis = this.axis; - - return axis.getPosition ? - axis.getPosition(pos) : - proceed.call(this, horiz, pos, tickmarkOffset, old); -}); - -/** - * Wrap the getLabelPosition function to find the center position of the label - * based on the distance option - */ -wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) { - var axis = this.axis, - optionsY = labelOptions.y, - ret, - align = labelOptions.align, - angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360; - - if (axis.isRadial) { - ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25)); - - // Automatically rotated - if (labelOptions.rotation === 'auto') { - label.attr({ - rotation: angle - }); - - // Vertically centered - } else if (optionsY === null) { - optionsY = axis.chart.renderer.fontMetrics(label.styles.fontSize).b - label.getBBox().height / 2; - } - - // Automatic alignment - if (align === null) { - if (axis.isCircular) { - if (angle > 20 && angle < 160) { - align = 'left'; // right hemisphere - } else if (angle > 200 && angle < 340) { - align = 'right'; // left hemisphere - } else { - align = 'center'; // top or bottom - } - } else { - align = 'center'; - } - label.attr({ - align: align - }); - } - - ret.x += labelOptions.x; - ret.y += optionsY; - - } else { - ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step); - } - return ret; -}); - -/** - * Wrap the getMarkPath function to return the path of the radial marker - */ -wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) { - var axis = this.axis, - endPoint, - ret; - - if (axis.isRadial) { - endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength); - ret = [ - 'M', - x, - y, - 'L', - endPoint.x, - endPoint.y - ]; - } else { - ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer); - } - return ret; -});/* - * The AreaRangeSeries class - * - */ - -/** - * Extend the default options with map options - */ -defaultPlotOptions.arearange = merge(defaultPlotOptions.area, { - lineWidth: 1, - marker: null, - threshold: null, - tooltip: { - pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>' - }, - trackByArea: true, - dataLabels: { - verticalAlign: null, - xLow: 0, - xHigh: 0, - yLow: 0, - yHigh: 0 - } -}); - -/** - * Add the series type - */ -seriesTypes.arearange = extendClass(seriesTypes.area, { - type: 'arearange', - pointArrayMap: ['low', 'high'], - toYData: function (point) { - return [point.low, point.high]; - }, - pointValKey: 'low', - - /** - * Extend getSegments to force null points if the higher value is null. #1703. - */ - getSegments: function () { - var series = this; - - each(series.points, function (point) { - if (!series.options.connectNulls && (point.low === null || point.high === null)) { - point.y = null; - } else if (point.low === null && point.high !== null) { - point.y = point.high; - } - }); - Series.prototype.getSegments.call(this); - }, - - /** - * Translate data points from raw values x and y to plotX and plotY - */ - translate: function () { - var series = this, - yAxis = series.yAxis; - - seriesTypes.area.prototype.translate.apply(series); - - // Set plotLow and plotHigh - each(series.points, function (point) { - - var low = point.low, - high = point.high, - plotY = point.plotY; - - if (high === null && low === null) { - point.y = null; - } else if (low === null) { - point.plotLow = point.plotY = null; - point.plotHigh = yAxis.translate(high, 0, 1, 0, 1); - } else if (high === null) { - point.plotLow = plotY; - point.plotHigh = null; - } else { - point.plotLow = plotY; - point.plotHigh = yAxis.translate(high, 0, 1, 0, 1); - } - }); - }, - - /** - * Extend the line series' getSegmentPath method by applying the segment - * path to both lower and higher values of the range - */ - getSegmentPath: function (segment) { - - var lowSegment, - highSegment = [], - i = segment.length, - baseGetSegmentPath = Series.prototype.getSegmentPath, - point, - linePath, - lowerPath, - options = this.options, - step = options.step, - higherPath; - - // Remove nulls from low segment - lowSegment = HighchartsAdapter.grep(segment, function (point) { - return point.plotLow !== null; - }); - - // Make a segment with plotX and plotY for the top values - while (i--) { - point = segment[i]; - if (point.plotHigh !== null) { - highSegment.push({ - plotX: point.plotX, - plotY: point.plotHigh - }); - } - } - - // Get the paths - lowerPath = baseGetSegmentPath.call(this, lowSegment); - if (step) { - if (step === true) { - step = 'left'; - } - options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath - } - higherPath = baseGetSegmentPath.call(this, highSegment); - options.step = step; - - // Create a line on both top and bottom of the range - linePath = [].concat(lowerPath, higherPath); - - // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo' - higherPath[0] = 'L'; // this probably doesn't work for spline - this.areaPath = this.areaPath.concat(lowerPath, higherPath); - - return linePath; - }, - - /** - * Extend the basic drawDataLabels method by running it for both lower and higher - * values. - */ - drawDataLabels: function () { - - var data = this.data, - length = data.length, - i, - originalDataLabels = [], - seriesProto = Series.prototype, - dataLabelOptions = this.options.dataLabels, - point, - inverted = this.chart.inverted; - - if (dataLabelOptions.enabled || this._hasPointLabels) { - - // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels - i = length; - while (i--) { - point = data[i]; - - // Set preliminary values - point.y = point.high; - point.plotY = point.plotHigh; - - // Store original data labels and set preliminary label objects to be picked up - // in the uber method - originalDataLabels[i] = point.dataLabel; - point.dataLabel = point.dataLabelUpper; - - // Set the default offset - point.below = false; - if (inverted) { - dataLabelOptions.align = 'left'; - dataLabelOptions.x = dataLabelOptions.xHigh; - } else { - dataLabelOptions.y = dataLabelOptions.yHigh; - } - } - - if (seriesProto.drawDataLabels) { - seriesProto.drawDataLabels.apply(this, arguments); // #1209 - } - - // Step 2: reorganize and handle data labels for the lower values - i = length; - while (i--) { - point = data[i]; - - // Move the generated labels from step 1, and reassign the original data labels - point.dataLabelUpper = point.dataLabel; - point.dataLabel = originalDataLabels[i]; - - // Reset values - point.y = point.low; - point.plotY = point.plotLow; - - // Set the default offset - point.below = true; - if (inverted) { - dataLabelOptions.align = 'right'; - dataLabelOptions.x = dataLabelOptions.xLow; - } else { - dataLabelOptions.y = dataLabelOptions.yLow; - } - } - if (seriesProto.drawDataLabels) { - seriesProto.drawDataLabels.apply(this, arguments); - } - } - - }, - - alignDataLabel: function () { - seriesTypes.column.prototype.alignDataLabel.apply(this, arguments); - }, - - getSymbol: seriesTypes.column.prototype.getSymbol, - - drawPoints: noop -});/** - * The AreaSplineRangeSeries class - */ - -defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange); - -/** - * AreaSplineRangeSeries object - */ -seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, { - type: 'areasplinerange', - getPointSpline: seriesTypes.spline.prototype.getPointSpline -}); - -(function () { - - var colProto = seriesTypes.column.prototype; - - /** - * The ColumnRangeSeries class - */ - defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, { - lineWidth: 1, - pointRange: null - }); - - /** - * ColumnRangeSeries object - */ - seriesTypes.columnrange = extendClass(seriesTypes.arearange, { - type: 'columnrange', - /** - * Translate data points from raw values x and y to plotX and plotY - */ - translate: function () { - var series = this, - yAxis = series.yAxis, - plotHigh; - - colProto.translate.apply(series); - - // Set plotLow and plotHigh - each(series.points, function (point) { - var shapeArgs = point.shapeArgs, - minPointLength = series.options.minPointLength, - heightDifference, - height, - y; - - point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1); - point.plotLow = point.plotY; - - // adjust shape - y = plotHigh; - height = point.plotY - plotHigh; - - if (height < minPointLength) { - heightDifference = (minPointLength - height); - height += heightDifference; - y -= heightDifference / 2; - } - shapeArgs.height = height; - shapeArgs.y = y; - }); - }, - trackerGroups: ['group', 'dataLabels'], - drawGraph: noop, - pointAttrToOptions: colProto.pointAttrToOptions, - drawPoints: colProto.drawPoints, - drawTracker: colProto.drawTracker, - animate: colProto.animate, - getColumnMetrics: colProto.getColumnMetrics - }); -}()); - -/* - * The GaugeSeries class - */ - - - -/** - * Extend the default options - */ -defaultPlotOptions.gauge = merge(defaultPlotOptions.line, { - dataLabels: { - enabled: true, - y: 15, - borderWidth: 1, - borderColor: 'silver', - borderRadius: 3, - crop: false, - style: { - fontWeight: 'bold' - }, - verticalAlign: 'top', - zIndex: 2 - }, - dial: { - // radius: '80%', - // backgroundColor: 'black', - // borderColor: 'silver', - // borderWidth: 0, - // baseWidth: 3, - // topWidth: 1, - // baseLength: '70%' // of radius - // rearLength: '10%' - }, - pivot: { - //radius: 5, - //borderWidth: 0 - //borderColor: 'silver', - //backgroundColor: 'black' - }, - tooltip: { - headerFormat: '' - }, - showInLegend: false -}); - -/** - * Extend the point object - */ -var GaugePoint = extendClass(Point, { - /** - * Don't do any hover colors or anything - */ - setState: function (state) { - this.state = state; - } -}); - - -/** - * Add the series type - */ -var GaugeSeries = { - type: 'gauge', - pointClass: GaugePoint, - - // chart.angular will be set to true when a gauge series is present, and this will - // be used on the axes - angular: true, - drawGraph: noop, - fixedBox: true, - forceDL: true, - trackerGroups: ['group', 'dataLabels'], - - /** - * Calculate paths etc - */ - translate: function () { - - var series = this, - yAxis = series.yAxis, - options = series.options, - center = yAxis.center; - - series.generatePoints(); - - each(series.points, function (point) { - - var dialOptions = merge(options.dial, point.dial), - radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200, - baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100, - rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100, - baseWidth = dialOptions.baseWidth || 3, - topWidth = dialOptions.topWidth || 1, - rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true); - - // Handle the wrap option - if (options.wrap === false) { - rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation)); - } - rotation = rotation * 180 / Math.PI; - - point.shapeType = 'path'; - point.shapeArgs = { - d: dialOptions.path || [ - 'M', - -rearLength, -baseWidth / 2, - 'L', - baseLength, -baseWidth / 2, - radius, -topWidth / 2, - radius, topWidth / 2, - baseLength, baseWidth / 2, - -rearLength, baseWidth / 2, - 'z' - ], - translateX: center[0], - translateY: center[1], - rotation: rotation - }; - - // Positions for data label - point.plotX = center[0]; - point.plotY = center[1]; - }); - }, - - /** - * Draw the points where each point is one needle - */ - drawPoints: function () { - - var series = this, - center = series.yAxis.center, - pivot = series.pivot, - options = series.options, - pivotOptions = options.pivot, - renderer = series.chart.renderer; - - each(series.points, function (point) { - - var graphic = point.graphic, - shapeArgs = point.shapeArgs, - d = shapeArgs.d, - dialOptions = merge(options.dial, point.dial); // #1233 - - if (graphic) { - graphic.animate(shapeArgs); - shapeArgs.d = d; // animate alters it - } else { - point.graphic = renderer[point.shapeType](shapeArgs) - .attr({ - stroke: dialOptions.borderColor || 'none', - 'stroke-width': dialOptions.borderWidth || 0, - fill: dialOptions.backgroundColor || 'black', - rotation: shapeArgs.rotation // required by VML when animation is false - }) - .add(series.group); - } - }); - - // Add or move the pivot - if (pivot) { - pivot.animate({ // #1235 - translateX: center[0], - translateY: center[1] - }); - } else { - series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5)) - .attr({ - 'stroke-width': pivotOptions.borderWidth || 0, - stroke: pivotOptions.borderColor || 'silver', - fill: pivotOptions.backgroundColor || 'black' - }) - .translate(center[0], center[1]) - .add(series.group); - } - }, - - /** - * Animate the arrow up from startAngle - */ - animate: function (init) { - var series = this; - - if (!init) { - each(series.points, function (point) { - var graphic = point.graphic; - - if (graphic) { - // start value - graphic.attr({ - rotation: series.yAxis.startAngleRad * 180 / Math.PI - }); - - // animate - graphic.animate({ - rotation: point.shapeArgs.rotation - }, series.options.animation); - } - }); - - // delete this function to allow it only once - series.animate = null; - } - }, - - render: function () { - this.group = this.plotGroup( - 'group', - 'series', - this.visible ? 'visible' : 'hidden', - this.options.zIndex, - this.chart.seriesGroup - ); - Series.prototype.render.call(this); - this.group.clip(this.chart.clipRect); - }, - - /** - * Extend the basic setData method by running processData and generatePoints immediately, - * in order to access the points from the legend. - */ - setData: function (data, redraw) { - Series.prototype.setData.call(this, data, false); - this.processData(); - this.generatePoints(); - if (pick(redraw, true)) { - this.chart.redraw(); - } - }, - drawTracker: TrackerMixin.drawTrackerPoint -}; -seriesTypes.gauge = extendClass(seriesTypes.line, GaugeSeries); - -/* **************************************************************************** - * Start Box plot series code * - *****************************************************************************/ - -// Set default options -defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, { - fillColor: '#FFFFFF', - lineWidth: 1, - //medianColor: null, - medianWidth: 2, - states: { - hover: { - brightness: -0.3 - } - }, - //stemColor: null, - //stemDashStyle: 'solid' - //stemWidth: null, - threshold: null, - tooltip: { - pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' + - 'Maximum: {point.high}<br/>' + - 'Upper quartile: {point.q3}<br/>' + - 'Median: {point.median}<br/>' + - 'Lower quartile: {point.q1}<br/>' + - 'Minimum: {point.low}<br/>' - - }, - //whiskerColor: null, - whiskerLength: '50%', - whiskerWidth: 2 -}); - -// Create the series object -seriesTypes.boxplot = extendClass(seriesTypes.column, { - type: 'boxplot', - pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this - toYData: function (point) { // return a plain array for speedy calculation - return [point.low, point.q1, point.median, point.q3, point.high]; - }, - pointValKey: 'high', // defines the top of the tracker - - /** - * One-to-one mapping from options to SVG attributes - */ - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - fill: 'fillColor', - stroke: 'color', - 'stroke-width': 'lineWidth' - }, - - /** - * Disable data labels for box plot - */ - drawDataLabels: noop, - - /** - * Translate data points from raw values x and y to plotX and plotY - */ - translate: function () { - var series = this, - yAxis = series.yAxis, - pointArrayMap = series.pointArrayMap; - - seriesTypes.column.prototype.translate.apply(series); - - // do the translation on each point dimension - each(series.points, function (point) { - each(pointArrayMap, function (key) { - if (point[key] !== null) { - point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1); - } - }); - }); - }, - - /** - * Draw the data points - */ - drawPoints: function () { - var series = this, //state = series.state, - points = series.points, - options = series.options, - chart = series.chart, - renderer = chart.renderer, - pointAttr, - q1Plot, - q3Plot, - highPlot, - lowPlot, - medianPlot, - crispCorr, - crispX, - graphic, - stemPath, - stemAttr, - boxPath, - whiskersPath, - whiskersAttr, - medianPath, - medianAttr, - width, - left, - right, - halfWidth, - shapeArgs, - color, - doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles - whiskerLength = parseInt(series.options.whiskerLength, 10) / 100; - - - each(points, function (point) { - - graphic = point.graphic; - shapeArgs = point.shapeArgs; // the box - stemAttr = {}; - whiskersAttr = {}; - medianAttr = {}; - color = point.color || series.color; - - if (point.plotY !== UNDEFINED) { - - pointAttr = point.pointAttr[point.selected ? 'selected' : '']; - - // crisp vector coordinates - width = shapeArgs.width; - left = mathFloor(shapeArgs.x); - right = left + width; - halfWidth = mathRound(width / 2); - //crispX = mathRound(left + halfWidth) + crispCorr; - q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr; - q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr; - highPlot = mathFloor(point.highPlot);// + crispCorr; - lowPlot = mathFloor(point.lowPlot);// + crispCorr; - - // Stem attributes - stemAttr.stroke = point.stemColor || options.stemColor || color; - stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth); - stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle; - - // Whiskers attributes - whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color; - whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth); - - // Median attributes - medianAttr.stroke = point.medianColor || options.medianColor || color; - medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth); - medianAttr['stroke-linecap'] = 'round'; // #1638 - - - // The stem - crispCorr = (stemAttr['stroke-width'] % 2) / 2; - crispX = left + halfWidth + crispCorr; - stemPath = [ - // stem up - 'M', - crispX, q3Plot, - 'L', - crispX, highPlot, - - // stem down - 'M', - crispX, q1Plot, - 'L', - crispX, lowPlot, - 'z' - ]; - - // The box - if (doQuartiles) { - crispCorr = (pointAttr['stroke-width'] % 2) / 2; - crispX = mathFloor(crispX) + crispCorr; - q1Plot = mathFloor(q1Plot) + crispCorr; - q3Plot = mathFloor(q3Plot) + crispCorr; - left += crispCorr; - right += crispCorr; - boxPath = [ - 'M', - left, q3Plot, - 'L', - left, q1Plot, - 'L', - right, q1Plot, - 'L', - right, q3Plot, - 'L', - left, q3Plot, - 'z' - ]; - } - - // The whiskers - if (whiskerLength) { - crispCorr = (whiskersAttr['stroke-width'] % 2) / 2; - highPlot = highPlot + crispCorr; - lowPlot = lowPlot + crispCorr; - whiskersPath = [ - // High whisker - 'M', - crispX - halfWidth * whiskerLength, - highPlot, - 'L', - crispX + halfWidth * whiskerLength, - highPlot, - - // Low whisker - 'M', - crispX - halfWidth * whiskerLength, - lowPlot, - 'L', - crispX + halfWidth * whiskerLength, - lowPlot - ]; - } - - // The median - crispCorr = (medianAttr['stroke-width'] % 2) / 2; - medianPlot = mathRound(point.medianPlot) + crispCorr; - medianPath = [ - 'M', - left, - medianPlot, - 'L', - right, - medianPlot, - 'z' - ]; - - // Create or update the graphics - if (graphic) { // update - - point.stem.animate({ d: stemPath }); - if (whiskerLength) { - point.whiskers.animate({ d: whiskersPath }); - } - if (doQuartiles) { - point.box.animate({ d: boxPath }); - } - point.medianShape.animate({ d: medianPath }); - - } else { // create new - point.graphic = graphic = renderer.g() - .add(series.group); - - point.stem = renderer.path(stemPath) - .attr(stemAttr) - .add(graphic); - - if (whiskerLength) { - point.whiskers = renderer.path(whiskersPath) - .attr(whiskersAttr) - .add(graphic); - } - if (doQuartiles) { - point.box = renderer.path(boxPath) - .attr(pointAttr) - .add(graphic); - } - point.medianShape = renderer.path(medianPath) - .attr(medianAttr) - .add(graphic); - } - } - }); - - } - - -}); - -/* **************************************************************************** - * End Box plot series code * - *****************************************************************************/ -/* **************************************************************************** - * Start error bar series code * - *****************************************************************************/ - -// 1 - set default options -defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, { - color: '#000000', - grouping: false, - linkedTo: ':previous', - tooltip: { - pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>' - }, - whiskerWidth: null -}); - -// 2 - Create the series object -seriesTypes.errorbar = extendClass(seriesTypes.boxplot, { - type: 'errorbar', - pointArrayMap: ['low', 'high'], // array point configs are mapped to this - toYData: function (point) { // return a plain array for speedy calculation - return [point.low, point.high]; - }, - pointValKey: 'high', // defines the top of the tracker - doQuartiles: false, - - /** - * Get the width and X offset, either on top of the linked series column - * or standalone - */ - getColumnMetrics: function () { - return (this.linkedParent && this.linkedParent.columnMetrics) || - seriesTypes.column.prototype.getColumnMetrics.call(this); - } -}); - -/* **************************************************************************** - * End error bar series code * - *****************************************************************************/ -/* **************************************************************************** - * Start Waterfall series code * - *****************************************************************************/ - -// 1 - set default options -defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, { - lineWidth: 1, - lineColor: '#333', - dashStyle: 'dot', - borderColor: '#333' -}); - - -// 2 - Create the series object -seriesTypes.waterfall = extendClass(seriesTypes.column, { - type: 'waterfall', - - upColorProp: 'fill', - - pointArrayMap: ['low', 'y'], - - pointValKey: 'y', - - /** - * Init waterfall series, force stacking - */ - init: function (chart, options) { - // force stacking - options.stacking = true; - - seriesTypes.column.prototype.init.call(this, chart, options); - }, - - - /** - * Translate data points from raw values - */ - translate: function () { - var series = this, - options = series.options, - axis = series.yAxis, - len, - i, - points, - point, - shapeArgs, - stack, - y, - previousY, - stackPoint, - threshold = options.threshold, - crispCorr = (options.borderWidth % 2) / 2; - - // run column series translate - seriesTypes.column.prototype.translate.apply(this); - - previousY = threshold; - points = series.points; - - for (i = 0, len = points.length; i < len; i++) { - // cache current point object - point = points[i]; - shapeArgs = point.shapeArgs; - - // get current stack - stack = series.getStack(i); - stackPoint = stack.points[series.index]; - - // override point value for sums - if (isNaN(point.y)) { - point.y = series.yData[i]; - } - - // up points - y = mathMax(previousY, previousY + point.y) + stackPoint[0]; - shapeArgs.y = axis.translate(y, 0, 1); - - - // sum points - if (point.isSum || point.isIntermediateSum) { - shapeArgs.y = axis.translate(stackPoint[1], 0, 1); - shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y; - - // if it's not the sum point, update previous stack end position - } else { - previousY += stack.total; - } - - // negative points - if (shapeArgs.height < 0) { - shapeArgs.y += shapeArgs.height; - shapeArgs.height *= -1; - } - - point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - crispCorr; - shapeArgs.height = mathRound(shapeArgs.height); - point.yBottom = shapeArgs.y + shapeArgs.height; - } - }, - - /** - * Call default processData then override yData to reflect waterfall's extremes on yAxis - */ - processData: function (force) { - var series = this, - options = series.options, - yData = series.yData, - points = series.points, - point, - dataLength = yData.length, - threshold = options.threshold || 0, - subSum, - sum, - dataMin, - dataMax, - y, - i; - - sum = subSum = dataMin = dataMax = threshold; - - for (i = 0; i < dataLength; i++) { - y = yData[i]; - point = points && points[i] ? points[i] : {}; - - if (y === "sum" || point.isSum) { - yData[i] = sum; - } else if (y === "intermediateSum" || point.isIntermediateSum) { - yData[i] = subSum; - subSum = threshold; - } else { - sum += y; - subSum += y; - } - dataMin = Math.min(sum, dataMin); - dataMax = Math.max(sum, dataMax); - } - - Series.prototype.processData.call(this, force); - - // Record extremes - series.dataMin = dataMin; - series.dataMax = dataMax; - }, - - /** - * Return y value or string if point is sum - */ - toYData: function (pt) { - if (pt.isSum) { - return "sum"; - } else if (pt.isIntermediateSum) { - return "intermediateSum"; - } - - return pt.y; - }, - - /** - * Postprocess mapping between options and SVG attributes - */ - getAttribs: function () { - seriesTypes.column.prototype.getAttribs.apply(this, arguments); - - var series = this, - options = series.options, - stateOptions = options.states, - upColor = options.upColor || series.color, - hoverColor = Highcharts.Color(upColor).brighten(0.1).get(), - seriesDownPointAttr = merge(series.pointAttr), - upColorProp = series.upColorProp; - - seriesDownPointAttr[''][upColorProp] = upColor; - seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor; - seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor; - - each(series.points, function (point) { - if (point.y > 0 && !point.color) { - point.pointAttr = seriesDownPointAttr; - point.color = upColor; - } - }); - }, - - /** - * Draw columns' connector lines - */ - getGraphPath: function () { - - var data = this.data, - length = data.length, - lineWidth = this.options.lineWidth + this.options.borderWidth, - normalizer = mathRound(lineWidth) % 2 / 2, - path = [], - M = 'M', - L = 'L', - prevArgs, - pointArgs, - i, - d; - - for (i = 1; i < length; i++) { - pointArgs = data[i].shapeArgs; - prevArgs = data[i - 1].shapeArgs; - - d = [ - M, - prevArgs.x + prevArgs.width, prevArgs.y + normalizer, - L, - pointArgs.x, prevArgs.y + normalizer - ]; - - if (data[i - 1].y < 0) { - d[2] += prevArgs.height; - d[5] += prevArgs.height; - } - - path = path.concat(d); - } - - return path; - }, - - /** - * Extremes are recorded in processData - */ - getExtremes: noop, - - /** - * Return stack for given index - */ - getStack: function (i) { - var axis = this.yAxis, - stacks = axis.stacks, - key = this.stackKey; - - if (this.processedYData[i] < this.options.threshold) { - key = '-' + key; - } - - return stacks[key][i]; - }, - - drawGraph: Series.prototype.drawGraph -}); - -/* **************************************************************************** - * End Waterfall series code * - *****************************************************************************/ -/* **************************************************************************** - * Start Bubble series code * - *****************************************************************************/ - -// 1 - set default options -defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, { - dataLabels: { - inside: true, - style: { - color: 'white', - textShadow: '0px 0px 3px black' - }, - verticalAlign: 'middle' - }, - // displayNegative: true, - marker: { - // fillOpacity: 0.5, - lineColor: null, // inherit from series.color - lineWidth: 1 - }, - minSize: 8, - maxSize: '20%', - // negativeColor: null, - // sizeBy: 'area' - tooltip: { - pointFormat: '({point.x}, {point.y}), Size: {point.z}' - }, - turboThreshold: 0, - zThreshold: 0 -}); - -// 2 - Create the series object -seriesTypes.bubble = extendClass(seriesTypes.scatter, { - type: 'bubble', - pointArrayMap: ['y', 'z'], - parallelArrays: ['x', 'y', 'z'], - trackerGroups: ['group', 'dataLabelsGroup'], - bubblePadding: true, - - /** - * Mapping between SVG attributes and the corresponding options - */ - pointAttrToOptions: { - stroke: 'lineColor', - 'stroke-width': 'lineWidth', - fill: 'fillColor' - }, - - /** - * Apply the fillOpacity to all fill positions - */ - applyOpacity: function (fill) { - var markerOptions = this.options.marker, - fillOpacity = pick(markerOptions.fillOpacity, 0.5); - - // When called from Legend.colorizeItem, the fill isn't predefined - fill = fill || markerOptions.fillColor || this.color; - - if (fillOpacity !== 1) { - fill = Color(fill).setOpacity(fillOpacity).get('rgba'); - } - return fill; - }, - - /** - * Extend the convertAttribs method by applying opacity to the fill - */ - convertAttribs: function () { - var obj = Series.prototype.convertAttribs.apply(this, arguments); - - obj.fill = this.applyOpacity(obj.fill); - - return obj; - }, - - /** - * Get the radius for each point based on the minSize, maxSize and each point's Z value. This - * must be done prior to Series.translate because the axis needs to add padding in - * accordance with the point sizes. - */ - getRadii: function (zMin, zMax, minSize, maxSize) { - var len, - i, - pos, - zData = this.zData, - radii = [], - sizeByArea = this.options.sizeBy !== 'width', - zRange; - - // Set the shape type and arguments to be picked up in drawPoints - for (i = 0, len = zData.length; i < len; i++) { - zRange = zMax - zMin; - pos = zRange > 0 ? // relative size, a number between 0 and 1 - (zData[i] - zMin) / (zMax - zMin) : - 0.5; - if (sizeByArea && pos >= 0) { - pos = Math.sqrt(pos); - } - radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2); - } - this.radii = radii; - }, - - /** - * Perform animation on the bubbles - */ - animate: function (init) { - var animation = this.options.animation; - - if (!init) { // run the animation - each(this.points, function (point) { - var graphic = point.graphic, - shapeArgs = point.shapeArgs; - - if (graphic && shapeArgs) { - // start values - graphic.attr('r', 1); - - // animate - graphic.animate({ - r: shapeArgs.r - }, animation); - } - }); - - // delete this function to allow it only once - this.animate = null; - } - }, - - /** - * Extend the base translate method to handle bubble size - */ - translate: function () { - - var i, - data = this.data, - point, - radius, - radii = this.radii; - - // Run the parent method - seriesTypes.scatter.prototype.translate.call(this); - - // Set the shape type and arguments to be picked up in drawPoints - i = data.length; - - while (i--) { - point = data[i]; - radius = radii ? radii[i] : 0; // #1737 - - // Flag for negativeColor to be applied in Series.js - point.negative = point.z < (this.options.zThreshold || 0); - - if (radius >= this.minPxSize / 2) { - // Shape arguments - point.shapeType = 'circle'; - point.shapeArgs = { - x: point.plotX, - y: point.plotY, - r: radius - }; - - // Alignment box for the data label - point.dlBox = { - x: point.plotX - radius, - y: point.plotY - radius, - width: 2 * radius, - height: 2 * radius - }; - } else { // below zThreshold - point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691 - } - } - }, - - /** - * Get the series' symbol in the legend - * - * @param {Object} legend The legend object - * @param {Object} item The series (this) or point - */ - drawLegendSymbol: function (legend, item) { - var radius = pInt(legend.itemStyle.fontSize) / 2; - - item.legendSymbol = this.chart.renderer.circle( - radius, - legend.baseline - radius, - radius - ).attr({ - zIndex: 3 - }).add(item.legendGroup); - item.legendSymbol.isMarker = true; - - }, - - drawPoints: seriesTypes.column.prototype.drawPoints, - alignDataLabel: seriesTypes.column.prototype.alignDataLabel -}); - -/** - * Add logic to pad each axis with the amount of pixels - * necessary to avoid the bubbles to overflow. - */ -Axis.prototype.beforePadding = function () { - var axis = this, - axisLength = this.len, - chart = this.chart, - pxMin = 0, - pxMax = axisLength, - isXAxis = this.isXAxis, - dataKey = isXAxis ? 'xData' : 'yData', - min = this.min, - extremes = {}, - smallestSize = math.min(chart.plotWidth, chart.plotHeight), - zMin = Number.MAX_VALUE, - zMax = -Number.MAX_VALUE, - range = this.max - min, - transA = axisLength / range, - activeSeries = []; - - // Handle padding on the second pass, or on redraw - if (this.tickPositions) { - each(this.series, function (series) { - - var seriesOptions = series.options, - zData; - - if (series.bubblePadding && series.visible) { - - // Correction for #1673 - axis.allowZoomOutside = true; - - // Cache it - activeSeries.push(series); - - if (isXAxis) { // because X axis is evaluated first - - // For each series, translate the size extremes to pixel values - each(['minSize', 'maxSize'], function (prop) { - var length = seriesOptions[prop], - isPercent = /%$/.test(length); - - length = pInt(length); - extremes[prop] = isPercent ? - smallestSize * length / 100 : - length; - - }); - series.minPxSize = extremes.minSize; - - // Find the min and max Z - zData = series.zData; - if (zData.length) { // #1735 - zMin = math.min( - zMin, - math.max( - arrayMin(zData), - seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE - ) - ); - zMax = math.max(zMax, arrayMax(zData)); - } - } - } - }); - - each(activeSeries, function (series) { - - var data = series[dataKey], - i = data.length, - radius; - - if (isXAxis) { - series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize); - } - - if (range > 0) { - while (i--) { - if (typeof data[i] === 'number') { - radius = series.radii[i]; - pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin); - pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax); - } - } - } - }); - - if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) { - pxMax -= axisLength; - transA *= (axisLength + pxMin - pxMax) / axisLength; - this.min += pxMin / transA; - this.max += pxMax / transA; - } - } -}; - -/* **************************************************************************** - * End Bubble series code * - *****************************************************************************/ - -(function () { - - /** - * Extensions for polar charts. Additionally, much of the geometry required for polar charts is - * gathered in RadialAxes.js. - * - */ - - var seriesProto = Series.prototype, - pointerProto = Pointer.prototype, - colProto; - - /** - * Translate a point's plotX and plotY from the internal angle and radius measures to - * true plotX, plotY coordinates - */ - seriesProto.toXY = function (point) { - var xy, - chart = this.chart, - plotX = point.plotX, - plotY = point.plotY; - - // Save rectangular plotX, plotY for later computation - point.rectPlotX = plotX; - point.rectPlotY = plotY; - - // Record the angle in degrees for use in tooltip - point.clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360; - - // Find the polar plotX and plotY - xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY); - point.plotX = point.polarPlotX = xy.x - chart.plotLeft; - point.plotY = point.polarPlotY = xy.y - chart.plotTop; - }; - - /** - * Order the tooltip points to get the mouse capture ranges correct. #1915. - */ - seriesProto.orderTooltipPoints = function (points) { - if (this.chart.polar) { - points.sort(function (a, b) { - return a.clientX - b.clientX; - }); - - // Wrap mouse tracking around to capture movement on the segment to the left - // of the north point (#1469, #2093). - if (points[0]) { - points[0].wrappedClientX = points[0].clientX + 360; - points.push(points[0]); - } - } - }; - - - /** - * Add some special init logic to areas and areasplines - */ - function initArea(proceed, chart, options) { - proceed.call(this, chart, options); - if (this.chart.polar) { - - /** - * Overridden method to close a segment path. While in a cartesian plane the area - * goes down to the threshold, in the polar chart it goes to the center. - */ - this.closeSegment = function (path) { - var center = this.xAxis.center; - path.push( - 'L', - center[0], - center[1] - ); - }; - - // Instead of complicated logic to draw an area around the inner area in a stack, - // just draw it behind - this.closedStacks = true; - } - } - - if (seriesTypes.area) { - wrap(seriesTypes.area.prototype, 'init', initArea); - } - if (seriesTypes.areaspline) { - wrap(seriesTypes.areaspline.prototype, 'init', initArea); - } - - if (seriesTypes.spline) { - /** - * Overridden method for calculating a spline from one point to the next - */ - wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) { - - var ret, - smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc; - denom = smoothing + 1, - plotX, - plotY, - lastPoint, - nextPoint, - lastX, - lastY, - nextX, - nextY, - leftContX, - leftContY, - rightContX, - rightContY, - distanceLeftControlPoint, - distanceRightControlPoint, - leftContAngle, - rightContAngle, - jointAngle; - - - if (this.chart.polar) { - - plotX = point.plotX; - plotY = point.plotY; - lastPoint = segment[i - 1]; - nextPoint = segment[i + 1]; - - // Connect ends - if (this.connectEnds) { - if (!lastPoint) { - lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected - } - if (!nextPoint) { - nextPoint = segment[1]; - } - } - - // find control points - if (lastPoint && nextPoint) { - - lastX = lastPoint.plotX; - lastY = lastPoint.plotY; - nextX = nextPoint.plotX; - nextY = nextPoint.plotY; - leftContX = (smoothing * plotX + lastX) / denom; - leftContY = (smoothing * plotY + lastY) / denom; - rightContX = (smoothing * plotX + nextX) / denom; - rightContY = (smoothing * plotY + nextY) / denom; - distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2)); - distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2)); - leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX); - rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX); - jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2); - - - // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle - if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) { - jointAngle -= Math.PI; - } - - // Find the corrected control points for a spline straight through the point - leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint; - leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint; - rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint; - rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint; - - // Record for drawing in next point - point.rightContX = rightContX; - point.rightContY = rightContY; - - } - - - // moveTo or lineTo - if (!i) { - ret = ['M', plotX, plotY]; - } else { // curve from last point to this - ret = [ - 'C', - lastPoint.rightContX || lastPoint.plotX, - lastPoint.rightContY || lastPoint.plotY, - leftContX || plotX, - leftContY || plotY, - plotX, - plotY - ]; - lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later - } - - - } else { - ret = proceed.call(this, segment, point, i); - } - return ret; - }); - } - - /** - * Extend translate. The plotX and plotY values are computed as if the polar chart were a - * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from - * center. - */ - wrap(seriesProto, 'translate', function (proceed) { - - // Run uber method - proceed.call(this); - - // Postprocess plot coordinates - if (this.chart.polar && !this.preventPostTranslate) { - var points = this.points, - i = points.length; - while (i--) { - // Translate plotX, plotY from angle and radius to true plot coordinates - this.toXY(points[i]); - } - } - }); - - /** - * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in - * line-like series. - */ - wrap(seriesProto, 'getSegmentPath', function (proceed, segment) { - - var points = this.points; - - // Connect the path - if (this.chart.polar && this.options.connectEnds !== false && - segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) { - this.connectEnds = true; // re-used in splines - segment = [].concat(segment, [points[0]]); - } - - // Run uber method - return proceed.call(this, segment); - - }); - - - function polarAnimate(proceed, init) { - var chart = this.chart, - animation = this.options.animation, - group = this.group, - markerGroup = this.markerGroup, - center = this.xAxis.center, - plotLeft = chart.plotLeft, - plotTop = chart.plotTop, - attribs; - - // Specific animation for polar charts - if (chart.polar) { - - // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation - // would be so slow it would't matter. - if (chart.renderer.isSVG) { - - if (animation === true) { - animation = {}; - } - - // Initialize the animation - if (init) { - - // Scale down the group and place it in the center - attribs = { - translateX: center[0] + plotLeft, - translateY: center[1] + plotTop, - scaleX: 0.001, // #1499 - scaleY: 0.001 - }; - - group.attr(attribs); - if (markerGroup) { - markerGroup.attrSetters = group.attrSetters; - markerGroup.attr(attribs); - } - - // Run the animation - } else { - attribs = { - translateX: plotLeft, - translateY: plotTop, - scaleX: 1, - scaleY: 1 - }; - group.animate(attribs, animation); - if (markerGroup) { - markerGroup.animate(attribs, animation); - } - - // Delete this function to allow it only once - this.animate = null; - } - } - - // For non-polar charts, revert to the basic animation - } else { - proceed.call(this, init); - } - } - - // Define the animate method for regular series - wrap(seriesProto, 'animate', polarAnimate); - - /** - * Throw in a couple of properties to let setTooltipPoints know we're indexing the points - * in degrees (0-360), not plot pixel width. - */ - wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) { - - if (this.chart.polar) { - extend(this.xAxis, { - tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array - }); - } - - // Run uber method - return proceed.call(this, renew); - }); - - - if (seriesTypes.column) { - - colProto = seriesTypes.column.prototype; - /** - * Define the animate method for columnseries - */ - wrap(colProto, 'animate', polarAnimate); - - - /** - * Extend the column prototype's translate method - */ - wrap(colProto, 'translate', function (proceed) { - - var xAxis = this.xAxis, - len = this.yAxis.len, - center = xAxis.center, - startAngleRad = xAxis.startAngleRad, - renderer = this.chart.renderer, - start, - points, - point, - i; - - this.preventPostTranslate = true; - - // Run uber method - proceed.call(this); - - // Postprocess plot coordinates - if (xAxis.isRadial) { - points = this.points; - i = points.length; - while (i--) { - point = points[i]; - start = point.barX + startAngleRad; - point.shapeType = 'path'; - point.shapeArgs = { - d: renderer.symbols.arc( - center[0], - center[1], - len - point.plotY, - null, - { - start: start, - end: start + point.pointWidth, - innerR: len - pick(point.yBottom, len) - } - ) - }; - this.toXY(point); // provide correct plotX, plotY for tooltip - } - } - }); - - - /** - * Align column data labels outside the columns. #1199. - */ - wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) { - - if (this.chart.polar) { - var angle = point.rectPlotX / Math.PI * 180, - align, - verticalAlign; - - // Align nicely outside the perimeter of the columns - if (options.align === null) { - if (angle > 20 && angle < 160) { - align = 'left'; // right hemisphere - } else if (angle > 200 && angle < 340) { - align = 'right'; // left hemisphere - } else { - align = 'center'; // top or bottom - } - options.align = align; - } - if (options.verticalAlign === null) { - if (angle < 45 || angle > 315) { - verticalAlign = 'bottom'; // top part - } else if (angle > 135 && angle < 225) { - verticalAlign = 'top'; // bottom part - } else { - verticalAlign = 'middle'; // left or right - } - options.verticalAlign = verticalAlign; - } - - seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); - } else { - proceed.call(this, point, dataLabel, options, alignTo, isNew); - } - - }); - } - - - /** - * Extend the mouse tracker to return the tooltip position index in terms of - * degrees rather than pixels - */ - wrap(pointerProto, 'getIndex', function (proceed, e) { - var ret, - chart = this.chart, - center, - x, - y; - - if (chart.polar) { - center = chart.xAxis[0].center; - x = e.chartX - center[0] - chart.plotLeft; - y = e.chartY - center[1] - chart.plotTop; - - ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180); - - } else { - - // Run uber method - ret = proceed.call(this, e); - } - return ret; - }); - - /** - * Extend getCoordinates to prepare for polar axis values - */ - wrap(pointerProto, 'getCoordinates', function (proceed, e) { - var chart = this.chart, - ret = { - xAxis: [], - yAxis: [] - }; - - if (chart.polar) { - - each(chart.axes, function (axis) { - var isXAxis = axis.isXAxis, - center = axis.center, - x = e.chartX - center[0] - chart.plotLeft, - y = e.chartY - center[1] - chart.plotTop; - - ret[isXAxis ? 'xAxis' : 'yAxis'].push({ - axis: axis, - value: axis.translate( - isXAxis ? - Math.PI - Math.atan2(x, y) : // angle - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center - true - ) - }); - }); - - } else { - ret = proceed.call(this, e); - } - - return ret; - }); - -}()); - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/highcharts.js b/pykeg/web/static/highcharts/js/highcharts.js deleted file mode 100644 index 31565d097..000000000 --- a/pykeg/web/static/highcharts/js/highcharts.js +++ /dev/null @@ -1,294 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - - (c) 2009-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(){function r(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function x(){var a,b=arguments,c,d={},e=function(a,b){var c,d;typeof a!=="object"&&(a={});for(d in b)b.hasOwnProperty(d)&&(c=b[d],a[d]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?e(a[d]||{},c):b[d]);return a};b[0]===!0&&(d=b[1],b=Array.prototype.slice.call(b,2));c=b.length;for(a=0;a<c;a++)d=e(d,b[a]);return d}function z(a,b){return parseInt(a,b||10)}function fa(a){return typeof a=== -"string"}function S(a){return typeof a==="object"}function Ka(a){return Object.prototype.toString.call(a)==="[object Array]"}function wa(a){return typeof a==="number"}function xa(a){return P.log(a)/P.LN10}function ga(a){return P.pow(10,a)}function ha(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function t(a){return a!==u&&a!==null}function v(a,b,c){var d,e;if(fa(b))t(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(t(b)&&S(b))for(d in b)a.setAttribute(d,b[d]); -return e}function ja(a){return Ka(a)?a:[a]}function n(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function D(a,b){if(ya&&b&&b.opacity!==u)b.filter="alpha(opacity="+b.opacity*100+")";r(a.style,b)}function T(a,b,c,d,e){a=y.createElement(a);b&&r(a,b);e&&D(a,{padding:0,border:Q,margin:0});c&&D(a,c);d&&d.appendChild(a);return a}function ia(a,b){var c=function(){};c.prototype=new a;r(c.prototype,b);return c}function Da(a,b,c,d){var e=G.lang,a=+a|| -0,f=b===-1?(a.toString().split(".")[1]||"").length:isNaN(b=M(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(z(a=M(a).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+M(a-c).toFixed(f).slice(2):"")}function Ea(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function Va(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);a.unshift(d);return c.apply(this, -a)}}function Fa(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=G.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e=Da(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:"")):e=ab(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function mb(a){return P.pow(10,N(P.log(a)/P.LN10))}function nb(a,b,c,d){var e,c=n(c, -1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Ab(){this.symbol=this.color=0}function ob(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}function La(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function za(a){for(var b=a.length,c=a[0];b--;)a[b]>c&&(c= -a[b]);return c}function Ma(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Na(a){bb||(bb=T(Ga));a&&bb.appendChild(a);bb.innerHTML=""}function ka(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else C.console&&console.log(c)}function aa(a){return parseFloat(a.toPrecision(14))}function Oa(a,b){oa=n(a,b.animation)}function Bb(){var a=G.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Pa=(a&&G.global.timezoneOffset||0)*6E4;cb=a? -Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,n(c,1),n(g,0),n(h,0),n(i,0))).getTime()};pb=b+"Minutes";qb=b+"Hours";rb=b+"Day";Wa=b+"Date";db=b+"Month";eb=b+"FullYear";Cb=c+"Minutes";Db=c+"Hours";sb=c+"Date";Eb=c+"Month";Fb=c+"FullYear"}function pa(){}function Qa(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function qa(){this.init.apply(this,arguments)}function Gb(a,b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d; -this.total=null;this.points={};this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:n(b.y,g?4:c?14:-6),x:n(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function tb(){this.init.apply(this,arguments)}function fb(){this.init.apply(this,arguments)}var u,y=document,C=window,P=Math,w=P.round,N=P.floor,Ha=P.ceil,s=P.max,I=P.min,M=P.abs,U=P.cos,ba=P.sin,Aa=P.PI,Ba= -Aa*2/360,ra=navigator.userAgent,Hb=C.opera,ya=/msie/i.test(ra)&&!Hb,gb=y.documentMode===8,hb=/AppleWebKit/.test(ra),Xa=/Firefox/.test(ra),Ib=/(Mobile|Android|Windows Phone)/.test(ra),Ca="http://www.w3.org/2000/svg",V=!!y.createElementNS&&!!y.createElementNS(Ca,"svg").createSVGRect,Nb=Xa&&parseInt(ra.split("Firefox/")[1],10)<4,da=!V&&!ya&&!!y.createElement("canvas").getContext,Ya,ib=y.documentElement.ontouchstart!==u,Jb={},ub=0,bb,G,ab,oa,vb,E,la=function(){},Ia=[],Ga="div",Q="none",Ob=/^[0-9]+$/, -Kb="rgba(192,192,192,"+(V?1.0E-4:0.002)+")",Lb="stroke-width",cb,Pa,pb,qb,rb,Wa,db,eb,Cb,Db,sb,Eb,Fb,L={};C.Highcharts=C.Highcharts?ka(16,!0):{};ab=function(a,b,c){if(!t(b)||isNaN(b))return"Invalid date";var a=n(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b-Pa),e,f=d[qb](),g=d[rb](),h=d[Wa](),i=d[db](),j=d[eb](),k=G.lang,l=k.weekdays,d=r({a:l[g].substr(0,3),A:l[g],d:Ea(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ea(i+1),y:j.toString().substr(2,2),Y:j,H:Ea(f),I:Ea(f%12||12),l:f%12||12,M:Ea(d[pb]()),p:f<12?"AM": -"PM",P:f<12?"am":"pm",S:Ea(d.getSeconds()),L:Ea(w(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Ab.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};E=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}("millisecond",1,"second",1E3,"minute",6E4,"hour",36E5,"day", -864E5,"week",6048E5,"month",26784E5,"year",31556952E3);vb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f&&b.length===c.length)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f, -f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){C.HighchartsAdapter=C.HighchartsAdapter||a&&{init:function(b){var c=a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur", -"_default","width","height","opacity"],function(a,b){var e=d,k;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){var d,c=a?c:this;if(c.prop!=="align")return d=c.elem,d.attr?d.attr(c.prop,b==="cur"?u:c.now):k.apply(this,arguments)})});Va(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}: -d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==u)c.chart=c.chart||{},c.chart.renderTo=this[0],new Highcharts[a](c,b[1]),d=this;c===u&&(d=Ia[v(this[0],"data-highcharts-chart")]);return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b, -c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=y.removeEventListener?"removeEventListener":"detachEvent";y[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,c,d,e){var f=a.Event(c),g="detached"+c,h;!ya&&d&&(delete d.layerX,delete d.layerY);r(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault", -"stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===u)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style={};if(c.d)b.toD=c.d,c.d=1;e.stop();c.opacity!==u&&b.attr&&(c.opacity+="px");e.animate(c,d)},stop:function(b){a(b).stop()}}})(C.jQuery);var W= -C.HighchartsAdapter,J=W||{};W&&W.init.call(W,vb);var jb=J.adapterRun,Pb=J.getScript,sa=J.inArray,p=J.each,wb=J.grep,Qb=J.offset,Ra=J.map,F=J.addEvent,X=J.removeEvent,A=J.fireEvent,Rb=J.washMouseEvent,kb=J.animate,Za=J.stop,J={enabled:!0,x:0,y:15,style:{color:"#666",cursor:"default",fontSize:"11px"}};G={colors:"#2f7ed8,#0d233a,#8bbc21,#910000,#1aadce,#492970,#f28f43,#77a1e5,#c42525,#a6c96a".split(","),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","), -shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,canvasToolsURL:"http://code.highcharts.com/3.0.9/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/3.0.9/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:5, -defaultSeriesType:"line",ignoreHiddenSeries:!0,spacing:[10,10,15,10],style:{fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif',fontSize:"12px"},backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",margin:15,style:{color:"#274b6d",fontSize:"16px"}},subtitle:{text:"",align:"center",style:{color:"#4d759e"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1, -animation:{duration:1E3},events:{},lineWidth:2,marker:{enabled:!0,lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:x(J,{align:"center",enabled:!1,formatter:function(){return this.y===null?"":Da(this.y,-1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,states:{hover:{marker:{}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3}},labels:{style:{position:"absolute",color:"#3E576F"}}, -legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderWidth:1,borderColor:"#909090",borderRadius:5,navigation:{activeColor:"#274b6d",inactiveColor:"#CCC"},shadow:!1,itemStyle:{cursor:"pointer",color:"#274b6d",fontSize:"12px"},itemHoverStyle:{color:"#000"},itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold", -position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:V,backgroundColor:"rgba(255, 255, 255, .85)",borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>', -pointFormat:'<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',shadow:!0,snap:Ib?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var Y=G.plotOptions,W=Y.line;Bb();var Sb=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/, -Tb=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,Ub=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,ta=function(a){var b=[],c,d;(function(a){a&&a.stops?d=Ra(a.stops,function(a){return ta(a[1])}):(c=Sb.exec(a))?b=[z(c[1]),z(c[2]),z(c[3]),parseFloat(c[4],10)]:(c=Tb.exec(a))?b=[z(c[1],16),z(c[2],16),z(c[3],16),1]:(c=Ub.exec(a))&&(b=[z(c[1]),z(c[2]),z(c[3]),1])})(a);return{get:function(c){var f;d?(f=x(a),f.stops=[].concat(f.stops),p(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})): -f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)p(d,function(b){b.brighten(a)});else if(wa(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=z(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};pa.prototype={init:function(a,b){this.element=b==="span"?T(b):y.createElementNS(Ca,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=n(b,oa,!0); -Za(this);if(b){b=x(b);if(c)b.complete=c;kb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,l=this.shadows,m,q,o=this;fa(a)&&t(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),o=v(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&c!=="fill"&&(o=parseFloat(o));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==u&&(d=e);if(c==="d")d&& -d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;e<g.childNodes.length;e++)f=g.childNodes[e],v(f,"x")===v(g,"x")&&v(f,"x",d);else if(this.rotation&&(c==="x"||c==="y"))q=!0;else if(c==="fill")d=i.color(d,g,c);else if(h==="circle"&&(c==="x"||c==="y"))c={x:"cx",y:"cy"}[c]||c;else if(h==="rect"&&c==="r")v(g,{rx:d,ry:d}),j=!0;else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="verticalAlign"||c==="scaleX"||c==="scaleY")j=q=!0;else if(c==="stroke")d= -i.color(d,g,c);else if(c==="dashstyle")if(c="stroke-dasharray",d=d&&d.toLowerCase(),d==="solid")d=Q;else{if(d){d=d.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=d.length;e--;)d[e]=z(d[e])*n(a["stroke-width"],this["stroke-width"]);d=d.join(",")}}else if(c==="width")d=z(d);else if(c==="align")c="text-anchor",d= -{left:"start",center:"middle",right:"end"}[d];else if(c==="title")e=g.getElementsByTagName("title")[0],e||(e=y.createElementNS(Ca,"title"),g.appendChild(e)),e.textContent=d;c==="strokeWidth"&&(c="stroke-width");if(c==="stroke-width"||c==="stroke"){this[c]=d;if(this.stroke&&this["stroke-width"])v(g,"stroke",this.stroke),v(g,"stroke-width",this["stroke-width"]),this.hasStroke=!0;else if(c==="stroke-width"&&d===0&&this.hasStroke)g.removeAttribute("stroke"),this.hasStroke=!1;j=!0}this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&& -(m||(this.symbolAttr(a),m=!0),j=!0);if(l&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(c))for(e=l.length;e--;)v(l[e],c,c==="height"?s(d-(l[e].cutHeight||0),0):d);if((c==="width"||c==="height")&&h==="rect"&&d<0)d=0;this[c]=d;c==="text"?(d!==this.textStr&&delete this.bBox,this.textStr=d,this.added&&i.buildText(this)):j||v(g,c,d)}q&&this.updateTransform()}return o},addClass:function(a){var b=this.element,c=v(b,"class")||"";c.indexOf(a)===-1&&v(b,"class",c+" "+a);return this},symbolAttr:function(a){var b= -this;p("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=n(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+a.id+")":Q)},crisp:function(a,b,c,d,e){var f,g={},h={},i,a=a||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;i=w(a)%2/2;h.x=N(b||this.x||0)+i;h.y=N(c||this.y||0)+i;h.width=N((d||this.width||0)-2*i);h.height=N((e||this.height||0)-2*i);h.strokeWidth= -a;for(f in h)this[f]!==h[f]&&(this[f]=g[f]=h[f]);return g},css:function(a){var b=this.element,c=this.textWidth=a&&a.width&&b.nodeName.toLowerCase()==="text"&&z(a.width),d,e="",f=function(a,b){return"-"+b.toLowerCase()};if(a&&a.color)a.fill=a.color;this.styles=a=r(this.styles,a);c&&delete a.width;if(ya&&!V)D(this.element,a);else{for(d in a)e+=d.replace(/([A-Z])/g,f)+":"+a[d]+";";v(b,"style",e)}c&&this.added&&this.renderer.buildText(this);return this},on:function(a,b){var c=this,d=c.element;ib&&a=== -"click"?(d.ontouchstart=function(a){c.touchEventFired=Date.now();a.preventDefault();b.call(d,a)},d.onclick=function(a){(ra.indexOf("Android")===-1||Date.now()-(c.touchEventFired||0)>1100)&&b.call(d,a)}):d["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY|| -0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")");(t(c)||t(d))&&a.push("scale("+n(c,1)+" "+n(d,1)+")");a.length&&v(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions= -a,this.alignByTranslate=b,!c||fa(c))this.alignTo=d=c||"renderer",ha(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=n(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=w(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=w(g);this[this.placed?"animate":"attr"](h);this.placed= -!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d,e=this.rotation;c=this.element;var f=this.styles,g=e*Ba;d=this.textStr;var h;if(d===""||Ob.test(d))h=d.length+"|"+f.fontSize+"|"+f.fontFamily,a=b.cache[h];if(!a){if(c.namespaceURI===Ca||b.forExport){try{a=c.getBBox?r({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(i){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){c=a.width;d=a.height;if(ya&&f&&f.fontSize==="11px"&& -d.toPrecision(3)==="16.9")a.height=d=14;if(e)a.width=M(d*ba(g))+M(c*U(g)),a.height=M(d*U(g))+M(c*ba(g))}this.bBox=a;h&&(b.cache[h]=a)}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=v(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted= -a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=z(g);if(c.handleZ)for(c=0;c<e.length;c++)if(a=e[c],b=v(a,"zIndex"),a!==f&&(z(b)>g||!t(g)&&t(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;A(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.renderer.isSVG&&b.nodeName==="SPAN"&&a.parentGroup,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point= -null;Za(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f<a.stops.length;f++)a.stops[f]=a.stops[f].destroy();a.stops=null}a.safeRemoveChild(b);for(c&&p(c,function(b){a.safeRemoveChild(b)});d&&d.div.childNodes.length===0;)b=d.parentGroup,a.safeRemoveChild(d.div),delete d.div,d=b;a.alignTo&&ha(a.renderer.alignedObjects,a);for(e in a)delete a[e];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=n(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted? -"(-1,-1)":"("+n(a.offsetX,1)+", "+n(a.offsetY,1)+")";for(e=1;e<=i;e++){f=g.cloneNode(0);h=i*2+1-2*e;v(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:Q});if(c)v(f,"height",s(v(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this}};var ua=function(){this.init.apply(this,arguments)};ua.prototype={Element:pa,init:function(a,b,c,d){var e=location,f,g;f=this.createElement("svg").attr({version:"1.1"}); -g=f.element;a.appendChild(g);a.innerHTML.indexOf("xmlns")===-1&&v(g,"xmlns",Ca);this.isSVG=!0;this.box=g;this.boxWrapper=f;this.alignedObjects=[];this.url=(Xa||hb)&&y.getElementsByTagName("base").length?e.href.replace(/#.*?$/,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(y.createTextNode("Created with Highcharts 3.0.9"));this.defs=this.createElement("defs").add();this.forExport=d;this.gradients={};this.cache={};this.setSize(b,c,!1);var h; -if(Xa&&a.getBoundingClientRect)this.subPixelFix=b=function(){D(a,{left:0,top:0});h=a.getBoundingClientRect();D(a,{left:Ha(h.left)-h.left+"px",top:Ha(h.top)-h.top+"px"})},b(),F(C,"resize",b)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Ma(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&X(C,"resize",this.subPixelFix);return this.alignedObjects=null},createElement:function(a){var b= -new this.Element;b.init(this,a);return b},draw:function(){},buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=n(a.textStr,"").toString().replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="(http[^"]+)"/,i=v(b,"x"),j=a.styles,k=a.textWidth,l=j&&j.lineHeight,m=f.length,q=function(a){return l?z(l): -c.fontMetrics(/px$/.test(a&&a.style.fontSize)?a.style.fontSize:j.fontSize||11).h};m--;)b.removeChild(f[m]);k&&!a.added&&this.box.appendChild(b);e[e.length-1]===""&&e.pop();p(e,function(e,f){var l,m=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");l=e.split("|||");p(l,function(e){if(e!==""||l.length===1){var o={},n=y.createElementNS(Ca,"tspan"),p;g.test(e)&&(p=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),v(n,"style",p));h.test(e)&&!d&&(v(n,"onclick",'location.href="'+ -e.match(h)[1]+'"'),D(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");if(e!==" "&&(n.appendChild(y.createTextNode(e)),m?o.dx=0:o.x=i,v(n,o),!m&&f&&(!V&&d&&D(n,{display:"block"}),v(n,"dy",q(n),hb&&n.offsetHeight)),b.appendChild(n),m++,k))for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),o=e.length>1&&j.whiteSpace!=="nowrap",t,s,w=a._clipHeight,u=[],r=q(),$=1;o&&(e.length||u.length);)delete a.bBox,t=a.getBBox(),s=t.width,!V&&c.forExport&&(s=c.measureSpanWidth(n.firstChild.data, -a.styles)),t=s>k,!t||e.length===1?(e=u,u=[],e.length&&($++,w&&$*r>w?(e=["..."],a.attr("title",a.textStr)):(n=y.createElementNS(Ca,"tspan"),v(n,{dy:r,x:i}),p&&v(n,"style",p),b.appendChild(n),s>k&&(k=s)))):(n.removeChild(n.firstChild),u.unshift(e.pop())),e.length&&n.appendChild(y.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g,h,i){var j=this.label(a,b,c,i,null,null,null,null,"button"),k=0,l,m,q,o,n,p,a={x1:0,y1:0,x2:0,y2:1},e=x({"stroke-width":1,stroke:"#CCCCCC", -fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);q=e.style;delete e.style;f=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);o=f.style;delete f.style;g=x(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);n=g.style;delete g.style;h=x(e,{style:{color:"#CCC"}},h);p=h.style;delete h.style;F(j.element,ya?"mouseover":"mouseenter",function(){k!==3&&j.attr(f).css(o)});F(j.element,ya?"mouseout":"mouseleave", -function(){k!==3&&(l=[e,f,g][k],m=[q,o,n][k],j.attr(l).css(m))});j.setState=function(a){(j.state=k=a)?a===2?j.attr(g).css(n):a===3&&j.attr(h).css(p):j.attr(e).css(q)};return j.on("click",function(){k!==3&&d.call(j)}).attr(e).css(r({cursor:"default"},q))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=w(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=w(a[2])+b%2/2);return a},path:function(a){var b={fill:Q};Ka(a)?b.d=a:S(a)&&r(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=S(a)?a:{x:a, -y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(S(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;a=this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0});a.r=c;return a},rect:function(a,b,c,d,e,f){e=S(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:Q});return e.attr(S(a)?a:e.crisp(f,a,b,s(c,0),s(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[n(c,!0)?"animate":"attr"]({width:a, -height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return t(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:Q};arguments.length>1&&r(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(w(b),w(c),d,e,f),i=/^url\((.*?)\)$/, -j,k;if(h)g=this.path(h),r(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&r(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(w((d-b[0])/2),w((e-b[1])/2)))},j=a.match(i)[1],a=Jb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),T("img",{onload:function(){k(g,Jb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/ -2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=U(f),j=ba(f),k=U(g),g=ba(g),e=e.end-f<Aa?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e, -1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]}},clipRect:function(a,b,c,d){var e="highcharts-"+ub++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},color:function(a,b,c){var d=this,e,f=/^rgba/,g,h,i,j,k,l,m,q=[];a&&a.linearGradient?g="linearGradient":a&&a.radialGradient&&(g="radialGradient");if(g){c=a[g];h=d.gradients;j=a.stops;b=b.radialReference;Ka(c)&&(a[g]=c={x1:c[0],y1:c[1],x2:c[2],y2:c[3],gradientUnits:"userSpaceOnUse"}); -g==="radialGradient"&&b&&!t(c.gradientUnits)&&(c=x(c,{cx:b[0]-b[2]/2+c.cx*b[2],cy:b[1]-b[2]/2+c.cy*b[2],r:c.r*b[2],gradientUnits:"userSpaceOnUse"}));for(m in c)m!=="id"&&q.push(m,c[m]);for(m in j)q.push(j[m]);q=q.join(",");h[q]?a=h[q].id:(c.id=a="highcharts-"+ub++,h[q]=i=d.createElement(g).attr(c).add(d.defs),i.stops=[],p(j,function(a){f.test(a[1])?(e=ta(a[1]),k=e.get("rgb"),l=e.get("a")):(k=a[1],l=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":l}).add(i);i.stops.push(a)})); -return"url("+d.url+"#"+a+")"}else return f.test(a)?(e=ta(a),v(b,c+"-opacity",e.get("a")),e.get("rgb")):(b.removeAttribute(c+"-opacity"),a)},text:function(a,b,c,d){var e=G.chart.style,f=da||!V&&this.forExport;if(d&&!this.forExport)return this.html(a,b,c);b=w(n(b,0));c=w(n(c,0));a=this.createElement("text").attr({x:b,y:c,text:a}).css({fontFamily:e.fontFamily,fontSize:e.fontSize});f&&a.css({position:"absolute"});a.x=b;a.y=c;return a},fontMetrics:function(a){var a=z(a||11),a=a<24?a+4:w(a*1.2),b=w(a*0.8); -return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=n.element.style;va=(Z===void 0||Sa===void 0||o.styles.textAlign)&&n.getBBox();o.width=(Z||va.width||0)+2*ea+lb;o.height=(Sa||va.height||0)+2*ea;$=ea+q.fontMetrics(a&&a.fontSize).b;if(y){if(!H)a=w(-s*ea),b=h?-$:0,o.box=H=d?q.symbol(d,a,b,o.width,o.height,v):q.rect(a,b,o.width,o.height,0,v[Lb]),H.add(o);H.isImg||H.attr(x({width:o.width,height:o.height},v));v=null}}function k(){var a=o.styles,a=a&&a.textAlign,b=lb+ea*(1-s),c;c= -h?0:$;if(t(Z)&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Z-va.width);(b!==n.x||c!==n.y)&&n.attr({x:b,y:c});n.x=b;n.y=c}function l(a,b){H?H.attr(a,b):v[a]=b}function m(){n.add(o);o.attr({text:a,x:b,y:c});H&&t(e)&&o.attr({anchorX:e,anchorY:f})}var q=this,o=q.g(i),n=q.text("",0,0,g).attr({zIndex:1}),H,va,s=0,ea=3,lb=0,Z,Sa,Ta,K,B=0,v={},$,g=o.attrSetters,y;F(o,"add",m);g.width=function(a){Z=a;return!1};g.height=function(a){Sa=a;return!1};g.padding=function(a){t(a)&&a!==ea&&(ea=a,k());return!1}; -g.paddingLeft=function(a){t(a)&&a!==lb&&(lb=a,k());return!1};g.align=function(a){s={left:0,center:0.5,right:1}[a];return!1};g.text=function(a,b){n.attr(b,a);j();k();return!1};g[Lb]=function(a,b){y=!0;B=a%2/2;l(b,a);return!1};g.stroke=g.fill=g.r=function(a,b){b==="fill"&&(y=!0);l(b,a);return!1};g.anchorX=function(a,b){e=a;l(b,a+B-Ta);return!1};g.anchorY=function(a,b){f=a;l(b,a-K);return!1};g.x=function(a){o.x=a;a-=s*((Z||va.width)+ea);Ta=w(a);o.attr("translateX",Ta);return!1};g.y=function(a){K=o.y= -w(a);o.attr("translateY",K);return!1};var z=o.css;return r(o,{css:function(a){if(a){var b={},a=x(a);p("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration,textShadow".split(","),function(c){a[c]!==u&&(b[c]=a[c],delete a[c])});n.css(b)}return z.call(o,a)},getBBox:function(){return{width:va.width+2*ea,height:va.height+2*ea,x:va.x-ea,y:va.y-ea}},shadow:function(a){H&&H.shadow(a);return o},destroy:function(){X(o,"add",m);X(o.element,"mouseenter");X(o.element,"mouseleave");n&&(n=n.destroy()); -H&&(H=H.destroy());pa.prototype.destroy.call(o);o=q=j=k=l=m=null}})}};Ya=ua;r(pa.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=r(this.styles,a);D(this.element,a);return this},htmlGetBBox:function(){var a=this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a= -this.renderer,b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=this.shadows;D(b,{marginLeft:c,marginTop:d});i&&p(i,function(a){D(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&p(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var j=this.rotation,k,l=z(this.textWidth),m=[j,g,b.innerHTML,this.textWidth].join(",");if(m!==this.cTT){k=a.fontMetrics(b.style.fontSize).b;t(j)&&this.setSpanRotation(j, -h,k);i=n(this.elemWidth,b.offsetWidth);if(i>l&&/[ \-]/.test(b.textContent||b.innerText))D(b,{width:l+"px",display:"block",whiteSpace:"normal"}),i=l;this.getSpanCorrection(i,k,h,j,g)}D(b,{left:e+(this.xCorr||0)+"px",top:f+(this.yCorr||0)+"px"});if(hb)k=b.offsetHeight;this.cTT=m}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,c){var d={},e=ya?"-ms-transform":hb?"-webkit-transform":Xa?"MozTransform":Hb?"-o-transform":"";d[e]=d.transform="rotate("+a+"deg)";d[e+(Xa?"Origin":"-origin")]=b*100+"% "+ -c+"px";D(this.element,d)},getSpanCorrection:function(a,b,c){this.xCorr=-a*c;this.yCorr=-b}});r(ua.prototype,{html:function(a,b,c){var d=G.chart.style,e=this.createElement("span"),f=e.attrSetters,g=e.element,h=e.renderer;f.text=function(a){a!==g.innerHTML&&delete this.bBox;g.innerHTML=a;return!1};f.x=f.y=f.align=f.rotation=function(a,b){b==="align"&&(b="textAlign");e[b]=a;e.htmlUpdateTransform();return!1};e.attr({text:a,x:w(b),y:w(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:d.fontFamily, -fontSize:d.fontSize});e.css=e.htmlCss;if(h.isSVG)e.add=function(a){var b,c=h.box.parentNode,d=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)d.push(a),a=a.parentGroup;p(d.reverse(),function(a){var d;b=a.div=a.div||T(Ga,{className:v(a.element,"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;r(a.attrSetters,{translateX:function(a){d.left=a+"px"},translateY:function(a){d.top=a+"px"},visibility:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(g); -e.added=!0;e.alignOnAdd&&e.htmlUpdateTransform();return e};return e}});var R;if(!V&&!da){Highcharts.VMLElement=R={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",";"],e=b===Ga;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=T(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer, -c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();A(this,"add");return this},updateTransform:pa.prototype.htmlUpdateTransform,setSpanRotation:function(){var a=this.rotation,b=U(a*Ba),c=ba(a*Ba);D(this.element,{filter:a?["progid:DXImageTransform.Microsoft.Matrix(M11=",b,", M12=",-c,", M21=",c,", M22=",b,", sizingMethod='auto expand')"].join(""):Q})},getSpanCorrection:function(a, -b,c,d,e){var f=d?U(d*Ba):1,g=d?ba(d*Ba):0,h=n(this.elemHeight,this.element.offsetHeight),i;this.xCorr=f<0&&-a;this.yCorr=g<0&&-h;i=f*g<0;this.xCorr+=g*b*(i?1-c:c);this.yCorr-=f*b*(d?i?c:1-c:1);e&&e!=="left"&&(this.xCorr-=a*c*(f<0?-1:1),d&&(this.yCorr-=h*c*(g<0?-1:1)),D(this.element,{textAlign:e}))},pathToVML:function(a){for(var b=a.length,c=[];b--;)if(wa(a[b]))c[b]=w(a[b]*10)-5;else if(a[b]==="Z")c[b]="x";else if(c[b]=a[b],a.isArc&&(a[b]==="wa"||a[b]==="at"))c[b+5]===c[b+7]&&(c[b+7]+=a[b+7]>a[b+5]? -1:-1),c[b+6]===c[b+8]&&(c[b+8]+=a[b+8]>a[b+6]?1:-1);return c.join(" ")||"x"},attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,l=this.shadows,m,q=this.attrSetters,o=this;fa(a)&&t(b)&&(c=a,a={},a[c]=b);if(fa(a))c=a,o=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],m=!1,e=q[c]&&q[c].call(this,d,c),e!==!1&&d!==null){e!==u&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k|| -(this.symbolAttr(a),k=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");f.path=d=this.pathToVML(d);if(l)for(e=l.length;e--;)l[e].path=l[e].cutOff?this.cutOffPath(d,l[e].cutOff):d;m=!0}else if(c==="visibility"){if(l)for(e=l.length;e--;)l[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,gb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;m=!0}else if(c==="zIndex")d&&(g[c]=d),m=!0;else if(sa(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=s(0,d),this.updateClipping? -(this[c]=d,this.updateClipping()):g[c]=d,m=!0;else if(c==="class"&&h==="DIV")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,wa(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||T(i.prepVML(["<stroke/>"]),null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==Q?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c=== -"opacity")m=!0;else if(h==="shape"&&c==="rotation")this[c]=f.style[c]=d,f.style.left=-w(ba(d*Ba)+1)+"px",f.style.top=w(U(d*Ba))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),m=!0;m||(gb?f[c]=d:v(f,c,d))}return o},clip:function(a){var b=this,c;a?(c=a.members,ha(c,b),c.push(b),b.destroyClip=function(){ha(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:gb?"inherit":"rect(auto)"});return b.css(a)},css:pa.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&& -Na(a)},destroy:function(){this.destroyClip&&this.destroyClip();return pa.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=C.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=z(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,l,m,q,o;k&&typeof k.value!=="string"&&(k="x");m=k;if(a){q=n(a.width,3);o=(a.opacity|| -0.15)/q;for(e=1;e<=3;e++){l=q*2+1-2*e;c&&(m=this.cutOffPath(k.value,l+0.5));j=['<shape isShadow="true" strokeweight="',l,'" filled="false" path="',m,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=T(g.prepVML(j),null,{left:z(i.left)+n(a.offsetX,1),top:z(i.top)+n(a.offsetY,1)});if(c)h.cutOff=l+1;j=['<stroke color="',a.color||"black",'" opacity="',o*e,'"/>'];T(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};R=ia(pa,R); -var xb={Element:R,isIE8:ra.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(Ga);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.cache={};this.setSize(b,c,!1);if(!y.namespaces.hcv){y.namespaces.add("hcv","urn:schemas-microsoft-com:vml");try{y.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}catch(f){y.styleSheets[0].cssText+= -"hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}}},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=S(a);return r(e,{members:[],left:(f?a.x:a)+1,top:(f?a.y:b)+1,width:(f?a.width:c)-1,height:(f?a.height:d)-1,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+w(a?e:d)+"px,"+w(a?f: -b)+"px,"+w(a?b:f)+"px,"+w(a?d:e)+"px)"};!a&&gb&&c==="DIV"&&r(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){p(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=Q;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,l,m=a.linearGradient||a.radialGradient,q,o,n,H,s,t="",a=a.stops,u,w=[],r=function(){h=['<fill colors="'+w.join(",")+'" opacity="',n,'" o:opacity2="',o,'" type="',i,'" ',t,'focus="100%" method="any" />']; -T(e.prepVML(h),null,null,b)};q=a[0];u=a[a.length-1];q[0]>0&&a.unshift([0,q[1]]);u[0]<1&&a.push([1,u[1]]);p(a,function(a,b){g.test(a[1])?(f=ta(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);w.push(a[0]*100+"% "+k);b?(n=l,H=k):(o=l,s=k)});if(c==="fill")if(i==="gradient")c=m.x1||m[0]||0,a=m.y1||m[1]||0,q=m.x2||m[2]||0,m=m.y2||m[3]||0,t='angle="'+(90-P.atan((m-a)/(q-c))*180/Aa)+'"',r();else{var j=m.r,Sa=j*2,Ta=j*2,v=m.cx,B=m.cy,x=b.radialReference,$,j=function(){x&&($=d.getBBox(),v+=(x[0]-$.x)/$.width- -0.5,B+=(x[1]-$.y)/$.height-0.5,Sa*=x[2]/$.width,Ta*=x[2]/$.height);t='src="'+G.global.VMLRadialGradientURL+'" size="'+Sa+","+Ta+'" origin="0.5,0.5" position="'+v+","+B+'" color2="'+s+'" ';r()};d.added?j():F(d,"add",j);j=H}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ta(a),h=["<",c,' opacity="',f.get("a"),'"/>'],T(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join(""); -b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:ua.prototype.html,path:function(a){var b={coordsize:"10 10"};Ka(a)?b.d=a:S(a)&&r(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(S(a))c=a.r,b=a.y,a=a.x;d.isCircle= -!0;d.r=c;return d.attr({x:a,y:b})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(Ga).attr(b)},image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){var g=this.symbol("rect");g.r=S(a)?a.r:e;return g.attr(S(a)?a:g.crisp(f,a,b,s(c,0),s(d,0)))},invertChild:function(a,b){var c=b.style;D(a,{flip:"x",left:z(c.width)-1,top:z(c.height)-1,rotation:-90})}, -symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=U(f),i=ba(f),j=U(g),k=ba(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&(c=d=2*e.r);e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!t(e)||!e.r?f=ua.prototype.symbols.square.apply(0, -arguments):(h=I(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*h,f,g,f,g-h,f-h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}};Highcharts.VMLRenderer=R=function(){this.init.apply(this,arguments)};R.prototype=x(ua.prototype,xb);Ya=R}ua.prototype.measureSpanWidth=function(a,b){var c=y.createElement("span"),d;d=y.createTextNode(a);c.appendChild(d);D(c,b);this.box.appendChild(c);d=c.offsetWidth;Na(c); -return d};var Mb;if(da)Highcharts.CanVGRenderer=R=function(){Ca="http://www.w3.org/1999/xhtml"},R.prototype.symbols={},Mb=function(){function a(){var a=b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Pb(d,a);b.push(c)}}}(),Ya=R;Qa.prototype={addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.margin[3]|| -c.chartWidth*0.33),j=g===i[0],k=g===i[i.length-1],l,f=e?n(e[g],f[g],g):g,e=this.label,m=i.info;a.isDatetimeAxis&&m&&(l=b.dateTimeLabelFormats[m.higherRanks[g]||m.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,isLast:k,dateTimeLabelFormat:l,value:a.isLog?aa(ga(f)):f});g=d&&{width:s(1,w(d-2*(h.padding||10)))+"px"};g=r(g,h.style);if(t(e))e&&e.attr({text:b}).css(g);else{l={align:a.labelAlign};if(wa(h.rotation))l.rotation=h.rotation;if(d&&h.ellipsis)l._clipHeight= -a.len/i.length;this.label=t(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(l).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,b=this.axis;return a?a.getBBox()[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.label.getBBox(),b=this.axis,c=b.horiz,d=b.options.labels,a=c?a.width:a.height,b=c?a*{left:0,center:0.5,right:1}[b.labelAlign]-d.x:a;return[-b,a-b]},handleOverflow:function(a,b){var B;var c=!0,d=this.axis,e=this.isFirst,f=this.isLast,g=d.horiz?b.x: -b.y,h=d.reversed,i=d.tickPositions,j=this.getLabelSides(),k=j[0],j=j[1],l=d.pos,m=l+d.len,q=this.label.line||0,o=d.labelEdge,n=d.justifyLabels&&(e||f);o[q]===u||g+k>o[q]?o[q]=g+j:n||(c=!1);if(n)B=(d=d.ticks[i[a+(e?1:-1)]])&&d.label.xy&&d.label.xy.x+d.getLabelSides()[e?0:1],i=B,e&&!h||f&&h?g+k<l&&(g=l-k,d&&g+j>i&&(c=!1)):g+j>m&&(g=m-j,d&&g+k<i&&(c=!1)),b.x=g;return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+ -e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,l=i.staggerLines,m=i.chart.renderer.fontMetrics(e.style.fontSize).b,q=e.rotation,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);q&&i.side===2&&(b-=m-m*U(q*Ba));!t(e.y)&&!q&&(b+=m-c.getBBox().height/2);if(l)c.line=g/(h||1)%l, -b+=c.line*(i.labelOffset/l);return{x:a,y:b}},getMarkPath:function(a,b,c,d,e,f){return f.crispLine(["M",a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,l=this.gridLine,m=h?h+"Grid":"grid",q=h?h+"Tick":"tick",o=e[m+"LineWidth"],p=e[m+"LineColor"],H=e[m+"LineDashStyle"],s=e[q+"Length"],m=e[q+"Width"]||0,t=e[q+"Color"],w=e[q+"Position"],q=this.mark,r=k.step,Z=!0,x=d.tickmarkOffset,v=this.getPosition(g, -j,x,b),y=v.x,v=v.y,B=g&&y===d.pos+d.len||!g&&v===d.pos?-1:1;this.isActive=!0;if(o){j=d.getPlotLinePath(j+x,o*B,b,!0);if(l===u){l={stroke:p,"stroke-width":o};if(H)l.dashstyle=H;if(!h)l.zIndex=1;if(b)l.opacity=0;this.gridLine=l=o?f.path(j).attr(l).add(d.gridGroup):null}if(!b&&l&&j)l[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(m&&s)w==="inside"&&(s=-s),d.opposite&&(s=-s),h=this.getMarkPath(y,v,s,m*B,g,f),q?q.animate({d:h,opacity:c}):this.mark=f.path(h).attr({stroke:t,"stroke-width":m,opacity:c}).add(d.axisGroup); -if(i&&!isNaN(y))i.xy=v=this.getLabelPosition(y,v,i,g,k,x,a,r),this.isFirst&&!this.isLast&&!n(e.showFirstLabel,1)||this.isLast&&!this.isFirst&&!n(e.showLastLabel,1)?Z=!1:!d.isRadial&&!k.step&&!k.rotation&&!b&&c!==0&&(Z=this.handleOverflow(a,v)),r&&a%r&&(Z=!1),Z&&!isNaN(v.y)?(v.opacity=c,i[this.isNew?"attr":"animate"](v),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Ma(this,this.axis)}};var yb=function(a,b){this.axis=a;if(b)this.options=b,this.id=b.id};yb.prototype={render:function(){var a=this, -b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,g=a.label,h=e.width,i=e.to,j=e.from,k=t(j)&&t(i),l=e.value,m=e.dashStyle,q=a.svgElem,o=[],p,H=e.color,w=e.zIndex,r=e.events,u=b.chart.renderer;b.isLog&&(j=xa(j),i=xa(i),l=xa(l));if(h){if(o=b.getPlotLinePath(l,h),d={stroke:H,"stroke-width":h},m)d.dashstyle=m}else if(k){if(j=s(j,b.min-d),i=I(i,b.max+d),o=b.getPlotBandPath(j,i,e),d={fill:H},e.borderWidth)d.stroke=e.borderColor,d["stroke-width"]=e.borderWidth}else return;if(t(w))d.zIndex= -w;if(q)if(o)q.animate({d:o},null,q.onGetPath);else{if(q.hide(),q.onGetPath=function(){q.show()},g)a.label=g=g.destroy()}else if(o&&o.length&&(a.svgElem=q=u.path(o).attr(d).add(),r))for(p in e=function(b){q.on(b,function(c){r[b].apply(a,[c])})},r)e(p);if(f&&t(f.text)&&o&&o.length&&b.width>0&&b.height>0){f=x({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=u.text(f.text,0,0,f.useHTML).attr({align:f.textAlign||f.align,rotation:f.rotation, -zIndex:w}).css(f.style).add();b=[o[1],o[4],n(o[6],o[1])];o=[o[2],o[5],n(o[7],o[2])];c=La(b);k=La(o);g.align(f,!1,{x:c,y:k,width:za(b)-c,height:za(o)-k});g.show()}else g&&g.hide();return a},destroy:function(){ha(this.axis.plotLinesAndBands,this);delete this.axis;Ma(this)}};qa.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:J,lineColor:"#C0D0E0", -lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e",fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8,y:3},lineWidth:0, -maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return Da(this.total,-1)},style:J.style}},defaultLeftAxisOptions:{labels:{x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:8,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.coll= -(this.isXAxis=c)?"xAxis":"yAxis";this.opposite=b.opposite;this.side=b.side||(this.horiz?this.opposite?0:2:this.opposite?1:3);this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.names=[];this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=t(d.linkedTo); -this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.labelEdge=[];this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.oldStacks={};this.stackExtremes={};this.min=this.max=null;this.crosshair=n(d.crosshair,ja(a.options.tooltip.crosshairs)[c?0:1],!1);var f,d=this.options.events;sa(this,a.axes)===-1&&(a.axes.push(this), -a[this.coll].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===u)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)F(this,f,d[f]);if(this.isLog)this.val2lin=xa,this.lin2val=ga},setOptions:function(a){this.options=x(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],x(G[this.coll],a))},defaultLabelFormatter:function(){var a= -this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=G.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=Fa(h,this);else if(c)g=b;else if(d)g=ab(d,b);else if(f&&a>=1E3)for(;f--&&g===u;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=Da(b/c,-1)+e[f]);g===u&&(g=b>=1E4?Da(b,0):Da(b,-1,u,""));return g},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;a.stackExtremes={};a.buildStacks();p(a.series,function(c){if(c.visible|| -!b.options.chart.ignoreHiddenSeries){var d;d=c.options.threshold;var e;a.hasVisibleSeries=!0;a.isLog&&d<=0&&(d=null);if(a.isXAxis){if(d=c.xData,d.length)a.dataMin=I(n(a.dataMin,d[0]),La(d)),a.dataMax=s(n(a.dataMax,d[0]),za(d))}else{c.getExtremes();e=c.dataMax;c=c.dataMin;if(t(c)&&t(e))a.dataMin=I(n(a.dataMin,c),c),a.dataMax=s(n(a.dataMax,e),e);if(t(d))if(a.dataMin>=d)a.dataMin=d,a.ignoreMinPadding=!0;else if(a.dataMax<d)a.dataMax=d,a.ignoreMaxPadding=!0}}})},translate:function(a,b,c,d,e,f){var g= -this.len,h=1,i=0,j=d?this.oldTransA:this.transA,d=d?this.oldMin:this.min,k=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!j)j=this.transA;c&&(h*=-1,i=g);this.reversed&&(h*=-1,i-=h*g);b?(a=a*h+i,a-=k,a=a/j+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),f==="between"&&(f=0.5),a=h*(a-d)*j+i+h*k+(wa(f)?j*f*this.pointRange:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a- -(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,e){var f=this.chart,g=this.left,h=this.top,i,j,k=c&&f.oldChartHeight||f.chartHeight,l=c&&f.oldChartWidth||f.chartWidth,m;i=this.transB;e=n(e,this.translate(a,null,null,c));a=c=w(e+i);i=j=w(k-e-i);if(isNaN(e))m=!0;else if(this.horiz){if(i=h,j=k-this.bottom,a<g||a>g+this.width)m=!0}else if(a=g,c=l-this.right,i<h||i>h+this.height)m=!0;return m&&!d?null:f.renderer.crispLine(["M",a,i,"L",c,j],b||1)},getLinearTickPositions:function(a, -b,c){for(var d,b=aa(N(b/a)*a),c=aa(Ha(c/a)*a),e=[];b<=c;){e.push(b);b=aa(b+a);if(b===d)break;d=b}return e},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(this.getTimeTicks(this.normalizeTimeTickInterval(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&d.shift();else for(b=this.min+ -(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===u&&!this.isLog)t(a.min)||t(a.max)?this.minRange=null:(p(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===u||h<f)f=h}),this.minRange=I(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=(k-c+b)/2;d=[b-d,n(a.min,b-d)]; -if(e)d[2]=this.dataMin;b=za(d);c=[b+k,n(a.max,b+k)];if(e)c[2]=this.dataMax;c=La(c);c-b<k&&(d[0]=c-k,d[1]=n(a.min,c-k),b=za(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this.max-this.min,c=0,d,e=0,f=0,g=this.linkedParent,h=!!this.categories,i=this.transA;if(this.isXAxis||h)g?(e=g.minPointOffset,f=g.pointRangePadding):p(this.series,function(a){var g=s(a.pointRange,+h),i=a.options.pointPlacement,m=a.closestPointRange;g>b&&(g=0);c=s(c,g);e=s(e,fa(i)?0:g/2);f=s(f,i==="on"?0:g);!a.noSharedTooltip&& -t(m)&&(d=t(d)?I(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=I(c,b),this.closestPointRange=d;if(a)this.oldTransA=i;this.translationSlope=this.transA=i=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding=i*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,l=d.tickInterval, -m=d.minTickInterval,q=d.tickPixelInterval,o,ma=b.categories;h?(b.linkedParent=c[b.coll][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=n(c.min,c.dataMin),b.max=n(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&ka(11,1)):(b.min=n(b.userMin,d.min,b.dataMin),b.max=n(b.userMax,d.max,b.dataMax));if(e)!a&&I(b.min,n(b.dataMin,b.min))<=0&&ka(10,1),b.min=aa(xa(b.min)),b.max=aa(xa(b.max));if(b.range&&t(b.max))b.userMin=b.min=s(b.min,b.max-b.range),b.userMax=b.max,b.range=null;b.beforePadding&&b.beforePadding(); -b.adjustForMinRange();if(!ma&&!b.usePercentage&&!h&&t(b.min)&&t(b.max)&&(c=b.max-b.min)){if(!t(d.min)&&!t(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!t(d.max)&&!t(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.min===b.max||b.min===void 0||b.max===void 0?b.tickInterval=1:h&&!l&&q===b.linkedParent.options.tickPixelInterval?b.tickInterval=b.linkedParent.tickInterval:(b.tickInterval=n(l,ma?1:(b.max-b.min)*q/s(b.len,q)),!t(l)&&b.len<q&&!this.isRadial&&!ma&&d.startOnTick&& -d.endOnTick&&(o=!0,b.tickInterval/=4));g&&!a&&p(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(b.pointRange)b.tickInterval=s(b.pointRange,b.tickInterval);if(!l&&b.tickInterval<m)b.tickInterval=m;if(!f&&!e&&!l)b.tickInterval=nb(b.tickInterval,null,mb(b.tickInterval),d);b.minorTickInterval=d.minorTickInterval=== -"auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=a=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!a)!b.ordinalPositions&&(b.max-b.min)/b.tickInterval>s(2*b.len,200)&&ka(19,!0),a=f?b.getTimeTicks(b.normalizeTimeTickInterval(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),o&&a.splice(1,a.length-2), -b.tickPositions=a;if(!h)e=a[0],f=a[a.length-1],h=b.minPointOffset||0,d.startOnTick?b.min=e:b.min-h>e&&a.shift(),d.endOnTick?b.max=f:b.max+h<f&&a.pop(),a.length===1&&(b.min-=0.001,b.max+=0.001)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.coll,this.pos,this.len].join("-");if(!this.isLinked&&!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey, -b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1&&this.min!==u){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(aa(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(t(d)&&a!==d)this.isDirty=!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!== -this.oldAxisLength;p(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=!0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax){if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].total=null,a[b][c].cum=0;this.forceRedraw=!1;this.getSeriesExtremes();this.setTickPositions();this.oldUserMin=this.userMin;this.oldUserMax=this.userMax;if(!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax}else if(!this.isXAxis){if(this.oldStacks)a= -this.stacks=this.oldStacks;for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total}this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=n(c,!0),e=r(e,{min:a,max:b});A(f,"setExtremes",e,function(){f.userMin=a;f.userMax=b;f.eventArgs=e;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){this.allowZoomOutside||(t(this.dataMin)&&a<=this.dataMin&&(a=u),t(this.dataMax)&&b>=this.dataMax&&(b=u));this.displayBtn=a!==u||b!==u;this.setExtremes(a,b,!1,u,{trigger:"zoom"});return!0},setAxisSize:function(){var a= -this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=n(b.left,a.plotLeft+c);this.top=f=n(b.top,a.plotTop);this.width=c=n(b.width,a.plotWidth-c+d);this.height=b=n(b.height,a.plotHeight);this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=s(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?aa(ga(this.min)):this.min,max:a?aa(ga(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}}, -getThreshold:function(a){var b=this.isLog,c=b?ga(this.min):this.min,b=b?ga(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},autoLabelAlign:function(a){a=(n(a,0)-this.side*90+720)%360;return a>15&&a<165?"right":a>195&&a<345?"left":"center"},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,l,m=0,q=d.title,o=d.labels,ma=0,H=b.axisOffset,w=b.clipOffset,r=[-1,1,1,-1][h],v, -x=1,Z=n(o.maxStaggerLines,5),y,z,K,B;a.hasData=j=a.hasVisibleSeries||t(a.min)&&t(a.max)&&!!e;a.showAxis=b=j||n(d.showEmpty,!0);a.staggerLines=a.horiz&&o.staggerLines;if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:o.zIndex||7}).add();if(j||a.isLinked){a.labelAlign=n(o.align||a.autoLabelAlign(o.rotation));p(e,function(b){f[b]?f[b].addLabel():f[b]=new Qa(a,b)});if(a.horiz&& -!a.staggerLines&&Z&&!o.rotation){for(v=a.reversed?[].concat(e).reverse():e;x<Z;){j=[];y=!1;for(o=0;o<v.length;o++)z=v[o],K=(K=f[z].label&&f[z].label.getBBox())?K.width:0,B=o%x,K&&(z=a.translate(z),j[B]!==u&&z<j[B]&&(y=!0),j[B]=z+K);if(y)x++;else break}if(x>1)a.staggerLines=x}p(e,function(b){if(h===0||h===2||{1:"left",3:"right"}[h]===a.labelAlign)ma=s(f[b].getLabelSize(),ma)});if(a.staggerLines)ma*=a.staggerLines,a.labelOffset=ma}else for(v in f)f[v].destroy(),delete f[v];if(q&&q.text&&q.enabled!== -!1){if(!a.axisTitle)a.axisTitle=c.text(q.text,0,0,q.useHTML).attr({zIndex:7,rotation:q.rotation||0,align:q.textAlign||{low:"left",middle:"center",high:"right"}[q.align]}).css(q.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(b)k=a.axisTitle.getBBox()[g?"height":"width"],m=n(q.margin,g?5:10),l=q.offset;a.axisTitle[b?"show":"hide"]()}a.offset=r*n(d.offset,H[h]);a.axisTitleMargin=n(l,ma+m+(h!==2&&ma&&r*d.labels[g?"y":"x"]));H[h]=s(H[h],a.axisTitleMargin+k+r*a.offset);w[i]=s(w[i],N(d.lineWidth/2)*2)}, -getLinePath:function(a){var b=this.chart,c=this.opposite,d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d,d=b.chartHeight-this.bottom-(c?this.height:0)+d;c&&(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=z(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+d/2,high:f+ -(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.horiz,c=a.reversed,d=a.chart,e=d.renderer,f=a.options,g=a.isLog,h=a.isLinked,i=a.tickPositions,j,k=a.axisTitle,l=a.stacks,m=a.ticks,q=a.minorTicks,o=a.alternateBands,n=f.stackLabels,H=f.alternateGridColor,s=a.tickmarkOffset,r=f.lineWidth,w=d.hasRendered&&t(a.oldMin)&&!isNaN(a.oldMin),v= -a.hasData,x=a.showAxis,y,z=a.justifyLabels=!a.staggerLines&&b&&f.labels.overflow==="justify",K;a.labelEdge.length=0;p([m,q,o],function(a){for(var b in a)a[b].isActive=!1});if(v||h)if(a.minorTickInterval&&!a.categories&&p(a.getMinorTickPositions(),function(b){q[b]||(q[b]=new Qa(a,b,"minor"));w&&q[b].isNew&&q[b].render(null,!0);q[b].render(null,!1,1)}),i.length&&(j=i.slice(),(b&&c||!b&&!c)&&j.reverse(),z&&(j=j.slice(1).concat([j[0]])),p(j,function(b,c){z&&(c=c===j.length-1?0:c+1);if(!h||b>=a.min&&b<= -a.max)m[b]||(m[b]=new Qa(a,b)),w&&m[b].isNew&&m[b].render(c,!0,0.1),m[b].render(c,!1,1)}),s&&a.min===0&&(m[-1]||(m[-1]=new Qa(a,-1,null,!0)),m[-1].render(-1))),H&&p(i,function(b,c){if(c%2===0&&b<a.max)o[b]||(o[b]=new yb(a)),y=b+s,K=i[c+1]!==u?i[c+1]+s:a.max,o[b].options={from:g?ga(y):y,to:g?ga(K):K,color:H},o[b].render(),o[b].isActive=!0}),!a._addedPlotLB)p((f.plotLines||[]).concat(f.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;p([m,q,o],function(a){var b,c,e=[],f=oa?oa.duration|| -500:0,g=function(){for(c=e.length;c--;)a[e[c]]&&!a[e[c]].isActive&&(a[e[c]].destroy(),delete a[e[c]])};for(b in a)if(!a[b].isActive)a[b].render(b,!1,0),a[b].isActive=!1,e.push(b);a===o||!d.hasRendered||!f?g():f&&setTimeout(g,f)});if(r)b=a.getLinePath(r),a.axisLine?a.axisLine.animate({d:b}):a.axisLine=e.path(b).attr({stroke:f.lineColor,"stroke-width":r,zIndex:7}).add(a.axisGroup),a.axisLine[x?"show":"hide"]();if(k&&x)k[k.isNew?"attr":"animate"](a.getTitlePosition()),k.isNew=!1;if(n&&n.enabled){var B, -A,f=a.stackTotalGroup;if(!f)a.stackTotalGroup=f=e.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();f.translate(d.plotLeft,d.plotTop);for(B in l)for(A in e=l[B],e)e[A].render(f)}a.isDirty=!1},redraw:function(){var a=this.chart.pointer;a.reset&&a.reset(!0);this.render();p(this.plotLinesAndBands,function(a){a.render()});p(this.series,function(a){a.isDirty=!0})},buildStacks:function(){var a=this.series,b=a.length;if(!this.isXAxis){for(;b--;)a[b].setStackedPoints();if(this.usePercentage)for(b= -0;b<a.length;b++)a[b].setPercentStacks()}},destroy:function(a){var b=this,c=b.stacks,d,e=b.plotLinesAndBands;a||X(b);for(d in c)Ma(c[d]),c[d]=null;p([b.ticks,b.minorTicks,b.alternateBands],function(a){Ma(a)});for(a=e.length;a--;)e[a].destroy();p("stackTotalGroup,axisLine,axisTitle,axisGroup,cross,gridGroup,labelGroup".split(","),function(a){b[a]&&(b[a]=b[a].destroy())});this.cross&&this.cross.destroy()},drawCrosshair:function(a,b){if(this.crosshair)if((t(b)||!n(this.crosshair.snap,!0))===!1)this.hideCrosshair(); -else{var c,d=this.crosshair,e=d.animation;n(d.snap,!0)?t(b)&&(c=this.chart.inverted!=this.horiz?b.plotX:this.len-b.plotY):c=this.horiz?a.chartX-this.pos:this.len-a.chartY+this.pos;c=this.isRadial?this.getPlotLinePath(this.isXAxis?b.x:n(b.stackY,b.y)):this.getPlotLinePath(null,null,null,null,c);if(c===null)this.hideCrosshair();else if(this.cross)this.cross.attr({visibility:"visible"})[e?"animate":"attr"]({d:c},e);else{e={"stroke-width":d.width||1,stroke:d.color||"#C0C0C0",zIndex:d.zIndex||2};if(d.dashStyle)e.dashstyle= -d.dashStyle;this.cross=this.chart.renderer.path(c).attr(e).add()}}},hideCrosshair:function(){this.cross&&this.cross.hide()}};r(qa.prototype,{getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},addPlotLine:function(a){this.addPlotBandOrLine(a,"plotLines")},addPlotBandOrLine:function(a,b){var c=(new yb(this,a)).render(),d=this.userOptions;c&&(b&&(d[b]=d[b]|| -[],d[b].push(a)),this.plotLinesAndBands.push(c));return c},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=this.options,d=this.userOptions,e=b.length;e--;)b[e].id===a&&b[e].destroy();p([c.plotLines||[],d.plotLines||[],c.plotBands||[],d.plotBands||[]],function(b){for(e=b.length;e--;)b[e].id===a&&ha(b,b[e])})}});qa.prototype.getLogTickPositions=function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=w(a),g=this.getLinearTickPositions(a, -b,c);else if(a>=0.08)for(var f=N(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!l;f++){i=e.length;for(h=0;h<i&&!l;h++)j=xa(ga(f)*e[h]),j>b&&(!d||k<=c)&&g.push(k),k>c&&(l=!0),k=j}else if(b=ga(b),c=ga(c),a=e[d?"minorTickInterval":"tickInterval"],a=n(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=nb(a,null,mb(a)),g=Ra(this.getLinearTickPositions(a,b,c),xa),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval= -a;return g};qa.prototype.getTimeTicks=function(a,b,c,d){var e=[],f={},g=G.global.useUTC,h,i=new Date(b-Pa),j=a.unitRange,k=a.count;if(t(b)){j>=E.second&&(i.setMilliseconds(0),i.setSeconds(j>=E.minute?0:k*N(i.getSeconds()/k)));if(j>=E.minute)i[Cb](j>=E.hour?0:k*N(i[pb]()/k));if(j>=E.hour)i[Db](j>=E.day?0:k*N(i[qb]()/k));if(j>=E.day)i[sb](j>=E.month?1:k*N(i[Wa]()/k));j>=E.month&&(i[Eb](j>=E.year?0:k*N(i[db]()/k)),h=i[eb]());j>=E.year&&(h-=h%k,i[Fb](h));if(j===E.week)i[sb](i[Wa]()-i[rb]()+n(d,1));b= -1;Pa&&(i=new Date(i.getTime()+Pa));h=i[eb]();for(var d=i.getTime(),l=i[db](),m=i[Wa](),q=g?Pa:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===E.year?d=cb(h+b*k,0):j===E.month?d=cb(h,l+b*k):!g&&(j===E.day||j===E.week)?d=cb(h,l,m+b*k*(j===E.day?1:7)):d+=j*k,b++;e.push(d);p(wb(e,function(a){return j<=E.hour&&a%E.day===q}),function(a){f[a]="day"})}e.info=r(a,{higherRanks:f,totalRange:j*k});return e};qa.prototype.normalizeTimeTickInterval=function(a,b){var c=b||[["millisecond",[1,2,5,10,20, -25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]],d=c[c.length-1],e=E[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=E[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+E[c[g+1][0]])/2)break;e===E.year&&a<5*e&&(f=[1,2,5]);c=nb(a/e,f,d[0]==="year"?s(mb(a/e),1):1);return{unitRange:e,count:c,unitName:d[0]}};Gb.prototype={destroy:function(){Ma(this,this.axis)},render:function(a){var b=this.options, -c=b.format,c=c?Fa(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=M(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e? -c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?V?"inherit":"visible":"hidden"})}};tb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=z(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).add().attr({y:-999}); -da||this.label.shadow(b.shadow);this.shared=b.shared},destroy:function(){if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden;r(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:g?(2*f.anchorX+c)/3:c,anchorY:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(M(a-f.x)>1||M(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b, -c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},n(this.options.hideDelay,500)),b&&p(b,function(a){a.setState()}),this.chart.hoverPoints=null},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=d.plotTop,g=0,h=0,i,a=ja(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===u&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(p(a,function(a){i= -a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Ra(c,w)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=n(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,l;d<7&&(d=e+s(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,l=!0);k<f+5&&(k=f+5,l&&c>=k&&c<=k+b&&(k=c+ -f+i));k+b>f+h&&(k=s(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ja(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];p(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat||"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h={},i,j=[];i=e.formatter||this.defaultFormatter;var h=c.hoverPoints,k,l=this.shared; -clearTimeout(this.hideTimer);this.followPointer=ja(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];l&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,h&&p(h,function(a){a.setState()}),p(a,function(a){a.setState("hover");j.push(a.getLabelConfig())}),h={x:a[0].category,y:a[0].y},h.points=j,a=a[0]):h=a.getLabelConfig();i=i.call(h,this);h=a.series;i===!1?this.hide():(this.isHidden&&(Za(d),d.attr("opacity",1).show()),d.attr({text:i}),k=e.borderColor||a.color||h.color|| -"#606060",d.attr({stroke:k}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);A(c,"tooltipRefresh",{text:i,x:f+c.plotLeft,y:g+c.plotTop,borderColor:k})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(w(c.x),w(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};var $a=Highcharts.Pointer=function(a,b){this.init(a,b)};$a.prototype={init:function(a,b){var c=b.chart,d=c.events,e=da?"":c.zoomType,c=a.inverted, -f;this.options=b;this.chart=a;this.zoomX=f=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=f&&!c||e&&c;this.zoomVert=e&&!c||f&&c;this.runChartClick=d&&!!d.click;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new tb(a,b.tooltip);this.setDOMEvents()},normalize:function(a,b){var c,d,a=a||C.event;if(!a.target)a.target=a.srcElement;a=Rb(a);d=a.touches?a.touches.item(0):a;if(!b)this.chartPosition=b=Qb(this.chart.container);d.pageX===u?(c=s(a.x,a.clientX-b.left),d=a.y):(c=d.pageX- -b.left,d=d.pageY-b.top);return r(a,{chartX:w(c),chartY:w(d)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};p(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this,c=b.chart,d=c.series,e=c.tooltip,f,g,h=c.hoverPoint,i=c.hoverSeries,j,k,l=c.chartWidth,m=b.getIndex(a);if(e&& -b.options.tooltip.shared&&(!i||!i.noSharedTooltip)){g=[];j=d.length;for(k=0;k<j;k++)if(d[k].visible&&d[k].options.enableMouseTracking!==!1&&!d[k].noSharedTooltip&&d[k].tooltipPoints.length&&(f=d[k].tooltipPoints[m])&&f.series)f._dist=M(m-f.clientX),l=I(l,f._dist),g.push(f);for(j=g.length;j--;)g[j]._dist>l&&g.splice(j,1);if(g.length&&g[0].clientX!==b.hoverX)e.refresh(g,a),b.hoverX=g[0].clientX}if(i&&i.tracker){if((f=i.tooltipPoints[m])&&f!==h)f.onMouseOver(a)}else e&&e.followPointer&&!e.isHidden&& -(d=e.getAnchor([{}],a),e.updatePosition({plotX:d[0],plotY:d[1]}));if(e&&!b._onDocumentMouseMove)b._onDocumentMouseMove=function(a){b.onDocumentMouseMove(a)},F(y,"mousemove",b._onDocumentMouseMove);p(c.axes,function(b){b.drawCrosshair(a,n(f,h))})},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,f=e&&e.shared?b.hoverPoints:d;(a=a&&e&&f)&&ja(f)[0].plotX===u&&(a=!1);if(a)e.refresh(f),d&&d.setState(d.state,!0);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&e.hide();if(this._onDocumentMouseMove)X(y, -"mousemove",this._onDocumentMouseMove),this._onDocumentMouseMove=null;p(b.axes,function(a){a.hideCrosshair()});this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart,d;p(c.series,function(e){d=a||e.getPlotBox();e.xAxis&&e.xAxis.zoomEnabled&&(e.group.attr(d),e.markerGroup&&(e.markerGroup.attr(d),e.markerGroup.clip(b?c.clipRect:null)),e.dataLabelsGroup&&e.dataLabelsGroup.attr(d))});c.clipRect.attr(b||c.clipBox)},pinchTranslate:function(a,b,c,d,e,f,g,h){a&&this.pinchTranslateDirection(!0,c,d, -e,f,g,h);b&&this.pinchTranslateDirection(!1,c,d,e,f,g,h)},pinchTranslateDirection:function(a,b,c,d,e,f,g,h){var i=this.chart,j=a?"x":"y",k=a?"X":"Y",l="chart"+k,m=a?"width":"height",q=i["plot"+(a?"Left":"Top")],o,n,p=h||1,s=i.inverted,t=i.bounds[a?"h":"v"],r=b.length===1,w=b[0][l],u=c[0][l],v=!r&&b[1][l],x=!r&&c[1][l],y,c=function(){!r&&M(w-v)>20&&(p=h||M(u-x)/M(w-v));n=(q-u)/p+w;o=i["plot"+(a?"Width":"Height")]/p};c();b=n;b<t.min?(b=t.min,y=!0):b+o>t.max&&(b=t.max-o,y=!0);y?(u-=0.8*(u-g[j][0]),r|| -(x-=0.8*(x-g[j][1])),c()):g[j]=[u,x];s||(f[j]=n-q,f[m]=o);f=s?1/p:p;e[m]=o;e[j]=b;d[s?a?"scaleY":"scaleX":"scale"+k]=p;d["translate"+k]=f*q+(u-f*w)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,l=b.selectionMarker,m={},q=g===1&&(b.inClass(a.target,"highcharts-tracker")&&c.runTrackerClick||c.runChartClick),o={};(k||e)&&!q&&a.preventDefault();Ra(f, -function(a){return b.normalize(a)});if(a.type==="touchstart")p(f,function(a,b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],p(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=I(e,f),e=s(e,f);b.min=I(a.pos,g-d);b.max=s(a.pos+a.len,e+d)}});else if(d.length){if(!l)b.selectionMarker=l=r({destroy:la},c.plotBox);b.pinchTranslate(i,j,d,f,m,l,o,h);b.hasPinched= -k;b.scaleGroups(m,o);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,f=this.zoomHor,g=this.zoomVert,h=b.plotLeft,i=b.plotTop,j=b.plotWidth,k=b.plotHeight,l,m=this.mouseDownX,q=this.mouseDownY;d<h?d=h:d>h+j&&(d=h+j);e<i?e=i:e>i+k&&(e=i+k);this.hasDragged=Math.sqrt(Math.pow(m- -d,2)+Math.pow(q-e,2));if(this.hasDragged>10){l=b.isInsidePlot(m-h,q-i);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(h,i,f?1:j,g?1:k,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&f&&(d-=m,this.selectionMarker.attr({width:M(d),x:(d>0?0:d)+m}));this.selectionMarker&&g&&(d=e-q,this.selectionMarker.attr({height:M(d),y:(d>0?0:d)+q}));l&&!this.selectionMarker&&c.panning&&b.pan(a,c.panning)}}, -drop:function(a){var b=this.chart,c=this.hasPinched;if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)p(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.coll].push({axis:a,min:I(c,b),max:s(c,b)}),h=!0)}}),h&&A(b,"selection",d,function(a){b.zoom(r(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy(); -c&&this.scaleGroups()}if(b)D(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=this.normalize(a,c);c&&d&&!this.inClass(a.target,"highcharts-tracker")&&!b.isInsidePlot(a.chartX-b.plotLeft, -a.chartY-b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);b.mouseIsDown==="mousedown"&&this.drag(a);(this.inClass(a.target,"highcharts-tracker")||b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop))&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=v(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a= -a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries,c=(a=a.relatedTarget||a.toElement)&&a.point&&a.point.series;if(b&&!b.options.stickyTracking&&!this.inClass(a,"highcharts-tooltip")&&c!==b)b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,r(c,{pageX:g.left+d+(f? -b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),A(c.series,"click",r(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(r(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&A(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)?(this.runPointActions(a),this.pinch(a)):this.reset()):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length=== -1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a=this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[y,"mouseup","onDocumentMouseUp"]];ib&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[y,"touchend","onDocumentTouchEnd"]);p(c,function(b){a["_"+ -b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]:F(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;p(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:X(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};J=Highcharts.TrackerMixin={drawTrackerPoint:function(){var a=this,b=a.chart,c=b.pointer,d=a.options.cursor,e=d&&{cursor:d},f=function(c){var d=c.target,e;if(b.hoverSeries!==a)a.onMouseOver();for(;d&&!e;)e=d.point,d=d.parentNode; -if(e!==u&&e!==b.hoverPoint)e.onMouseOver(c)};p(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});if(!a._hasTracking)p(a.trackerGroups,function(b){if(a[b]&&(a[b].addClass("highcharts-tracker").on("mouseover",f).on("mouseout",function(a){c.onTrackerMouseOut(a)}).css(e),ib))a[b].on("touchstart",f)}),a._hasTracking=!0},drawTrackerGraph:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer, -h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,l=k&&{cursor:k},k=a.singlePoints,m,q=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-i,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+i,d[m-1]);for(m=0;m<k.length;m++)e=k[m],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);j?j.attr({d:d}):(a.tracker=h.path(d).attr({"stroke-linejoin":"round",visibility:a.visible?"visible":"hidden",stroke:Kb,fill:c?Kb:Q,"stroke-width":b.lineWidth+ -(c?0:2*i),zIndex:2}).add(a.group),p([a.tracker,a.markerGroup],function(a){a.addClass("highcharts-tracker").on("mouseover",q).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(l);if(ib)a.on("touchstart",q)}))}};if(C.PointerEvent||C.MSPointerEvent){var na={};$a.prototype.getWebkitTouches=function(){var a,b=[];b.item=function(a){return this[a]};for(a in na)na.hasOwnProperty(a)&&b.push({pageX:na[a].pageX,pageY:na[a].pageY,target:na[a].target});return b};Va($a.prototype,"init",function(a,b,c){b.container.style["-ms-touch-action"]= -b.container.style["touch-action"]="none";a.call(this,b,c)});Va($a.prototype,"setDOMEvents",function(a){var b=this;a.apply(this,Array.prototype.slice.call(arguments,1));p([[this.chart.container,"PointerDown","touchstart","onContainerTouchStart",function(a){na[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}}],[this.chart.container,"PointerMove","touchmove","onContainerTouchMove",function(a){na[a.pointerId]={pageX:a.pageX,pageY:a.pageY};if(!na[a.pointerId].target)na[a.pointerId].target= -a.currentTarget}],[document,"PointerUp","touchend","onDocumentTouchEnd",function(a){delete na[a.pointerId]}]],function(a){F(a[0],window.PointerEvent?a[1].toLowerCase():"MS"+a[1],function(d){d=d.originalEvent;if(d.pointerType==="touch"||d.pointerType===d.MSPOINTER_TYPE_TOUCH)a[4](d),b[a[3]]({type:a[2],target:d.currentTarget,preventDefault:la,touches:b.getWebkitTouches()})})})})}var zb=Highcharts.Legend=function(a,b){this.init(a,b)};zb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=n(b.padding, -8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=z(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=x(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight=0,c.lastLineHeight=0,c.symbolWidth=n(b.symbolWidth,16),c.pages=[],c.render(),F(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c= -b?c.itemStyle.color:g,h=b?a.legendColor||a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g&&f.isMarker)for(j in g=a.convertAttribs(g),g)d=g[j],d!==u&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;p(["legendItem", -"legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&Na(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY,p(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,D(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":Q}))})},renderTitle:function(){var a= -this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);a=this.title.getBBox();c=a.height;this.offsetWidth=a.width;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var B;var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=b.symbolWidth,h=e.symbolPadding,i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding, -l=f?n(e.itemDistance,8):0,m=!e.rtl,q=e.width,o=e.itemMarginBottom||0,p=b.itemMarginTop,t=b.initialItemX,r=a.legendItem,u=a.series&&a.series.drawLegendSymbol?a.series:a,v=u.options,v=v&&v.showCheckbox,y=e.useHTML;if(!r&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),u.drawLegendSymbol(b,a),a.legendItem=r=d.text(e.labelFormat?Fa(e.labelFormat,a):e.labelFormatter.call(a),m?g+h:-h,b.baseline,y).css(x(a.visible?i:j)).attr({align:m?"left":"right",zIndex:2}).add(a.legendGroup),(y? -r:a.legendGroup).on("mouseover",function(){a.setState("hover");r.css(b.options.itemHoverStyle)}).on("mouseout",function(){r.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):A(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),v))a.checkbox=T("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},e.itemCheckboxStyle,c.container),F(a.checkbox,"click",function(b){A(a, -"checkboxClick",{checked:b.target.checked},function(){a.select()})});d=r.getBBox();B=a.legendItemWidth=e.itemWidth||a.legendItemWidth||g+h+d.width+l+(v?20:0),e=B;b.itemHeight=g=w(a.legendItemHeight||d.height);if(f&&b.itemX-t+e>(q||c.chartWidth-2*k-t))b.itemX=t,b.itemY+=p+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=s(b.maxItemWidth,e);b.lastItemY=p+b.itemY+o;b.lastLineHeight=s(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=p+g+o,b.lastLineHeight=g);b.offsetWidth= -q||s((f?b.itemX-t-l:e)+k,b.offsetWidth)},getAllItems:function(){var a=[];p(this.chart.series,function(b){var c=b.options;if(n(c.showInLegend,!t(c.linkedTo)?u:!1,!0))a=a.concat(b.legendItems||(c.legendType==="point"?b.data:b))});return a},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,l=j.borderWidth,m=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(), -a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=a.getAllItems();ob(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;p(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(l||m){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)), -i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,"stroke-width":l||0,fill:m||Q}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;p(e,function(b){a.positionItem(b)});f&&d.align(r({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight, -h,i=this.clipRect,j=e.navigation,k=n(j.animation,!0),l=j.arrowSize||12,m=this.nav,q=this.pages,o,s=this.allItems;e.layout==="horizontal"&&(f/=2);g&&(f=I(f,g));q.length=0;if(a>f&&!e.useHTML){this.clipHeight=h=f-20-this.titleHeight-this.padding;this.currentPage=n(this.currentPage,1);this.fullHeight=a;p(s,function(a,b){var c=a._legendItemPos[1],d=w(a.legendItem.bBox.height),e=q.length;if(!e||c-q[e-1]>h)q.push(o||c);b===s.length-1&&c+d-q[e-1]>h&&q.push(c);c!==o&&(o=c)});if(!i)i=b.clipRect=d.clipRect(0, -this.padding,9999,0),b.contentGroup.clip(i);i.attr({height:h});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,l,l).on("click",function(){b.scroll(-1,k)}).add(m),this.pager=d.text("",15,10).css(j.style).add(m),this.down=d.symbol("triangle-down",0,0,l,l).on("click",function(){b.scroll(1,k)}).add(m);b.scroll(0);a=f}else if(m)i.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pages, -d=c.length,e=this.currentPage+a,f=this.clipHeight,g=this.options.navigation,h=g.activeColor,g=g.inactiveColor,i=this.pager,j=this.padding;e>d&&(e=d);if(e>0)b!==u&&Oa(b,this.chart),this.nav.attr({translateX:j,translateY:f+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:e===1?g:h}).css({cursor:e===1?"default":"pointer"}),i.attr({text:e+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,fill:e===d?g:h}).css({cursor:e===d?"default":"pointer"}),c=-c[e-1]+this.initialItemY, -this.scrollGroup.animate({translateY:c}),this.currentPage=e,this.positionCheckboxes(c)}};R=Highcharts.LegendSymbolMixin={drawRectangle:function(a,b){var c=a.options.symbolHeight||12;b.legendSymbol=this.chart.renderer.rect(0,a.baseline-5-c/2,a.symbolWidth,c,n(a.options.symbolRadius,2)).attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b=this.options,c=b.marker,d;d=a.symbolWidth;var e=this.chart.renderer,f=this.legendGroup,a=a.baseline-w(e.fontMetrics(a.options.itemStyle.fontSize).b* -0.3),g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a,"L",d,a]).attr(g).add(f)}if(c&&c.enabled)b=c.radius,this.legendSymbol=d=e.symbol(this.symbol,d/2-b,a-b,2*b,2*b).add(f),d.isMarker=!0}};/Trident\/7\.0/.test(ra)&&Va(zb.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c,b)};c.chart.renderer.forExport?d():setTimeout(d)});fb.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=x(G, -a);c.series=a.series=d;this.userOptions=a;d=c.chart;this.margin=this.splashArray("margin",d);this.spacing=this.splashArray("spacing",d);var e=d.events;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ia.length;Ia.push(f);d.reflow!==!1&&F(f,"load",function(){f.initReflow()});if(e)for(g in e)F(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=da?!1:n(d.animation,!0);f.pointCount=0;f.counters=new Ab; -f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=L[a.type||b.type||b.defaultSeriesType])||ka(17,!0);b=new b;b.init(this,a);return b},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&p(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h,i=this.isDirtyBox, -j=c.length,k=j,l=this.renderer,m=l.isHidden(),q=[];Oa(a,this);m&&this.cloneRenderTo();for(this.layOutTitles();k--;)if(a=c[k],a.options.stacking&&(g=!0,a.isDirty)){h=!0;break}if(h)for(k=j;k--;)if(a=c[k],a.options.stacking)a.isDirty=!0;p(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;g&&this.getStacks();if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,p(b,function(a){a.setScale()});this.adjustTickAmounts(); -this.getMargins();p(b,function(a){a.isDirty&&(i=!0)});p(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,q.push(function(){A(a,"afterSetExtremes",r(a.eventArgs,a.getExtremes()));delete a.eventArgs});(i||g)&&a.redraw()})}i&&this.drawChartBox();p(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);l.draw();A(this,"redraw");m&&this.cloneRenderTo(!0);p(q,function(a){a.call()})},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id=== -a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||[];for(b=0;b<e.length;b++)if(e[b].id===a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=ja(b.xAxis||{}),b=b.yAxis=ja(b.yAxis||{});p(c,function(a,b){a.index=b;a.isX=!0});p(b,function(a,b){a.index=b});c=c.concat(b);p(c,function(b){new qa(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];p(this.series,function(b){a=a.concat(wb(b.points||[], -function(a){return a.selected}))});return a},getSelectedSeries:function(){return wb(this.series,function(a){return a.selected})},getStacks:function(){var a=this;p(a.yAxis,function(a){if(a.stacks&&a.hasVisibleSeries)a.oldStacks=a.stacks});p(a.series,function(b){if(b.options.stacking&&(b.visible===!0||a.options.chart.ignoreHiddenSeries===!1))b.stackKey=b.type+n(b.options.stack,"")})},showResetZoom:function(){var a=this,b=G.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo=== -"chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=this;A(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?p(this.axes,function(a){b=a.zoom()}):p(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b= -e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&S(e))this.resetZoomButton=e.destroy();b&&this.redraw(n(this.options.chart.animation,a&&a.animation,this.pointCount<100))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&p(d,function(a){a.setState()});p(b==="xy"?[1,0]:[1],function(b){var d=a[b?"chartX":"chartY"],h=c[b?"xAxis":"yAxis"][0],i=c[b?"mouseDownX":"mouseDownY"],j=(h.pointRange||0)/2,k=h.getExtremes(),l=h.toValue(i-d,!0)+j,i=h.toValue(i+ -c[b?"plotWidth":"plotHeight"]-d,!0)-j;h.series.length&&l>I(k.dataMin,k.min)&&i<s(k.dataMax,k.max)&&(h.setExtremes(l,i,!1,!1,{trigger:"pan"}),e=!0);c[b?"mouseDownX":"mouseDownY"]=d});e&&c.redraw(!1);D(c.container,{cursor:"move"})},setTitle:function(a,b){var f;var c=this,d=c.options,e;e=d.title=x(d.title,a);f=d.subtitle=x(d.subtitle,b),d=f;p([["title",a,e],["subtitle",b,d]],function(a){var b=a[0],d=c[b],e=a[1],a=a[2];d&&e&&(c[b]=d=d.destroy());a&&a.text&&!d&&(c[b]=c.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align, -"class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add())});c.layOutTitles()},layOutTitles:function(){var a=0,b=this.title,c=this.subtitle,d=this.options,e=d.title,d=d.subtitle,f=this.spacingBox.width-44;if(b&&(b.css({width:(e.width||f)+"px"}).align(r({y:15},e),!1,"spacingBox"),!e.floating&&!e.verticalAlign))a=b.getBBox().height,a>=18&&a<=25&&(a=15);c&&(c.css({width:(d.width||f)+"px"}).align(r({y:a+e.margin},d),!1,"spacingBox"),!d.floating&&!d.verticalAlign&&(a=Ha(a+c.getBBox().height)));this.titleOffset= -a},getChartSize:function(){var a=this.options.chart,b=this.renderToClone||this.renderTo;this.containerWidth=jb(b,"width");this.containerHeight=jb(b,"height");this.chartWidth=s(0,a.width||this.containerWidth||600);this.chartHeight=s(0,n(a.height,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Na(b),delete this.renderToClone):(c&&c.parentNode===this.renderTo&&this.renderTo.removeChild(c),this.renderToClone= -b=this.renderTo.cloneNode(0),D(b,{position:"absolute",top:"-9999px",display:"block"}),y.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+ub++;if(fa(a))this.renderTo=a=y.getElementById(a);a||ka(13,!0);c=z(v(a,"data-highcharts-chart"));!isNaN(c)&&Ia[c]&&Ia[c].destroy();v(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight; -this.container=a=T(Ga,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},r({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new ua(a,c,d,!0):new Ya(a,c,d);da&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.spacing,b,c=this.legend,d=this.margin,e=this.options.legend, -f=n(e.margin,10),g=e.x,h=e.y,i=e.align,j=e.verticalAlign,k=this.titleOffset;this.resetMargins();b=this.axisOffset;if(k&&!t(d[0]))this.plotTop=s(this.plotTop,k+this.options.title.margin+a[0]);if(c.display&&!e.floating)if(i==="right"){if(!t(d[1]))this.marginRight=s(this.marginRight,c.legendWidth-g+f+a[1])}else if(i==="left"){if(!t(d[3]))this.plotLeft=s(this.plotLeft,c.legendWidth+g+f+a[3])}else if(j==="top"){if(!t(d[0]))this.plotTop=s(this.plotTop,c.legendHeight+h+f+a[0])}else if(j==="bottom"&&!t(d[2]))this.marginBottom= -s(this.marginBottom,c.legendHeight-h+f+a[2]);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&p(this.axes,function(a){a.getOffset()});t(d[3])||(this.plotLeft+=b[3]);t(d[0])||(this.plotTop+=b[0]);t(d[2])||(this.marginBottom+=b[2]);t(d[1])||(this.marginRight+=b[1]);this.setChartSize()},reflow:function(a){var b=this,c=b.options.chart,d=b.renderTo,e=c.width||jb(d,"width"),f=c.height||jb(d,"height"),c= -a?a.target:C,d=function(){if(b.container)b.setSize(e,f,!1),b.hasUserSize=null};if(!b.hasUserSize&&e&&f&&(c===C||c===y)){if(e!==b.containerWidth||f!==b.containerHeight)clearTimeout(b.reflowTimeout),a?b.reflowTimeout=setTimeout(d,100):d();b.containerWidth=e;b.containerHeight=f}},initReflow:function(){var a=this,b=function(b){a.reflow(b)};F(C,"resize",b);F(a,"destroy",function(){X(C,"resize",b)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&A(d,"endResize",null,function(){d.isResizing-= -1})};Oa(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(t(a))d.chartWidth=e=s(0,w(a)),d.hasUserSize=!!e;if(t(b))d.chartHeight=f=s(0,w(b));(oa?kb:D)(d.container,{width:e+"px",height:f+"px"},oa);d.setChartSize(!0);d.renderer.setSize(e,f,c);d.maxTicks=null;p(d.axes,function(a){a.isDirty=!0;a.setScale()});p(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;A(d,"resize");oa===!1?g():setTimeout(g,oa&&oa.duration|| -500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=this.spacing,h=this.clipOffset,i,j,k,l;this.plotLeft=i=w(this.plotLeft);this.plotTop=j=w(this.plotTop);this.plotWidth=k=s(0,w(d-i-this.marginRight));this.plotHeight=l=s(0,w(e-j-this.marginBottom));this.plotSizeX=b?l:k;this.plotSizeY=b?k:l;this.plotBorderWidth=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:e-g[0]-g[2]};this.plotBox= -c.plotBox={x:i,y:j,width:k,height:l};d=2*N(this.plotBorderWidth/2);b=Ha(s(d,h[3])/2);c=Ha(s(d,h[0])/2);this.clipBox={x:b,y:c,width:N(this.plotSizeX-s(d,h[1])/2-b),height:N(this.plotSizeY-s(d,h[2])/2-c)};a||p(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this.spacing,b=this.margin;this.plotTop=n(b[0],a[0]);this.marginRight=n(b[1],a[1]);this.marginBottom=n(b[2],a[2]);this.plotLeft=n(b[3],a[3]);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a= -this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,m=a.plotBorderWidth||0,q,o=this.plotLeft,n=this.plotTop,p=this.plotWidth,s=this.plotHeight,r=this.plotBox,t=this.clipRect,w=this.clipBox;q=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-q,d-q));else{e={fill:j||Q};if(i)e.stroke=a.borderColor,e["stroke-width"]= -i;this.chartBackground=b.rect(q/2,q/2,c-q,d-q,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?f.animate(r):this.plotBackground=b.rect(o,n,p,s,0).attr({fill:k}).add().shadow(a.plotShadow);if(l)h?h.animate(r):this.plotBGImage=b.image(l,o,n,p,s).add();t?t.animate({width:w.width,height:w.height}):this.clipRect=b.clipRect(w);if(m)g?g.animate(g.crisp(null,o,n,p,s)):this.plotBorder=b.rect(o,n,p,s,0,-m).attr({stroke:a.plotBorderColor,"stroke-width":m,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a= -this,b=a.options.chart,c,d=a.options.series,e,f;p(["inverted","angular","polar"],function(g){c=L[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=L[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;p(b,function(a){a.linkedSeries.length=0});p(b,function(b){var d=b.options.linkedTo;if(fa(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),b.linkedParent=d})},render:function(){var a=this,b=a.axes, -c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new zb(a,d.legend);a.getStacks();p(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;p(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&p(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();p(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&p(e.items,function(b){var d= -r(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;A(a,"destroy");Ia[a.index]=u;a.renderTo.removeAttribute("data-highcharts-chart"); -X(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();p("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",X(d),f&&Na(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!V&&C==C.top&&y.readyState!=="complete"||da&&!C.canvg?(da?Mb.push(function(){a.firstRender()}, -a.options.global.canvasToolsURL):y.attachEvent("onreadystatechange",function(){y.detachEvent("onreadystatechange",a.firstRender);y.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),A(a,"init"),a.resetMargins(),a.setChartSize(),a.propFromSeries(),a.getAxes(),p(b.series||[],function(b){a.initSeries(b)}),a.linkSeries(),A(a,"beforeRender"),a.pointer=new $a(a,b),a.render(),a.renderer.draw(),c&&c.apply(a, -[a]),p(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),A(a,"load")},splashArray:function(a,b){var c=b[a],c=S(c)?c:[c,c,c,c];return[n(b[a+"Top"],c[0]),n(b[a+"Right"],c[1]),n(b[a+"Bottom"],c[2]),n(b[a+"Left"],c[3])]}};fb.prototype.callbacks=[];var xb=Highcharts.CenteredSeriesMixin={getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-2*c,b=a.center,a=[n(b[0],"50%"),n(b[1],"50%"),a.size||"100%",a.innerSize||0],g=I(e,f),h;return Ra(a, -function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*z(a)/100:a)+(d?c:0)})}},Ja=function(){};Ja.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ja.prototype.optionsToObject.call(this,a);r(this, -a);this.options=this.options?r(this.options,a):a;if(d)this.y=this[d];if(this.x===u&&c)this.x=b===u?c.autoIncrement():b;return this},optionsToObject:function(a){var b={},c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b[d[0]]=a;else if(Ka(a)){if(a.length>e){c=typeof a[0];if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers= -!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),ha(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)X(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category, -y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series,e=d.chart,a=n(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=c.options.selected=a;d.options.data[sa(c,d.data)]=c.options;c.setState(a&&"select");b||p(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[sa(a,d.data)]=a.options,a.setState(""), -a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||sa(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=n(c.valueDecimals, -""),e=c.valuePrefix||"",f=c.valueSuffix||"";p(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return Fa(a,{point:this,series:this.series})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});A(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a= -x(this.series.options.point,this.options).events,b;this.events=a;for(b in a)F(this,b,a[b]);this.hasImportedEvents=!0}},setState:function(a,b){var c=this.plotX,d=this.plotY,e=this.series,f=e.options.states,g=Y[e.type].marker&&e.options.marker,h=g&&!g.enabled,i=g&&g.states[a],j=i&&i.enabled===!1,k=e.stateMarkerGraphic,l=this.marker||{},m=e.chart,q=this.pointAttr,a=a||"",b=b&&k;if(!(a===this.state&&!b||this.selected&&a!=="select"||f[a]&&f[a].enabled===!1||a&&(j||h&&!i.enabled)||a&&l.states&&l.states[a]&& -l.states[a].enabled===!1)){if(this.graphic)f=g&&this.graphic.symbolName&&q[a].r,this.graphic.attr(x(q[a],f?{x:c-f,y:d-f,width:2*f,height:2*f}:{}));else{if(a&&i)if(f=i.radius,l=l.symbol||e.symbol,k&&k.currentSymbol!==l&&(k=k.destroy()),k)k[b?"animate":"attr"]({x:c-f,y:d-f});else e.stateMarkerGraphic=k=m.renderer.symbol(l,c-f,d-f,2*f,2*f).attr(q[a]).add(e.markerGroup),k.currentSymbol=l;if(k)k[a&&m.isInsidePlot(c,d,m.inverted)?"show":"hide"]()}this.state=a}}};var O=function(){};O.prototype={isCartesian:!0, -type:"line",pointClass:Ja,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],init:function(a,b){var c=this,d,e,f=a.series,g=function(a,b){return n(a.options.index,a._i)-n(b.options.index,b._i)};c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();r(c,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if(da)b.animation= -!1;e=b.events;for(d in e)F(c,d,e[d]);if(e&&e.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor();c.getSymbol();p(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);if(c.isCartesian)a.hasCartesianSeries=!0;f.push(c);c._i=f.length-1;ob(f,g);this.yAxis&&ob(this.yAxis.series,g);p(f,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)})},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;p(a.axisTypes||[],function(e){p(c[e],function(c){d= -c.options;if(b[e]===d.index||b[e]!==u&&b[e]===d.id||b[e]===u&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});!a[e]&&a.optionalAxis!==e&&ka(18,!0)})},updateParallelArrays:function(a,b){var c=a.series,d=arguments;p(c.parallelArrays,typeof b==="number"?function(d){var f=d==="y"&&c.toYData?c.toYData(a):a[d];c[d+"Data"][b]=f}:function(a){Array.prototype[b].apply(c[a+"Data"],Array.prototype.slice.call(d,2))})},autoIncrement:function(){var a=this.options,b=this.xIncrement,b=n(b,a.pointStart,0);this.pointInterval= -n(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else p(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart,c=b.options.plotOptions,b=b.userOptions||{},d=b.plotOptions||{},e=c[this.type];this.userOptions= -a;c=x(e,c.series,a);this.tooltipOptions=x(G.tooltip,G.plotOptions[this.type].tooltip,b.tooltip,d.series&&d.series.tooltip,d[this.type]&&d[this.type].tooltip,a.tooltip);e.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||Y[this.type].color;if(!e&&!a.colorByPoint)t(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a= -this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,c=c.counters;this.symbol=b.symbol;if(!this.symbol)t(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:R.drawLineMarker,setData:function(a,b){var c=this,d=c.points,e=c.options,f=c.chart,g=null,h=c.xAxis,i=h&&!!h.categories,j;c.xIncrement=null;c.pointRange=i?1:e.pointRange;c.colorCounter=0;var a=a||[],k=a.length; -j=e.turboThreshold;var l=this.xData,m=this.yData,q=c.pointArrayMap,q=q&&q.length;p(this.parallelArrays,function(a){c[a+"Data"].length=0});if(j&&k>j){for(j=0;g===null&&j<k;)g=a[j],j++;if(wa(g)){i=n(e.pointStart,0);e=n(e.pointInterval,1);for(j=0;j<k;j++)l[j]=i,m[j]=a[j],i+=e;c.xIncrement=i}else if(Ka(g))if(q)for(j=0;j<k;j++)e=a[j],l[j]=e[0],m[j]=e.slice(1,q+1);else for(j=0;j<k;j++)e=a[j],l[j]=e[0],m[j]=e[1];else ka(12)}else for(j=0;j<k;j++)if(a[j]!==u&&(e={series:c},c.pointClass.prototype.applyOptions.apply(e, -[a[j]]),c.updateParallelArrays(e,j),i&&e.name))h.names[e.x]=e.name;fa(m[0])&&ka(14,!0);c.data=[];c.options.data=a;for(j=d&&d.length||0;j--;)d[j]&&d[j].destroy&&d[j].destroy();if(h)h.minRange=h.userMinRange;c.isDirty=c.isDirtyData=f.isDirtyBox=!0;n(b,!0)&&f.redraw(!1)},processData:function(a){var b=this.xData,c=this.yData,d=b.length,e;e=0;var f,g,h=this.xAxis,i=this.options,j=i.cropThreshold,k=this.isCartesian;if(k&&!this.isDirty&&!h.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(k&&this.sorted&&(!j|| -d>j||this.forceCrop))if(a=h.min,h=h.max,b[d-1]<a||b[0]>h)b=[],c=[];else if(b[0]<a||b[d-1]>h)e=this.cropData(this.xData,this.yData,a,h),b=e.xData,c=e.yData,e=e.start,f=!0;for(h=b.length-1;h>=0;h--)d=b[h]-b[h-1],d>0&&(g===u||d<g)?g=d:d<0&&this.requireSorting&&ka(15);this.cropped=f;this.cropStart=e;this.processedXData=b;this.processedYData=c;if(i.pointRange===null)this.pointRange=g||1;this.closestPointRange=g},cropData:function(a,b,c,d){var e=a.length,f=0,g=e,h=n(this.cropShoulder,1),i;for(i=0;i<e;i++)if(a[i]>= -c){f=s(0,i-h);break}for(;i<e;i++)if(a[i]>d){g=i+h;break}return{xData:a.slice(f,g),yData:b.slice(f,g),start:f,end:g}},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,l=[],m;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(m=0;m<g;m++)i=h+m,j?l[m]=(new f).init(this,[d[m]].concat(ja(e[m]))):(b[i]?k=b[i]:a[i]!==u&&(b[i]=k=(new f).init(this,a[i],d[m])),l[m]=k);if(b&& -(g!==(c=b.length)||j))for(m=0;m<c;m++)if(m===h&&!j&&(m+=g),b[m])b[m].destroyElements(),b[m].plotX=u;this.data=b;this.points=l},setStackedPoints:function(){if(this.options.stacking&&!(this.visible!==!0&&this.chart.options.chart.ignoreHiddenSeries!==!1)){var a=this.processedXData,b=this.processedYData,c=[],d=b.length,e=this.options,f=e.threshold,g=e.stack,e=e.stacking,h=this.stackKey,i="-"+h,j=this.negStacks,k=this.yAxis,l=k.stacks,m=k.oldStacks,q,o,n,p,r;for(n=0;n<d;n++){p=a[n];r=b[n];o=(q=j&&r<f)? -i:h;l[o]||(l[o]={});if(!l[o][p])m[o]&&m[o][p]?(l[o][p]=m[o][p],l[o][p].total=null):l[o][p]=new Gb(k,k.options.stackLabels,q,p,g,e);o=l[o][p];o.points[this.index]=[o.cum||0];e==="percent"?(q=q?h:i,j&&l[q]&&l[q][p]?(q=l[q][p],o.total=q.total=s(q.total,o.total)+M(r)||0):o.total=aa(o.total+(M(r)||0))):o.total=aa(o.total+(r||0));o.cum=(o.cum||0)+(r||0);o.points[this.index].push(o.cum);c[n]=o.cum}if(e==="percent")k.usePercentage=!0;this.stackedYData=c;k.oldStacks={}}},setPercentStacks:function(){var a= -this,b=a.stackKey,c=a.yAxis.stacks;p([b,"-"+b],function(b){var d;for(var e=a.xData.length,f,g;e--;)if(f=a.xData[e],d=(g=c[b]&&c[b][f])&&g.points[a.index],f=d)g=g.total?100/g.total:0,f[0]=aa(f[0]*g),f[1]=aa(f[1]*g),a.stackedYData[e]=f[1]})},getExtremes:function(a){var b=this.yAxis,c=this.processedXData,d,e=[],f=0;d=this.xAxis.getExtremes();var g=d.min,h=d.max,i,j,k,l,a=a||this.stackedYData||this.processedYData;d=a.length;for(l=0;l<d;l++)if(j=c[l],k=a[l],i=k!==null&&k!==u&&(!b.isLog||k.length||k>0), -j=this.getExtremesFromAll||this.cropped||(c[l+1]||j)>=g&&(c[l-1]||j)<=h,i&&j)if(i=k.length)for(;i--;)k[i]!==null&&(e[f++]=k[i]);else e[f++]=k;this.dataMin=n(void 0,La(e));this.dataMax=n(void 0,za(e))},translate:function(){this.processedXData||this.processData();this.generatePoints();for(var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i=a.pointPlacement,j=i==="between"||wa(i),k=a.threshold,a=0;a<g;a++){var l=f[a],m=l.x,q=l.y,o= -l.low,p=b&&e.stacks[(this.negStacks&&q<k?"-":"")+this.stackKey];if(e.isLog&&q<=0)l.y=q=null;l.plotX=c.translate(m,0,0,0,1,i,this.type==="flags");if(b&&this.visible&&p&&p[m])p=p[m],q=p.points[this.index],o=q[0],q=q[1],o===0&&(o=n(k,e.min)),e.isLog&&o<=0&&(o=null),l.total=l.stackTotal=p.total,l.percentage=b==="percent"&&l.y/p.total*100,l.stackY=q,p.setOffset(this.pointXOffset||0,this.barW||0);l.yBottom=t(o)?e.translate(o,0,1,0,1):null;h&&(q=this.modifyValue(q,l));l.plotY=typeof q==="number"&&q!==Infinity? -e.translate(q,0,1,0,1):u;l.clientX=j?c.translate(m,0,0,0,1):l.plotX;l.negative=l.y<(k||0);l.category=d&&d[l.x]!==u?d[l.x]:l.x}this.getSegments()},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||e.len:this.chart.plotSizeX,h,i,j=[];if(this.options.enableMouseTracking!==!1){if(a)this.tooltipPoints=null;p(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(i= -0;i<a;i++)if(e=b[i],c=e.x,c>=f.min&&c<=f.max){h=b[i+1];c=d===u?0:d+1;for(d=b[i+1]?I(s(0,N((e.clientX+(h?h.wrappedClientX||h.clientX:g))/2)),g):g;c>=0&&c<=d;)j[c++]=e}this.tooltipPoints=j}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.dateTimeLabelFormats,d=b.xDateFormat,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!d){if(e)for(g in E){if(E[g]>=e){d=c[g];break}}else d=c.day;d=d||c.year}f&&d&&wa(a.key)&&(b=b.replace("{point.key}", -"{point.key:"+d+"}"));return Fa(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&A(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&A(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b= -this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!S(e))e=Y[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],e=c[h+"m"],a||(c[h]=a=d.clipRect(r(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()}, -e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,l=this.options.marker,m,q=this.markerGroup;if(l.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=N(g.plotX),e=g.plotY,k=g.graphic,i=g.marker||{},a=l.enabled&&i.enabled=== -u||i.enabled,m=c.isInsidePlot(w(d),e,c.inverted),a&&e!==u&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=n(i.symbol,this.symbol),j=i.indexOf("url")===0,k)k.attr({visibility:m?V?"inherit":"visible":"hidden"}).animate(r({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(m&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(q)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{}, -c=c||{},d=d||{};for(f in e)g=e[f],h[f]=n(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=Y[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color,h={stroke:g,fill:g},i=a.points||[],j=[],k,l=a.pointAttrToOptions,m=b.negativeColor,n=c.lineColor,o=c.fillColor,s;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||ta(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);p(["hover","select"],function(b){j[b]=a.convertAttribs(d[b], -j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&m)h.color=h.fillColor=m;k=b.colorByPoint||h.color;if(h.options)for(s in l)t(c[l[s]])&&(k=!0);if(k){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=ta(f.color||h.color).brighten(f.brightness||e.brightness).get();f={color:h.color};if(!o)f.fillColor=h.color;if(!n)f.lineColor=h.color;k[""]=a.convertAttribs(r(f,c),j[""]);k.hover=a.convertAttribs(d.hover, -j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""])}else k=j;h.pointAttr=k}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(ra),d,e,f=a.data||[],g,h,i;A(a,"destroy");X(a);p(a.axisTypes||[],function(b){if(i=a[b])ha(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);p("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","), -function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ha(b.series,a);for(h in a)delete a[h]},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;p(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},getGraphPath:function(){var a= -this,b=[],c,d=[];p(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=b.linecap!=="square",g=this.getGraphPath(),h=b.negativeColor;h&&c.push(["graphNeg",h]);p(c,function(c,h){var k=c[0],l=a[k];if(l)Za(l),l.animate({d:g});else if(d&&g.length)l={stroke:c[1],"stroke-width":d,zIndex:1},e?l.dashstyle=e:f&&(l["stroke-linecap"]= -l["stroke-linejoin"]="round"),a[k]=a.chart.renderer.path(g).attr(l).add(a.group).shadow(!h&&b.shadow)})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor||a.negativeFillColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=s(e,j),l=this.yAxis;if(d&&(f||g)){d=w(l.toPixels(a.threshold||0,!0));d<0&&(k-=d);a={x:0,y:0,width:k,height:d};k={x:0,y:d,width:k,height:k};if(b.inverted)a.height=k.y=b.plotWidth-d,c.isVML&&(a={x:b.plotWidth- -d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,height:e});l.reversed?(b=k,e=a):(b=a,e=k);h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&this.graphNeg&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))}},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};p(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)F(c,"resize",a),F(b, -"destroy",function(){X(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=!f;g&&(this[a]=f=this.chart.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"](this.getPlotBox());return f},getPlotBox:function(){return{translateX:this.xAxis?this.xAxis.left:this.chart.plotLeft,translateY:this.yAxis?this.yAxis.top:this.chart.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG, -e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup","markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels&&this.drawDataLabels();this.visible&&this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&& -!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:n(d&&d.left,a.plotLeft),translateY:n(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&A(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg, -e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a,b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===u?!h:a)?"show":"hide";p(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c, -a);c.isDirty=!0;c.options.stacking&&p(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});p(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();A(c,f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===u?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;A(this,a?"select":"unselect")},drawTracker:J.drawTrackerGraph};r(fb.prototype,{addSeries:function(a,b,c){var d,e=this;a&&(b= -n(b,!0),A(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;e.linkSeries();b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var e=b?"xAxis":"yAxis",f=this.options;new qa(this,x(a,{index:this[e].length,isX:b}));f[e]=ja(f[e]||{});f[e].push(a);n(c,!0)&&this.redraw(d)},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=T(Ga,{className:"highcharts-loading"},r(d.style,{zIndex:10,display:Q}),this.container),this.loadingSpan=T("span", -null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)D(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),kb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&kb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){D(b,{display:Q})}});this.loadingShown=!1}});r(Ja.prototype,{update:function(a, -b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=e.chart,j=e.options,b=n(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);if(S(a)){e.getAttribs();if(f)a&&a.marker&&a.marker.symbol?d.graphic=f.destroy():f.attr(d.pointAttr[d.state||""]);if(a&&a.dataLabels&&d.dataLabel)d.dataLabel=d.dataLabel.destroy()}g=sa(d,h);e.updateParallelArrays(d,g);j.data[g]=d.options;e.isDirty=e.isDirtyData=!0;if(!e.fixedBox&&e.hasCartesianSeries)i.isDirtyBox=!0;j.legendType==="point"&&i.legend.destroyItem(d); -b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.points,f=d.chart,g,h=d.data;Oa(b,f);a=n(a,!0);c.firePointEvent("remove",null,function(){g=sa(c,h);h.length===e.length&&e.splice(g,1);h.splice(g,1);d.options.data.splice(g,1);d.updateParallelArrays(c,"splice",g,1);c.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&f.redraw()})}});r(O.prototype,{addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xAxis&&this.xAxis.names,k=g&&g.shift||0,l=e.data, -m,q=this.xData;Oa(d,i);c&&p([g,h,this.graphNeg,this.areaNeg],function(a){if(a)a.shift=k+1});if(h)h.isArea=!0;b=n(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);g=d.x;h=q.length;if(this.requireSorting&&g<q[h-1])for(m=!0;h&&q[h-1]>g;)h--;this.updateParallelArrays(d,"splice",h,0,0);this.updateParallelArrays(d,h);if(j)j[g]=d.name;l.splice(h,0,a);m&&(this.data.splice(h,0,null),this.processData());e.legendType==="point"&&this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1): -(f.shift(),this.updateParallelArrays(d,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&(this.getAttribs(),i.redraw())},remove:function(a,b){var c=this,d=c.chart,a=n(a,!0);if(!c.isRemoving)c.isRemoving=!0,A(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;d.linkSeries();a&&d.redraw(b)});c.isRemoving=!1},update:function(a,b){var c=this.chart,d=this.type,e=L[d].prototype,f,a=x(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},{data:this.options.data}, -a);this.remove(!1);for(f in e)e.hasOwnProperty(f)&&(this[f]=u);r(this,L[a.type||d].prototype);this.init(c,a);n(b,!0)&&c.redraw(!1)}});r(qa.prototype,{update:function(a,b){var c=this.chart,a=c.options[this.coll][this.options.index]=x(this.userOptions,a);this.destroy(!0);this._addedPlotLB=this.userMin=this.userMax=u;this.init(c,r(a,{events:u}));c.isDirtyBox=!0;n(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.coll;p(this.series,function(a){a.remove(!1)});ha(b.axes,this);ha(b[c],this); -b.options[c].splice(this.options.index,1);p(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;n(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}});var ca=ia(O);L.line=ca;Y.area=x(W,{threshold:0});var Ua=ia(O,{type:"area",getSegments:function(){var a=[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k=this.options.connectNulls,l,m,n;if(this.options.stacking&&!this.cropped){for(m= -0;m<j.length;m++)g[j[m].x]=j[m];for(n in f)f[n].total!==null&&c.push(+n);c.sort(function(a,b){return a-b});p(c,function(a){if(!k||g[a]&&g[a].y!==null)g[a]?b.push(g[a]):(h=d.translate(a),l=f[a].percent?f[a].total?f[a].cum*100/f[a].total:0:f[a].cum,i=e.toPixels(l,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:la}))});b.length&&a.push(b)}else O.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=O.prototype.getSegmentPath.call(this,a), -c=[].concat(b),d,e=this.options;d=b.length;var f=this.yAxis.getThreshold(e.threshold),g;d===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)g=n(a[d].yBottom,f),d<a.length-1&&e.step&&c.push(a[d+1].plotX,g),c.push(a[d].plotX,g);else this.closeSegment(c,a,f);this.areaPath=this.areaPath.concat(c);return b},closeSegment:function(a,b,c){a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];O.prototype.drawGraph.apply(this);var a=this, -b=this.areaPath,c=this.options,d=c.negativeColor,e=c.negativeFillColor,f=[["area",this.color,c.fillColor]];(d||e)&&f.push(["areaNeg",d,e]);p(f,function(d){var e=d[0],f=a[e];f?f.animate({d:b}):a[e]=a.chart.renderer.path(b).attr({fill:n(d[2],ta(d[1]).setOpacity(n(c.fillOpacity,0.75)).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:R.drawRectangle});L.area=Ua;Y.spline=x(W);ca=ia(O,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j= -g.plotX;var g=g.plotY,l;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;l=(k-i)*(j-d)/(j-h)+e-k;i+=l;k+=l;i>a&&i>e?(i=s(a,e),k=2*e-i):i<a&&i<e&&(i=I(a,e),k=2*e-i);k>g&&k>e?(k=s(g,e),i=2*e-k):k<g&&k<e&&(k=I(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?(b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=f.rightContY=null):b=["M",d,e];return b}});L.spline=ca;Y.areaspline=x(Y.area);Ua=Ua.prototype;ca=ia(ca,{type:"areaspline",closedStacks:!0,getSegmentPath:Ua.getSegmentPath, -closeSegment:Ua.closeSegment,drawGraph:Ua.drawGraph,drawLegendSymbol:R.drawRectangle});L.areaspline=ca;Y.column=x(W,{borderColor:"#FFFFFF",borderWidth:1,borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,shadow:!1},select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,threshold:0});ca=ia(O,{type:"column",pointAttrToOptions:{stroke:"borderColor", -"stroke-width":"borderWidth",fill:"color",r:"borderRadius"},cropShoulder:0,trackerGroups:["group","dataLabelsGroup"],negStacks:!0,init:function(){O.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},getColumnMetrics:function(){var a=this,b=a.options,c=a.xAxis,d=a.yAxis,e=c.reversed,f,g={},h,i=0;b.grouping===!1?i=1:p(a.chart.series,function(b){var c=b.options,e=b.yAxis;if(b.type===a.type&&b.visible&&d.len===e.len&&d.pos=== -e.pos)c.stacking?(f=b.stackKey,g[f]===u&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var c=I(M(c.transA)*(c.ordinalSlope||b.pointRange||c.closestPointRange||1),c.len),j=c*b.groupPadding,k=(c-2*j)/i,l=b.pointWidth,b=t(l)?(k-l)/2:k*b.pointPadding,l=n(l,k-2*b);return a.columnMetrics={width:l,offset:b+(j+((e?i-(a.columnIndex||0):a.columnIndex)||0)*k-c/2)*(e?-1:1)}},translate:function(){var a=this.chart,b=this.options,c=b.borderWidth,d=this.yAxis,e=this.translatedThreshold=d.getThreshold(b.threshold), -f=n(b.minPointLength,5),b=this.getColumnMetrics(),g=b.width,h=this.barW=Ha(s(g,1+2*c)),i=this.pointXOffset=b.offset,j=-(c%2?0.5:0),k=c%2?0.5:1;a.renderer.isVML&&a.inverted&&(k+=1);O.prototype.translate.apply(this);p(this.points,function(a){var b=n(a.yBottom,e),c=I(s(-999-b,a.plotY),d.len+999+b),o=a.plotX+i,p=h,r=I(c,b),t,c=s(c,b)-r;M(c)<f&&f&&(c=f,r=w(M(r-e)>f?b-f:e-(d.translate(a.y,0,1,0,1)<=e?f:0)));a.barX=o;a.pointWidth=g;b=M(o)<0.5;p=w(o+p)+j;o=w(o)+j;p-=o;t=M(r)<0.5;c=w(r+c)+k;r=w(r)+k;c-=r; -b&&(o+=1,p-=1);t&&(r-=1,c+=1);a.shapeType="rect";a.shapeArgs={x:o,y:r,width:p,height:c}})},getSymbol:la,drawLegendSymbol:R.drawRectangle,drawGraph:la,drawPoints:function(){var a=this,b=this.chart,c=a.options,d=b.renderer,e=b.options.animationLimit||250,f;p(a.points,function(g){var h=g.plotY,i=g.graphic;if(h!==u&&!isNaN(h)&&g.y!==null)f=g.shapeArgs,i?(Za(i),i[b.pointCount<e?"animate":"attr"](x(f))):g.graphic=d[g.shapeType](f).attr(g.pointAttr[g.selected?"select":""]).add(a.group).shadow(c.shadow,null, -c.stacking&&!c.borderRadius);else if(i)g.graphic=i.destroy()})},drawTracker:J.drawTrackerPoint,animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(V)a?(e.scaleY=0.001,a=I(b.pos+b.len,s(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},remove:function(){var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type=== -a.type)b.isDirty=!0});O.prototype.remove.apply(a,arguments)}});L.column=ca;Y.bar=x(Y.column);ca=ia(ca,{type:"bar",inverted:!0});L.bar=ca;Y.scatter=x(W,{lineWidth:0,tooltip:{headerFormat:'<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>",followPointer:!0},stickyTracking:!1});ca=ia(O,{type:"scatter",sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["markerGroup"],takeOrdinalPosition:!1,drawTracker:J.drawTrackerPoint, -drawGraph:function(){this.options.lineWidth&&O.prototype.drawGraph.call(this)},setTooltipPoints:la});L.scatter=ca;Y.pie=x(W,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});W={type:"pie",isCartesian:!1,pointClass:ia(Ja, -{init:function(){Ja.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;r(a,{visible:a.visible!==!1,name:n(a.name,"Slice")});b=function(b){a.slice(b.type==="select")};F(a,"select",b);F(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===u?!b.visible:a;c.options.data[sa(b,c.data)]=b.options;e=a?"show":"hide";p(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b, -a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Oa(c,d.chart);n(b,!0);this.sliced=this.options.sliced=a=t(a)?a:!this.sliced;d.options.data[sa(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth", -fill:"color"},getColor:la,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)p(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){O.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();n(b,!0)&&this.chart.redraw()},generatePoints:function(){var a,b=0,c,d,e,f=this.options.ignoreHiddenPoint;O.prototype.generatePoints.call(this); -c=this.points;d=c.length;for(a=0;a<d;a++)e=c[a],b+=f&&!e.visible?0:e.y;this.total=b;for(a=0;a<d;a++)e=c[a],e.percentage=b>0?e.y/b*100:0,e.total=b},translate:function(a){this.generatePoints();var b=0,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g,h,i=c.startAngle||0,j=this.startAngleRad=Aa/180*(i-90),i=(this.endAngleRad=Aa/180*((c.endAngle||i+360)-90))-j,k=this.points,l=c.dataLabels.distance,c=c.ignoreHiddenPoint,m,n=k.length,o;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){h= -P.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*U(h)*(a[2]/2+l)};for(m=0;m<n;m++){o=k[m];f=j+b*i;if(!c||o.visible)b+=o.percentage/100;g=j+b*i;o.shapeType="arc";o.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:w(f*1E3)/1E3,end:w(g*1E3)/1E3};h=(g+f)/2;h>0.75*i&&(h-=2*Aa);o.slicedTranslation={translateX:w(U(h)*d),translateY:w(ba(h)*d)};f=U(h)*a[2]/2;g=ba(h)*a[2]/2;o.tooltipPos=[a[0]+f*0.7,a[1]+g*0.7];o.half=h<-Aa/2||h>Aa/2?1:0;o.angle=h;e=I(e,l/2);o.labelPos=[a[0]+f+U(h)*l,a[1]+g+ba(h)*l,a[0]+ -f+U(h)*e,a[1]+g+ba(h)*e,a[0]+f,a[1]+g,l<0?"center":o.half?"right":"left",h]}},setTooltipPoints:la,drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);p(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};f&&f.attr(c);d?d.animate(r(g,c)):h.graphic=d=b.arc(g).setRadialReference(a.center).attr(h.pointAttr[h.selected? -"select":""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible!==void 0&&h.setVisible(h.visible)})},sortByAngle:function(a,b){a.sort(function(a,d){return a.angle!==void 0&&(d.angle-a.angle)*b})},drawTracker:J.drawTrackerPoint,drawLegendSymbol:R.drawRectangle,getCenter:xb.getCenter,getSymbol:la};W=ia(O,W);L.pie=W;O.prototype.drawDataLabels=function(){var a=this,b=a.options,c=b.cursor,d=b.dataLabels,b=a.points,e,f,g,h;if(d.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(d), -h=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",d.zIndex||6),f=d,p(b,function(b){var j,k=b.dataLabel,l,m,p=b.connector,o=!0;e=b.options&&b.options.dataLabels;j=n(e&&e.enabled,f.enabled);if(k&&!j)b.dataLabel=k.destroy();else if(j){d=x(f,e);j=d.rotation;l=b.getLabelConfig();g=d.format?Fa(d.format,l):d.formatter.call(l,d);d.style.color=n(d.color,d.style.color,a.color,"black");if(k)if(t(g))k.attr({text:g}),o=!1;else{if(b.dataLabel=k=k.destroy(),p)b.connector=p.destroy()}else if(t(g)){k= -{fill:d.backgroundColor,stroke:d.borderColor,"stroke-width":d.borderWidth,r:d.borderRadius||0,rotation:j,padding:d.padding,zIndex:1};for(m in k)k[m]===u&&delete k[m];k=b.dataLabel=a.chart.renderer[j?"text":"label"](g,0,-999,null,null,null,d.useHTML).attr(k).css(r(d.style,c&&{cursor:c})).add(h).shadow(d.shadow)}k&&a.alignDataLabel(b,k,d,null,o)}})};O.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=n(a.plotX,-999),i=n(a.plotY,-999),j=b.getBBox();if(a=this.visible&&(a.series.forceDL|| -f.isInsidePlot(a.plotX,a.plotY,g)))d=r({x:g?f.plotWidth-i:h,y:w(g?f.plotHeight-h:i),width:0,height:0},d),r(c,{width:j.width,height:j.height}),c.rotation?(g={align:c.align,x:d.x+c.x+d.width/2,y:d.y+c.y+d.height/2},b[e?"attr":"animate"](g)):(b.align(c,null,d),g=b.alignAttr,n(c.overflow,"justify")==="justify"?this.justifyDataLabel(b,c,g,j,d,e):n(c.crop,!0)&&(a=f.isInsidePlot(g.x,g.y)&&f.isInsidePlot(g.x+j.width,g.y+j.height)));if(!a)b.attr({y:-999}),b.placed=!1};O.prototype.justifyDataLabel=function(a, -b,c,d,e,f){var g=this.chart,h=b.align,i=b.verticalAlign,j,k;j=c.x;if(j<0)h==="right"?b.align="left":b.x=-j,k=!0;j=c.x+d.width;if(j>g.plotWidth)h==="left"?b.align="right":b.x=g.plotWidth-j,k=!0;j=c.y;if(j<0)i==="bottom"?b.verticalAlign="top":b.y=-j,k=!0;j=c.y+d.height;if(j>g.plotHeight)i==="top"?b.verticalAlign="bottom":b.y=g.plotHeight-j,k=!0;if(k)a.placed=!f,a.align(b,null,e)};if(L.pie)L.pie.prototype.drawDataLabels=function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=n(e.connectorPadding, -10),g=n(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=n(e.softConnector,!0),l=e.distance,m=a.center,q=m[2]/2,o=m[1],r=l>0,t,u,v,x,y=[[],[]],z,A,E,K,B,D=[0,0,0,0],I=function(a,b){return b.y-a.y};if(a.visible&&(e.enabled||a._hasPointLabels)){O.prototype.drawDataLabels.apply(a);p(b,function(a){a.dataLabel&&a.visible&&y[a.half].push(a)});for(K=0;!x&&b[K];)x=b[K]&&b[K].dataLabel&&(b[K].dataLabel.getBBox().height||21),K++;for(K=2;K--;){var b=[],J=[],F=y[K],G=F.length,C;a.sortByAngle(F,K-0.5);if(l> -0){for(B=o-q-l;B<=o+q+l;B+=x)b.push(B);u=b.length;if(G>u){c=[].concat(F);c.sort(I);for(B=G;B--;)c[B].rank=B;for(B=G;B--;)F[B].rank>=u&&F.splice(B,1);G=F.length}for(B=0;B<G;B++){c=F[B];v=c.labelPos;c=9999;var L,N;for(N=0;N<u;N++)L=M(b[N]-v[1]),L<c&&(c=L,C=N);if(C<B&&b[B]!==null)C=B;else for(u<G-B+C&&b[B]!==null&&(C=u-G+B);b[C]===null;)C++;J.push({i:C,y:b[C]});b[C]=null}J.sort(I)}for(B=0;B<G;B++){c=F[B];v=c.labelPos;t=c.dataLabel;E=c.visible===!1?"hidden":"visible";c=v[1];if(l>0){if(u=J.pop(),C=u.i, -A=u.y,c>A&&b[C+1]!==null||c<A&&b[C-1]!==null)A=c}else A=c;z=e.justify?m[0]+(K?-1:1)*(q+l):a.getX(C===0||C===b.length-1?c:A,K);t._attr={visibility:E,align:v[6]};t._pos={x:z+e.x+({left:f,right:-f}[v[6]]||0),y:A+e.y-10};t.connX=z;t.connY=A;if(this.options.size===null)u=t.width,z-u<f?D[3]=s(w(u-z+f),D[3]):z+u>h-f&&(D[1]=s(w(z+u-h+f),D[1])),A-x/2<0?D[0]=s(w(-A+x/2),D[0]):A+x/2>d&&(D[2]=s(w(A+x/2-d),D[2]))}}if(za(D)===0||this.verifyDataLabelOverflow(D))this.placeDataLabels(),r&&g&&p(this.points,function(b){i= -b.connector;v=b.labelPos;if((t=b.dataLabel)&&t._pos)E=t._attr.visibility,z=t.connX,A=t.connY,j=k?["M",z+(v[6]==="left"?5:-5),A,"C",z,A,2*v[2]-v[4],2*v[3]-v[5],v[2],v[3],"L",v[4],v[5]]:["M",z+(v[6]==="left"?5:-5),A,"L",v[2],v[3],"L",v[4],v[5]],i?(i.animate({d:j}),i.attr("visibility",E)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:E}).add(a.group);else if(i)b.connector=i.destroy()})}},L.pie.prototype.placeDataLabels=function(){p(this.points, -function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},L.pie.prototype.alignDataLabel=la,L.pie.prototype.verifyDataLabelOverflow=function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=s(b[2]-s(a[1],a[3]),c):(e=s(b[2]-a[1]-a[3],c),b[0]+=(a[3]-a[1])/2);d[1]!==null?e=s(I(e,b[2]-s(a[0],a[2])),c):(e=s(I(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),p(this.points,function(a){if(a.dataLabel)a.dataLabel._pos= -null}),this.drawDataLabels&&this.drawDataLabels()):f=!0;return f};if(L.column)L.column.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>n(this.translatedThreshold,f.plotSizeY),j=n(c.inside,!!this.options.stacking);if(h&&(d=x(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:0,d.height=0);c.align=n(c.align,!g||j?"center":i?"right":"left"); -c.verticalAlign=n(c.verticalAlign,g||j?"middle":i?"top":"bottom");O.prototype.alignDataLabel.call(this,a,b,c,d,e)};r(Highcharts,{Axis:qa,Chart:fb,Color:ta,Point:Ja,Tick:Qa,Tooltip:tb,Renderer:Ya,Series:O,SVGElement:pa,SVGRenderer:ua,arrayMin:La,arrayMax:za,charts:Ia,dateFormat:ab,format:Fa,pathAnim:vb,getOptions:function(){return G},hasBidiBug:Nb,isTouchDevice:Ib,numberFormat:Da,seriesTypes:L,setOptions:function(a){G=x(!0,G,a);Bb();return G},addEvent:F,removeEvent:X,createElement:T,discardElement:Na, -css:D,each:p,extend:r,map:Ra,merge:x,pick:n,splat:ja,extendClass:ia,pInt:z,wrap:Va,svg:V,canvas:da,vml:!V&&!da,product:"Highcharts",version:"3.0.9"})})(); diff --git a/pykeg/web/static/highcharts/js/highcharts.src.js b/pykeg/web/static/highcharts/js/highcharts.src.js deleted file mode 100644 index a347d328f..000000000 --- a/pykeg/web/static/highcharts/js/highcharts.src.js +++ /dev/null @@ -1,17356 +0,0 @@ -// ==ClosureCompiler== -// @compilation_level SIMPLE_OPTIMIZATIONS - -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * - * (c) 2009-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -// JSLint options: -/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */ - -(function () { -// encapsulated variables -var UNDEFINED, - doc = document, - win = window, - math = Math, - mathRound = math.round, - mathFloor = math.floor, - mathCeil = math.ceil, - mathMax = math.max, - mathMin = math.min, - mathAbs = math.abs, - mathCos = math.cos, - mathSin = math.sin, - mathPI = math.PI, - deg2rad = mathPI * 2 / 360, - - - // some variables - userAgent = navigator.userAgent, - isOpera = win.opera, - isIE = /msie/i.test(userAgent) && !isOpera, - docMode8 = doc.documentMode === 8, - isWebKit = /AppleWebKit/.test(userAgent), - isFirefox = /Firefox/.test(userAgent), - isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent), - SVG_NS = 'http://www.w3.org/2000/svg', - hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect, - hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38 - useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext, - Renderer, - hasTouch = doc.documentElement.ontouchstart !== UNDEFINED, - symbolSizes = {}, - idCounter = 0, - garbageBin, - defaultOptions, - dateFormat, // function - globalAnimation, - pathAnim, - timeUnits, - noop = function () {}, - charts = [], - PRODUCT = 'Highcharts', - VERSION = '3.0.9', - - // some constants for frequently used strings - DIV = 'div', - ABSOLUTE = 'absolute', - RELATIVE = 'relative', - HIDDEN = 'hidden', - PREFIX = 'highcharts-', - VISIBLE = 'visible', - PX = 'px', - NONE = 'none', - M = 'M', - L = 'L', - numRegex = /^[0-9]+$/, - /* - * Empirical lowest possible opacities for TRACKER_FILL - * IE6: 0.002 - * IE7: 0.002 - * IE8: 0.002 - * IE9: 0.00000000001 (unlimited) - * IE10: 0.0001 (exporting only) - * FF: 0.00000000001 (unlimited) - * Chrome: 0.000001 - * Safari: 0.000001 - * Opera: 0.00000000001 (unlimited) - */ - TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable - //TRACKER_FILL = 'rgba(192,192,192,0.5)', - NORMAL_STATE = '', - HOVER_STATE = 'hover', - SELECT_STATE = 'select', - MILLISECOND = 'millisecond', - SECOND = 'second', - MINUTE = 'minute', - HOUR = 'hour', - DAY = 'day', - WEEK = 'week', - MONTH = 'month', - YEAR = 'year', - - // Object for extending Axis - AxisPlotLineOrBandExtension, - - // constants for attributes - LINEAR_GRADIENT = 'linearGradient', - STOPS = 'stops', - STROKE_WIDTH = 'stroke-width', - - // time methods, changed based on whether or not UTC is used - makeTime, - timezoneOffset, - getMinutes, - getHours, - getDay, - getDate, - getMonth, - getFullYear, - setMinutes, - setHours, - setDate, - setMonth, - setFullYear, - - - // lookup over the types and the associated classes - seriesTypes = {}; - -// The Highcharts namespace -win.Highcharts = win.Highcharts ? error(16, true) : {}; - -/** - * Extend an object with the members of another - * @param {Object} a The object to be extended - * @param {Object} b The object to add to the first one - */ -function extend(a, b) { - var n; - if (!a) { - a = {}; - } - for (n in b) { - a[n] = b[n]; - } - return a; -} - -/** - * Deep merge two or more objects and return a third object. If the first argument is - * true, the contents of the second object is copied into the first object. - * Previously this function redirected to jQuery.extend(true), but this had two limitations. - * First, it deep merged arrays, which lead to workarounds in Highcharts. Second, - * it copied properties from extended prototypes. - */ -function merge() { - var i, - args = arguments, - len, - ret = {}, - doCopy = function (copy, original) { - var value, key; - - // An object is replacing a primitive - if (typeof copy !== 'object') { - copy = {}; - } - - for (key in original) { - if (original.hasOwnProperty(key)) { - value = original[key]; - - // Copy the contents of objects, but not arrays or DOM nodes - if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]' - && typeof value.nodeType !== 'number') { - copy[key] = doCopy(copy[key] || {}, value); - - // Primitives and arrays are copied over directly - } else { - copy[key] = original[key]; - } - } - } - return copy; - }; - - // If first argument is true, copy into the existing object. Used in setOptions. - if (args[0] === true) { - ret = args[1]; - args = Array.prototype.slice.call(args, 2); - } - - // For each argument, extend the return - len = args.length; - for (i = 0; i < len; i++) { - ret = doCopy(ret, args[i]); - } - - return ret; -} - -/** - * Take an array and turn into a hash with even number arguments as keys and odd numbers as - * values. Allows creating constants for commonly used style properties, attributes etc. - * Avoid it in performance critical situations like looping - */ -function hash() { - var i = 0, - args = arguments, - length = args.length, - obj = {}; - for (; i < length; i++) { - obj[args[i++]] = args[i]; - } - return obj; -} - -/** - * Shortcut for parseInt - * @param {Object} s - * @param {Number} mag Magnitude - */ -function pInt(s, mag) { - return parseInt(s, mag || 10); -} - -/** - * Check for string - * @param {Object} s - */ -function isString(s) { - return typeof s === 'string'; -} - -/** - * Check for object - * @param {Object} obj - */ -function isObject(obj) { - return typeof obj === 'object'; -} - -/** - * Check for array - * @param {Object} obj - */ -function isArray(obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; -} - -/** - * Check for number - * @param {Object} n - */ -function isNumber(n) { - return typeof n === 'number'; -} - -function log2lin(num) { - return math.log(num) / math.LN10; -} -function lin2log(num) { - return math.pow(10, num); -} - -/** - * Remove last occurence of an item from an array - * @param {Array} arr - * @param {Mixed} item - */ -function erase(arr, item) { - var i = arr.length; - while (i--) { - if (arr[i] === item) { - arr.splice(i, 1); - break; - } - } - //return arr; -} - -/** - * Returns true if the object is not null or undefined. Like MooTools' $.defined. - * @param {Object} obj - */ -function defined(obj) { - return obj !== UNDEFINED && obj !== null; -} - -/** - * Set or get an attribute or an object of attributes. Can't use jQuery attr because - * it attempts to set expando properties on the SVG element, which is not allowed. - * - * @param {Object} elem The DOM element to receive the attribute(s) - * @param {String|Object} prop The property or an abject of key-value pairs - * @param {String} value The value if a single property is set - */ -function attr(elem, prop, value) { - var key, - setAttribute = 'setAttribute', - ret; - - // if the prop is a string - if (isString(prop)) { - // set the value - if (defined(value)) { - - elem[setAttribute](prop, value); - - // get the value - } else if (elem && elem.getAttribute) { // elem not defined when printing pie demo... - ret = elem.getAttribute(prop); - } - - // else if prop is defined, it is a hash of key/value pairs - } else if (defined(prop) && isObject(prop)) { - for (key in prop) { - elem[setAttribute](key, prop[key]); - } - } - return ret; -} -/** - * Check if an element is an array, and if not, make it into an array. Like - * MooTools' $.splat. - */ -function splat(obj) { - return isArray(obj) ? obj : [obj]; -} - - -/** - * Return the first value that is defined. Like MooTools' $.pick. - */ -function pick() { - var args = arguments, - i, - arg, - length = args.length; - for (i = 0; i < length; i++) { - arg = args[i]; - if (typeof arg !== 'undefined' && arg !== null) { - return arg; - } - } -} - -/** - * Set CSS on a given element - * @param {Object} el - * @param {Object} styles Style object with camel case property names - */ -function css(el, styles) { - if (isIE) { - if (styles && styles.opacity !== UNDEFINED) { - styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')'; - } - } - extend(el.style, styles); -} - -/** - * Utility function to create element with attributes and styles - * @param {Object} tag - * @param {Object} attribs - * @param {Object} styles - * @param {Object} parent - * @param {Object} nopad - */ -function createElement(tag, attribs, styles, parent, nopad) { - var el = doc.createElement(tag); - if (attribs) { - extend(el, attribs); - } - if (nopad) { - css(el, {padding: 0, border: NONE, margin: 0}); - } - if (styles) { - css(el, styles); - } - if (parent) { - parent.appendChild(el); - } - return el; -} - -/** - * Extend a prototyped class by new members - * @param {Object} parent - * @param {Object} members - */ -function extendClass(parent, members) { - var object = function () {}; - object.prototype = new parent(); - extend(object.prototype, members); - return object; -} - -/** - * Format a number and return a string based on input settings - * @param {Number} number The input number to format - * @param {Number} decimals The amount of decimals - * @param {String} decPoint The decimal point, defaults to the one given in the lang options - * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options - */ -function numberFormat(number, decimals, decPoint, thousandsSep) { - var lang = defaultOptions.lang, - // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/ - n = +number || 0, - c = decimals === -1 ? - (n.toString().split('.')[1] || '').length : // preserve decimals - (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals), - d = decPoint === undefined ? lang.decimalPoint : decPoint, - t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep, - s = n < 0 ? "-" : "", - i = String(pInt(n = mathAbs(n).toFixed(c))), - j = i.length > 3 ? i.length % 3 : 0; - - return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + - (c ? d + mathAbs(n - i).toFixed(c).slice(2) : ""); -} - -/** - * Pad a string to a given length by adding 0 to the beginning - * @param {Number} number - * @param {Number} length - */ -function pad(number, length) { - // Create an array of the remaining length +1 and join it with 0's - return new Array((length || 2) + 1 - String(number).length).join(0) + number; -} - -/** - * Wrap a method with extended functionality, preserving the original function - * @param {Object} obj The context object that the method belongs to - * @param {String} method The name of the method to extend - * @param {Function} func A wrapper function callback. This function is called with the same arguments - * as the original function, except that the original function is unshifted and passed as the first - * argument. - */ -function wrap(obj, method, func) { - var proceed = obj[method]; - obj[method] = function () { - var args = Array.prototype.slice.call(arguments); - args.unshift(proceed); - return func.apply(this, args); - }; -} - -/** - * Based on http://www.php.net/manual/en/function.strftime.php - * @param {String} format - * @param {Number} timestamp - * @param {Boolean} capitalize - */ -dateFormat = function (format, timestamp, capitalize) { - if (!defined(timestamp) || isNaN(timestamp)) { - return 'Invalid date'; - } - format = pick(format, '%Y-%m-%d %H:%M:%S'); - - var date = new Date(timestamp - timezoneOffset), - key, // used in for constuct below - // get the basic time values - hours = date[getHours](), - day = date[getDay](), - dayOfMonth = date[getDate](), - month = date[getMonth](), - fullYear = date[getFullYear](), - lang = defaultOptions.lang, - langWeekdays = lang.weekdays, - - // List all format keys. Custom formats can be added from the outside. - replacements = extend({ - - // Day - 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon' - 'A': langWeekdays[day], // Long weekday, like 'Monday' - 'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31 - 'e': dayOfMonth, // Day of the month, 1 through 31 - - // Week (none implemented) - //'W': weekNumber(), - - // Month - 'b': lang.shortMonths[month], // Short month, like 'Jan' - 'B': lang.months[month], // Long month, like 'January' - 'm': pad(month + 1), // Two digit month number, 01 through 12 - - // Year - 'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009 - 'Y': fullYear, // Four digits year, like 2009 - - // Time - 'H': pad(hours), // Two digits hours in 24h format, 00 through 23 - 'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11 - 'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12 - 'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59 - 'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM - 'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM - 'S': pad(date.getSeconds()), // Two digits seconds, 00 through 59 - 'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby) - }, Highcharts.dateFormats); - - - // do the replaces - for (key in replacements) { - while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster - format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]); - } - } - - // Optionally capitalize the string and return - return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format; -}; - -/** - * Format a single variable. Similar to sprintf, without the % prefix. - */ -function formatSingle(format, val) { - var floatRegex = /f$/, - decRegex = /\.([0-9])/, - lang = defaultOptions.lang, - decimals; - - if (floatRegex.test(format)) { // float - decimals = format.match(decRegex); - decimals = decimals ? decimals[1] : -1; - val = numberFormat( - val, - decimals, - lang.decimalPoint, - format.indexOf(',') > -1 ? lang.thousandsSep : '' - ); - } else { - val = dateFormat(format, val); - } - return val; -} - -/** - * Format a string according to a subset of the rules of Python's String.format method. - */ -function format(str, ctx) { - var splitter = '{', - isInside = false, - segment, - valueAndFormat, - path, - i, - len, - ret = [], - val, - index; - - while ((index = str.indexOf(splitter)) !== -1) { - - segment = str.slice(0, index); - if (isInside) { // we're on the closing bracket looking back - - valueAndFormat = segment.split(':'); - path = valueAndFormat.shift().split('.'); // get first and leave format - len = path.length; - val = ctx; - - // Assign deeper paths - for (i = 0; i < len; i++) { - val = val[path[i]]; - } - - // Format the replacement - if (valueAndFormat.length) { - val = formatSingle(valueAndFormat.join(':'), val); - } - - // Push the result and advance the cursor - ret.push(val); - - } else { - ret.push(segment); - - } - str = str.slice(index + 1); // the rest - isInside = !isInside; // toggle - splitter = isInside ? '}' : '{'; // now look for next matching bracket - } - ret.push(str); - return ret.join(''); -} - -/** - * Get the magnitude of a number - */ -function getMagnitude(num) { - return math.pow(10, mathFloor(math.log(num) / math.LN10)); -} - -/** - * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5 - * @param {Number} interval - * @param {Array} multiples - * @param {Number} magnitude - * @param {Object} options - */ -function normalizeTickInterval(interval, multiples, magnitude, options) { - var normalized, i; - - // round to a tenfold of 1, 2, 2.5 or 5 - magnitude = pick(magnitude, 1); - normalized = interval / magnitude; - - // multiples for a linear scale - if (!multiples) { - multiples = [1, 2, 2.5, 5, 10]; - - // the allowDecimals option - if (options && options.allowDecimals === false) { - if (magnitude === 1) { - multiples = [1, 2, 5, 10]; - } else if (magnitude <= 0.1) { - multiples = [1 / magnitude]; - } - } - } - - // normalize the interval to the nearest multiple - for (i = 0; i < multiples.length; i++) { - interval = multiples[i]; - if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) { - break; - } - } - - // multiply back to the correct magnitude - interval *= magnitude; - - return interval; -} - - -/** - * Helper class that contains variuos counters that are local to the chart. - */ -function ChartCounters() { - this.color = 0; - this.symbol = 0; -} - -ChartCounters.prototype = { - /** - * Wraps the color counter if it reaches the specified length. - */ - wrapColor: function (length) { - if (this.color >= length) { - this.color = 0; - } - }, - - /** - * Wraps the symbol counter if it reaches the specified length. - */ - wrapSymbol: function (length) { - if (this.symbol >= length) { - this.symbol = 0; - } - } -}; - - -/** - * Utility method that sorts an object array and keeping the order of equal items. - * ECMA script standard does not specify the behaviour when items are equal. - */ -function stableSort(arr, sortFunction) { - var length = arr.length, - sortValue, - i; - - // Add index to each item - for (i = 0; i < length; i++) { - arr[i].ss_i = i; // stable sort index - } - - arr.sort(function (a, b) { - sortValue = sortFunction(a, b); - return sortValue === 0 ? a.ss_i - b.ss_i : sortValue; - }); - - // Remove index from items - for (i = 0; i < length; i++) { - delete arr[i].ss_i; // stable sort index - } -} - -/** - * Non-recursive method to find the lowest member of an array. Math.min raises a maximum - * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This - * method is slightly slower, but safe. - */ -function arrayMin(data) { - var i = data.length, - min = data[0]; - - while (i--) { - if (data[i] < min) { - min = data[i]; - } - } - return min; -} - -/** - * Non-recursive method to find the lowest member of an array. Math.min raises a maximum - * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This - * method is slightly slower, but safe. - */ -function arrayMax(data) { - var i = data.length, - max = data[0]; - - while (i--) { - if (data[i] > max) { - max = data[i]; - } - } - return max; -} - -/** - * Utility method that destroys any SVGElement or VMLElement that are properties on the given object. - * It loops all properties and invokes destroy if there is a destroy method. The property is - * then delete'ed. - * @param {Object} The object to destroy properties on - * @param {Object} Exception, do not destroy this property, only delete it. - */ -function destroyObjectProperties(obj, except) { - var n; - for (n in obj) { - // If the object is non-null and destroy is defined - if (obj[n] && obj[n] !== except && obj[n].destroy) { - // Invoke the destroy - obj[n].destroy(); - } - - // Delete the property from the object. - delete obj[n]; - } -} - - -/** - * Discard an element by moving it to the bin and delete - * @param {Object} The HTML node to discard - */ -function discardElement(element) { - // create a garbage bin element, not part of the DOM - if (!garbageBin) { - garbageBin = createElement(DIV); - } - - // move the node and empty bin - if (element) { - garbageBin.appendChild(element); - } - garbageBin.innerHTML = ''; -} - -/** - * Provide error messages for debugging, with links to online explanation - */ -function error(code, stop) { - var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code; - if (stop) { - throw msg; - } else if (win.console) { - console.log(msg); - } -} - -/** - * Fix JS round off float errors - * @param {Number} num - */ -function correctFloat(num) { - return parseFloat( - num.toPrecision(14) - ); -} - -/** - * Set the global animation to either a given value, or fall back to the - * given chart's animation option - * @param {Object} animation - * @param {Object} chart - */ -function setAnimation(animation, chart) { - globalAnimation = pick(animation, chart.animation); -} - -/** - * The time unit lookup - */ -/*jslint white: true*/ -timeUnits = hash( - MILLISECOND, 1, - SECOND, 1000, - MINUTE, 60000, - HOUR, 3600000, - DAY, 24 * 3600000, - WEEK, 7 * 24 * 3600000, - MONTH, 31 * 24 * 3600000, - YEAR, 31556952000 -); -/*jslint white: false*/ -/** - * Path interpolation algorithm used across adapters - */ -pathAnim = { - /** - * Prepare start and end values so that the path can be animated one to one - */ - init: function (elem, fromD, toD) { - fromD = fromD || ''; - var shift = elem.shift, - bezier = fromD.indexOf('C') > -1, - numParams = bezier ? 7 : 3, - endLength, - slice, - i, - start = fromD.split(' '), - end = [].concat(toD), // copy - startBaseLine, - endBaseLine, - sixify = function (arr) { // in splines make move points have six parameters like bezier curves - i = arr.length; - while (i--) { - if (arr[i] === M) { - arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]); - } - } - }; - - if (bezier) { - sixify(start); - sixify(end); - } - - // pull out the base lines before padding - if (elem.isArea) { - startBaseLine = start.splice(start.length - 6, 6); - endBaseLine = end.splice(end.length - 6, 6); - } - - // if shifting points, prepend a dummy point to the end path - if (shift <= end.length / numParams && start.length === end.length) { - while (shift--) { - end = [].concat(end).splice(0, numParams).concat(end); - } - } - elem.shift = 0; // reset for following animations - - // copy and append last point until the length matches the end length - if (start.length) { - endLength = end.length; - while (start.length < endLength) { - - //bezier && sixify(start); - slice = [].concat(start).splice(start.length - numParams, numParams); - if (bezier) { // disable first control point - slice[numParams - 6] = slice[numParams - 2]; - slice[numParams - 5] = slice[numParams - 1]; - } - start = start.concat(slice); - } - } - - if (startBaseLine) { // append the base lines for areas - start = start.concat(startBaseLine); - end = end.concat(endBaseLine); - } - return [start, end]; - }, - - /** - * Interpolate each value of the path and return the array - */ - step: function (start, end, pos, complete) { - var ret = [], - i = start.length, - startVal; - - if (pos === 1) { // land on the final path without adjustment points appended in the ends - ret = complete; - - } else if (i === end.length && pos < 1) { - while (i--) { - startVal = parseFloat(start[i]); - ret[i] = - isNaN(startVal) ? // a letter instruction like M or L - start[i] : - pos * (parseFloat(end[i] - startVal)) + startVal; - - } - } else { // if animation is finished or length not matching, land on right value - ret = end; - } - return ret; - } -}; - -(function ($) { - /** - * The default HighchartsAdapter for jQuery - */ - win.HighchartsAdapter = win.HighchartsAdapter || ($ && { - - /** - * Initialize the adapter by applying some extensions to jQuery - */ - init: function (pathAnim) { - - // extend the animate function to allow SVG animations - var Fx = $.fx, - Step = Fx.step, - dSetter, - Tween = $.Tween, - propHooks = Tween && Tween.propHooks, - opacityHook = $.cssHooks.opacity; - - /*jslint unparam: true*//* allow unused param x in this function */ - $.extend($.easing, { - easeOutQuad: function (x, t, b, c, d) { - return -c * (t /= d) * (t - 2) + b; - } - }); - /*jslint unparam: false*/ - - // extend some methods to check for elem.attr, which means it is a Highcharts SVG object - $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) { - var obj = Step, - base; - - // Handle different parent objects - if (fn === 'cur') { - obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype - - } else if (fn === '_default' && Tween) { // jQuery 1.8 model - obj = propHooks[fn]; - fn = 'set'; - } - - // Overwrite the method - base = obj[fn]; - if (base) { // step.width and step.height don't exist in jQuery < 1.7 - - // create the extended function replacement - obj[fn] = function (fx) { - - var elem; - - // Fx.prototype.cur does not use fx argument - fx = i ? fx : this; - - // Don't run animations on textual properties like align (#1821) - if (fx.prop === 'align') { - return; - } - - // shortcut - elem = fx.elem; - - // Fx.prototype.cur returns the current value. The other ones are setters - // and returning a value has no effect. - return elem.attr ? // is SVG element wrapper - elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method - base.apply(this, arguments); // use jQuery's built-in method - }; - } - }); - - // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+ - wrap(opacityHook, 'get', function (proceed, elem, computed) { - return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed); - }); - - - // Define the setter function for d (path definitions) - dSetter = function (fx) { - var elem = fx.elem, - ends; - - // Normally start and end should be set in state == 0, but sometimes, - // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped - // in these cases - if (!fx.started) { - ends = pathAnim.init(elem, elem.d, elem.toD); - fx.start = ends[0]; - fx.end = ends[1]; - fx.started = true; - } - - - // interpolate each value of the path - elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD)); - }; - - // jQuery 1.8 style - if (Tween) { - propHooks.d = { - set: dSetter - }; - // pre 1.8 - } else { - // animate paths - Step.d = dSetter; - } - - /** - * Utility for iterating over an array. Parameters are reversed compared to jQuery. - * @param {Array} arr - * @param {Function} fn - */ - this.each = Array.prototype.forEach ? - function (arr, fn) { // modern browsers - return Array.prototype.forEach.call(arr, fn); - - } : - function (arr, fn) { // legacy - var i = 0, - len = arr.length; - for (; i < len; i++) { - if (fn.call(arr[i], arr[i], i, arr) === false) { - return i; - } - } - }; - - /** - * Register Highcharts as a plugin in the respective framework - */ - $.fn.highcharts = function () { - var constr = 'Chart', // default constructor - args = arguments, - options, - ret, - chart; - - if (isString(args[0])) { - constr = args[0]; - args = Array.prototype.slice.call(args, 1); - } - options = args[0]; - - // Create the chart - if (options !== UNDEFINED) { - /*jslint unused:false*/ - options.chart = options.chart || {}; - options.chart.renderTo = this[0]; - chart = new Highcharts[constr](options, args[1]); - ret = this; - /*jslint unused:true*/ - } - - // When called without parameters or with the return argument, get a predefined chart - if (options === UNDEFINED) { - ret = charts[attr(this[0], 'data-highcharts-chart')]; - } - - return ret; - }; - - }, - - - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript: $.getScript, - - /** - * Return the index of an item in an array, or -1 if not found - */ - inArray: $.inArray, - - /** - * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method. - * @param {Object} elem The HTML element - * @param {String} method Which method to run on the wrapped element - */ - adapterRun: function (elem, method) { - return $(elem)[method](); - }, - - /** - * Filter an array - */ - grep: $.grep, - - /** - * Map an array - * @param {Array} arr - * @param {Function} fn - */ - map: function (arr, fn) { - //return jQuery.map(arr, fn); - var results = [], - i = 0, - len = arr.length; - for (; i < len; i++) { - results[i] = fn.call(arr[i], arr[i], i, arr); - } - return results; - - }, - - /** - * Get the position of an element relative to the top left of the page - */ - offset: function (el) { - return $(el).offset(); - }, - - /** - * Add an event listener - * @param {Object} el A HTML element or custom object - * @param {String} event The event type - * @param {Function} fn The event handler - */ - addEvent: function (el, event, fn) { - $(el).bind(event, fn); - }, - - /** - * Remove event added with addEvent - * @param {Object} el The object - * @param {String} eventType The event type. Leave blank to remove all events. - * @param {Function} handler The function to remove - */ - removeEvent: function (el, eventType, handler) { - // workaround for jQuery issue with unbinding custom events: - // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2 - var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent'; - if (doc[func] && el && !el[func]) { - el[func] = function () {}; - } - - $(el).unbind(eventType, handler); - }, - - /** - * Fire an event on a custom object - * @param {Object} el - * @param {String} type - * @param {Object} eventArguments - * @param {Function} defaultFunction - */ - fireEvent: function (el, type, eventArguments, defaultFunction) { - var event = $.Event(type), - detachedType = 'detached' + type, - defaultPrevented; - - // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts - // never uses these properties, Chrome includes them in the default click event and - // raises the warning when they are copied over in the extend statement below. - // - // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid - // testing if they are there (warning in chrome) the only option is to test if running IE. - if (!isIE && eventArguments) { - delete eventArguments.layerX; - delete eventArguments.layerY; - } - - extend(event, eventArguments); - - // Prevent jQuery from triggering the object method that is named the - // same as the event. For example, if the event is 'select', jQuery - // attempts calling el.select and it goes into a loop. - if (el[type]) { - el[detachedType] = el[type]; - el[type] = null; - } - - // Wrap preventDefault and stopPropagation in try/catch blocks in - // order to prevent JS errors when cancelling events on non-DOM - // objects. #615. - /*jslint unparam: true*/ - $.each(['preventDefault', 'stopPropagation'], function (i, fn) { - var base = event[fn]; - event[fn] = function () { - try { - base.call(event); - } catch (e) { - if (fn === 'preventDefault') { - defaultPrevented = true; - } - } - }; - }); - /*jslint unparam: false*/ - - // trigger it - $(el).trigger(event); - - // attach the method - if (el[detachedType]) { - el[type] = el[detachedType]; - el[detachedType] = null; - } - - if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) { - defaultFunction(event); - } - }, - - /** - * Extension method needed for MooTools - */ - washMouseEvent: function (e) { - var ret = e.originalEvent || e; - - // computed by jQuery, needed by IE8 - if (ret.pageX === UNDEFINED) { // #1236 - ret.pageX = e.pageX; - ret.pageY = e.pageY; - } - - return ret; - }, - - /** - * Animate a HTML element or SVG element wrapper - * @param {Object} el - * @param {Object} params - * @param {Object} options jQuery-like animation options: duration, easing, callback - */ - animate: function (el, params, options) { - var $el = $(el); - if (!el.style) { - el.style = {}; // #1881 - } - if (params.d) { - el.toD = params.d; // keep the array form for paths, used in $.fx.step.d - params.d = 1; // because in jQuery, animating to an array has a different meaning - } - - $el.stop(); - if (params.opacity !== UNDEFINED && el.attr) { - params.opacity += 'px'; // force jQuery to use same logic as width and height (#2161) - } - $el.animate(params, options); - - }, - /** - * Stop running animation - */ - stop: function (el) { - $(el).stop(); - } - }); -}(win.jQuery)); - - -// check for a custom HighchartsAdapter defined prior to this file -var globalAdapter = win.HighchartsAdapter, - adapter = globalAdapter || {}; - -// Initialize the adapter -if (globalAdapter) { - globalAdapter.init.call(globalAdapter, pathAnim); -} - - -// Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object -// and all the utility functions will be null. In that case they are populated by the -// default adapters below. -var adapterRun = adapter.adapterRun, - getScript = adapter.getScript, - inArray = adapter.inArray, - each = adapter.each, - grep = adapter.grep, - offset = adapter.offset, - map = adapter.map, - addEvent = adapter.addEvent, - removeEvent = adapter.removeEvent, - fireEvent = adapter.fireEvent, - washMouseEvent = adapter.washMouseEvent, - animate = adapter.animate, - stop = adapter.stop; - - - -/* **************************************************************************** - * Handle the options * - *****************************************************************************/ -var - -defaultLabelOptions = { - enabled: true, - // rotation: 0, - // align: 'center', - x: 0, - y: 15, - /*formatter: function () { - return this.value; - },*/ - style: { - color: '#666', - cursor: 'default', - fontSize: '11px' - } -}; - -defaultOptions = { - colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce', '#492970', - '#f28f43', '#77a1e5', '#c42525', '#a6c96a'], - symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'], - lang: { - loading: 'Loading...', - months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', - 'August', 'September', 'October', 'November', 'December'], - shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], - weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - decimalPoint: '.', - numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels - resetZoom: 'Reset zoom', - resetZoomTitle: 'Reset zoom level 1:1', - thousandsSep: ',' - }, - global: { - useUTC: true, - //timezoneOffset: 0, - canvasToolsURL: 'http://code.highcharts.com/3.0.9/modules/canvas-tools.js', - VMLRadialGradientURL: 'http://code.highcharts.com/3.0.9/gfx/vml-radial-gradient.png' - }, - chart: { - //animation: true, - //alignTicks: false, - //reflow: true, - //className: null, - //events: { load, selection }, - //margin: [null], - //marginTop: null, - //marginRight: null, - //marginBottom: null, - //marginLeft: null, - borderColor: '#4572A7', - //borderWidth: 0, - borderRadius: 5, - defaultSeriesType: 'line', - ignoreHiddenSeries: true, - //inverted: false, - //shadow: false, - spacing: [10, 10, 15, 10], - //spacingTop: 10, - //spacingRight: 10, - //spacingBottom: 15, - //spacingLeft: 10, - style: { - fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font - fontSize: '12px' - }, - backgroundColor: '#FFFFFF', - //plotBackgroundColor: null, - plotBorderColor: '#C0C0C0', - //plotBorderWidth: 0, - //plotShadow: false, - //zoomType: '' - resetZoomButton: { - theme: { - zIndex: 20 - }, - position: { - align: 'right', - x: -10, - //verticalAlign: 'top', - y: 10 - } - // relativeTo: 'plot' - } - }, - title: { - text: 'Chart title', - align: 'center', - // floating: false, - margin: 15, - // x: 0, - // verticalAlign: 'top', - // y: null, - style: { - color: '#274b6d',//#3E576F', - fontSize: '16px' - } - - }, - subtitle: { - text: '', - align: 'center', - // floating: false - // x: 0, - // verticalAlign: 'top', - // y: null, - style: { - color: '#4d759e' - } - }, - - plotOptions: { - line: { // base series options - allowPointSelect: false, - showCheckbox: false, - animation: { - duration: 1000 - }, - //connectNulls: false, - //cursor: 'default', - //clip: true, - //dashStyle: null, - //enableMouseTracking: true, - events: {}, - //legendIndex: 0, - //linecap: 'round', - lineWidth: 2, - //shadow: false, - // stacking: null, - marker: { - enabled: true, - //symbol: null, - lineWidth: 0, - radius: 4, - lineColor: '#FFFFFF', - //fillColor: null, - states: { // states for a single point - hover: { - enabled: true - //radius: base + 2 - }, - select: { - fillColor: '#FFFFFF', - lineColor: '#000000', - lineWidth: 2 - } - } - }, - point: { - events: {} - }, - dataLabels: merge(defaultLabelOptions, { - align: 'center', - enabled: false, - formatter: function () { - return this.y === null ? '' : numberFormat(this.y, -1); - }, - verticalAlign: 'bottom', // above singular point - y: 0 - // backgroundColor: undefined, - // borderColor: undefined, - // borderRadius: undefined, - // borderWidth: undefined, - // padding: 3, - // shadow: false - }), - cropThreshold: 300, // draw points outside the plot area when the number of points is less than this - pointRange: 0, - //pointStart: 0, - //pointInterval: 1, - //showInLegend: null, // auto: true for standalone series, false for linked series - states: { // states for the entire series - hover: { - //enabled: false, - //lineWidth: base + 1, - marker: { - // lineWidth: base + 1, - // radius: base + 1 - } - }, - select: { - marker: {} - } - }, - stickyTracking: true, - //tooltip: { - //pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b>' - //valueDecimals: null, - //xDateFormat: '%A, %b %e, %Y', - //valuePrefix: '', - //ySuffix: '' - //} - turboThreshold: 1000 - // zIndex: null - } - }, - labels: { - //items: [], - style: { - //font: defaultFont, - position: ABSOLUTE, - color: '#3E576F' - } - }, - legend: { - enabled: true, - align: 'center', - //floating: false, - layout: 'horizontal', - labelFormatter: function () { - return this.name; - }, - borderWidth: 1, - borderColor: '#909090', - borderRadius: 5, - navigation: { - // animation: true, - activeColor: '#274b6d', - // arrowSize: 12 - inactiveColor: '#CCC' - // style: {} // text styles - }, - // margin: 10, - // reversed: false, - shadow: false, - // backgroundColor: null, - /*style: { - padding: '5px' - },*/ - itemStyle: { - cursor: 'pointer', - color: '#274b6d', - fontSize: '12px' - }, - itemHoverStyle: { - //cursor: 'pointer', removed as of #601 - color: '#000' - }, - itemHiddenStyle: { - color: '#CCC' - }, - itemCheckboxStyle: { - position: ABSOLUTE, - width: '13px', // for IE precision - height: '13px' - }, - // itemWidth: undefined, - // symbolWidth: 16, - symbolPadding: 5, - verticalAlign: 'bottom', - // width: undefined, - x: 0, - y: 0, - title: { - //text: null, - style: { - fontWeight: 'bold' - } - } - }, - - loading: { - // hideDuration: 100, - labelStyle: { - fontWeight: 'bold', - position: RELATIVE, - top: '1em' - }, - // showDuration: 0, - style: { - position: ABSOLUTE, - backgroundColor: 'white', - opacity: 0.5, - textAlign: 'center' - } - }, - - tooltip: { - enabled: true, - animation: hasSVG, - //crosshairs: null, - backgroundColor: 'rgba(255, 255, 255, .85)', - borderWidth: 1, - borderRadius: 3, - dateTimeLabelFormats: { - millisecond: '%A, %b %e, %H:%M:%S.%L', - second: '%A, %b %e, %H:%M:%S', - minute: '%A, %b %e, %H:%M', - hour: '%A, %b %e, %H:%M', - day: '%A, %b %e, %Y', - week: 'Week from %A, %b %e, %Y', - month: '%B %Y', - year: '%Y' - }, - //formatter: defaultFormatter, - headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>', - pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>', - shadow: true, - //shared: false, - snap: isTouchDevice ? 25 : 10, - style: { - color: '#333333', - cursor: 'default', - fontSize: '12px', - padding: '8px', - whiteSpace: 'nowrap' - } - //xDateFormat: '%A, %b %e, %Y', - //valueDecimals: null, - //valuePrefix: '', - //valueSuffix: '' - }, - - credits: { - enabled: true, - text: 'Highcharts.com', - href: 'http://www.highcharts.com', - position: { - align: 'right', - x: -10, - verticalAlign: 'bottom', - y: -5 - }, - style: { - cursor: 'pointer', - color: '#909090', - fontSize: '9px' - } - } -}; - - - - -// Series defaults -var defaultPlotOptions = defaultOptions.plotOptions, - defaultSeriesOptions = defaultPlotOptions.line; - -// set the default time methods -setTimeMethods(); - - - -/** - * Set the time methods globally based on the useUTC option. Time method can be either - * local time or UTC (default). - */ -function setTimeMethods() { - var useUTC = defaultOptions.global.useUTC, - GET = useUTC ? 'getUTC' : 'get', - SET = useUTC ? 'setUTC' : 'set'; - - - timezoneOffset = ((useUTC && defaultOptions.global.timezoneOffset) || 0) * 60000; - makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) { - return new Date( - year, - month, - pick(date, 1), - pick(hours, 0), - pick(minutes, 0), - pick(seconds, 0) - ).getTime(); - }; - getMinutes = GET + 'Minutes'; - getHours = GET + 'Hours'; - getDay = GET + 'Day'; - getDate = GET + 'Date'; - getMonth = GET + 'Month'; - getFullYear = GET + 'FullYear'; - setMinutes = SET + 'Minutes'; - setHours = SET + 'Hours'; - setDate = SET + 'Date'; - setMonth = SET + 'Month'; - setFullYear = SET + 'FullYear'; - -} - -/** - * Merge the default options with custom options and return the new options structure - * @param {Object} options The new custom options - */ -function setOptions(options) { - - // Copy in the default options - defaultOptions = merge(true, defaultOptions, options); - - // Apply UTC - setTimeMethods(); - - return defaultOptions; -} - -/** - * Get the updated default options. Until 3.0.7, merely exposing defaultOptions for outside modules - * wasn't enough because the setOptions method created a new object. - */ -function getOptions() { - return defaultOptions; -} - - -/** - * Handle color operations. The object methods are chainable. - * @param {String} input The input color in either rbga or hex format - */ -var rgbaRegEx = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/, - hexRegEx = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, - rgbRegEx = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/; - -var Color = function (input) { - // declare variables - var rgba = [], result, stops; - - /** - * Parse the input color to rgba array - * @param {String} input - */ - function init(input) { - - // Gradients - if (input && input.stops) { - stops = map(input.stops, function (stop) { - return Color(stop[1]); - }); - - // Solid colors - } else { - // rgba - result = rgbaRegEx.exec(input); - if (result) { - rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)]; - } else { - // hex - result = hexRegEx.exec(input); - if (result) { - rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1]; - } else { - // rgb - result = rgbRegEx.exec(input); - if (result) { - rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1]; - } - } - } - } - - } - /** - * Return the color a specified format - * @param {String} format - */ - function get(format) { - var ret; - - if (stops) { - ret = merge(input); - ret.stops = [].concat(ret.stops); - each(stops, function (stop, i) { - ret.stops[i] = [ret.stops[i][0], stop.get(format)]; - }); - - // it's NaN if gradient colors on a column chart - } else if (rgba && !isNaN(rgba[0])) { - if (format === 'rgb') { - ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')'; - } else if (format === 'a') { - ret = rgba[3]; - } else { - ret = 'rgba(' + rgba.join(',') + ')'; - } - } else { - ret = input; - } - return ret; - } - - /** - * Brighten the color - * @param {Number} alpha - */ - function brighten(alpha) { - if (stops) { - each(stops, function (stop) { - stop.brighten(alpha); - }); - - } else if (isNumber(alpha) && alpha !== 0) { - var i; - for (i = 0; i < 3; i++) { - rgba[i] += pInt(alpha * 255); - - if (rgba[i] < 0) { - rgba[i] = 0; - } - if (rgba[i] > 255) { - rgba[i] = 255; - } - } - } - return this; - } - /** - * Set the color's opacity to a given alpha value - * @param {Number} alpha - */ - function setOpacity(alpha) { - rgba[3] = alpha; - return this; - } - - // initialize: parse the input - init(input); - - // public methods - return { - get: get, - brighten: brighten, - rgba: rgba, - setOpacity: setOpacity - }; -}; - - -/** - * A wrapper object for SVG elements - */ -function SVGElement() {} - -SVGElement.prototype = { - /** - * Initialize the SVG renderer - * @param {Object} renderer - * @param {String} nodeName - */ - init: function (renderer, nodeName) { - var wrapper = this; - wrapper.element = nodeName === 'span' ? - createElement(nodeName) : - doc.createElementNS(SVG_NS, nodeName); - wrapper.renderer = renderer; - /** - * A collection of attribute setters. These methods, if defined, are called right before a certain - * attribute is set on an element wrapper. Returning false prevents the default attribute - * setter to run. Returning a value causes the default setter to set that value. Used in - * Renderer.label. - */ - wrapper.attrSetters = {}; - }, - /** - * Default base for animation - */ - opacity: 1, - /** - * Animate a given attribute - * @param {Object} params - * @param {Number} options The same options as in jQuery animation - * @param {Function} complete Function to perform at the end of animation - */ - animate: function (params, options, complete) { - var animOptions = pick(options, globalAnimation, true); - stop(this); // stop regardless of animation actually running, or reverting to .attr (#607) - if (animOptions) { - animOptions = merge(animOptions); - if (complete) { // allows using a callback with the global animation without overwriting it - animOptions.complete = complete; - } - animate(this, params, animOptions); - } else { - this.attr(params); - if (complete) { - complete(); - } - } - }, - /** - * Set or get a given attribute - * @param {Object|String} hash - * @param {Mixed|Undefined} val - */ - attr: function (hash, val) { - var wrapper = this, - key, - value, - result, - i, - child, - element = wrapper.element, - nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text" - renderer = wrapper.renderer, - skipAttr, - titleNode, - attrSetters = wrapper.attrSetters, - shadows = wrapper.shadows, - hasSetSymbolSize, - doTransform, - ret = wrapper; - - // single key-value pair - if (isString(hash) && defined(val)) { - key = hash; - hash = {}; - hash[key] = val; - } - - // used as a getter: first argument is a string, second is undefined - if (isString(hash)) { - key = hash; - if (nodeName === 'circle') { - key = { x: 'cx', y: 'cy' }[key] || key; - } else if (key === 'strokeWidth') { - key = 'stroke-width'; - } - ret = attr(element, key) || wrapper[key] || 0; - if (key !== 'd' && key !== 'visibility' && key !== 'fill') { // 'd' is string in animation step - ret = parseFloat(ret); - } - - // setter - } else { - - for (key in hash) { - skipAttr = false; // reset - value = hash[key]; - - // check for a specific attribute setter - result = attrSetters[key] && attrSetters[key].call(wrapper, value, key); - - if (result !== false) { - if (result !== UNDEFINED) { - value = result; // the attribute setter has returned a new value to set - } - - - // paths - if (key === 'd') { - if (value && value.join) { // join path - value = value.join(' '); - } - if (/(NaN| {2}|^$)/.test(value)) { - value = 'M 0 0'; - } - //wrapper.d = value; // shortcut for animations - - // update child tspans x values - } else if (key === 'x' && nodeName === 'text') { - for (i = 0; i < element.childNodes.length; i++) { - child = element.childNodes[i]; - // if the x values are equal, the tspan represents a linebreak - if (attr(child, 'x') === attr(element, 'x')) { - //child.setAttribute('x', value); - attr(child, 'x', value); - } - } - - } else if (wrapper.rotation && (key === 'x' || key === 'y')) { - doTransform = true; - - // apply gradients - } else if (key === 'fill') { - value = renderer.color(value, element, key); - - // circle x and y - } else if (nodeName === 'circle' && (key === 'x' || key === 'y')) { - key = { x: 'cx', y: 'cy' }[key] || key; - - // rectangle border radius - } else if (nodeName === 'rect' && key === 'r') { - attr(element, { - rx: value, - ry: value - }); - skipAttr = true; - - // translation and text rotation - } else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || - key === 'verticalAlign' || key === 'scaleX' || key === 'scaleY') { - doTransform = true; - skipAttr = true; - - // apply opacity as subnode (required by legacy WebKit and Batik) - } else if (key === 'stroke') { - value = renderer.color(value, element, key); - - // emulate VML's dashstyle implementation - } else if (key === 'dashstyle') { - key = 'stroke-dasharray'; - value = value && value.toLowerCase(); - if (value === 'solid') { - value = NONE; - } else if (value) { - value = value - .replace('shortdashdotdot', '3,1,1,1,1,1,') - .replace('shortdashdot', '3,1,1,1') - .replace('shortdot', '1,1,') - .replace('shortdash', '3,1,') - .replace('longdash', '8,3,') - .replace(/dot/g, '1,3,') - .replace('dash', '4,3,') - .replace(/,$/, '') - .split(','); // ending comma - - i = value.length; - while (i--) { - value[i] = pInt(value[i]) * pick(hash['stroke-width'], wrapper['stroke-width']); - } - value = value.join(','); - } - - // IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2 - // is unable to cast them. Test again with final IE9. - } else if (key === 'width') { - value = pInt(value); - - // Text alignment - } else if (key === 'align') { - key = 'text-anchor'; - value = { left: 'start', center: 'middle', right: 'end' }[value]; - - // Title requires a subnode, #431 - } else if (key === 'title') { - titleNode = element.getElementsByTagName('title')[0]; - if (!titleNode) { - titleNode = doc.createElementNS(SVG_NS, 'title'); - element.appendChild(titleNode); - } - titleNode.textContent = value; - } - - // jQuery animate changes case - if (key === 'strokeWidth') { - key = 'stroke-width'; - } - - // In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke- - // width is 0. #1369 - if (key === 'stroke-width' || key === 'stroke') { - wrapper[key] = value; - // Only apply the stroke attribute if the stroke width is defined and larger than 0 - if (wrapper.stroke && wrapper['stroke-width']) { - attr(element, 'stroke', wrapper.stroke); - attr(element, 'stroke-width', wrapper['stroke-width']); - wrapper.hasStroke = true; - } else if (key === 'stroke-width' && value === 0 && wrapper.hasStroke) { - element.removeAttribute('stroke'); - wrapper.hasStroke = false; - } - skipAttr = true; - } - - // symbols - if (wrapper.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) { - - - if (!hasSetSymbolSize) { - wrapper.symbolAttr(hash); - hasSetSymbolSize = true; - } - skipAttr = true; - } - - // let the shadow follow the main element - if (shadows && /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(key)) { - i = shadows.length; - while (i--) { - attr( - shadows[i], - key, - key === 'height' ? - mathMax(value - (shadows[i].cutHeight || 0), 0) : - value - ); - } - } - - // validate heights - if ((key === 'width' || key === 'height') && nodeName === 'rect' && value < 0) { - value = 0; - } - - // Record for animation and quick access without polling the DOM - wrapper[key] = value; - - - if (key === 'text') { - // Delete bBox memo when the text changes - if (value !== wrapper.textStr) { - delete wrapper.bBox; - } - wrapper.textStr = value; - if (wrapper.added) { - renderer.buildText(wrapper); - } - } else if (!skipAttr) { - attr(element, key, value); - } - - } - - } - - // Update transform. Do this outside the loop to prevent redundant updating for batch setting - // of attributes. - if (doTransform) { - wrapper.updateTransform(); - } - - } - - return ret; - }, - - - /** - * Add a class name to an element - */ - addClass: function (className) { - var element = this.element, - currentClassName = attr(element, 'class') || ''; - - if (currentClassName.indexOf(className) === -1) { - attr(element, 'class', currentClassName + ' ' + className); - } - return this; - }, - /* hasClass and removeClass are not (yet) needed - hasClass: function (className) { - return attr(this.element, 'class').indexOf(className) !== -1; - }, - removeClass: function (className) { - attr(this.element, 'class', attr(this.element, 'class').replace(className, '')); - return this; - }, - */ - - /** - * If one of the symbol size affecting parameters are changed, - * check all the others only once for each call to an element's - * .attr() method - * @param {Object} hash - */ - symbolAttr: function (hash) { - var wrapper = this; - - each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) { - wrapper[key] = pick(hash[key], wrapper[key]); - }); - - wrapper.attr({ - d: wrapper.renderer.symbols[wrapper.symbolName]( - wrapper.x, - wrapper.y, - wrapper.width, - wrapper.height, - wrapper - ) - }); - }, - - /** - * Apply a clipping path to this object - * @param {String} id - */ - clip: function (clipRect) { - return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE); - }, - - /** - * Calculate the coordinates needed for drawing a rectangle crisply and return the - * calculated attributes - * @param {Number} strokeWidth - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - */ - crisp: function (strokeWidth, x, y, width, height) { - - var wrapper = this, - key, - attribs = {}, - values = {}, - normalizer; - - strokeWidth = strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0; - normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors - - // normalize for crisp edges - values.x = mathFloor(x || wrapper.x || 0) + normalizer; - values.y = mathFloor(y || wrapper.y || 0) + normalizer; - values.width = mathFloor((width || wrapper.width || 0) - 2 * normalizer); - values.height = mathFloor((height || wrapper.height || 0) - 2 * normalizer); - values.strokeWidth = strokeWidth; - - for (key in values) { - if (wrapper[key] !== values[key]) { // only set attribute if changed - wrapper[key] = attribs[key] = values[key]; - } - } - - return attribs; - }, - - /** - * Set styles for the element - * @param {Object} styles - */ - css: function (styles) { - /*jslint unparam: true*//* allow unused param a in the regexp function below */ - var elemWrapper = this, - elem = elemWrapper.element, - textWidth = elemWrapper.textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text' && pInt(styles.width), - n, - serializedCss = '', - hyphenate = function (a, b) { return '-' + b.toLowerCase(); }; - /*jslint unparam: false*/ - - // convert legacy - if (styles && styles.color) { - styles.fill = styles.color; - } - - // Merge the new styles with the old ones - styles = extend( - elemWrapper.styles, - styles - ); - - // store object - elemWrapper.styles = styles; - - if (textWidth) { - delete styles.width; - } - - // serialize and set style attribute - if (isIE && !hasSVG) { - css(elemWrapper.element, styles); - } else { - for (n in styles) { - serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';'; - } - attr(elem, 'style', serializedCss); // #1881 - } - - - // re-build text - if (textWidth && elemWrapper.added) { - elemWrapper.renderer.buildText(elemWrapper); - } - - return elemWrapper; - }, - - /** - * Add an event listener - * @param {String} eventType - * @param {Function} handler - */ - on: function (eventType, handler) { - var svgElement = this, - element = svgElement.element; - - // touch - if (hasTouch && eventType === 'click') { - element.ontouchstart = function (e) { - svgElement.touchEventFired = Date.now(); - e.preventDefault(); - handler.call(element, e); - }; - element.onclick = function (e) { - if (userAgent.indexOf('Android') === -1 || Date.now() - (svgElement.touchEventFired || 0) > 1100) { // #2269 - handler.call(element, e); - } - }; - } else { - // simplest possible event model for internal use - element['on' + eventType] = handler; - } - return this; - }, - - /** - * Set the coordinates needed to draw a consistent radial gradient across - * pie slices regardless of positioning inside the chart. The format is - * [centerX, centerY, diameter] in pixels. - */ - setRadialReference: function (coordinates) { - this.element.radialReference = coordinates; - return this; - }, - - /** - * Move an object and its children by x and y values - * @param {Number} x - * @param {Number} y - */ - translate: function (x, y) { - return this.attr({ - translateX: x, - translateY: y - }); - }, - - /** - * Invert a group, rotate and flip - */ - invert: function () { - var wrapper = this; - wrapper.inverted = true; - wrapper.updateTransform(); - return wrapper; - }, - - /** - * Private method to update the transform attribute based on internal - * properties - */ - updateTransform: function () { - var wrapper = this, - translateX = wrapper.translateX || 0, - translateY = wrapper.translateY || 0, - scaleX = wrapper.scaleX, - scaleY = wrapper.scaleY, - inverted = wrapper.inverted, - rotation = wrapper.rotation, - transform; - - // flipping affects translate as adjustment for flipping around the group's axis - if (inverted) { - translateX += wrapper.attr('width'); - translateY += wrapper.attr('height'); - } - - // Apply translate. Nearly all transformed elements have translation, so instead - // of checking for translate = 0, do it always (#1767, #1846). - transform = ['translate(' + translateX + ',' + translateY + ')']; - - // apply rotation - if (inverted) { - transform.push('rotate(90) scale(-1,1)'); - } else if (rotation) { // text rotation - transform.push('rotate(' + rotation + ' ' + (wrapper.x || 0) + ' ' + (wrapper.y || 0) + ')'); - } - - // apply scale - if (defined(scaleX) || defined(scaleY)) { - transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')'); - } - - if (transform.length) { - attr(wrapper.element, 'transform', transform.join(' ')); - } - }, - /** - * Bring the element to the front - */ - toFront: function () { - var element = this.element; - element.parentNode.appendChild(element); - return this; - }, - - - /** - * Break down alignment options like align, verticalAlign, x and y - * to x and y relative to the chart. - * - * @param {Object} alignOptions - * @param {Boolean} alignByTranslate - * @param {String[Object} box The box to align to, needs a width and height. When the - * box is a string, it refers to an object in the Renderer. For example, when - * box is 'spacingBox', it refers to Renderer.spacingBox which holds width, height - * x and y properties. - * - */ - align: function (alignOptions, alignByTranslate, box) { - var align, - vAlign, - x, - y, - attribs = {}, - alignTo, - renderer = this.renderer, - alignedObjects = renderer.alignedObjects; - - // First call on instanciate - if (alignOptions) { - this.alignOptions = alignOptions; - this.alignByTranslate = alignByTranslate; - if (!box || isString(box)) { // boxes other than renderer handle this internally - this.alignTo = alignTo = box || 'renderer'; - erase(alignedObjects, this); // prevent duplicates, like legendGroup after resize - alignedObjects.push(this); - box = null; // reassign it below - } - - // When called on resize, no arguments are supplied - } else { - alignOptions = this.alignOptions; - alignByTranslate = this.alignByTranslate; - alignTo = this.alignTo; - } - - box = pick(box, renderer[alignTo], renderer); - - // Assign variables - align = alignOptions.align; - vAlign = alignOptions.verticalAlign; - x = (box.x || 0) + (alignOptions.x || 0); // default: left align - y = (box.y || 0) + (alignOptions.y || 0); // default: top align - - // Align - if (align === 'right' || align === 'center') { - x += (box.width - (alignOptions.width || 0)) / - { right: 1, center: 2 }[align]; - } - attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x); - - - // Vertical align - if (vAlign === 'bottom' || vAlign === 'middle') { - y += (box.height - (alignOptions.height || 0)) / - ({ bottom: 1, middle: 2 }[vAlign] || 1); - - } - attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y); - - // Animate only if already placed - this[this.placed ? 'animate' : 'attr'](attribs); - this.placed = true; - this.alignAttr = attribs; - - return this; - }, - - /** - * Get the bounding box (width, height, x and y) for the element - */ - getBBox: function () { - var wrapper = this, - bBox = wrapper.bBox, - renderer = wrapper.renderer, - width, - height, - rotation = wrapper.rotation, - element = wrapper.element, - styles = wrapper.styles, - rad = rotation * deg2rad, - textStr = wrapper.textStr, - numKey; - - // Since numbers are monospaced, and numerical labels appear a lot in a chart, - // we assume that a label of n characters has the same bounding box as others - // of the same length. - if (textStr === '' || numRegex.test(textStr)) { - numKey = textStr.length + '|' + styles.fontSize + '|' + styles.fontFamily; - bBox = renderer.cache[numKey]; - } - - // No cache found - if (!bBox) { - - // SVG elements - if (element.namespaceURI === SVG_NS || renderer.forExport) { - try { // Fails in Firefox if the container has display: none. - - bBox = element.getBBox ? - // SVG: use extend because IE9 is not allowed to change width and height in case - // of rotation (below) - extend({}, element.getBBox()) : - // Canvas renderer and legacy IE in export mode - { - width: element.offsetWidth, - height: element.offsetHeight - }; - } catch (e) {} - - // If the bBox is not set, the try-catch block above failed. The other condition - // is for Opera that returns a width of -Infinity on hidden elements. - if (!bBox || bBox.width < 0) { - bBox = { width: 0, height: 0 }; - } - - - // VML Renderer or useHTML within SVG - } else { - - bBox = wrapper.htmlGetBBox(); - - } - - // True SVG elements as well as HTML elements in modern browsers using the .useHTML option - // need to compensated for rotation - if (renderer.isSVG) { - width = bBox.width; - height = bBox.height; - - // Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669, #2568) - if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '16.9') { - bBox.height = height = 14; - } - - // Adjust for rotated text - if (rotation) { - bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad)); - bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad)); - } - } - - // Cache it - wrapper.bBox = bBox; - if (numKey) { - renderer.cache[numKey] = bBox; - } - } - return bBox; - }, - - /** - * Show the element - */ - show: function () { - return this.attr({ visibility: VISIBLE }); - }, - - /** - * Hide the element - */ - hide: function () { - return this.attr({ visibility: HIDDEN }); - }, - - fadeOut: function (duration) { - var elemWrapper = this; - elemWrapper.animate({ - opacity: 0 - }, { - duration: duration || 150, - complete: function () { - elemWrapper.hide(); - } - }); - }, - - /** - * Add the element - * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined - * to append the element to the renderer.box. - */ - add: function (parent) { - - var renderer = this.renderer, - parentWrapper = parent || renderer, - parentNode = parentWrapper.element || renderer.box, - childNodes = parentNode.childNodes, - element = this.element, - zIndex = attr(element, 'zIndex'), - otherElement, - otherZIndex, - i, - inserted; - - if (parent) { - this.parentGroup = parent; - } - - // mark as inverted - this.parentInverted = parent && parent.inverted; - - // build formatted text - if (this.textStr !== undefined) { - renderer.buildText(this); - } - - // mark the container as having z indexed children - if (zIndex) { - parentWrapper.handleZ = true; - zIndex = pInt(zIndex); - } - - // insert according to this and other elements' zIndex - if (parentWrapper.handleZ) { // this element or any of its siblings has a z index - for (i = 0; i < childNodes.length; i++) { - otherElement = childNodes[i]; - otherZIndex = attr(otherElement, 'zIndex'); - if (otherElement !== element && ( - // insert before the first element with a higher zIndex - pInt(otherZIndex) > zIndex || - // if no zIndex given, insert before the first element with a zIndex - (!defined(zIndex) && defined(otherZIndex)) - - )) { - parentNode.insertBefore(element, otherElement); - inserted = true; - break; - } - } - } - - // default: append at the end - if (!inserted) { - parentNode.appendChild(element); - } - - // mark as added - this.added = true; - - // fire an event for internal hooks - fireEvent(this, 'add'); - - return this; - }, - - /** - * Removes a child either by removeChild or move to garbageBin. - * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not. - */ - safeRemoveChild: function (element) { - var parentNode = element.parentNode; - if (parentNode) { - parentNode.removeChild(element); - } - }, - - /** - * Destroy the element and element wrapper - */ - destroy: function () { - var wrapper = this, - element = wrapper.element || {}, - shadows = wrapper.shadows, - parentToClean = wrapper.renderer.isSVG && element.nodeName === 'SPAN' && wrapper.parentGroup, - grandParent, - key, - i; - - // remove events - element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null; - stop(wrapper); // stop running animations - - if (wrapper.clipPath) { - wrapper.clipPath = wrapper.clipPath.destroy(); - } - - // Destroy stops in case this is a gradient object - if (wrapper.stops) { - for (i = 0; i < wrapper.stops.length; i++) { - wrapper.stops[i] = wrapper.stops[i].destroy(); - } - wrapper.stops = null; - } - - // remove element - wrapper.safeRemoveChild(element); - - // destroy shadows - if (shadows) { - each(shadows, function (shadow) { - wrapper.safeRemoveChild(shadow); - }); - } - - // In case of useHTML, clean up empty containers emulating SVG groups (#1960, #2393). - while (parentToClean && parentToClean.div.childNodes.length === 0) { - grandParent = parentToClean.parentGroup; - wrapper.safeRemoveChild(parentToClean.div); - delete parentToClean.div; - parentToClean = grandParent; - } - - // remove from alignObjects - if (wrapper.alignTo) { - erase(wrapper.renderer.alignedObjects, wrapper); - } - - for (key in wrapper) { - delete wrapper[key]; - } - - return null; - }, - - /** - * Add a shadow to the element. Must be done after the element is added to the DOM - * @param {Boolean|Object} shadowOptions - */ - shadow: function (shadowOptions, group, cutOff) { - var shadows = [], - i, - shadow, - element = this.element, - strokeWidth, - shadowWidth, - shadowElementOpacity, - - // compensate for inverted plot area - transform; - - - if (shadowOptions) { - shadowWidth = pick(shadowOptions.width, 3); - shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth; - transform = this.parentInverted ? - '(-1,-1)' : - '(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')'; - for (i = 1; i <= shadowWidth; i++) { - shadow = element.cloneNode(0); - strokeWidth = (shadowWidth * 2) + 1 - (2 * i); - attr(shadow, { - 'isShadow': 'true', - 'stroke': shadowOptions.color || 'black', - 'stroke-opacity': shadowElementOpacity * i, - 'stroke-width': strokeWidth, - 'transform': 'translate' + transform, - 'fill': NONE - }); - if (cutOff) { - attr(shadow, 'height', mathMax(attr(shadow, 'height') - strokeWidth, 0)); - shadow.cutHeight = strokeWidth; - } - - if (group) { - group.element.appendChild(shadow); - } else { - element.parentNode.insertBefore(shadow, element); - } - - shadows.push(shadow); - } - - this.shadows = shadows; - } - return this; - - } -}; - - -/** - * The default SVG renderer - */ -var SVGRenderer = function () { - this.init.apply(this, arguments); -}; -SVGRenderer.prototype = { - Element: SVGElement, - - /** - * Initialize the SVGRenderer - * @param {Object} container - * @param {Number} width - * @param {Number} height - * @param {Boolean} forExport - */ - init: function (container, width, height, forExport) { - var renderer = this, - loc = location, - boxWrapper, - element, - desc; - - boxWrapper = renderer.createElement('svg') - .attr({ - version: '1.1' - }); - element = boxWrapper.element; - container.appendChild(element); - - // For browsers other than IE, add the namespace attribute (#1978) - if (container.innerHTML.indexOf('xmlns') === -1) { - attr(element, 'xmlns', SVG_NS); - } - - // object properties - renderer.isSVG = true; - renderer.box = element; - renderer.boxWrapper = boxWrapper; - renderer.alignedObjects = []; - - // Page url used for internal references. #24, #672, #1070 - renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ? - loc.href - .replace(/#.*?$/, '') // remove the hash - .replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes - .replace(/ /g, '%20') : // replace spaces (needed for Safari only) - ''; - - // Add description - desc = this.createElement('desc').add(); - desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION)); - - - renderer.defs = this.createElement('defs').add(); - renderer.forExport = forExport; - renderer.gradients = {}; // Object where gradient SvgElements are stored - renderer.cache = {}; // Cache for numerical bounding boxes - - renderer.setSize(width, height, false); - - - - // Issue 110 workaround: - // In Firefox, if a div is positioned by percentage, its pixel position may land - // between pixels. The container itself doesn't display this, but an SVG element - // inside this container will be drawn at subpixel precision. In order to draw - // sharp lines, this must be compensated for. This doesn't seem to work inside - // iframes though (like in jsFiddle). - var subPixelFix, rect; - if (isFirefox && container.getBoundingClientRect) { - renderer.subPixelFix = subPixelFix = function () { - css(container, { left: 0, top: 0 }); - rect = container.getBoundingClientRect(); - css(container, { - left: (mathCeil(rect.left) - rect.left) + PX, - top: (mathCeil(rect.top) - rect.top) + PX - }); - }; - - // run the fix now - subPixelFix(); - - // run it on resize - addEvent(win, 'resize', subPixelFix); - } - }, - - /** - * Detect whether the renderer is hidden. This happens when one of the parent elements - * has display: none. #608. - */ - isHidden: function () { - return !this.boxWrapper.getBBox().width; - }, - - /** - * Destroys the renderer and its allocated members. - */ - destroy: function () { - var renderer = this, - rendererDefs = renderer.defs; - renderer.box = null; - renderer.boxWrapper = renderer.boxWrapper.destroy(); - - // Call destroy on all gradient elements - destroyObjectProperties(renderer.gradients || {}); - renderer.gradients = null; - - // Defs are null in VMLRenderer - // Otherwise, destroy them here. - if (rendererDefs) { - renderer.defs = rendererDefs.destroy(); - } - - // Remove sub pixel fix handler - // We need to check that there is a handler, otherwise all functions that are registered for event 'resize' are removed - // See issue #982 - if (renderer.subPixelFix) { - removeEvent(win, 'resize', renderer.subPixelFix); - } - - renderer.alignedObjects = null; - - return null; - }, - - /** - * Create a wrapper for an SVG element - * @param {Object} nodeName - */ - createElement: function (nodeName) { - var wrapper = new this.Element(); - wrapper.init(this, nodeName); - return wrapper; - }, - - /** - * Dummy function for use in canvas renderer - */ - draw: function () {}, - - /** - * Parse a simple HTML string into SVG tspans - * - * @param {Object} textNode The parent text SVG node - */ - buildText: function (wrapper) { - var textNode = wrapper.element, - renderer = this, - forExport = renderer.forExport, - lines = pick(wrapper.textStr, '').toString() - .replace(/<(b|strong)>/g, '<span style="font-weight:bold">') - .replace(/<(i|em)>/g, '<span style="font-style:italic">') - .replace(/<a/g, '<span') - .replace(/<\/(b|strong|i|em|a)>/g, '</span>') - .split(/<br.*?>/g), - childNodes = textNode.childNodes, - styleRegex = /style="([^"]+)"/, - hrefRegex = /href="(http[^"]+)"/, - parentX = attr(textNode, 'x'), - textStyles = wrapper.styles, - width = wrapper.textWidth, - textLineHeight = textStyles && textStyles.lineHeight, - i = childNodes.length, - getLineHeight = function (tspan) { - return textLineHeight ? - pInt(textLineHeight) : - renderer.fontMetrics( - /px$/.test(tspan && tspan.style.fontSize) ? - tspan.style.fontSize : - (textStyles.fontSize || 11) - ).h; - }; - - /// remove old text - while (i--) { - textNode.removeChild(childNodes[i]); - } - - if (width && !wrapper.added) { - this.box.appendChild(textNode); // attach it to the DOM to read offset width - } - - // remove empty line at end - if (lines[lines.length - 1] === '') { - lines.pop(); - } - - // build the lines - each(lines, function (line, lineNo) { - var spans, spanNo = 0; - - line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||'); - spans = line.split('|||'); - - each(spans, function (span) { - if (span !== '' || spans.length === 1) { - var attributes = {}, - tspan = doc.createElementNS(SVG_NS, 'tspan'), - spanStyle; // #390 - if (styleRegex.test(span)) { - spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2'); - attr(tspan, 'style', spanStyle); - } - if (hrefRegex.test(span) && !forExport) { // Not for export - #1529 - attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"'); - css(tspan, { cursor: 'pointer' }); - } - - span = (span.replace(/<(.|\n)*?>/g, '') || ' ') - .replace(/</g, '<') - .replace(/>/g, '>'); - - // Nested tags aren't supported, and cause crash in Safari (#1596) - if (span !== ' ') { - - // add the text node - tspan.appendChild(doc.createTextNode(span)); - - if (!spanNo) { // first span in a line, align it to the left - attributes.x = parentX; - } else { - attributes.dx = 0; // #16 - } - - // add attributes - attr(tspan, attributes); - - // first span on subsequent line, add the line height - if (!spanNo && lineNo) { - - // allow getting the right offset height in exporting in IE - if (!hasSVG && forExport) { - css(tspan, { display: 'block' }); - } - - // Set the line height based on the font size of either - // the text element or the tspan element - attr( - tspan, - 'dy', - getLineHeight(tspan), - // Safari 6.0.2 - too optimized for its own good (#1539) - // TODO: revisit this with future versions of Safari - isWebKit && tspan.offsetHeight - ); - } - - // Append it - textNode.appendChild(tspan); - - spanNo++; - - // check width and apply soft breaks - if (width) { - var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273 - hasWhiteSpace = words.length > 1 && textStyles.whiteSpace !== 'nowrap', - tooLong, - actualWidth, - clipHeight = wrapper._clipHeight, - rest = [], - dy = getLineHeight(), - softLineNo = 1, - bBox; - - while (hasWhiteSpace && (words.length || rest.length)) { - delete wrapper.bBox; // delete cache - bBox = wrapper.getBBox(); - actualWidth = bBox.width; - - // Old IE cannot measure the actualWidth for SVG elements (#2314) - if (!hasSVG && renderer.forExport) { - actualWidth = renderer.measureSpanWidth(tspan.firstChild.data, wrapper.styles); - } - - tooLong = actualWidth > width; - if (!tooLong || words.length === 1) { // new line needed - words = rest; - rest = []; - if (words.length) { - softLineNo++; - - if (clipHeight && softLineNo * dy > clipHeight) { - words = ['...']; - wrapper.attr('title', wrapper.textStr); - } else { - - tspan = doc.createElementNS(SVG_NS, 'tspan'); - attr(tspan, { - dy: dy, - x: parentX - }); - if (spanStyle) { // #390 - attr(tspan, 'style', spanStyle); - } - textNode.appendChild(tspan); - - if (actualWidth > width) { // a single word is pressing it out - width = actualWidth; - } - } - } - } else { // append to existing line tspan - tspan.removeChild(tspan.firstChild); - rest.unshift(words.pop()); - } - if (words.length) { - tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-'))); - } - } - } - } - } - }); - }); - }, - - /** - * Create a button with preset states - * @param {String} text - * @param {Number} x - * @param {Number} y - * @param {Function} callback - * @param {Object} normalState - * @param {Object} hoverState - * @param {Object} pressedState - */ - button: function (text, x, y, callback, normalState, hoverState, pressedState, disabledState, shape) { - var label = this.label(text, x, y, shape, null, null, null, null, 'button'), - curState = 0, - stateOptions, - stateStyle, - normalStyle, - hoverStyle, - pressedStyle, - disabledStyle, - STYLE = 'style', - verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 }; - - // Normal state - prepare the attributes - normalState = merge({ - 'stroke-width': 1, - stroke: '#CCCCCC', - fill: { - linearGradient: verticalGradient, - stops: [ - [0, '#FEFEFE'], - [1, '#F6F6F6'] - ] - }, - r: 2, - padding: 5, - style: { - color: 'black' - } - }, normalState); - normalStyle = normalState[STYLE]; - delete normalState[STYLE]; - - // Hover state - hoverState = merge(normalState, { - stroke: '#68A', - fill: { - linearGradient: verticalGradient, - stops: [ - [0, '#FFF'], - [1, '#ACF'] - ] - } - }, hoverState); - hoverStyle = hoverState[STYLE]; - delete hoverState[STYLE]; - - // Pressed state - pressedState = merge(normalState, { - stroke: '#68A', - fill: { - linearGradient: verticalGradient, - stops: [ - [0, '#9BD'], - [1, '#CDF'] - ] - } - }, pressedState); - pressedStyle = pressedState[STYLE]; - delete pressedState[STYLE]; - - // Disabled state - disabledState = merge(normalState, { - style: { - color: '#CCC' - } - }, disabledState); - disabledStyle = disabledState[STYLE]; - delete disabledState[STYLE]; - - // Add the events. IE9 and IE10 need mouseover and mouseout to funciton (#667). - addEvent(label.element, isIE ? 'mouseover' : 'mouseenter', function () { - if (curState !== 3) { - label.attr(hoverState) - .css(hoverStyle); - } - }); - addEvent(label.element, isIE ? 'mouseout' : 'mouseleave', function () { - if (curState !== 3) { - stateOptions = [normalState, hoverState, pressedState][curState]; - stateStyle = [normalStyle, hoverStyle, pressedStyle][curState]; - label.attr(stateOptions) - .css(stateStyle); - } - }); - - label.setState = function (state) { - label.state = curState = state; - if (!state) { - label.attr(normalState) - .css(normalStyle); - } else if (state === 2) { - label.attr(pressedState) - .css(pressedStyle); - } else if (state === 3) { - label.attr(disabledState) - .css(disabledStyle); - } - }; - - return label - .on('click', function () { - if (curState !== 3) { - callback.call(label); - } - }) - .attr(normalState) - .css(extend({ cursor: 'default' }, normalStyle)); - }, - - /** - * Make a straight line crisper by not spilling out to neighbour pixels - * @param {Array} points - * @param {Number} width - */ - crispLine: function (points, width) { - // points format: [M, 0, 0, L, 100, 0] - // normalize to a crisp line - if (points[1] === points[4]) { - // Substract due to #1129. Now bottom and left axis gridlines behave the same. - points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2); - } - if (points[2] === points[5]) { - points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2); - } - return points; - }, - - - /** - * Draw a path - * @param {Array} path An SVG path in array form - */ - path: function (path) { - var attr = { - fill: NONE - }; - if (isArray(path)) { - attr.d = path; - } else if (isObject(path)) { // attributes - extend(attr, path); - } - return this.createElement('path').attr(attr); - }, - - /** - * Draw and return an SVG circle - * @param {Number} x The x position - * @param {Number} y The y position - * @param {Number} r The radius - */ - circle: function (x, y, r) { - var attr = isObject(x) ? - x : - { - x: x, - y: y, - r: r - }; - - return this.createElement('circle').attr(attr); - }, - - /** - * Draw and return an arc - * @param {Number} x X position - * @param {Number} y Y position - * @param {Number} r Radius - * @param {Number} innerR Inner radius like used in donut charts - * @param {Number} start Starting angle - * @param {Number} end Ending angle - */ - arc: function (x, y, r, innerR, start, end) { - var arc; - - if (isObject(x)) { - y = x.y; - r = x.r; - innerR = x.innerR; - start = x.start; - end = x.end; - x = x.x; - } - - // Arcs are defined as symbols for the ability to set - // attributes in attr and animate - arc = this.symbol('arc', x || 0, y || 0, r || 0, r || 0, { - innerR: innerR || 0, - start: start || 0, - end: end || 0 - }); - arc.r = r; // #959 - return arc; - }, - - /** - * Draw and return a rectangle - * @param {Number} x Left position - * @param {Number} y Top position - * @param {Number} width - * @param {Number} height - * @param {Number} r Border corner radius - * @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing - */ - rect: function (x, y, width, height, r, strokeWidth) { - - r = isObject(x) ? x.r : r; - - var wrapper = this.createElement('rect').attr({ - rx: r, - ry: r, - fill: NONE - }); - return wrapper.attr( - isObject(x) ? - x : - // do not crispify when an object is passed in (as in column charts) - wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0)) - ); - }, - - /** - * Resize the box and re-align all aligned elements - * @param {Object} width - * @param {Object} height - * @param {Boolean} animate - * - */ - setSize: function (width, height, animate) { - var renderer = this, - alignedObjects = renderer.alignedObjects, - i = alignedObjects.length; - - renderer.width = width; - renderer.height = height; - - renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({ - width: width, - height: height - }); - - while (i--) { - alignedObjects[i].align(); - } - }, - - /** - * Create a group - * @param {String} name The group will be given a class name of 'highcharts-{name}'. - * This can be used for styling and scripting. - */ - g: function (name) { - var elem = this.createElement('g'); - return defined(name) ? elem.attr({ 'class': PREFIX + name }) : elem; - }, - - /** - * Display an image - * @param {String} src - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - */ - image: function (src, x, y, width, height) { - var attribs = { - preserveAspectRatio: NONE - }, - elemWrapper; - - // optional properties - if (arguments.length > 1) { - extend(attribs, { - x: x, - y: y, - width: width, - height: height - }); - } - - elemWrapper = this.createElement('image').attr(attribs); - - // set the href in the xlink namespace - if (elemWrapper.element.setAttributeNS) { - elemWrapper.element.setAttributeNS('http://www.w3.org/1999/xlink', - 'href', src); - } else { - // could be exporting in IE - // using href throws "not supported" in ie7 and under, requries regex shim to fix later - elemWrapper.element.setAttribute('hc-svg-href', src); - } - - return elemWrapper; - }, - - /** - * Draw a symbol out of pre-defined shape paths from the namespace 'symbol' object. - * - * @param {Object} symbol - * @param {Object} x - * @param {Object} y - * @param {Object} radius - * @param {Object} options - */ - symbol: function (symbol, x, y, width, height, options) { - - var obj, - - // get the symbol definition function - symbolFn = this.symbols[symbol], - - // check if there's a path defined for this symbol - path = symbolFn && symbolFn( - mathRound(x), - mathRound(y), - width, - height, - options - ), - - imageElement, - imageRegex = /^url\((.*?)\)$/, - imageSrc, - imageSize, - centerImage; - - if (path) { - - obj = this.path(path); - // expando properties for use in animate and attr - extend(obj, { - symbolName: symbol, - x: x, - y: y, - width: width, - height: height - }); - if (options) { - extend(obj, options); - } - - - // image symbols - } else if (imageRegex.test(symbol)) { - - // On image load, set the size and position - centerImage = function (img, size) { - if (img.element) { // it may be destroyed in the meantime (#1390) - img.attr({ - width: size[0], - height: size[1] - }); - - if (!img.alignByTranslate) { // #185 - img.translate( - mathRound((width - size[0]) / 2), // #1378 - mathRound((height - size[1]) / 2) - ); - } - } - }; - - imageSrc = symbol.match(imageRegex)[1]; - imageSize = symbolSizes[imageSrc]; - - // Ireate the image synchronously, add attribs async - obj = this.image(imageSrc) - .attr({ - x: x, - y: y - }); - obj.isImg = true; - - if (imageSize) { - centerImage(obj, imageSize); - } else { - // Initialize image to be 0 size so export will still function if there's no cached sizes. - // - obj.attr({ width: 0, height: 0 }); - - // Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8, - // the created element must be assigned to a variable in order to load (#292). - imageElement = createElement('img', { - onload: function () { - centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]); - }, - src: imageSrc - }); - } - } - - return obj; - }, - - /** - * An extendable collection of functions for defining symbol paths. - */ - symbols: { - 'circle': function (x, y, w, h) { - var cpw = 0.166 * w; - return [ - M, x + w / 2, y, - 'C', x + w + cpw, y, x + w + cpw, y + h, x + w / 2, y + h, - 'C', x - cpw, y + h, x - cpw, y, x + w / 2, y, - 'Z' - ]; - }, - - 'square': function (x, y, w, h) { - return [ - M, x, y, - L, x + w, y, - x + w, y + h, - x, y + h, - 'Z' - ]; - }, - - 'triangle': function (x, y, w, h) { - return [ - M, x + w / 2, y, - L, x + w, y + h, - x, y + h, - 'Z' - ]; - }, - - 'triangle-down': function (x, y, w, h) { - return [ - M, x, y, - L, x + w, y, - x + w / 2, y + h, - 'Z' - ]; - }, - 'diamond': function (x, y, w, h) { - return [ - M, x + w / 2, y, - L, x + w, y + h / 2, - x + w / 2, y + h, - x, y + h / 2, - 'Z' - ]; - }, - 'arc': function (x, y, w, h, options) { - var start = options.start, - radius = options.r || w || h, - end = options.end - 0.001, // to prevent cos and sin of start and end from becoming equal on 360 arcs (related: #1561) - innerRadius = options.innerR, - open = options.open, - cosStart = mathCos(start), - sinStart = mathSin(start), - cosEnd = mathCos(end), - sinEnd = mathSin(end), - longArc = options.end - start < mathPI ? 0 : 1; - - return [ - M, - x + radius * cosStart, - y + radius * sinStart, - 'A', // arcTo - radius, // x radius - radius, // y radius - 0, // slanting - longArc, // long or short arc - 1, // clockwise - x + radius * cosEnd, - y + radius * sinEnd, - open ? M : L, - x + innerRadius * cosEnd, - y + innerRadius * sinEnd, - 'A', // arcTo - innerRadius, // x radius - innerRadius, // y radius - 0, // slanting - longArc, // long or short arc - 0, // clockwise - x + innerRadius * cosStart, - y + innerRadius * sinStart, - - open ? '' : 'Z' // close - ]; - } - }, - - /** - * Define a clipping rectangle - * @param {String} id - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - */ - clipRect: function (x, y, width, height) { - var wrapper, - id = PREFIX + idCounter++, - - clipPath = this.createElement('clipPath').attr({ - id: id - }).add(this.defs); - - wrapper = this.rect(x, y, width, height, 0).add(clipPath); - wrapper.id = id; - wrapper.clipPath = clipPath; - - return wrapper; - }, - - - /** - * Take a color and return it if it's a string, make it a gradient if it's a - * gradient configuration object. Prior to Highstock, an array was used to define - * a linear gradient with pixel positions relative to the SVG. In newer versions - * we change the coordinates to apply relative to the shape, using coordinates - * 0-1 within the shape. To preserve backwards compatibility, linearGradient - * in this definition is an object of x1, y1, x2 and y2. - * - * @param {Object} color The color or config object - */ - color: function (color, elem, prop) { - var renderer = this, - colorObject, - regexRgba = /^rgba/, - gradName, - gradAttr, - gradients, - gradientObject, - stops, - stopColor, - stopOpacity, - radialReference, - n, - id, - key = []; - - // Apply linear or radial gradients - if (color && color.linearGradient) { - gradName = 'linearGradient'; - } else if (color && color.radialGradient) { - gradName = 'radialGradient'; - } - - if (gradName) { - gradAttr = color[gradName]; - gradients = renderer.gradients; - stops = color.stops; - radialReference = elem.radialReference; - - // Keep < 2.2 kompatibility - if (isArray(gradAttr)) { - color[gradName] = gradAttr = { - x1: gradAttr[0], - y1: gradAttr[1], - x2: gradAttr[2], - y2: gradAttr[3], - gradientUnits: 'userSpaceOnUse' - }; - } - - // Correct the radial gradient for the radial reference system - if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) { - gradAttr = merge(gradAttr, { - cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2], - cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2], - r: gradAttr.r * radialReference[2], - gradientUnits: 'userSpaceOnUse' - }); - } - - // Build the unique key to detect whether we need to create a new element (#1282) - for (n in gradAttr) { - if (n !== 'id') { - key.push(n, gradAttr[n]); - } - } - for (n in stops) { - key.push(stops[n]); - } - key = key.join(','); - - // Check if a gradient object with the same config object is created within this renderer - if (gradients[key]) { - id = gradients[key].id; - - } else { - - // Set the id and create the element - gradAttr.id = id = PREFIX + idCounter++; - gradients[key] = gradientObject = renderer.createElement(gradName) - .attr(gradAttr) - .add(renderer.defs); - - - // The gradient needs to keep a list of stops to be able to destroy them - gradientObject.stops = []; - each(stops, function (stop) { - var stopObject; - if (regexRgba.test(stop[1])) { - colorObject = Color(stop[1]); - stopColor = colorObject.get('rgb'); - stopOpacity = colorObject.get('a'); - } else { - stopColor = stop[1]; - stopOpacity = 1; - } - stopObject = renderer.createElement('stop').attr({ - offset: stop[0], - 'stop-color': stopColor, - 'stop-opacity': stopOpacity - }).add(gradientObject); - - // Add the stop element to the gradient - gradientObject.stops.push(stopObject); - }); - } - - // Return the reference to the gradient object - return 'url(' + renderer.url + '#' + id + ')'; - - // Webkit and Batik can't show rgba. - } else if (regexRgba.test(color)) { - colorObject = Color(color); - attr(elem, prop + '-opacity', colorObject.get('a')); - - return colorObject.get('rgb'); - - - } else { - // Remove the opacity attribute added above. Does not throw if the attribute is not there. - elem.removeAttribute(prop + '-opacity'); - - return color; - } - - }, - - - /** - * Add text to the SVG object - * @param {String} str - * @param {Number} x Left position - * @param {Number} y Top position - * @param {Boolean} useHTML Use HTML to render the text - */ - text: function (str, x, y, useHTML) { - - // declare variables - var renderer = this, - defaultChartStyle = defaultOptions.chart.style, - fakeSVG = useCanVG || (!hasSVG && renderer.forExport), - wrapper; - - if (useHTML && !renderer.forExport) { - return renderer.html(str, x, y); - } - - x = mathRound(pick(x, 0)); - y = mathRound(pick(y, 0)); - - wrapper = renderer.createElement('text') - .attr({ - x: x, - y: y, - text: str - }) - .css({ - fontFamily: defaultChartStyle.fontFamily, - fontSize: defaultChartStyle.fontSize - }); - - // Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063) - if (fakeSVG) { - wrapper.css({ - position: ABSOLUTE - }); - } - - wrapper.x = x; - wrapper.y = y; - return wrapper; - }, - - /** - * Utility to return the baseline offset and total line height from the font size - */ - fontMetrics: function (fontSize) { - fontSize = pInt(fontSize || 11); - - // Empirical values found by comparing font size and bounding box height. - // Applies to the default font family. http://jsfiddle.net/highcharts/7xvn7/ - var lineHeight = fontSize < 24 ? fontSize + 4 : mathRound(fontSize * 1.2), - baseline = mathRound(lineHeight * 0.8); - - return { - h: lineHeight, - b: baseline - }; - }, - - /** - * Add a label, a text item that can hold a colored or gradient background - * as well as a border and shadow. - * @param {string} str - * @param {Number} x - * @param {Number} y - * @param {String} shape - * @param {Number} anchorX In case the shape has a pointer, like a flag, this is the - * coordinates it should be pinned to - * @param {Number} anchorY - * @param {Boolean} baseline Whether to position the label relative to the text baseline, - * like renderer.text, or to the upper border of the rectangle. - * @param {String} className Class name for the group - */ - label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) { - - var renderer = this, - wrapper = renderer.g(className), - text = renderer.text('', 0, 0, useHTML) - .attr({ - zIndex: 1 - }), - //.add(wrapper), - box, - bBox, - alignFactor = 0, - padding = 3, - paddingLeft = 0, - width, - height, - wrapperX, - wrapperY, - crispAdjust = 0, - deferredAttr = {}, - baselineOffset, - attrSetters = wrapper.attrSetters, - needsBox; - - /** - * This function runs after the label is added to the DOM (when the bounding box is - * available), and after the text of the label is updated to detect the new bounding - * box and reflect it in the border box. - */ - function updateBoxSize() { - var boxX, - boxY, - style = text.element.style; - - bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) && - text.getBBox(); - wrapper.width = (width || bBox.width || 0) + 2 * padding + paddingLeft; - wrapper.height = (height || bBox.height || 0) + 2 * padding; - - // update the label-scoped y offset - baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b; - - if (needsBox) { - - // create the border box if it is not already present - if (!box) { - boxX = mathRound(-alignFactor * padding); - boxY = baseline ? -baselineOffset : 0; - - wrapper.box = box = shape ? - renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height, deferredAttr) : - renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]); - box.add(wrapper); - } - - // apply the box attributes - if (!box.isImg) { // #1630 - box.attr(merge({ - width: wrapper.width, - height: wrapper.height - }, deferredAttr)); - } - deferredAttr = null; - } - } - - /** - * This function runs after setting text or padding, but only if padding is changed - */ - function updateTextPadding() { - var styles = wrapper.styles, - textAlign = styles && styles.textAlign, - x = paddingLeft + padding * (1 - alignFactor), - y; - - // determin y based on the baseline - y = baseline ? 0 : baselineOffset; - - // compensate for alignment - if (defined(width) && (textAlign === 'center' || textAlign === 'right')) { - x += { center: 0.5, right: 1 }[textAlign] * (width - bBox.width); - } - - // update if anything changed - if (x !== text.x || y !== text.y) { - text.attr({ - x: x, - y: y - }); - } - - // record current values - text.x = x; - text.y = y; - } - - /** - * Set a box attribute, or defer it if the box is not yet created - * @param {Object} key - * @param {Object} value - */ - function boxAttr(key, value) { - if (box) { - box.attr(key, value); - } else { - deferredAttr[key] = value; - } - } - - function getSizeAfterAdd() { - text.add(wrapper); - wrapper.attr({ - text: str, // alignment is available now - x: x, - y: y - }); - - if (box && defined(anchorX)) { - wrapper.attr({ - anchorX: anchorX, - anchorY: anchorY - }); - } - } - - /** - * After the text element is added, get the desired size of the border box - * and add it before the text in the DOM. - */ - addEvent(wrapper, 'add', getSizeAfterAdd); - - /* - * Add specific attribute setters. - */ - - // only change local variables - attrSetters.width = function (value) { - width = value; - return false; - }; - attrSetters.height = function (value) { - height = value; - return false; - }; - attrSetters.padding = function (value) { - if (defined(value) && value !== padding) { - padding = value; - updateTextPadding(); - } - return false; - }; - attrSetters.paddingLeft = function (value) { - if (defined(value) && value !== paddingLeft) { - paddingLeft = value; - updateTextPadding(); - } - return false; - }; - - - // change local variable and set attribue as well - attrSetters.align = function (value) { - alignFactor = { left: 0, center: 0.5, right: 1 }[value]; - return false; // prevent setting text-anchor on the group - }; - - // apply these to the box and the text alike - attrSetters.text = function (value, key) { - text.attr(key, value); - updateBoxSize(); - updateTextPadding(); - return false; - }; - - // apply these to the box but not to the text - attrSetters[STROKE_WIDTH] = function (value, key) { - needsBox = true; - crispAdjust = value % 2 / 2; - boxAttr(key, value); - return false; - }; - attrSetters.stroke = attrSetters.fill = attrSetters.r = function (value, key) { - if (key === 'fill') { - needsBox = true; - } - boxAttr(key, value); - return false; - }; - attrSetters.anchorX = function (value, key) { - anchorX = value; - boxAttr(key, value + crispAdjust - wrapperX); - return false; - }; - attrSetters.anchorY = function (value, key) { - anchorY = value; - boxAttr(key, value - wrapperY); - return false; - }; - - // rename attributes - attrSetters.x = function (value) { - wrapper.x = value; // for animation getter - value -= alignFactor * ((width || bBox.width) + padding); - wrapperX = mathRound(value); - - wrapper.attr('translateX', wrapperX); - return false; - }; - attrSetters.y = function (value) { - wrapperY = wrapper.y = mathRound(value); - wrapper.attr('translateY', wrapperY); - return false; - }; - - // Redirect certain methods to either the box or the text - var baseCss = wrapper.css; - return extend(wrapper, { - /** - * Pick up some properties and apply them to the text instead of the wrapper - */ - css: function (styles) { - if (styles) { - var textStyles = {}; - styles = merge(styles); // create a copy to avoid altering the original object (#537) - each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration', 'textShadow'], function (prop) { - if (styles[prop] !== UNDEFINED) { - textStyles[prop] = styles[prop]; - delete styles[prop]; - } - }); - text.css(textStyles); - } - return baseCss.call(wrapper, styles); - }, - /** - * Return the bounding box of the box, not the group - */ - getBBox: function () { - return { - width: bBox.width + 2 * padding, - height: bBox.height + 2 * padding, - x: bBox.x - padding, - y: bBox.y - padding - }; - }, - /** - * Apply the shadow to the box - */ - shadow: function (b) { - if (box) { - box.shadow(b); - } - return wrapper; - }, - /** - * Destroy and release memory. - */ - destroy: function () { - removeEvent(wrapper, 'add', getSizeAfterAdd); - - // Added by button implementation - removeEvent(wrapper.element, 'mouseenter'); - removeEvent(wrapper.element, 'mouseleave'); - - if (text) { - text = text.destroy(); - } - if (box) { - box = box.destroy(); - } - // Call base implementation to destroy the rest - SVGElement.prototype.destroy.call(wrapper); - - // Release local pointers (#1298) - wrapper = renderer = updateBoxSize = updateTextPadding = boxAttr = getSizeAfterAdd = null; - } - }); - } -}; // end SVGRenderer - - -// general renderer -Renderer = SVGRenderer; -// extend SvgElement for useHTML option -extend(SVGElement.prototype, { - /** - * Apply CSS to HTML elements. This is used in text within SVG rendering and - * by the VML renderer - */ - htmlCss: function (styles) { - var wrapper = this, - element = wrapper.element, - textWidth = styles && element.tagName === 'SPAN' && styles.width; - - if (textWidth) { - delete styles.width; - wrapper.textWidth = textWidth; - wrapper.updateTransform(); - } - - wrapper.styles = extend(wrapper.styles, styles); - css(wrapper.element, styles); - - return wrapper; - }, - - /** - * VML and useHTML method for calculating the bounding box based on offsets - * @param {Boolean} refresh Whether to force a fresh value from the DOM or to - * use the cached value - * - * @return {Object} A hash containing values for x, y, width and height - */ - - htmlGetBBox: function () { - var wrapper = this, - element = wrapper.element, - bBox = wrapper.bBox; - - // faking getBBox in exported SVG in legacy IE - if (!bBox) { - // faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?) - if (element.nodeName === 'text') { - element.style.position = ABSOLUTE; - } - - bBox = wrapper.bBox = { - x: element.offsetLeft, - y: element.offsetTop, - width: element.offsetWidth, - height: element.offsetHeight - }; - } - - return bBox; - }, - - /** - * VML override private method to update elements based on internal - * properties based on SVG transform - */ - htmlUpdateTransform: function () { - // aligning non added elements is expensive - if (!this.added) { - this.alignOnAdd = true; - return; - } - - var wrapper = this, - renderer = wrapper.renderer, - elem = wrapper.element, - translateX = wrapper.translateX || 0, - translateY = wrapper.translateY || 0, - x = wrapper.x || 0, - y = wrapper.y || 0, - align = wrapper.textAlign || 'left', - alignCorrection = { left: 0, center: 0.5, right: 1 }[align], - shadows = wrapper.shadows; - - // apply translate - css(elem, { - marginLeft: translateX, - marginTop: translateY - }); - if (shadows) { // used in labels/tooltip - each(shadows, function (shadow) { - css(shadow, { - marginLeft: translateX + 1, - marginTop: translateY + 1 - }); - }); - } - - // apply inversion - if (wrapper.inverted) { // wrapper is a group - each(elem.childNodes, function (child) { - renderer.invertChild(child, elem); - }); - } - - if (elem.tagName === 'SPAN') { - - var width, - rotation = wrapper.rotation, - baseline, - textWidth = pInt(wrapper.textWidth), - currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','); - - if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed - - - baseline = renderer.fontMetrics(elem.style.fontSize).b; - - // Renderer specific handling of span rotation - if (defined(rotation)) { - wrapper.setSpanRotation(rotation, alignCorrection, baseline); - } - - width = pick(wrapper.elemWidth, elem.offsetWidth); - - // Update textWidth - if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254 - css(elem, { - width: textWidth + PX, - display: 'block', - whiteSpace: 'normal' - }); - width = textWidth; - } - - wrapper.getSpanCorrection(width, baseline, alignCorrection, rotation, align); - } - - // apply position with correction - css(elem, { - left: (x + (wrapper.xCorr || 0)) + PX, - top: (y + (wrapper.yCorr || 0)) + PX - }); - - // force reflow in webkit to apply the left and top on useHTML element (#1249) - if (isWebKit) { - baseline = elem.offsetHeight; // assigned to baseline for JSLint purpose - } - - // record current text transform - wrapper.cTT = currentTextTransform; - } - }, - - /** - * Set the rotation of an individual HTML span - */ - setSpanRotation: function (rotation, alignCorrection, baseline) { - var rotationStyle = {}, - cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : ''; - - rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)'; - rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] = (alignCorrection * 100) + '% ' + baseline + 'px'; - css(this.element, rotationStyle); - }, - - /** - * Get the correction in X and Y positioning as the element is rotated. - */ - getSpanCorrection: function (width, baseline, alignCorrection) { - this.xCorr = -width * alignCorrection; - this.yCorr = -baseline; - } -}); - -// Extend SvgRenderer for useHTML option. -extend(SVGRenderer.prototype, { - /** - * Create HTML text node. This is used by the VML renderer as well as the SVG - * renderer through the useHTML option. - * - * @param {String} str - * @param {Number} x - * @param {Number} y - */ - html: function (str, x, y) { - var defaultChartStyle = defaultOptions.chart.style, - wrapper = this.createElement('span'), - attrSetters = wrapper.attrSetters, - element = wrapper.element, - renderer = wrapper.renderer; - - // Text setter - attrSetters.text = function (value) { - if (value !== element.innerHTML) { - delete this.bBox; - } - element.innerHTML = value; - return false; - }; - - // Various setters which rely on update transform - attrSetters.x = attrSetters.y = attrSetters.align = attrSetters.rotation = function (value, key) { - if (key === 'align') { - key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML. - } - wrapper[key] = value; - wrapper.htmlUpdateTransform(); - return false; - }; - - // Set the default attributes - wrapper.attr({ - text: str, - x: mathRound(x), - y: mathRound(y) - }) - .css({ - position: ABSOLUTE, - whiteSpace: 'nowrap', - fontFamily: defaultChartStyle.fontFamily, - fontSize: defaultChartStyle.fontSize - }); - - // Use the HTML specific .css method - wrapper.css = wrapper.htmlCss; - - // This is specific for HTML within SVG - if (renderer.isSVG) { - wrapper.add = function (svgGroupWrapper) { - - var htmlGroup, - container = renderer.box.parentNode, - parentGroup, - parents = []; - - this.parentGroup = svgGroupWrapper; - - // Create a mock group to hold the HTML elements - if (svgGroupWrapper) { - htmlGroup = svgGroupWrapper.div; - if (!htmlGroup) { - - // Read the parent chain into an array and read from top down - parentGroup = svgGroupWrapper; - while (parentGroup) { - - parents.push(parentGroup); - - // Move up to the next parent group - parentGroup = parentGroup.parentGroup; - } - - // Ensure dynamically updating position when any parent is translated - each(parents.reverse(), function (parentGroup) { - var htmlGroupStyle; - - // Create a HTML div and append it to the parent div to emulate - // the SVG group structure - htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, { - className: attr(parentGroup.element, 'class') - }, { - position: ABSOLUTE, - left: (parentGroup.translateX || 0) + PX, - top: (parentGroup.translateY || 0) + PX - }, htmlGroup || container); // the top group is appended to container - - // Shortcut - htmlGroupStyle = htmlGroup.style; - - // Set listeners to update the HTML div's position whenever the SVG group - // position is changed - extend(parentGroup.attrSetters, { - translateX: function (value) { - htmlGroupStyle.left = value + PX; - }, - translateY: function (value) { - htmlGroupStyle.top = value + PX; - }, - visibility: function (value, key) { - htmlGroupStyle[key] = value; - } - }); - }); - - } - } else { - htmlGroup = container; - } - - htmlGroup.appendChild(element); - - // Shared with VML: - wrapper.added = true; - if (wrapper.alignOnAdd) { - wrapper.htmlUpdateTransform(); - } - - return wrapper; - }; - } - return wrapper; - } -}); - -/* **************************************************************************** - * * - * START OF INTERNET EXPLORER <= 8 SPECIFIC CODE * - * * - * For applications and websites that don't need IE support, like platform * - * targeted mobile apps and web apps, this code can be removed. * - * * - *****************************************************************************/ - -/** - * @constructor - */ -var VMLRenderer, VMLElement; -if (!hasSVG && !useCanVG) { - -/** - * The VML element wrapper. - */ -Highcharts.VMLElement = VMLElement = { - - /** - * Initialize a new VML element wrapper. It builds the markup as a string - * to minimize DOM traffic. - * @param {Object} renderer - * @param {Object} nodeName - */ - init: function (renderer, nodeName) { - var wrapper = this, - markup = ['<', nodeName, ' filled="f" stroked="f"'], - style = ['position: ', ABSOLUTE, ';'], - isDiv = nodeName === DIV; - - // divs and shapes need size - if (nodeName === 'shape' || isDiv) { - style.push('left:0;top:0;width:1px;height:1px;'); - } - style.push('visibility: ', isDiv ? HIDDEN : VISIBLE); - - markup.push(' style="', style.join(''), '"/>'); - - // create element with default attributes and style - if (nodeName) { - markup = isDiv || nodeName === 'span' || nodeName === 'img' ? - markup.join('') - : renderer.prepVML(markup); - wrapper.element = createElement(markup); - } - - wrapper.renderer = renderer; - wrapper.attrSetters = {}; - }, - - /** - * Add the node to the given parent - * @param {Object} parent - */ - add: function (parent) { - var wrapper = this, - renderer = wrapper.renderer, - element = wrapper.element, - box = renderer.box, - inverted = parent && parent.inverted, - - // get the parent node - parentNode = parent ? - parent.element || parent : - box; - - - // if the parent group is inverted, apply inversion on all children - if (inverted) { // only on groups - renderer.invertChild(element, parentNode); - } - - // append it - parentNode.appendChild(element); - - // align text after adding to be able to read offset - wrapper.added = true; - if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) { - wrapper.updateTransform(); - } - - // fire an event for internal hooks - fireEvent(wrapper, 'add'); - - return wrapper; - }, - - /** - * VML always uses htmlUpdateTransform - */ - updateTransform: SVGElement.prototype.htmlUpdateTransform, - - /** - * Set the rotation of a span with oldIE's filter - */ - setSpanRotation: function () { - // Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented - // but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+ - // has support for CSS3 transform. The getBBox method also needs to be updated - // to compensate for the rotation, like it currently does for SVG. - // Test case: http://jsfiddle.net/highcharts/Ybt44/ - - var rotation = this.rotation, - costheta = mathCos(rotation * deg2rad), - sintheta = mathSin(rotation * deg2rad); - - css(this.element, { - filter: rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta, - ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta, - ', sizingMethod=\'auto expand\')'].join('') : NONE - }); - }, - - /** - * Get the positioning correction for the span after rotating. - */ - getSpanCorrection: function (width, baseline, alignCorrection, rotation, align) { - - var costheta = rotation ? mathCos(rotation * deg2rad) : 1, - sintheta = rotation ? mathSin(rotation * deg2rad) : 0, - height = pick(this.elemHeight, this.element.offsetHeight), - quad, - nonLeft = align && align !== 'left'; - - // correct x and y - this.xCorr = costheta < 0 && -width; - this.yCorr = sintheta < 0 && -height; - - // correct for baseline and corners spilling out after rotation - quad = costheta * sintheta < 0; - this.xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection); - this.yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1); - // correct for the length/height of the text - if (nonLeft) { - this.xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1); - if (rotation) { - this.yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1); - } - css(this.element, { - textAlign: align - }); - } - }, - - /** - * Converts a subset of an SVG path definition to its VML counterpart. Takes an array - * as the parameter and returns a string. - */ - pathToVML: function (value) { - // convert paths - var i = value.length, - path = []; - - while (i--) { - - // Multiply by 10 to allow subpixel precision. - // Substracting half a pixel seems to make the coordinates - // align with SVG, but this hasn't been tested thoroughly - if (isNumber(value[i])) { - path[i] = mathRound(value[i] * 10) - 5; - } else if (value[i] === 'Z') { // close the path - path[i] = 'x'; - } else { - path[i] = value[i]; - - // When the start X and end X coordinates of an arc are too close, - // they are rounded to the same value above. In this case, substract or - // add 1 from the end X and Y positions. #186, #760, #1371, #1410. - if (value.isArc && (value[i] === 'wa' || value[i] === 'at')) { - // Start and end X - if (path[i + 5] === path[i + 7]) { - path[i + 7] += value[i + 7] > value[i + 5] ? 1 : -1; - } - // Start and end Y - if (path[i + 6] === path[i + 8]) { - path[i + 8] += value[i + 8] > value[i + 6] ? 1 : -1; - } - } - } - } - - - // Loop up again to handle path shortcuts (#2132) - /*while (i++ < path.length) { - if (path[i] === 'H') { // horizontal line to - path[i] = 'L'; - path.splice(i + 2, 0, path[i - 1]); - } else if (path[i] === 'V') { // vertical line to - path[i] = 'L'; - path.splice(i + 1, 0, path[i - 2]); - } - }*/ - return path.join(' ') || 'x'; - }, - - /** - * Get or set attributes - */ - attr: function (hash, val) { - var wrapper = this, - key, - value, - i, - result, - element = wrapper.element || {}, - elemStyle = element.style, - nodeName = element.nodeName, - renderer = wrapper.renderer, - symbolName = wrapper.symbolName, - hasSetSymbolSize, - shadows = wrapper.shadows, - skipAttr, - attrSetters = wrapper.attrSetters, - ret = wrapper; - - // single key-value pair - if (isString(hash) && defined(val)) { - key = hash; - hash = {}; - hash[key] = val; - } - - // used as a getter, val is undefined - if (isString(hash)) { - key = hash; - if (key === 'strokeWidth' || key === 'stroke-width') { - ret = wrapper.strokeweight; - } else { - ret = wrapper[key]; - } - - // setter - } else { - for (key in hash) { - value = hash[key]; - skipAttr = false; - - // check for a specific attribute setter - result = attrSetters[key] && attrSetters[key].call(wrapper, value, key); - - if (result !== false && value !== null) { // #620 - - if (result !== UNDEFINED) { - value = result; // the attribute setter has returned a new value to set - } - - - // prepare paths - // symbols - if (symbolName && /^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(key)) { - // if one of the symbol size affecting parameters are changed, - // check all the others only once for each call to an element's - // .attr() method - if (!hasSetSymbolSize) { - wrapper.symbolAttr(hash); - - hasSetSymbolSize = true; - } - skipAttr = true; - - } else if (key === 'd') { - value = value || []; - wrapper.d = value.join(' '); // used in getter for animation - - element.path = value = wrapper.pathToVML(value); - - // update shadows - if (shadows) { - i = shadows.length; - while (i--) { - shadows[i].path = shadows[i].cutOff ? this.cutOffPath(value, shadows[i].cutOff) : value; - } - } - skipAttr = true; - - // handle visibility - } else if (key === 'visibility') { - - // let the shadow follow the main element - if (shadows) { - i = shadows.length; - while (i--) { - shadows[i].style[key] = value; - } - } - - // Instead of toggling the visibility CSS property, move the div out of the viewport. - // This works around #61 and #586 - if (nodeName === 'DIV') { - value = value === HIDDEN ? '-999em' : 0; - - // In order to redraw, IE7 needs the div to be visible when tucked away - // outside the viewport. So the visibility is actually opposite of - // the expected value. This applies to the tooltip only. - if (!docMode8) { - elemStyle[key] = value ? VISIBLE : HIDDEN; - } - key = 'top'; - } - elemStyle[key] = value; - skipAttr = true; - - // directly mapped to css - } else if (key === 'zIndex') { - - if (value) { - elemStyle[key] = value; - } - skipAttr = true; - - // x, y, width, height - } else if (inArray(key, ['x', 'y', 'width', 'height']) !== -1) { - - wrapper[key] = value; // used in getter - - if (key === 'x' || key === 'y') { - key = { x: 'left', y: 'top' }[key]; - } else { - value = mathMax(0, value); // don't set width or height below zero (#311) - } - - // clipping rectangle special - if (wrapper.updateClipping) { - wrapper[key] = value; // the key is now 'left' or 'top' for 'x' and 'y' - wrapper.updateClipping(); - } else { - // normal - elemStyle[key] = value; - } - - skipAttr = true; - - // class name - } else if (key === 'class' && nodeName === 'DIV') { - // IE8 Standards mode has problems retrieving the className - element.className = value; - - // stroke - } else if (key === 'stroke') { - - value = renderer.color(value, element, key); - - key = 'strokecolor'; - - // stroke width - } else if (key === 'stroke-width' || key === 'strokeWidth') { - element.stroked = value ? true : false; - key = 'strokeweight'; - wrapper[key] = value; // used in getter, issue #113 - if (isNumber(value)) { - value += PX; - } - - // dashStyle - } else if (key === 'dashstyle') { - var strokeElem = element.getElementsByTagName('stroke')[0] || - createElement(renderer.prepVML(['<stroke/>']), null, null, element); - strokeElem[key] = value || 'solid'; - wrapper.dashstyle = value; /* because changing stroke-width will change the dash length - and cause an epileptic effect */ - skipAttr = true; - - // fill - } else if (key === 'fill') { - - if (nodeName === 'SPAN') { // text color - elemStyle.color = value; - } else if (nodeName !== 'IMG') { // #1336 - element.filled = value !== NONE ? true : false; - - value = renderer.color(value, element, key, wrapper); - - key = 'fillcolor'; - } - - // opacity: don't bother - animation is too slow and filters introduce artifacts - } else if (key === 'opacity') { - /*css(element, { - opacity: value - });*/ - skipAttr = true; - - // rotation on VML elements - } else if (nodeName === 'shape' && key === 'rotation') { - - wrapper[key] = element.style[key] = value; // style is for #1873 - - // Correction for the 1x1 size of the shape container. Used in gauge needles. - element.style.left = -mathRound(mathSin(value * deg2rad) + 1) + PX; - element.style.top = mathRound(mathCos(value * deg2rad)) + PX; - - // translation for animation - } else if (key === 'translateX' || key === 'translateY' || key === 'rotation') { - wrapper[key] = value; - wrapper.updateTransform(); - - skipAttr = true; - - } - - - if (!skipAttr) { - if (docMode8) { // IE8 setAttribute bug - element[key] = value; - } else { - attr(element, key, value); - } - } - - } - } - } - return ret; - }, - - /** - * Set the element's clipping to a predefined rectangle - * - * @param {String} id The id of the clip rectangle - */ - clip: function (clipRect) { - var wrapper = this, - clipMembers, - cssRet; - - if (clipRect) { - clipMembers = clipRect.members; - erase(clipMembers, wrapper); // Ensure unique list of elements (#1258) - clipMembers.push(wrapper); - wrapper.destroyClip = function () { - erase(clipMembers, wrapper); - }; - cssRet = clipRect.getCSS(wrapper); - - } else { - if (wrapper.destroyClip) { - wrapper.destroyClip(); - } - cssRet = { clip: docMode8 ? 'inherit' : 'rect(auto)' }; // #1214 - } - - return wrapper.css(cssRet); - - }, - - /** - * Set styles for the element - * @param {Object} styles - */ - css: SVGElement.prototype.htmlCss, - - /** - * Removes a child either by removeChild or move to garbageBin. - * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not. - */ - safeRemoveChild: function (element) { - // discardElement will detach the node from its parent before attaching it - // to the garbage bin. Therefore it is important that the node is attached and have parent. - if (element.parentNode) { - discardElement(element); - } - }, - - /** - * Extend element.destroy by removing it from the clip members array - */ - destroy: function () { - if (this.destroyClip) { - this.destroyClip(); - } - - return SVGElement.prototype.destroy.apply(this); - }, - - /** - * Add an event listener. VML override for normalizing event parameters. - * @param {String} eventType - * @param {Function} handler - */ - on: function (eventType, handler) { - // simplest possible event model for internal use - this.element['on' + eventType] = function () { - var evt = win.event; - evt.target = evt.srcElement; - handler(evt); - }; - return this; - }, - - /** - * In stacked columns, cut off the shadows so that they don't overlap - */ - cutOffPath: function (path, length) { - - var len; - - path = path.split(/[ ,]/); - len = path.length; - - if (len === 9 || len === 11) { - path[len - 4] = path[len - 2] = pInt(path[len - 2]) - 10 * length; - } - return path.join(' '); - }, - - /** - * Apply a drop shadow by copying elements and giving them different strokes - * @param {Boolean|Object} shadowOptions - */ - shadow: function (shadowOptions, group, cutOff) { - var shadows = [], - i, - element = this.element, - renderer = this.renderer, - shadow, - elemStyle = element.style, - markup, - path = element.path, - strokeWidth, - modifiedPath, - shadowWidth, - shadowElementOpacity; - - // some times empty paths are not strings - if (path && typeof path.value !== 'string') { - path = 'x'; - } - modifiedPath = path; - - if (shadowOptions) { - shadowWidth = pick(shadowOptions.width, 3); - shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth; - for (i = 1; i <= 3; i++) { - - strokeWidth = (shadowWidth * 2) + 1 - (2 * i); - - // Cut off shadows for stacked column items - if (cutOff) { - modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5); - } - - markup = ['<shape isShadow="true" strokeweight="', strokeWidth, - '" filled="false" path="', modifiedPath, - '" coordsize="10 10" style="', element.style.cssText, '" />']; - - shadow = createElement(renderer.prepVML(markup), - null, { - left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1), - top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1) - } - ); - if (cutOff) { - shadow.cutOff = strokeWidth + 1; - } - - // apply the opacity - markup = ['<stroke color="', shadowOptions.color || 'black', '" opacity="', shadowElementOpacity * i, '"/>']; - createElement(renderer.prepVML(markup), null, null, shadow); - - - // insert it - if (group) { - group.element.appendChild(shadow); - } else { - element.parentNode.insertBefore(shadow, element); - } - - // record it - shadows.push(shadow); - - } - - this.shadows = shadows; - } - return this; - - } -}; -VMLElement = extendClass(SVGElement, VMLElement); - -/** - * The VML renderer - */ -var VMLRendererExtension = { // inherit SVGRenderer - - Element: VMLElement, - isIE8: userAgent.indexOf('MSIE 8.0') > -1, - - - /** - * Initialize the VMLRenderer - * @param {Object} container - * @param {Number} width - * @param {Number} height - */ - init: function (container, width, height) { - var renderer = this, - boxWrapper, - box, - css; - - renderer.alignedObjects = []; - - boxWrapper = renderer.createElement(DIV); - box = boxWrapper.element; - box.style.position = RELATIVE; // for freeform drawing using renderer directly - container.appendChild(boxWrapper.element); - - - // generate the containing box - renderer.isVML = true; - renderer.box = box; - renderer.boxWrapper = boxWrapper; - renderer.cache = {}; - - - renderer.setSize(width, height, false); - - // The only way to make IE6 and IE7 print is to use a global namespace. However, - // with IE8 the only way to make the dynamic shapes visible in screen and print mode - // seems to be to add the xmlns attribute and the behaviour style inline. - if (!doc.namespaces.hcv) { - - doc.namespaces.add('hcv', 'urn:schemas-microsoft-com:vml'); - - // Setup default CSS (#2153, #2368, #2384) - css = 'hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke' + - '{ behavior:url(#default#VML); display: inline-block; } '; - try { - doc.createStyleSheet().cssText = css; - } catch (e) { - doc.styleSheets[0].cssText += css; - } - - } - }, - - - /** - * Detect whether the renderer is hidden. This happens when one of the parent elements - * has display: none - */ - isHidden: function () { - return !this.box.offsetWidth; - }, - - /** - * Define a clipping rectangle. In VML it is accomplished by storing the values - * for setting the CSS style to all associated members. - * - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - */ - clipRect: function (x, y, width, height) { - - // create a dummy element - var clipRect = this.createElement(), - isObj = isObject(x); - - // mimic a rectangle with its style object for automatic updating in attr - return extend(clipRect, { - members: [], - left: (isObj ? x.x : x) + 1, - top: (isObj ? x.y : y) + 1, - width: (isObj ? x.width : width) - 1, - height: (isObj ? x.height : height) - 1, - getCSS: function (wrapper) { - var element = wrapper.element, - nodeName = element.nodeName, - isShape = nodeName === 'shape', - inverted = wrapper.inverted, - rect = this, - top = rect.top - (isShape ? element.offsetTop : 0), - left = rect.left, - right = left + rect.width, - bottom = top + rect.height, - ret = { - clip: 'rect(' + - mathRound(inverted ? left : top) + 'px,' + - mathRound(inverted ? bottom : right) + 'px,' + - mathRound(inverted ? right : bottom) + 'px,' + - mathRound(inverted ? top : left) + 'px)' - }; - - // issue 74 workaround - if (!inverted && docMode8 && nodeName === 'DIV') { - extend(ret, { - width: right + PX, - height: bottom + PX - }); - } - return ret; - }, - - // used in attr and animation to update the clipping of all members - updateClipping: function () { - each(clipRect.members, function (member) { - member.css(clipRect.getCSS(member)); - }); - } - }); - - }, - - - /** - * Take a color and return it if it's a string, make it a gradient if it's a - * gradient configuration object, and apply opacity. - * - * @param {Object} color The color or config object - */ - color: function (color, elem, prop, wrapper) { - var renderer = this, - colorObject, - regexRgba = /^rgba/, - markup, - fillType, - ret = NONE; - - // Check for linear or radial gradient - if (color && color.linearGradient) { - fillType = 'gradient'; - } else if (color && color.radialGradient) { - fillType = 'pattern'; - } - - - if (fillType) { - - var stopColor, - stopOpacity, - gradient = color.linearGradient || color.radialGradient, - x1, - y1, - x2, - y2, - opacity1, - opacity2, - color1, - color2, - fillAttr = '', - stops = color.stops, - firstStop, - lastStop, - colors = [], - addFillNode = function () { - // Add the fill subnode. When colors attribute is used, the meanings of opacity and o:opacity2 - // are reversed. - markup = ['<fill colors="' + colors.join(',') + '" opacity="', opacity2, '" o:opacity2="', opacity1, - '" type="', fillType, '" ', fillAttr, 'focus="100%" method="any" />']; - createElement(renderer.prepVML(markup), null, null, elem); - }; - - // Extend from 0 to 1 - firstStop = stops[0]; - lastStop = stops[stops.length - 1]; - if (firstStop[0] > 0) { - stops.unshift([ - 0, - firstStop[1] - ]); - } - if (lastStop[0] < 1) { - stops.push([ - 1, - lastStop[1] - ]); - } - - // Compute the stops - each(stops, function (stop, i) { - if (regexRgba.test(stop[1])) { - colorObject = Color(stop[1]); - stopColor = colorObject.get('rgb'); - stopOpacity = colorObject.get('a'); - } else { - stopColor = stop[1]; - stopOpacity = 1; - } - - // Build the color attribute - colors.push((stop[0] * 100) + '% ' + stopColor); - - // Only start and end opacities are allowed, so we use the first and the last - if (!i) { - opacity1 = stopOpacity; - color2 = stopColor; - } else { - opacity2 = stopOpacity; - color1 = stopColor; - } - }); - - // Apply the gradient to fills only. - if (prop === 'fill') { - - // Handle linear gradient angle - if (fillType === 'gradient') { - x1 = gradient.x1 || gradient[0] || 0; - y1 = gradient.y1 || gradient[1] || 0; - x2 = gradient.x2 || gradient[2] || 0; - y2 = gradient.y2 || gradient[3] || 0; - fillAttr = 'angle="' + (90 - math.atan( - (y2 - y1) / // y vector - (x2 - x1) // x vector - ) * 180 / mathPI) + '"'; - - addFillNode(); - - // Radial (circular) gradient - } else { - - var r = gradient.r, - sizex = r * 2, - sizey = r * 2, - cx = gradient.cx, - cy = gradient.cy, - radialReference = elem.radialReference, - bBox, - applyRadialGradient = function () { - if (radialReference) { - bBox = wrapper.getBBox(); - cx += (radialReference[0] - bBox.x) / bBox.width - 0.5; - cy += (radialReference[1] - bBox.y) / bBox.height - 0.5; - sizex *= radialReference[2] / bBox.width; - sizey *= radialReference[2] / bBox.height; - } - fillAttr = 'src="' + defaultOptions.global.VMLRadialGradientURL + '" ' + - 'size="' + sizex + ',' + sizey + '" ' + - 'origin="0.5,0.5" ' + - 'position="' + cx + ',' + cy + '" ' + - 'color2="' + color2 + '" '; - - addFillNode(); - }; - - // Apply radial gradient - if (wrapper.added) { - applyRadialGradient(); - } else { - // We need to know the bounding box to get the size and position right - addEvent(wrapper, 'add', applyRadialGradient); - } - - // The fill element's color attribute is broken in IE8 standards mode, so we - // need to set the parent shape's fillcolor attribute instead. - ret = color1; - } - - // Gradients are not supported for VML stroke, return the first color. #722. - } else { - ret = stopColor; - } - - // if the color is an rgba color, split it and add a fill node - // to hold the opacity component - } else if (regexRgba.test(color) && elem.tagName !== 'IMG') { - - colorObject = Color(color); - - markup = ['<', prop, ' opacity="', colorObject.get('a'), '"/>']; - createElement(this.prepVML(markup), null, null, elem); - - ret = colorObject.get('rgb'); - - - } else { - var propNodes = elem.getElementsByTagName(prop); // 'stroke' or 'fill' node - if (propNodes.length) { - propNodes[0].opacity = 1; - propNodes[0].type = 'solid'; - } - ret = color; - } - - return ret; - }, - - /** - * Take a VML string and prepare it for either IE8 or IE6/IE7. - * @param {Array} markup A string array of the VML markup to prepare - */ - prepVML: function (markup) { - var vmlStyle = 'display:inline-block;behavior:url(#default#VML);', - isIE8 = this.isIE8; - - markup = markup.join(''); - - if (isIE8) { // add xmlns and style inline - markup = markup.replace('/>', ' xmlns="urn:schemas-microsoft-com:vml" />'); - if (markup.indexOf('style="') === -1) { - markup = markup.replace('/>', ' style="' + vmlStyle + '" />'); - } else { - markup = markup.replace('style="', 'style="' + vmlStyle); - } - - } else { // add namespace - markup = markup.replace('<', '<hcv:'); - } - - return markup; - }, - - /** - * Create rotated and aligned text - * @param {String} str - * @param {Number} x - * @param {Number} y - */ - text: SVGRenderer.prototype.html, - - /** - * Create and return a path element - * @param {Array} path - */ - path: function (path) { - var attr = { - // subpixel precision down to 0.1 (width and height = 1px) - coordsize: '10 10' - }; - if (isArray(path)) { - attr.d = path; - } else if (isObject(path)) { // attributes - extend(attr, path); - } - // create the shape - return this.createElement('shape').attr(attr); - }, - - /** - * Create and return a circle element. In VML circles are implemented as - * shapes, which is faster than v:oval - * @param {Number} x - * @param {Number} y - * @param {Number} r - */ - circle: function (x, y, r) { - var circle = this.symbol('circle'); - if (isObject(x)) { - r = x.r; - y = x.y; - x = x.x; - } - circle.isCircle = true; // Causes x and y to mean center (#1682) - circle.r = r; - return circle.attr({ x: x, y: y }); - }, - - /** - * Create a group using an outer div and an inner v:group to allow rotating - * and flipping. A simple v:group would have problems with positioning - * child HTML elements and CSS clip. - * - * @param {String} name The name of the group - */ - g: function (name) { - var wrapper, - attribs; - - // set the class name - if (name) { - attribs = { 'className': PREFIX + name, 'class': PREFIX + name }; - } - - // the div to hold HTML and clipping - wrapper = this.createElement(DIV).attr(attribs); - - return wrapper; - }, - - /** - * VML override to create a regular HTML image - * @param {String} src - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - */ - image: function (src, x, y, width, height) { - var obj = this.createElement('img') - .attr({ src: src }); - - if (arguments.length > 1) { - obj.attr({ - x: x, - y: y, - width: width, - height: height - }); - } - return obj; - }, - - /** - * VML uses a shape for rect to overcome bugs and rotation problems - */ - rect: function (x, y, width, height, r, strokeWidth) { - - var wrapper = this.symbol('rect'); - wrapper.r = isObject(x) ? x.r : r; - - //return wrapper.attr(wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0))); - return wrapper.attr( - isObject(x) ? - x : - // do not crispify when an object is passed in (as in column charts) - wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0)) - ); - }, - - /** - * In the VML renderer, each child of an inverted div (group) is inverted - * @param {Object} element - * @param {Object} parentNode - */ - invertChild: function (element, parentNode) { - var parentStyle = parentNode.style; - css(element, { - flip: 'x', - left: pInt(parentStyle.width) - 1, - top: pInt(parentStyle.height) - 1, - rotation: -90 - }); - }, - - /** - * Symbol definitions that override the parent SVG renderer's symbols - * - */ - symbols: { - // VML specific arc function - arc: function (x, y, w, h, options) { - var start = options.start, - end = options.end, - radius = options.r || w || h, - innerRadius = options.innerR, - cosStart = mathCos(start), - sinStart = mathSin(start), - cosEnd = mathCos(end), - sinEnd = mathSin(end), - ret; - - if (end - start === 0) { // no angle, don't show it. - return ['x']; - } - - ret = [ - 'wa', // clockwise arc to - x - radius, // left - y - radius, // top - x + radius, // right - y + radius, // bottom - x + radius * cosStart, // start x - y + radius * sinStart, // start y - x + radius * cosEnd, // end x - y + radius * sinEnd // end y - ]; - - if (options.open && !innerRadius) { - ret.push( - 'e', - M, - x,// - innerRadius, - y// - innerRadius - ); - } - - ret.push( - 'at', // anti clockwise arc to - x - innerRadius, // left - y - innerRadius, // top - x + innerRadius, // right - y + innerRadius, // bottom - x + innerRadius * cosEnd, // start x - y + innerRadius * sinEnd, // start y - x + innerRadius * cosStart, // end x - y + innerRadius * sinStart, // end y - 'x', // finish path - 'e' // close - ); - - ret.isArc = true; - return ret; - - }, - // Add circle symbol path. This performs significantly faster than v:oval. - circle: function (x, y, w, h, wrapper) { - - if (wrapper) { - w = h = 2 * wrapper.r; - } - - // Center correction, #1682 - if (wrapper && wrapper.isCircle) { - x -= w / 2; - y -= h / 2; - } - - // Return the path - return [ - 'wa', // clockwisearcto - x, // left - y, // top - x + w, // right - y + h, // bottom - x + w, // start x - y + h / 2, // start y - x + w, // end x - y + h / 2, // end y - //'x', // finish path - 'e' // close - ]; - }, - /** - * Add rectangle symbol path which eases rotation and omits arcsize problems - * compared to the built-in VML roundrect shape - * - * @param {Number} left Left position - * @param {Number} top Top position - * @param {Number} r Border radius - * @param {Object} options Width and height - */ - - rect: function (left, top, width, height, options) { - - var right = left + width, - bottom = top + height, - ret, - r; - - // No radius, return the more lightweight square - if (!defined(options) || !options.r) { - ret = SVGRenderer.prototype.symbols.square.apply(0, arguments); - - // Has radius add arcs for the corners - } else { - - r = mathMin(options.r, width, height); - ret = [ - M, - left + r, top, - - L, - right - r, top, - 'wa', - right - 2 * r, top, - right, top + 2 * r, - right - r, top, - right, top + r, - - L, - right, bottom - r, - 'wa', - right - 2 * r, bottom - 2 * r, - right, bottom, - right, bottom - r, - right - r, bottom, - - L, - left + r, bottom, - 'wa', - left, bottom - 2 * r, - left + 2 * r, bottom, - left + r, bottom, - left, bottom - r, - - L, - left, top + r, - 'wa', - left, top, - left + 2 * r, top + 2 * r, - left, top + r, - left + r, top, - - - 'x', - 'e' - ]; - } - return ret; - } - } -}; -Highcharts.VMLRenderer = VMLRenderer = function () { - this.init.apply(this, arguments); -}; -VMLRenderer.prototype = merge(SVGRenderer.prototype, VMLRendererExtension); - - // general renderer - Renderer = VMLRenderer; -} - -// This method is used with exporting in old IE, when emulating SVG (see #2314) -SVGRenderer.prototype.measureSpanWidth = function (text, styles) { - var measuringSpan = doc.createElement('span'), - offsetWidth, - textNode = doc.createTextNode(text); - - measuringSpan.appendChild(textNode); - css(measuringSpan, styles); - this.box.appendChild(measuringSpan); - offsetWidth = measuringSpan.offsetWidth; - discardElement(measuringSpan); // #2463 - return offsetWidth; -}; - - -/* **************************************************************************** - * * - * END OF INTERNET EXPLORER <= 8 SPECIFIC CODE * - * * - *****************************************************************************/ -/* **************************************************************************** - * * - * START OF ANDROID < 3 SPECIFIC CODE. THIS CAN BE REMOVED IF YOU'RE NOT * - * TARGETING THAT SYSTEM. * - * * - *****************************************************************************/ -var CanVGRenderer, - CanVGController; - -if (useCanVG) { - /** - * The CanVGRenderer is empty from start to keep the source footprint small. - * When requested, the CanVGController downloads the rest of the source packaged - * together with the canvg library. - */ - Highcharts.CanVGRenderer = CanVGRenderer = function () { - // Override the global SVG namespace to fake SVG/HTML that accepts CSS - SVG_NS = 'http://www.w3.org/1999/xhtml'; - }; - - /** - * Start with an empty symbols object. This is needed when exporting is used (exporting.src.js will add a few symbols), but - * the implementation from SvgRenderer will not be merged in until first render. - */ - CanVGRenderer.prototype.symbols = {}; - - /** - * Handles on demand download of canvg rendering support. - */ - CanVGController = (function () { - // List of renderering calls - var deferredRenderCalls = []; - - /** - * When downloaded, we are ready to draw deferred charts. - */ - function drawDeferred() { - var callLength = deferredRenderCalls.length, - callIndex; - - // Draw all pending render calls - for (callIndex = 0; callIndex < callLength; callIndex++) { - deferredRenderCalls[callIndex](); - } - // Clear the list - deferredRenderCalls = []; - } - - return { - push: function (func, scriptLocation) { - // Only get the script once - if (deferredRenderCalls.length === 0) { - getScript(scriptLocation, drawDeferred); - } - // Register render call - deferredRenderCalls.push(func); - } - }; - }()); - - Renderer = CanVGRenderer; -} // end CanVGRenderer - -/* **************************************************************************** - * * - * END OF ANDROID < 3 SPECIFIC CODE * - * * - *****************************************************************************/ - -/** - * The Tick class - */ -function Tick(axis, pos, type, noLabel) { - this.axis = axis; - this.pos = pos; - this.type = type || ''; - this.isNew = true; - - if (!type && !noLabel) { - this.addLabel(); - } -} - -Tick.prototype = { - /** - * Write the tick label - */ - addLabel: function () { - var tick = this, - axis = tick.axis, - options = axis.options, - chart = axis.chart, - horiz = axis.horiz, - categories = axis.categories, - names = axis.names, - pos = tick.pos, - labelOptions = options.labels, - str, - tickPositions = axis.tickPositions, - width = (horiz && categories && - !labelOptions.step && !labelOptions.staggerLines && - !labelOptions.rotation && - chart.plotWidth / tickPositions.length) || - (!horiz && (chart.margin[3] || chart.chartWidth * 0.33)), // #1580, #1931 - isFirst = pos === tickPositions[0], - isLast = pos === tickPositions[tickPositions.length - 1], - css, - attr, - value = categories ? - pick(categories[pos], names[pos], pos) : - pos, - label = tick.label, - tickPositionInfo = tickPositions.info, - dateTimeLabelFormat; - - // Set the datetime label format. If a higher rank is set for this position, use that. If not, - // use the general format. - if (axis.isDatetimeAxis && tickPositionInfo) { - dateTimeLabelFormat = options.dateTimeLabelFormats[tickPositionInfo.higherRanks[pos] || tickPositionInfo.unitName]; - } - - // set properties for access in render method - tick.isFirst = isFirst; - tick.isLast = isLast; - - // get the string - str = axis.labelFormatter.call({ - axis: axis, - chart: chart, - isFirst: isFirst, - isLast: isLast, - dateTimeLabelFormat: dateTimeLabelFormat, - value: axis.isLog ? correctFloat(lin2log(value)) : value - }); - - // prepare CSS - css = width && { width: mathMax(1, mathRound(width - 2 * (labelOptions.padding || 10))) + PX }; - css = extend(css, labelOptions.style); - - // first call - if (!defined(label)) { - attr = { - align: axis.labelAlign - }; - if (isNumber(labelOptions.rotation)) { - attr.rotation = labelOptions.rotation; - } - if (width && labelOptions.ellipsis) { - attr._clipHeight = axis.len / tickPositions.length; - } - - tick.label = - defined(str) && labelOptions.enabled ? - chart.renderer.text( - str, - 0, - 0, - labelOptions.useHTML - ) - .attr(attr) - // without position absolute, IE export sometimes is wrong - .css(css) - .add(axis.labelGroup) : - null; - - // update - } else if (label) { - label.attr({ - text: str - }) - .css(css); - } - }, - - /** - * Get the offset height or width of the label - */ - getLabelSize: function () { - var label = this.label, - axis = this.axis; - return label ? - label.getBBox()[axis.horiz ? 'height' : 'width'] : - 0; - }, - - /** - * Find how far the labels extend to the right and left of the tick's x position. Used for anti-collision - * detection with overflow logic. - */ - getLabelSides: function () { - var bBox = this.label.getBBox(), - axis = this.axis, - horiz = axis.horiz, - options = axis.options, - labelOptions = options.labels, - size = horiz ? bBox.width : bBox.height, - leftSide = horiz ? - size * { left: 0, center: 0.5, right: 1 }[axis.labelAlign] - labelOptions.x : - size; - - return [-leftSide, size - leftSide]; - }, - - /** - * Handle the label overflow by adjusting the labels to the left and right edge, or - * hide them if they collide into the neighbour label. - */ - handleOverflow: function (index, xy) { - var show = true, - axis = this.axis, - isFirst = this.isFirst, - isLast = this.isLast, - horiz = axis.horiz, - pxPos = horiz ? xy.x : xy.y, - reversed = axis.reversed, - tickPositions = axis.tickPositions, - sides = this.getLabelSides(), - leftSide = sides[0], - rightSide = sides[1], - axisLeft = axis.pos, - axisRight = axisLeft + axis.len, - neighbour, - neighbourEdge, - line = this.label.line || 0, - labelEdge = axis.labelEdge, - justifyLabel = axis.justifyLabels && (isFirst || isLast); - - // Hide it if it now overlaps the neighbour label - if (labelEdge[line] === UNDEFINED || pxPos + leftSide > labelEdge[line]) { - labelEdge[line] = pxPos + rightSide; - - } else if (!justifyLabel) { - show = false; - } - - if (justifyLabel) { - neighbour = axis.ticks[tickPositions[index + (isFirst ? 1 : -1)]]; - neighbourEdge = neighbour && neighbour.label.xy && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1]; - - if ((isFirst && !reversed) || (isLast && reversed)) { - // Is the label spilling out to the left of the plot area? - if (pxPos + leftSide < axisLeft) { - - // Align it to plot left - pxPos = axisLeft - leftSide; - - // Hide it if it now overlaps the neighbour label - if (neighbour && pxPos + rightSide > neighbourEdge) { - show = false; - } - } - - } else { - // Is the label spilling out to the right of the plot area? - if (pxPos + rightSide > axisRight) { - - // Align it to plot right - pxPos = axisRight - rightSide; - - // Hide it if it now overlaps the neighbour label - if (neighbour && pxPos + leftSide < neighbourEdge) { - show = false; - } - - } - } - - // Set the modified x position of the label - xy.x = pxPos; - } - return show; - }, - - /** - * Get the x and y position for ticks and labels - */ - getPosition: function (horiz, pos, tickmarkOffset, old) { - var axis = this.axis, - chart = axis.chart, - cHeight = (old && chart.oldChartHeight) || chart.chartHeight; - - return { - x: horiz ? - axis.translate(pos + tickmarkOffset, null, null, old) + axis.transB : - axis.left + axis.offset + (axis.opposite ? ((old && chart.oldChartWidth) || chart.chartWidth) - axis.right - axis.left : 0), - - y: horiz ? - cHeight - axis.bottom + axis.offset - (axis.opposite ? axis.height : 0) : - cHeight - axis.translate(pos + tickmarkOffset, null, null, old) - axis.transB - }; - - }, - - /** - * Get the x, y position of the tick label - */ - getLabelPosition: function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) { - var axis = this.axis, - transA = axis.transA, - reversed = axis.reversed, - staggerLines = axis.staggerLines, - baseline = axis.chart.renderer.fontMetrics(labelOptions.style.fontSize).b, - rotation = labelOptions.rotation; - - x = x + labelOptions.x - (tickmarkOffset && horiz ? - tickmarkOffset * transA * (reversed ? -1 : 1) : 0); - y = y + labelOptions.y - (tickmarkOffset && !horiz ? - tickmarkOffset * transA * (reversed ? 1 : -1) : 0); - - // Correct for rotation (#1764) - if (rotation && axis.side === 2) { - y -= baseline - baseline * mathCos(rotation * deg2rad); - } - - // Vertically centered - if (!defined(labelOptions.y) && !rotation) { // #1951 - y += baseline - label.getBBox().height / 2; - } - - // Correct for staggered labels - if (staggerLines) { - label.line = (index / (step || 1) % staggerLines); - y += label.line * (axis.labelOffset / staggerLines); - } - - return { - x: x, - y: y - }; - }, - - /** - * Extendible method to return the path of the marker - */ - getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) { - return renderer.crispLine([ - M, - x, - y, - L, - x + (horiz ? 0 : -tickLength), - y + (horiz ? tickLength : 0) - ], tickWidth); - }, - - /** - * Put everything in place - * - * @param index {Number} - * @param old {Boolean} Use old coordinates to prepare an animation into new position - */ - render: function (index, old, opacity) { - var tick = this, - axis = tick.axis, - options = axis.options, - chart = axis.chart, - renderer = chart.renderer, - horiz = axis.horiz, - type = tick.type, - label = tick.label, - pos = tick.pos, - labelOptions = options.labels, - gridLine = tick.gridLine, - gridPrefix = type ? type + 'Grid' : 'grid', - tickPrefix = type ? type + 'Tick' : 'tick', - gridLineWidth = options[gridPrefix + 'LineWidth'], - gridLineColor = options[gridPrefix + 'LineColor'], - dashStyle = options[gridPrefix + 'LineDashStyle'], - tickLength = options[tickPrefix + 'Length'], - tickWidth = options[tickPrefix + 'Width'] || 0, - tickColor = options[tickPrefix + 'Color'], - tickPosition = options[tickPrefix + 'Position'], - gridLinePath, - mark = tick.mark, - markPath, - step = labelOptions.step, - attribs, - show = true, - tickmarkOffset = axis.tickmarkOffset, - xy = tick.getPosition(horiz, pos, tickmarkOffset, old), - x = xy.x, - y = xy.y, - reverseCrisp = ((horiz && x === axis.pos + axis.len) || (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687 - - this.isActive = true; - - // create the grid line - if (gridLineWidth) { - gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLineWidth * reverseCrisp, old, true); - - if (gridLine === UNDEFINED) { - attribs = { - stroke: gridLineColor, - 'stroke-width': gridLineWidth - }; - if (dashStyle) { - attribs.dashstyle = dashStyle; - } - if (!type) { - attribs.zIndex = 1; - } - if (old) { - attribs.opacity = 0; - } - tick.gridLine = gridLine = - gridLineWidth ? - renderer.path(gridLinePath) - .attr(attribs).add(axis.gridGroup) : - null; - } - - // If the parameter 'old' is set, the current call will be followed - // by another call, therefore do not do any animations this time - if (!old && gridLine && gridLinePath) { - gridLine[tick.isNew ? 'attr' : 'animate']({ - d: gridLinePath, - opacity: opacity - }); - } - } - - // create the tick mark - if (tickWidth && tickLength) { - - // negate the length - if (tickPosition === 'inside') { - tickLength = -tickLength; - } - if (axis.opposite) { - tickLength = -tickLength; - } - - markPath = tick.getMarkPath(x, y, tickLength, tickWidth * reverseCrisp, horiz, renderer); - - if (mark) { // updating - mark.animate({ - d: markPath, - opacity: opacity - }); - } else { // first time - tick.mark = renderer.path( - markPath - ).attr({ - stroke: tickColor, - 'stroke-width': tickWidth, - opacity: opacity - }).add(axis.axisGroup); - } - } - - // the label is created on init - now move it into place - if (label && !isNaN(x)) { - label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step); - - // Apply show first and show last. If the tick is both first and last, it is - // a single centered tick, in which case we show the label anyway (#2100). - if ((tick.isFirst && !tick.isLast && !pick(options.showFirstLabel, 1)) || - (tick.isLast && !tick.isFirst && !pick(options.showLastLabel, 1))) { - show = false; - - // Handle label overflow and show or hide accordingly - } else if (!axis.isRadial && !labelOptions.step && !labelOptions.rotation && !old && opacity !== 0) { - show = tick.handleOverflow(index, xy); - } - - // apply step - if (step && index % step) { - // show those indices dividable by step - show = false; - } - - // Set the new position, and show or hide - if (show && !isNaN(xy.y)) { - xy.opacity = opacity; - label[tick.isNew ? 'attr' : 'animate'](xy); - tick.isNew = false; - } else { - label.attr('y', -9999); // #1338 - } - } - }, - - /** - * Destructor for the tick prototype - */ - destroy: function () { - destroyObjectProperties(this, this.axis); - } -}; - -/** - * The object wrapper for plot lines and plot bands - * @param {Object} options - */ -var PlotLineOrBand = function (axis, options) { - this.axis = axis; - - if (options) { - this.options = options; - this.id = options.id; - } -}; - -PlotLineOrBand.prototype = { - - /** - * Render the plot line or plot band. If it is already existing, - * move it. - */ - render: function () { - var plotLine = this, - axis = plotLine.axis, - horiz = axis.horiz, - halfPointRange = (axis.pointRange || 0) / 2, - options = plotLine.options, - optionsLabel = options.label, - label = plotLine.label, - width = options.width, - to = options.to, - from = options.from, - isBand = defined(from) && defined(to), - value = options.value, - dashStyle = options.dashStyle, - svgElem = plotLine.svgElem, - path = [], - addEvent, - eventType, - xs, - ys, - x, - y, - color = options.color, - zIndex = options.zIndex, - events = options.events, - attribs, - renderer = axis.chart.renderer; - - // logarithmic conversion - if (axis.isLog) { - from = log2lin(from); - to = log2lin(to); - value = log2lin(value); - } - - // plot line - if (width) { - path = axis.getPlotLinePath(value, width); - attribs = { - stroke: color, - 'stroke-width': width - }; - if (dashStyle) { - attribs.dashstyle = dashStyle; - } - } else if (isBand) { // plot band - - // keep within plot area - from = mathMax(from, axis.min - halfPointRange); - to = mathMin(to, axis.max + halfPointRange); - - path = axis.getPlotBandPath(from, to, options); - attribs = { - fill: color - }; - if (options.borderWidth) { - attribs.stroke = options.borderColor; - attribs['stroke-width'] = options.borderWidth; - } - } else { - return; - } - // zIndex - if (defined(zIndex)) { - attribs.zIndex = zIndex; - } - - // common for lines and bands - if (svgElem) { - if (path) { - svgElem.animate({ - d: path - }, null, svgElem.onGetPath); - } else { - svgElem.hide(); - svgElem.onGetPath = function () { - svgElem.show(); - }; - if (label) { - plotLine.label = label = label.destroy(); - } - } - } else if (path && path.length) { - plotLine.svgElem = svgElem = renderer.path(path) - .attr(attribs).add(); - - // events - if (events) { - addEvent = function (eventType) { - svgElem.on(eventType, function (e) { - events[eventType].apply(plotLine, [e]); - }); - }; - for (eventType in events) { - addEvent(eventType); - } - } - } - - // the plot band/line label - if (optionsLabel && defined(optionsLabel.text) && path && path.length && axis.width > 0 && axis.height > 0) { - // apply defaults - optionsLabel = merge({ - align: horiz && isBand && 'center', - x: horiz ? !isBand && 4 : 10, - verticalAlign : !horiz && isBand && 'middle', - y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4, - rotation: horiz && !isBand && 90 - }, optionsLabel); - - // add the SVG element - if (!label) { - plotLine.label = label = renderer.text( - optionsLabel.text, - 0, - 0, - optionsLabel.useHTML - ) - .attr({ - align: optionsLabel.textAlign || optionsLabel.align, - rotation: optionsLabel.rotation, - zIndex: zIndex - }) - .css(optionsLabel.style) - .add(); - } - - // get the bounding box and align the label - xs = [path[1], path[4], pick(path[6], path[1])]; - ys = [path[2], path[5], pick(path[7], path[2])]; - x = arrayMin(xs); - y = arrayMin(ys); - - label.align(optionsLabel, false, { - x: x, - y: y, - width: arrayMax(xs) - x, - height: arrayMax(ys) - y - }); - label.show(); - - } else if (label) { // move out of sight - label.hide(); - } - - // chainable - return plotLine; - }, - - /** - * Remove the plot line or band - */ - destroy: function () { - // remove it from the lookup - erase(this.axis.plotLinesAndBands, this); - - delete this.axis; - destroyObjectProperties(this); - } -}; - -/** - * Object with members for extending the Axis prototype - */ - -AxisPlotLineOrBandExtension = { - - /** - * Create the path for a plot band - */ - getPlotBandPath: function (from, to) { - var toPath = this.getPlotLinePath(to), - path = this.getPlotLinePath(from); - - if (path && toPath) { - path.push( - toPath[4], - toPath[5], - toPath[1], - toPath[2] - ); - } else { // outside the axis area - path = null; - } - - return path; - }, - - addPlotBand: function (options) { - this.addPlotBandOrLine(options, 'plotBands'); - }, - - addPlotLine: function (options) { - this.addPlotBandOrLine(options, 'plotLines'); - }, - - /** - * Add a plot band or plot line after render time - * - * @param options {Object} The plotBand or plotLine configuration object - */ - addPlotBandOrLine: function (options, coll) { - var obj = new PlotLineOrBand(this, options).render(), - userOptions = this.userOptions; - - if (obj) { // #2189 - // Add it to the user options for exporting and Axis.update - if (coll) { - userOptions[coll] = userOptions[coll] || []; - userOptions[coll].push(options); - } - this.plotLinesAndBands.push(obj); - } - - return obj; - }, - - /** - * Remove a plot band or plot line from the chart by id - * @param {Object} id - */ - removePlotBandOrLine: function (id) { - var plotLinesAndBands = this.plotLinesAndBands, - options = this.options, - userOptions = this.userOptions, - i = plotLinesAndBands.length; - while (i--) { - if (plotLinesAndBands[i].id === id) { - plotLinesAndBands[i].destroy(); - } - } - each([options.plotLines || [], userOptions.plotLines || [], options.plotBands || [], userOptions.plotBands || []], function (arr) { - i = arr.length; - while (i--) { - if (arr[i].id === id) { - erase(arr, arr[i]); - } - } - }); - } -}; - -/** - * Create a new axis object - * @param {Object} chart - * @param {Object} options - */ -function Axis() { - this.init.apply(this, arguments); -} - -Axis.prototype = { - - /** - * Default options for the X axis - the Y axis has extended defaults - */ - defaultOptions: { - // allowDecimals: null, - // alternateGridColor: null, - // categories: [], - dateTimeLabelFormats: { - millisecond: '%H:%M:%S.%L', - second: '%H:%M:%S', - minute: '%H:%M', - hour: '%H:%M', - day: '%e. %b', - week: '%e. %b', - month: '%b \'%y', - year: '%Y' - }, - endOnTick: false, - gridLineColor: '#C0C0C0', - // gridLineDashStyle: 'solid', - // gridLineWidth: 0, - // reversed: false, - - labels: defaultLabelOptions, - // { step: null }, - lineColor: '#C0D0E0', - lineWidth: 1, - //linkedTo: null, - //max: undefined, - //min: undefined, - minPadding: 0.01, - maxPadding: 0.01, - //minRange: null, - minorGridLineColor: '#E0E0E0', - // minorGridLineDashStyle: null, - minorGridLineWidth: 1, - minorTickColor: '#A0A0A0', - //minorTickInterval: null, - minorTickLength: 2, - minorTickPosition: 'outside', // inside or outside - //minorTickWidth: 0, - //opposite: false, - //offset: 0, - //plotBands: [{ - // events: {}, - // zIndex: 1, - // labels: { align, x, verticalAlign, y, style, rotation, textAlign } - //}], - //plotLines: [{ - // events: {} - // dashStyle: {} - // zIndex: - // labels: { align, x, verticalAlign, y, style, rotation, textAlign } - //}], - //reversed: false, - // showFirstLabel: true, - // showLastLabel: true, - startOfWeek: 1, - startOnTick: false, - tickColor: '#C0D0E0', - //tickInterval: null, - tickLength: 5, - tickmarkPlacement: 'between', // on or between - tickPixelInterval: 100, - tickPosition: 'outside', - tickWidth: 1, - title: { - //text: null, - align: 'middle', // low, middle or high - //margin: 0 for horizontal, 10 for vertical axes, - //rotation: 0, - //side: 'outside', - style: { - color: '#4d759e', - //font: defaultFont.replace('normal', 'bold') - fontWeight: 'bold' - } - //x: 0, - //y: 0 - }, - type: 'linear' // linear, logarithmic or datetime - }, - - /** - * This options set extends the defaultOptions for Y axes - */ - defaultYAxisOptions: { - endOnTick: true, - gridLineWidth: 1, - tickPixelInterval: 72, - showLastLabel: true, - labels: { - x: -8, - y: 3 - }, - lineWidth: 0, - maxPadding: 0.05, - minPadding: 0.05, - startOnTick: true, - tickWidth: 0, - title: { - rotation: 270, - text: 'Values' - }, - stackLabels: { - enabled: false, - //align: dynamic, - //y: dynamic, - //x: dynamic, - //verticalAlign: dynamic, - //textAlign: dynamic, - //rotation: 0, - formatter: function () { - return numberFormat(this.total, -1); - }, - style: defaultLabelOptions.style - } - }, - - /** - * These options extend the defaultOptions for left axes - */ - defaultLeftAxisOptions: { - labels: { - x: -8, - y: null - }, - title: { - rotation: 270 - } - }, - - /** - * These options extend the defaultOptions for right axes - */ - defaultRightAxisOptions: { - labels: { - x: 8, - y: null - }, - title: { - rotation: 90 - } - }, - - /** - * These options extend the defaultOptions for bottom axes - */ - defaultBottomAxisOptions: { - labels: { - x: 0, - y: 14 - // overflow: undefined, - // staggerLines: null - }, - title: { - rotation: 0 - } - }, - /** - * These options extend the defaultOptions for left axes - */ - defaultTopAxisOptions: { - labels: { - x: 0, - y: -5 - // overflow: undefined - // staggerLines: null - }, - title: { - rotation: 0 - } - }, - - /** - * Initialize the axis - */ - init: function (chart, userOptions) { - - - var isXAxis = userOptions.isX, - axis = this; - - // Flag, is the axis horizontal - axis.horiz = chart.inverted ? !isXAxis : isXAxis; - - // Flag, isXAxis - axis.isXAxis = isXAxis; - axis.coll = isXAxis ? 'xAxis' : 'yAxis'; - - axis.opposite = userOptions.opposite; // needed in setOptions - axis.side = userOptions.side || (axis.horiz ? - (axis.opposite ? 0 : 2) : // top : bottom - (axis.opposite ? 1 : 3)); // right : left - - axis.setOptions(userOptions); - - - var options = this.options, - type = options.type, - isDatetimeAxis = type === 'datetime'; - - axis.labelFormatter = options.labels.formatter || axis.defaultLabelFormatter; // can be overwritten by dynamic format - - - // Flag, stagger lines or not - axis.userOptions = userOptions; - - //axis.axisTitleMargin = UNDEFINED,// = options.title.margin, - axis.minPixelPadding = 0; - //axis.ignoreMinPadding = UNDEFINED; // can be set to true by a column or bar series - //axis.ignoreMaxPadding = UNDEFINED; - - axis.chart = chart; - axis.reversed = options.reversed; - axis.zoomEnabled = options.zoomEnabled !== false; - - // Initial categories - axis.categories = options.categories || type === 'category'; - axis.names = []; - - // Elements - //axis.axisGroup = UNDEFINED; - //axis.gridGroup = UNDEFINED; - //axis.axisTitle = UNDEFINED; - //axis.axisLine = UNDEFINED; - - // Shorthand types - axis.isLog = type === 'logarithmic'; - axis.isDatetimeAxis = isDatetimeAxis; - - // Flag, if axis is linked to another axis - axis.isLinked = defined(options.linkedTo); - // Linked axis. - //axis.linkedParent = UNDEFINED; - - // Tick positions - //axis.tickPositions = UNDEFINED; // array containing predefined positions - // Tick intervals - //axis.tickInterval = UNDEFINED; - //axis.minorTickInterval = UNDEFINED; - - axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0; - - // Major ticks - axis.ticks = {}; - axis.labelEdge = []; - // Minor ticks - axis.minorTicks = {}; - //axis.tickAmount = UNDEFINED; - - // List of plotLines/Bands - axis.plotLinesAndBands = []; - - // Alternate bands - axis.alternateBands = {}; - - // Axis metrics - //axis.left = UNDEFINED; - //axis.top = UNDEFINED; - //axis.width = UNDEFINED; - //axis.height = UNDEFINED; - //axis.bottom = UNDEFINED; - //axis.right = UNDEFINED; - //axis.transA = UNDEFINED; - //axis.transB = UNDEFINED; - //axis.oldTransA = UNDEFINED; - axis.len = 0; - //axis.oldMin = UNDEFINED; - //axis.oldMax = UNDEFINED; - //axis.oldUserMin = UNDEFINED; - //axis.oldUserMax = UNDEFINED; - //axis.oldAxisLength = UNDEFINED; - axis.minRange = axis.userMinRange = options.minRange || options.maxZoom; - axis.range = options.range; - axis.offset = options.offset || 0; - - - // Dictionary for stacks - axis.stacks = {}; - axis.oldStacks = {}; - - // Dictionary for stacks max values - axis.stackExtremes = {}; - - // Min and max in the data - //axis.dataMin = UNDEFINED, - //axis.dataMax = UNDEFINED, - - // The axis range - axis.max = null; - axis.min = null; - - // User set min and max - //axis.userMin = UNDEFINED, - //axis.userMax = UNDEFINED, - - // Crosshair options - axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false); - // Run Axis - - var eventType, - events = axis.options.events; - - // Register - if (inArray(axis, chart.axes) === -1) { // don't add it again on Axis.update() - chart.axes.push(axis); - chart[axis.coll].push(axis); - } - - axis.series = axis.series || []; // populated by Series - - // inverted charts have reversed xAxes as default - if (chart.inverted && isXAxis && axis.reversed === UNDEFINED) { - axis.reversed = true; - } - - axis.removePlotBand = axis.removePlotBandOrLine; - axis.removePlotLine = axis.removePlotBandOrLine; - - - // register event listeners - for (eventType in events) { - addEvent(axis, eventType, events[eventType]); - } - - // extend logarithmic axis - if (axis.isLog) { - axis.val2lin = log2lin; - axis.lin2val = lin2log; - } - }, - - /** - * Merge and set options - */ - setOptions: function (userOptions) { - this.options = merge( - this.defaultOptions, - this.isXAxis ? {} : this.defaultYAxisOptions, - [this.defaultTopAxisOptions, this.defaultRightAxisOptions, - this.defaultBottomAxisOptions, this.defaultLeftAxisOptions][this.side], - merge( - defaultOptions[this.coll], // if set in setOptions (#1053) - userOptions - ) - ); - }, - - /** - * The default label formatter. The context is a special config object for the label. - */ - defaultLabelFormatter: function () { - var axis = this.axis, - value = this.value, - categories = axis.categories, - dateTimeLabelFormat = this.dateTimeLabelFormat, - numericSymbols = defaultOptions.lang.numericSymbols, - i = numericSymbols && numericSymbols.length, - multi, - ret, - formatOption = axis.options.labels.format, - - // make sure the same symbol is added for all labels on a linear axis - numericSymbolDetector = axis.isLog ? value : axis.tickInterval; - - if (formatOption) { - ret = format(formatOption, this); - - } else if (categories) { - ret = value; - - } else if (dateTimeLabelFormat) { // datetime axis - ret = dateFormat(dateTimeLabelFormat, value); - - } else if (i && numericSymbolDetector >= 1000) { - // Decide whether we should add a numeric symbol like k (thousands) or M (millions). - // If we are to enable this in tooltip or other places as well, we can move this - // logic to the numberFormatter and enable it by a parameter. - while (i-- && ret === UNDEFINED) { - multi = Math.pow(1000, i + 1); - if (numericSymbolDetector >= multi && numericSymbols[i] !== null) { - ret = numberFormat(value / multi, -1) + numericSymbols[i]; - } - } - } - - if (ret === UNDEFINED) { - if (value >= 10000) { // add thousands separators - ret = numberFormat(value, 0); - - } else { // small numbers - ret = numberFormat(value, -1, UNDEFINED, ''); // #2466 - } - } - - return ret; - }, - - /** - * Get the minimum and maximum for the series of each axis - */ - getSeriesExtremes: function () { - var axis = this, - chart = axis.chart; - - axis.hasVisibleSeries = false; - - // reset dataMin and dataMax in case we're redrawing - axis.dataMin = axis.dataMax = null; - - // reset cached stacking extremes - axis.stackExtremes = {}; - - axis.buildStacks(); - - // loop through this axis' series - each(axis.series, function (series) { - - if (series.visible || !chart.options.chart.ignoreHiddenSeries) { - - var seriesOptions = series.options, - xData, - threshold = seriesOptions.threshold, - seriesDataMin, - seriesDataMax; - - axis.hasVisibleSeries = true; - - // Validate threshold in logarithmic axes - if (axis.isLog && threshold <= 0) { - threshold = null; - } - - // Get dataMin and dataMax for X axes - if (axis.isXAxis) { - xData = series.xData; - if (xData.length) { - axis.dataMin = mathMin(pick(axis.dataMin, xData[0]), arrayMin(xData)); - axis.dataMax = mathMax(pick(axis.dataMax, xData[0]), arrayMax(xData)); - } - - // Get dataMin and dataMax for Y axes, as well as handle stacking and processed data - } else { - - // Get this particular series extremes - series.getExtremes(); - seriesDataMax = series.dataMax; - seriesDataMin = series.dataMin; - - // Get the dataMin and dataMax so far. If percentage is used, the min and max are - // always 0 and 100. If seriesDataMin and seriesDataMax is null, then series - // doesn't have active y data, we continue with nulls - if (defined(seriesDataMin) && defined(seriesDataMax)) { - axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin); - axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax); - } - - // Adjust to threshold - if (defined(threshold)) { - if (axis.dataMin >= threshold) { - axis.dataMin = threshold; - axis.ignoreMinPadding = true; - } else if (axis.dataMax < threshold) { - axis.dataMax = threshold; - axis.ignoreMaxPadding = true; - } - } - } - } - }); - }, - - /** - * Translate from axis value to pixel position on the chart, or back - * - */ - translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacement) { - var axis = this, - axisLength = axis.len, - sign = 1, - cvsOffset = 0, - localA = old ? axis.oldTransA : axis.transA, - localMin = old ? axis.oldMin : axis.min, - returnValue, - minPixelPadding = axis.minPixelPadding, - postTranslate = (axis.options.ordinal || (axis.isLog && handleLog)) && axis.lin2val; - - if (!localA) { - localA = axis.transA; - } - - // In vertical axes, the canvas coordinates start from 0 at the top like in - // SVG. - if (cvsCoord) { - sign *= -1; // canvas coordinates inverts the value - cvsOffset = axisLength; - } - - // Handle reversed axis - if (axis.reversed) { - sign *= -1; - cvsOffset -= sign * axisLength; - } - - // From pixels to value - if (backwards) { // reverse translation - - val = val * sign + cvsOffset; - val -= minPixelPadding; - returnValue = val / localA + localMin; // from chart pixel to value - if (postTranslate) { // log and ordinal axes - returnValue = axis.lin2val(returnValue); - } - - // From value to pixels - } else { - if (postTranslate) { // log and ordinal axes - val = axis.val2lin(val); - } - if (pointPlacement === 'between') { - pointPlacement = 0.5; - } - returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * minPixelPadding) + - (isNumber(pointPlacement) ? localA * pointPlacement * axis.pointRange : 0); - } - - return returnValue; - }, - - /** - * Utility method to translate an axis value to pixel position. - * @param {Number} value A value in terms of axis units - * @param {Boolean} paneCoordinates Whether to return the pixel coordinate relative to the chart - * or just the axis/pane itself. - */ - toPixels: function (value, paneCoordinates) { - return this.translate(value, false, !this.horiz, null, true) + (paneCoordinates ? 0 : this.pos); - }, - - /* - * Utility method to translate a pixel position in to an axis value - * @param {Number} pixel The pixel value coordinate - * @param {Boolean} paneCoordiantes Whether the input pixel is relative to the chart or just the - * axis/pane itself. - */ - toValue: function (pixel, paneCoordinates) { - return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true); - }, - - /** - * Create the path for a plot line that goes from the given value on - * this axis, across the plot to the opposite side - * @param {Number} value - * @param {Number} lineWidth Used for calculation crisp line - * @param {Number] old Use old coordinates (for resizing and rescaling) - */ - getPlotLinePath: function (value, lineWidth, old, force, translatedValue) { - var axis = this, - chart = axis.chart, - axisLeft = axis.left, - axisTop = axis.top, - x1, - y1, - x2, - y2, - cHeight = (old && chart.oldChartHeight) || chart.chartHeight, - cWidth = (old && chart.oldChartWidth) || chart.chartWidth, - skip, - transB = axis.transB; - - translatedValue = pick(translatedValue, axis.translate(value, null, null, old)); - x1 = x2 = mathRound(translatedValue + transB); - y1 = y2 = mathRound(cHeight - translatedValue - transB); - - if (isNaN(translatedValue)) { // no min or max - skip = true; - - } else if (axis.horiz) { - y1 = axisTop; - y2 = cHeight - axis.bottom; - if (x1 < axisLeft || x1 > axisLeft + axis.width) { - skip = true; - } - } else { - x1 = axisLeft; - x2 = cWidth - axis.right; - - if (y1 < axisTop || y1 > axisTop + axis.height) { - skip = true; - } - } - return skip && !force ? - null : - chart.renderer.crispLine([M, x1, y1, L, x2, y2], lineWidth || 1); - }, - - /** - * Set the tick positions of a linear axis to round values like whole tens or every five. - */ - getLinearTickPositions: function (tickInterval, min, max) { - var pos, - lastPos, - roundedMin = correctFloat(mathFloor(min / tickInterval) * tickInterval), - roundedMax = correctFloat(mathCeil(max / tickInterval) * tickInterval), - tickPositions = []; - - // Populate the intermediate values - pos = roundedMin; - while (pos <= roundedMax) { - - // Place the tick on the rounded value - tickPositions.push(pos); - - // Always add the raw tickInterval, not the corrected one. - pos = correctFloat(pos + tickInterval); - - // If the interval is not big enough in the current min - max range to actually increase - // the loop variable, we need to break out to prevent endless loop. Issue #619 - if (pos === lastPos) { - break; - } - - // Record the last value - lastPos = pos; - } - return tickPositions; - }, - - /** - * Return the minor tick positions. For logarithmic axes, reuse the same logic - * as for major ticks. - */ - getMinorTickPositions: function () { - var axis = this, - options = axis.options, - tickPositions = axis.tickPositions, - minorTickInterval = axis.minorTickInterval, - minorTickPositions = [], - pos, - i, - len; - - if (axis.isLog) { - len = tickPositions.length; - for (i = 1; i < len; i++) { - minorTickPositions = minorTickPositions.concat( - axis.getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true) - ); - } - } else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314 - minorTickPositions = minorTickPositions.concat( - axis.getTimeTicks( - axis.normalizeTimeTickInterval(minorTickInterval), - axis.min, - axis.max, - options.startOfWeek - ) - ); - if (minorTickPositions[0] < axis.min) { - minorTickPositions.shift(); - } - } else { - for (pos = axis.min + (tickPositions[0] - axis.min) % minorTickInterval; pos <= axis.max; pos += minorTickInterval) { - minorTickPositions.push(pos); - } - } - return minorTickPositions; - }, - - /** - * Adjust the min and max for the minimum range. Keep in mind that the series data is - * not yet processed, so we don't have information on data cropping and grouping, or - * updated axis.pointRange or series.pointRange. The data can't be processed until - * we have finally established min and max. - */ - adjustForMinRange: function () { - var axis = this, - options = axis.options, - min = axis.min, - max = axis.max, - zoomOffset, - spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange, - closestDataRange, - i, - distance, - xData, - loopLength, - minArgs, - maxArgs; - - // Set the automatic minimum range based on the closest point distance - if (axis.isXAxis && axis.minRange === UNDEFINED && !axis.isLog) { - - if (defined(options.min) || defined(options.max)) { - axis.minRange = null; // don't do this again - - } else { - - // Find the closest distance between raw data points, as opposed to - // closestPointRange that applies to processed points (cropped and grouped) - each(axis.series, function (series) { - xData = series.xData; - loopLength = series.xIncrement ? 1 : xData.length - 1; - for (i = loopLength; i > 0; i--) { - distance = xData[i] - xData[i - 1]; - if (closestDataRange === UNDEFINED || distance < closestDataRange) { - closestDataRange = distance; - } - } - }); - axis.minRange = mathMin(closestDataRange * 5, axis.dataMax - axis.dataMin); - } - } - - // if minRange is exceeded, adjust - if (max - min < axis.minRange) { - var minRange = axis.minRange; - zoomOffset = (minRange - max + min) / 2; - - // if min and max options have been set, don't go beyond it - minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)]; - if (spaceAvailable) { // if space is available, stay within the data range - minArgs[2] = axis.dataMin; - } - min = arrayMax(minArgs); - - maxArgs = [min + minRange, pick(options.max, min + minRange)]; - if (spaceAvailable) { // if space is availabe, stay within the data range - maxArgs[2] = axis.dataMax; - } - - max = arrayMin(maxArgs); - - // now if the max is adjusted, adjust the min back - if (max - min < minRange) { - minArgs[0] = max - minRange; - minArgs[1] = pick(options.min, max - minRange); - min = arrayMax(minArgs); - } - } - - // Record modified extremes - axis.min = min; - axis.max = max; - }, - - /** - * Update translation information - */ - setAxisTranslation: function (saveOld) { - var axis = this, - range = axis.max - axis.min, - pointRange = 0, - closestPointRange, - minPointOffset = 0, - pointRangePadding = 0, - linkedParent = axis.linkedParent, - ordinalCorrection, - hasCategories = !!axis.categories, - transA = axis.transA; - - // Adjust translation for padding. Y axis with categories need to go through the same (#1784). - if (axis.isXAxis || hasCategories) { - if (linkedParent) { - minPointOffset = linkedParent.minPointOffset; - pointRangePadding = linkedParent.pointRangePadding; - - } else { - each(axis.series, function (series) { - var seriesPointRange = mathMax(series.pointRange, +hasCategories), - pointPlacement = series.options.pointPlacement, - seriesClosestPointRange = series.closestPointRange; - - if (seriesPointRange > range) { // #1446 - seriesPointRange = 0; - } - pointRange = mathMax(pointRange, seriesPointRange); - - // minPointOffset is the value padding to the left of the axis in order to make - // room for points with a pointRange, typically columns. When the pointPlacement option - // is 'between' or 'on', this padding does not apply. - minPointOffset = mathMax( - minPointOffset, - isString(pointPlacement) ? 0 : seriesPointRange / 2 - ); - - // Determine the total padding needed to the length of the axis to make room for the - // pointRange. If the series' pointPlacement is 'on', no padding is added. - pointRangePadding = mathMax( - pointRangePadding, - pointPlacement === 'on' ? 0 : seriesPointRange - ); - - // Set the closestPointRange - if (!series.noSharedTooltip && defined(seriesClosestPointRange)) { - closestPointRange = defined(closestPointRange) ? - mathMin(closestPointRange, seriesClosestPointRange) : - seriesClosestPointRange; - } - }); - } - - // Record minPointOffset and pointRangePadding - ordinalCorrection = axis.ordinalSlope && closestPointRange ? axis.ordinalSlope / closestPointRange : 1; // #988, #1853 - axis.minPointOffset = minPointOffset = minPointOffset * ordinalCorrection; - axis.pointRangePadding = pointRangePadding = pointRangePadding * ordinalCorrection; - - // pointRange means the width reserved for each point, like in a column chart - axis.pointRange = mathMin(pointRange, range); - - // closestPointRange means the closest distance between points. In columns - // it is mostly equal to pointRange, but in lines pointRange is 0 while closestPointRange - // is some other value - axis.closestPointRange = closestPointRange; - } - - // Secondary values - if (saveOld) { - axis.oldTransA = transA; - } - axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRangePadding) || 1); - axis.transB = axis.horiz ? axis.left : axis.bottom; // translation addend - axis.minPixelPadding = transA * minPointOffset; - }, - - /** - * Set the tick positions to round values and optionally extend the extremes - * to the nearest tick - */ - setTickPositions: function (secondPass) { - var axis = this, - chart = axis.chart, - options = axis.options, - isLog = axis.isLog, - isDatetimeAxis = axis.isDatetimeAxis, - isXAxis = axis.isXAxis, - isLinked = axis.isLinked, - tickPositioner = axis.options.tickPositioner, - maxPadding = options.maxPadding, - minPadding = options.minPadding, - length, - linkedParentExtremes, - tickIntervalOption = options.tickInterval, - minTickIntervalOption = options.minTickInterval, - tickPixelIntervalOption = options.tickPixelInterval, - tickPositions, - keepTwoTicksOnly, - categories = axis.categories; - - // linked axis gets the extremes from the parent axis - if (isLinked) { - axis.linkedParent = chart[axis.coll][options.linkedTo]; - linkedParentExtremes = axis.linkedParent.getExtremes(); - axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin); - axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax); - if (options.type !== axis.linkedParent.options.type) { - error(11, 1); // Can't link axes of different type - } - } else { // initial min and max from the extreme data values - axis.min = pick(axis.userMin, options.min, axis.dataMin); - axis.max = pick(axis.userMax, options.max, axis.dataMax); - } - - if (isLog) { - if (!secondPass && mathMin(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978 - error(10, 1); // Can't plot negative values on log axis - } - axis.min = correctFloat(log2lin(axis.min)); // correctFloat cures #934 - axis.max = correctFloat(log2lin(axis.max)); - } - - // handle zoomed range - if (axis.range && defined(axis.max)) { - axis.userMin = axis.min = mathMax(axis.min, axis.max - axis.range); // #618 - axis.userMax = axis.max; - - axis.range = null; // don't use it when running setExtremes - } - - // Hook for adjusting this.min and this.max. Used by bubble series. - if (axis.beforePadding) { - axis.beforePadding(); - } - - // adjust min and max for the minimum range - axis.adjustForMinRange(); - - // Pad the values to get clear of the chart's edges. To avoid tickInterval taking the padding - // into account, we do this after computing tick interval (#1337). - if (!categories && !axis.usePercentage && !isLinked && defined(axis.min) && defined(axis.max)) { - length = axis.max - axis.min; - if (length) { - if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) { - axis.min -= length * minPadding; - } - if (!defined(options.max) && !defined(axis.userMax) && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) { - axis.max += length * maxPadding; - } - } - } - - // get tickInterval - if (axis.min === axis.max || axis.min === undefined || axis.max === undefined) { - axis.tickInterval = 1; - } else if (isLinked && !tickIntervalOption && - tickPixelIntervalOption === axis.linkedParent.options.tickPixelInterval) { - axis.tickInterval = axis.linkedParent.tickInterval; - } else { - axis.tickInterval = pick( - tickIntervalOption, - categories ? // for categoried axis, 1 is default, for linear axis use tickPix - 1 : - // don't let it be more than the data range - (axis.max - axis.min) * tickPixelIntervalOption / mathMax(axis.len, tickPixelIntervalOption) - ); - // For squished axes, set only two ticks - if (!defined(tickIntervalOption) && axis.len < tickPixelIntervalOption && !this.isRadial && - !categories && options.startOnTick && options.endOnTick) { - keepTwoTicksOnly = true; - axis.tickInterval /= 4; // tick extremes closer to the real values - } - } - - // Now we're finished detecting min and max, crop and group series data. This - // is in turn needed in order to find tick positions in ordinal axes. - if (isXAxis && !secondPass) { - each(axis.series, function (series) { - series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax); - }); - } - - // set the translation factor used in translate function - axis.setAxisTranslation(true); - - // hook for ordinal axes and radial axes - if (axis.beforeSetTickPositions) { - axis.beforeSetTickPositions(); - } - - // hook for extensions, used in Highstock ordinal axes - if (axis.postProcessTickInterval) { - axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval); - } - - // In column-like charts, don't cramp in more ticks than there are points (#1943) - if (axis.pointRange) { - axis.tickInterval = mathMax(axis.pointRange, axis.tickInterval); - } - - // Before normalizing the tick interval, handle minimum tick interval. This applies only if tickInterval is not defined. - if (!tickIntervalOption && axis.tickInterval < minTickIntervalOption) { - axis.tickInterval = minTickIntervalOption; - } - - // for linear axes, get magnitude and normalize the interval - if (!isDatetimeAxis && !isLog) { // linear - if (!tickIntervalOption) { - axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, getMagnitude(axis.tickInterval), options); - } - } - - // get minorTickInterval - axis.minorTickInterval = options.minorTickInterval === 'auto' && axis.tickInterval ? - axis.tickInterval / 5 : options.minorTickInterval; - - // find the tick positions - axis.tickPositions = tickPositions = options.tickPositions ? - [].concat(options.tickPositions) : // Work on a copy (#1565) - (tickPositioner && tickPositioner.apply(axis, [axis.min, axis.max])); - if (!tickPositions) { - - // Too many ticks - if (!axis.ordinalPositions && (axis.max - axis.min) / axis.tickInterval > mathMax(2 * axis.len, 200)) { - error(19, true); - } - - if (isDatetimeAxis) { - tickPositions = axis.getTimeTicks( - axis.normalizeTimeTickInterval(axis.tickInterval, options.units), - axis.min, - axis.max, - options.startOfWeek, - axis.ordinalPositions, - axis.closestPointRange, - true - ); - } else if (isLog) { - tickPositions = axis.getLogTickPositions(axis.tickInterval, axis.min, axis.max); - } else { - tickPositions = axis.getLinearTickPositions(axis.tickInterval, axis.min, axis.max); - } - - if (keepTwoTicksOnly) { - tickPositions.splice(1, tickPositions.length - 2); - } - - axis.tickPositions = tickPositions; - } - - if (!isLinked) { - - // reset min/max or remove extremes based on start/end on tick - var roundedMin = tickPositions[0], - roundedMax = tickPositions[tickPositions.length - 1], - minPointOffset = axis.minPointOffset || 0, - singlePad; - - if (options.startOnTick) { - axis.min = roundedMin; - } else if (axis.min - minPointOffset > roundedMin) { - tickPositions.shift(); - } - - if (options.endOnTick) { - axis.max = roundedMax; - } else if (axis.max + minPointOffset < roundedMax) { - tickPositions.pop(); - } - - // When there is only one point, or all points have the same value on this axis, then min - // and max are equal and tickPositions.length is 1. In this case, add some padding - // in order to center the point, but leave it with one tick. #1337. - if (tickPositions.length === 1) { - singlePad = 0.001; // The lowest possible number to avoid extra padding on columns - axis.min -= singlePad; - axis.max += singlePad; - } - } - }, - - /** - * Set the max ticks of either the x and y axis collection - */ - setMaxTicks: function () { - - var chart = this.chart, - maxTicks = chart.maxTicks || {}, - tickPositions = this.tickPositions, - key = this._maxTicksKey = [this.coll, this.pos, this.len].join('-'); - - if (!this.isLinked && !this.isDatetimeAxis && tickPositions && tickPositions.length > (maxTicks[key] || 0) && this.options.alignTicks !== false) { - maxTicks[key] = tickPositions.length; - } - chart.maxTicks = maxTicks; - }, - - /** - * When using multiple axes, adjust the number of ticks to match the highest - * number of ticks in that group - */ - adjustTickAmount: function () { - var axis = this, - chart = axis.chart, - key = axis._maxTicksKey, - tickPositions = axis.tickPositions, - maxTicks = chart.maxTicks; - - if (maxTicks && maxTicks[key] && !axis.isDatetimeAxis && !axis.categories && !axis.isLinked && - axis.options.alignTicks !== false && this.min !== UNDEFINED) { - var oldTickAmount = axis.tickAmount, - calculatedTickAmount = tickPositions.length, - tickAmount; - - // set the axis-level tickAmount to use below - axis.tickAmount = tickAmount = maxTicks[key]; - - if (calculatedTickAmount < tickAmount) { - while (tickPositions.length < tickAmount) { - tickPositions.push(correctFloat( - tickPositions[tickPositions.length - 1] + axis.tickInterval - )); - } - axis.transA *= (calculatedTickAmount - 1) / (tickAmount - 1); - axis.max = tickPositions[tickPositions.length - 1]; - - } - if (defined(oldTickAmount) && tickAmount !== oldTickAmount) { - axis.isDirty = true; - } - } - }, - - /** - * Set the scale based on data min and max, user set min and max or options - * - */ - setScale: function () { - var axis = this, - stacks = axis.stacks, - type, - i, - isDirtyData, - isDirtyAxisLength; - - axis.oldMin = axis.min; - axis.oldMax = axis.max; - axis.oldAxisLength = axis.len; - - // set the new axisLength - axis.setAxisSize(); - //axisLength = horiz ? axisWidth : axisHeight; - isDirtyAxisLength = axis.len !== axis.oldAxisLength; - - // is there new data? - each(axis.series, function (series) { - if (series.isDirtyData || series.isDirty || - series.xAxis.isDirty) { // when x axis is dirty, we need new data extremes for y as well - isDirtyData = true; - } - }); - - // do we really need to go through all this? - if (isDirtyAxisLength || isDirtyData || axis.isLinked || axis.forceRedraw || - axis.userMin !== axis.oldUserMin || axis.userMax !== axis.oldUserMax) { - - // reset stacks - if (!axis.isXAxis) { - for (type in stacks) { - for (i in stacks[type]) { - stacks[type][i].total = null; - stacks[type][i].cum = 0; - } - } - } - - axis.forceRedraw = false; - - // get data extremes if needed - axis.getSeriesExtremes(); - - // get fixed positions based on tickInterval - axis.setTickPositions(); - - // record old values to decide whether a rescale is necessary later on (#540) - axis.oldUserMin = axis.userMin; - axis.oldUserMax = axis.userMax; - - // Mark as dirty if it is not already set to dirty and extremes have changed. #595. - if (!axis.isDirty) { - axis.isDirty = isDirtyAxisLength || axis.min !== axis.oldMin || axis.max !== axis.oldMax; - } - } else if (!axis.isXAxis) { - if (axis.oldStacks) { - stacks = axis.stacks = axis.oldStacks; - } - - // reset stacks - for (type in stacks) { - for (i in stacks[type]) { - stacks[type][i].cum = stacks[type][i].total; - } - } - } - - // Set the maximum tick amount - axis.setMaxTicks(); - }, - - /** - * Set the extremes and optionally redraw - * @param {Number} newMin - * @param {Number} newMax - * @param {Boolean} redraw - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - * @param {Object} eventArguments - * - */ - setExtremes: function (newMin, newMax, redraw, animation, eventArguments) { - var axis = this, - chart = axis.chart; - - redraw = pick(redraw, true); // defaults to true - - // Extend the arguments with min and max - eventArguments = extend(eventArguments, { - min: newMin, - max: newMax - }); - - // Fire the event - fireEvent(axis, 'setExtremes', eventArguments, function () { // the default event handler - - axis.userMin = newMin; - axis.userMax = newMax; - axis.eventArgs = eventArguments; - - // Mark for running afterSetExtremes - axis.isDirtyExtremes = true; - - // redraw - if (redraw) { - chart.redraw(animation); - } - }); - }, - - /** - * Overridable method for zooming chart. Pulled out in a separate method to allow overriding - * in stock charts. - */ - zoom: function (newMin, newMax) { - - // Prevent pinch zooming out of range. Check for defined is for #1946. - if (!this.allowZoomOutside) { - if (defined(this.dataMin) && newMin <= this.dataMin) { - newMin = UNDEFINED; - } - if (defined(this.dataMax) && newMax >= this.dataMax) { - newMax = UNDEFINED; - } - } - - // In full view, displaying the reset zoom button is not required - this.displayBtn = newMin !== UNDEFINED || newMax !== UNDEFINED; - - // Do it - this.setExtremes( - newMin, - newMax, - false, - UNDEFINED, - { trigger: 'zoom' } - ); - return true; - }, - - /** - * Update the axis metrics - */ - setAxisSize: function () { - var chart = this.chart, - options = this.options, - offsetLeft = options.offsetLeft || 0, - offsetRight = options.offsetRight || 0, - horiz = this.horiz, - width, - height, - top, - left; - - // Expose basic values to use in Series object and navigator - this.left = left = pick(options.left, chart.plotLeft + offsetLeft); - this.top = top = pick(options.top, chart.plotTop); - this.width = width = pick(options.width, chart.plotWidth - offsetLeft + offsetRight); - this.height = height = pick(options.height, chart.plotHeight); - this.bottom = chart.chartHeight - height - top; - this.right = chart.chartWidth - width - left; - - // Direction agnostic properties - this.len = mathMax(horiz ? width : height, 0); // mathMax fixes #905 - this.pos = horiz ? left : top; // distance from SVG origin - }, - - /** - * Get the actual axis extremes - */ - getExtremes: function () { - var axis = this, - isLog = axis.isLog; - - return { - min: isLog ? correctFloat(lin2log(axis.min)) : axis.min, - max: isLog ? correctFloat(lin2log(axis.max)) : axis.max, - dataMin: axis.dataMin, - dataMax: axis.dataMax, - userMin: axis.userMin, - userMax: axis.userMax - }; - }, - - /** - * Get the zero plane either based on zero or on the min or max value. - * Used in bar and area plots - */ - getThreshold: function (threshold) { - var axis = this, - isLog = axis.isLog; - - var realMin = isLog ? lin2log(axis.min) : axis.min, - realMax = isLog ? lin2log(axis.max) : axis.max; - - if (realMin > threshold || threshold === null) { - threshold = realMin; - } else if (realMax < threshold) { - threshold = realMax; - } - - return axis.translate(threshold, 0, 1, 0, 1); - }, - - /** - * Compute auto alignment for the axis label based on which side the axis is on - * and the given rotation for the label - */ - autoLabelAlign: function (rotation) { - var ret, - angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360; - - if (angle > 15 && angle < 165) { - ret = 'right'; - } else if (angle > 195 && angle < 345) { - ret = 'left'; - } else { - ret = 'center'; - } - return ret; - }, - - /** - * Render the tick labels to a preliminary position to get their sizes - */ - getOffset: function () { - var axis = this, - chart = axis.chart, - renderer = chart.renderer, - options = axis.options, - tickPositions = axis.tickPositions, - ticks = axis.ticks, - horiz = axis.horiz, - side = axis.side, - invertedSide = chart.inverted ? [1, 0, 3, 2][side] : side, - hasData, - showAxis, - titleOffset = 0, - titleOffsetOption, - titleMargin = 0, - axisTitleOptions = options.title, - labelOptions = options.labels, - labelOffset = 0, // reset - axisOffset = chart.axisOffset, - clipOffset = chart.clipOffset, - directionFactor = [-1, 1, 1, -1][side], - n, - i, - autoStaggerLines = 1, - maxStaggerLines = pick(labelOptions.maxStaggerLines, 5), - sortedPositions, - lastRight, - overlap, - pos, - bBox, - x, - w, - lineNo; - - // For reuse in Axis.render - axis.hasData = hasData = (axis.hasVisibleSeries || (defined(axis.min) && defined(axis.max) && !!tickPositions)); - axis.showAxis = showAxis = hasData || pick(options.showEmpty, true); - - // Set/reset staggerLines - axis.staggerLines = axis.horiz && labelOptions.staggerLines; - - // Create the axisGroup and gridGroup elements on first iteration - if (!axis.axisGroup) { - axis.gridGroup = renderer.g('grid') - .attr({ zIndex: options.gridZIndex || 1 }) - .add(); - axis.axisGroup = renderer.g('axis') - .attr({ zIndex: options.zIndex || 2 }) - .add(); - axis.labelGroup = renderer.g('axis-labels') - .attr({ zIndex: labelOptions.zIndex || 7 }) - .add(); - } - - if (hasData || axis.isLinked) { - - // Set the explicit or automatic label alignment - axis.labelAlign = pick(labelOptions.align || axis.autoLabelAlign(labelOptions.rotation)); - - // Generate ticks - each(tickPositions, function (pos) { - if (!ticks[pos]) { - ticks[pos] = new Tick(axis, pos); - } else { - ticks[pos].addLabel(); // update labels depending on tick interval - } - }); - - // Handle automatic stagger lines - if (axis.horiz && !axis.staggerLines && maxStaggerLines && !labelOptions.rotation) { - sortedPositions = axis.reversed ? [].concat(tickPositions).reverse() : tickPositions; - while (autoStaggerLines < maxStaggerLines) { - lastRight = []; - overlap = false; - - for (i = 0; i < sortedPositions.length; i++) { - pos = sortedPositions[i]; - bBox = ticks[pos].label && ticks[pos].label.getBBox(); - w = bBox ? bBox.width : 0; - lineNo = i % autoStaggerLines; - - if (w) { - x = axis.translate(pos); // don't handle log - if (lastRight[lineNo] !== UNDEFINED && x < lastRight[lineNo]) { - overlap = true; - } - lastRight[lineNo] = x + w; - } - } - if (overlap) { - autoStaggerLines++; - } else { - break; - } - } - - if (autoStaggerLines > 1) { - axis.staggerLines = autoStaggerLines; - } - } - - - each(tickPositions, function (pos) { - // left side must be align: right and right side must have align: left for labels - if (side === 0 || side === 2 || { 1: 'left', 3: 'right' }[side] === axis.labelAlign) { - - // get the highest offset - labelOffset = mathMax( - ticks[pos].getLabelSize(), - labelOffset - ); - } - - }); - if (axis.staggerLines) { - labelOffset *= axis.staggerLines; - axis.labelOffset = labelOffset; - } - - - } else { // doesn't have data - for (n in ticks) { - ticks[n].destroy(); - delete ticks[n]; - } - } - - if (axisTitleOptions && axisTitleOptions.text && axisTitleOptions.enabled !== false) { - if (!axis.axisTitle) { - axis.axisTitle = renderer.text( - axisTitleOptions.text, - 0, - 0, - axisTitleOptions.useHTML - ) - .attr({ - zIndex: 7, - rotation: axisTitleOptions.rotation || 0, - align: - axisTitleOptions.textAlign || - { low: 'left', middle: 'center', high: 'right' }[axisTitleOptions.align] - }) - .css(axisTitleOptions.style) - .add(axis.axisGroup); - axis.axisTitle.isNew = true; - } - - if (showAxis) { - titleOffset = axis.axisTitle.getBBox()[horiz ? 'height' : 'width']; - titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10); - titleOffsetOption = axisTitleOptions.offset; - } - - // hide or show the title depending on whether showEmpty is set - axis.axisTitle[showAxis ? 'show' : 'hide'](); - } - - // handle automatic or user set offset - axis.offset = directionFactor * pick(options.offset, axisOffset[side]); - - axis.axisTitleMargin = - pick(titleOffsetOption, - labelOffset + titleMargin + - (side !== 2 && labelOffset && directionFactor * options.labels[horiz ? 'y' : 'x']) - ); - - axisOffset[side] = mathMax( - axisOffset[side], - axis.axisTitleMargin + titleOffset + directionFactor * axis.offset - ); - clipOffset[invertedSide] = mathMax(clipOffset[invertedSide], mathFloor(options.lineWidth / 2) * 2); - }, - - /** - * Get the path for the axis line - */ - getLinePath: function (lineWidth) { - var chart = this.chart, - opposite = this.opposite, - offset = this.offset, - horiz = this.horiz, - lineLeft = this.left + (opposite ? this.width : 0) + offset, - lineTop = chart.chartHeight - this.bottom - (opposite ? this.height : 0) + offset; - - if (opposite) { - lineWidth *= -1; // crispify the other way - #1480, #1687 - } - - return chart.renderer.crispLine([ - M, - horiz ? - this.left : - lineLeft, - horiz ? - lineTop : - this.top, - L, - horiz ? - chart.chartWidth - this.right : - lineLeft, - horiz ? - lineTop : - chart.chartHeight - this.bottom - ], lineWidth); - }, - - /** - * Position the title - */ - getTitlePosition: function () { - // compute anchor points for each of the title align options - var horiz = this.horiz, - axisLeft = this.left, - axisTop = this.top, - axisLength = this.len, - axisTitleOptions = this.options.title, - margin = horiz ? axisLeft : axisTop, - opposite = this.opposite, - offset = this.offset, - fontSize = pInt(axisTitleOptions.style.fontSize || 12), - - // the position in the length direction of the axis - alongAxis = { - low: margin + (horiz ? 0 : axisLength), - middle: margin + axisLength / 2, - high: margin + (horiz ? axisLength : 0) - }[axisTitleOptions.align], - - // the position in the perpendicular direction of the axis - offAxis = (horiz ? axisTop + this.height : axisLeft) + - (horiz ? 1 : -1) * // horizontal axis reverses the margin - (opposite ? -1 : 1) * // so does opposite axes - this.axisTitleMargin + - (this.side === 2 ? fontSize : 0); - - return { - x: horiz ? - alongAxis : - offAxis + (opposite ? this.width : 0) + offset + - (axisTitleOptions.x || 0), // x - y: horiz ? - offAxis - (opposite ? this.height : 0) + offset : - alongAxis + (axisTitleOptions.y || 0) // y - }; - }, - - /** - * Render the axis - */ - render: function () { - var axis = this, - horiz = axis.horiz, - reversed = axis.reversed, - chart = axis.chart, - renderer = chart.renderer, - options = axis.options, - isLog = axis.isLog, - isLinked = axis.isLinked, - tickPositions = axis.tickPositions, - sortedPositions, - axisTitle = axis.axisTitle, - stacks = axis.stacks, - ticks = axis.ticks, - minorTicks = axis.minorTicks, - alternateBands = axis.alternateBands, - stackLabelOptions = options.stackLabels, - alternateGridColor = options.alternateGridColor, - tickmarkOffset = axis.tickmarkOffset, - lineWidth = options.lineWidth, - linePath, - hasRendered = chart.hasRendered, - slideInTicks = hasRendered && defined(axis.oldMin) && !isNaN(axis.oldMin), - hasData = axis.hasData, - showAxis = axis.showAxis, - from, - justifyLabels = axis.justifyLabels = !axis.staggerLines && horiz && options.labels.overflow === 'justify', - to; - - // Reset - axis.labelEdge.length = 0; - - // Mark all elements inActive before we go over and mark the active ones - each([ticks, minorTicks, alternateBands], function (coll) { - var pos; - for (pos in coll) { - coll[pos].isActive = false; - } - }); - - // If the series has data draw the ticks. Else only the line and title - if (hasData || isLinked) { - - // minor ticks - if (axis.minorTickInterval && !axis.categories) { - each(axis.getMinorTickPositions(), function (pos) { - if (!minorTicks[pos]) { - minorTicks[pos] = new Tick(axis, pos, 'minor'); - } - - // render new ticks in old position - if (slideInTicks && minorTicks[pos].isNew) { - minorTicks[pos].render(null, true); - } - - minorTicks[pos].render(null, false, 1); - }); - } - - // Major ticks. Pull out the first item and render it last so that - // we can get the position of the neighbour label. #808. - if (tickPositions.length) { // #1300 - sortedPositions = tickPositions.slice(); - if ((horiz && reversed) || (!horiz && !reversed)) { - sortedPositions.reverse(); - } - if (justifyLabels) { - sortedPositions = sortedPositions.slice(1).concat([sortedPositions[0]]); - } - each(sortedPositions, function (pos, i) { - - // Reorganize the indices - if (justifyLabels) { - i = (i === sortedPositions.length - 1) ? 0 : i + 1; - } - - // linked axes need an extra check to find out if - if (!isLinked || (pos >= axis.min && pos <= axis.max)) { - - if (!ticks[pos]) { - ticks[pos] = new Tick(axis, pos); - } - - // render new ticks in old position - if (slideInTicks && ticks[pos].isNew) { - ticks[pos].render(i, true, 0.1); - } - - ticks[pos].render(i, false, 1); - } - - }); - // In a categorized axis, the tick marks are displayed between labels. So - // we need to add a tick mark and grid line at the left edge of the X axis. - if (tickmarkOffset && axis.min === 0) { - if (!ticks[-1]) { - ticks[-1] = new Tick(axis, -1, null, true); - } - ticks[-1].render(-1); - } - - } - - // alternate grid color - if (alternateGridColor) { - each(tickPositions, function (pos, i) { - if (i % 2 === 0 && pos < axis.max) { - if (!alternateBands[pos]) { - alternateBands[pos] = new PlotLineOrBand(axis); - } - from = pos + tickmarkOffset; // #949 - to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max; - alternateBands[pos].options = { - from: isLog ? lin2log(from) : from, - to: isLog ? lin2log(to) : to, - color: alternateGridColor - }; - alternateBands[pos].render(); - alternateBands[pos].isActive = true; - } - }); - } - - // custom plot lines and bands - if (!axis._addedPlotLB) { // only first time - each((options.plotLines || []).concat(options.plotBands || []), function (plotLineOptions) { - axis.addPlotBandOrLine(plotLineOptions); - }); - axis._addedPlotLB = true; - } - - } // end if hasData - - // Remove inactive ticks - each([ticks, minorTicks, alternateBands], function (coll) { - var pos, - i, - forDestruction = [], - delay = globalAnimation ? globalAnimation.duration || 500 : 0, - destroyInactiveItems = function () { - i = forDestruction.length; - while (i--) { - // When resizing rapidly, the same items may be destroyed in different timeouts, - // or the may be reactivated - if (coll[forDestruction[i]] && !coll[forDestruction[i]].isActive) { - coll[forDestruction[i]].destroy(); - delete coll[forDestruction[i]]; - } - } - - }; - - for (pos in coll) { - - if (!coll[pos].isActive) { - // Render to zero opacity - coll[pos].render(pos, false, 0); - coll[pos].isActive = false; - forDestruction.push(pos); - } - } - - // When the objects are finished fading out, destroy them - if (coll === alternateBands || !chart.hasRendered || !delay) { - destroyInactiveItems(); - } else if (delay) { - setTimeout(destroyInactiveItems, delay); - } - }); - - // Static items. As the axis group is cleared on subsequent calls - // to render, these items are added outside the group. - // axis line - if (lineWidth) { - linePath = axis.getLinePath(lineWidth); - if (!axis.axisLine) { - axis.axisLine = renderer.path(linePath) - .attr({ - stroke: options.lineColor, - 'stroke-width': lineWidth, - zIndex: 7 - }) - .add(axis.axisGroup); - } else { - axis.axisLine.animate({ d: linePath }); - } - - // show or hide the line depending on options.showEmpty - axis.axisLine[showAxis ? 'show' : 'hide'](); - } - - if (axisTitle && showAxis) { - - axisTitle[axisTitle.isNew ? 'attr' : 'animate']( - axis.getTitlePosition() - ); - axisTitle.isNew = false; - } - - // Stacked totals: - if (stackLabelOptions && stackLabelOptions.enabled) { - var stackKey, oneStack, stackCategory, - stackTotalGroup = axis.stackTotalGroup; - - // Create a separate group for the stack total labels - if (!stackTotalGroup) { - axis.stackTotalGroup = stackTotalGroup = - renderer.g('stack-labels') - .attr({ - visibility: VISIBLE, - zIndex: 6 - }) - .add(); - } - - // plotLeft/Top will change when y axis gets wider so we need to translate the - // stackTotalGroup at every render call. See bug #506 and #516 - stackTotalGroup.translate(chart.plotLeft, chart.plotTop); - - // Render each stack total - for (stackKey in stacks) { - oneStack = stacks[stackKey]; - for (stackCategory in oneStack) { - oneStack[stackCategory].render(stackTotalGroup); - } - } - } - // End stacked totals - - axis.isDirty = false; - }, - - /** - * Redraw the axis to reflect changes in the data or axis extremes - */ - redraw: function () { - var axis = this, - chart = axis.chart, - pointer = chart.pointer; - - // hide tooltip and hover states - if (pointer.reset) { - pointer.reset(true); - } - - // render the axis - axis.render(); - - // move plot lines and bands - each(axis.plotLinesAndBands, function (plotLine) { - plotLine.render(); - }); - - // mark associated series as dirty and ready for redraw - each(axis.series, function (series) { - series.isDirty = true; - }); - - }, - - /** - * Build the stacks from top down - */ - buildStacks: function () { - var series = this.series, - i = series.length; - if (!this.isXAxis) { - while (i--) { - series[i].setStackedPoints(); - } - // Loop up again to compute percent stack - if (this.usePercentage) { - for (i = 0; i < series.length; i++) { - series[i].setPercentStacks(); - } - } - } - }, - - /** - * Destroys an Axis instance. - */ - destroy: function (keepEvents) { - var axis = this, - stacks = axis.stacks, - stackKey, - plotLinesAndBands = axis.plotLinesAndBands, - i; - - // Remove the events - if (!keepEvents) { - removeEvent(axis); - } - - // Destroy each stack total - for (stackKey in stacks) { - destroyObjectProperties(stacks[stackKey]); - - stacks[stackKey] = null; - } - - // Destroy collections - each([axis.ticks, axis.minorTicks, axis.alternateBands], function (coll) { - destroyObjectProperties(coll); - }); - i = plotLinesAndBands.length; - while (i--) { // #1975 - plotLinesAndBands[i].destroy(); - } - - // Destroy local variables - each(['stackTotalGroup', 'axisLine', 'axisTitle', 'axisGroup', 'cross', 'gridGroup', 'labelGroup'], function (prop) { - if (axis[prop]) { - axis[prop] = axis[prop].destroy(); - } - }); - - // Destroy crosshair - if (this.cross) { - this.cross.destroy(); - } - }, - - /** - * Draw the crosshair - */ - drawCrosshair: function (e, point) { - if (!this.crosshair) { return; }// Do not draw crosshairs if you don't have too. - - if ((defined(point) || !pick(this.crosshair.snap, true)) === false) { - this.hideCrosshair(); - return; - } - - var path, - options = this.crosshair, - animation = options.animation, - pos; - - // Get the path - if (!pick(options.snap, true)) { - pos = (this.horiz ? e.chartX - this.pos : this.len - e.chartY + this.pos); - } else if (defined(point)) { - /*jslint eqeq: true*/ - pos = (this.chart.inverted != this.horiz) ? point.plotX : this.len - point.plotY; - /*jslint eqeq: false*/ - } - - if (this.isRadial) { - path = this.getPlotLinePath(this.isXAxis ? point.x : pick(point.stackY, point.y)); - } else { - path = this.getPlotLinePath(null, null, null, null, pos); - } - - if (path === null) { - this.hideCrosshair(); - return; - } - - // Draw the cross - if (this.cross) { - this.cross - .attr({ visibility: VISIBLE })[animation ? 'animate' : 'attr']({ d: path }, animation); - } else { - var attribs = { - 'stroke-width': options.width || 1, - stroke: options.color || '#C0C0C0', - zIndex: options.zIndex || 2 - }; - if (options.dashStyle) { - attribs.dashstyle = options.dashStyle; - } - this.cross = this.chart.renderer.path(path).attr(attribs).add(); - } - }, - - /** - * Hide the crosshair. - */ - hideCrosshair: function () { - if (this.cross) { - this.cross.hide(); - } - } -}; // end Axis - -extend(Axis.prototype, AxisPlotLineOrBandExtension); -/** - * Methods defined on the Axis prototype - */ - -/** - * Set the tick positions of a logarithmic axis - */ -Axis.prototype.getLogTickPositions = function (interval, min, max, minor) { - var axis = this, - options = axis.options, - axisLength = axis.len, - // Since we use this method for both major and minor ticks, - // use a local variable and return the result - positions = []; - - // Reset - if (!minor) { - axis._minorAutoInterval = null; - } - - // First case: All ticks fall on whole logarithms: 1, 10, 100 etc. - if (interval >= 0.5) { - interval = mathRound(interval); - positions = axis.getLinearTickPositions(interval, min, max); - - // Second case: We need intermediary ticks. For example - // 1, 2, 4, 6, 8, 10, 20, 40 etc. - } else if (interval >= 0.08) { - var roundedMin = mathFloor(min), - intermediate, - i, - j, - len, - pos, - lastPos, - break2; - - if (interval > 0.3) { - intermediate = [1, 2, 4]; - } else if (interval > 0.15) { // 0.2 equals five minor ticks per 1, 10, 100 etc - intermediate = [1, 2, 4, 6, 8]; - } else { // 0.1 equals ten minor ticks per 1, 10, 100 etc - intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - } - - for (i = roundedMin; i < max + 1 && !break2; i++) { - len = intermediate.length; - for (j = 0; j < len && !break2; j++) { - pos = log2lin(lin2log(i) * intermediate[j]); - - if (pos > min && (!minor || lastPos <= max)) { // #1670 - positions.push(lastPos); - } - - if (lastPos > max) { - break2 = true; - } - lastPos = pos; - } - } - - // Third case: We are so deep in between whole logarithmic values that - // we might as well handle the tick positions like a linear axis. For - // example 1.01, 1.02, 1.03, 1.04. - } else { - var realMin = lin2log(min), - realMax = lin2log(max), - tickIntervalOption = options[minor ? 'minorTickInterval' : 'tickInterval'], - filteredTickIntervalOption = tickIntervalOption === 'auto' ? null : tickIntervalOption, - tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1), - totalPixelLength = minor ? axisLength / axis.tickPositions.length : axisLength; - - interval = pick( - filteredTickIntervalOption, - axis._minorAutoInterval, - (realMax - realMin) * tickPixelIntervalOption / (totalPixelLength || 1) - ); - - interval = normalizeTickInterval( - interval, - null, - getMagnitude(interval) - ); - - positions = map(axis.getLinearTickPositions( - interval, - realMin, - realMax - ), log2lin); - - if (!minor) { - axis._minorAutoInterval = interval / 5; - } - } - - // Set the axis-level tickInterval variable - if (!minor) { - axis.tickInterval = interval; - } - return positions; -}; -/** - * Set the tick positions to a time unit that makes sense, for example - * on the first of each month or on every Monday. Return an array - * with the time positions. Used in datetime axes as well as for grouping - * data on a datetime axis. - * - * @param {Object} normalizedInterval The interval in axis values (ms) and the count - * @param {Number} min The minimum in axis values - * @param {Number} max The maximum in axis values - * @param {Number} startOfWeek - */ -Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek) { - var tickPositions = [], - i, - higherRanks = {}, - useUTC = defaultOptions.global.useUTC, - minYear, // used in months and years as a basis for Date.UTC() - minDate = new Date(min - timezoneOffset), - interval = normalizedInterval.unitRange, - count = normalizedInterval.count; - - if (defined(min)) { // #1300 - if (interval >= timeUnits[SECOND]) { // second - minDate.setMilliseconds(0); - minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 : - count * mathFloor(minDate.getSeconds() / count)); - } - - if (interval >= timeUnits[MINUTE]) { // minute - minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 : - count * mathFloor(minDate[getMinutes]() / count)); - } - - if (interval >= timeUnits[HOUR]) { // hour - minDate[setHours](interval >= timeUnits[DAY] ? 0 : - count * mathFloor(minDate[getHours]() / count)); - } - - if (interval >= timeUnits[DAY]) { // day - minDate[setDate](interval >= timeUnits[MONTH] ? 1 : - count * mathFloor(minDate[getDate]() / count)); - } - - if (interval >= timeUnits[MONTH]) { // month - minDate[setMonth](interval >= timeUnits[YEAR] ? 0 : - count * mathFloor(minDate[getMonth]() / count)); - minYear = minDate[getFullYear](); - } - - if (interval >= timeUnits[YEAR]) { // year - minYear -= minYear % count; - minDate[setFullYear](minYear); - } - - // week is a special case that runs outside the hierarchy - if (interval === timeUnits[WEEK]) { - // get start of current week, independent of count - minDate[setDate](minDate[getDate]() - minDate[getDay]() + - pick(startOfWeek, 1)); - } - - - // get tick positions - i = 1; - if (timezoneOffset) { - minDate = new Date(minDate.getTime() + timezoneOffset); - } - minYear = minDate[getFullYear](); - var time = minDate.getTime(), - minMonth = minDate[getMonth](), - minDateDate = minDate[getDate](), - localTimezoneOffset = useUTC ? - timezoneOffset : - (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950 - - // iterate and add tick positions at appropriate values - while (time < max) { - tickPositions.push(time); - - // if the interval is years, use Date.UTC to increase years - if (interval === timeUnits[YEAR]) { - time = makeTime(minYear + i * count, 0); - - // if the interval is months, use Date.UTC to increase months - } else if (interval === timeUnits[MONTH]) { - time = makeTime(minYear, minMonth + i * count); - - // if we're using global time, the interval is not fixed as it jumps - // one hour at the DST crossover - } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) { - time = makeTime(minYear, minMonth, minDateDate + - i * count * (interval === timeUnits[DAY] ? 1 : 7)); - - // else, the interval is fixed and we use simple addition - } else { - time += interval * count; - } - - i++; - } - - // push the last time - tickPositions.push(time); - - - // mark new days if the time is dividible by day (#1649, #1760) - each(grep(tickPositions, function (time) { - return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === localTimezoneOffset; - }), function (time) { - higherRanks[time] = DAY; - }); - } - - - // record information on the chosen unit - for dynamic label formatter - tickPositions.info = extend(normalizedInterval, { - higherRanks: higherRanks, - totalRange: interval * count - }); - - return tickPositions; -}; - -/** - * Get a normalized tick interval for dates. Returns a configuration object with - * unit range (interval), count and name. Used to prepare data for getTimeTicks. - * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs - * of segments in stock charts, the normalizing logic was extracted in order to - * prevent it for running over again for each segment having the same interval. - * #662, #697. - */ -Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) { - var units = unitsOption || [[ - MILLISECOND, // unit name - [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples - ], [ - SECOND, - [1, 2, 5, 10, 15, 30] - ], [ - MINUTE, - [1, 2, 5, 10, 15, 30] - ], [ - HOUR, - [1, 2, 3, 4, 6, 8, 12] - ], [ - DAY, - [1, 2] - ], [ - WEEK, - [1, 2] - ], [ - MONTH, - [1, 2, 3, 4, 6] - ], [ - YEAR, - null - ]], - unit = units[units.length - 1], // default unit is years - interval = timeUnits[unit[0]], - multiples = unit[1], - count, - i; - - // loop through the units to find the one that best fits the tickInterval - for (i = 0; i < units.length; i++) { - unit = units[i]; - interval = timeUnits[unit[0]]; - multiples = unit[1]; - - - if (units[i + 1]) { - // lessThan is in the middle between the highest multiple and the next unit. - var lessThan = (interval * multiples[multiples.length - 1] + - timeUnits[units[i + 1][0]]) / 2; - - // break and keep the current unit - if (tickInterval <= lessThan) { - break; - } - } - } - - // prevent 2.5 years intervals, though 25, 250 etc. are allowed - if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) { - multiples = [1, 2, 5]; - } - - // get the count - count = normalizeTickInterval( - tickInterval / interval, - multiples, - unit[0] === YEAR ? mathMax(getMagnitude(tickInterval / interval), 1) : 1 // #1913, #2360 - ); - - return { - unitRange: interval, - count: count, - unitName: unit[0] - }; -};/** - * The class for stack items - */ -function StackItem(axis, options, isNegative, x, stackOption, stacking) { - - var inverted = axis.chart.inverted; - - this.axis = axis; - - // Tells if the stack is negative - this.isNegative = isNegative; - - // Save the options to be able to style the label - this.options = options; - - // Save the x value to be able to position the label later - this.x = x; - - // Initialize total value - this.total = null; - - // This will keep each points' extremes stored by series.index - this.points = {}; - - // Save the stack option on the series configuration object, and whether to treat it as percent - this.stack = stackOption; - this.percent = stacking === 'percent'; - - // The align options and text align varies on whether the stack is negative and - // if the chart is inverted or not. - // First test the user supplied value, then use the dynamic. - this.alignOptions = { - align: options.align || (inverted ? (isNegative ? 'left' : 'right') : 'center'), - verticalAlign: options.verticalAlign || (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')), - y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)), - x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0) - }; - - this.textAlign = options.textAlign || (inverted ? (isNegative ? 'right' : 'left') : 'center'); -} - -StackItem.prototype = { - destroy: function () { - destroyObjectProperties(this, this.axis); - }, - - /** - * Renders the stack total label and adds it to the stack label group. - */ - render: function (group) { - var options = this.options, - formatOption = options.format, - str = formatOption ? - format(formatOption, this) : - options.formatter.call(this); // format the text in the label - - // Change the text to reflect the new total and set visibility to hidden in case the serie is hidden - if (this.label) { - this.label.attr({text: str, visibility: HIDDEN}); - // Create new label - } else { - this.label = - this.axis.chart.renderer.text(str, 0, 0, options.useHTML) // dummy positions, actual position updated with setOffset method in columnseries - .css(options.style) // apply style - .attr({ - align: this.textAlign, // fix the text-anchor - rotation: options.rotation, // rotation - visibility: HIDDEN // hidden until setOffset is called - }) - .add(group); // add to the labels-group - } - }, - - /** - * Sets the offset that the stack has from the x value and repositions the label. - */ - setOffset: function (xOffset, xWidth) { - var stackItem = this, - axis = stackItem.axis, - chart = axis.chart, - inverted = chart.inverted, - neg = this.isNegative, // special treatment is needed for negative stacks - y = axis.translate(this.percent ? 100 : this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates - yZero = axis.translate(0), // stack origin - h = mathAbs(y - yZero), // stack height - x = chart.xAxis[0].translate(this.x) + xOffset, // stack x position - plotHeight = chart.plotHeight, - stackBox = { // this is the box for the complete stack - x: inverted ? (neg ? y : y - h) : x, - y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y), - width: inverted ? h : xWidth, - height: inverted ? xWidth : h - }, - label = this.label, - alignAttr; - - if (label) { - label.align(this.alignOptions, null, stackBox); // align the label to the box - - // Set visibility (#678) - alignAttr = label.alignAttr; - label.attr({ - visibility: this.options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) ? - (hasSVG ? 'inherit' : VISIBLE) : - HIDDEN - }); - } - } -}; -/** - * The tooltip object - * @param {Object} chart The chart instance - * @param {Object} options Tooltip options - */ -function Tooltip() { - this.init.apply(this, arguments); -} - -Tooltip.prototype = { - - init: function (chart, options) { - - var borderWidth = options.borderWidth, - style = options.style, - padding = pInt(style.padding); - - // Save the chart and options - this.chart = chart; - this.options = options; - - // Keep track of the current series - //this.currentSeries = UNDEFINED; - - // List of crosshairs - this.crosshairs = []; - - // Current values of x and y when animating - this.now = { x: 0, y: 0 }; - - // The tooltip is initially hidden - this.isHidden = true; - - - // create the label - this.label = chart.renderer.label('', 0, 0, options.shape, null, null, options.useHTML, null, 'tooltip') - .attr({ - padding: padding, - fill: options.backgroundColor, - 'stroke-width': borderWidth, - r: options.borderRadius, - zIndex: 8 - }) - .css(style) - .css({ padding: 0 }) // Remove it from VML, the padding is applied as an attribute instead (#1117) - .add() - .attr({ y: -999 }); // #2301 - - // When using canVG the shadow shows up as a gray circle - // even if the tooltip is hidden. - if (!useCanVG) { - this.label.shadow(options.shadow); - } - - // Public property for getting the shared state. - this.shared = options.shared; - }, - - /** - * Destroy the tooltip and its elements. - */ - destroy: function () { - // Destroy and clear local variables - if (this.label) { - this.label = this.label.destroy(); - } - clearTimeout(this.hideTimer); - clearTimeout(this.tooltipTimeout); - }, - - /** - * Provide a soft movement for the tooltip - * - * @param {Number} x - * @param {Number} y - * @private - */ - move: function (x, y, anchorX, anchorY) { - var tooltip = this, - now = tooltip.now, - animate = tooltip.options.animation !== false && !tooltip.isHidden; - - // get intermediate values for animation - extend(now, { - x: animate ? (2 * now.x + x) / 3 : x, - y: animate ? (now.y + y) / 2 : y, - anchorX: animate ? (2 * now.anchorX + anchorX) / 3 : anchorX, - anchorY: animate ? (now.anchorY + anchorY) / 2 : anchorY - }); - - // move to the intermediate value - tooltip.label.attr(now); - - - // run on next tick of the mouse tracker - if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) { - - // never allow two timeouts - clearTimeout(this.tooltipTimeout); - - // set the fixed interval ticking for the smooth tooltip - this.tooltipTimeout = setTimeout(function () { - // The interval function may still be running during destroy, so check that the chart is really there before calling. - if (tooltip) { - tooltip.move(x, y, anchorX, anchorY); - } - }, 32); - - } - }, - - /** - * Hide the tooltip - */ - hide: function () { - var tooltip = this, - hoverPoints; - - clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766) - if (!this.isHidden) { - hoverPoints = this.chart.hoverPoints; - - this.hideTimer = setTimeout(function () { - tooltip.label.fadeOut(); - tooltip.isHidden = true; - }, pick(this.options.hideDelay, 500)); - - // hide previous hoverPoints and set new - if (hoverPoints) { - each(hoverPoints, function (point) { - point.setState(); - }); - } - - this.chart.hoverPoints = null; - } - }, - - /** - * Extendable method to get the anchor position of the tooltip - * from a point or set of points - */ - getAnchor: function (points, mouseEvent) { - var ret, - chart = this.chart, - inverted = chart.inverted, - plotTop = chart.plotTop, - plotX = 0, - plotY = 0, - yAxis; - - points = splat(points); - - // Pie uses a special tooltipPos - ret = points[0].tooltipPos; - - // When tooltip follows mouse, relate the position to the mouse - if (this.followPointer && mouseEvent) { - if (mouseEvent.chartX === UNDEFINED) { - mouseEvent = chart.pointer.normalize(mouseEvent); - } - ret = [ - mouseEvent.chartX - chart.plotLeft, - mouseEvent.chartY - plotTop - ]; - } - // When shared, use the average position - if (!ret) { - each(points, function (point) { - yAxis = point.series.yAxis; - plotX += point.plotX; - plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) + - (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151 - }); - - plotX /= points.length; - plotY /= points.length; - - ret = [ - inverted ? chart.plotWidth - plotY : plotX, - this.shared && !inverted && points.length > 1 && mouseEvent ? - mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424) - inverted ? chart.plotHeight - plotX : plotY - ]; - } - - return map(ret, mathRound); - }, - - /** - * Place the tooltip in a chart without spilling over - * and not covering the point it self. - */ - getPosition: function (boxWidth, boxHeight, point) { - - // Set up the variables - var chart = this.chart, - plotLeft = chart.plotLeft, - plotTop = chart.plotTop, - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - distance = pick(this.options.distance, 12), - pointX = point.plotX, - pointY = point.plotY, - x = pointX + plotLeft + (chart.inverted ? distance : -boxWidth - distance), - y = pointY - boxHeight + plotTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip - alignedRight; - - // It is too far to the left, adjust it - if (x < 7) { - x = plotLeft + mathMax(pointX, 0) + distance; - } - - // Test to see if the tooltip is too far to the right, - // if it is, move it back to be inside and then up to not cover the point. - if ((x + boxWidth) > (plotLeft + plotWidth)) { - x -= (x + boxWidth) - (plotLeft + plotWidth); - y = pointY - boxHeight + plotTop - distance; - alignedRight = true; - } - - // If it is now above the plot area, align it to the top of the plot area - if (y < plotTop + 5) { - y = plotTop + 5; - - // If the tooltip is still covering the point, move it below instead - if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) { - y = pointY + plotTop + distance; // below - } - } - - // Now if the tooltip is below the chart, move it up. It's better to cover the - // point than to disappear outside the chart. #834. - if (y + boxHeight > plotTop + plotHeight) { - y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below - } - - return {x: x, y: y}; - }, - - /** - * In case no user defined formatter is given, this will be used. Note that the context - * here is an object holding point, series, x, y etc. - */ - defaultFormatter: function (tooltip) { - var items = this.points || splat(this), - series = items[0].series, - s; - - // build the header - s = [series.tooltipHeaderFormatter(items[0])]; - - // build the values - each(items, function (item) { - series = item.series; - s.push((series.tooltipFormatter && series.tooltipFormatter(item)) || - item.point.tooltipFormatter(series.tooltipOptions.pointFormat)); - }); - - // footer - s.push(tooltip.options.footerFormat || ''); - - return s.join(''); - }, - - /** - * Refresh the tooltip's text and position. - * @param {Object} point - */ - refresh: function (point, mouseEvent) { - var tooltip = this, - chart = tooltip.chart, - label = tooltip.label, - options = tooltip.options, - x, - y, - anchor, - textConfig = {}, - text, - pointConfig = [], - formatter = options.formatter || tooltip.defaultFormatter, - hoverPoints = chart.hoverPoints, - borderColor, - shared = tooltip.shared, - currentSeries; - - clearTimeout(this.hideTimer); - - // get the reference point coordinates (pie charts use tooltipPos) - tooltip.followPointer = splat(point)[0].series.tooltipOptions.followPointer; - anchor = tooltip.getAnchor(point, mouseEvent); - x = anchor[0]; - y = anchor[1]; - - // shared tooltip, array is sent over - if (shared && !(point.series && point.series.noSharedTooltip)) { - - // hide previous hoverPoints and set new - - chart.hoverPoints = point; - if (hoverPoints) { - each(hoverPoints, function (point) { - point.setState(); - }); - } - - each(point, function (item) { - item.setState(HOVER_STATE); - - pointConfig.push(item.getLabelConfig()); - }); - - textConfig = { - x: point[0].category, - y: point[0].y - }; - textConfig.points = pointConfig; - point = point[0]; - - // single point tooltip - } else { - textConfig = point.getLabelConfig(); - } - text = formatter.call(textConfig, tooltip); - - // register the current series - currentSeries = point.series; - - // update the inner HTML - if (text === false) { - this.hide(); - } else { - - // show it - if (tooltip.isHidden) { - stop(label); - label.attr('opacity', 1).show(); - } - - // update text - label.attr({ - text: text - }); - - // set the stroke color of the box - borderColor = options.borderColor || point.color || currentSeries.color || '#606060'; - label.attr({ - stroke: borderColor - }); - - tooltip.updatePosition({ plotX: x, plotY: y }); - - this.isHidden = false; - } - fireEvent(chart, 'tooltipRefresh', { - text: text, - x: x + chart.plotLeft, - y: y + chart.plotTop, - borderColor: borderColor - }); - }, - - /** - * Find the new position and perform the move - */ - updatePosition: function (point) { - var chart = this.chart, - label = this.label, - pos = (this.options.positioner || this.getPosition).call( - this, - label.width, - label.height, - point - ); - - // do the move - this.move( - mathRound(pos.x), - mathRound(pos.y), - point.plotX + chart.plotLeft, - point.plotY + chart.plotTop - ); - } -}; -/** - * The mouse tracker object. All methods starting with "on" are primary DOM event handlers. - * Subsequent methods should be named differently from what they are doing. - * @param {Object} chart The Chart instance - * @param {Object} options The root options object - */ -var Pointer = Highcharts.Pointer = function (chart, options) { - this.init(chart, options); -}; - -Pointer.prototype = { - /** - * Initialize Pointer - */ - init: function (chart, options) { - - var chartOptions = options.chart, - chartEvents = chartOptions.events, - zoomType = useCanVG ? '' : chartOptions.zoomType, - inverted = chart.inverted, - zoomX, - zoomY; - - // Store references - this.options = options; - this.chart = chart; - - // Zoom status - this.zoomX = zoomX = /x/.test(zoomType); - this.zoomY = zoomY = /y/.test(zoomType); - this.zoomHor = (zoomX && !inverted) || (zoomY && inverted); - this.zoomVert = (zoomY && !inverted) || (zoomX && inverted); - - // Do we need to handle click on a touch device? - this.runChartClick = chartEvents && !!chartEvents.click; - - this.pinchDown = []; - this.lastValidTouch = {}; - - if (options.tooltip.enabled) { - chart.tooltip = new Tooltip(chart, options.tooltip); - } - - this.setDOMEvents(); - }, - - /** - * Add crossbrowser support for chartX and chartY - * @param {Object} e The event object in standard browsers - */ - normalize: function (e, chartPosition) { - var chartX, - chartY, - ePos; - - // common IE normalizing - e = e || win.event; - if (!e.target) { - e.target = e.srcElement; - } - - // Framework specific normalizing (#1165) - e = washMouseEvent(e); - - // iOS - ePos = e.touches ? e.touches.item(0) : e; - - // Get mouse position - if (!chartPosition) { - this.chartPosition = chartPosition = offset(this.chart.container); - } - - // chartX and chartY - if (ePos.pageX === UNDEFINED) { // IE < 9. #886. - chartX = mathMax(e.x, e.clientX - chartPosition.left); // #2005, #2129: the second case is - // for IE10 quirks mode within framesets - chartY = e.y; - } else { - chartX = ePos.pageX - chartPosition.left; - chartY = ePos.pageY - chartPosition.top; - } - - return extend(e, { - chartX: mathRound(chartX), - chartY: mathRound(chartY) - }); - }, - - /** - * Get the click position in terms of axis values. - * - * @param {Object} e A pointer event - */ - getCoordinates: function (e) { - var coordinates = { - xAxis: [], - yAxis: [] - }; - - each(this.chart.axes, function (axis) { - coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({ - axis: axis, - value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY']) - }); - }); - return coordinates; - }, - - /** - * Return the index in the tooltipPoints array, corresponding to pixel position in - * the plot area. - */ - getIndex: function (e) { - var chart = this.chart; - return chart.inverted ? - chart.plotHeight + chart.plotTop - e.chartY : - e.chartX - chart.plotLeft; - }, - - /** - * With line type charts with a single tracker, get the point closest to the mouse. - * Run Point.onMouseOver and display tooltip for the point or points. - */ - runPointActions: function (e) { - var pointer = this, - chart = pointer.chart, - series = chart.series, - tooltip = chart.tooltip, - point, - points, - hoverPoint = chart.hoverPoint, - hoverSeries = chart.hoverSeries, - i, - j, - distance = chart.chartWidth, - index = pointer.getIndex(e), - anchor; - - // shared tooltip - if (tooltip && pointer.options.tooltip.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) { - points = []; - - // loop over all series and find the ones with points closest to the mouse - i = series.length; - for (j = 0; j < i; j++) { - if (series[j].visible && - series[j].options.enableMouseTracking !== false && - !series[j].noSharedTooltip && series[j].tooltipPoints.length) { - point = series[j].tooltipPoints[index]; - if (point && point.series) { // not a dummy point, #1544 - point._dist = mathAbs(index - point.clientX); - distance = mathMin(distance, point._dist); - points.push(point); - } - } - } - // remove furthest points - i = points.length; - while (i--) { - if (points[i]._dist > distance) { - points.splice(i, 1); - } - } - // refresh the tooltip if necessary - if (points.length && (points[0].clientX !== pointer.hoverX)) { - tooltip.refresh(points, e); - pointer.hoverX = points[0].clientX; - } - } - - // separate tooltip and general mouse events - if (hoverSeries && hoverSeries.tracker) { // only use for line-type series with common tracker - - // get the point - point = hoverSeries.tooltipPoints[index]; - - // a new point is hovered, refresh the tooltip - if (point && point !== hoverPoint) { - - // trigger the events - point.onMouseOver(e); - - } - - } else if (tooltip && tooltip.followPointer && !tooltip.isHidden) { - anchor = tooltip.getAnchor([{}], e); - tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] }); - } - - // Start the event listener to pick up the tooltip - if (tooltip && !pointer._onDocumentMouseMove) { - pointer._onDocumentMouseMove = function (e) { - pointer.onDocumentMouseMove(e); - }; - addEvent(doc, 'mousemove', pointer._onDocumentMouseMove); - } - - // Draw independent crosshairs - each(chart.axes, function (axis) { - axis.drawCrosshair(e, pick(point, hoverPoint)); - }); - }, - - - - /** - * Reset the tracking by hiding the tooltip, the hover series state and the hover point - * - * @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible - */ - reset: function (allowMove) { - var pointer = this, - chart = pointer.chart, - hoverSeries = chart.hoverSeries, - hoverPoint = chart.hoverPoint, - tooltip = chart.tooltip, - tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint; - - // Narrow in allowMove - allowMove = allowMove && tooltip && tooltipPoints; - - // Check if the points have moved outside the plot area, #1003 - if (allowMove && splat(tooltipPoints)[0].plotX === UNDEFINED) { - allowMove = false; - } - - // Just move the tooltip, #349 - if (allowMove) { - tooltip.refresh(tooltipPoints); - if (hoverPoint) { // #2500 - hoverPoint.setState(hoverPoint.state, true); - } - - // Full reset - } else { - - if (hoverPoint) { - hoverPoint.onMouseOut(); - } - - if (hoverSeries) { - hoverSeries.onMouseOut(); - } - - if (tooltip) { - tooltip.hide(); - } - - if (pointer._onDocumentMouseMove) { - removeEvent(doc, 'mousemove', pointer._onDocumentMouseMove); - pointer._onDocumentMouseMove = null; - } - - // Remove crosshairs - each(chart.axes, function (axis) { - axis.hideCrosshair(); - }); - - pointer.hoverX = null; - - } - }, - - /** - * Scale series groups to a certain scale and translation - */ - scaleGroups: function (attribs, clip) { - - var chart = this.chart, - seriesAttribs; - - // Scale each series - each(chart.series, function (series) { - seriesAttribs = attribs || series.getPlotBox(); // #1701 - if (series.xAxis && series.xAxis.zoomEnabled) { - series.group.attr(seriesAttribs); - if (series.markerGroup) { - series.markerGroup.attr(seriesAttribs); - series.markerGroup.clip(clip ? chart.clipRect : null); - } - if (series.dataLabelsGroup) { - series.dataLabelsGroup.attr(seriesAttribs); - } - } - }); - - // Clip - chart.clipRect.attr(clip || chart.clipBox); - }, - - /** - * Run translation operations - */ - pinchTranslate: function (zoomHor, zoomVert, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) { - if (zoomHor) { - this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); - } - if (zoomVert) { - this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); - } - }, - - /** - * Run translation operations for each direction (horizontal and vertical) independently - */ - pinchTranslateDirection: function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) { - var chart = this.chart, - xy = horiz ? 'x' : 'y', - XY = horiz ? 'X' : 'Y', - sChartXY = 'chart' + XY, - wh = horiz ? 'width' : 'height', - plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], - selectionWH, - selectionXY, - clipXY, - scale = forcedScale || 1, - inverted = chart.inverted, - bounds = chart.bounds[horiz ? 'h' : 'v'], - singleTouch = pinchDown.length === 1, - touch0Start = pinchDown[0][sChartXY], - touch0Now = touches[0][sChartXY], - touch1Start = !singleTouch && pinchDown[1][sChartXY], - touch1Now = !singleTouch && touches[1][sChartXY], - outOfBounds, - transformScale, - scaleKey, - setScale = function () { - if (!singleTouch && mathAbs(touch0Start - touch1Start) > 20) { // Don't zoom if fingers are too close on this axis - scale = forcedScale || mathAbs(touch0Now - touch1Now) / mathAbs(touch0Start - touch1Start); - } - - clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start; - selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale; - }; - - // Set the scale, first pass - setScale(); - - selectionXY = clipXY; // the clip position (x or y) is altered if out of bounds, the selection position is not - - // Out of bounds - if (selectionXY < bounds.min) { - selectionXY = bounds.min; - outOfBounds = true; - } else if (selectionXY + selectionWH > bounds.max) { - selectionXY = bounds.max - selectionWH; - outOfBounds = true; - } - - // Is the chart dragged off its bounds, determined by dataMin and dataMax? - if (outOfBounds) { - - // Modify the touchNow position in order to create an elastic drag movement. This indicates - // to the user that the chart is responsive but can't be dragged further. - touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]); - if (!singleTouch) { - touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]); - } - - // Set the scale, second pass to adapt to the modified touchNow positions - setScale(); - - } else { - lastValidTouch[xy] = [touch0Now, touch1Now]; - } - - // Set geometry for clipping, selection and transformation - if (!inverted) { // TODO: implement clipping for inverted charts - clip[xy] = clipXY - plotLeftTop; - clip[wh] = selectionWH; - } - scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY; - transformScale = inverted ? 1 / scale : scale; - - selectionMarker[wh] = selectionWH; - selectionMarker[xy] = selectionXY; - transform[scaleKey] = scale; - transform['translate' + XY] = (transformScale * plotLeftTop) + (touch0Now - (transformScale * touch0Start)); - }, - - /** - * Handle touch events with two touches - */ - pinch: function (e) { - - var self = this, - chart = self.chart, - pinchDown = self.pinchDown, - followTouchMove = chart.tooltip && chart.tooltip.options.followTouchMove, - touches = e.touches, - touchesLength = touches.length, - lastValidTouch = self.lastValidTouch, - zoomHor = self.zoomHor || self.pinchHor, - zoomVert = self.zoomVert || self.pinchVert, - hasZoom = zoomHor || zoomVert, - selectionMarker = self.selectionMarker, - transform = {}, - fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, PREFIX + 'tracker') && - chart.runTrackerClick) || chart.runChartClick), - clip = {}; - - // On touch devices, only proceed to trigger click if a handler is defined - if ((hasZoom || followTouchMove) && !fireClickEvent) { - e.preventDefault(); - } - - // Normalize each touch - map(touches, function (e) { - return self.normalize(e); - }); - - // Register the touch start position - if (e.type === 'touchstart') { - each(touches, function (e, i) { - pinchDown[i] = { chartX: e.chartX, chartY: e.chartY }; - }); - lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] && pinchDown[1].chartX]; - lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] && pinchDown[1].chartY]; - - // Identify the data bounds in pixels - each(chart.axes, function (axis) { - if (axis.zoomEnabled) { - var bounds = chart.bounds[axis.horiz ? 'h' : 'v'], - minPixelPadding = axis.minPixelPadding, - min = axis.toPixels(axis.dataMin), - max = axis.toPixels(axis.dataMax), - absMin = mathMin(min, max), - absMax = mathMax(min, max); - - // Store the bounds for use in the touchmove handler - bounds.min = mathMin(axis.pos, absMin - minPixelPadding); - bounds.max = mathMax(axis.pos + axis.len, absMax + minPixelPadding); - } - }); - - // Event type is touchmove, handle panning and pinching - } else if (pinchDown.length) { // can be 0 when releasing, if touchend fires first - - - // Set the marker - if (!selectionMarker) { - self.selectionMarker = selectionMarker = extend({ - destroy: noop - }, chart.plotBox); - } - - self.pinchTranslate(zoomHor, zoomVert, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); - - self.hasPinched = hasZoom; - - // Scale and translate the groups to provide visual feedback during pinching - self.scaleGroups(transform, clip); - - // Optionally move the tooltip on touchmove - if (!hasZoom && followTouchMove && touchesLength === 1) { - this.runPointActions(self.normalize(e)); - } - } - }, - - /** - * Start a drag operation - */ - dragStart: function (e) { - var chart = this.chart; - - // Record the start position - chart.mouseIsDown = e.type; - chart.cancelClick = false; - chart.mouseDownX = this.mouseDownX = e.chartX; - chart.mouseDownY = this.mouseDownY = e.chartY; - }, - - /** - * Perform a drag operation in response to a mousemove event while the mouse is down - */ - drag: function (e) { - - var chart = this.chart, - chartOptions = chart.options.chart, - chartX = e.chartX, - chartY = e.chartY, - zoomHor = this.zoomHor, - zoomVert = this.zoomVert, - plotLeft = chart.plotLeft, - plotTop = chart.plotTop, - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - clickedInside, - size, - mouseDownX = this.mouseDownX, - mouseDownY = this.mouseDownY; - - // If the mouse is outside the plot area, adjust to cooordinates - // inside to prevent the selection marker from going outside - if (chartX < plotLeft) { - chartX = plotLeft; - } else if (chartX > plotLeft + plotWidth) { - chartX = plotLeft + plotWidth; - } - - if (chartY < plotTop) { - chartY = plotTop; - } else if (chartY > plotTop + plotHeight) { - chartY = plotTop + plotHeight; - } - - // determine if the mouse has moved more than 10px - this.hasDragged = Math.sqrt( - Math.pow(mouseDownX - chartX, 2) + - Math.pow(mouseDownY - chartY, 2) - ); - if (this.hasDragged > 10) { - clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop); - - // make a selection - if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside) { - if (!this.selectionMarker) { - this.selectionMarker = chart.renderer.rect( - plotLeft, - plotTop, - zoomHor ? 1 : plotWidth, - zoomVert ? 1 : plotHeight, - 0 - ) - .attr({ - fill: chartOptions.selectionMarkerFill || 'rgba(69,114,167,0.25)', - zIndex: 7 - }) - .add(); - } - } - - // adjust the width of the selection marker - if (this.selectionMarker && zoomHor) { - size = chartX - mouseDownX; - this.selectionMarker.attr({ - width: mathAbs(size), - x: (size > 0 ? 0 : size) + mouseDownX - }); - } - // adjust the height of the selection marker - if (this.selectionMarker && zoomVert) { - size = chartY - mouseDownY; - this.selectionMarker.attr({ - height: mathAbs(size), - y: (size > 0 ? 0 : size) + mouseDownY - }); - } - - // panning - if (clickedInside && !this.selectionMarker && chartOptions.panning) { - chart.pan(e, chartOptions.panning); - } - } - }, - - /** - * On mouse up or touch end across the entire document, drop the selection. - */ - drop: function (e) { - var chart = this.chart, - hasPinched = this.hasPinched; - - if (this.selectionMarker) { - var selectionData = { - xAxis: [], - yAxis: [], - originalEvent: e.originalEvent || e - }, - selectionBox = this.selectionMarker, - selectionLeft = selectionBox.x, - selectionTop = selectionBox.y, - runZoom; - // a selection has been made - if (this.hasDragged || hasPinched) { - - // record each axis' min and max - each(chart.axes, function (axis) { - if (axis.zoomEnabled) { - var horiz = axis.horiz, - selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)), - selectionMax = axis.toValue((horiz ? selectionLeft + selectionBox.width : selectionTop + selectionBox.height)); - - if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859 - selectionData[axis.coll].push({ - axis: axis, - min: mathMin(selectionMin, selectionMax), // for reversed axes, - max: mathMax(selectionMin, selectionMax) - }); - runZoom = true; - } - } - }); - if (runZoom) { - fireEvent(chart, 'selection', selectionData, function (args) { - chart.zoom(extend(args, hasPinched ? { animation: false } : null)); - }); - } - - } - this.selectionMarker = this.selectionMarker.destroy(); - - // Reset scaling preview - if (hasPinched) { - this.scaleGroups(); - } - } - - // Reset all - if (chart) { // it may be destroyed on mouse up - #877 - css(chart.container, { cursor: chart._cursor }); - chart.cancelClick = this.hasDragged > 10; // #370 - chart.mouseIsDown = this.hasDragged = this.hasPinched = false; - this.pinchDown = []; - } - }, - - onContainerMouseDown: function (e) { - - e = this.normalize(e); - - // issue #295, dragging not always working in Firefox - if (e.preventDefault) { - e.preventDefault(); - } - - this.dragStart(e); - }, - - - - onDocumentMouseUp: function (e) { - this.drop(e); - }, - - /** - * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea. - * Issue #149 workaround. The mouseleave event does not always fire. - */ - onDocumentMouseMove: function (e) { - var chart = this.chart, - chartPosition = this.chartPosition, - hoverSeries = chart.hoverSeries; - - e = this.normalize(e, chartPosition); - - // If we're outside, hide the tooltip - if (chartPosition && hoverSeries && !this.inClass(e.target, 'highcharts-tracker') && - !chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - this.reset(); - } - }, - - /** - * When mouse leaves the container, hide the tooltip. - */ - onContainerMouseLeave: function () { - this.reset(); - this.chartPosition = null; // also reset the chart position, used in #149 fix - }, - - // The mousemove, touchmove and touchstart event handler - onContainerMouseMove: function (e) { - - var chart = this.chart; - - // normalize - e = this.normalize(e); - - if (chart.mouseIsDown === 'mousedown') { - this.drag(e); - } - - // Show the tooltip and run mouse over events (#977) - if ((this.inClass(e.target, 'highcharts-tracker') || - chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) && !chart.openMenu) { - this.runPointActions(e); - } - }, - - /** - * Utility to detect whether an element has, or has a parent with, a specific - * class name. Used on detection of tracker objects and on deciding whether - * hovering the tooltip should cause the active series to mouse out. - */ - inClass: function (element, className) { - var elemClassName; - while (element) { - elemClassName = attr(element, 'class'); - if (elemClassName) { - if (elemClassName.indexOf(className) !== -1) { - return true; - } else if (elemClassName.indexOf(PREFIX + 'container') !== -1) { - return false; - } - } - element = element.parentNode; - } - }, - - onTrackerMouseOut: function (e) { - var series = this.chart.hoverSeries, - relatedTarget = e.relatedTarget || e.toElement, - relatedSeries = relatedTarget && relatedTarget.point && relatedTarget.point.series; // #2499 - - if (series && !series.options.stickyTracking && !this.inClass(relatedTarget, PREFIX + 'tooltip') && - relatedSeries !== series) { - series.onMouseOut(); - } - }, - - onContainerClick: function (e) { - var chart = this.chart, - hoverPoint = chart.hoverPoint, - plotLeft = chart.plotLeft, - plotTop = chart.plotTop, - inverted = chart.inverted, - chartPosition, - plotX, - plotY; - - e = this.normalize(e); - e.cancelBubble = true; // IE specific - - if (!chart.cancelClick) { - - // On tracker click, fire the series and point events. #783, #1583 - if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) { - chartPosition = this.chartPosition; - plotX = hoverPoint.plotX; - plotY = hoverPoint.plotY; - - // add page position info - extend(hoverPoint, { - pageX: chartPosition.left + plotLeft + - (inverted ? chart.plotWidth - plotY : plotX), - pageY: chartPosition.top + plotTop + - (inverted ? chart.plotHeight - plotX : plotY) - }); - - // the series click event - fireEvent(hoverPoint.series, 'click', extend(e, { - point: hoverPoint - })); - - // the point click event - if (chart.hoverPoint) { // it may be destroyed (#1844) - hoverPoint.firePointEvent('click', e); - } - - // When clicking outside a tracker, fire a chart event - } else { - extend(e, this.getCoordinates(e)); - - // fire a click event in the chart - if (chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) { - fireEvent(chart, 'click', e); - } - } - - - } - }, - - onContainerTouchStart: function (e) { - var chart = this.chart; - - if (e.touches.length === 1) { - - e = this.normalize(e); - - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - - // Prevent the click pseudo event from firing unless it is set in the options - /*if (!chart.runChartClick) { - e.preventDefault(); - }*/ - - // Run mouse events and display tooltip etc - this.runPointActions(e); - - this.pinch(e); - - } else { - // Hide the tooltip on touching outside the plot area (#1203) - this.reset(); - } - - } else if (e.touches.length === 2) { - this.pinch(e); - } - }, - - onContainerTouchMove: function (e) { - if (e.touches.length === 1 || e.touches.length === 2) { - this.pinch(e); - } - }, - - onDocumentTouchEnd: function (e) { - this.drop(e); - }, - - /** - * Set the JS DOM events on the container and document. This method should contain - * a one-to-one assignment between methods and their handlers. Any advanced logic should - * be moved to the handler reflecting the event's name. - */ - setDOMEvents: function () { - - var pointer = this, - container = pointer.chart.container, - events; - - this._events = events = [ - [container, 'onmousedown', 'onContainerMouseDown'], - [container, 'onmousemove', 'onContainerMouseMove'], - [container, 'onclick', 'onContainerClick'], - [container, 'mouseleave', 'onContainerMouseLeave'], - [doc, 'mouseup', 'onDocumentMouseUp'] - ]; - - if (hasTouch) { - events.push( - [container, 'ontouchstart', 'onContainerTouchStart'], - [container, 'ontouchmove', 'onContainerTouchMove'], - [doc, 'touchend', 'onDocumentTouchEnd'] - ); - } - - each(events, function (eventConfig) { - - // First, create the callback function that in turn calls the method on Pointer - pointer['_' + eventConfig[2]] = function (e) { - pointer[eventConfig[2]](e); - }; - - // Now attach the function, either as a direct property or through addEvent - if (eventConfig[1].indexOf('on') === 0) { - eventConfig[0][eventConfig[1]] = pointer['_' + eventConfig[2]]; - } else { - addEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]); - } - }); - - - }, - - /** - * Destroys the Pointer object and disconnects DOM events. - */ - destroy: function () { - var pointer = this; - - // Release all DOM events - each(pointer._events, function (eventConfig) { - if (eventConfig[1].indexOf('on') === 0) { - eventConfig[0][eventConfig[1]] = null; // delete breaks oldIE - } else { - removeEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]); - } - }); - delete pointer._events; - - // memory and CPU leak - clearInterval(pointer.tooltipTimeout); - } -}; - - -/** - * PointTrackerMixin - */ - -var TrackerMixin = Highcharts.TrackerMixin = { - drawTrackerPoint: function () { - var series = this, - chart = series.chart, - pointer = chart.pointer, - cursor = series.options.cursor, - css = cursor && { cursor: cursor }, - onMouseOver = function (e) { - var target = e.target, - point; - - if (chart.hoverSeries !== series) { - series.onMouseOver(); - } - while (target && !point) { - point = target.point; - target = target.parentNode; - } - if (point !== UNDEFINED && point !== chart.hoverPoint) { // undefined on graph in scatterchart - point.onMouseOver(e); - } - }; - - // Add reference to the point - each(series.points, function (point) { - if (point.graphic) { - point.graphic.element.point = point; - } - if (point.dataLabel) { - point.dataLabel.element.point = point; - } - }); - - // Add the event listeners, we need to do this only once - if (!series._hasTracking) { - each(series.trackerGroups, function (key) { - if (series[key]) { // we don't always have dataLabelsGroup - series[key] - .addClass(PREFIX + 'tracker') - .on('mouseover', onMouseOver) - .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); }) - .css(css); - if (hasTouch) { - series[key].on('touchstart', onMouseOver); - } - } - }); - series._hasTracking = true; - } - }, - - /** - * Draw the tracker object that sits above all data labels and markers to - * track mouse events on the graph or points. For the line type charts - * the tracker uses the same graphPath, but with a greater stroke width - * for better control. - */ - drawTrackerGraph: function () { - var series = this, - options = series.options, - trackByArea = options.trackByArea, - trackerPath = [].concat(trackByArea ? series.areaPath : series.graphPath), - trackerPathLength = trackerPath.length, - chart = series.chart, - pointer = chart.pointer, - renderer = chart.renderer, - snap = chart.options.tooltip.snap, - tracker = series.tracker, - cursor = options.cursor, - css = cursor && { cursor: cursor }, - singlePoints = series.singlePoints, - singlePoint, - i, - onMouseOver = function () { - if (chart.hoverSeries !== series) { - series.onMouseOver(); - } - }; - - // Extend end points. A better way would be to use round linecaps, - // but those are not clickable in VML. - if (trackerPathLength && !trackByArea) { - i = trackerPathLength + 1; - while (i--) { - if (trackerPath[i] === M) { // extend left side - trackerPath.splice(i + 1, 0, trackerPath[i + 1] - snap, trackerPath[i + 2], L); - } - if ((i && trackerPath[i] === M) || i === trackerPathLength) { // extend right side - trackerPath.splice(i, 0, L, trackerPath[i - 2] + snap, trackerPath[i - 1]); - } - } - } - - // handle single points - for (i = 0; i < singlePoints.length; i++) { - singlePoint = singlePoints[i]; - trackerPath.push(M, singlePoint.plotX - snap, singlePoint.plotY, - L, singlePoint.plotX + snap, singlePoint.plotY); - } - - // draw the tracker - if (tracker) { - tracker.attr({ d: trackerPath }); - - } else { // create - - series.tracker = renderer.path(trackerPath) - .attr({ - 'stroke-linejoin': 'round', // #1225 - visibility: series.visible ? VISIBLE : HIDDEN, - stroke: TRACKER_FILL, - fill: trackByArea ? TRACKER_FILL : NONE, - 'stroke-width' : options.lineWidth + (trackByArea ? 0 : 2 * snap), - zIndex: 2 - }) - .add(series.group); - - // The tracker is added to the series group, which is clipped, but is covered - // by the marker group. So the marker group also needs to capture events. - each([series.tracker, series.markerGroup], function (tracker) { - tracker.addClass(PREFIX + 'tracker') - .on('mouseover', onMouseOver) - .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); }) - .css(css); - - if (hasTouch) { - tracker.on('touchstart', onMouseOver); - } - }); - } - - } -}; -if (win.PointerEvent || win.MSPointerEvent) { - - // The touches object keeps track of the points being touched at all times - var touches = {}; - - // Emulate a Webkit TouchList - Pointer.prototype.getWebkitTouches = function () { - var key, fake = []; - fake.item = function (i) { return this[i]; }; - for (key in touches) { - if (touches.hasOwnProperty(key)) { - fake.push({ - pageX: touches[key].pageX, - pageY: touches[key].pageY, - target: touches[key].target - }); - } - } - return fake; - }; - - // Disable default IE actions for pinch and such on chart element - wrap(Pointer.prototype, 'init', function (proceed, chart, options) { - chart.container.style["-ms-touch-action"] = chart.container.style["touch-action"] = "none"; - proceed.call(this, chart, options); - }); - - // Add IE specific touch events to chart - wrap(Pointer.prototype, 'setDOMEvents', function (proceed) { - var pointer = this, eventmap; - proceed.apply(this, Array.prototype.slice.call(arguments, 1)); - eventmap = [ - [this.chart.container, "PointerDown", "touchstart", "onContainerTouchStart", function (e) { - touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY, target: e.currentTarget }; - }], - [this.chart.container, "PointerMove", "touchmove", "onContainerTouchMove", function (e) { - touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY }; - if (!touches[e.pointerId].target) { - touches[e.pointerId].target = e.currentTarget; - } - }], - [document, "PointerUp", "touchend", "onDocumentTouchEnd", function (e) { - delete touches[e.pointerId]; - }] - ]; - - each(eventmap, function (eventConfig) { - addEvent(eventConfig[0], window.PointerEvent ? eventConfig[1].toLowerCase() : "MS" + eventConfig[1], function (e) { - e = e.originalEvent; - if (e.pointerType === "touch" || e.pointerType === e.MSPOINTER_TYPE_TOUCH) { - eventConfig[4](e); - - // This event corresponds to ontouchstart - call onContainerTouchStart - pointer[eventConfig[3]]({ - type: eventConfig[2], - target: e.currentTarget, - preventDefault: noop, - touches: pointer.getWebkitTouches() - }); - } - }); - }); - - }); -} -/** - * The overview of the chart's series - */ -var Legend = Highcharts.Legend = function (chart, options) { - this.init(chart, options); -}; - -Legend.prototype = { - - /** - * Initialize the legend - */ - init: function (chart, options) { - - var legend = this, - itemStyle = options.itemStyle, - padding = pick(options.padding, 8), - itemMarginTop = options.itemMarginTop || 0; - - this.options = options; - - if (!options.enabled) { - return; - } - - legend.baseline = pInt(itemStyle.fontSize) + 3 + itemMarginTop; // used in Series prototype - legend.itemStyle = itemStyle; - legend.itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle); - legend.itemMarginTop = itemMarginTop; - legend.padding = padding; - legend.initialItemX = padding; - legend.initialItemY = padding - 5; // 5 is the number of pixels above the text - legend.maxItemWidth = 0; - legend.chart = chart; - legend.itemHeight = 0; - legend.lastLineHeight = 0; - legend.symbolWidth = pick(options.symbolWidth, 16); - legend.pages = []; - - - // Render it - legend.render(); - - // move checkboxes - addEvent(legend.chart, 'endResize', function () { - legend.positionCheckboxes(); - }); - - }, - - /** - * Set the colors for the legend item - * @param {Object} item A Series or Point instance - * @param {Object} visible Dimmed or colored - */ - colorizeItem: function (item, visible) { - var legend = this, - options = legend.options, - legendItem = item.legendItem, - legendLine = item.legendLine, - legendSymbol = item.legendSymbol, - hiddenColor = legend.itemHiddenStyle.color, - textColor = visible ? options.itemStyle.color : hiddenColor, - symbolColor = visible ? (item.legendColor || item.color) : hiddenColor, - markerOptions = item.options && item.options.marker, - symbolAttr = { - stroke: symbolColor, - fill: symbolColor - }, - key, - val; - - if (legendItem) { - legendItem.css({ fill: textColor, color: textColor }); // color for #1553, oldIE - } - if (legendLine) { - legendLine.attr({ stroke: symbolColor }); - } - - if (legendSymbol) { - - // Apply marker options - if (markerOptions && legendSymbol.isMarker) { // #585 - markerOptions = item.convertAttribs(markerOptions); - for (key in markerOptions) { - val = markerOptions[key]; - if (val !== UNDEFINED) { - symbolAttr[key] = val; - } - } - } - - legendSymbol.attr(symbolAttr); - } - }, - - /** - * Position the legend item - * @param {Object} item A Series or Point instance - */ - positionItem: function (item) { - var legend = this, - options = legend.options, - symbolPadding = options.symbolPadding, - ltr = !options.rtl, - legendItemPos = item._legendItemPos, - itemX = legendItemPos[0], - itemY = legendItemPos[1], - checkbox = item.checkbox; - - if (item.legendGroup) { - item.legendGroup.translate( - ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4, - itemY - ); - } - - if (checkbox) { - checkbox.x = itemX; - checkbox.y = itemY; - } - }, - - /** - * Destroy a single legend item - * @param {Object} item The series or point - */ - destroyItem: function (item) { - var checkbox = item.checkbox; - - // destroy SVG elements - each(['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'], function (key) { - if (item[key]) { - item[key] = item[key].destroy(); - } - }); - - if (checkbox) { - discardElement(item.checkbox); - } - }, - - /** - * Destroys the legend. - */ - destroy: function () { - var legend = this, - legendGroup = legend.group, - box = legend.box; - - if (box) { - legend.box = box.destroy(); - } - - if (legendGroup) { - legend.group = legendGroup.destroy(); - } - }, - - /** - * Position the checkboxes after the width is determined - */ - positionCheckboxes: function (scrollOffset) { - var alignAttr = this.group.alignAttr, - translateY, - clipHeight = this.clipHeight || this.legendHeight; - - if (alignAttr) { - translateY = alignAttr.translateY; - each(this.allItems, function (item) { - var checkbox = item.checkbox, - top; - - if (checkbox) { - top = (translateY + checkbox.y + (scrollOffset || 0) + 3); - css(checkbox, { - left: (alignAttr.translateX + item.legendItemWidth + checkbox.x - 20) + PX, - top: top + PX, - display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : NONE - }); - } - }); - } - }, - - /** - * Render the legend title on top of the legend - */ - renderTitle: function () { - var options = this.options, - padding = this.padding, - titleOptions = options.title, - titleHeight = 0, - bBox; - - if (titleOptions.text) { - if (!this.title) { - this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title') - .attr({ zIndex: 1 }) - .css(titleOptions.style) - .add(this.group); - } - bBox = this.title.getBBox(); - titleHeight = bBox.height; - this.offsetWidth = bBox.width; // #1717 - this.contentGroup.attr({ translateY: titleHeight }); - } - this.titleHeight = titleHeight; - }, - - /** - * Render a single specific legend item - * @param {Object} item A series or point - */ - renderItem: function (item) { - var legend = this, - chart = legend.chart, - renderer = chart.renderer, - options = legend.options, - horizontal = options.layout === 'horizontal', - symbolWidth = legend.symbolWidth, - symbolPadding = options.symbolPadding, - itemStyle = legend.itemStyle, - itemHiddenStyle = legend.itemHiddenStyle, - padding = legend.padding, - itemDistance = horizontal ? pick(options.itemDistance, 8) : 0, - ltr = !options.rtl, - itemHeight, - widthOption = options.width, - itemMarginBottom = options.itemMarginBottom || 0, - itemMarginTop = legend.itemMarginTop, - initialItemX = legend.initialItemX, - bBox, - itemWidth, - li = item.legendItem, - series = item.series && item.series.drawLegendSymbol ? item.series : item, - seriesOptions = series.options, - showCheckbox = seriesOptions && seriesOptions.showCheckbox, - useHTML = options.useHTML; - - if (!li) { // generate it once, later move it - - // Generate the group box - // A group to hold the symbol and text. Text is to be appended in Legend class. - item.legendGroup = renderer.g('legend-item') - .attr({ zIndex: 1 }) - .add(legend.scrollGroup); - - // Draw the legend symbol inside the group box - series.drawLegendSymbol(legend, item); - - // Generate the list item text and add it to the group - item.legendItem = li = renderer.text( - options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item), - ltr ? symbolWidth + symbolPadding : -symbolPadding, - legend.baseline, - useHTML - ) - .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021) - .attr({ - align: ltr ? 'left' : 'right', - zIndex: 2 - }) - .add(item.legendGroup); - - // Set the events on the item group, or in case of useHTML, the item itself (#1249) - (useHTML ? li : item.legendGroup).on('mouseover', function () { - item.setState(HOVER_STATE); - li.css(legend.options.itemHoverStyle); - }) - .on('mouseout', function () { - li.css(item.visible ? itemStyle : itemHiddenStyle); - item.setState(); - }) - .on('click', function (event) { - var strLegendItemClick = 'legendItemClick', - fnLegendItemClick = function () { - item.setVisible(); - }; - - // Pass over the click/touch event. #4. - event = { - browserEvent: event - }; - - // click the name or symbol - if (item.firePointEvent) { // point - item.firePointEvent(strLegendItemClick, event, fnLegendItemClick); - } else { - fireEvent(item, strLegendItemClick, event, fnLegendItemClick); - } - }); - - // Colorize the items - legend.colorizeItem(item, item.visible); - - // add the HTML checkbox on top - if (showCheckbox) { - item.checkbox = createElement('input', { - type: 'checkbox', - checked: item.selected, - defaultChecked: item.selected // required by IE7 - }, options.itemCheckboxStyle, chart.container); - - addEvent(item.checkbox, 'click', function (event) { - var target = event.target; - fireEvent(item, 'checkboxClick', { - checked: target.checked - }, - function () { - item.select(); - } - ); - }); - } - } - - // calculate the positions for the next line - bBox = li.getBBox(); - - itemWidth = item.legendItemWidth = - options.itemWidth || item.legendItemWidth || symbolWidth + symbolPadding + bBox.width + itemDistance + - (showCheckbox ? 20 : 0); - legend.itemHeight = itemHeight = mathRound(item.legendItemHeight || bBox.height); - - // if the item exceeds the width, start a new line - if (horizontal && legend.itemX - initialItemX + itemWidth > - (widthOption || (chart.chartWidth - 2 * padding - initialItemX))) { - legend.itemX = initialItemX; - legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom; - legend.lastLineHeight = 0; // reset for next line - } - - // If the item exceeds the height, start a new column - /*if (!horizontal && legend.itemY + options.y + itemHeight > chart.chartHeight - spacingTop - spacingBottom) { - legend.itemY = legend.initialItemY; - legend.itemX += legend.maxItemWidth; - legend.maxItemWidth = 0; - }*/ - - // Set the edge positions - legend.maxItemWidth = mathMax(legend.maxItemWidth, itemWidth); - legend.lastItemY = itemMarginTop + legend.itemY + itemMarginBottom; - legend.lastLineHeight = mathMax(itemHeight, legend.lastLineHeight); // #915 - - // cache the position of the newly generated or reordered items - item._legendItemPos = [legend.itemX, legend.itemY]; - - // advance - if (horizontal) { - legend.itemX += itemWidth; - - } else { - legend.itemY += itemMarginTop + itemHeight + itemMarginBottom; - legend.lastLineHeight = itemHeight; - } - - // the width of the widest item - legend.offsetWidth = widthOption || mathMax( - (horizontal ? legend.itemX - initialItemX - itemDistance : itemWidth) + padding, - legend.offsetWidth - ); - }, - - /** - * Get all items, which is one item per series for normal series and one item per point - * for pie series. - */ - getAllItems: function () { - var allItems = []; - each(this.chart.series, function (series) { - var seriesOptions = series.options; - - // Handle showInLegend. If the series is linked to another series, defaults to false. - if (!pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? UNDEFINED : false, true)) { - return; - } - - // use points or series for the legend item depending on legendType - allItems = allItems.concat( - series.legendItems || - (seriesOptions.legendType === 'point' ? - series.data : - series) - ); - }); - return allItems; - }, - - /** - * Render the legend. This method can be called both before and after - * chart.render. If called after, it will only rearrange items instead - * of creating new ones. - */ - render: function () { - var legend = this, - chart = legend.chart, - renderer = chart.renderer, - legendGroup = legend.group, - allItems, - display, - legendWidth, - legendHeight, - box = legend.box, - options = legend.options, - padding = legend.padding, - legendBorderWidth = options.borderWidth, - legendBackgroundColor = options.backgroundColor; - - legend.itemX = legend.initialItemX; - legend.itemY = legend.initialItemY; - legend.offsetWidth = 0; - legend.lastItemY = 0; - - if (!legendGroup) { - legend.group = legendGroup = renderer.g('legend') - .attr({ zIndex: 7 }) - .add(); - legend.contentGroup = renderer.g() - .attr({ zIndex: 1 }) // above background - .add(legendGroup); - legend.scrollGroup = renderer.g() - .add(legend.contentGroup); - } - - legend.renderTitle(); - - // add each series or point - allItems = legend.getAllItems(); - - // sort by legendIndex - stableSort(allItems, function (a, b) { - return ((a.options && a.options.legendIndex) || 0) - ((b.options && b.options.legendIndex) || 0); - }); - - // reversed legend - if (options.reversed) { - allItems.reverse(); - } - - legend.allItems = allItems; - legend.display = display = !!allItems.length; - - // render the items - each(allItems, function (item) { - legend.renderItem(item); - }); - - // Draw the border - legendWidth = options.width || legend.offsetWidth; - legendHeight = legend.lastItemY + legend.lastLineHeight + legend.titleHeight; - - - legendHeight = legend.handleOverflow(legendHeight); - - if (legendBorderWidth || legendBackgroundColor) { - legendWidth += padding; - legendHeight += padding; - - if (!box) { - legend.box = box = renderer.rect( - 0, - 0, - legendWidth, - legendHeight, - options.borderRadius, - legendBorderWidth || 0 - ).attr({ - stroke: options.borderColor, - 'stroke-width': legendBorderWidth || 0, - fill: legendBackgroundColor || NONE - }) - .add(legendGroup) - .shadow(options.shadow); - box.isNew = true; - - } else if (legendWidth > 0 && legendHeight > 0) { - box[box.isNew ? 'attr' : 'animate']( - box.crisp(null, null, null, legendWidth, legendHeight) - ); - box.isNew = false; - } - - // hide the border if no items - box[display ? 'show' : 'hide'](); - } - - legend.legendWidth = legendWidth; - legend.legendHeight = legendHeight; - - // Now that the legend width and height are established, put the items in the - // final position - each(allItems, function (item) { - legend.positionItem(item); - }); - - // 1.x compatibility: positioning based on style - /*var props = ['left', 'right', 'top', 'bottom'], - prop, - i = 4; - while (i--) { - prop = props[i]; - if (options.style[prop] && options.style[prop] !== 'auto') { - options[i < 2 ? 'align' : 'verticalAlign'] = prop; - options[i < 2 ? 'x' : 'y'] = pInt(options.style[prop]) * (i % 2 ? -1 : 1); - } - }*/ - - if (display) { - legendGroup.align(extend({ - width: legendWidth, - height: legendHeight - }, options), true, 'spacingBox'); - } - - if (!chart.isResizing) { - this.positionCheckboxes(); - } - }, - - /** - * Set up the overflow handling by adding navigation with up and down arrows below the - * legend. - */ - handleOverflow: function (legendHeight) { - var legend = this, - chart = this.chart, - renderer = chart.renderer, - options = this.options, - optionsY = options.y, - alignTop = options.verticalAlign === 'top', - spaceHeight = chart.spacingBox.height + (alignTop ? -optionsY : optionsY) - this.padding, - maxHeight = options.maxHeight, - clipHeight, - clipRect = this.clipRect, - navOptions = options.navigation, - animation = pick(navOptions.animation, true), - arrowSize = navOptions.arrowSize || 12, - nav = this.nav, - pages = this.pages, - lastY, - allItems = this.allItems; - - // Adjust the height - if (options.layout === 'horizontal') { - spaceHeight /= 2; - } - if (maxHeight) { - spaceHeight = mathMin(spaceHeight, maxHeight); - } - - // Reset the legend height and adjust the clipping rectangle - pages.length = 0; - if (legendHeight > spaceHeight && !options.useHTML) { - - this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight - this.padding; - this.currentPage = pick(this.currentPage, 1); - this.fullHeight = legendHeight; - - // Fill pages with Y positions so that the top of each a legend item defines - // the scroll top for each page (#2098) - each(allItems, function (item, i) { - var y = item._legendItemPos[1], - h = mathRound(item.legendItem.bBox.height), - len = pages.length; - - if (!len || (y - pages[len - 1] > clipHeight)) { - pages.push(lastY || y); - } - - if (i === allItems.length - 1 && y + h - pages[len - 1] > clipHeight) { - pages.push(y); - } - if (y !== lastY) { - lastY = y; - } - }); - - // Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787) - if (!clipRect) { - clipRect = legend.clipRect = renderer.clipRect(0, this.padding, 9999, 0); - legend.contentGroup.clip(clipRect); - } - clipRect.attr({ - height: clipHeight - }); - - // Add navigation elements - if (!nav) { - this.nav = nav = renderer.g().attr({ zIndex: 1 }).add(this.group); - this.up = renderer.symbol('triangle', 0, 0, arrowSize, arrowSize) - .on('click', function () { - legend.scroll(-1, animation); - }) - .add(nav); - this.pager = renderer.text('', 15, 10) - .css(navOptions.style) - .add(nav); - this.down = renderer.symbol('triangle-down', 0, 0, arrowSize, arrowSize) - .on('click', function () { - legend.scroll(1, animation); - }) - .add(nav); - } - - // Set initial position - legend.scroll(0); - - legendHeight = spaceHeight; - - } else if (nav) { - clipRect.attr({ - height: chart.chartHeight - }); - nav.hide(); - this.scrollGroup.attr({ - translateY: 1 - }); - this.clipHeight = 0; // #1379 - } - - return legendHeight; - }, - - /** - * Scroll the legend by a number of pages - * @param {Object} scrollBy - * @param {Object} animation - */ - scroll: function (scrollBy, animation) { - var pages = this.pages, - pageCount = pages.length, - currentPage = this.currentPage + scrollBy, - clipHeight = this.clipHeight, - navOptions = this.options.navigation, - activeColor = navOptions.activeColor, - inactiveColor = navOptions.inactiveColor, - pager = this.pager, - padding = this.padding, - scrollOffset; - - // When resizing while looking at the last page - if (currentPage > pageCount) { - currentPage = pageCount; - } - - if (currentPage > 0) { - - if (animation !== UNDEFINED) { - setAnimation(animation, this.chart); - } - - this.nav.attr({ - translateX: padding, - translateY: clipHeight + this.padding + 7 + this.titleHeight, - visibility: VISIBLE - }); - this.up.attr({ - fill: currentPage === 1 ? inactiveColor : activeColor - }) - .css({ - cursor: currentPage === 1 ? 'default' : 'pointer' - }); - pager.attr({ - text: currentPage + '/' + pageCount - }); - this.down.attr({ - x: 18 + this.pager.getBBox().width, // adjust to text width - fill: currentPage === pageCount ? inactiveColor : activeColor - }) - .css({ - cursor: currentPage === pageCount ? 'default' : 'pointer' - }); - - scrollOffset = -pages[currentPage - 1] + this.initialItemY; - - this.scrollGroup.animate({ - translateY: scrollOffset - }); - - this.currentPage = currentPage; - this.positionCheckboxes(scrollOffset); - } - - } - -}; - -/* - * LegendSymbolMixin - */ - -var LegendSymbolMixin = Highcharts.LegendSymbolMixin = { - - /** - * Get the series' symbol in the legend - * - * @param {Object} legend The legend object - * @param {Object} item The series (this) or point - */ - drawRectangle: function (legend, item) { - var symbolHeight = legend.options.symbolHeight || 12; - - item.legendSymbol = this.chart.renderer.rect( - 0, - legend.baseline - 5 - (symbolHeight / 2), - legend.symbolWidth, - symbolHeight, - pick(legend.options.symbolRadius, 2) - ).attr({ - zIndex: 3 - }).add(item.legendGroup); - - }, - - /** - * Get the series' symbol in the legend. This method should be overridable to create custom - * symbols through Highcharts.seriesTypes[type].prototype.drawLegendSymbols. - * - * @param {Object} legend The legend object - */ - drawLineMarker: function (legend) { - - var options = this.options, - markerOptions = options.marker, - radius, - legendOptions = legend.options, - legendSymbol, - symbolWidth = legend.symbolWidth, - renderer = this.chart.renderer, - legendItemGroup = this.legendGroup, - verticalCenter = legend.baseline - mathRound(renderer.fontMetrics(legendOptions.itemStyle.fontSize).b * 0.3), - attr; - - // Draw the line - if (options.lineWidth) { - attr = { - 'stroke-width': options.lineWidth - }; - if (options.dashStyle) { - attr.dashstyle = options.dashStyle; - } - this.legendLine = renderer.path([ - M, - 0, - verticalCenter, - L, - symbolWidth, - verticalCenter - ]) - .attr(attr) - .add(legendItemGroup); - } - - // Draw the marker - if (markerOptions && markerOptions.enabled) { - radius = markerOptions.radius; - this.legendSymbol = legendSymbol = renderer.symbol( - this.symbol, - (symbolWidth / 2) - radius, - verticalCenter - radius, - 2 * radius, - 2 * radius - ) - .add(legendItemGroup); - legendSymbol.isMarker = true; - } - } -}; - -// Workaround for #2030, horizontal legend items not displaying in IE11 Preview. -// TODO: When IE11 is released, check again for this bug, and remove the fix -// or make a better one. -if (/Trident\/7\.0/.test(userAgent)) { - wrap(Legend.prototype, 'positionItem', function (proceed, item) { - var legend = this, - runPositionItem = function () { // If chart destroyed in sync, this is undefined (#2030) - if (item._legendItemPos) { - proceed.call(legend, item); - } - }; - - if (legend.chart.renderer.forExport) { - runPositionItem(); - } else { - setTimeout(runPositionItem); - } - }); -} -/** - * The chart class - * @param {Object} options - * @param {Function} callback Function to run when the chart has loaded - */ -function Chart() { - this.init.apply(this, arguments); -} - -Chart.prototype = { - - /** - * Initialize the chart - */ - init: function (userOptions, callback) { - - // Handle regular options - var options, - seriesOptions = userOptions.series; // skip merging data points to increase performance - - userOptions.series = null; - options = merge(defaultOptions, userOptions); // do the merge - options.series = userOptions.series = seriesOptions; // set back the series data - this.userOptions = userOptions; - - var optionsChart = options.chart; - - // Create margin & spacing array - this.margin = this.splashArray('margin', optionsChart); - this.spacing = this.splashArray('spacing', optionsChart); - - var chartEvents = optionsChart.events; - - //this.runChartClick = chartEvents && !!chartEvents.click; - this.bounds = { h: {}, v: {} }; // Pixel data bounds for touch zoom - - this.callback = callback; - this.isResizing = 0; - this.options = options; - //chartTitleOptions = UNDEFINED; - //chartSubtitleOptions = UNDEFINED; - - this.axes = []; - this.series = []; - this.hasCartesianSeries = optionsChart.showAxes; - //this.axisOffset = UNDEFINED; - //this.maxTicks = UNDEFINED; // handle the greatest amount of ticks on grouped axes - //this.inverted = UNDEFINED; - //this.loadingShown = UNDEFINED; - //this.container = UNDEFINED; - //this.chartWidth = UNDEFINED; - //this.chartHeight = UNDEFINED; - //this.marginRight = UNDEFINED; - //this.marginBottom = UNDEFINED; - //this.containerWidth = UNDEFINED; - //this.containerHeight = UNDEFINED; - //this.oldChartWidth = UNDEFINED; - //this.oldChartHeight = UNDEFINED; - - //this.renderTo = UNDEFINED; - //this.renderToClone = UNDEFINED; - - //this.spacingBox = UNDEFINED - - //this.legend = UNDEFINED; - - // Elements - //this.chartBackground = UNDEFINED; - //this.plotBackground = UNDEFINED; - //this.plotBGImage = UNDEFINED; - //this.plotBorder = UNDEFINED; - //this.loadingDiv = UNDEFINED; - //this.loadingSpan = UNDEFINED; - - var chart = this, - eventType; - - // Add the chart to the global lookup - chart.index = charts.length; - charts.push(chart); - - // Set up auto resize - if (optionsChart.reflow !== false) { - addEvent(chart, 'load', function () { - chart.initReflow(); - }); - } - - // Chart event handlers - if (chartEvents) { - for (eventType in chartEvents) { - addEvent(chart, eventType, chartEvents[eventType]); - } - } - - chart.xAxis = []; - chart.yAxis = []; - - // Expose methods and variables - chart.animation = useCanVG ? false : pick(optionsChart.animation, true); - chart.pointCount = 0; - chart.counters = new ChartCounters(); - - chart.firstRender(); - }, - - /** - * Initialize an individual series, called internally before render time - */ - initSeries: function (options) { - var chart = this, - optionsChart = chart.options.chart, - type = options.type || optionsChart.type || optionsChart.defaultSeriesType, - series, - constr = seriesTypes[type]; - - // No such series type - if (!constr) { - error(17, true); - } - - series = new constr(); - series.init(this, options); - return series; - }, - - /** - * Check whether a given point is within the plot area - * - * @param {Number} plotX Pixel x relative to the plot area - * @param {Number} plotY Pixel y relative to the plot area - * @param {Boolean} inverted Whether the chart is inverted - */ - isInsidePlot: function (plotX, plotY, inverted) { - var x = inverted ? plotY : plotX, - y = inverted ? plotX : plotY; - - return x >= 0 && - x <= this.plotWidth && - y >= 0 && - y <= this.plotHeight; - }, - - /** - * Adjust all axes tick amounts - */ - adjustTickAmounts: function () { - if (this.options.chart.alignTicks !== false) { - each(this.axes, function (axis) { - axis.adjustTickAmount(); - }); - } - this.maxTicks = null; - }, - - /** - * Redraw legend, axes or series based on updated data - * - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - */ - redraw: function (animation) { - var chart = this, - axes = chart.axes, - series = chart.series, - pointer = chart.pointer, - legend = chart.legend, - redrawLegend = chart.isDirtyLegend, - hasStackedSeries, - hasDirtyStacks, - isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed? - seriesLength = series.length, - i = seriesLength, - serie, - renderer = chart.renderer, - isHiddenChart = renderer.isHidden(), - afterRedraw = []; - - setAnimation(animation, chart); - - if (isHiddenChart) { - chart.cloneRenderTo(); - } - - // Adjust title layout (reflow multiline text) - chart.layOutTitles(); - - // link stacked series - while (i--) { - serie = series[i]; - - if (serie.options.stacking) { - hasStackedSeries = true; - - if (serie.isDirty) { - hasDirtyStacks = true; - break; - } - } - } - if (hasDirtyStacks) { // mark others as dirty - i = seriesLength; - while (i--) { - serie = series[i]; - if (serie.options.stacking) { - serie.isDirty = true; - } - } - } - - // handle updated data in the series - each(series, function (serie) { - if (serie.isDirty) { // prepare the data so axis can read it - if (serie.options.legendType === 'point') { - redrawLegend = true; - } - } - }); - - // handle added or removed series - if (redrawLegend && legend.options.enabled) { // series or pie points are added or removed - // draw legend graphics - legend.render(); - - chart.isDirtyLegend = false; - } - - // reset stacks - if (hasStackedSeries) { - chart.getStacks(); - } - - - if (chart.hasCartesianSeries) { - if (!chart.isResizing) { - - // reset maxTicks - chart.maxTicks = null; - - // set axes scales - each(axes, function (axis) { - axis.setScale(); - }); - } - - chart.adjustTickAmounts(); - chart.getMargins(); - - // If one axis is dirty, all axes must be redrawn (#792, #2169) - each(axes, function (axis) { - if (axis.isDirty) { - isDirtyBox = true; - } - }); - - // redraw axes - each(axes, function (axis) { - - // Fire 'afterSetExtremes' only if extremes are set - if (axis.isDirtyExtremes) { // #821 - axis.isDirtyExtremes = false; - afterRedraw.push(function () { // prevent a recursive call to chart.redraw() (#1119) - fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751 - delete axis.eventArgs; - }); - } - - if (isDirtyBox || hasStackedSeries) { - axis.redraw(); - } - }); - - - } - // the plot areas size has changed - if (isDirtyBox) { - chart.drawChartBox(); - } - - - // redraw affected series - each(series, function (serie) { - if (serie.isDirty && serie.visible && - (!serie.isCartesian || serie.xAxis)) { // issue #153 - serie.redraw(); - } - }); - - // move tooltip or reset - if (pointer && pointer.reset) { - pointer.reset(true); - } - - // redraw if canvas - renderer.draw(); - - // fire the event - fireEvent(chart, 'redraw'); // jQuery breaks this when calling it from addEvent. Overwrites chart.redraw - - if (isHiddenChart) { - chart.cloneRenderTo(true); - } - - // Fire callbacks that are put on hold until after the redraw - each(afterRedraw, function (callback) { - callback.call(); - }); - }, - - /** - * Get an axis, series or point object by id. - * @param id {String} The id as given in the configuration options - */ - get: function (id) { - var chart = this, - axes = chart.axes, - series = chart.series; - - var i, - j, - points; - - // search axes - for (i = 0; i < axes.length; i++) { - if (axes[i].options.id === id) { - return axes[i]; - } - } - - // search series - for (i = 0; i < series.length; i++) { - if (series[i].options.id === id) { - return series[i]; - } - } - - // search points - for (i = 0; i < series.length; i++) { - points = series[i].points || []; - for (j = 0; j < points.length; j++) { - if (points[j].id === id) { - return points[j]; - } - } - } - return null; - }, - - /** - * Create the Axis instances based on the config options - */ - getAxes: function () { - var chart = this, - options = this.options, - xAxisOptions = options.xAxis = splat(options.xAxis || {}), - yAxisOptions = options.yAxis = splat(options.yAxis || {}), - optionsArray, - axis; - - // make sure the options are arrays and add some members - each(xAxisOptions, function (axis, i) { - axis.index = i; - axis.isX = true; - }); - - each(yAxisOptions, function (axis, i) { - axis.index = i; - }); - - // concatenate all axis options into one array - optionsArray = xAxisOptions.concat(yAxisOptions); - - each(optionsArray, function (axisOptions) { - axis = new Axis(chart, axisOptions); - }); - - chart.adjustTickAmounts(); - }, - - - /** - * Get the currently selected points from all series - */ - getSelectedPoints: function () { - var points = []; - each(this.series, function (serie) { - points = points.concat(grep(serie.points || [], function (point) { - return point.selected; - })); - }); - return points; - }, - - /** - * Get the currently selected series - */ - getSelectedSeries: function () { - return grep(this.series, function (serie) { - return serie.selected; - }); - }, - - /** - * Generate stacks for each series and calculate stacks total values - */ - getStacks: function () { - var chart = this; - - // reset stacks for each yAxis - each(chart.yAxis, function (axis) { - if (axis.stacks && axis.hasVisibleSeries) { - axis.oldStacks = axis.stacks; - } - }); - - each(chart.series, function (series) { - if (series.options.stacking && (series.visible === true || chart.options.chart.ignoreHiddenSeries === false)) { - series.stackKey = series.type + pick(series.options.stack, ''); - } - }); - }, - - /** - * Display the zoom button - */ - showResetZoom: function () { - var chart = this, - lang = defaultOptions.lang, - btnOptions = chart.options.chart.resetZoomButton, - theme = btnOptions.theme, - states = theme.states, - alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox'; - - this.resetZoomButton = chart.renderer.button(lang.resetZoom, null, null, function () { chart.zoomOut(); }, theme, states && states.hover) - .attr({ - align: btnOptions.position.align, - title: lang.resetZoomTitle - }) - .add() - .align(btnOptions.position, false, alignTo); - - }, - - /** - * Zoom out to 1:1 - */ - zoomOut: function () { - var chart = this; - fireEvent(chart, 'selection', { resetSelection: true }, function () { - chart.zoom(); - }); - }, - - /** - * Zoom into a given portion of the chart given by axis coordinates - * @param {Object} event - */ - zoom: function (event) { - var chart = this, - hasZoomed, - pointer = chart.pointer, - displayButton = false, - resetZoomButton; - - // If zoom is called with no arguments, reset the axes - if (!event || event.resetSelection) { - each(chart.axes, function (axis) { - hasZoomed = axis.zoom(); - }); - } else { // else, zoom in on all axes - each(event.xAxis.concat(event.yAxis), function (axisData) { - var axis = axisData.axis, - isXAxis = axis.isXAxis; - - // don't zoom more than minRange - if (pointer[isXAxis ? 'zoomX' : 'zoomY'] || pointer[isXAxis ? 'pinchX' : 'pinchY']) { - hasZoomed = axis.zoom(axisData.min, axisData.max); - if (axis.displayBtn) { - displayButton = true; - } - } - }); - } - - // Show or hide the Reset zoom button - resetZoomButton = chart.resetZoomButton; - if (displayButton && !resetZoomButton) { - chart.showResetZoom(); - } else if (!displayButton && isObject(resetZoomButton)) { - chart.resetZoomButton = resetZoomButton.destroy(); - } - - - // Redraw - if (hasZoomed) { - chart.redraw( - pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100) // animation - ); - } - }, - - /** - * Pan the chart by dragging the mouse across the pane. This function is called - * on mouse move, and the distance to pan is computed from chartX compared to - * the first chartX position in the dragging operation. - */ - pan: function (e, panning) { - - var chart = this, - hoverPoints = chart.hoverPoints, - doRedraw; - - // remove active points for shared tooltip - if (hoverPoints) { - each(hoverPoints, function (point) { - point.setState(); - }); - } - - each(panning === 'xy' ? [1, 0] : [1], function (isX) { // xy is used in maps - var mousePos = e[isX ? 'chartX' : 'chartY'], - axis = chart[isX ? 'xAxis' : 'yAxis'][0], - startPos = chart[isX ? 'mouseDownX' : 'mouseDownY'], - halfPointRange = (axis.pointRange || 0) / 2, - extremes = axis.getExtremes(), - newMin = axis.toValue(startPos - mousePos, true) + halfPointRange, - newMax = axis.toValue(startPos + chart[isX ? 'plotWidth' : 'plotHeight'] - mousePos, true) - halfPointRange; - - if (axis.series.length && newMin > mathMin(extremes.dataMin, extremes.min) && newMax < mathMax(extremes.dataMax, extremes.max)) { - axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' }); - doRedraw = true; - } - - chart[isX ? 'mouseDownX' : 'mouseDownY'] = mousePos; // set new reference for next run - }); - - if (doRedraw) { - chart.redraw(false); - } - css(chart.container, { cursor: 'move' }); - }, - - /** - * Show the title and subtitle of the chart - * - * @param titleOptions {Object} New title options - * @param subtitleOptions {Object} New subtitle options - * - */ - setTitle: function (titleOptions, subtitleOptions) { - var chart = this, - options = chart.options, - chartTitleOptions, - chartSubtitleOptions; - - chartTitleOptions = options.title = merge(options.title, titleOptions); - chartSubtitleOptions = options.subtitle = merge(options.subtitle, subtitleOptions); - - // add title and subtitle - each([ - ['title', titleOptions, chartTitleOptions], - ['subtitle', subtitleOptions, chartSubtitleOptions] - ], function (arr) { - var name = arr[0], - title = chart[name], - titleOptions = arr[1], - chartTitleOptions = arr[2]; - - if (title && titleOptions) { - chart[name] = title = title.destroy(); // remove old - } - - if (chartTitleOptions && chartTitleOptions.text && !title) { - chart[name] = chart.renderer.text( - chartTitleOptions.text, - 0, - 0, - chartTitleOptions.useHTML - ) - .attr({ - align: chartTitleOptions.align, - 'class': PREFIX + name, - zIndex: chartTitleOptions.zIndex || 4 - }) - .css(chartTitleOptions.style) - .add(); - } - }); - chart.layOutTitles(); - }, - - /** - * Lay out the chart titles and cache the full offset height for use in getMargins - */ - layOutTitles: function () { - var titleOffset = 0, - title = this.title, - subtitle = this.subtitle, - options = this.options, - titleOptions = options.title, - subtitleOptions = options.subtitle, - autoWidth = this.spacingBox.width - 44; // 44 makes room for default context button - - if (title) { - title - .css({ width: (titleOptions.width || autoWidth) + PX }) - .align(extend({ y: 15 }, titleOptions), false, 'spacingBox'); - - if (!titleOptions.floating && !titleOptions.verticalAlign) { - titleOffset = title.getBBox().height; - - // Adjust for browser consistency + backwards compat after #776 fix - if (titleOffset >= 18 && titleOffset <= 25) { - titleOffset = 15; - } - } - } - if (subtitle) { - subtitle - .css({ width: (subtitleOptions.width || autoWidth) + PX }) - .align(extend({ y: titleOffset + titleOptions.margin }, subtitleOptions), false, 'spacingBox'); - - if (!subtitleOptions.floating && !subtitleOptions.verticalAlign) { - titleOffset = mathCeil(titleOffset + subtitle.getBBox().height); - } - } - - this.titleOffset = titleOffset; // used in getMargins - }, - - /** - * Get chart width and height according to options and container size - */ - getChartSize: function () { - var chart = this, - optionsChart = chart.options.chart, - renderTo = chart.renderToClone || chart.renderTo; - - // get inner width and height from jQuery (#824) - chart.containerWidth = adapterRun(renderTo, 'width'); - chart.containerHeight = adapterRun(renderTo, 'height'); - - chart.chartWidth = mathMax(0, optionsChart.width || chart.containerWidth || 600); // #1393, 1460 - chart.chartHeight = mathMax(0, pick(optionsChart.height, - // the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7: - chart.containerHeight > 19 ? chart.containerHeight : 400)); - }, - - /** - * Create a clone of the chart's renderTo div and place it outside the viewport to allow - * size computation on chart.render and chart.redraw - */ - cloneRenderTo: function (revert) { - var clone = this.renderToClone, - container = this.container; - - // Destroy the clone and bring the container back to the real renderTo div - if (revert) { - if (clone) { - this.renderTo.appendChild(container); - discardElement(clone); - delete this.renderToClone; - } - - // Set up the clone - } else { - if (container && container.parentNode === this.renderTo) { - this.renderTo.removeChild(container); // do not clone this - } - this.renderToClone = clone = this.renderTo.cloneNode(0); - css(clone, { - position: ABSOLUTE, - top: '-9999px', - display: 'block' // #833 - }); - doc.body.appendChild(clone); - if (container) { - clone.appendChild(container); - } - } - }, - - /** - * Get the containing element, determine the size and create the inner container - * div to hold the chart - */ - getContainer: function () { - var chart = this, - container, - optionsChart = chart.options.chart, - chartWidth, - chartHeight, - renderTo, - indexAttrName = 'data-highcharts-chart', - oldChartIndex, - containerId; - - chart.renderTo = renderTo = optionsChart.renderTo; - containerId = PREFIX + idCounter++; - - if (isString(renderTo)) { - chart.renderTo = renderTo = doc.getElementById(renderTo); - } - - // Display an error if the renderTo is wrong - if (!renderTo) { - error(13, true); - } - - // If the container already holds a chart, destroy it - oldChartIndex = pInt(attr(renderTo, indexAttrName)); - if (!isNaN(oldChartIndex) && charts[oldChartIndex]) { - charts[oldChartIndex].destroy(); - } - - // Make a reference to the chart from the div - attr(renderTo, indexAttrName, chart.index); - - // remove previous chart - renderTo.innerHTML = ''; - - // If the container doesn't have an offsetWidth, it has or is a child of a node - // that has display:none. We need to temporarily move it out to a visible - // state to determine the size, else the legend and tooltips won't render - // properly - if (!renderTo.offsetWidth) { - chart.cloneRenderTo(); - } - - // get the width and height - chart.getChartSize(); - chartWidth = chart.chartWidth; - chartHeight = chart.chartHeight; - - // create the inner container - chart.container = container = createElement(DIV, { - className: PREFIX + 'container' + - (optionsChart.className ? ' ' + optionsChart.className : ''), - id: containerId - }, extend({ - position: RELATIVE, - overflow: HIDDEN, // needed for context menu (avoid scrollbars) and - // content overflow in IE - width: chartWidth + PX, - height: chartHeight + PX, - textAlign: 'left', - lineHeight: 'normal', // #427 - zIndex: 0, // #1072 - '-webkit-tap-highlight-color': 'rgba(0,0,0,0)' - }, optionsChart.style), - chart.renderToClone || renderTo - ); - - // cache the cursor (#1650) - chart._cursor = container.style.cursor; - - chart.renderer = - optionsChart.forExport ? // force SVG, used for SVG export - new SVGRenderer(container, chartWidth, chartHeight, true) : - new Renderer(container, chartWidth, chartHeight); - - if (useCanVG) { - // If we need canvg library, extend and configure the renderer - // to get the tracker for translating mouse events - chart.renderer.create(chart, container, chartWidth, chartHeight); - } - }, - - /** - * Calculate margins by rendering axis labels in a preliminary position. Title, - * subtitle and legend have already been rendered at this stage, but will be - * moved into their final positions - */ - getMargins: function () { - var chart = this, - spacing = chart.spacing, - axisOffset, - legend = chart.legend, - margin = chart.margin, - legendOptions = chart.options.legend, - legendMargin = pick(legendOptions.margin, 10), - legendX = legendOptions.x, - legendY = legendOptions.y, - align = legendOptions.align, - verticalAlign = legendOptions.verticalAlign, - titleOffset = chart.titleOffset; - - chart.resetMargins(); - axisOffset = chart.axisOffset; - - // Adjust for title and subtitle - if (titleOffset && !defined(margin[0])) { - chart.plotTop = mathMax(chart.plotTop, titleOffset + chart.options.title.margin + spacing[0]); - } - - // Adjust for legend - if (legend.display && !legendOptions.floating) { - if (align === 'right') { // horizontal alignment handled first - if (!defined(margin[1])) { - chart.marginRight = mathMax( - chart.marginRight, - legend.legendWidth - legendX + legendMargin + spacing[1] - ); - } - } else if (align === 'left') { - if (!defined(margin[3])) { - chart.plotLeft = mathMax( - chart.plotLeft, - legend.legendWidth + legendX + legendMargin + spacing[3] - ); - } - - } else if (verticalAlign === 'top') { - if (!defined(margin[0])) { - chart.plotTop = mathMax( - chart.plotTop, - legend.legendHeight + legendY + legendMargin + spacing[0] - ); - } - - } else if (verticalAlign === 'bottom') { - if (!defined(margin[2])) { - chart.marginBottom = mathMax( - chart.marginBottom, - legend.legendHeight - legendY + legendMargin + spacing[2] - ); - } - } - } - - // adjust for scroller - if (chart.extraBottomMargin) { - chart.marginBottom += chart.extraBottomMargin; - } - if (chart.extraTopMargin) { - chart.plotTop += chart.extraTopMargin; - } - - // pre-render axes to get labels offset width - if (chart.hasCartesianSeries) { - each(chart.axes, function (axis) { - axis.getOffset(); - }); - } - - if (!defined(margin[3])) { - chart.plotLeft += axisOffset[3]; - } - if (!defined(margin[0])) { - chart.plotTop += axisOffset[0]; - } - if (!defined(margin[2])) { - chart.marginBottom += axisOffset[2]; - } - if (!defined(margin[1])) { - chart.marginRight += axisOffset[1]; - } - - chart.setChartSize(); - - }, - - /** - * Resize the chart to its container if size is not explicitly set - */ - reflow: function (e) { - var chart = this, - optionsChart = chart.options.chart, - renderTo = chart.renderTo, - width = optionsChart.width || adapterRun(renderTo, 'width'), - height = optionsChart.height || adapterRun(renderTo, 'height'), - target = e ? e.target : win, // #805 - MooTools doesn't supply e - doReflow = function () { - if (chart.container) { // It may have been destroyed in the meantime (#1257) - chart.setSize(width, height, false); - chart.hasUserSize = null; - } - }; - - // Width and height checks for display:none. Target is doc in IE8 and Opera, - // win in Firefox, Chrome and IE9. - if (!chart.hasUserSize && width && height && (target === win || target === doc)) { - - if (width !== chart.containerWidth || height !== chart.containerHeight) { - clearTimeout(chart.reflowTimeout); - if (e) { // Called from window.resize - chart.reflowTimeout = setTimeout(doReflow, 100); - } else { // Called directly (#2224) - doReflow(); - } - } - chart.containerWidth = width; - chart.containerHeight = height; - } - }, - - /** - * Add the event handlers necessary for auto resizing - */ - initReflow: function () { - var chart = this, - reflow = function (e) { - chart.reflow(e); - }; - - - addEvent(win, 'resize', reflow); - addEvent(chart, 'destroy', function () { - removeEvent(win, 'resize', reflow); - }); - }, - - /** - * Resize the chart to a given width and height - * @param {Number} width - * @param {Number} height - * @param {Object|Boolean} animation - */ - setSize: function (width, height, animation) { - var chart = this, - chartWidth, - chartHeight, - fireEndResize; - - // Handle the isResizing counter - chart.isResizing += 1; - fireEndResize = function () { - if (chart) { - fireEvent(chart, 'endResize', null, function () { - chart.isResizing -= 1; - }); - } - }; - - // set the animation for the current process - setAnimation(animation, chart); - - chart.oldChartHeight = chart.chartHeight; - chart.oldChartWidth = chart.chartWidth; - if (defined(width)) { - chart.chartWidth = chartWidth = mathMax(0, mathRound(width)); - chart.hasUserSize = !!chartWidth; - } - if (defined(height)) { - chart.chartHeight = chartHeight = mathMax(0, mathRound(height)); - } - - // Resize the container with the global animation applied if enabled (#2503) - (globalAnimation ? animate : css)(chart.container, { - width: chartWidth + PX, - height: chartHeight + PX - }, globalAnimation); - - chart.setChartSize(true); - chart.renderer.setSize(chartWidth, chartHeight, animation); - - // handle axes - chart.maxTicks = null; - each(chart.axes, function (axis) { - axis.isDirty = true; - axis.setScale(); - }); - - // make sure non-cartesian series are also handled - each(chart.series, function (serie) { - serie.isDirty = true; - }); - - chart.isDirtyLegend = true; // force legend redraw - chart.isDirtyBox = true; // force redraw of plot and chart border - - chart.getMargins(); - - chart.redraw(animation); - - - chart.oldChartHeight = null; - fireEvent(chart, 'resize'); - - // fire endResize and set isResizing back - // If animation is disabled, fire without delay - if (globalAnimation === false) { - fireEndResize(); - } else { // else set a timeout with the animation duration - setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500); - } - }, - - /** - * Set the public chart properties. This is done before and after the pre-render - * to determine margin sizes - */ - setChartSize: function (skipAxes) { - var chart = this, - inverted = chart.inverted, - renderer = chart.renderer, - chartWidth = chart.chartWidth, - chartHeight = chart.chartHeight, - optionsChart = chart.options.chart, - spacing = chart.spacing, - clipOffset = chart.clipOffset, - clipX, - clipY, - plotLeft, - plotTop, - plotWidth, - plotHeight, - plotBorderWidth; - - chart.plotLeft = plotLeft = mathRound(chart.plotLeft); - chart.plotTop = plotTop = mathRound(chart.plotTop); - chart.plotWidth = plotWidth = mathMax(0, mathRound(chartWidth - plotLeft - chart.marginRight)); - chart.plotHeight = plotHeight = mathMax(0, mathRound(chartHeight - plotTop - chart.marginBottom)); - - chart.plotSizeX = inverted ? plotHeight : plotWidth; - chart.plotSizeY = inverted ? plotWidth : plotHeight; - - chart.plotBorderWidth = optionsChart.plotBorderWidth || 0; - - // Set boxes used for alignment - chart.spacingBox = renderer.spacingBox = { - x: spacing[3], - y: spacing[0], - width: chartWidth - spacing[3] - spacing[1], - height: chartHeight - spacing[0] - spacing[2] - }; - chart.plotBox = renderer.plotBox = { - x: plotLeft, - y: plotTop, - width: plotWidth, - height: plotHeight - }; - - plotBorderWidth = 2 * mathFloor(chart.plotBorderWidth / 2); - clipX = mathCeil(mathMax(plotBorderWidth, clipOffset[3]) / 2); - clipY = mathCeil(mathMax(plotBorderWidth, clipOffset[0]) / 2); - chart.clipBox = { - x: clipX, - y: clipY, - width: mathFloor(chart.plotSizeX - mathMax(plotBorderWidth, clipOffset[1]) / 2 - clipX), - height: mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY) - }; - - if (!skipAxes) { - each(chart.axes, function (axis) { - axis.setAxisSize(); - axis.setAxisTranslation(); - }); - } - }, - - /** - * Initial margins before auto size margins are applied - */ - resetMargins: function () { - var chart = this, - spacing = chart.spacing, - margin = chart.margin; - - chart.plotTop = pick(margin[0], spacing[0]); - chart.marginRight = pick(margin[1], spacing[1]); - chart.marginBottom = pick(margin[2], spacing[2]); - chart.plotLeft = pick(margin[3], spacing[3]); - chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left - chart.clipOffset = [0, 0, 0, 0]; - }, - - /** - * Draw the borders and backgrounds for chart and plot area - */ - drawChartBox: function () { - var chart = this, - optionsChart = chart.options.chart, - renderer = chart.renderer, - chartWidth = chart.chartWidth, - chartHeight = chart.chartHeight, - chartBackground = chart.chartBackground, - plotBackground = chart.plotBackground, - plotBorder = chart.plotBorder, - plotBGImage = chart.plotBGImage, - chartBorderWidth = optionsChart.borderWidth || 0, - chartBackgroundColor = optionsChart.backgroundColor, - plotBackgroundColor = optionsChart.plotBackgroundColor, - plotBackgroundImage = optionsChart.plotBackgroundImage, - plotBorderWidth = optionsChart.plotBorderWidth || 0, - mgn, - bgAttr, - plotLeft = chart.plotLeft, - plotTop = chart.plotTop, - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - plotBox = chart.plotBox, - clipRect = chart.clipRect, - clipBox = chart.clipBox; - - // Chart area - mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0); - - if (chartBorderWidth || chartBackgroundColor) { - if (!chartBackground) { - - bgAttr = { - fill: chartBackgroundColor || NONE - }; - if (chartBorderWidth) { // #980 - bgAttr.stroke = optionsChart.borderColor; - bgAttr['stroke-width'] = chartBorderWidth; - } - chart.chartBackground = renderer.rect(mgn / 2, mgn / 2, chartWidth - mgn, chartHeight - mgn, - optionsChart.borderRadius, chartBorderWidth) - .attr(bgAttr) - .add() - .shadow(optionsChart.shadow); - - } else { // resize - chartBackground.animate( - chartBackground.crisp(null, null, null, chartWidth - mgn, chartHeight - mgn) - ); - } - } - - - // Plot background - if (plotBackgroundColor) { - if (!plotBackground) { - chart.plotBackground = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0) - .attr({ - fill: plotBackgroundColor - }) - .add() - .shadow(optionsChart.plotShadow); - } else { - plotBackground.animate(plotBox); - } - } - if (plotBackgroundImage) { - if (!plotBGImage) { - chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight) - .add(); - } else { - plotBGImage.animate(plotBox); - } - } - - // Plot clip - if (!clipRect) { - chart.clipRect = renderer.clipRect(clipBox); - } else { - clipRect.animate({ - width: clipBox.width, - height: clipBox.height - }); - } - - // Plot area border - if (plotBorderWidth) { - if (!plotBorder) { - chart.plotBorder = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0, -plotBorderWidth) - .attr({ - stroke: optionsChart.plotBorderColor, - 'stroke-width': plotBorderWidth, - zIndex: 1 - }) - .add(); - } else { - plotBorder.animate( - plotBorder.crisp(null, plotLeft, plotTop, plotWidth, plotHeight) - ); - } - } - - // reset - chart.isDirtyBox = false; - }, - - /** - * Detect whether a certain chart property is needed based on inspecting its options - * and series. This mainly applies to the chart.invert property, and in extensions to - * the chart.angular and chart.polar properties. - */ - propFromSeries: function () { - var chart = this, - optionsChart = chart.options.chart, - klass, - seriesOptions = chart.options.series, - i, - value; - - - each(['inverted', 'angular', 'polar'], function (key) { - - // The default series type's class - klass = seriesTypes[optionsChart.type || optionsChart.defaultSeriesType]; - - // Get the value from available chart-wide properties - value = ( - chart[key] || // 1. it is set before - optionsChart[key] || // 2. it is set in the options - (klass && klass.prototype[key]) // 3. it's default series class requires it - ); - - // 4. Check if any the chart's series require it - i = seriesOptions && seriesOptions.length; - while (!value && i--) { - klass = seriesTypes[seriesOptions[i].type]; - if (klass && klass.prototype[key]) { - value = true; - } - } - - // Set the chart property - chart[key] = value; - }); - - }, - - /** - * Link two or more series together. This is done initially from Chart.render, - * and after Chart.addSeries and Series.remove. - */ - linkSeries: function () { - var chart = this, - chartSeries = chart.series; - - // Reset links - each(chartSeries, function (series) { - series.linkedSeries.length = 0; - }); - - // Apply new links - each(chartSeries, function (series) { - var linkedTo = series.options.linkedTo; - if (isString(linkedTo)) { - if (linkedTo === ':previous') { - linkedTo = chart.series[series.index - 1]; - } else { - linkedTo = chart.get(linkedTo); - } - if (linkedTo) { - linkedTo.linkedSeries.push(series); - series.linkedParent = linkedTo; - } - } - }); - }, - - /** - * Render all graphics for the chart - */ - render: function () { - var chart = this, - axes = chart.axes, - renderer = chart.renderer, - options = chart.options; - - var labels = options.labels, - credits = options.credits, - creditsHref; - - // Title - chart.setTitle(); - - - // Legend - chart.legend = new Legend(chart, options.legend); - - chart.getStacks(); // render stacks - - // Get margins by pre-rendering axes - // set axes scales - each(axes, function (axis) { - axis.setScale(); - }); - - chart.getMargins(); - - chart.maxTicks = null; // reset for second pass - each(axes, function (axis) { - axis.setTickPositions(true); // update to reflect the new margins - axis.setMaxTicks(); - }); - chart.adjustTickAmounts(); - chart.getMargins(); // second pass to check for new labels - - - // Draw the borders and backgrounds - chart.drawChartBox(); - - - // Axes - if (chart.hasCartesianSeries) { - each(axes, function (axis) { - axis.render(); - }); - } - - // The series - if (!chart.seriesGroup) { - chart.seriesGroup = renderer.g('series-group') - .attr({ zIndex: 3 }) - .add(); - } - each(chart.series, function (serie) { - serie.translate(); - serie.setTooltipPoints(); - serie.render(); - }); - - // Labels - if (labels.items) { - each(labels.items, function (label) { - var style = extend(labels.style, label.style), - x = pInt(style.left) + chart.plotLeft, - y = pInt(style.top) + chart.plotTop + 12; - - // delete to prevent rewriting in IE - delete style.left; - delete style.top; - - renderer.text( - label.html, - x, - y - ) - .attr({ zIndex: 2 }) - .css(style) - .add(); - - }); - } - - // Credits - if (credits.enabled && !chart.credits) { - creditsHref = credits.href; - chart.credits = renderer.text( - credits.text, - 0, - 0 - ) - .on('click', function () { - if (creditsHref) { - location.href = creditsHref; - } - }) - .attr({ - align: credits.position.align, - zIndex: 8 - }) - .css(credits.style) - .add() - .align(credits.position); - } - - // Set flag - chart.hasRendered = true; - - }, - - /** - * Clean up memory usage - */ - destroy: function () { - var chart = this, - axes = chart.axes, - series = chart.series, - container = chart.container, - i, - parentNode = container && container.parentNode; - - // fire the chart.destoy event - fireEvent(chart, 'destroy'); - - // Delete the chart from charts lookup array - charts[chart.index] = UNDEFINED; - chart.renderTo.removeAttribute('data-highcharts-chart'); - - // remove events - removeEvent(chart); - - // ==== Destroy collections: - // Destroy axes - i = axes.length; - while (i--) { - axes[i] = axes[i].destroy(); - } - - // Destroy each series - i = series.length; - while (i--) { - series[i] = series[i].destroy(); - } - - // ==== Destroy chart properties: - each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage', - 'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'pointer', 'scroller', - 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) { - var prop = chart[name]; - - if (prop && prop.destroy) { - chart[name] = prop.destroy(); - } - }); - - // remove container and all SVG - if (container) { // can break in IE when destroyed before finished loading - container.innerHTML = ''; - removeEvent(container); - if (parentNode) { - discardElement(container); - } - - } - - // clean it all up - for (i in chart) { - delete chart[i]; - } - - }, - - - /** - * VML namespaces can't be added until after complete. Listening - * for Perini's doScroll hack is not enough. - */ - isReadyToRender: function () { - var chart = this; - - // Note: in spite of JSLint's complaints, win == win.top is required - /*jslint eqeq: true*/ - if ((!hasSVG && (win == win.top && doc.readyState !== 'complete')) || (useCanVG && !win.canvg)) { - /*jslint eqeq: false*/ - if (useCanVG) { - // Delay rendering until canvg library is downloaded and ready - CanVGController.push(function () { chart.firstRender(); }, chart.options.global.canvasToolsURL); - } else { - doc.attachEvent('onreadystatechange', function () { - doc.detachEvent('onreadystatechange', chart.firstRender); - if (doc.readyState === 'complete') { - chart.firstRender(); - } - }); - } - return false; - } - return true; - }, - - /** - * Prepare for first rendering after all data are loaded - */ - firstRender: function () { - var chart = this, - options = chart.options, - callback = chart.callback; - - // Check whether the chart is ready to render - if (!chart.isReadyToRender()) { - return; - } - - // Create the container - chart.getContainer(); - - // Run an early event after the container and renderer are established - fireEvent(chart, 'init'); - - - chart.resetMargins(); - chart.setChartSize(); - - // Set the common chart properties (mainly invert) from the given series - chart.propFromSeries(); - - // get axes - chart.getAxes(); - - // Initialize the series - each(options.series || [], function (serieOptions) { - chart.initSeries(serieOptions); - }); - - chart.linkSeries(); - - // Run an event after axes and series are initialized, but before render. At this stage, - // the series data is indexed and cached in the xData and yData arrays, so we can access - // those before rendering. Used in Highstock. - fireEvent(chart, 'beforeRender'); - - // depends on inverted and on margins being set - chart.pointer = new Pointer(chart, options); - - chart.render(); - - // add canvas - chart.renderer.draw(); - // run callbacks - if (callback) { - callback.apply(chart, [chart]); - } - each(chart.callbacks, function (fn) { - fn.apply(chart, [chart]); - }); - - - // If the chart was rendered outside the top container, put it back in - chart.cloneRenderTo(true); - - fireEvent(chart, 'load'); - - }, - - /** - * Creates arrays for spacing and margin from given options. - */ - splashArray: function (target, options) { - var oVar = options[target], - tArray = isObject(oVar) ? oVar : [oVar, oVar, oVar, oVar]; - - return [pick(options[target + 'Top'], tArray[0]), - pick(options[target + 'Right'], tArray[1]), - pick(options[target + 'Bottom'], tArray[2]), - pick(options[target + 'Left'], tArray[3])]; - } -}; // end Chart - -// Hook for exporting module -Chart.prototype.callbacks = []; - -var CenteredSeriesMixin = Highcharts.CenteredSeriesMixin = { - /** - * Get the center of the pie based on the size and center options relative to the - * plot area. Borrowed by the polar and gauge series types. - */ - getCenter: function () { - - var options = this.options, - chart = this.chart, - slicingRoom = 2 * (options.slicedOffset || 0), - handleSlicingRoom, - plotWidth = chart.plotWidth - 2 * slicingRoom, - plotHeight = chart.plotHeight - 2 * slicingRoom, - centerOption = options.center, - positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0], - smallestSize = mathMin(plotWidth, plotHeight), - isPercent; - - return map(positions, function (length, i) { - isPercent = /%$/.test(length); - handleSlicingRoom = i < 2 || (i === 2 && isPercent); - return (isPercent ? - // i == 0: centerX, relative to width - // i == 1: centerY, relative to height - // i == 2: size, relative to smallestSize - // i == 4: innerSize, relative to smallestSize - [plotWidth, plotHeight, smallestSize, smallestSize][i] * - pInt(length) / 100 : - length) + (handleSlicingRoom ? slicingRoom : 0); - }); - } -}; - -/** - * The Point object and prototype. Inheritable and used as base for PiePoint - */ -var Point = function () {}; -Point.prototype = { - - /** - * Initialize the point - * @param {Object} series The series object containing this point - * @param {Object} options The data in either number, array or object format - */ - init: function (series, options, x) { - - var point = this, - colors; - point.series = series; - point.applyOptions(options, x); - point.pointAttr = {}; - - if (series.options.colorByPoint) { - colors = series.options.colors || series.chart.options.colors; - point.color = point.color || colors[series.colorCounter++]; - // loop back to zero - if (series.colorCounter === colors.length) { - series.colorCounter = 0; - } - } - - series.chart.pointCount++; - return point; - }, - /** - * Apply the options containing the x and y data and possible some extra properties. - * This is called on point init or from point.update. - * - * @param {Object} options - */ - applyOptions: function (options, x) { - var point = this, - series = point.series, - pointValKey = series.pointValKey; - - options = Point.prototype.optionsToObject.call(this, options); - - // copy options directly to point - extend(point, options); - point.options = point.options ? extend(point.options, options) : options; - - // For higher dimension series types. For instance, for ranges, point.y is mapped to point.low. - if (pointValKey) { - point.y = point[pointValKey]; - } - - // If no x is set by now, get auto incremented value. All points must have an - // x value, however the y value can be null to create a gap in the series - if (point.x === UNDEFINED && series) { - point.x = x === UNDEFINED ? series.autoIncrement() : x; - } - - return point; - }, - - /** - * Transform number or array configs into objects - */ - optionsToObject: function (options) { - var ret = {}, - series = this.series, - pointArrayMap = series.pointArrayMap || ['y'], - valueCount = pointArrayMap.length, - firstItemType, - i = 0, - j = 0; - - if (typeof options === 'number' || options === null) { - ret[pointArrayMap[0]] = options; - - } else if (isArray(options)) { - // with leading x value - if (options.length > valueCount) { - firstItemType = typeof options[0]; - if (firstItemType === 'string') { - ret.name = options[0]; - } else if (firstItemType === 'number') { - ret.x = options[0]; - } - i++; - } - while (j < valueCount) { - ret[pointArrayMap[j++]] = options[i++]; - } - } else if (typeof options === 'object') { - ret = options; - - // This is the fastest way to detect if there are individual point dataLabels that need - // to be considered in drawDataLabels. These can only occur in object configs. - if (options.dataLabels) { - series._hasPointLabels = true; - } - - // Same approach as above for markers - if (options.marker) { - series._hasPointMarkers = true; - } - } - return ret; - }, - - /** - * Destroy a point to clear memory. Its reference still stays in series.data. - */ - destroy: function () { - var point = this, - series = point.series, - chart = series.chart, - hoverPoints = chart.hoverPoints, - prop; - - chart.pointCount--; - - if (hoverPoints) { - point.setState(); - erase(hoverPoints, point); - if (!hoverPoints.length) { - chart.hoverPoints = null; - } - - } - if (point === chart.hoverPoint) { - point.onMouseOut(); - } - - // remove all events - if (point.graphic || point.dataLabel) { // removeEvent and destroyElements are performance expensive - removeEvent(point); - point.destroyElements(); - } - - if (point.legendItem) { // pies have legend items - chart.legend.destroyItem(point); - } - - for (prop in point) { - point[prop] = null; - } - - - }, - - /** - * Destroy SVG elements associated with the point - */ - destroyElements: function () { - var point = this, - props = ['graphic', 'dataLabel', 'dataLabelUpper', 'group', 'connector', 'shadowGroup'], - prop, - i = 6; - while (i--) { - prop = props[i]; - if (point[prop]) { - point[prop] = point[prop].destroy(); - } - } - }, - - /** - * Return the configuration hash needed for the data label and tooltip formatters - */ - getLabelConfig: function () { - var point = this; - return { - x: point.category, - y: point.y, - key: point.name || point.category, - series: point.series, - point: point, - percentage: point.percentage, - total: point.total || point.stackTotal - }; - }, - - /** - * Toggle the selection status of a point - * @param {Boolean} selected Whether to select or unselect the point. - * @param {Boolean} accumulate Whether to add to the previous selection. By default, - * this happens if the control key (Cmd on Mac) was pressed during clicking. - */ - select: function (selected, accumulate) { - var point = this, - series = point.series, - chart = series.chart; - - selected = pick(selected, !point.selected); - - // fire the event with the defalut handler - point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () { - point.selected = point.options.selected = selected; - series.options.data[inArray(point, series.data)] = point.options; - - point.setState(selected && SELECT_STATE); - - // unselect all other points unless Ctrl or Cmd + click - if (!accumulate) { - each(chart.getSelectedPoints(), function (loopPoint) { - if (loopPoint.selected && loopPoint !== point) { - loopPoint.selected = loopPoint.options.selected = false; - series.options.data[inArray(loopPoint, series.data)] = loopPoint.options; - loopPoint.setState(NORMAL_STATE); - loopPoint.firePointEvent('unselect'); - } - }); - } - }); - }, - - /** - * Runs on mouse over the point - */ - onMouseOver: function (e) { - var point = this, - series = point.series, - chart = series.chart, - tooltip = chart.tooltip, - hoverPoint = chart.hoverPoint; - - // set normal state to previous series - if (hoverPoint && hoverPoint !== point) { - hoverPoint.onMouseOut(); - } - - // trigger the event - point.firePointEvent('mouseOver'); - - // update the tooltip - if (tooltip && (!tooltip.shared || series.noSharedTooltip)) { - tooltip.refresh(point, e); - } - - // hover this - point.setState(HOVER_STATE); - chart.hoverPoint = point; - }, - - /** - * Runs on mouse out from the point - */ - onMouseOut: function () { - var chart = this.series.chart, - hoverPoints = chart.hoverPoints; - - if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887 - this.firePointEvent('mouseOut'); - - this.setState(); - chart.hoverPoint = null; - } - }, - - /** - * Extendable method for formatting each point's tooltip line - * - * @return {String} A string to be concatenated in to the common tooltip text - */ - tooltipFormatter: function (pointFormat) { - - // Insert options for valueDecimals, valuePrefix, and valueSuffix - var series = this.series, - seriesTooltipOptions = series.tooltipOptions, - valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), - valuePrefix = seriesTooltipOptions.valuePrefix || '', - valueSuffix = seriesTooltipOptions.valueSuffix || ''; - - // Loop over the point array map and replace unformatted values with sprintf formatting markup - each(series.pointArrayMap || ['y'], function (key) { - key = '{point.' + key; // without the closing bracket - if (valuePrefix || valueSuffix) { - pointFormat = pointFormat.replace(key + '}', valuePrefix + key + '}' + valueSuffix); - } - pointFormat = pointFormat.replace(key + '}', key + ':,.' + valueDecimals + 'f}'); - }); - - return format(pointFormat, { - point: this, - series: this.series - }); - }, - - /** - * Fire an event on the Point object. Must not be renamed to fireEvent, as this - * causes a name clash in MooTools - * @param {String} eventType - * @param {Object} eventArgs Additional event arguments - * @param {Function} defaultFunction Default event handler - */ - firePointEvent: function (eventType, eventArgs, defaultFunction) { - var point = this, - series = this.series, - seriesOptions = series.options; - - // load event handlers on demand to save time on mouseover/out - if (seriesOptions.point.events[eventType] || (point.options && point.options.events && point.options.events[eventType])) { - this.importEvents(); - } - - // add default handler if in selection mode - if (eventType === 'click' && seriesOptions.allowPointSelect) { - defaultFunction = function (event) { - // Control key is for Windows, meta (= Cmd key) for Mac, Shift for Opera - point.select(null, event.ctrlKey || event.metaKey || event.shiftKey); - }; - } - - fireEvent(this, eventType, eventArgs, defaultFunction); - }, - /** - * Import events from the series' and point's options. Only do it on - * demand, to save processing time on hovering. - */ - importEvents: function () { - if (!this.hasImportedEvents) { - var point = this, - options = merge(point.series.options.point, point.options), - events = options.events, - eventType; - - point.events = events; - - for (eventType in events) { - addEvent(point, eventType, events[eventType]); - } - this.hasImportedEvents = true; - - } - }, - - /** - * Set the point's state - * @param {String} state - */ - setState: function (state, move) { - var point = this, - plotX = point.plotX, - plotY = point.plotY, - series = point.series, - stateOptions = series.options.states, - markerOptions = defaultPlotOptions[series.type].marker && series.options.marker, - normalDisabled = markerOptions && !markerOptions.enabled, - markerStateOptions = markerOptions && markerOptions.states[state], - stateDisabled = markerStateOptions && markerStateOptions.enabled === false, - stateMarkerGraphic = series.stateMarkerGraphic, - pointMarker = point.marker || {}, - chart = series.chart, - radius, - newSymbol, - pointAttr = point.pointAttr; - - state = state || NORMAL_STATE; // empty string - move = move && stateMarkerGraphic; - - if ( - // already has this state - (state === point.state && !move) || - // selected points don't respond to hover - (point.selected && state !== SELECT_STATE) || - // series' state options is disabled - (stateOptions[state] && stateOptions[state].enabled === false) || - // general point marker's state options is disabled - (state && (stateDisabled || (normalDisabled && !markerStateOptions.enabled))) || - // individual point marker's state options is disabled - (state && pointMarker.states && pointMarker.states[state] && pointMarker.states[state].enabled === false) // #1610 - - ) { - return; - } - - - // apply hover styles to the existing point - if (point.graphic) { - radius = markerOptions && point.graphic.symbolName && pointAttr[state].r; - point.graphic.attr(merge( - pointAttr[state], - radius ? { // new symbol attributes (#507, #612) - x: plotX - radius, - y: plotY - radius, - width: 2 * radius, - height: 2 * radius - } : {} - )); - } else { - // if a graphic is not applied to each point in the normal state, create a shared - // graphic for the hover state - if (state && markerStateOptions) { - radius = markerStateOptions.radius; - newSymbol = pointMarker.symbol || series.symbol; - - // If the point has another symbol than the previous one, throw away the - // state marker graphic and force a new one (#1459) - if (stateMarkerGraphic && stateMarkerGraphic.currentSymbol !== newSymbol) { - stateMarkerGraphic = stateMarkerGraphic.destroy(); - } - - // Add a new state marker graphic - if (!stateMarkerGraphic) { - series.stateMarkerGraphic = stateMarkerGraphic = chart.renderer.symbol( - newSymbol, - plotX - radius, - plotY - radius, - 2 * radius, - 2 * radius - ) - .attr(pointAttr[state]) - .add(series.markerGroup); - stateMarkerGraphic.currentSymbol = newSymbol; - - // Move the existing graphic - } else { - stateMarkerGraphic[move ? 'animate' : 'attr']({ // #1054 - x: plotX - radius, - y: plotY - radius - }); - } - } - - if (stateMarkerGraphic) { - stateMarkerGraphic[state && chart.isInsidePlot(plotX, plotY, chart.inverted) ? 'show' : 'hide'](); // #2450 - } - } - - point.state = state; - } -};/** - * @classDescription The base function which all other series types inherit from. The data in the series is stored - * in various arrays. - * - * - First, series.options.data contains all the original config options for - * each point whether added by options or methods like series.addPoint. - * - Next, series.data contains those values converted to points, but in case the series data length - * exceeds the cropThreshold, or if the data is grouped, series.data doesn't contain all the points. It - * only contains the points that have been created on demand. - * - Then there's series.points that contains all currently visible point objects. In case of cropping, - * the cropped-away points are not part of this array. The series.points array starts at series.cropStart - * compared to series.data and series.options.data. If however the series data is grouped, these can't - * be correlated one to one. - * - series.xData and series.processedXData contain clean x values, equivalent to series.data and series.points. - * - series.yData and series.processedYData contain clean x values, equivalent to series.data and series.points. - * - * @param {Object} chart - * @param {Object} options - */ -var Series = function () {}; - -Series.prototype = { - - isCartesian: true, - type: 'line', - pointClass: Point, - sorted: true, // requires the data to be sorted - requireSorting: true, - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'lineColor', - 'stroke-width': 'lineWidth', - fill: 'fillColor', - r: 'radius' - }, - axisTypes: ['xAxis', 'yAxis'], - colorCounter: 0, - parallelArrays: ['x', 'y'], // each point's x and y values are stored in this.xData and this.yData - init: function (chart, options) { - var series = this, - eventType, - events, - chartSeries = chart.series, - sortByIndex = function (a, b) { - return pick(a.options.index, a._i) - pick(b.options.index, b._i); - }; - - series.chart = chart; - series.options = options = series.setOptions(options); // merge with plotOptions - series.linkedSeries = []; - - // bind the axes - series.bindAxes(); - - // set some variables - extend(series, { - name: options.name, - state: NORMAL_STATE, - pointAttr: {}, - visible: options.visible !== false, // true by default - selected: options.selected === true // false by default - }); - - // special - if (useCanVG) { - options.animation = false; - } - - // register event listeners - events = options.events; - for (eventType in events) { - addEvent(series, eventType, events[eventType]); - } - if ( - (events && events.click) || - (options.point && options.point.events && options.point.events.click) || - options.allowPointSelect - ) { - chart.runTrackerClick = true; - } - - series.getColor(); - series.getSymbol(); - - // Set the data - each(series.parallelArrays, function (key) { - series[key + 'Data'] = []; - }); - series.setData(options.data, false); - - // Mark cartesian - if (series.isCartesian) { - chart.hasCartesianSeries = true; - } - - // Register it in the chart - chartSeries.push(series); - series._i = chartSeries.length - 1; - - // Sort series according to index option (#248, #1123, #2456) - stableSort(chartSeries, sortByIndex); - if (this.yAxis) { - stableSort(this.yAxis.series, sortByIndex); - } - - each(chartSeries, function (series, i) { - series.index = i; - series.name = series.name || 'Series ' + (i + 1); - }); - - }, - - /** - * Set the xAxis and yAxis properties of cartesian series, and register the series - * in the axis.series array - */ - bindAxes: function () { - var series = this, - seriesOptions = series.options, - chart = series.chart, - axisOptions; - - each(series.axisTypes || [], function (AXIS) { // repeat for xAxis and yAxis - - each(chart[AXIS], function (axis) { // loop through the chart's axis objects - axisOptions = axis.options; - - // apply if the series xAxis or yAxis option mathches the number of the - // axis, or if undefined, use the first axis - if ((seriesOptions[AXIS] === axisOptions.index) || - (seriesOptions[AXIS] !== UNDEFINED && seriesOptions[AXIS] === axisOptions.id) || - (seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) { - - // register this series in the axis.series lookup - axis.series.push(series); - - // set this series.xAxis or series.yAxis reference - series[AXIS] = axis; - - // mark dirty for redraw - axis.isDirty = true; - } - }); - - // The series needs an X and an Y axis - if (!series[AXIS] && series.optionalAxis !== AXIS) { - error(18, true); - } - - }); - }, - - /** - * For simple series types like line and column, the data values are held in arrays like - * xData and yData for quick lookup to find extremes and more. For multidimensional series - * like bubble and map, this can be extended with arrays like zData and valueData by - * adding to the series.parallelArrays array. - */ - updateParallelArrays: function (point, i) { - var series = point.series, - args = arguments, - fn = typeof i === 'number' ? - // Insert the value in the given position - function (key) { - var val = key === 'y' && series.toYData ? series.toYData(point) : point[key]; - series[key + 'Data'][i] = val; - } : - // Apply the method specified in i with the following arguments as arguments - function (key) { - Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2)); - }; - - each(series.parallelArrays, fn); - }, - - /** - * Return an auto incremented x value based on the pointStart and pointInterval options. - * This is only used if an x value is not given for the point that calls autoIncrement. - */ - autoIncrement: function () { - var series = this, - options = series.options, - xIncrement = series.xIncrement; - - xIncrement = pick(xIncrement, options.pointStart, 0); - - series.pointInterval = pick(series.pointInterval, options.pointInterval, 1); - - series.xIncrement = xIncrement + series.pointInterval; - return xIncrement; - }, - - /** - * Divide the series data into segments divided by null values. - */ - getSegments: function () { - var series = this, - lastNull = -1, - segments = [], - i, - points = series.points, - pointsLength = points.length; - - if (pointsLength) { // no action required for [] - - // if connect nulls, just remove null points - if (series.options.connectNulls) { - i = pointsLength; - while (i--) { - if (points[i].y === null) { - points.splice(i, 1); - } - } - if (points.length) { - segments = [points]; - } - - // else, split on null points - } else { - each(points, function (point, i) { - if (point.y === null) { - if (i > lastNull + 1) { - segments.push(points.slice(lastNull + 1, i)); - } - lastNull = i; - } else if (i === pointsLength - 1) { // last value - segments.push(points.slice(lastNull + 1, i + 1)); - } - }); - } - } - - // register it - series.segments = segments; - }, - - /** - * Set the series options by merging from the options tree - * @param {Object} itemOptions - */ - setOptions: function (itemOptions) { - var chart = this.chart, - chartOptions = chart.options, - plotOptions = chartOptions.plotOptions, - userOptions = chart.userOptions || {}, - userPlotOptions = userOptions.plotOptions || {}, - typeOptions = plotOptions[this.type], - options; - - this.userOptions = itemOptions; - - options = merge( - typeOptions, - plotOptions.series, - itemOptions - ); - - // The tooltip options are merged between global and series specific options - this.tooltipOptions = merge( - defaultOptions.tooltip, - defaultOptions.plotOptions[this.type].tooltip, - userOptions.tooltip, - userPlotOptions.series && userPlotOptions.series.tooltip, - userPlotOptions[this.type] && userPlotOptions[this.type].tooltip, - itemOptions.tooltip - ); - - // Delete marker object if not allowed (#1125) - if (typeOptions.marker === null) { - delete options.marker; - } - - return options; - - }, - /** - * Get the series' color - */ - getColor: function () { - var options = this.options, - userOptions = this.userOptions, - defaultColors = this.chart.options.colors, - counters = this.chart.counters, - color, - colorIndex; - - color = options.color || defaultPlotOptions[this.type].color; - - if (!color && !options.colorByPoint) { - if (defined(userOptions._colorIndex)) { // after Series.update() - colorIndex = userOptions._colorIndex; - } else { - userOptions._colorIndex = counters.color; - colorIndex = counters.color++; - } - color = defaultColors[colorIndex]; - } - - this.color = color; - counters.wrapColor(defaultColors.length); - }, - /** - * Get the series' symbol - */ - getSymbol: function () { - var series = this, - userOptions = series.userOptions, - seriesMarkerOption = series.options.marker, - chart = series.chart, - defaultSymbols = chart.options.symbols, - counters = chart.counters, - symbolIndex; - - series.symbol = seriesMarkerOption.symbol; - if (!series.symbol) { - if (defined(userOptions._symbolIndex)) { // after Series.update() - symbolIndex = userOptions._symbolIndex; - } else { - userOptions._symbolIndex = counters.symbol; - symbolIndex = counters.symbol++; - } - series.symbol = defaultSymbols[symbolIndex]; - } - - // don't substract radius in image symbols (#604) - if (/^url/.test(series.symbol)) { - seriesMarkerOption.radius = 0; - } - counters.wrapSymbol(defaultSymbols.length); - }, - - drawLegendSymbol: LegendSymbolMixin.drawLineMarker, - - /** - * Replace the series data with a new set of data - * @param {Object} data - * @param {Object} redraw - */ - setData: function (data, redraw) { - var series = this, - oldData = series.points, - options = series.options, - chart = series.chart, - firstPoint = null, - xAxis = series.xAxis, - hasCategories = xAxis && !!xAxis.categories, - i; - - // reset properties - series.xIncrement = null; - series.pointRange = hasCategories ? 1 : options.pointRange; - - series.colorCounter = 0; // for series with colorByPoint (#1547) - data = data || []; - - // parallel arrays - var dataLength = data.length, - turboThreshold = options.turboThreshold, - pt, - xData = this.xData, - yData = this.yData, - pointArrayMap = series.pointArrayMap, - valueCount = pointArrayMap && pointArrayMap.length; - - each(this.parallelArrays, function (key) { - series[key + 'Data'].length = 0; - }); - - // In turbo mode, only one- or twodimensional arrays of numbers are allowed. The - // first value is tested, and we assume that all the rest are defined the same - // way. Although the 'for' loops are similar, they are repeated inside each - // if-else conditional for max performance. - if (turboThreshold && dataLength > turboThreshold) { - - // find the first non-null point - i = 0; - while (firstPoint === null && i < dataLength) { - firstPoint = data[i]; - i++; - } - - - if (isNumber(firstPoint)) { // assume all points are numbers - var x = pick(options.pointStart, 0), - pointInterval = pick(options.pointInterval, 1); - - for (i = 0; i < dataLength; i++) { - xData[i] = x; - yData[i] = data[i]; - x += pointInterval; - } - series.xIncrement = x; - } else if (isArray(firstPoint)) { // assume all points are arrays - if (valueCount) { // [x, low, high] or [x, o, h, l, c] - for (i = 0; i < dataLength; i++) { - pt = data[i]; - xData[i] = pt[0]; - yData[i] = pt.slice(1, valueCount + 1); - } - } else { // [x, y] - for (i = 0; i < dataLength; i++) { - pt = data[i]; - xData[i] = pt[0]; - yData[i] = pt[1]; - } - } - } else { - error(12); // Highcharts expects configs to be numbers or arrays in turbo mode - } - } else { - for (i = 0; i < dataLength; i++) { - if (data[i] !== UNDEFINED) { // stray commas in oldIE - pt = { series: series }; - series.pointClass.prototype.applyOptions.apply(pt, [data[i]]); - series.updateParallelArrays(pt, i); - if (hasCategories && pt.name) { - xAxis.names[pt.x] = pt.name; // #2046 - } - } - } - } - - // Forgetting to cast strings to numbers is a common caveat when handling CSV or JSON - if (isString(yData[0])) { - error(14, true); - } - - series.data = []; - series.options.data = data; - //series.zData = zData; - - // destroy old points - i = (oldData && oldData.length) || 0; - while (i--) { - if (oldData[i] && oldData[i].destroy) { - oldData[i].destroy(); - } - } - - // reset minRange (#878) - if (xAxis) { - xAxis.minRange = xAxis.userMinRange; - } - - // redraw - series.isDirty = series.isDirtyData = chart.isDirtyBox = true; - if (pick(redraw, true)) { - chart.redraw(false); - } - }, - - /** - * Process the data by cropping away unused data points if the series is longer - * than the crop threshold. This saves computing time for lage series. - */ - processData: function (force) { - var series = this, - processedXData = series.xData, // copied during slice operation below - processedYData = series.yData, - dataLength = processedXData.length, - croppedData, - cropStart = 0, - cropped, - distance, - closestPointRange, - xAxis = series.xAxis, - i, // loop variable - options = series.options, - cropThreshold = options.cropThreshold, - isCartesian = series.isCartesian; - - // If the series data or axes haven't changed, don't go through this. Return false to pass - // the message on to override methods like in data grouping. - if (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) { - return false; - } - - - // optionally filter out points outside the plot area - if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) { - var min = xAxis.min, - max = xAxis.max; - - // it's outside current extremes - if (processedXData[dataLength - 1] < min || processedXData[0] > max) { - processedXData = []; - processedYData = []; - - // only crop if it's actually spilling out - } else if (processedXData[0] < min || processedXData[dataLength - 1] > max) { - croppedData = this.cropData(series.xData, series.yData, min, max); - processedXData = croppedData.xData; - processedYData = croppedData.yData; - cropStart = croppedData.start; - cropped = true; - } - } - - - // Find the closest distance between processed points - for (i = processedXData.length - 1; i >= 0; i--) { - distance = processedXData[i] - processedXData[i - 1]; - if (distance > 0 && (closestPointRange === UNDEFINED || distance < closestPointRange)) { - closestPointRange = distance; - - // Unsorted data is not supported by the line tooltip, as well as data grouping and - // navigation in Stock charts (#725) and width calculation of columns (#1900) - } else if (distance < 0 && series.requireSorting) { - error(15); - } - } - - // Record the properties - series.cropped = cropped; // undefined or true - series.cropStart = cropStart; - series.processedXData = processedXData; - series.processedYData = processedYData; - - if (options.pointRange === null) { // null means auto, as for columns, candlesticks and OHLC - series.pointRange = closestPointRange || 1; - } - series.closestPointRange = closestPointRange; - - }, - - /** - * Iterate over xData and crop values between min and max. Returns object containing crop start/end - * cropped xData with corresponding part of yData, dataMin and dataMax within the cropped range - */ - cropData: function (xData, yData, min, max) { - var dataLength = xData.length, - cropStart = 0, - cropEnd = dataLength, - cropShoulder = pick(this.cropShoulder, 1), // line-type series need one point outside - i; - - // iterate up to find slice start - for (i = 0; i < dataLength; i++) { - if (xData[i] >= min) { - cropStart = mathMax(0, i - cropShoulder); - break; - } - } - - // proceed to find slice end - for (; i < dataLength; i++) { - if (xData[i] > max) { - cropEnd = i + cropShoulder; - break; - } - } - - return { - xData: xData.slice(cropStart, cropEnd), - yData: yData.slice(cropStart, cropEnd), - start: cropStart, - end: cropEnd - }; - }, - - - /** - * Generate the data point after the data has been processed by cropping away - * unused points and optionally grouped in Highcharts Stock. - */ - generatePoints: function () { - var series = this, - options = series.options, - dataOptions = options.data, - data = series.data, - dataLength, - processedXData = series.processedXData, - processedYData = series.processedYData, - pointClass = series.pointClass, - processedDataLength = processedXData.length, - cropStart = series.cropStart || 0, - cursor, - hasGroupedData = series.hasGroupedData, - point, - points = [], - i; - - if (!data && !hasGroupedData) { - var arr = []; - arr.length = dataOptions.length; - data = series.data = arr; - } - - for (i = 0; i < processedDataLength; i++) { - cursor = cropStart + i; - if (!hasGroupedData) { - if (data[cursor]) { - point = data[cursor]; - } else if (dataOptions[cursor] !== UNDEFINED) { // #970 - data[cursor] = point = (new pointClass()).init(series, dataOptions[cursor], processedXData[i]); - } - points[i] = point; - } else { - // splat the y data in case of ohlc data array - points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i]))); - } - } - - // Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when - // swithching view from non-grouped data to grouped data (#637) - if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) { - for (i = 0; i < dataLength; i++) { - if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points - i += processedDataLength; - } - if (data[i]) { - data[i].destroyElements(); - data[i].plotX = UNDEFINED; // #1003 - } - } - } - - series.data = data; - series.points = points; - }, - - /** - * Adds series' points value to corresponding stack - */ - setStackedPoints: function () { - if (!this.options.stacking || (this.visible !== true && this.chart.options.chart.ignoreHiddenSeries !== false)) { - return; - } - - var series = this, - xData = series.processedXData, - yData = series.processedYData, - stackedYData = [], - yDataLength = yData.length, - seriesOptions = series.options, - threshold = seriesOptions.threshold, - stackOption = seriesOptions.stack, - stacking = seriesOptions.stacking, - stackKey = series.stackKey, - negKey = '-' + stackKey, - negStacks = series.negStacks, - yAxis = series.yAxis, - stacks = yAxis.stacks, - oldStacks = yAxis.oldStacks, - isNegative, - stack, - other, - key, - i, - x, - y; - - // loop over the non-null y values and read them into a local array - for (i = 0; i < yDataLength; i++) { - x = xData[i]; - y = yData[i]; - - // Read stacked values into a stack based on the x value, - // the sign of y and the stack key. Stacking is also handled for null values (#739) - isNegative = negStacks && y < threshold; - key = isNegative ? negKey : stackKey; - - // Create empty object for this stack if it doesn't exist yet - if (!stacks[key]) { - stacks[key] = {}; - } - - // Initialize StackItem for this x - if (!stacks[key][x]) { - if (oldStacks[key] && oldStacks[key][x]) { - stacks[key][x] = oldStacks[key][x]; - stacks[key][x].total = null; - } else { - stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, isNegative, x, stackOption, stacking); - } - } - - // If the StackItem doesn't exist, create it first - stack = stacks[key][x]; - stack.points[series.index] = [stack.cum || 0]; - - // Add value to the stack total - if (stacking === 'percent') { - - // Percent stacked column, totals are the same for the positive and negative stacks - other = isNegative ? stackKey : negKey; - if (negStacks && stacks[other] && stacks[other][x]) { - other = stacks[other][x]; - stack.total = other.total = mathMax(other.total, stack.total) + mathAbs(y) || 0; - - // Percent stacked areas - } else { - stack.total = correctFloat(stack.total + (mathAbs(y) || 0)); - } - } else { - stack.total = correctFloat(stack.total + (y || 0)); - } - - stack.cum = (stack.cum || 0) + (y || 0); - - stack.points[series.index].push(stack.cum); - stackedYData[i] = stack.cum; - - } - - if (stacking === 'percent') { - yAxis.usePercentage = true; - } - - this.stackedYData = stackedYData; // To be used in getExtremes - - // Reset old stacks - yAxis.oldStacks = {}; - }, - - /** - * Iterate over all stacks and compute the absolute values to percent - */ - setPercentStacks: function () { - var series = this, - stackKey = series.stackKey, - stacks = series.yAxis.stacks; - - each([stackKey, '-' + stackKey], function (key) { - var i = series.xData.length, - x, - stack, - pointExtremes, - totalFactor; - - while (i--) { - x = series.xData[i]; - stack = stacks[key] && stacks[key][x]; - pointExtremes = stack && stack.points[series.index]; - if (pointExtremes) { - totalFactor = stack.total ? 100 / stack.total : 0; - pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor); // Y bottom value - pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor); // Y value - series.stackedYData[i] = pointExtremes[1]; - } - } - }); - }, - - /** - * Calculate Y extremes for visible data - */ - getExtremes: function (yData) { - var xAxis = this.xAxis, - yAxis = this.yAxis, - xData = this.processedXData, - yDataLength, - activeYData = [], - activeCounter = 0, - xExtremes = xAxis.getExtremes(), // #2117, need to compensate for log X axis - xMin = xExtremes.min, - xMax = xExtremes.max, - validValue, - withinRange, - dataMin, - dataMax, - x, - y, - i, - j; - - yData = yData || this.stackedYData || this.processedYData; - yDataLength = yData.length; - - for (i = 0; i < yDataLength; i++) { - - x = xData[i]; - y = yData[i]; - - // For points within the visible range, including the first point outside the - // visible range, consider y extremes - validValue = y !== null && y !== UNDEFINED && (!yAxis.isLog || (y.length || y > 0)); - withinRange = this.getExtremesFromAll || this.cropped || ((xData[i + 1] || x) >= xMin && - (xData[i - 1] || x) <= xMax); - - if (validValue && withinRange) { - - j = y.length; - if (j) { // array, like ohlc or range data - while (j--) { - if (y[j] !== null) { - activeYData[activeCounter++] = y[j]; - } - } - } else { - activeYData[activeCounter++] = y; - } - } - } - this.dataMin = pick(dataMin, arrayMin(activeYData)); - this.dataMax = pick(dataMax, arrayMax(activeYData)); - }, - - /** - * Translate data points from raw data values to chart specific positioning data - * needed later in drawPoints, drawGraph and drawTracker. - */ - translate: function () { - if (!this.processedXData) { // hidden series - this.processData(); - } - this.generatePoints(); - var series = this, - options = series.options, - stacking = options.stacking, - xAxis = series.xAxis, - categories = xAxis.categories, - yAxis = series.yAxis, - points = series.points, - dataLength = points.length, - hasModifyValue = !!series.modifyValue, - i, - pointPlacement = options.pointPlacement, - dynamicallyPlaced = pointPlacement === 'between' || isNumber(pointPlacement), - threshold = options.threshold; - - // Translate each point - for (i = 0; i < dataLength; i++) { - var point = points[i], - xValue = point.x, - yValue = point.y, - yBottom = point.low, - stack = stacking && yAxis.stacks[(series.negStacks && yValue < threshold ? '-' : '') + series.stackKey], - pointStack, - stackValues; - - // Discard disallowed y values for log axes - if (yAxis.isLog && yValue <= 0) { - point.y = yValue = null; - } - - // Get the plotX translation - point.plotX = xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags'); // Math.round fixes #591 - - - // Calculate the bottom y value for stacked series - if (stacking && series.visible && stack && stack[xValue]) { - - pointStack = stack[xValue]; - stackValues = pointStack.points[series.index]; - yBottom = stackValues[0]; - yValue = stackValues[1]; - - if (yBottom === 0) { - yBottom = pick(threshold, yAxis.min); - } - if (yAxis.isLog && yBottom <= 0) { // #1200, #1232 - yBottom = null; - } - - point.total = point.stackTotal = pointStack.total; - point.percentage = stacking === 'percent' && (point.y / pointStack.total * 100); - point.stackY = yValue; - - // Place the stack label - pointStack.setOffset(series.pointXOffset || 0, series.barW || 0); - - } - - // Set translated yBottom or remove it - point.yBottom = defined(yBottom) ? - yAxis.translate(yBottom, 0, 1, 0, 1) : - null; - - // general hook, used for Highstock compare mode - if (hasModifyValue) { - yValue = series.modifyValue(yValue, point); - } - - // Set the the plotY value, reset it for redraws - point.plotY = (typeof yValue === 'number' && yValue !== Infinity) ? - //mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10 : // Math.round fixes #591 - yAxis.translate(yValue, 0, 1, 0, 1) : - UNDEFINED; - - // Set client related positions for mouse tracking - point.clientX = dynamicallyPlaced ? xAxis.translate(xValue, 0, 0, 0, 1) : point.plotX; // #1514 - - point.negative = point.y < (threshold || 0); - - // some API data - point.category = categories && categories[point.x] !== UNDEFINED ? - categories[point.x] : point.x; - - - } - - // now that we have the cropped data, build the segments - series.getSegments(); - }, - /** - * Memoize tooltip texts and positions - */ - setTooltipPoints: function (renew) { - var series = this, - points = [], - pointsLength, - low, - high, - xAxis = series.xAxis, - xExtremes = xAxis && xAxis.getExtremes(), - axisLength = xAxis ? (xAxis.tooltipLen || xAxis.len) : series.chart.plotSizeX, // tooltipLen and tooltipPosName used in polar - point, - pointX, - nextPoint, - i, - tooltipPoints = []; // a lookup array for each pixel in the x dimension - - // don't waste resources if tracker is disabled - if (series.options.enableMouseTracking === false) { - return; - } - - // renew - if (renew) { - series.tooltipPoints = null; - } - - // concat segments to overcome null values - each(series.segments || series.points, function (segment) { - points = points.concat(segment); - }); - - // Reverse the points in case the X axis is reversed - if (xAxis && xAxis.reversed) { - points = points.reverse(); - } - - // Polar needs additional shaping - if (series.orderTooltipPoints) { - series.orderTooltipPoints(points); - } - - // Assign each pixel position to the nearest point - pointsLength = points.length; - for (i = 0; i < pointsLength; i++) { - point = points[i]; - pointX = point.x; - if (pointX >= xExtremes.min && pointX <= xExtremes.max) { // #1149 - nextPoint = points[i + 1]; - - // Set this range's low to the last range's high plus one - low = high === UNDEFINED ? 0 : high + 1; - // Now find the new high - high = points[i + 1] ? - mathMin(mathMax(0, mathFloor( // #2070 - (point.clientX + (nextPoint ? (nextPoint.wrappedClientX || nextPoint.clientX) : axisLength)) / 2 - )), axisLength) : - axisLength; - - while (low >= 0 && low <= high) { - tooltipPoints[low++] = point; - } - } - } - series.tooltipPoints = tooltipPoints; - }, - - /** - * Format the header of the tooltip - */ - tooltipHeaderFormatter: function (point) { - var series = this, - tooltipOptions = series.tooltipOptions, - dateTimeLabelFormats = tooltipOptions.dateTimeLabelFormats, - xDateFormat = tooltipOptions.xDateFormat, - xAxis = series.xAxis, - isDateTime = xAxis && xAxis.options.type === 'datetime', - headerFormat = tooltipOptions.headerFormat, - closestPointRange = xAxis && xAxis.closestPointRange, - n; - - // Guess the best date format based on the closest point distance (#568) - if (isDateTime && !xDateFormat) { - if (closestPointRange) { - for (n in timeUnits) { - if (timeUnits[n] >= closestPointRange) { - xDateFormat = dateTimeLabelFormats[n]; - break; - } - } - } else { - xDateFormat = dateTimeLabelFormats.day; - } - - xDateFormat = xDateFormat || dateTimeLabelFormats.year; // #2546, 2581 - - } - - // Insert the header date format if any - if (isDateTime && xDateFormat && isNumber(point.key)) { - headerFormat = headerFormat.replace('{point.key}', '{point.key:' + xDateFormat + '}'); - } - - return format(headerFormat, { - point: point, - series: series - }); - }, - - /** - * Series mouse over handler - */ - onMouseOver: function () { - var series = this, - chart = series.chart, - hoverSeries = chart.hoverSeries; - - // set normal state to previous series - if (hoverSeries && hoverSeries !== series) { - hoverSeries.onMouseOut(); - } - - // trigger the event, but to save processing time, - // only if defined - if (series.options.events.mouseOver) { - fireEvent(series, 'mouseOver'); - } - - // hover this - series.setState(HOVER_STATE); - chart.hoverSeries = series; - }, - - /** - * Series mouse out handler - */ - onMouseOut: function () { - // trigger the event only if listeners exist - var series = this, - options = series.options, - chart = series.chart, - tooltip = chart.tooltip, - hoverPoint = chart.hoverPoint; - - // trigger mouse out on the point, which must be in this series - if (hoverPoint) { - hoverPoint.onMouseOut(); - } - - // fire the mouse out event - if (series && options.events.mouseOut) { - fireEvent(series, 'mouseOut'); - } - - - // hide the tooltip - if (tooltip && !options.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) { - tooltip.hide(); - } - - // set normal state - series.setState(); - chart.hoverSeries = null; - }, - - /** - * Animate in the series - */ - animate: function (init) { - var series = this, - chart = series.chart, - renderer = chart.renderer, - clipRect, - markerClipRect, - animation = series.options.animation, - clipBox = chart.clipBox, - inverted = chart.inverted, - sharedClipKey; - - // Animation option is set to true - if (animation && !isObject(animation)) { - animation = defaultPlotOptions[series.type].animation; - } - sharedClipKey = '_sharedClip' + animation.duration + animation.easing; - - // Initialize the animation. Set up the clipping rectangle. - if (init) { - - // If a clipping rectangle with the same properties is currently present in the chart, use that. - clipRect = chart[sharedClipKey]; - markerClipRect = chart[sharedClipKey + 'm']; - if (!clipRect) { - chart[sharedClipKey] = clipRect = renderer.clipRect( - extend(clipBox, { width: 0 }) - ); - - chart[sharedClipKey + 'm'] = markerClipRect = renderer.clipRect( - -99, // include the width of the first marker - inverted ? -chart.plotLeft : -chart.plotTop, - 99, - inverted ? chart.chartWidth : chart.chartHeight - ); - } - series.group.clip(clipRect); - series.markerGroup.clip(markerClipRect); - series.sharedClipKey = sharedClipKey; - - // Run the animation - } else { - clipRect = chart[sharedClipKey]; - if (clipRect) { - clipRect.animate({ - width: chart.plotSizeX - }, animation); - chart[sharedClipKey + 'm'].animate({ - width: chart.plotSizeX + 99 - }, animation); - } - - // Delete this function to allow it only once - series.animate = null; - - // Call the afterAnimate function on animation complete (but don't overwrite the animation.complete option - // which should be available to the user). - series.animationTimeout = setTimeout(function () { - series.afterAnimate(); - }, animation.duration); - } - }, - - /** - * This runs after animation to land on the final plot clipping - */ - afterAnimate: function () { - var chart = this.chart, - sharedClipKey = this.sharedClipKey, - group = this.group; - - if (group && this.options.clip !== false) { - group.clip(chart.clipRect); - this.markerGroup.clip(); // no clip - } - - // Remove the shared clipping rectancgle when all series are shown - setTimeout(function () { - if (sharedClipKey && chart[sharedClipKey]) { - chart[sharedClipKey] = chart[sharedClipKey].destroy(); - chart[sharedClipKey + 'm'] = chart[sharedClipKey + 'm'].destroy(); - } - }, 100); - }, - - /** - * Draw the markers - */ - drawPoints: function () { - var series = this, - pointAttr, - points = series.points, - chart = series.chart, - plotX, - plotY, - i, - point, - radius, - symbol, - isImage, - graphic, - options = series.options, - seriesMarkerOptions = options.marker, - pointMarkerOptions, - enabled, - isInside, - markerGroup = series.markerGroup; - - if (seriesMarkerOptions.enabled || series._hasPointMarkers) { - - i = points.length; - while (i--) { - point = points[i]; - plotX = mathFloor(point.plotX); // #1843 - plotY = point.plotY; - graphic = point.graphic; - pointMarkerOptions = point.marker || {}; - enabled = (seriesMarkerOptions.enabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled; - isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858 - - // only draw the point if y is defined - if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { - - // shortcuts - pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]; - radius = pointAttr.r; - symbol = pick(pointMarkerOptions.symbol, series.symbol); - isImage = symbol.indexOf('url') === 0; - - if (graphic) { // update - graphic - .attr({ // Since the marker group isn't clipped, each individual marker must be toggled - visibility: isInside ? (hasSVG ? 'inherit' : VISIBLE) : HIDDEN - }) - .animate(extend({ - x: plotX - radius, - y: plotY - radius - }, graphic.symbolName ? { // don't apply to image symbols #507 - width: 2 * radius, - height: 2 * radius - } : {})); - } else if (isInside && (radius > 0 || isImage)) { - point.graphic = graphic = chart.renderer.symbol( - symbol, - plotX - radius, - plotY - radius, - 2 * radius, - 2 * radius - ) - .attr(pointAttr) - .add(markerGroup); - } - - } else if (graphic) { - point.graphic = graphic.destroy(); // #1269 - } - } - } - - }, - - /** - * Convert state properties from API naming conventions to SVG attributes - * - * @param {Object} options API options object - * @param {Object} base1 SVG attribute object to inherit from - * @param {Object} base2 Second level SVG attribute object to inherit from - */ - convertAttribs: function (options, base1, base2, base3) { - var conversion = this.pointAttrToOptions, - attr, - option, - obj = {}; - - options = options || {}; - base1 = base1 || {}; - base2 = base2 || {}; - base3 = base3 || {}; - - for (attr in conversion) { - option = conversion[attr]; - obj[attr] = pick(options[option], base1[attr], base2[attr], base3[attr]); - } - return obj; - }, - - /** - * Get the state attributes. Each series type has its own set of attributes - * that are allowed to change on a point's state change. Series wide attributes are stored for - * all series, and additionally point specific attributes are stored for all - * points with individual marker options. If such options are not defined for the point, - * a reference to the series wide attributes is stored in point.pointAttr. - */ - getAttribs: function () { - var series = this, - seriesOptions = series.options, - normalOptions = defaultPlotOptions[series.type].marker ? seriesOptions.marker : seriesOptions, - stateOptions = normalOptions.states, - stateOptionsHover = stateOptions[HOVER_STATE], - pointStateOptionsHover, - seriesColor = series.color, - normalDefaults = { - stroke: seriesColor, - fill: seriesColor - }, - points = series.points || [], // #927 - i, - point, - seriesPointAttr = [], - pointAttr, - pointAttrToOptions = series.pointAttrToOptions, - hasPointSpecificOptions, - negativeColor = seriesOptions.negativeColor, - defaultLineColor = normalOptions.lineColor, - defaultFillColor = normalOptions.fillColor, - attr, - key; - - // series type specific modifications - if (seriesOptions.marker) { // line, spline, area, areaspline, scatter - - // if no hover radius is given, default to normal radius + 2 - stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2; - stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1; - - } else { // column, bar, pie - - // if no hover color is given, brighten the normal color - stateOptionsHover.color = stateOptionsHover.color || - Color(stateOptionsHover.color || seriesColor) - .brighten(stateOptionsHover.brightness).get(); - } - - // general point attributes for the series normal state - seriesPointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, normalDefaults); - - // HOVER_STATE and SELECT_STATE states inherit from normal state except the default radius - each([HOVER_STATE, SELECT_STATE], function (state) { - seriesPointAttr[state] = - series.convertAttribs(stateOptions[state], seriesPointAttr[NORMAL_STATE]); - }); - - // set it - series.pointAttr = seriesPointAttr; - - - // Generate the point-specific attribute collections if specific point - // options are given. If not, create a referance to the series wide point - // attributes - i = points.length; - while (i--) { - point = points[i]; - normalOptions = (point.options && point.options.marker) || point.options; - if (normalOptions && normalOptions.enabled === false) { - normalOptions.radius = 0; - } - - if (point.negative && negativeColor) { - point.color = point.fillColor = negativeColor; - } - - hasPointSpecificOptions = seriesOptions.colorByPoint || point.color; // #868 - - // check if the point has specific visual options - if (point.options) { - for (key in pointAttrToOptions) { - if (defined(normalOptions[pointAttrToOptions[key]])) { - hasPointSpecificOptions = true; - } - } - } - - // a specific marker config object is defined for the individual point: - // create it's own attribute collection - if (hasPointSpecificOptions) { - normalOptions = normalOptions || {}; - pointAttr = []; - stateOptions = normalOptions.states || {}; // reassign for individual point - pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {}; - - // Handle colors for column and pies - if (!seriesOptions.marker) { // column, bar, point - // if no hover color is given, brighten the normal color - pointStateOptionsHover.color = - Color(pointStateOptionsHover.color || point.color) - .brighten(pointStateOptionsHover.brightness || - stateOptionsHover.brightness).get(); - - } - - // normal point state inherits series wide normal state - attr = { color: point.color }; // #868 - if (!defaultFillColor) { // Individual point color or negative color markers (#2219) - attr.fillColor = point.color; - } - if (!defaultLineColor) { - attr.lineColor = point.color; // Bubbles take point color, line markers use white - } - pointAttr[NORMAL_STATE] = series.convertAttribs(extend(attr, normalOptions), seriesPointAttr[NORMAL_STATE]); - - // inherit from point normal and series hover - pointAttr[HOVER_STATE] = series.convertAttribs( - stateOptions[HOVER_STATE], - seriesPointAttr[HOVER_STATE], - pointAttr[NORMAL_STATE] - ); - - // inherit from point normal and series hover - pointAttr[SELECT_STATE] = series.convertAttribs( - stateOptions[SELECT_STATE], - seriesPointAttr[SELECT_STATE], - pointAttr[NORMAL_STATE] - ); - - - // no marker config object is created: copy a reference to the series-wide - // attribute collection - } else { - pointAttr = seriesPointAttr; - } - - point.pointAttr = pointAttr; - - } - }, - - /** - * Clear DOM objects and free up memory - */ - destroy: function () { - var series = this, - chart = series.chart, - issue134 = /AppleWebKit\/533/.test(userAgent), - destroy, - i, - data = series.data || [], - point, - prop, - axis; - - // add event hook - fireEvent(series, 'destroy'); - - // remove all events - removeEvent(series); - - // erase from axes - each(series.axisTypes || [], function (AXIS) { - axis = series[AXIS]; - if (axis) { - erase(axis.series, series); - axis.isDirty = axis.forceRedraw = true; - } - }); - - // remove legend items - if (series.legendItem) { - series.chart.legend.destroyItem(series); - } - - // destroy all points with their elements - i = data.length; - while (i--) { - point = data[i]; - if (point && point.destroy) { - point.destroy(); - } - } - series.points = null; - - // Clear the animation timeout if we are destroying the series during initial animation - clearTimeout(series.animationTimeout); - - // destroy all SVGElements associated to the series - each(['area', 'graph', 'dataLabelsGroup', 'group', 'markerGroup', 'tracker', - 'graphNeg', 'areaNeg', 'posClip', 'negClip'], function (prop) { - if (series[prop]) { - - // issue 134 workaround - destroy = issue134 && prop === 'group' ? - 'hide' : - 'destroy'; - - series[prop][destroy](); - } - }); - - // remove from hoverSeries - if (chart.hoverSeries === series) { - chart.hoverSeries = null; - } - erase(chart.series, series); - - // clear all members - for (prop in series) { - delete series[prop]; - } - }, - - /** - * Return the graph path of a segment - */ - getSegmentPath: function (segment) { - var series = this, - segmentPath = [], - step = series.options.step; - - // build the segment line - each(segment, function (point, i) { - - var plotX = point.plotX, - plotY = point.plotY, - lastPoint; - - if (series.getPointSpline) { // generate the spline as defined in the SplineSeries object - segmentPath.push.apply(segmentPath, series.getPointSpline(segment, point, i)); - - } else { - - // moveTo or lineTo - segmentPath.push(i ? L : M); - - // step line? - if (step && i) { - lastPoint = segment[i - 1]; - if (step === 'right') { - segmentPath.push( - lastPoint.plotX, - plotY - ); - - } else if (step === 'center') { - segmentPath.push( - (lastPoint.plotX + plotX) / 2, - lastPoint.plotY, - (lastPoint.plotX + plotX) / 2, - plotY - ); - - } else { - segmentPath.push( - plotX, - lastPoint.plotY - ); - } - } - - // normal line to next point - segmentPath.push( - point.plotX, - point.plotY - ); - } - }); - - return segmentPath; - }, - - /** - * Get the graph path - */ - getGraphPath: function () { - var series = this, - graphPath = [], - segmentPath, - singlePoints = []; // used in drawTracker - - // Divide into segments and build graph and area paths - each(series.segments, function (segment) { - - segmentPath = series.getSegmentPath(segment); - - // add the segment to the graph, or a single point for tracking - if (segment.length > 1) { - graphPath = graphPath.concat(segmentPath); - } else { - singlePoints.push(segment[0]); - } - }); - - // Record it for use in drawGraph and drawTracker, and return graphPath - series.singlePoints = singlePoints; - series.graphPath = graphPath; - - return graphPath; - - }, - - /** - * Draw the actual graph - */ - drawGraph: function () { - var series = this, - options = this.options, - props = [['graph', options.lineColor || this.color]], - lineWidth = options.lineWidth, - dashStyle = options.dashStyle, - roundCap = options.linecap !== 'square', - graphPath = this.getGraphPath(), - negativeColor = options.negativeColor; - - if (negativeColor) { - props.push(['graphNeg', negativeColor]); - } - - // draw the graph - each(props, function (prop, i) { - var graphKey = prop[0], - graph = series[graphKey], - attribs; - - if (graph) { - stop(graph); // cancel running animations, #459 - graph.animate({ d: graphPath }); - - } else if (lineWidth && graphPath.length) { // #1487 - attribs = { - stroke: prop[1], - 'stroke-width': lineWidth, - zIndex: 1 // #1069 - }; - if (dashStyle) { - attribs.dashstyle = dashStyle; - } else if (roundCap) { - attribs['stroke-linecap'] = attribs['stroke-linejoin'] = 'round'; - } - - series[graphKey] = series.chart.renderer.path(graphPath) - .attr(attribs) - .add(series.group) - .shadow(!i && options.shadow); - } - }); - }, - - /** - * Clip the graphs into the positive and negative coloured graphs - */ - clipNeg: function () { - var options = this.options, - chart = this.chart, - renderer = chart.renderer, - negativeColor = options.negativeColor || options.negativeFillColor, - translatedThreshold, - posAttr, - negAttr, - graph = this.graph, - area = this.area, - posClip = this.posClip, - negClip = this.negClip, - chartWidth = chart.chartWidth, - chartHeight = chart.chartHeight, - chartSizeMax = mathMax(chartWidth, chartHeight), - yAxis = this.yAxis, - above, - below; - - if (negativeColor && (graph || area)) { - translatedThreshold = mathRound(yAxis.toPixels(options.threshold || 0, true)); - if (translatedThreshold < 0) { - chartSizeMax -= translatedThreshold; // #2534 - } - above = { - x: 0, - y: 0, - width: chartSizeMax, - height: translatedThreshold - }; - below = { - x: 0, - y: translatedThreshold, - width: chartSizeMax, - height: chartSizeMax - }; - - if (chart.inverted) { - - above.height = below.y = chart.plotWidth - translatedThreshold; - if (renderer.isVML) { - above = { - x: chart.plotWidth - translatedThreshold - chart.plotLeft, - y: 0, - width: chartWidth, - height: chartHeight - }; - below = { - x: translatedThreshold + chart.plotLeft - chartWidth, - y: 0, - width: chart.plotLeft + translatedThreshold, - height: chartWidth - }; - } - } - - if (yAxis.reversed) { - posAttr = below; - negAttr = above; - } else { - posAttr = above; - negAttr = below; - } - - if (posClip) { // update - posClip.animate(posAttr); - negClip.animate(negAttr); - } else { - - this.posClip = posClip = renderer.clipRect(posAttr); - this.negClip = negClip = renderer.clipRect(negAttr); - - if (graph && this.graphNeg) { - graph.clip(posClip); - this.graphNeg.clip(negClip); - } - - if (area) { - area.clip(posClip); - this.areaNeg.clip(negClip); - } - } - } - }, - - /** - * Initialize and perform group inversion on series.group and series.markerGroup - */ - invertGroups: function () { - var series = this, - chart = series.chart; - - // Pie, go away (#1736) - if (!series.xAxis) { - return; - } - - // A fixed size is needed for inversion to work - function setInvert() { - var size = { - width: series.yAxis.len, - height: series.xAxis.len - }; - - each(['group', 'markerGroup'], function (groupName) { - if (series[groupName]) { - series[groupName].attr(size).invert(); - } - }); - } - - addEvent(chart, 'resize', setInvert); // do it on resize - addEvent(series, 'destroy', function () { - removeEvent(chart, 'resize', setInvert); - }); - - // Do it now - setInvert(); // do it now - - // On subsequent render and redraw, just do setInvert without setting up events again - series.invertGroups = setInvert; - }, - - /** - * General abstraction for creating plot groups like series.group, series.dataLabelsGroup and - * series.markerGroup. On subsequent calls, the group will only be adjusted to the updated plot size. - */ - plotGroup: function (prop, name, visibility, zIndex, parent) { - var group = this[prop], - isNew = !group; - - // Generate it on first call - if (isNew) { - this[prop] = group = this.chart.renderer.g(name) - .attr({ - visibility: visibility, - zIndex: zIndex || 0.1 // IE8 needs this - }) - .add(parent); - } - // Place it on first and subsequent (redraw) calls - group[isNew ? 'attr' : 'animate'](this.getPlotBox()); - return group; - }, - - /** - * Get the translation and scale for the plot area of this series - */ - getPlotBox: function () { - return { - translateX: this.xAxis ? this.xAxis.left : this.chart.plotLeft, - translateY: this.yAxis ? this.yAxis.top : this.chart.plotTop, - scaleX: 1, // #1623 - scaleY: 1 - }; - }, - - /** - * Render the graph and markers - */ - render: function () { - var series = this, - chart = series.chart, - group, - options = series.options, - animation = options.animation, - doAnimation = animation && !!series.animate && - chart.renderer.isSVG, // this animation doesn't work in IE8 quirks when the group div is hidden, - // and looks bad in other oldIE - visibility = series.visible ? VISIBLE : HIDDEN, - zIndex = options.zIndex, - hasRendered = series.hasRendered, - chartSeriesGroup = chart.seriesGroup; - - // the group - group = series.plotGroup( - 'group', - 'series', - visibility, - zIndex, - chartSeriesGroup - ); - - series.markerGroup = series.plotGroup( - 'markerGroup', - 'markers', - visibility, - zIndex, - chartSeriesGroup - ); - - // initiate the animation - if (doAnimation) { - series.animate(true); - } - - // cache attributes for shapes - series.getAttribs(); - - // SVGRenderer needs to know this before drawing elements (#1089, #1795) - group.inverted = series.isCartesian ? chart.inverted : false; - - // draw the graph if any - if (series.drawGraph) { - series.drawGraph(); - series.clipNeg(); - } - - // draw the data labels (inn pies they go before the points) - if (series.drawDataLabels) { - series.drawDataLabels(); - } - - // draw the points - if (series.visible) { - series.drawPoints(); - } - - - // draw the mouse tracking area - if (series.options.enableMouseTracking !== false) { - series.drawTracker(); - } - - // Handle inverted series and tracker groups - if (chart.inverted) { - series.invertGroups(); - } - - // Initial clipping, must be defined after inverting groups for VML - if (options.clip !== false && !series.sharedClipKey && !hasRendered) { - group.clip(chart.clipRect); - } - - // Run the animation - if (doAnimation) { - series.animate(); - } else if (!hasRendered) { - series.afterAnimate(); - } - - series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see - // (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see - series.hasRendered = true; - }, - - /** - * Redraw the series after an update in the axes. - */ - redraw: function () { - var series = this, - chart = series.chart, - wasDirtyData = series.isDirtyData, // cache it here as it is set to false in render, but used after - group = series.group, - xAxis = series.xAxis, - yAxis = series.yAxis; - - // reposition on resize - if (group) { - if (chart.inverted) { - group.attr({ - width: chart.plotWidth, - height: chart.plotHeight - }); - } - - group.animate({ - translateX: pick(xAxis && xAxis.left, chart.plotLeft), - translateY: pick(yAxis && yAxis.top, chart.plotTop) - }); - } - - series.translate(); - series.setTooltipPoints(true); - - series.render(); - if (wasDirtyData) { - fireEvent(series, 'updatedData'); - } - }, - - /** - * Set the state of the graph - */ - setState: function (state) { - var series = this, - options = series.options, - graph = series.graph, - graphNeg = series.graphNeg, - stateOptions = options.states, - lineWidth = options.lineWidth, - attribs; - - state = state || NORMAL_STATE; - - if (series.state !== state) { - series.state = state; - - if (stateOptions[state] && stateOptions[state].enabled === false) { - return; - } - - if (state) { - lineWidth = stateOptions[state].lineWidth || lineWidth + 1; - } - - if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML - attribs = { - 'stroke-width': lineWidth - }; - // use attr because animate will cause any other animation on the graph to stop - graph.attr(attribs); - if (graphNeg) { - graphNeg.attr(attribs); - } - } - } - }, - - /** - * Set the visibility of the graph - * - * @param vis {Boolean} True to show the series, false to hide. If UNDEFINED, - * the visibility is toggled. - */ - setVisible: function (vis, redraw) { - var series = this, - chart = series.chart, - legendItem = series.legendItem, - showOrHide, - ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, - oldVisibility = series.visible; - - // if called without an argument, toggle visibility - series.visible = vis = series.userOptions.visible = vis === UNDEFINED ? !oldVisibility : vis; - showOrHide = vis ? 'show' : 'hide'; - - // show or hide elements - each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) { - if (series[key]) { - series[key][showOrHide](); - } - }); - - - // hide tooltip (#1361) - if (chart.hoverSeries === series) { - series.onMouseOut(); - } - - - if (legendItem) { - chart.legend.colorizeItem(series, vis); - } - - - // rescale or adapt to resized chart - series.isDirty = true; - // in a stack, all other series are affected - if (series.options.stacking) { - each(chart.series, function (otherSeries) { - if (otherSeries.options.stacking && otherSeries.visible) { - otherSeries.isDirty = true; - } - }); - } - - // show or hide linked series - each(series.linkedSeries, function (otherSeries) { - otherSeries.setVisible(vis, false); - }); - - if (ignoreHiddenSeries) { - chart.isDirtyBox = true; - } - if (redraw !== false) { - chart.redraw(); - } - - fireEvent(series, showOrHide); - }, - - /** - * Show the graph - */ - show: function () { - this.setVisible(true); - }, - - /** - * Hide the graph - */ - hide: function () { - this.setVisible(false); - }, - - - /** - * Set the selected state of the graph - * - * @param selected {Boolean} True to select the series, false to unselect. If - * UNDEFINED, the selection state is toggled. - */ - select: function (selected) { - var series = this; - // if called without an argument, toggle - series.selected = selected = (selected === UNDEFINED) ? !series.selected : selected; - - if (series.checkbox) { - series.checkbox.checked = selected; - } - - fireEvent(series, selected ? 'select' : 'unselect'); - }, - - drawTracker: TrackerMixin.drawTrackerGraph - -}; // end Series prototype - -// Extend the Chart prototype for dynamic methods -extend(Chart.prototype, { - - /** - * Add a series dynamically after time - * - * @param {Object} options The config options - * @param {Boolean} redraw Whether to redraw the chart after adding. Defaults to true. - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - * - * @return {Object} series The newly created series object - */ - addSeries: function (options, redraw, animation) { - var series, - chart = this; - - if (options) { - redraw = pick(redraw, true); // defaults to true - - fireEvent(chart, 'addSeries', { options: options }, function () { - series = chart.initSeries(options); - - chart.isDirtyLegend = true; // the series array is out of sync with the display - chart.linkSeries(); - if (redraw) { - chart.redraw(animation); - } - }); - } - - return series; - }, - - /** - * Add an axis to the chart - * @param {Object} options The axis option - * @param {Boolean} isX Whether it is an X axis or a value axis - */ - addAxis: function (options, isX, redraw, animation) { - var key = isX ? 'xAxis' : 'yAxis', - chartOptions = this.options, - axis; - - /*jslint unused: false*/ - axis = new Axis(this, merge(options, { - index: this[key].length, - isX: isX - })); - /*jslint unused: true*/ - - // Push the new axis options to the chart options - chartOptions[key] = splat(chartOptions[key] || {}); - chartOptions[key].push(options); - - if (pick(redraw, true)) { - this.redraw(animation); - } - }, - - /** - * Dim the chart and show a loading text or symbol - * @param {String} str An optional text to show in the loading label instead of the default one - */ - showLoading: function (str) { - var chart = this, - options = chart.options, - loadingDiv = chart.loadingDiv; - - var loadingOptions = options.loading; - - // create the layer at the first call - if (!loadingDiv) { - chart.loadingDiv = loadingDiv = createElement(DIV, { - className: PREFIX + 'loading' - }, extend(loadingOptions.style, { - zIndex: 10, - display: NONE - }), chart.container); - - chart.loadingSpan = createElement( - 'span', - null, - loadingOptions.labelStyle, - loadingDiv - ); - - } - - // update text - chart.loadingSpan.innerHTML = str || options.lang.loading; - - // show it - if (!chart.loadingShown) { - css(loadingDiv, { - opacity: 0, - display: '', - left: chart.plotLeft + PX, - top: chart.plotTop + PX, - width: chart.plotWidth + PX, - height: chart.plotHeight + PX - }); - animate(loadingDiv, { - opacity: loadingOptions.style.opacity - }, { - duration: loadingOptions.showDuration || 0 - }); - chart.loadingShown = true; - } - }, - - /** - * Hide the loading layer - */ - hideLoading: function () { - var options = this.options, - loadingDiv = this.loadingDiv; - - if (loadingDiv) { - animate(loadingDiv, { - opacity: 0 - }, { - duration: options.loading.hideDuration || 100, - complete: function () { - css(loadingDiv, { display: NONE }); - } - }); - } - this.loadingShown = false; - } -}); - -// extend the Point prototype for dynamic methods -extend(Point.prototype, { - /** - * Update the point with new options (typically x/y data) and optionally redraw the series. - * - * @param {Object} options Point options as defined in the series.data array - * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - * - */ - update: function (options, redraw, animation) { - var point = this, - series = point.series, - graphic = point.graphic, - i, - data = series.data, - chart = series.chart, - seriesOptions = series.options; - - redraw = pick(redraw, true); - - // fire the event with a default handler of doing the update - point.firePointEvent('update', { options: options }, function () { - - point.applyOptions(options); - - // update visuals - if (isObject(options)) { - series.getAttribs(); - if (graphic) { - if (options && options.marker && options.marker.symbol) { - point.graphic = graphic.destroy(); - } else { - graphic.attr(point.pointAttr[point.state || '']); - } - } - if (options && options.dataLabels && point.dataLabel) { // #2468 - point.dataLabel = point.dataLabel.destroy(); - } - } - - // record changes in the parallel arrays - i = inArray(point, data); - series.updateParallelArrays(point, i); - - seriesOptions.data[i] = point.options; - - // redraw - series.isDirty = series.isDirtyData = true; - if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320 - chart.isDirtyBox = true; - } - - if (seriesOptions.legendType === 'point') { // #1831, #1885 - chart.legend.destroyItem(point); - } - if (redraw) { - chart.redraw(animation); - } - }); - }, - - /** - * Remove a point and optionally redraw the series and if necessary the axes - * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - */ - remove: function (redraw, animation) { - var point = this, - series = point.series, - points = series.points, - chart = series.chart, - i, - data = series.data; - - setAnimation(animation, chart); - redraw = pick(redraw, true); - - // fire the event with a default handler of removing the point - point.firePointEvent('remove', null, function () { - - // splice all the parallel arrays - i = inArray(point, data); - if (data.length === points.length) { - points.splice(i, 1); - } - data.splice(i, 1); - series.options.data.splice(i, 1); - series.updateParallelArrays(point, 'splice', i, 1); - - point.destroy(); - - // redraw - series.isDirty = true; - series.isDirtyData = true; - if (redraw) { - chart.redraw(); - } - }); - } -}); - -// Extend the series prototype for dynamic methods -extend(Series.prototype, { - /** - * Add a point dynamically after chart load time - * @param {Object} options Point options as given in series.data - * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call - * @param {Boolean} shift If shift is true, a point is shifted off the start - * of the series as one is appended to the end. - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - */ - addPoint: function (options, redraw, shift, animation) { - var series = this, - seriesOptions = series.options, - data = series.data, - graph = series.graph, - area = series.area, - chart = series.chart, - names = series.xAxis && series.xAxis.names, - currentShift = (graph && graph.shift) || 0, - dataOptions = seriesOptions.data, - point, - isInTheMiddle, - xData = series.xData, - x, - i; - - setAnimation(animation, chart); - - // Make graph animate sideways - if (shift) { - each([graph, area, series.graphNeg, series.areaNeg], function (shape) { - if (shape) { - shape.shift = currentShift + 1; - } - }); - } - if (area) { - area.isArea = true; // needed in animation, both with and without shift - } - - // Optional redraw, defaults to true - redraw = pick(redraw, true); - - // Get options and push the point to xData, yData and series.options. In series.generatePoints - // the Point instance will be created on demand and pushed to the series.data array. - point = { series: series }; - series.pointClass.prototype.applyOptions.apply(point, [options]); - x = point.x; - - // Get the insertion point - i = xData.length; - if (series.requireSorting && x < xData[i - 1]) { - isInTheMiddle = true; - while (i && xData[i - 1] > x) { - i--; - } - } - - series.updateParallelArrays(point, 'splice', i, 0, 0); // insert undefined item - series.updateParallelArrays(point, i); // update it - - if (names) { - names[x] = point.name; - } - dataOptions.splice(i, 0, options); - - if (isInTheMiddle) { - series.data.splice(i, 0, null); - series.processData(); - } - - // Generate points to be added to the legend (#1329) - if (seriesOptions.legendType === 'point') { - series.generatePoints(); - } - - // Shift the first point off the parallel arrays - // todo: consider series.removePoint(i) method - if (shift) { - if (data[0] && data[0].remove) { - data[0].remove(false); - } else { - data.shift(); - series.updateParallelArrays(point, 'shift'); - - dataOptions.shift(); - } - } - - // redraw - series.isDirty = true; - series.isDirtyData = true; - if (redraw) { - series.getAttribs(); // #1937 - chart.redraw(); - } - }, - - /** - * Remove a series and optionally redraw the chart - * - * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call - * @param {Boolean|Object} animation Whether to apply animation, and optionally animation - * configuration - */ - - remove: function (redraw, animation) { - var series = this, - chart = series.chart; - redraw = pick(redraw, true); - - if (!series.isRemoving) { /* prevent triggering native event in jQuery - (calling the remove function from the remove event) */ - series.isRemoving = true; - - // fire the event with a default handler of removing the point - fireEvent(series, 'remove', null, function () { - - - // destroy elements - series.destroy(); - - - // redraw - chart.isDirtyLegend = chart.isDirtyBox = true; - chart.linkSeries(); - - if (redraw) { - chart.redraw(animation); - } - }); - - } - series.isRemoving = false; - }, - - /** - * Update the series with a new set of options - */ - update: function (newOptions, redraw) { - var chart = this.chart, - // must use user options when changing type because this.options is merged - // in with type specific plotOptions - oldOptions = this.userOptions, - oldType = this.type, - proto = seriesTypes[oldType].prototype, - n; - - // Do the merge, with some forced options - newOptions = merge(oldOptions, { - animation: false, - index: this.index, - pointStart: this.xData[0] // when updating after addPoint - }, { data: this.options.data }, newOptions); - - // Destroy the series and reinsert methods from the type prototype - this.remove(false); - for (n in proto) { // Overwrite series-type specific methods (#2270) - if (proto.hasOwnProperty(n)) { - this[n] = UNDEFINED; - } - } - extend(this, seriesTypes[newOptions.type || oldType].prototype); - - - this.init(chart, newOptions); - if (pick(redraw, true)) { - chart.redraw(false); - } - } -}); - -// Extend the Axis.prototype for dynamic methods -extend(Axis.prototype, { - - /** - * Update the axis with a new options structure - */ - update: function (newOptions, redraw) { - var chart = this.chart; - - newOptions = chart.options[this.coll][this.options.index] = merge(this.userOptions, newOptions); - - this.destroy(true); - this._addedPlotLB = this.userMin = this.userMax = UNDEFINED; // #1611, #2306 - - this.init(chart, extend(newOptions, { events: UNDEFINED })); - - chart.isDirtyBox = true; - if (pick(redraw, true)) { - chart.redraw(); - } - }, - - /** - * Remove the axis from the chart - */ - remove: function (redraw) { - var chart = this.chart, - key = this.coll; // xAxis or yAxis - - // Remove associated series - each(this.series, function (series) { - series.remove(false); - }); - - // Remove the axis - erase(chart.axes, this); - erase(chart[key], this); - chart.options[key].splice(this.options.index, 1); - each(chart[key], function (axis, i) { // Re-index, #1706 - axis.options.index = i; - }); - this.destroy(); - chart.isDirtyBox = true; - - if (pick(redraw, true)) { - chart.redraw(); - } - }, - - /** - * Update the axis title by options - */ - setTitle: function (newTitleOptions, redraw) { - this.update({ title: newTitleOptions }, redraw); - }, - - /** - * Set new axis categories and optionally redraw - * @param {Array} categories - * @param {Boolean} redraw - */ - setCategories: function (categories, redraw) { - this.update({ categories: categories }, redraw); - } - -}); - - -/** - * LineSeries object - */ -var LineSeries = extendClass(Series); -seriesTypes.line = LineSeries; - -/** - * Set the default options for area - */ -defaultPlotOptions.area = merge(defaultSeriesOptions, { - threshold: 0 - // trackByArea: false, - // lineColor: null, // overrides color, but lets fillColor be unaltered - // fillOpacity: 0.75, - // fillColor: null -}); - -/** - * AreaSeries object - */ -var AreaSeries = extendClass(Series, { - type: 'area', - /** - * For stacks, don't split segments on null values. Instead, draw null values with - * no marker. Also insert dummy points for any X position that exists in other series - * in the stack. - */ - getSegments: function () { - var segments = [], - segment = [], - keys = [], - xAxis = this.xAxis, - yAxis = this.yAxis, - stack = yAxis.stacks[this.stackKey], - pointMap = {}, - plotX, - plotY, - points = this.points, - connectNulls = this.options.connectNulls, - val, - i, - x; - - if (this.options.stacking && !this.cropped) { // cropped causes artefacts in Stock, and perf issue - // Create a map where we can quickly look up the points by their X value. - for (i = 0; i < points.length; i++) { - pointMap[points[i].x] = points[i]; - } - - // Sort the keys (#1651) - for (x in stack) { - if (stack[x].total !== null) { // nulled after switching between grouping and not (#1651, #2336) - keys.push(+x); - } - } - keys.sort(function (a, b) { - return a - b; - }); - - each(keys, function (x) { - if (connectNulls && (!pointMap[x] || pointMap[x].y === null)) { // #1836 - return; - - // The point exists, push it to the segment - } else if (pointMap[x]) { - segment.push(pointMap[x]); - - // There is no point for this X value in this series, so we - // insert a dummy point in order for the areas to be drawn - // correctly. - } else { - plotX = xAxis.translate(x); - val = stack[x].percent ? (stack[x].total ? stack[x].cum * 100 / stack[x].total : 0) : stack[x].cum; // #1991 - plotY = yAxis.toPixels(val, true); - segment.push({ - y: null, - plotX: plotX, - clientX: plotX, - plotY: plotY, - yBottom: plotY, - onMouseOver: noop - }); - } - }); - - if (segment.length) { - segments.push(segment); - } - - } else { - Series.prototype.getSegments.call(this); - segments = this.segments; - } - - this.segments = segments; - }, - - /** - * Extend the base Series getSegmentPath method by adding the path for the area. - * This path is pushed to the series.areaPath property. - */ - getSegmentPath: function (segment) { - - var segmentPath = Series.prototype.getSegmentPath.call(this, segment), // call base method - areaSegmentPath = [].concat(segmentPath), // work on a copy for the area path - i, - options = this.options, - segLength = segmentPath.length, - translatedThreshold = this.yAxis.getThreshold(options.threshold), // #2181 - yBottom; - - if (segLength === 3) { // for animation from 1 to two points - areaSegmentPath.push(L, segmentPath[1], segmentPath[2]); - } - if (options.stacking && !this.closedStacks) { - - // Follow stack back. Todo: implement areaspline. A general solution could be to - // reverse the entire graphPath of the previous series, though may be hard with - // splines and with series with different extremes - for (i = segment.length - 1; i >= 0; i--) { - - yBottom = pick(segment[i].yBottom, translatedThreshold); - - // step line? - if (i < segment.length - 1 && options.step) { - areaSegmentPath.push(segment[i + 1].plotX, yBottom); - } - - areaSegmentPath.push(segment[i].plotX, yBottom); - } - - } else { // follow zero line back - this.closeSegment(areaSegmentPath, segment, translatedThreshold); - } - this.areaPath = this.areaPath.concat(areaSegmentPath); - return segmentPath; - }, - - /** - * Extendable method to close the segment path of an area. This is overridden in polar - * charts. - */ - closeSegment: function (path, segment, translatedThreshold) { - path.push( - L, - segment[segment.length - 1].plotX, - translatedThreshold, - L, - segment[0].plotX, - translatedThreshold - ); - }, - - /** - * Draw the graph and the underlying area. This method calls the Series base - * function and adds the area. The areaPath is calculated in the getSegmentPath - * method called from Series.prototype.drawGraph. - */ - drawGraph: function () { - - // Define or reset areaPath - this.areaPath = []; - - // Call the base method - Series.prototype.drawGraph.apply(this); - - // Define local variables - var series = this, - areaPath = this.areaPath, - options = this.options, - negativeColor = options.negativeColor, - negativeFillColor = options.negativeFillColor, - props = [['area', this.color, options.fillColor]]; // area name, main color, fill color - - if (negativeColor || negativeFillColor) { - props.push(['areaNeg', negativeColor, negativeFillColor]); - } - - each(props, function (prop) { - var areaKey = prop[0], - area = series[areaKey]; - - // Create or update the area - if (area) { // update - area.animate({ d: areaPath }); - - } else { // create - series[areaKey] = series.chart.renderer.path(areaPath) - .attr({ - fill: pick( - prop[2], - Color(prop[1]).setOpacity(pick(options.fillOpacity, 0.75)).get() - ), - zIndex: 0 // #1069 - }).add(series.group); - } - }); - }, - - drawLegendSymbol: LegendSymbolMixin.drawRectangle -}); - -seriesTypes.area = AreaSeries; -/** - * Set the default options for spline - */ -defaultPlotOptions.spline = merge(defaultSeriesOptions); - -/** - * SplineSeries object - */ -var SplineSeries = extendClass(Series, { - type: 'spline', - - /** - * Get the spline segment from a given point's previous neighbour to the given point - */ - getPointSpline: function (segment, point, i) { - var smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc - denom = smoothing + 1, - plotX = point.plotX, - plotY = point.plotY, - lastPoint = segment[i - 1], - nextPoint = segment[i + 1], - leftContX, - leftContY, - rightContX, - rightContY, - ret; - - // find control points - if (lastPoint && nextPoint) { - - var lastX = lastPoint.plotX, - lastY = lastPoint.plotY, - nextX = nextPoint.plotX, - nextY = nextPoint.plotY, - correction; - - leftContX = (smoothing * plotX + lastX) / denom; - leftContY = (smoothing * plotY + lastY) / denom; - rightContX = (smoothing * plotX + nextX) / denom; - rightContY = (smoothing * plotY + nextY) / denom; - - // have the two control points make a straight line through main point - correction = ((rightContY - leftContY) * (rightContX - plotX)) / - (rightContX - leftContX) + plotY - rightContY; - - leftContY += correction; - rightContY += correction; - - // to prevent false extremes, check that control points are between - // neighbouring points' y values - if (leftContY > lastY && leftContY > plotY) { - leftContY = mathMax(lastY, plotY); - rightContY = 2 * plotY - leftContY; // mirror of left control point - } else if (leftContY < lastY && leftContY < plotY) { - leftContY = mathMin(lastY, plotY); - rightContY = 2 * plotY - leftContY; - } - if (rightContY > nextY && rightContY > plotY) { - rightContY = mathMax(nextY, plotY); - leftContY = 2 * plotY - rightContY; - } else if (rightContY < nextY && rightContY < plotY) { - rightContY = mathMin(nextY, plotY); - leftContY = 2 * plotY - rightContY; - } - - // record for drawing in next point - point.rightContX = rightContX; - point.rightContY = rightContY; - - } - - // Visualize control points for debugging - /* - if (leftContX) { - this.chart.renderer.circle(leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, 2) - .attr({ - stroke: 'red', - 'stroke-width': 1, - fill: 'none' - }) - .add(); - this.chart.renderer.path(['M', leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, - 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop]) - .attr({ - stroke: 'red', - 'stroke-width': 1 - }) - .add(); - this.chart.renderer.circle(rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, 2) - .attr({ - stroke: 'green', - 'stroke-width': 1, - fill: 'none' - }) - .add(); - this.chart.renderer.path(['M', rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, - 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop]) - .attr({ - stroke: 'green', - 'stroke-width': 1 - }) - .add(); - } - */ - - // moveTo or lineTo - if (!i) { - ret = [M, plotX, plotY]; - } else { // curve from last point to this - ret = [ - 'C', - lastPoint.rightContX || lastPoint.plotX, - lastPoint.rightContY || lastPoint.plotY, - leftContX || plotX, - leftContY || plotY, - plotX, - plotY - ]; - lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later - } - return ret; - } -}); -seriesTypes.spline = SplineSeries; - -/** - * Set the default options for areaspline - */ -defaultPlotOptions.areaspline = merge(defaultPlotOptions.area); - -/** - * AreaSplineSeries object - */ -var areaProto = AreaSeries.prototype, - AreaSplineSeries = extendClass(SplineSeries, { - type: 'areaspline', - closedStacks: true, // instead of following the previous graph back, follow the threshold back - - // Mix in methods from the area series - getSegmentPath: areaProto.getSegmentPath, - closeSegment: areaProto.closeSegment, - drawGraph: areaProto.drawGraph, - drawLegendSymbol: LegendSymbolMixin.drawRectangle - }); - -seriesTypes.areaspline = AreaSplineSeries; - -/** - * Set the default options for column - */ -defaultPlotOptions.column = merge(defaultSeriesOptions, { - borderColor: '#FFFFFF', - borderWidth: 1, - borderRadius: 0, - //colorByPoint: undefined, - groupPadding: 0.2, - //grouping: true, - marker: null, // point options are specified in the base options - pointPadding: 0.1, - //pointWidth: null, - minPointLength: 0, - cropThreshold: 50, // when there are more points, they will not animate out of the chart on xAxis.setExtremes - pointRange: null, // null means auto, meaning 1 in a categorized axis and least distance between points if not categories - states: { - hover: { - brightness: 0.1, - shadow: false - }, - select: { - color: '#C0C0C0', - borderColor: '#000000', - shadow: false - } - }, - dataLabels: { - align: null, // auto - verticalAlign: null, // auto - y: null - }, - stickyTracking: false, - threshold: 0 -}); - -/** - * ColumnSeries object - */ -var ColumnSeries = extendClass(Series, { - type: 'column', - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'borderColor', - 'stroke-width': 'borderWidth', - fill: 'color', - r: 'borderRadius' - }, - cropShoulder: 0, - trackerGroups: ['group', 'dataLabelsGroup'], - negStacks: true, // use separate negative stacks, unlike area stacks where a negative - // point is substracted from previous (#1910) - - /** - * Initialize the series - */ - init: function () { - Series.prototype.init.apply(this, arguments); - - var series = this, - chart = series.chart; - - // if the series is added dynamically, force redraw of other - // series affected by a new column - if (chart.hasRendered) { - each(chart.series, function (otherSeries) { - if (otherSeries.type === series.type) { - otherSeries.isDirty = true; - } - }); - } - }, - - /** - * Return the width and x offset of the columns adjusted for grouping, groupPadding, pointPadding, - * pointWidth etc. - */ - getColumnMetrics: function () { - - var series = this, - options = series.options, - xAxis = series.xAxis, - yAxis = series.yAxis, - reversedXAxis = xAxis.reversed, - stackKey, - stackGroups = {}, - columnIndex, - columnCount = 0; - - // Get the total number of column type series. - // This is called on every series. Consider moving this logic to a - // chart.orderStacks() function and call it on init, addSeries and removeSeries - if (options.grouping === false) { - columnCount = 1; - } else { - each(series.chart.series, function (otherSeries) { - var otherOptions = otherSeries.options, - otherYAxis = otherSeries.yAxis; - if (otherSeries.type === series.type && otherSeries.visible && - yAxis.len === otherYAxis.len && yAxis.pos === otherYAxis.pos) { // #642, #2086 - if (otherOptions.stacking) { - stackKey = otherSeries.stackKey; - if (stackGroups[stackKey] === UNDEFINED) { - stackGroups[stackKey] = columnCount++; - } - columnIndex = stackGroups[stackKey]; - } else if (otherOptions.grouping !== false) { // #1162 - columnIndex = columnCount++; - } - otherSeries.columnIndex = columnIndex; - } - }); - } - - var categoryWidth = mathMin( - mathAbs(xAxis.transA) * (xAxis.ordinalSlope || options.pointRange || xAxis.closestPointRange || 1), - xAxis.len // #1535 - ), - groupPadding = categoryWidth * options.groupPadding, - groupWidth = categoryWidth - 2 * groupPadding, - pointOffsetWidth = groupWidth / columnCount, - optionPointWidth = options.pointWidth, - pointPadding = defined(optionPointWidth) ? (pointOffsetWidth - optionPointWidth) / 2 : - pointOffsetWidth * options.pointPadding, - pointWidth = pick(optionPointWidth, pointOffsetWidth - 2 * pointPadding), // exact point width, used in polar charts - colIndex = (reversedXAxis ? - columnCount - (series.columnIndex || 0) : // #1251 - series.columnIndex) || 0, - pointXOffset = pointPadding + (groupPadding + colIndex * - pointOffsetWidth - (categoryWidth / 2)) * - (reversedXAxis ? -1 : 1); - - // Save it for reading in linked series (Error bars particularly) - return (series.columnMetrics = { - width: pointWidth, - offset: pointXOffset - }); - - }, - - /** - * Translate each point to the plot area coordinate system and find shape positions - */ - translate: function () { - var series = this, - chart = series.chart, - options = series.options, - borderWidth = options.borderWidth, - yAxis = series.yAxis, - threshold = options.threshold, - translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold), - minPointLength = pick(options.minPointLength, 5), - metrics = series.getColumnMetrics(), - pointWidth = metrics.width, - seriesBarW = series.barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width - pointXOffset = series.pointXOffset = metrics.offset, - xCrisp = -(borderWidth % 2 ? 0.5 : 0), - yCrisp = borderWidth % 2 ? 0.5 : 1; - - if (chart.renderer.isVML && chart.inverted) { - yCrisp += 1; - } - - Series.prototype.translate.apply(series); - - // record the new values - each(series.points, function (point) { - var yBottom = pick(point.yBottom, translatedThreshold), - plotY = mathMin(mathMax(-999 - yBottom, point.plotY), yAxis.len + 999 + yBottom), // Don't draw too far outside plot area (#1303, #2241) - barX = point.plotX + pointXOffset, - barW = seriesBarW, - barY = mathMin(plotY, yBottom), - right, - bottom, - fromTop, - fromLeft, - barH = mathMax(plotY, yBottom) - barY; - - // Handle options.minPointLength - if (mathAbs(barH) < minPointLength) { - if (minPointLength) { - barH = minPointLength; - barY = - mathRound(mathAbs(barY - translatedThreshold) > minPointLength ? // stacked - yBottom - minPointLength : // keep position - translatedThreshold - (yAxis.translate(point.y, 0, 1, 0, 1) <= translatedThreshold ? minPointLength : 0)); // use exact yAxis.translation (#1485) - } - } - - // Cache for access in polar - point.barX = barX; - point.pointWidth = pointWidth; - - - // Round off to obtain crisp edges - fromLeft = mathAbs(barX) < 0.5; - right = mathRound(barX + barW) + xCrisp; - barX = mathRound(barX) + xCrisp; - barW = right - barX; - - fromTop = mathAbs(barY) < 0.5; - bottom = mathRound(barY + barH) + yCrisp; - barY = mathRound(barY) + yCrisp; - barH = bottom - barY; - - // Top and left edges are exceptions - if (fromLeft) { - barX += 1; - barW -= 1; - } - if (fromTop) { - barY -= 1; - barH += 1; - } - - // Register shape type and arguments to be used in drawPoints - point.shapeType = 'rect'; - point.shapeArgs = { - x: barX, - y: barY, - width: barW, - height: barH - }; - }); - - }, - - getSymbol: noop, - - /** - * Use a solid rectangle like the area series types - */ - drawLegendSymbol: LegendSymbolMixin.drawRectangle, - - - /** - * Columns have no graph - */ - drawGraph: noop, - - /** - * Draw the columns. For bars, the series.group is rotated, so the same coordinates - * apply for columns and bars. This method is inherited by scatter series. - * - */ - drawPoints: function () { - var series = this, - chart = this.chart, - options = series.options, - renderer = chart.renderer, - animationLimit = chart.options.animationLimit || 250, - shapeArgs; - - // draw the columns - each(series.points, function (point) { - var plotY = point.plotY, - graphic = point.graphic; - - if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { - shapeArgs = point.shapeArgs; - - if (graphic) { // update - stop(graphic); - graphic[chart.pointCount < animationLimit ? 'animate' : 'attr'](merge(shapeArgs)); - - } else { - point.graphic = graphic = renderer[point.shapeType](shapeArgs) - .attr(point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]) - .add(series.group) - .shadow(options.shadow, null, options.stacking && !options.borderRadius); - } - - } else if (graphic) { - point.graphic = graphic.destroy(); // #1269 - } - }); - }, - - /** - * Add tracking event listener to the series group, so the point graphics - * themselves act as trackers - */ - drawTracker: TrackerMixin.drawTrackerPoint, - - /** - * Animate the column heights one by one from zero - * @param {Boolean} init Whether to initialize the animation or run it - */ - animate: function (init) { - var series = this, - yAxis = this.yAxis, - options = series.options, - inverted = this.chart.inverted, - attr = {}, - translatedThreshold; - - if (hasSVG) { // VML is too slow anyway - if (init) { - attr.scaleY = 0.001; - translatedThreshold = mathMin(yAxis.pos + yAxis.len, mathMax(yAxis.pos, yAxis.toPixels(options.threshold))); - if (inverted) { - attr.translateX = translatedThreshold - yAxis.len; - } else { - attr.translateY = translatedThreshold; - } - series.group.attr(attr); - - } else { // run the animation - - attr.scaleY = 1; - attr[inverted ? 'translateX' : 'translateY'] = yAxis.pos; - series.group.animate(attr, series.options.animation); - - // delete this function to allow it only once - series.animate = null; - } - } - }, - - /** - * Remove this series from the chart - */ - remove: function () { - var series = this, - chart = series.chart; - - // column and bar series affects other series of the same type - // as they are either stacked or grouped - if (chart.hasRendered) { - each(chart.series, function (otherSeries) { - if (otherSeries.type === series.type) { - otherSeries.isDirty = true; - } - }); - } - - Series.prototype.remove.apply(series, arguments); - } -}); -seriesTypes.column = ColumnSeries; -/** - * Set the default options for bar - */ -defaultPlotOptions.bar = merge(defaultPlotOptions.column); -/** - * The Bar series class - */ -var BarSeries = extendClass(ColumnSeries, { - type: 'bar', - inverted: true -}); -seriesTypes.bar = BarSeries; - -/** - * Set the default options for scatter - */ -defaultPlotOptions.scatter = merge(defaultSeriesOptions, { - lineWidth: 0, - tooltip: { - headerFormat: '<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>', - pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>', - followPointer: true - }, - stickyTracking: false -}); - -/** - * The scatter series class - */ -var ScatterSeries = extendClass(Series, { - type: 'scatter', - sorted: false, - requireSorting: false, - noSharedTooltip: true, - trackerGroups: ['markerGroup'], - takeOrdinalPosition: false, // #2342 - drawTracker: TrackerMixin.drawTrackerPoint, - drawGraph: function () { - if (this.options.lineWidth) { - Series.prototype.drawGraph.call(this); - } - }, - setTooltipPoints: noop -}); - -seriesTypes.scatter = ScatterSeries; - -/** - * Set the default options for pie - */ -defaultPlotOptions.pie = merge(defaultSeriesOptions, { - borderColor: '#FFFFFF', - borderWidth: 1, - center: [null, null], - clip: false, - colorByPoint: true, // always true for pies - dataLabels: { - // align: null, - // connectorWidth: 1, - // connectorColor: point.color, - // connectorPadding: 5, - distance: 30, - enabled: true, - formatter: function () { - return this.point.name; - } - // softConnector: true, - //y: 0 - }, - ignoreHiddenPoint: true, - //innerSize: 0, - legendType: 'point', - marker: null, // point options are specified in the base options - size: null, - showInLegend: false, - slicedOffset: 10, - states: { - hover: { - brightness: 0.1, - shadow: false - } - }, - stickyTracking: false, - tooltip: { - followPointer: true - } -}); - -/** - * Extended point object for pies - */ -var PiePoint = extendClass(Point, { - /** - * Initiate the pie slice - */ - init: function () { - - Point.prototype.init.apply(this, arguments); - - var point = this, - toggleSlice; - - // Disallow negative values (#1530) - if (point.y < 0) { - point.y = null; - } - - //visible: options.visible !== false, - extend(point, { - visible: point.visible !== false, - name: pick(point.name, 'Slice') - }); - - // add event listener for select - toggleSlice = function (e) { - point.slice(e.type === 'select'); - }; - addEvent(point, 'select', toggleSlice); - addEvent(point, 'unselect', toggleSlice); - - return point; - }, - - /** - * Toggle the visibility of the pie slice - * @param {Boolean} vis Whether to show the slice or not. If undefined, the - * visibility is toggled - */ - setVisible: function (vis) { - var point = this, - series = point.series, - chart = series.chart, - method; - - // if called without an argument, toggle visibility - point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis; - series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data - - method = vis ? 'show' : 'hide'; - - // Show and hide associated elements - each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) { - if (point[key]) { - point[key][method](); - } - }); - - if (point.legendItem) { - chart.legend.colorizeItem(point, vis); - } - - // Handle ignore hidden slices - if (!series.isDirty && series.options.ignoreHiddenPoint) { - series.isDirty = true; - chart.redraw(); - } - }, - - /** - * Set or toggle whether the slice is cut out from the pie - * @param {Boolean} sliced When undefined, the slice state is toggled - * @param {Boolean} redraw Whether to redraw the chart. True by default. - */ - slice: function (sliced, redraw, animation) { - var point = this, - series = point.series, - chart = series.chart, - translation; - - setAnimation(animation, chart); - - // redraw is true by default - redraw = pick(redraw, true); - - // if called without an argument, toggle - point.sliced = point.options.sliced = sliced = defined(sliced) ? sliced : !point.sliced; - series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data - - translation = sliced ? point.slicedTranslation : { - translateX: 0, - translateY: 0 - }; - - point.graphic.animate(translation); - - if (point.shadowGroup) { - point.shadowGroup.animate(translation); - } - - } -}); - -/** - * The Pie series class - */ -var PieSeries = { - type: 'pie', - isCartesian: false, - pointClass: PiePoint, - requireSorting: false, - noSharedTooltip: true, - trackerGroups: ['group', 'dataLabelsGroup'], - axisTypes: [], - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'borderColor', - 'stroke-width': 'borderWidth', - fill: 'color' - }, - - /** - * Pies have one color each point - */ - getColor: noop, - - /** - * Animate the pies in - */ - animate: function (init) { - var series = this, - points = series.points, - startAngleRad = series.startAngleRad; - - if (!init) { - each(points, function (point) { - var graphic = point.graphic, - args = point.shapeArgs; - - if (graphic) { - // start values - graphic.attr({ - r: series.center[3] / 2, // animate from inner radius (#779) - start: startAngleRad, - end: startAngleRad - }); - - // animate - graphic.animate({ - r: args.r, - start: args.start, - end: args.end - }, series.options.animation); - } - }); - - // delete this function to allow it only once - series.animate = null; - } - }, - - /** - * Extend the basic setData method by running processData and generatePoints immediately, - * in order to access the points from the legend. - */ - setData: function (data, redraw) { - Series.prototype.setData.call(this, data, false); - this.processData(); - this.generatePoints(); - if (pick(redraw, true)) { - this.chart.redraw(); - } - }, - - /** - * Extend the generatePoints method by adding total and percentage properties to each point - */ - generatePoints: function () { - var i, - total = 0, - points, - len, - point, - ignoreHiddenPoint = this.options.ignoreHiddenPoint; - - Series.prototype.generatePoints.call(this); - - // Populate local vars - points = this.points; - len = points.length; - - // Get the total sum - for (i = 0; i < len; i++) { - point = points[i]; - total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y; - } - this.total = total; - - // Set each point's properties - for (i = 0; i < len; i++) { - point = points[i]; - point.percentage = total > 0 ? (point.y / total) * 100 : 0; - point.total = total; - } - - }, - - /** - * Do translation for pie slices - */ - translate: function (positions) { - this.generatePoints(); - - var series = this, - cumulative = 0, - precision = 1000, // issue #172 - options = series.options, - slicedOffset = options.slicedOffset, - connectorOffset = slicedOffset + options.borderWidth, - start, - end, - angle, - startAngle = options.startAngle || 0, - startAngleRad = series.startAngleRad = mathPI / 180 * (startAngle - 90), - endAngleRad = series.endAngleRad = mathPI / 180 * ((options.endAngle || (startAngle + 360)) - 90), - circ = endAngleRad - startAngleRad, //2 * mathPI, - points = series.points, - radiusX, // the x component of the radius vector for a given point - radiusY, - labelDistance = options.dataLabels.distance, - ignoreHiddenPoint = options.ignoreHiddenPoint, - i, - len = points.length, - point; - - // Get positions - either an integer or a percentage string must be given. - // If positions are passed as a parameter, we're in a recursive loop for adjusting - // space for data labels. - if (!positions) { - series.center = positions = series.getCenter(); - } - - // utility for getting the x value from a given y, used for anticollision logic in data labels - series.getX = function (y, left) { - - angle = math.asin((y - positions[1]) / (positions[2] / 2 + labelDistance)); - - return positions[0] + - (left ? -1 : 1) * - (mathCos(angle) * (positions[2] / 2 + labelDistance)); - }; - - // Calculate the geometry for each point - for (i = 0; i < len; i++) { - - point = points[i]; - - // set start and end angle - start = startAngleRad + (cumulative * circ); - if (!ignoreHiddenPoint || point.visible) { - cumulative += point.percentage / 100; - } - end = startAngleRad + (cumulative * circ); - - // set the shape - point.shapeType = 'arc'; - point.shapeArgs = { - x: positions[0], - y: positions[1], - r: positions[2] / 2, - innerR: positions[3] / 2, - start: mathRound(start * precision) / precision, - end: mathRound(end * precision) / precision - }; - - // center for the sliced out slice - angle = (end + start) / 2; - if (angle > 0.75 * circ) { - angle -= 2 * mathPI; - } - point.slicedTranslation = { - translateX: mathRound(mathCos(angle) * slicedOffset), - translateY: mathRound(mathSin(angle) * slicedOffset) - }; - - // set the anchor point for tooltips - radiusX = mathCos(angle) * positions[2] / 2; - radiusY = mathSin(angle) * positions[2] / 2; - point.tooltipPos = [ - positions[0] + radiusX * 0.7, - positions[1] + radiusY * 0.7 - ]; - - point.half = angle < -mathPI / 2 || angle > mathPI / 2 ? 1 : 0; - point.angle = angle; - - // set the anchor point for data labels - connectorOffset = mathMin(connectorOffset, labelDistance / 2); // #1678 - point.labelPos = [ - positions[0] + radiusX + mathCos(angle) * labelDistance, // first break of connector - positions[1] + radiusY + mathSin(angle) * labelDistance, // a/a - positions[0] + radiusX + mathCos(angle) * connectorOffset, // second break, right outside pie - positions[1] + radiusY + mathSin(angle) * connectorOffset, // a/a - positions[0] + radiusX, // landing point for connector - positions[1] + radiusY, // a/a - labelDistance < 0 ? // alignment - 'center' : - point.half ? 'right' : 'left', // alignment - angle // center angle - ]; - - } - }, - - setTooltipPoints: noop, - drawGraph: null, - - /** - * Draw the data points - */ - drawPoints: function () { - var series = this, - chart = series.chart, - renderer = chart.renderer, - groupTranslation, - //center, - graphic, - //group, - shadow = series.options.shadow, - shadowGroup, - shapeArgs; - - if (shadow && !series.shadowGroup) { - series.shadowGroup = renderer.g('shadow') - .add(series.group); - } - - // draw the slices - each(series.points, function (point) { - graphic = point.graphic; - shapeArgs = point.shapeArgs; - shadowGroup = point.shadowGroup; - - // put the shadow behind all points - if (shadow && !shadowGroup) { - shadowGroup = point.shadowGroup = renderer.g('shadow') - .add(series.shadowGroup); - } - - // if the point is sliced, use special translation, else use plot area traslation - groupTranslation = point.sliced ? point.slicedTranslation : { - translateX: 0, - translateY: 0 - }; - - //group.translate(groupTranslation[0], groupTranslation[1]); - if (shadowGroup) { - shadowGroup.attr(groupTranslation); - } - - // draw the slice - if (graphic) { - graphic.animate(extend(shapeArgs, groupTranslation)); - } else { - point.graphic = graphic = renderer.arc(shapeArgs) - .setRadialReference(series.center) - .attr( - point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] - ) - .attr({ 'stroke-linejoin': 'round' }) - .attr(groupTranslation) - .add(series.group) - .shadow(shadow, shadowGroup); - } - - // detect point specific visibility (#2430) - if (point.visible !== undefined) { - point.setVisible(point.visible); - } - - }); - - }, - - /** - * Utility for sorting data labels - */ - sortByAngle: function (points, sign) { - points.sort(function (a, b) { - return a.angle !== undefined && (b.angle - a.angle) * sign; - }); - }, - - /** - * Draw point specific tracker objects. Inherit directly from column series. - */ - drawTracker: TrackerMixin.drawTrackerPoint, - - /** - * Use a simple symbol from LegendSymbolMixin - */ - drawLegendSymbol: LegendSymbolMixin.drawRectangle, - - /** - * Use the getCenter method from drawLegendSymbol - */ - getCenter: CenteredSeriesMixin.getCenter, - - /** - * Pies don't have point marker symbols - */ - getSymbol: noop - -}; -PieSeries = extendClass(Series, PieSeries); -seriesTypes.pie = PieSeries; - -/** - * Draw the data labels - */ -Series.prototype.drawDataLabels = function () { - - var series = this, - seriesOptions = series.options, - cursor = seriesOptions.cursor, - options = seriesOptions.dataLabels, - points = series.points, - pointOptions, - generalOptions, - str, - dataLabelsGroup; - - if (options.enabled || series._hasPointLabels) { - - // Process default alignment of data labels for columns - if (series.dlProcessOptions) { - series.dlProcessOptions(options); - } - - // Create a separate group for the data labels to avoid rotation - dataLabelsGroup = series.plotGroup( - 'dataLabelsGroup', - 'data-labels', - series.visible ? VISIBLE : HIDDEN, - options.zIndex || 6 - ); - - // Make the labels for each point - generalOptions = options; - each(points, function (point) { - - var enabled, - dataLabel = point.dataLabel, - labelConfig, - attr, - name, - rotation, - connector = point.connector, - isNew = true; - - // Determine if each data label is enabled - pointOptions = point.options && point.options.dataLabels; - enabled = pick(pointOptions && pointOptions.enabled, generalOptions.enabled); // #2282 - - - // If the point is outside the plot area, destroy it. #678, #820 - if (dataLabel && !enabled) { - point.dataLabel = dataLabel.destroy(); - - // Individual labels are disabled if the are explicitly disabled - // in the point options, or if they fall outside the plot area. - } else if (enabled) { - - // Create individual options structure that can be extended without - // affecting others - options = merge(generalOptions, pointOptions); - - rotation = options.rotation; - - // Get the string - labelConfig = point.getLabelConfig(); - str = options.format ? - format(options.format, labelConfig) : - options.formatter.call(labelConfig, options); - - // Determine the color - options.style.color = pick(options.color, options.style.color, series.color, 'black'); - - - // update existing label - if (dataLabel) { - - if (defined(str)) { - dataLabel - .attr({ - text: str - }); - isNew = false; - - } else { // #1437 - the label is shown conditionally - point.dataLabel = dataLabel = dataLabel.destroy(); - if (connector) { - point.connector = connector.destroy(); - } - } - - // create new label - } else if (defined(str)) { - attr = { - //align: align, - fill: options.backgroundColor, - stroke: options.borderColor, - 'stroke-width': options.borderWidth, - r: options.borderRadius || 0, - rotation: rotation, - padding: options.padding, - zIndex: 1 - }; - // Remove unused attributes (#947) - for (name in attr) { - if (attr[name] === UNDEFINED) { - delete attr[name]; - } - } - - dataLabel = point.dataLabel = series.chart.renderer[rotation ? 'text' : 'label']( // labels don't support rotation - str, - 0, - -999, - null, - null, - null, - options.useHTML - ) - .attr(attr) - .css(extend(options.style, cursor && { cursor: cursor })) - .add(dataLabelsGroup) - .shadow(options.shadow); - - } - - if (dataLabel) { - // Now the data label is created and placed at 0,0, so we need to align it - series.alignDataLabel(point, dataLabel, options, null, isNew); - } - } - }); - } -}; - -/** - * Align each individual data label - */ -Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) { - var chart = this.chart, - inverted = chart.inverted, - plotX = pick(point.plotX, -999), - plotY = pick(point.plotY, -999), - bBox = dataLabel.getBBox(), - visible = this.visible && (point.series.forceDL || chart.isInsidePlot(point.plotX, point.plotY, inverted)), - alignAttr; // the final position; - - if (visible) { - - // The alignment box is a singular point - alignTo = extend({ - x: inverted ? chart.plotWidth - plotY : plotX, - y: mathRound(inverted ? chart.plotHeight - plotX : plotY), - width: 0, - height: 0 - }, alignTo); - - // Add the text size for alignment calculation - extend(options, { - width: bBox.width, - height: bBox.height - }); - - // Allow a hook for changing alignment in the last moment, then do the alignment - if (options.rotation) { // Fancy box alignment isn't supported for rotated text - alignAttr = { - align: options.align, - x: alignTo.x + options.x + alignTo.width / 2, - y: alignTo.y + options.y + alignTo.height / 2 - }; - dataLabel[isNew ? 'attr' : 'animate'](alignAttr); - } else { - dataLabel.align(options, null, alignTo); - alignAttr = dataLabel.alignAttr; - - // Handle justify or crop - if (pick(options.overflow, 'justify') === 'justify') { - this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew); - - } else if (pick(options.crop, true)) { - // Now check that the data label is within the plot area - visible = chart.isInsidePlot(alignAttr.x, alignAttr.y) && chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height); - - } - } - } - - // Show or hide based on the final aligned position - if (!visible) { - dataLabel.attr({ y: -999 }); - dataLabel.placed = false; // don't animate back in - } - -}; - -/** - * If data labels fall partly outside the plot area, align them back in, in a way that - * doesn't hide the point. - */ -Series.prototype.justifyDataLabel = function (dataLabel, options, alignAttr, bBox, alignTo, isNew) { - var chart = this.chart, - align = options.align, - verticalAlign = options.verticalAlign, - off, - justified; - - // Off left - off = alignAttr.x; - if (off < 0) { - if (align === 'right') { - options.align = 'left'; - } else { - options.x = -off; - } - justified = true; - } - - // Off right - off = alignAttr.x + bBox.width; - if (off > chart.plotWidth) { - if (align === 'left') { - options.align = 'right'; - } else { - options.x = chart.plotWidth - off; - } - justified = true; - } - - // Off top - off = alignAttr.y; - if (off < 0) { - if (verticalAlign === 'bottom') { - options.verticalAlign = 'top'; - } else { - options.y = -off; - } - justified = true; - } - - // Off bottom - off = alignAttr.y + bBox.height; - if (off > chart.plotHeight) { - if (verticalAlign === 'top') { - options.verticalAlign = 'bottom'; - } else { - options.y = chart.plotHeight - off; - } - justified = true; - } - - if (justified) { - dataLabel.placed = !isNew; - dataLabel.align(options, null, alignTo); - } -}; - -/** - * Override the base drawDataLabels method by pie specific functionality - */ -if (seriesTypes.pie) { - seriesTypes.pie.prototype.drawDataLabels = function () { - var series = this, - data = series.data, - point, - chart = series.chart, - options = series.options.dataLabels, - connectorPadding = pick(options.connectorPadding, 10), - connectorWidth = pick(options.connectorWidth, 1), - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - connector, - connectorPath, - softConnector = pick(options.softConnector, true), - distanceOption = options.distance, - seriesCenter = series.center, - radius = seriesCenter[2] / 2, - centerY = seriesCenter[1], - outside = distanceOption > 0, - dataLabel, - dataLabelWidth, - labelPos, - labelHeight, - halves = [// divide the points into right and left halves for anti collision - [], // right - [] // left - ], - x, - y, - visibility, - rankArr, - i, - j, - overflow = [0, 0, 0, 0], // top, right, bottom, left - sort = function (a, b) { - return b.y - a.y; - }; - - // get out if not enabled - if (!series.visible || (!options.enabled && !series._hasPointLabels)) { - return; - } - - // run parent method - Series.prototype.drawDataLabels.apply(series); - - // arrange points for detection collision - each(data, function (point) { - if (point.dataLabel && point.visible) { // #407, #2510 - halves[point.half].push(point); - } - }); - - // assume equal label heights - i = 0; - while (!labelHeight && data[i]) { // #1569 - labelHeight = data[i] && data[i].dataLabel && (data[i].dataLabel.getBBox().height || 21); // 21 is for #968 - i++; - } - - /* Loop over the points in each half, starting from the top and bottom - * of the pie to detect overlapping labels. - */ - i = 2; - while (i--) { - - var slots = [], - slotsLength, - usedSlots = [], - points = halves[i], - pos, - length = points.length, - slotIndex; - - // Sort by angle - series.sortByAngle(points, i - 0.5); - - // Only do anti-collision when we are outside the pie and have connectors (#856) - if (distanceOption > 0) { - - // build the slots - for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) { - slots.push(pos); - - // visualize the slot - /* - var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0), - slotY = pos + chart.plotTop; - if (!isNaN(slotX)) { - chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1) - .attr({ - 'stroke-width': 1, - stroke: 'silver' - }) - .add(); - chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4) - .attr({ - fill: 'silver' - }).add(); - } - */ - } - slotsLength = slots.length; - - // if there are more values than available slots, remove lowest values - if (length > slotsLength) { - // create an array for sorting and ranking the points within each quarter - rankArr = [].concat(points); - rankArr.sort(sort); - j = length; - while (j--) { - rankArr[j].rank = j; - } - j = length; - while (j--) { - if (points[j].rank >= slotsLength) { - points.splice(j, 1); - } - } - length = points.length; - } - - // The label goes to the nearest open slot, but not closer to the edge than - // the label's index. - for (j = 0; j < length; j++) { - - point = points[j]; - labelPos = point.labelPos; - - var closest = 9999, - distance, - slotI; - - // find the closest slot index - for (slotI = 0; slotI < slotsLength; slotI++) { - distance = mathAbs(slots[slotI] - labelPos[1]); - if (distance < closest) { - closest = distance; - slotIndex = slotI; - } - } - - // if that slot index is closer to the edges of the slots, move it - // to the closest appropriate slot - if (slotIndex < j && slots[j] !== null) { // cluster at the top - slotIndex = j; - } else if (slotsLength < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom - slotIndex = slotsLength - length + j; - while (slots[slotIndex] === null) { // make sure it is not taken - slotIndex++; - } - } else { - // Slot is taken, find next free slot below. In the next run, the next slice will find the - // slot above these, because it is the closest one - while (slots[slotIndex] === null) { // make sure it is not taken - slotIndex++; - } - } - - usedSlots.push({ i: slotIndex, y: slots[slotIndex] }); - slots[slotIndex] = null; // mark as taken - } - // sort them in order to fill in from the top - usedSlots.sort(sort); - } - - // now the used slots are sorted, fill them up sequentially - for (j = 0; j < length; j++) { - - var slot, naturalY; - - point = points[j]; - labelPos = point.labelPos; - dataLabel = point.dataLabel; - visibility = point.visible === false ? HIDDEN : VISIBLE; - naturalY = labelPos[1]; - - if (distanceOption > 0) { - slot = usedSlots.pop(); - slotIndex = slot.i; - - // if the slot next to currrent slot is free, the y value is allowed - // to fall back to the natural position - y = slot.y; - if ((naturalY > y && slots[slotIndex + 1] !== null) || - (naturalY < y && slots[slotIndex - 1] !== null)) { - y = naturalY; - } - - } else { - y = naturalY; - } - - // get the x - use the natural x position for first and last slot, to prevent the top - // and botton slice connectors from touching each other on either side - x = options.justify ? - seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) : - series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i); - - - // Record the placement and visibility - dataLabel._attr = { - visibility: visibility, - align: labelPos[6] - }; - dataLabel._pos = { - x: x + options.x + - ({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0), - y: y + options.y - 10 // 10 is for the baseline (label vs text) - }; - dataLabel.connX = x; - dataLabel.connY = y; - - - // Detect overflowing data labels - if (this.options.size === null) { - dataLabelWidth = dataLabel.width; - // Overflow left - if (x - dataLabelWidth < connectorPadding) { - overflow[3] = mathMax(mathRound(dataLabelWidth - x + connectorPadding), overflow[3]); - - // Overflow right - } else if (x + dataLabelWidth > plotWidth - connectorPadding) { - overflow[1] = mathMax(mathRound(x + dataLabelWidth - plotWidth + connectorPadding), overflow[1]); - } - - // Overflow top - if (y - labelHeight / 2 < 0) { - overflow[0] = mathMax(mathRound(-y + labelHeight / 2), overflow[0]); - - // Overflow left - } else if (y + labelHeight / 2 > plotHeight) { - overflow[2] = mathMax(mathRound(y + labelHeight / 2 - plotHeight), overflow[2]); - } - } - } // for each point - } // for each half - - // Do not apply the final placement and draw the connectors until we have verified - // that labels are not spilling over. - if (arrayMax(overflow) === 0 || this.verifyDataLabelOverflow(overflow)) { - - // Place the labels in the final position - this.placeDataLabels(); - - // Draw the connectors - if (outside && connectorWidth) { - each(this.points, function (point) { - connector = point.connector; - labelPos = point.labelPos; - dataLabel = point.dataLabel; - - if (dataLabel && dataLabel._pos) { - visibility = dataLabel._attr.visibility; - x = dataLabel.connX; - y = dataLabel.connY; - connectorPath = softConnector ? [ - M, - x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label - 'C', - x, y, // first break, next to the label - 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5], - labelPos[2], labelPos[3], // second break - L, - labelPos[4], labelPos[5] // base - ] : [ - M, - x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label - L, - labelPos[2], labelPos[3], // second break - L, - labelPos[4], labelPos[5] // base - ]; - - if (connector) { - connector.animate({ d: connectorPath }); - connector.attr('visibility', visibility); - - } else { - point.connector = connector = series.chart.renderer.path(connectorPath).attr({ - 'stroke-width': connectorWidth, - stroke: options.connectorColor || point.color || '#606060', - visibility: visibility - }) - .add(series.group); - } - } else if (connector) { - point.connector = connector.destroy(); - } - }); - } - } - }; - /** - * Perform the final placement of the data labels after we have verified that they - * fall within the plot area. - */ - seriesTypes.pie.prototype.placeDataLabels = function () { - each(this.points, function (point) { - var dataLabel = point.dataLabel, - _pos; - - if (dataLabel) { - _pos = dataLabel._pos; - if (_pos) { - dataLabel.attr(dataLabel._attr); - dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos); - dataLabel.moved = true; - } else if (dataLabel) { - dataLabel.attr({ y: -999 }); - } - } - }); - }; - - seriesTypes.pie.prototype.alignDataLabel = noop; - - /** - * Verify whether the data labels are allowed to draw, or we should run more translation and data - * label positioning to keep them inside the plot area. Returns true when data labels are ready - * to draw. - */ - seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) { - - var center = this.center, - options = this.options, - centerOption = options.center, - minSize = options.minSize || 80, - newSize = minSize, - ret; - - // Handle horizontal size and center - if (centerOption[0] !== null) { // Fixed center - newSize = mathMax(center[2] - mathMax(overflow[1], overflow[3]), minSize); - - } else { // Auto center - newSize = mathMax( - center[2] - overflow[1] - overflow[3], // horizontal overflow - minSize - ); - center[0] += (overflow[3] - overflow[1]) / 2; // horizontal center - } - - // Handle vertical size and center - if (centerOption[1] !== null) { // Fixed center - newSize = mathMax(mathMin(newSize, center[2] - mathMax(overflow[0], overflow[2])), minSize); - - } else { // Auto center - newSize = mathMax( - mathMin( - newSize, - center[2] - overflow[0] - overflow[2] // vertical overflow - ), - minSize - ); - center[1] += (overflow[0] - overflow[2]) / 2; // vertical center - } - - // If the size must be decreased, we need to run translate and drawDataLabels again - if (newSize < center[2]) { - center[2] = newSize; - this.translate(center); - each(this.points, function (point) { - if (point.dataLabel) { - point.dataLabel._pos = null; // reset - } - }); - - if (this.drawDataLabels) { - this.drawDataLabels(); - } - // Else, return true to indicate that the pie and its labels is within the plot area - } else { - ret = true; - } - return ret; - }; -} - -if (seriesTypes.column) { - - /** - * Override the basic data label alignment by adjusting for the position of the column - */ - seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) { - var chart = this.chart, - inverted = chart.inverted, - dlBox = point.dlBox || point.shapeArgs, // data label box for alignment - below = point.below || (point.plotY > pick(this.translatedThreshold, chart.plotSizeY)), - inside = pick(options.inside, !!this.options.stacking); // draw it inside the box? - - // Align to the column itself, or the top of it - if (dlBox) { // Area range uses this method but not alignTo - alignTo = merge(dlBox); - if (inverted) { - alignTo = { - x: chart.plotWidth - alignTo.y - alignTo.height, - y: chart.plotHeight - alignTo.x - alignTo.width, - width: alignTo.height, - height: alignTo.width - }; - } - - // Compute the alignment box - if (!inside) { - if (inverted) { - alignTo.x += below ? 0 : alignTo.width; - alignTo.width = 0; - } else { - alignTo.y += below ? alignTo.height : 0; - alignTo.height = 0; - } - } - } - - // When alignment is undefined (typically columns and bars), display the individual - // point below or above the point depending on the threshold - options.align = pick( - options.align, - !inverted || inside ? 'center' : below ? 'right' : 'left' - ); - options.verticalAlign = pick( - options.verticalAlign, - inverted || inside ? 'middle' : below ? 'top' : 'bottom' - ); - - // Call the parent method - Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); - }; -} - - - - -// global variables -extend(Highcharts, { - - // Constructors - Axis: Axis, - Chart: Chart, - Color: Color, - Point: Point, - Tick: Tick, - Tooltip: Tooltip, - Renderer: Renderer, - Series: Series, - SVGElement: SVGElement, - SVGRenderer: SVGRenderer, - - // Various - arrayMin: arrayMin, - arrayMax: arrayMax, - charts: charts, - dateFormat: dateFormat, - format: format, - pathAnim: pathAnim, - getOptions: getOptions, - hasBidiBug: hasBidiBug, - isTouchDevice: isTouchDevice, - numberFormat: numberFormat, - seriesTypes: seriesTypes, - setOptions: setOptions, - addEvent: addEvent, - removeEvent: removeEvent, - createElement: createElement, - discardElement: discardElement, - css: css, - each: each, - extend: extend, - map: map, - merge: merge, - pick: pick, - splat: splat, - extendClass: extendClass, - pInt: pInt, - wrap: wrap, - svg: hasSVG, - canvas: useCanVG, - vml: !hasSVG && !useCanVG, - product: PRODUCT, - version: VERSION -}); - -}()); diff --git a/pykeg/web/static/highcharts/js/modules/annotations.js b/pykeg/web/static/highcharts/js/modules/annotations.js deleted file mode 100644 index 53958c146..000000000 --- a/pykeg/web/static/highcharts/js/modules/annotations.js +++ /dev/null @@ -1,7 +0,0 @@ -(function(i,C){function m(a){return typeof a==="number"}function n(a){return a!==D&&a!==null}var D,p,r,s=i.Chart,t=i.extend,z=i.each;r=["path","rect","circle"];p={top:0,left:0,center:0.5,middle:0.5,bottom:1,right:1};var u=C.inArray,A=i.merge,B=function(){this.init.apply(this,arguments)};B.prototype={init:function(a,d){var c=d.shape&&d.shape.type;this.chart=a;var b,f;f={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",fill:"transparent",strokeWidth:2}}};b={circle:{params:{x:0, -y:0}}};if(b[c])f.shape=A(f.shape,b[c]);this.options=A({},f,d)},render:function(a){var d=this.chart,c=this.chart.renderer,b=this.group,f=this.title,e=this.shape,h=this.options,i=h.title,l=h.shape;if(!b)b=this.group=c.g();if(!e&&l&&u(l.type,r)!==-1)e=this.shape=c[h.shape.type](l.params),e.add(b);if(!f&&i)f=this.title=c.label(i),f.add(b);b.add(d.annotations.group);this.linkObjects();a!==!1&&this.redraw()},redraw:function(){var a=this.options,d=this.chart,c=this.group,b=this.title,f=this.shape,e=this.linkedObject, -h=d.xAxis[a.xAxis],v=d.yAxis[a.yAxis],l=a.width,w=a.height,x=p[a.anchorY],y=p[a.anchorX],j,o,g,q;if(e)j=e instanceof i.Point?"point":e instanceof i.Series?"series":null,j==="point"?(a.xValue=e.x,a.yValue=e.y,o=e.series):j==="series"&&(o=e),c.visibility!==o.group.visibility&&c.attr({visibility:o.group.visibility});e=n(a.xValue)?h.toPixels(a.xValue+h.minPointOffset)-h.minPixelPadding:a.x;j=n(a.yValue)?v.toPixels(a.yValue):a.y;if(!isNaN(e)&&!isNaN(j)&&m(e)&&m(j)){b&&(b.attr(a.title),b.css(a.title.style)); -if(f){b=t({},a.shape.params);if(a.units==="values"){for(g in b)u(g,["width","x"])>-1?b[g]=h.translate(b[g]):u(g,["height","y"])>-1&&(b[g]=v.translate(b[g]));b.width&&(b.width-=h.toPixels(0)-h.left);b.x&&(b.x+=h.minPixelPadding);if(a.shape.type==="path"){g=b.d;o=e;for(var r=j,s=g.length,k=0;k<s;)typeof g[k]==="number"&&typeof g[k+1]==="number"?(g[k]=h.toPixels(g[k])-o,g[k+1]=v.toPixels(g[k+1])-r,k+=2):k+=1}}a.shape.type==="circle"&&(b.x+=b.r,b.y+=b.r);f.attr(b)}c.bBox=null;if(!m(l))q=c.getBBox(),l= -q.width;if(!m(w))q||(q=c.getBBox()),w=q.height;if(!m(y))y=p.center;if(!m(x))x=p.center;e-=l*y;j-=w*x;d.animation&&n(c.translateX)&&n(c.translateY)?c.animate({translateX:e,translateY:j}):c.translate(e,j)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);c>-1&&d.splice(c,1);z(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){t(this.options,a);this.linkObjects();this.render(d)}, -linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;if(n(b)){if(!n(d)||b!==c)this.linkedObject=a.get(b)}else this.linkedObject=null}};t(s.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;Object.prototype.toString.call(a)==="[object Array]"||(a=[a]);for(e=a.length;e--;)f=new B(b,a[e]),c.push(f),f.render(d)},redraw:function(){z(this.allItems,function(a){a.redraw()})}}});s.prototype.callbacks.push(function(a){var d= -a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;Object.prototype.toString.call(d)==="[object Array]"&&d.length>0&&a.annotations.add(a.options.annotations);i.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(Highcharts,HighchartsAdapter); diff --git a/pykeg/web/static/highcharts/js/modules/annotations.src.js b/pykeg/web/static/highcharts/js/modules/annotations.src.js deleted file mode 100644 index 40ce8df36..000000000 --- a/pykeg/web/static/highcharts/js/modules/annotations.src.js +++ /dev/null @@ -1,401 +0,0 @@ -(function (Highcharts, HighchartsAdapter) { - -var UNDEFINED, - ALIGN_FACTOR, - ALLOWED_SHAPES, - Chart = Highcharts.Chart, - extend = Highcharts.extend, - each = Highcharts.each; - -ALLOWED_SHAPES = ["path", "rect", "circle"]; - -ALIGN_FACTOR = { - top: 0, - left: 0, - center: 0.5, - middle: 0.5, - bottom: 1, - right: 1 -}; - - -// Highcharts helper methods -var inArray = HighchartsAdapter.inArray, - merge = Highcharts.merge; - -function defaultOptions(shapeType) { - var shapeOptions, - options; - - options = { - xAxis: 0, - yAxis: 0, - title: { - style: {}, - text: "", - x: 0, - y: 0 - }, - shape: { - params: { - stroke: "#000000", - fill: "transparent", - strokeWidth: 2 - } - } - }; - - shapeOptions = { - circle: { - params: { - x: 0, - y: 0 - } - } - }; - - if (shapeOptions[shapeType]) { - options.shape = merge(options.shape, shapeOptions[shapeType]); - } - - return options; -} - -function isArray(obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; -} - -function isNumber(n) { - return typeof n === 'number'; -} - -function defined(obj) { - return obj !== UNDEFINED && obj !== null; -} - -function translatePath(d, xAxis, yAxis, xOffset, yOffset) { - var len = d.length, - i = 0; - - while (i < len) { - if (typeof d[i] === 'number' && typeof d[i + 1] === 'number') { - d[i] = xAxis.toPixels(d[i]) - xOffset; - d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset; - i += 2; - } else { - i += 1; - } - } - - return d; -} - - -// Define annotation prototype -var Annotation = function () { - this.init.apply(this, arguments); -}; -Annotation.prototype = { - /* - * Initialize the annotation - */ - init: function (chart, options) { - var shapeType = options.shape && options.shape.type; - - this.chart = chart; - this.options = merge({}, defaultOptions(shapeType), options); - }, - - /* - * Render the annotation - */ - render: function (redraw) { - var annotation = this, - chart = this.chart, - renderer = annotation.chart.renderer, - group = annotation.group, - title = annotation.title, - shape = annotation.shape, - options = annotation.options, - titleOptions = options.title, - shapeOptions = options.shape; - - if (!group) { - group = annotation.group = renderer.g(); - } - - - if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { - shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); - shape.add(group); - } - - if (!title && titleOptions) { - title = annotation.title = renderer.label(titleOptions); - title.add(group); - } - - group.add(chart.annotations.group); - - // link annotations to point or series - annotation.linkObjects(); - - if (redraw !== false) { - annotation.redraw(); - } - }, - - /* - * Redraw the annotation title or shape after options update - */ - redraw: function () { - var options = this.options, - chart = this.chart, - group = this.group, - title = this.title, - shape = this.shape, - linkedTo = this.linkedObject, - xAxis = chart.xAxis[options.xAxis], - yAxis = chart.yAxis[options.yAxis], - width = options.width, - height = options.height, - anchorY = ALIGN_FACTOR[options.anchorY], - anchorX = ALIGN_FACTOR[options.anchorX], - resetBBox = false, - shapeParams, - linkType, - series, - param, - bbox, - x, - y; - - if (linkedTo) { - linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : - (linkedTo instanceof Highcharts.Series) ? 'series' : null; - - if (linkType === 'point') { - options.xValue = linkedTo.x; - options.yValue = linkedTo.y; - series = linkedTo.series; - } else if (linkType === 'series') { - series = linkedTo; - } - - if (group.visibility !== series.group.visibility) { - group.attr({ - visibility: series.group.visibility - }); - } - } - - - // Based on given options find annotation pixel position - x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x); - y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; - - if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { - return; - } - - - if (title) { - title.attr(options.title); - title.css(options.title.style); - resetBBox = true; - } - - if (shape) { - shapeParams = extend({}, options.shape.params); - - if (options.units === 'values') { - for (param in shapeParams) { - if (inArray(param, ['width', 'x']) > -1) { - shapeParams[param] = xAxis.translate(shapeParams[param]); - } else if (inArray(param, ['height', 'y']) > -1) { - shapeParams[param] = yAxis.translate(shapeParams[param]); - } - } - - if (shapeParams.width) { - shapeParams.width -= xAxis.toPixels(0) - xAxis.left; - } - - if (shapeParams.x) { - shapeParams.x += xAxis.minPixelPadding; - } - - if (options.shape.type === 'path') { - translatePath(shapeParams.d, xAxis, yAxis, x, y); - } - } - - // move the center of the circle to shape x/y - if (options.shape.type === 'circle') { - shapeParams.x += shapeParams.r; - shapeParams.y += shapeParams.r; - } - - resetBBox = true; - shape.attr(shapeParams); - } - - group.bBox = null; - - // If annotation width or height is not defined in options use bounding box size - if (!isNumber(width)) { - bbox = group.getBBox(); - width = bbox.width; - } - - if (!isNumber(height)) { - // get bbox only if it wasn't set before - if (!bbox) { - bbox = group.getBBox(); - } - - height = bbox.height; - } - - // Calculate anchor point - if (!isNumber(anchorX)) { - anchorX = ALIGN_FACTOR.center; - } - - if (!isNumber(anchorY)) { - anchorY = ALIGN_FACTOR.center; - } - - // Translate group according to its dimension and anchor point - x = x - width * anchorX; - y = y - height * anchorY; - - if (chart.animation && defined(group.translateX) && defined(group.translateY)) { - group.animate({ - translateX: x, - translateY: y - }); - } else { - group.translate(x, y); - } - }, - - /* - * Destroy the annotation - */ - destroy: function () { - var annotation = this, - chart = this.chart, - allItems = chart.annotations.allItems, - index = allItems.indexOf(annotation); - - if (index > -1) { - allItems.splice(index, 1); - } - - each(['title', 'shape', 'group'], function (element) { - if (annotation[element]) { - annotation[element].destroy(); - annotation[element] = null; - } - }); - - annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; - }, - - /* - * Update the annotation with a given options - */ - update: function (options, redraw) { - extend(this.options, options); - - // update link to point or series - this.linkObjects(); - - this.render(redraw); - }, - - linkObjects: function () { - var annotation = this, - chart = annotation.chart, - linkedTo = annotation.linkedObject, - linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), - options = annotation.options, - id = options.linkedTo; - - if (!defined(id)) { - annotation.linkedObject = null; - } else if (!defined(linkedTo) || id !== linkedId) { - annotation.linkedObject = chart.get(id); - } - } -}; - - -// Add annotations methods to chart prototype -extend(Chart.prototype, { - annotations: { - /* - * Unified method for adding annotations to the chart - */ - add: function (options, redraw) { - var annotations = this.allItems, - chart = this.chart, - item, - len; - - if (!isArray(options)) { - options = [options]; - } - - len = options.length; - - while (len--) { - item = new Annotation(chart, options[len]); - annotations.push(item); - item.render(redraw); - } - }, - - /** - * Redraw all annotations, method used in chart events - */ - redraw: function () { - each(this.allItems, function (annotation) { - annotation.redraw(); - }); - } - } -}); - - -// Initialize on chart load -Chart.prototype.callbacks.push(function (chart) { - var options = chart.options.annotations, - group; - - group = chart.renderer.g("annotations"); - group.attr({ - zIndex: 7 - }); - group.add(); - - // initialize empty array for annotations - chart.annotations.allItems = []; - - // link chart object to annotations - chart.annotations.chart = chart; - - // link annotations group element to the chart - chart.annotations.group = group; - - if (isArray(options) && options.length > 0) { - chart.annotations.add(chart.options.annotations); - } - - // update annotations after chart redraw - Highcharts.addEvent(chart, 'redraw', function () { - chart.annotations.redraw(); - }); -}); -}(Highcharts, HighchartsAdapter)); diff --git a/pykeg/web/static/highcharts/js/modules/canvas-tools.js b/pykeg/web/static/highcharts/js/modules/canvas-tools.js deleted file mode 100644 index 5cfd50fad..000000000 --- a/pykeg/web/static/highcharts/js/modules/canvas-tools.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - A class to parse color values - @author Stoyan Stefanov <sstoo@gmail.com> - @link http://www.phpied.com/rgb-color-parser-in-javascript/ - Use it if you like it - - canvg.js - Javascript SVG parser and renderer on Canvas - MIT Licensed - Gabe Lerner (gabelerner@gmail.com) - http://code.google.com/p/canvg/ - - Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/ - - Highcharts JS v3.0.9 (2014-01-15) - CanVGRenderer Extension module - - (c) 2011-2012 Torstein Honsi, Erik Olsson - - License: www.highcharts.com/license -*/ -function RGBColor(m){this.ok=!1;m.charAt(0)=="#"&&(m=m.substr(1,6));var m=m.replace(/ /g,""),m=m.toLowerCase(),a={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b", -darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff", -gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa", -lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080", -oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd", -slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"},c;for(c in a)m==c&&(m=a[c]);var d=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(b){return[parseInt(b[1]),parseInt(b[2]),parseInt(b[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/, -example:["#00ff00","336699"],process:function(b){return[parseInt(b[1],16),parseInt(b[2],16),parseInt(b[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(b){return[parseInt(b[1]+b[1],16),parseInt(b[2]+b[2],16),parseInt(b[3]+b[3],16)]}}];for(c=0;c<d.length;c++){var b=d[c].process,k=d[c].re.exec(m);if(k)channels=b(k),this.r=channels[0],this.g=channels[1],this.b=channels[2],this.ok=!0}this.r=this.r<0||isNaN(this.r)?0:this.r>255?255:this.r;this.g=this.g<0||isNaN(this.g)?0: -this.g>255?255:this.g;this.b=this.b<0||isNaN(this.b)?0:this.b>255?255:this.b;this.toRGB=function(){return"rgb("+this.r+", "+this.g+", "+this.b+")"};this.toHex=function(){var b=this.r.toString(16),a=this.g.toString(16),d=this.b.toString(16);b.length==1&&(b="0"+b);a.length==1&&(a="0"+a);d.length==1&&(d="0"+d);return"#"+b+a+d};this.getHelpXML=function(){for(var b=[],k=0;k<d.length;k++)for(var c=d[k].example,j=0;j<c.length;j++)b[b.length]=c[j];for(var h in a)b[b.length]=h;c=document.createElement("ul"); -c.setAttribute("id","rgbcolor-examples");for(k=0;k<b.length;k++)try{var l=document.createElement("li"),o=new RGBColor(b[k]),n=document.createElement("div");n.style.cssText="margin: 3px; border: 1px solid black; background:"+o.toHex()+"; color:"+o.toHex();n.appendChild(document.createTextNode("test"));var q=document.createTextNode(" "+b[k]+" -> "+o.toRGB()+" -> "+o.toHex());l.appendChild(n);l.appendChild(q);c.appendChild(l)}catch(p){}return c}} -if(!window.console)window.console={},window.console.log=function(){},window.console.dir=function(){};if(!Array.prototype.indexOf)Array.prototype.indexOf=function(m){for(var a=0;a<this.length;a++)if(this[a]==m)return a;return-1}; -(function(){function m(){var a={FRAMERATE:30,MAX_VIRTUAL_PIXELS:3E4};a.init=function(c){a.Definitions={};a.Styles={};a.Animations=[];a.Images=[];a.ctx=c;a.ViewPort=new function(){this.viewPorts=[];this.Clear=function(){this.viewPorts=[]};this.SetCurrent=function(a,b){this.viewPorts.push({width:a,height:b})};this.RemoveCurrent=function(){this.viewPorts.pop()};this.Current=function(){return this.viewPorts[this.viewPorts.length-1]};this.width=function(){return this.Current().width};this.height=function(){return this.Current().height}; -this.ComputeSize=function(a){return a!=null&&typeof a=="number"?a:a=="x"?this.width():a=="y"?this.height():Math.sqrt(Math.pow(this.width(),2)+Math.pow(this.height(),2))/Math.sqrt(2)}}};a.init();a.ImagesLoaded=function(){for(var c=0;c<a.Images.length;c++)if(!a.Images[c].loaded)return!1;return!0};a.trim=function(a){return a.replace(/^\s+|\s+$/g,"")};a.compressSpaces=function(a){return a.replace(/[\s\r\t\n]+/gm," ")};a.ajax=function(a){var d;return(d=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"))? -(d.open("GET",a,!1),d.send(null),d.responseText):null};a.parseXml=function(a){if(window.DOMParser)return(new DOMParser).parseFromString(a,"text/xml");else{var a=a.replace(/<!DOCTYPE svg[^>]*>/,""),d=new ActiveXObject("Microsoft.XMLDOM");d.async="false";d.loadXML(a);return d}};a.Property=function(c,d){this.name=c;this.value=d;this.hasValue=function(){return this.value!=null&&this.value!==""};this.numValue=function(){if(!this.hasValue())return 0;var b=parseFloat(this.value);(this.value+"").match(/%$/)&& -(b/=100);return b};this.valueOrDefault=function(b){return this.hasValue()?this.value:b};this.numValueOrDefault=function(b){return this.hasValue()?this.numValue():b};var b=this;this.Color={addOpacity:function(d){var c=b.value;if(d!=null&&d!=""){var f=new RGBColor(b.value);f.ok&&(c="rgba("+f.r+", "+f.g+", "+f.b+", "+d+")")}return new a.Property(b.name,c)}};this.Definition={getDefinition:function(){var d=b.value.replace(/^(url\()?#([^\)]+)\)?$/,"$2");return a.Definitions[d]},isUrl:function(){return b.value.indexOf("url(")== -0},getFillStyle:function(b){var d=this.getDefinition();return d!=null&&d.createGradient?d.createGradient(a.ctx,b):d!=null&&d.createPattern?d.createPattern(a.ctx,b):null}};this.Length={DPI:function(){return 96},EM:function(b){var d=12,c=new a.Property("fontSize",a.Font.Parse(a.ctx.font).fontSize);c.hasValue()&&(d=c.Length.toPixels(b));return d},toPixels:function(d){if(!b.hasValue())return 0;var c=b.value+"";return c.match(/em$/)?b.numValue()*this.EM(d):c.match(/ex$/)?b.numValue()*this.EM(d)/2:c.match(/px$/)? -b.numValue():c.match(/pt$/)?b.numValue()*1.25:c.match(/pc$/)?b.numValue()*15:c.match(/cm$/)?b.numValue()*this.DPI(d)/2.54:c.match(/mm$/)?b.numValue()*this.DPI(d)/25.4:c.match(/in$/)?b.numValue()*this.DPI(d):c.match(/%$/)?b.numValue()*a.ViewPort.ComputeSize(d):b.numValue()}};this.Time={toMilliseconds:function(){if(!b.hasValue())return 0;var a=b.value+"";if(a.match(/s$/))return b.numValue()*1E3;a.match(/ms$/);return b.numValue()}};this.Angle={toRadians:function(){if(!b.hasValue())return 0;var a=b.value+ -"";return a.match(/deg$/)?b.numValue()*(Math.PI/180):a.match(/grad$/)?b.numValue()*(Math.PI/200):a.match(/rad$/)?b.numValue():b.numValue()*(Math.PI/180)}}};a.Font=new function(){this.Styles=["normal","italic","oblique","inherit"];this.Variants=["normal","small-caps","inherit"];this.Weights="normal,bold,bolder,lighter,100,200,300,400,500,600,700,800,900,inherit".split(",");this.CreateFont=function(d,b,c,e,f,g){g=g!=null?this.Parse(g):this.CreateFont("","","","","",a.ctx.font);return{fontFamily:f|| -g.fontFamily,fontSize:e||g.fontSize,fontStyle:d||g.fontStyle,fontWeight:c||g.fontWeight,fontVariant:b||g.fontVariant,toString:function(){return[this.fontStyle,this.fontVariant,this.fontWeight,this.fontSize,this.fontFamily].join(" ")}}};var c=this;this.Parse=function(d){for(var b={},d=a.trim(a.compressSpaces(d||"")).split(" "),k=!1,e=!1,f=!1,g=!1,j="",h=0;h<d.length;h++)if(!e&&c.Styles.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontStyle=d[h];e=!0}else if(!g&&c.Variants.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontVariant= -d[h];e=g=!0}else if(!f&&c.Weights.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontWeight=d[h];e=g=f=!0}else if(k)d[h]!="inherit"&&(j+=d[h]);else{if(d[h]!="inherit")b.fontSize=d[h].split("/")[0];e=g=f=k=!0}if(j!="")b.fontFamily=j;return b}};a.ToNumberArray=function(c){for(var c=a.trim(a.compressSpaces((c||"").replace(/,/g," "))).split(" "),d=0;d<c.length;d++)c[d]=parseFloat(c[d]);return c};a.Point=function(a,d){this.x=a;this.y=d;this.angleTo=function(b){return Math.atan2(b.y-this.y,b.x-this.x)};this.applyTransform= -function(b){var a=this.x*b[1]+this.y*b[3]+b[5];this.x=this.x*b[0]+this.y*b[2]+b[4];this.y=a}};a.CreatePoint=function(c){c=a.ToNumberArray(c);return new a.Point(c[0],c[1])};a.CreatePath=function(c){for(var c=a.ToNumberArray(c),d=[],b=0;b<c.length;b+=2)d.push(new a.Point(c[b],c[b+1]));return d};a.BoundingBox=function(a,d,b,k){this.y2=this.x2=this.y1=this.x1=Number.NaN;this.x=function(){return this.x1};this.y=function(){return this.y1};this.width=function(){return this.x2-this.x1};this.height=function(){return this.y2- -this.y1};this.addPoint=function(b,a){if(b!=null){if(isNaN(this.x1)||isNaN(this.x2))this.x2=this.x1=b;if(b<this.x1)this.x1=b;if(b>this.x2)this.x2=b}if(a!=null){if(isNaN(this.y1)||isNaN(this.y2))this.y2=this.y1=a;if(a<this.y1)this.y1=a;if(a>this.y2)this.y2=a}};this.addX=function(b){this.addPoint(b,null)};this.addY=function(b){this.addPoint(null,b)};this.addBoundingBox=function(b){this.addPoint(b.x1,b.y1);this.addPoint(b.x2,b.y2)};this.addQuadraticCurve=function(b,a,d,c,k,l){d=b+2/3*(d-b);c=a+2/3*(c- -a);this.addBezierCurve(b,a,d,d+1/3*(k-b),c,c+1/3*(l-a),k,l)};this.addBezierCurve=function(b,a,d,c,k,l,o,n){var q=[b,a],p=[d,c],t=[k,l],m=[o,n];this.addPoint(q[0],q[1]);this.addPoint(m[0],m[1]);for(i=0;i<=1;i++)b=function(b){return Math.pow(1-b,3)*q[i]+3*Math.pow(1-b,2)*b*p[i]+3*(1-b)*Math.pow(b,2)*t[i]+Math.pow(b,3)*m[i]},a=6*q[i]-12*p[i]+6*t[i],d=-3*q[i]+9*p[i]-9*t[i]+3*m[i],c=3*p[i]-3*q[i],d==0?a!=0&&(a=-c/a,0<a&&a<1&&(i==0&&this.addX(b(a)),i==1&&this.addY(b(a)))):(c=Math.pow(a,2)-4*c*d,c<0||(k= -(-a+Math.sqrt(c))/(2*d),0<k&&k<1&&(i==0&&this.addX(b(k)),i==1&&this.addY(b(k))),a=(-a-Math.sqrt(c))/(2*d),0<a&&a<1&&(i==0&&this.addX(b(a)),i==1&&this.addY(b(a)))))};this.isPointInBox=function(b,a){return this.x1<=b&&b<=this.x2&&this.y1<=a&&a<=this.y2};this.addPoint(a,d);this.addPoint(b,k)};a.Transform=function(c){var d=this;this.Type={};this.Type.translate=function(b){this.p=a.CreatePoint(b);this.apply=function(b){b.translate(this.p.x||0,this.p.y||0)};this.applyToPoint=function(b){b.applyTransform([1, -0,0,1,this.p.x||0,this.p.y||0])}};this.Type.rotate=function(b){b=a.ToNumberArray(b);this.angle=new a.Property("angle",b[0]);this.cx=b[1]||0;this.cy=b[2]||0;this.apply=function(b){b.translate(this.cx,this.cy);b.rotate(this.angle.Angle.toRadians());b.translate(-this.cx,-this.cy)};this.applyToPoint=function(b){var a=this.angle.Angle.toRadians();b.applyTransform([1,0,0,1,this.p.x||0,this.p.y||0]);b.applyTransform([Math.cos(a),Math.sin(a),-Math.sin(a),Math.cos(a),0,0]);b.applyTransform([1,0,0,1,-this.p.x|| -0,-this.p.y||0])}};this.Type.scale=function(b){this.p=a.CreatePoint(b);this.apply=function(b){b.scale(this.p.x||1,this.p.y||this.p.x||1)};this.applyToPoint=function(b){b.applyTransform([this.p.x||0,0,0,this.p.y||0,0,0])}};this.Type.matrix=function(b){this.m=a.ToNumberArray(b);this.apply=function(b){b.transform(this.m[0],this.m[1],this.m[2],this.m[3],this.m[4],this.m[5])};this.applyToPoint=function(b){b.applyTransform(this.m)}};this.Type.SkewBase=function(b){this.base=d.Type.matrix;this.base(b);this.angle= -new a.Property("angle",b)};this.Type.SkewBase.prototype=new this.Type.matrix;this.Type.skewX=function(b){this.base=d.Type.SkewBase;this.base(b);this.m=[1,0,Math.tan(this.angle.Angle.toRadians()),1,0,0]};this.Type.skewX.prototype=new this.Type.SkewBase;this.Type.skewY=function(b){this.base=d.Type.SkewBase;this.base(b);this.m=[1,Math.tan(this.angle.Angle.toRadians()),0,1,0,0]};this.Type.skewY.prototype=new this.Type.SkewBase;this.transforms=[];this.apply=function(b){for(var a=0;a<this.transforms.length;a++)this.transforms[a].apply(b)}; -this.applyToPoint=function(b){for(var a=0;a<this.transforms.length;a++)this.transforms[a].applyToPoint(b)};for(var c=a.trim(a.compressSpaces(c)).split(/\s(?=[a-z])/),b=0;b<c.length;b++){var k=c[b].split("(")[0],e=c[b].split("(")[1].replace(")","");this.transforms.push(new this.Type[k](e))}};a.AspectRatio=function(c,d,b,k,e,f,g,j,h,l){var d=a.compressSpaces(d),d=d.replace(/^defer\s/,""),o=d.split(" ")[0]||"xMidYMid",d=d.split(" ")[1]||"meet",n=b/k,q=e/f,p=Math.min(n,q),m=Math.max(n,q);d=="meet"&&(k*= -p,f*=p);d=="slice"&&(k*=m,f*=m);h=new a.Property("refX",h);l=new a.Property("refY",l);h.hasValue()&&l.hasValue()?c.translate(-p*h.Length.toPixels("x"),-p*l.Length.toPixels("y")):(o.match(/^xMid/)&&(d=="meet"&&p==q||d=="slice"&&m==q)&&c.translate(b/2-k/2,0),o.match(/YMid$/)&&(d=="meet"&&p==n||d=="slice"&&m==n)&&c.translate(0,e/2-f/2),o.match(/^xMax/)&&(d=="meet"&&p==q||d=="slice"&&m==q)&&c.translate(b-k,0),o.match(/YMax$/)&&(d=="meet"&&p==n||d=="slice"&&m==n)&&c.translate(0,e-f));o=="none"?c.scale(n, -q):d=="meet"?c.scale(p,p):d=="slice"&&c.scale(m,m);c.translate(g==null?0:-g,j==null?0:-j)};a.Element={};a.Element.ElementBase=function(c){this.attributes={};this.styles={};this.children=[];this.attribute=function(b,d){var c=this.attributes[b];if(c!=null)return c;c=new a.Property(b,"");d==!0&&(this.attributes[b]=c);return c};this.style=function(b,d){var c=this.styles[b];if(c!=null)return c;c=this.attribute(b);if(c!=null&&c.hasValue())return c;c=this.parent;if(c!=null&&(c=c.style(b),c!=null&&c.hasValue()))return c; -c=new a.Property(b,"");d==!0&&(this.styles[b]=c);return c};this.render=function(b){if(this.style("display").value!="none"&&this.attribute("visibility").value!="hidden"){b.save();this.setContext(b);if(this.attribute("mask").hasValue()){var a=this.attribute("mask").Definition.getDefinition();a!=null&&a.apply(b,this)}else this.style("filter").hasValue()?(a=this.style("filter").Definition.getDefinition(),a!=null&&a.apply(b,this)):this.renderChildren(b);this.clearContext(b);b.restore()}};this.setContext= -function(){};this.clearContext=function(){};this.renderChildren=function(b){for(var a=0;a<this.children.length;a++)this.children[a].render(b)};this.addChild=function(b,d){var c=b;d&&(c=a.CreateElement(b));c.parent=this;this.children.push(c)};if(c!=null&&c.nodeType==1){for(var d=0;d<c.childNodes.length;d++){var b=c.childNodes[d];b.nodeType==1&&this.addChild(b,!0)}for(d=0;d<c.attributes.length;d++)b=c.attributes[d],this.attributes[b.nodeName]=new a.Property(b.nodeName,b.nodeValue);b=a.Styles[c.nodeName]; -if(b!=null)for(var k in b)this.styles[k]=b[k];if(this.attribute("class").hasValue())for(var d=a.compressSpaces(this.attribute("class").value).split(" "),e=0;e<d.length;e++){b=a.Styles["."+d[e]];if(b!=null)for(k in b)this.styles[k]=b[k];b=a.Styles[c.nodeName+"."+d[e]];if(b!=null)for(k in b)this.styles[k]=b[k]}if(this.attribute("style").hasValue()){b=this.attribute("style").value.split(";");for(d=0;d<b.length;d++)a.trim(b[d])!=""&&(c=b[d].split(":"),k=a.trim(c[0]),c=a.trim(c[1]),this.styles[k]=new a.Property(k, -c))}this.attribute("id").hasValue()&&a.Definitions[this.attribute("id").value]==null&&(a.Definitions[this.attribute("id").value]=this)}};a.Element.RenderedElementBase=function(c){this.base=a.Element.ElementBase;this.base(c);this.setContext=function(d){if(this.style("fill").Definition.isUrl()){var b=this.style("fill").Definition.getFillStyle(this);if(b!=null)d.fillStyle=b}else if(this.style("fill").hasValue())b=this.style("fill"),this.style("fill-opacity").hasValue()&&(b=b.Color.addOpacity(this.style("fill-opacity").value)), -d.fillStyle=b.value=="none"?"rgba(0,0,0,0)":b.value;if(this.style("stroke").Definition.isUrl()){if(b=this.style("stroke").Definition.getFillStyle(this),b!=null)d.strokeStyle=b}else if(this.style("stroke").hasValue())b=this.style("stroke"),this.style("stroke-opacity").hasValue()&&(b=b.Color.addOpacity(this.style("stroke-opacity").value)),d.strokeStyle=b.value=="none"?"rgba(0,0,0,0)":b.value;if(this.style("stroke-width").hasValue())d.lineWidth=this.style("stroke-width").Length.toPixels();if(this.style("stroke-linecap").hasValue())d.lineCap= -this.style("stroke-linecap").value;if(this.style("stroke-linejoin").hasValue())d.lineJoin=this.style("stroke-linejoin").value;if(this.style("stroke-miterlimit").hasValue())d.miterLimit=this.style("stroke-miterlimit").value;if(typeof d.font!="undefined")d.font=a.Font.CreateFont(this.style("font-style").value,this.style("font-variant").value,this.style("font-weight").value,this.style("font-size").hasValue()?this.style("font-size").Length.toPixels()+"px":"",this.style("font-family").value).toString(); -this.attribute("transform").hasValue()&&(new a.Transform(this.attribute("transform").value)).apply(d);this.attribute("clip-path").hasValue()&&(b=this.attribute("clip-path").Definition.getDefinition(),b!=null&&b.apply(d));if(this.style("opacity").hasValue())d.globalAlpha=this.style("opacity").numValue()}};a.Element.RenderedElementBase.prototype=new a.Element.ElementBase;a.Element.PathElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.path=function(d){d!=null&&d.beginPath(); -return new a.BoundingBox};this.renderChildren=function(d){this.path(d);a.Mouse.checkPath(this,d);d.fillStyle!=""&&d.fill();d.strokeStyle!=""&&d.stroke();var b=this.getMarkers();if(b!=null){if(this.style("marker-start").Definition.isUrl()){var c=this.style("marker-start").Definition.getDefinition();c.render(d,b[0][0],b[0][1])}if(this.style("marker-mid").Definition.isUrl())for(var c=this.style("marker-mid").Definition.getDefinition(),e=1;e<b.length-1;e++)c.render(d,b[e][0],b[e][1]);this.style("marker-end").Definition.isUrl()&& -(c=this.style("marker-end").Definition.getDefinition(),c.render(d,b[b.length-1][0],b[b.length-1][1]))}};this.getBoundingBox=function(){return this.path()};this.getMarkers=function(){return null}};a.Element.PathElementBase.prototype=new a.Element.RenderedElementBase;a.Element.svg=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseClearContext=this.clearContext;this.clearContext=function(d){this.baseClearContext(d);a.ViewPort.RemoveCurrent()};this.baseSetContext=this.setContext; -this.setContext=function(d){d.strokeStyle="rgba(0,0,0,0)";d.lineCap="butt";d.lineJoin="miter";d.miterLimit=4;this.baseSetContext(d);this.attribute("x").hasValue()&&this.attribute("y").hasValue()&&d.translate(this.attribute("x").Length.toPixels("x"),this.attribute("y").Length.toPixels("y"));var b=a.ViewPort.width(),c=a.ViewPort.height();if(typeof this.root=="undefined"&&this.attribute("width").hasValue()&&this.attribute("height").hasValue()){var b=this.attribute("width").Length.toPixels("x"),c=this.attribute("height").Length.toPixels("y"), -e=0,f=0;this.attribute("refX").hasValue()&&this.attribute("refY").hasValue()&&(e=-this.attribute("refX").Length.toPixels("x"),f=-this.attribute("refY").Length.toPixels("y"));d.beginPath();d.moveTo(e,f);d.lineTo(b,f);d.lineTo(b,c);d.lineTo(e,c);d.closePath();d.clip()}a.ViewPort.SetCurrent(b,c);if(this.attribute("viewBox").hasValue()){var e=a.ToNumberArray(this.attribute("viewBox").value),f=e[0],g=e[1],b=e[2],c=e[3];a.AspectRatio(d,this.attribute("preserveAspectRatio").value,a.ViewPort.width(),b,a.ViewPort.height(), -c,f,g,this.attribute("refX").value,this.attribute("refY").value);a.ViewPort.RemoveCurrent();a.ViewPort.SetCurrent(e[2],e[3])}}};a.Element.svg.prototype=new a.Element.RenderedElementBase;a.Element.rect=function(c){this.base=a.Element.PathElementBase;this.base(c);this.path=function(d){var b=this.attribute("x").Length.toPixels("x"),c=this.attribute("y").Length.toPixels("y"),e=this.attribute("width").Length.toPixels("x"),f=this.attribute("height").Length.toPixels("y"),g=this.attribute("rx").Length.toPixels("x"), -j=this.attribute("ry").Length.toPixels("y");this.attribute("rx").hasValue()&&!this.attribute("ry").hasValue()&&(j=g);this.attribute("ry").hasValue()&&!this.attribute("rx").hasValue()&&(g=j);d!=null&&(d.beginPath(),d.moveTo(b+g,c),d.lineTo(b+e-g,c),d.quadraticCurveTo(b+e,c,b+e,c+j),d.lineTo(b+e,c+f-j),d.quadraticCurveTo(b+e,c+f,b+e-g,c+f),d.lineTo(b+g,c+f),d.quadraticCurveTo(b,c+f,b,c+f-j),d.lineTo(b,c+j),d.quadraticCurveTo(b,c,b+g,c),d.closePath());return new a.BoundingBox(b,c,b+e,c+f)}};a.Element.rect.prototype= -new a.Element.PathElementBase;a.Element.circle=function(c){this.base=a.Element.PathElementBase;this.base(c);this.path=function(d){var b=this.attribute("cx").Length.toPixels("x"),c=this.attribute("cy").Length.toPixels("y"),e=this.attribute("r").Length.toPixels();d!=null&&(d.beginPath(),d.arc(b,c,e,0,Math.PI*2,!0),d.closePath());return new a.BoundingBox(b-e,c-e,b+e,c+e)}};a.Element.circle.prototype=new a.Element.PathElementBase;a.Element.ellipse=function(c){this.base=a.Element.PathElementBase;this.base(c); -this.path=function(d){var b=4*((Math.sqrt(2)-1)/3),c=this.attribute("rx").Length.toPixels("x"),e=this.attribute("ry").Length.toPixels("y"),f=this.attribute("cx").Length.toPixels("x"),g=this.attribute("cy").Length.toPixels("y");d!=null&&(d.beginPath(),d.moveTo(f,g-e),d.bezierCurveTo(f+b*c,g-e,f+c,g-b*e,f+c,g),d.bezierCurveTo(f+c,g+b*e,f+b*c,g+e,f,g+e),d.bezierCurveTo(f-b*c,g+e,f-c,g+b*e,f-c,g),d.bezierCurveTo(f-c,g-b*e,f-b*c,g-e,f,g-e),d.closePath());return new a.BoundingBox(f-c,g-e,f+c,g+e)}};a.Element.ellipse.prototype= -new a.Element.PathElementBase;a.Element.line=function(c){this.base=a.Element.PathElementBase;this.base(c);this.getPoints=function(){return[new a.Point(this.attribute("x1").Length.toPixels("x"),this.attribute("y1").Length.toPixels("y")),new a.Point(this.attribute("x2").Length.toPixels("x"),this.attribute("y2").Length.toPixels("y"))]};this.path=function(d){var b=this.getPoints();d!=null&&(d.beginPath(),d.moveTo(b[0].x,b[0].y),d.lineTo(b[1].x,b[1].y));return new a.BoundingBox(b[0].x,b[0].y,b[1].x,b[1].y)}; -this.getMarkers=function(){var a=this.getPoints(),b=a[0].angleTo(a[1]);return[[a[0],b],[a[1],b]]}};a.Element.line.prototype=new a.Element.PathElementBase;a.Element.polyline=function(c){this.base=a.Element.PathElementBase;this.base(c);this.points=a.CreatePath(this.attribute("points").value);this.path=function(d){var b=new a.BoundingBox(this.points[0].x,this.points[0].y);d!=null&&(d.beginPath(),d.moveTo(this.points[0].x,this.points[0].y));for(var c=1;c<this.points.length;c++)b.addPoint(this.points[c].x, -this.points[c].y),d!=null&&d.lineTo(this.points[c].x,this.points[c].y);return b};this.getMarkers=function(){for(var a=[],b=0;b<this.points.length-1;b++)a.push([this.points[b],this.points[b].angleTo(this.points[b+1])]);a.push([this.points[this.points.length-1],a[a.length-1][1]]);return a}};a.Element.polyline.prototype=new a.Element.PathElementBase;a.Element.polygon=function(c){this.base=a.Element.polyline;this.base(c);this.basePath=this.path;this.path=function(a){var b=this.basePath(a);a!=null&&(a.lineTo(this.points[0].x, -this.points[0].y),a.closePath());return b}};a.Element.polygon.prototype=new a.Element.polyline;a.Element.path=function(c){this.base=a.Element.PathElementBase;this.base(c);c=this.attribute("d").value;c=c.replace(/,/gm," ");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,"$1 $2");c=c.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([0-9])([+\-])/gm, -"$1 $2");c=c.replace(/(\.[0-9]*)(\.)/gm,"$1 $2");c=c.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,"$1 $3 $4 ");c=a.compressSpaces(c);c=a.trim(c);this.PathParser=new function(d){this.tokens=d.split(" ");this.reset=function(){this.i=-1;this.previousCommand=this.command="";this.start=new a.Point(0,0);this.control=new a.Point(0,0);this.current=new a.Point(0,0);this.points=[];this.angles=[]};this.isEnd=function(){return this.i>=this.tokens.length-1};this.isCommandOrEnd=function(){return this.isEnd()? -!0:this.tokens[this.i+1].match(/^[A-Za-z]$/)!=null};this.isRelativeCommand=function(){return this.command==this.command.toLowerCase()};this.getToken=function(){this.i+=1;return this.tokens[this.i]};this.getScalar=function(){return parseFloat(this.getToken())};this.nextCommand=function(){this.previousCommand=this.command;this.command=this.getToken()};this.getPoint=function(){return this.makeAbsolute(new a.Point(this.getScalar(),this.getScalar()))};this.getAsControlPoint=function(){var b=this.getPoint(); -return this.control=b};this.getAsCurrentPoint=function(){var b=this.getPoint();return this.current=b};this.getReflectedControlPoint=function(){return this.previousCommand.toLowerCase()!="c"&&this.previousCommand.toLowerCase()!="s"?this.current:new a.Point(2*this.current.x-this.control.x,2*this.current.y-this.control.y)};this.makeAbsolute=function(b){if(this.isRelativeCommand())b.x=this.current.x+b.x,b.y=this.current.y+b.y;return b};this.addMarker=function(b,a,d){d!=null&&this.angles.length>0&&this.angles[this.angles.length- -1]==null&&(this.angles[this.angles.length-1]=this.points[this.points.length-1].angleTo(d));this.addMarkerAngle(b,a==null?null:a.angleTo(b))};this.addMarkerAngle=function(b,a){this.points.push(b);this.angles.push(a)};this.getMarkerPoints=function(){return this.points};this.getMarkerAngles=function(){for(var b=0;b<this.angles.length;b++)if(this.angles[b]==null)for(var a=b+1;a<this.angles.length;a++)if(this.angles[a]!=null){this.angles[b]=this.angles[a];break}return this.angles}}(c);this.path=function(d){var b= -this.PathParser;b.reset();var c=new a.BoundingBox;for(d!=null&&d.beginPath();!b.isEnd();)switch(b.nextCommand(),b.command.toUpperCase()){case "M":var e=b.getAsCurrentPoint();b.addMarker(e);c.addPoint(e.x,e.y);d!=null&&d.moveTo(e.x,e.y);for(b.start=b.current;!b.isCommandOrEnd();)e=b.getAsCurrentPoint(),b.addMarker(e,b.start),c.addPoint(e.x,e.y),d!=null&&d.lineTo(e.x,e.y);break;case "L":for(;!b.isCommandOrEnd();){var f=b.current,e=b.getAsCurrentPoint();b.addMarker(e,f);c.addPoint(e.x,e.y);d!=null&& -d.lineTo(e.x,e.y)}break;case "H":for(;!b.isCommandOrEnd();)e=new a.Point((b.isRelativeCommand()?b.current.x:0)+b.getScalar(),b.current.y),b.addMarker(e,b.current),b.current=e,c.addPoint(b.current.x,b.current.y),d!=null&&d.lineTo(b.current.x,b.current.y);break;case "V":for(;!b.isCommandOrEnd();)e=new a.Point(b.current.x,(b.isRelativeCommand()?b.current.y:0)+b.getScalar()),b.addMarker(e,b.current),b.current=e,c.addPoint(b.current.x,b.current.y),d!=null&&d.lineTo(b.current.x,b.current.y);break;case "C":for(;!b.isCommandOrEnd();){var g= -b.current,f=b.getPoint(),j=b.getAsControlPoint(),e=b.getAsCurrentPoint();b.addMarker(e,j,f);c.addBezierCurve(g.x,g.y,f.x,f.y,j.x,j.y,e.x,e.y);d!=null&&d.bezierCurveTo(f.x,f.y,j.x,j.y,e.x,e.y)}break;case "S":for(;!b.isCommandOrEnd();)g=b.current,f=b.getReflectedControlPoint(),j=b.getAsControlPoint(),e=b.getAsCurrentPoint(),b.addMarker(e,j,f),c.addBezierCurve(g.x,g.y,f.x,f.y,j.x,j.y,e.x,e.y),d!=null&&d.bezierCurveTo(f.x,f.y,j.x,j.y,e.x,e.y);break;case "Q":for(;!b.isCommandOrEnd();)g=b.current,j=b.getAsControlPoint(), -e=b.getAsCurrentPoint(),b.addMarker(e,j,j),c.addQuadraticCurve(g.x,g.y,j.x,j.y,e.x,e.y),d!=null&&d.quadraticCurveTo(j.x,j.y,e.x,e.y);break;case "T":for(;!b.isCommandOrEnd();)g=b.current,j=b.getReflectedControlPoint(),b.control=j,e=b.getAsCurrentPoint(),b.addMarker(e,j,j),c.addQuadraticCurve(g.x,g.y,j.x,j.y,e.x,e.y),d!=null&&d.quadraticCurveTo(j.x,j.y,e.x,e.y);break;case "A":for(;!b.isCommandOrEnd();){var g=b.current,h=b.getScalar(),l=b.getScalar(),f=b.getScalar()*(Math.PI/180),o=b.getScalar(),j=b.getScalar(), -e=b.getAsCurrentPoint(),n=new a.Point(Math.cos(f)*(g.x-e.x)/2+Math.sin(f)*(g.y-e.y)/2,-Math.sin(f)*(g.x-e.x)/2+Math.cos(f)*(g.y-e.y)/2),q=Math.pow(n.x,2)/Math.pow(h,2)+Math.pow(n.y,2)/Math.pow(l,2);q>1&&(h*=Math.sqrt(q),l*=Math.sqrt(q));o=(o==j?-1:1)*Math.sqrt((Math.pow(h,2)*Math.pow(l,2)-Math.pow(h,2)*Math.pow(n.y,2)-Math.pow(l,2)*Math.pow(n.x,2))/(Math.pow(h,2)*Math.pow(n.y,2)+Math.pow(l,2)*Math.pow(n.x,2)));isNaN(o)&&(o=0);var p=new a.Point(o*h*n.y/l,o*-l*n.x/h),g=new a.Point((g.x+e.x)/2+Math.cos(f)* -p.x-Math.sin(f)*p.y,(g.y+e.y)/2+Math.sin(f)*p.x+Math.cos(f)*p.y),m=function(b,a){return(b[0]*a[0]+b[1]*a[1])/(Math.sqrt(Math.pow(b[0],2)+Math.pow(b[1],2))*Math.sqrt(Math.pow(a[0],2)+Math.pow(a[1],2)))},s=function(b,a){return(b[0]*a[1]<b[1]*a[0]?-1:1)*Math.acos(m(b,a))},o=s([1,0],[(n.x-p.x)/h,(n.y-p.y)/l]),q=[(n.x-p.x)/h,(n.y-p.y)/l],p=[(-n.x-p.x)/h,(-n.y-p.y)/l],n=s(q,p);if(m(q,p)<=-1)n=Math.PI;m(q,p)>=1&&(n=0);j==0&&n>0&&(n-=2*Math.PI);j==1&&n<0&&(n+=2*Math.PI);q=new a.Point(g.x-h*Math.cos((o+n)/ -2),g.y-l*Math.sin((o+n)/2));b.addMarkerAngle(q,(o+n)/2+(j==0?1:-1)*Math.PI/2);b.addMarkerAngle(e,n+(j==0?1:-1)*Math.PI/2);c.addPoint(e.x,e.y);d!=null&&(m=h>l?h:l,e=h>l?1:h/l,h=h>l?l/h:1,d.translate(g.x,g.y),d.rotate(f),d.scale(e,h),d.arc(0,0,m,o,o+n,1-j),d.scale(1/e,1/h),d.rotate(-f),d.translate(-g.x,-g.y))}break;case "Z":d!=null&&d.closePath(),b.current=b.start}return c};this.getMarkers=function(){for(var a=this.PathParser.getMarkerPoints(),b=this.PathParser.getMarkerAngles(),c=[],e=0;e<a.length;e++)c.push([a[e], -b[e]]);return c}};a.Element.path.prototype=new a.Element.PathElementBase;a.Element.pattern=function(c){this.base=a.Element.ElementBase;this.base(c);this.createPattern=function(d){var b=new a.Element.svg;b.attributes.viewBox=new a.Property("viewBox",this.attribute("viewBox").value);b.attributes.x=new a.Property("x",this.attribute("x").value);b.attributes.y=new a.Property("y",this.attribute("y").value);b.attributes.width=new a.Property("width",this.attribute("width").value);b.attributes.height=new a.Property("height", -this.attribute("height").value);b.children=this.children;var c=document.createElement("canvas");c.width=this.attribute("width").Length.toPixels("x");c.height=this.attribute("height").Length.toPixels("y");b.render(c.getContext("2d"));return d.createPattern(c,"repeat")}};a.Element.pattern.prototype=new a.Element.ElementBase;a.Element.marker=function(c){this.base=a.Element.ElementBase;this.base(c);this.baseRender=this.render;this.render=function(d,b,c){d.translate(b.x,b.y);this.attribute("orient").valueOrDefault("auto")== -"auto"&&d.rotate(c);this.attribute("markerUnits").valueOrDefault("strokeWidth")=="strokeWidth"&&d.scale(d.lineWidth,d.lineWidth);d.save();var e=new a.Element.svg;e.attributes.viewBox=new a.Property("viewBox",this.attribute("viewBox").value);e.attributes.refX=new a.Property("refX",this.attribute("refX").value);e.attributes.refY=new a.Property("refY",this.attribute("refY").value);e.attributes.width=new a.Property("width",this.attribute("markerWidth").value);e.attributes.height=new a.Property("height", -this.attribute("markerHeight").value);e.attributes.fill=new a.Property("fill",this.attribute("fill").valueOrDefault("black"));e.attributes.stroke=new a.Property("stroke",this.attribute("stroke").valueOrDefault("none"));e.children=this.children;e.render(d);d.restore();this.attribute("markerUnits").valueOrDefault("strokeWidth")=="strokeWidth"&&d.scale(1/d.lineWidth,1/d.lineWidth);this.attribute("orient").valueOrDefault("auto")=="auto"&&d.rotate(-c);d.translate(-b.x,-b.y)}};a.Element.marker.prototype= -new a.Element.ElementBase;a.Element.defs=function(c){this.base=a.Element.ElementBase;this.base(c);this.render=function(){}};a.Element.defs.prototype=new a.Element.ElementBase;a.Element.GradientBase=function(c){this.base=a.Element.ElementBase;this.base(c);this.gradientUnits=this.attribute("gradientUnits").valueOrDefault("objectBoundingBox");this.stops=[];for(c=0;c<this.children.length;c++)this.stops.push(this.children[c]);this.getGradient=function(){};this.createGradient=function(d,b){var c=this;this.attribute("xlink:href").hasValue()&& -(c=this.attribute("xlink:href").Definition.getDefinition());for(var e=this.getGradient(d,b),f=0;f<c.stops.length;f++)e.addColorStop(c.stops[f].offset,c.stops[f].color);if(this.attribute("gradientTransform").hasValue()){c=a.ViewPort.viewPorts[0];f=new a.Element.rect;f.attributes.x=new a.Property("x",-a.MAX_VIRTUAL_PIXELS/3);f.attributes.y=new a.Property("y",-a.MAX_VIRTUAL_PIXELS/3);f.attributes.width=new a.Property("width",a.MAX_VIRTUAL_PIXELS);f.attributes.height=new a.Property("height",a.MAX_VIRTUAL_PIXELS); -var g=new a.Element.g;g.attributes.transform=new a.Property("transform",this.attribute("gradientTransform").value);g.children=[f];f=new a.Element.svg;f.attributes.x=new a.Property("x",0);f.attributes.y=new a.Property("y",0);f.attributes.width=new a.Property("width",c.width);f.attributes.height=new a.Property("height",c.height);f.children=[g];g=document.createElement("canvas");g.width=c.width;g.height=c.height;c=g.getContext("2d");c.fillStyle=e;f.render(c);return c.createPattern(g,"no-repeat")}return e}}; -a.Element.GradientBase.prototype=new a.Element.ElementBase;a.Element.linearGradient=function(c){this.base=a.Element.GradientBase;this.base(c);this.getGradient=function(a,b){var c=b.getBoundingBox(),e=this.gradientUnits=="objectBoundingBox"?c.x()+c.width()*this.attribute("x1").numValue():this.attribute("x1").Length.toPixels("x"),f=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("y1").numValue():this.attribute("y1").Length.toPixels("y"),g=this.gradientUnits=="objectBoundingBox"? -c.x()+c.width()*this.attribute("x2").numValue():this.attribute("x2").Length.toPixels("x"),c=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("y2").numValue():this.attribute("y2").Length.toPixels("y");return a.createLinearGradient(e,f,g,c)}};a.Element.linearGradient.prototype=new a.Element.GradientBase;a.Element.radialGradient=function(c){this.base=a.Element.GradientBase;this.base(c);this.getGradient=function(a,b){var c=b.getBoundingBox(),e=this.gradientUnits=="objectBoundingBox"? -c.x()+c.width()*this.attribute("cx").numValue():this.attribute("cx").Length.toPixels("x"),f=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("cy").numValue():this.attribute("cy").Length.toPixels("y"),g=e,j=f;this.attribute("fx").hasValue()&&(g=this.gradientUnits=="objectBoundingBox"?c.x()+c.width()*this.attribute("fx").numValue():this.attribute("fx").Length.toPixels("x"));this.attribute("fy").hasValue()&&(j=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("fy").numValue(): -this.attribute("fy").Length.toPixels("y"));c=this.gradientUnits=="objectBoundingBox"?(c.width()+c.height())/2*this.attribute("r").numValue():this.attribute("r").Length.toPixels();return a.createRadialGradient(g,j,0,e,f,c)}};a.Element.radialGradient.prototype=new a.Element.GradientBase;a.Element.stop=function(c){this.base=a.Element.ElementBase;this.base(c);this.offset=this.attribute("offset").numValue();c=this.style("stop-color");this.style("stop-opacity").hasValue()&&(c=c.Color.addOpacity(this.style("stop-opacity").value)); -this.color=c.value};a.Element.stop.prototype=new a.Element.ElementBase;a.Element.AnimateBase=function(c){this.base=a.Element.ElementBase;this.base(c);a.Animations.push(this);this.duration=0;this.begin=this.attribute("begin").Time.toMilliseconds();this.maxDuration=this.begin+this.attribute("dur").Time.toMilliseconds();this.getProperty=function(){var a=this.attribute("attributeType").value,b=this.attribute("attributeName").value;return a=="CSS"?this.parent.style(b,!0):this.parent.attribute(b,!0)};this.initialValue= -null;this.removed=!1;this.calcValue=function(){return""};this.update=function(a){if(this.initialValue==null)this.initialValue=this.getProperty().value;if(this.duration>this.maxDuration)if(this.attribute("repeatCount").value=="indefinite")this.duration=0;else return this.attribute("fill").valueOrDefault("remove")=="remove"&&!this.removed?(this.removed=!0,this.getProperty().value=this.initialValue,!0):!1;this.duration+=a;a=!1;if(this.begin<this.duration)a=this.calcValue(),this.attribute("type").hasValue()&& -(a=this.attribute("type").value+"("+a+")"),this.getProperty().value=a,a=!0;return a};this.progress=function(){return(this.duration-this.begin)/(this.maxDuration-this.begin)}};a.Element.AnimateBase.prototype=new a.Element.ElementBase;a.Element.animate=function(c){this.base=a.Element.AnimateBase;this.base(c);this.calcValue=function(){var a=this.attribute("from").numValue(),b=this.attribute("to").numValue();return a+(b-a)*this.progress()}};a.Element.animate.prototype=new a.Element.AnimateBase;a.Element.animateColor= -function(c){this.base=a.Element.AnimateBase;this.base(c);this.calcValue=function(){var a=new RGBColor(this.attribute("from").value),b=new RGBColor(this.attribute("to").value);if(a.ok&&b.ok){var c=a.r+(b.r-a.r)*this.progress(),e=a.g+(b.g-a.g)*this.progress(),a=a.b+(b.b-a.b)*this.progress();return"rgb("+parseInt(c,10)+","+parseInt(e,10)+","+parseInt(a,10)+")"}return this.attribute("from").value}};a.Element.animateColor.prototype=new a.Element.AnimateBase;a.Element.animateTransform=function(c){this.base= -a.Element.animate;this.base(c)};a.Element.animateTransform.prototype=new a.Element.animate;a.Element.font=function(c){this.base=a.Element.ElementBase;this.base(c);this.horizAdvX=this.attribute("horiz-adv-x").numValue();this.isArabic=this.isRTL=!1;this.missingGlyph=this.fontFace=null;this.glyphs=[];for(c=0;c<this.children.length;c++){var d=this.children[c];if(d.type=="font-face")this.fontFace=d,d.style("font-family").hasValue()&&(a.Definitions[d.style("font-family").value]=this);else if(d.type=="missing-glyph")this.missingGlyph= -d;else if(d.type=="glyph")d.arabicForm!=""?(this.isArabic=this.isRTL=!0,typeof this.glyphs[d.unicode]=="undefined"&&(this.glyphs[d.unicode]=[]),this.glyphs[d.unicode][d.arabicForm]=d):this.glyphs[d.unicode]=d}};a.Element.font.prototype=new a.Element.ElementBase;a.Element.fontface=function(c){this.base=a.Element.ElementBase;this.base(c);this.ascent=this.attribute("ascent").value;this.descent=this.attribute("descent").value;this.unitsPerEm=this.attribute("units-per-em").numValue()};a.Element.fontface.prototype= -new a.Element.ElementBase;a.Element.missingglyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=0};a.Element.missingglyph.prototype=new a.Element.path;a.Element.glyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=this.attribute("horiz-adv-x").numValue();this.unicode=this.attribute("unicode").value;this.arabicForm=this.attribute("arabic-form").value};a.Element.glyph.prototype=new a.Element.path;a.Element.text=function(c){this.base=a.Element.RenderedElementBase; -this.base(c);if(c!=null){this.children=[];for(var d=0;d<c.childNodes.length;d++){var b=c.childNodes[d];b.nodeType==1?this.addChild(b,!0):b.nodeType==3&&this.addChild(new a.Element.tspan(b),!1)}}this.baseSetContext=this.setContext;this.setContext=function(b){this.baseSetContext(b);if(this.style("dominant-baseline").hasValue())b.textBaseline=this.style("dominant-baseline").value;if(this.style("alignment-baseline").hasValue())b.textBaseline=this.style("alignment-baseline").value};this.renderChildren= -function(b){for(var a=this.style("text-anchor").valueOrDefault("start"),c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),j=0;j<this.children.length;j++){var h=this.children[j];h.attribute("x").hasValue()?h.x=h.attribute("x").Length.toPixels("x"):(h.attribute("dx").hasValue()&&(c+=h.attribute("dx").Length.toPixels("x")),h.x=c);c=h.measureText(b);if(a!="start"&&(j==0||h.attribute("x").hasValue())){for(var l=c,o=j+1;o<this.children.length;o++){var n=this.children[o]; -if(n.attribute("x").hasValue())break;l+=n.measureText(b)}h.x-=a=="end"?l:l/2}c=h.x+c;h.attribute("y").hasValue()?h.y=h.attribute("y").Length.toPixels("y"):(h.attribute("dy").hasValue()&&(d+=h.attribute("dy").Length.toPixels("y")),h.y=d);d=h.y;h.render(b)}}};a.Element.text.prototype=new a.Element.RenderedElementBase;a.Element.TextElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getGlyph=function(a,b,c){var e=b[c],f=null;if(a.isArabic){var g="isolated";if((c==0||b[c- -1]==" ")&&c<b.length-2&&b[c+1]!=" ")g="terminal";c>0&&b[c-1]!=" "&&c<b.length-2&&b[c+1]!=" "&&(g="medial");if(c>0&&b[c-1]!=" "&&(c==b.length-1||b[c+1]==" "))g="initial";typeof a.glyphs[e]!="undefined"&&(f=a.glyphs[e][g],f==null&&a.glyphs[e].type=="glyph"&&(f=a.glyphs[e]))}else f=a.glyphs[e];if(f==null)f=a.missingGlyph;return f};this.renderChildren=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var k=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize), -e=this.parent.style("font-style").valueOrDefault(a.Font.Parse(a.ctx.font).fontStyle),f=this.getText();b.isRTL&&(f=f.split("").reverse().join(""));for(var g=a.ToNumberArray(this.parent.attribute("dx").value),j=0;j<f.length;j++){var h=this.getGlyph(b,f,j),l=k/b.fontFace.unitsPerEm;c.translate(this.x,this.y);c.scale(l,-l);var o=c.lineWidth;c.lineWidth=c.lineWidth*b.fontFace.unitsPerEm/k;e=="italic"&&c.transform(1,0,0.4,1,0,0);h.render(c);e=="italic"&&c.transform(1,0,-0.4,1,0,0);c.lineWidth=o;c.scale(1/ -l,-1/l);c.translate(-this.x,-this.y);this.x+=k*(h.horizAdvX||b.horizAdvX)/b.fontFace.unitsPerEm;typeof g[j]!="undefined"&&!isNaN(g[j])&&(this.x+=g[j])}}else c.strokeStyle!=""&&c.strokeText(a.compressSpaces(this.getText()),this.x,this.y),c.fillStyle!=""&&c.fillText(a.compressSpaces(this.getText()),this.x,this.y)};this.getText=function(){};this.measureText=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var c=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize), -k=0,e=this.getText();b.isRTL&&(e=e.split("").reverse().join(""));for(var f=a.ToNumberArray(this.parent.attribute("dx").value),g=0;g<e.length;g++){var j=this.getGlyph(b,e,g);k+=(j.horizAdvX||b.horizAdvX)*c/b.fontFace.unitsPerEm;typeof f[g]!="undefined"&&!isNaN(f[g])&&(k+=f[g])}return k}b=a.compressSpaces(this.getText());if(!c.measureText)return b.length*10;c.save();this.setContext(c);b=c.measureText(b).width;c.restore();return b}};a.Element.TextElementBase.prototype=new a.Element.RenderedElementBase; -a.Element.tspan=function(c){this.base=a.Element.TextElementBase;this.base(c);this.text=c.nodeType==3?c.nodeValue:c.childNodes.length>0?c.childNodes[0].nodeValue:c.text;this.getText=function(){return this.text}};a.Element.tspan.prototype=new a.Element.TextElementBase;a.Element.tref=function(c){this.base=a.Element.TextElementBase;this.base(c);this.getText=function(){var a=this.attribute("xlink:href").Definition.getDefinition();if(a!=null)return a.children[0].getText()}};a.Element.tref.prototype=new a.Element.TextElementBase; -a.Element.a=function(c){this.base=a.Element.TextElementBase;this.base(c);this.hasText=!0;for(var d=0;d<c.childNodes.length;d++)if(c.childNodes[d].nodeType!=3)this.hasText=!1;this.text=this.hasText?c.childNodes[0].nodeValue:"";this.getText=function(){return this.text};this.baseRenderChildren=this.renderChildren;this.renderChildren=function(b){if(this.hasText){this.baseRenderChildren(b);var c=new a.Property("fontSize",a.Font.Parse(a.ctx.font).fontSize);a.Mouse.checkBoundingBox(this,new a.BoundingBox(this.x, -this.y-c.Length.toPixels("y"),this.x+this.measureText(b),this.y))}else c=new a.Element.g,c.children=this.children,c.parent=this,c.render(b)};this.onclick=function(){window.open(this.attribute("xlink:href").value)};this.onmousemove=function(){a.ctx.canvas.style.cursor="pointer"}};a.Element.a.prototype=new a.Element.TextElementBase;a.Element.image=function(c){this.base=a.Element.RenderedElementBase;this.base(c);a.Images.push(this);this.img=document.createElement("img");this.loaded=!1;var d=this;this.img.onload= -function(){d.loaded=!0};this.img.src=this.attribute("xlink:href").value;this.renderChildren=function(b){var c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),f=this.attribute("width").Length.toPixels("x"),g=this.attribute("height").Length.toPixels("y");f==0||g==0||(b.save(),b.translate(c,d),a.AspectRatio(b,this.attribute("preserveAspectRatio").value,f,this.img.width,g,this.img.height,0,0),b.drawImage(this.img,0,0),b.restore())}};a.Element.image.prototype=new a.Element.RenderedElementBase; -a.Element.g=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getBoundingBox=function(){for(var c=new a.BoundingBox,b=0;b<this.children.length;b++)c.addBoundingBox(this.children[b].getBoundingBox());return c}};a.Element.g.prototype=new a.Element.RenderedElementBase;a.Element.symbol=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseSetContext=this.setContext;this.setContext=function(c){this.baseSetContext(c);if(this.attribute("viewBox").hasValue()){var b= -a.ToNumberArray(this.attribute("viewBox").value),k=b[0],e=b[1];width=b[2];height=b[3];a.AspectRatio(c,this.attribute("preserveAspectRatio").value,this.attribute("width").Length.toPixels("x"),width,this.attribute("height").Length.toPixels("y"),height,k,e);a.ViewPort.SetCurrent(b[2],b[3])}}};a.Element.symbol.prototype=new a.Element.RenderedElementBase;a.Element.style=function(c){this.base=a.Element.ElementBase;this.base(c);for(var c=c.childNodes[0].nodeValue+(c.childNodes.length>1?c.childNodes[1].nodeValue: -""),c=c.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm,""),c=a.compressSpaces(c),c=c.split("}"),d=0;d<c.length;d++)if(a.trim(c[d])!="")for(var b=c[d].split("{"),k=b[0].split(","),b=b[1].split(";"),e=0;e<k.length;e++){var f=a.trim(k[e]);if(f!=""){for(var g={},j=0;j<b.length;j++){var h=b[j].indexOf(":"),l=b[j].substr(0,h),h=b[j].substr(h+1,b[j].length-h);l!=null&&h!=null&&(g[a.trim(l)]=new a.Property(a.trim(l),a.trim(h)))}a.Styles[f]=g;if(f=="@font-face"){f=g["font-family"].value.replace(/"/g, -"");g=g.src.value.split(",");for(j=0;j<g.length;j++)if(g[j].indexOf('format("svg")')>0){l=g[j].indexOf("url");h=g[j].indexOf(")",l);l=g[j].substr(l+5,h-l-6);l=a.parseXml(a.ajax(l)).getElementsByTagName("font");for(h=0;h<l.length;h++){var o=a.CreateElement(l[h]);a.Definitions[f]=o}}}}}};a.Element.style.prototype=new a.Element.ElementBase;a.Element.use=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseSetContext=this.setContext;this.setContext=function(a){this.baseSetContext(a); -this.attribute("x").hasValue()&&a.translate(this.attribute("x").Length.toPixels("x"),0);this.attribute("y").hasValue()&&a.translate(0,this.attribute("y").Length.toPixels("y"))};this.getDefinition=function(){var a=this.attribute("xlink:href").Definition.getDefinition();if(this.attribute("width").hasValue())a.attribute("width",!0).value=this.attribute("width").value;if(this.attribute("height").hasValue())a.attribute("height",!0).value=this.attribute("height").value;return a};this.path=function(a){var b= -this.getDefinition();b!=null&&b.path(a)};this.renderChildren=function(a){var b=this.getDefinition();b!=null&&b.render(a)}};a.Element.use.prototype=new a.Element.RenderedElementBase;a.Element.mask=function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,b){var c=this.attribute("x").Length.toPixels("x"),e=this.attribute("y").Length.toPixels("y"),f=this.attribute("width").Length.toPixels("x"),g=this.attribute("height").Length.toPixels("y"),j=b.attribute("mask").value;b.attribute("mask").value= -"";var h=document.createElement("canvas");h.width=c+f;h.height=e+g;var l=h.getContext("2d");this.renderChildren(l);var o=document.createElement("canvas");o.width=c+f;o.height=e+g;var n=o.getContext("2d");b.render(n);n.globalCompositeOperation="destination-in";n.fillStyle=l.createPattern(h,"no-repeat");n.fillRect(0,0,c+f,e+g);a.fillStyle=n.createPattern(o,"no-repeat");a.fillRect(0,0,c+f,e+g);b.attribute("mask").value=j};this.render=function(){}};a.Element.mask.prototype=new a.Element.ElementBase;a.Element.clipPath= -function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a){for(var b=0;b<this.children.length;b++)this.children[b].path&&(this.children[b].path(a),a.clip())};this.render=function(){}};a.Element.clipPath.prototype=new a.Element.ElementBase;a.Element.filter=function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,b){var c=b.getBoundingBox(),e=this.attribute("x").Length.toPixels("x"),f=this.attribute("y").Length.toPixels("y");if(e==0||f==0)e=c.x1,f=c.y1;var g= -this.attribute("width").Length.toPixels("x"),j=this.attribute("height").Length.toPixels("y");if(g==0||j==0)g=c.width(),j=c.height();c=b.style("filter").value;b.style("filter").value="";var h=0.2*g,l=0.2*j,o=document.createElement("canvas");o.width=g+2*h;o.height=j+2*l;var n=o.getContext("2d");n.translate(-e+h,-f+l);b.render(n);for(var q=0;q<this.children.length;q++)this.children[q].apply(n,0,0,g+2*h,j+2*l);a.drawImage(o,0,0,g+2*h,j+2*l,e-h,f-l,g+2*h,j+2*l);b.style("filter",!0).value=c};this.render= -function(){}};a.Element.filter.prototype=new a.Element.ElementBase;a.Element.feGaussianBlur=function(c){function d(a,c,d,f,g){for(var j=0;j<g;j++)for(var h=0;h<f;h++)for(var l=a[j*f*4+h*4+3]/255,o=0;o<4;o++){for(var n=d[0]*(l==0?255:a[j*f*4+h*4+o])*(l==0||o==3?1:l),q=1;q<d.length;q++){var p=Math.max(h-q,0),m=a[j*f*4+p*4+3]/255,p=Math.min(h+q,f-1),p=a[j*f*4+p*4+3]/255,s=d[q],r;m==0?r=255:(r=Math.max(h-q,0),r=a[j*f*4+r*4+o]);m=r*(m==0||o==3?1:m);p==0?r=255:(r=Math.min(h+q,f-1),r=a[j*f*4+r*4+o]);n+= -s*(m+r*(p==0||o==3?1:p))}c[h*g*4+j*4+o]=n}}this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,c,e,f,g){var e=this.attribute("stdDeviation").numValue(),c=a.getImageData(0,0,f,g),e=Math.max(e,0.01),j=Math.ceil(e*4)+1;mask=[];for(var h=0;h<j;h++)mask[h]=Math.exp(-0.5*(h/e)*(h/e));e=mask;j=0;for(h=1;h<e.length;h++)j+=Math.abs(e[h]);j=2*j+Math.abs(e[0]);for(h=0;h<e.length;h++)e[h]/=j;tmp=[];d(c.data,tmp,e,f,g);d(tmp,c.data,e,g,f);a.clearRect(0,0,f,g);a.putImageData(c,0,0)}};a.Element.filter.prototype= -new a.Element.feGaussianBlur;a.Element.title=function(){};a.Element.title.prototype=new a.Element.ElementBase;a.Element.desc=function(){};a.Element.desc.prototype=new a.Element.ElementBase;a.Element.MISSING=function(a){console.log("ERROR: Element '"+a.nodeName+"' not yet implemented.")};a.Element.MISSING.prototype=new a.Element.ElementBase;a.CreateElement=function(c){var d=c.nodeName.replace(/^[^:]+:/,""),d=d.replace(/\-/g,""),b=null,b=typeof a.Element[d]!="undefined"?new a.Element[d](c):new a.Element.MISSING(c); -b.type=c.nodeName;return b};a.load=function(c,d){a.loadXml(c,a.ajax(d))};a.loadXml=function(c,d){a.loadXmlDoc(c,a.parseXml(d))};a.loadXmlDoc=function(c,d){a.init(c);var b=function(a){for(var b=c.canvas;b;)a.x-=b.offsetLeft,a.y-=b.offsetTop,b=b.offsetParent;window.scrollX&&(a.x+=window.scrollX);window.scrollY&&(a.y+=window.scrollY);return a};if(a.opts.ignoreMouse!=!0)c.canvas.onclick=function(c){c=b(new a.Point(c!=null?c.clientX:event.clientX,c!=null?c.clientY:event.clientY));a.Mouse.onclick(c.x,c.y)}, -c.canvas.onmousemove=function(c){c=b(new a.Point(c!=null?c.clientX:event.clientX,c!=null?c.clientY:event.clientY));a.Mouse.onmousemove(c.x,c.y)};var k=a.CreateElement(d.documentElement),e=k.root=!0,f=function(){a.ViewPort.Clear();c.canvas.parentNode&&a.ViewPort.SetCurrent(c.canvas.parentNode.clientWidth,c.canvas.parentNode.clientHeight);if(a.opts.ignoreDimensions!=!0){if(k.style("width").hasValue())c.canvas.width=k.style("width").Length.toPixels("x"),c.canvas.style.width=c.canvas.width+"px";if(k.style("height").hasValue())c.canvas.height= -k.style("height").Length.toPixels("y"),c.canvas.style.height=c.canvas.height+"px"}var b=c.canvas.clientWidth||c.canvas.width,d=c.canvas.clientHeight||c.canvas.height;a.ViewPort.SetCurrent(b,d);if(a.opts!=null&&a.opts.offsetX!=null)k.attribute("x",!0).value=a.opts.offsetX;if(a.opts!=null&&a.opts.offsetY!=null)k.attribute("y",!0).value=a.opts.offsetY;if(a.opts!=null&&a.opts.scaleWidth!=null&&a.opts.scaleHeight!=null){var f=1,g=1;k.attribute("width").hasValue()&&(f=k.attribute("width").Length.toPixels("x")/ -a.opts.scaleWidth);k.attribute("height").hasValue()&&(g=k.attribute("height").Length.toPixels("y")/a.opts.scaleHeight);k.attribute("width",!0).value=a.opts.scaleWidth;k.attribute("height",!0).value=a.opts.scaleHeight;k.attribute("viewBox",!0).value="0 0 "+b*f+" "+d*g;k.attribute("preserveAspectRatio",!0).value="none"}a.opts.ignoreClear!=!0&&c.clearRect(0,0,b,d);k.render(c);e&&(e=!1,a.opts!=null&&typeof a.opts.renderCallback=="function"&&a.opts.renderCallback())},g=!0;a.ImagesLoaded()&&(g=!1,f()); -a.intervalID=setInterval(function(){var b=!1;g&&a.ImagesLoaded()&&(g=!1,b=!0);a.opts.ignoreMouse!=!0&&(b|=a.Mouse.hasEvents());if(a.opts.ignoreAnimation!=!0)for(var c=0;c<a.Animations.length;c++)b|=a.Animations[c].update(1E3/a.FRAMERATE);a.opts!=null&&typeof a.opts.forceRedraw=="function"&&a.opts.forceRedraw()==!0&&(b=!0);b&&(f(),a.Mouse.runEvents())},1E3/a.FRAMERATE)};a.stop=function(){a.intervalID&&clearInterval(a.intervalID)};a.Mouse=new function(){this.events=[];this.hasEvents=function(){return this.events.length!= -0};this.onclick=function(a,d){this.events.push({type:"onclick",x:a,y:d,run:function(a){if(a.onclick)a.onclick()}})};this.onmousemove=function(a,d){this.events.push({type:"onmousemove",x:a,y:d,run:function(a){if(a.onmousemove)a.onmousemove()}})};this.eventElements=[];this.checkPath=function(a,d){for(var b=0;b<this.events.length;b++){var k=this.events[b];d.isPointInPath&&d.isPointInPath(k.x,k.y)&&(this.eventElements[b]=a)}};this.checkBoundingBox=function(a,d){for(var b=0;b<this.events.length;b++){var k= -this.events[b];d.isPointInBox(k.x,k.y)&&(this.eventElements[b]=a)}};this.runEvents=function(){a.ctx.canvas.style.cursor="";for(var c=0;c<this.events.length;c++)for(var d=this.events[c],b=this.eventElements[c];b;)d.run(b),b=b.parent;this.events=[];this.eventElements=[]}};return a}this.canvg=function(a,c,d){if(a==null&&c==null&&d==null)for(var c=document.getElementsByTagName("svg"),b=0;b<c.length;b++){a=c[b];d=document.createElement("canvas");d.width=a.clientWidth;d.height=a.clientHeight;a.parentNode.insertBefore(d, -a);a.parentNode.removeChild(a);var k=document.createElement("div");k.appendChild(a);canvg(d,k.innerHTML)}else d=d||{},typeof a=="string"&&(a=document.getElementById(a)),a.svg==null?(b=m(),a.svg=b):(b=a.svg,b.stop()),b.opts=d,a=a.getContext("2d"),typeof c.documentElement!="undefined"?b.loadXmlDoc(a,c):c.substr(0,1)=="<"?b.loadXml(a,c):b.load(a,c)}})(); -if(CanvasRenderingContext2D)CanvasRenderingContext2D.prototype.drawSvg=function(m,a,c,d,b){canvg(this.canvas,m,{ignoreMouse:!0,ignoreAnimation:!0,ignoreDimensions:!0,ignoreClear:!0,offsetX:a,offsetY:c,scaleWidth:d,scaleHeight:b})}; -(function(m){var a=m.css,c=m.CanVGRenderer,d=m.SVGRenderer,b=m.extend,k=m.merge,e=m.addEvent,f=m.createElement,g=m.discardElement;b(c.prototype,d.prototype);b(c.prototype,{create:function(a,b,c,d){this.setContainer(b,c,d);this.configure(a)},setContainer:function(a,b,c){var d=a.style,e=a.parentNode,g=d.left,d=d.top,k=a.offsetWidth,m=a.offsetHeight,s={visibility:"hidden",position:"absolute"};this.init.apply(this,[a,b,c]);this.canvas=f("canvas",{width:k,height:m},{position:"relative",left:g,top:d},a); -this.ttLine=f("div",null,s,e);this.ttDiv=f("div",null,s,e);this.ttTimer=void 0;this.hiddenSvg=a=f("div",{width:k,height:m},{visibility:"hidden",left:g,top:d},e);a.appendChild(this.box)},configure:function(b){var c=this,d=b.options.tooltip,f=d.borderWidth,g=c.ttDiv,m=d.style,p=c.ttLine,t=parseInt(m.padding,10),m=k(m,{padding:t+"px","background-color":d.backgroundColor,"border-style":"solid","border-width":f+"px","border-radius":d.borderRadius+"px"});d.shadow&&(m=k(m,{"box-shadow":"1px 1px 3px gray", -"-webkit-box-shadow":"1px 1px 3px gray"}));a(g,m);a(p,{"border-left":"1px solid darkgray"});e(b,"tooltipRefresh",function(d){var e=b.container,f=e.offsetLeft,e=e.offsetTop,k;g.innerHTML=d.text;k=b.tooltip.getPosition(g.offsetWidth,g.offsetHeight,{plotX:d.x,plotY:d.y});a(g,{visibility:"visible",left:k.x+"px",top:k.y+"px","border-color":d.borderColor});a(p,{visibility:"visible",left:f+d.x+"px",top:e+b.plotTop+"px",height:b.plotHeight+"px"});c.ttTimer!==void 0&&clearTimeout(c.ttTimer);c.ttTimer=setTimeout(function(){a(g, -{visibility:"hidden"});a(p,{visibility:"hidden"})},3E3)})},destroy:function(){g(this.canvas);this.ttTimer!==void 0&&clearTimeout(this.ttTimer);g(this.ttLine);g(this.ttDiv);g(this.hiddenSvg);return d.prototype.destroy.apply(this)},color:function(a,b,c){a&&a.linearGradient&&(a=a.stops[a.stops.length-1][1]);return d.prototype.color.call(this,a,b,c)},draw:function(){window.canvg(this.canvas,this.hiddenSvg.innerHTML)}})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/canvas-tools.src.js b/pykeg/web/static/highcharts/js/modules/canvas-tools.src.js deleted file mode 100644 index 7234681dc..000000000 --- a/pykeg/web/static/highcharts/js/modules/canvas-tools.src.js +++ /dev/null @@ -1,3113 +0,0 @@ -/** - * @license A class to parse color values - * @author Stoyan Stefanov <sstoo@gmail.com> - * @link http://www.phpied.com/rgb-color-parser-in-javascript/ - * Use it if you like it - * - */ -function RGBColor(color_string) -{ - this.ok = false; - - // strip any leading # - if (color_string.charAt(0) == '#') { // remove # if any - color_string = color_string.substr(1,6); - } - - color_string = color_string.replace(/ /g,''); - color_string = color_string.toLowerCase(); - - // before getting into regexps, try simple matches - // and overwrite the input - var simple_colors = { - aliceblue: 'f0f8ff', - antiquewhite: 'faebd7', - aqua: '00ffff', - aquamarine: '7fffd4', - azure: 'f0ffff', - beige: 'f5f5dc', - bisque: 'ffe4c4', - black: '000000', - blanchedalmond: 'ffebcd', - blue: '0000ff', - blueviolet: '8a2be2', - brown: 'a52a2a', - burlywood: 'deb887', - cadetblue: '5f9ea0', - chartreuse: '7fff00', - chocolate: 'd2691e', - coral: 'ff7f50', - cornflowerblue: '6495ed', - cornsilk: 'fff8dc', - crimson: 'dc143c', - cyan: '00ffff', - darkblue: '00008b', - darkcyan: '008b8b', - darkgoldenrod: 'b8860b', - darkgray: 'a9a9a9', - darkgreen: '006400', - darkkhaki: 'bdb76b', - darkmagenta: '8b008b', - darkolivegreen: '556b2f', - darkorange: 'ff8c00', - darkorchid: '9932cc', - darkred: '8b0000', - darksalmon: 'e9967a', - darkseagreen: '8fbc8f', - darkslateblue: '483d8b', - darkslategray: '2f4f4f', - darkturquoise: '00ced1', - darkviolet: '9400d3', - deeppink: 'ff1493', - deepskyblue: '00bfff', - dimgray: '696969', - dodgerblue: '1e90ff', - feldspar: 'd19275', - firebrick: 'b22222', - floralwhite: 'fffaf0', - forestgreen: '228b22', - fuchsia: 'ff00ff', - gainsboro: 'dcdcdc', - ghostwhite: 'f8f8ff', - gold: 'ffd700', - goldenrod: 'daa520', - gray: '808080', - green: '008000', - greenyellow: 'adff2f', - honeydew: 'f0fff0', - hotpink: 'ff69b4', - indianred : 'cd5c5c', - indigo : '4b0082', - ivory: 'fffff0', - khaki: 'f0e68c', - lavender: 'e6e6fa', - lavenderblush: 'fff0f5', - lawngreen: '7cfc00', - lemonchiffon: 'fffacd', - lightblue: 'add8e6', - lightcoral: 'f08080', - lightcyan: 'e0ffff', - lightgoldenrodyellow: 'fafad2', - lightgrey: 'd3d3d3', - lightgreen: '90ee90', - lightpink: 'ffb6c1', - lightsalmon: 'ffa07a', - lightseagreen: '20b2aa', - lightskyblue: '87cefa', - lightslateblue: '8470ff', - lightslategray: '778899', - lightsteelblue: 'b0c4de', - lightyellow: 'ffffe0', - lime: '00ff00', - limegreen: '32cd32', - linen: 'faf0e6', - magenta: 'ff00ff', - maroon: '800000', - mediumaquamarine: '66cdaa', - mediumblue: '0000cd', - mediumorchid: 'ba55d3', - mediumpurple: '9370d8', - mediumseagreen: '3cb371', - mediumslateblue: '7b68ee', - mediumspringgreen: '00fa9a', - mediumturquoise: '48d1cc', - mediumvioletred: 'c71585', - midnightblue: '191970', - mintcream: 'f5fffa', - mistyrose: 'ffe4e1', - moccasin: 'ffe4b5', - navajowhite: 'ffdead', - navy: '000080', - oldlace: 'fdf5e6', - olive: '808000', - olivedrab: '6b8e23', - orange: 'ffa500', - orangered: 'ff4500', - orchid: 'da70d6', - palegoldenrod: 'eee8aa', - palegreen: '98fb98', - paleturquoise: 'afeeee', - palevioletred: 'd87093', - papayawhip: 'ffefd5', - peachpuff: 'ffdab9', - peru: 'cd853f', - pink: 'ffc0cb', - plum: 'dda0dd', - powderblue: 'b0e0e6', - purple: '800080', - red: 'ff0000', - rosybrown: 'bc8f8f', - royalblue: '4169e1', - saddlebrown: '8b4513', - salmon: 'fa8072', - sandybrown: 'f4a460', - seagreen: '2e8b57', - seashell: 'fff5ee', - sienna: 'a0522d', - silver: 'c0c0c0', - skyblue: '87ceeb', - slateblue: '6a5acd', - slategray: '708090', - snow: 'fffafa', - springgreen: '00ff7f', - steelblue: '4682b4', - tan: 'd2b48c', - teal: '008080', - thistle: 'd8bfd8', - tomato: 'ff6347', - turquoise: '40e0d0', - violet: 'ee82ee', - violetred: 'd02090', - wheat: 'f5deb3', - white: 'ffffff', - whitesmoke: 'f5f5f5', - yellow: 'ffff00', - yellowgreen: '9acd32' - }; - for (var key in simple_colors) { - if (color_string == key) { - color_string = simple_colors[key]; - } - } - // emd of simple type-in colors - - // array of color definition objects - var color_defs = [ - { - re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, - example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], - process: function (bits){ - return [ - parseInt(bits[1]), - parseInt(bits[2]), - parseInt(bits[3]) - ]; - } - }, - { - re: /^(\w{2})(\w{2})(\w{2})$/, - example: ['#00ff00', '336699'], - process: function (bits){ - return [ - parseInt(bits[1], 16), - parseInt(bits[2], 16), - parseInt(bits[3], 16) - ]; - } - }, - { - re: /^(\w{1})(\w{1})(\w{1})$/, - example: ['#fb0', 'f0f'], - process: function (bits){ - return [ - parseInt(bits[1] + bits[1], 16), - parseInt(bits[2] + bits[2], 16), - parseInt(bits[3] + bits[3], 16) - ]; - } - } - ]; - - // search through the definitions to find a match - for (var i = 0; i < color_defs.length; i++) { - var re = color_defs[i].re; - var processor = color_defs[i].process; - var bits = re.exec(color_string); - if (bits) { - channels = processor(bits); - this.r = channels[0]; - this.g = channels[1]; - this.b = channels[2]; - this.ok = true; - } - - } - - // validate/cleanup values - this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r); - this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g); - this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b); - - // some getters - this.toRGB = function () { - return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; - } - this.toHex = function () { - var r = this.r.toString(16); - var g = this.g.toString(16); - var b = this.b.toString(16); - if (r.length == 1) r = '0' + r; - if (g.length == 1) g = '0' + g; - if (b.length == 1) b = '0' + b; - return '#' + r + g + b; - } - - // help - this.getHelpXML = function () { - - var examples = new Array(); - // add regexps - for (var i = 0; i < color_defs.length; i++) { - var example = color_defs[i].example; - for (var j = 0; j < example.length; j++) { - examples[examples.length] = example[j]; - } - } - // add type-in colors - for (var sc in simple_colors) { - examples[examples.length] = sc; - } - - var xml = document.createElement('ul'); - xml.setAttribute('id', 'rgbcolor-examples'); - for (var i = 0; i < examples.length; i++) { - try { - var list_item = document.createElement('li'); - var list_color = new RGBColor(examples[i]); - var example_div = document.createElement('div'); - example_div.style.cssText = - 'margin: 3px; ' - + 'border: 1px solid black; ' - + 'background:' + list_color.toHex() + '; ' - + 'color:' + list_color.toHex() - ; - example_div.appendChild(document.createTextNode('test')); - var list_item_value = document.createTextNode( - ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex() - ); - list_item.appendChild(example_div); - list_item.appendChild(list_item_value); - xml.appendChild(list_item); - - } catch(e){} - } - return xml; - - } - -} - -/** - * @license canvg.js - Javascript SVG parser and renderer on Canvas - * MIT Licensed - * Gabe Lerner (gabelerner@gmail.com) - * http://code.google.com/p/canvg/ - * - * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/ - * - */ -if(!window.console) { - window.console = {}; - window.console.log = function(str) {}; - window.console.dir = function(str) {}; -} - -if(!Array.prototype.indexOf){ - Array.prototype.indexOf = function(obj){ - for(var i=0; i<this.length; i++){ - if(this[i]==obj){ - return i; - } - } - return -1; - } -} - -(function(){ - // canvg(target, s) - // empty parameters: replace all 'svg' elements on page with 'canvas' elements - // target: canvas element or the id of a canvas element - // s: svg string, url to svg file, or xml document - // opts: optional hash of options - // ignoreMouse: true => ignore mouse events - // ignoreAnimation: true => ignore animations - // ignoreDimensions: true => does not try to resize canvas - // ignoreClear: true => does not clear canvas - // offsetX: int => draws at a x offset - // offsetY: int => draws at a y offset - // scaleWidth: int => scales horizontally to width - // scaleHeight: int => scales vertically to height - // renderCallback: function => will call the function after the first render is completed - // forceRedraw: function => will call the function on every frame, if it returns true, will redraw - this.canvg = function (target, s, opts) { - // no parameters - if (target == null && s == null && opts == null) { - var svgTags = document.getElementsByTagName('svg'); - for (var i=0; i<svgTags.length; i++) { - var svgTag = svgTags[i]; - var c = document.createElement('canvas'); - c.width = svgTag.clientWidth; - c.height = svgTag.clientHeight; - svgTag.parentNode.insertBefore(c, svgTag); - svgTag.parentNode.removeChild(svgTag); - var div = document.createElement('div'); - div.appendChild(svgTag); - canvg(c, div.innerHTML); - } - return; - } - opts = opts || {}; - - if (typeof target == 'string') { - target = document.getElementById(target); - } - - // reuse class per canvas - var svg; - if (target.svg == null) { - svg = build(); - target.svg = svg; - } - else { - svg = target.svg; - svg.stop(); - } - svg.opts = opts; - - var ctx = target.getContext('2d'); - if (typeof(s.documentElement) != 'undefined') { - // load from xml doc - svg.loadXmlDoc(ctx, s); - } - else if (s.substr(0,1) == '<') { - // load from xml string - svg.loadXml(ctx, s); - } - else { - // load from url - svg.load(ctx, s); - } - } - - function build() { - var svg = { }; - - svg.FRAMERATE = 30; - svg.MAX_VIRTUAL_PIXELS = 30000; - - // globals - svg.init = function(ctx) { - svg.Definitions = {}; - svg.Styles = {}; - svg.Animations = []; - svg.Images = []; - svg.ctx = ctx; - svg.ViewPort = new (function () { - this.viewPorts = []; - this.Clear = function() { this.viewPorts = []; } - this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); } - this.RemoveCurrent = function() { this.viewPorts.pop(); } - this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; } - this.width = function() { return this.Current().width; } - this.height = function() { return this.Current().height; } - this.ComputeSize = function(d) { - if (d != null && typeof(d) == 'number') return d; - if (d == 'x') return this.width(); - if (d == 'y') return this.height(); - return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2); - } - }); - } - svg.init(); - - // images loaded - svg.ImagesLoaded = function() { - for (var i=0; i<svg.Images.length; i++) { - if (!svg.Images[i].loaded) return false; - } - return true; - } - - // trim - svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); } - - // compress spaces - svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); } - - // ajax - svg.ajax = function(url) { - var AJAX; - if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();} - else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');} - if(AJAX){ - AJAX.open('GET',url,false); - AJAX.send(null); - return AJAX.responseText; - } - return null; - } - - // parse xml - svg.parseXml = function(xml) { - if (window.DOMParser) - { - var parser = new DOMParser(); - return parser.parseFromString(xml, 'text/xml'); - } - else - { - xml = xml.replace(/<!DOCTYPE svg[^>]*>/, ''); - var xmlDoc = new ActiveXObject('Microsoft.XMLDOM'); - xmlDoc.async = 'false'; - xmlDoc.loadXML(xml); - return xmlDoc; - } - } - - svg.Property = function(name, value) { - this.name = name; - this.value = value; - - this.hasValue = function() { - return (this.value != null && this.value !== ''); - } - - // return the numerical value of the property - this.numValue = function() { - if (!this.hasValue()) return 0; - - var n = parseFloat(this.value); - if ((this.value + '').match(/%$/)) { - n = n / 100.0; - } - return n; - } - - this.valueOrDefault = function(def) { - if (this.hasValue()) return this.value; - return def; - } - - this.numValueOrDefault = function(def) { - if (this.hasValue()) return this.numValue(); - return def; - } - - /* EXTENSIONS */ - var that = this; - - // color extensions - this.Color = { - // augment the current color value with the opacity - addOpacity: function(opacity) { - var newValue = that.value; - if (opacity != null && opacity != '') { - var color = new RGBColor(that.value); - if (color.ok) { - newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')'; - } - } - return new svg.Property(that.name, newValue); - } - } - - // definition extensions - this.Definition = { - // get the definition from the definitions table - getDefinition: function() { - var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2'); - return svg.Definitions[name]; - }, - - isUrl: function() { - return that.value.indexOf('url(') == 0 - }, - - getFillStyle: function(e) { - var def = this.getDefinition(); - - // gradient - if (def != null && def.createGradient) { - return def.createGradient(svg.ctx, e); - } - - // pattern - if (def != null && def.createPattern) { - return def.createPattern(svg.ctx, e); - } - - return null; - } - } - - // length extensions - this.Length = { - DPI: function(viewPort) { - return 96.0; // TODO: compute? - }, - - EM: function(viewPort) { - var em = 12; - - var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize); - if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort); - - return em; - }, - - // get the length as pixels - toPixels: function(viewPort) { - if (!that.hasValue()) return 0; - var s = that.value+''; - if (s.match(/em$/)) return that.numValue() * this.EM(viewPort); - if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0; - if (s.match(/px$/)) return that.numValue(); - if (s.match(/pt$/)) return that.numValue() * 1.25; - if (s.match(/pc$/)) return that.numValue() * 15; - if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54; - if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4; - if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort); - if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort); - return that.numValue(); - } - } - - // time extensions - this.Time = { - // get the time as milliseconds - toMilliseconds: function() { - if (!that.hasValue()) return 0; - var s = that.value+''; - if (s.match(/s$/)) return that.numValue() * 1000; - if (s.match(/ms$/)) return that.numValue(); - return that.numValue(); - } - } - - // angle extensions - this.Angle = { - // get the angle as radians - toRadians: function() { - if (!that.hasValue()) return 0; - var s = that.value+''; - if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0); - if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0); - if (s.match(/rad$/)) return that.numValue(); - return that.numValue() * (Math.PI / 180.0); - } - } - } - - // fonts - svg.Font = new (function() { - this.Styles = ['normal','italic','oblique','inherit']; - this.Variants = ['normal','small-caps','inherit']; - this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit']; - - this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) { - var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font); - return { - fontFamily: fontFamily || f.fontFamily, - fontSize: fontSize || f.fontSize, - fontStyle: fontStyle || f.fontStyle, - fontWeight: fontWeight || f.fontWeight, - fontVariant: fontVariant || f.fontVariant, - toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') } - } - } - - var that = this; - this.Parse = function(s) { - var f = {}; - var d = svg.trim(svg.compressSpaces(s || '')).split(' '); - var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false } - var ff = ''; - for (var i=0; i<d.length; i++) { - if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; } - else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; } - else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; } - else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; } - else { if (d[i] != 'inherit') ff += d[i]; } - } if (ff != '') f.fontFamily = ff; - return f; - } - }); - - // points and paths - svg.ToNumberArray = function(s) { - var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' '); - for (var i=0; i<a.length; i++) { - a[i] = parseFloat(a[i]); - } - return a; - } - svg.Point = function(x, y) { - this.x = x; - this.y = y; - - this.angleTo = function(p) { - return Math.atan2(p.y - this.y, p.x - this.x); - } - - this.applyTransform = function(v) { - var xp = this.x * v[0] + this.y * v[2] + v[4]; - var yp = this.x * v[1] + this.y * v[3] + v[5]; - this.x = xp; - this.y = yp; - } - } - svg.CreatePoint = function(s) { - var a = svg.ToNumberArray(s); - return new svg.Point(a[0], a[1]); - } - svg.CreatePath = function(s) { - var a = svg.ToNumberArray(s); - var path = []; - for (var i=0; i<a.length; i+=2) { - path.push(new svg.Point(a[i], a[i+1])); - } - return path; - } - - // bounding box - svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want - this.x1 = Number.NaN; - this.y1 = Number.NaN; - this.x2 = Number.NaN; - this.y2 = Number.NaN; - - this.x = function() { return this.x1; } - this.y = function() { return this.y1; } - this.width = function() { return this.x2 - this.x1; } - this.height = function() { return this.y2 - this.y1; } - - this.addPoint = function(x, y) { - if (x != null) { - if (isNaN(this.x1) || isNaN(this.x2)) { - this.x1 = x; - this.x2 = x; - } - if (x < this.x1) this.x1 = x; - if (x > this.x2) this.x2 = x; - } - - if (y != null) { - if (isNaN(this.y1) || isNaN(this.y2)) { - this.y1 = y; - this.y2 = y; - } - if (y < this.y1) this.y1 = y; - if (y > this.y2) this.y2 = y; - } - } - this.addX = function(x) { this.addPoint(x, null); } - this.addY = function(y) { this.addPoint(null, y); } - - this.addBoundingBox = function(bb) { - this.addPoint(bb.x1, bb.y1); - this.addPoint(bb.x2, bb.y2); - } - - this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) { - var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0) - var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0) - var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0) - var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0) - this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y); - } - - this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { - // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y]; - this.addPoint(p0[0], p0[1]); - this.addPoint(p3[0], p3[1]); - - for (i=0; i<=1; i++) { - var f = function(t) { - return Math.pow(1-t, 3) * p0[i] - + 3 * Math.pow(1-t, 2) * t * p1[i] - + 3 * (1-t) * Math.pow(t, 2) * p2[i] - + Math.pow(t, 3) * p3[i]; - } - - var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i]; - var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i]; - var c = 3 * p1[i] - 3 * p0[i]; - - if (a == 0) { - if (b == 0) continue; - var t = -c / b; - if (0 < t && t < 1) { - if (i == 0) this.addX(f(t)); - if (i == 1) this.addY(f(t)); - } - continue; - } - - var b2ac = Math.pow(b, 2) - 4 * c * a; - if (b2ac < 0) continue; - var t1 = (-b + Math.sqrt(b2ac)) / (2 * a); - if (0 < t1 && t1 < 1) { - if (i == 0) this.addX(f(t1)); - if (i == 1) this.addY(f(t1)); - } - var t2 = (-b - Math.sqrt(b2ac)) / (2 * a); - if (0 < t2 && t2 < 1) { - if (i == 0) this.addX(f(t2)); - if (i == 1) this.addY(f(t2)); - } - } - } - - this.isPointInBox = function(x, y) { - return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2); - } - - this.addPoint(x1, y1); - this.addPoint(x2, y2); - } - - // transforms - svg.Transform = function(v) { - var that = this; - this.Type = {} - - // translate - this.Type.translate = function(s) { - this.p = svg.CreatePoint(s); - this.apply = function(ctx) { - ctx.translate(this.p.x || 0.0, this.p.y || 0.0); - } - this.applyToPoint = function(p) { - p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]); - } - } - - // rotate - this.Type.rotate = function(s) { - var a = svg.ToNumberArray(s); - this.angle = new svg.Property('angle', a[0]); - this.cx = a[1] || 0; - this.cy = a[2] || 0; - this.apply = function(ctx) { - ctx.translate(this.cx, this.cy); - ctx.rotate(this.angle.Angle.toRadians()); - ctx.translate(-this.cx, -this.cy); - } - this.applyToPoint = function(p) { - var a = this.angle.Angle.toRadians(); - p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]); - p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]); - p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]); - } - } - - this.Type.scale = function(s) { - this.p = svg.CreatePoint(s); - this.apply = function(ctx) { - ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0); - } - this.applyToPoint = function(p) { - p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]); - } - } - - this.Type.matrix = function(s) { - this.m = svg.ToNumberArray(s); - this.apply = function(ctx) { - ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]); - } - this.applyToPoint = function(p) { - p.applyTransform(this.m); - } - } - - this.Type.SkewBase = function(s) { - this.base = that.Type.matrix; - this.base(s); - this.angle = new svg.Property('angle', s); - } - this.Type.SkewBase.prototype = new this.Type.matrix; - - this.Type.skewX = function(s) { - this.base = that.Type.SkewBase; - this.base(s); - this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0]; - } - this.Type.skewX.prototype = new this.Type.SkewBase; - - this.Type.skewY = function(s) { - this.base = that.Type.SkewBase; - this.base(s); - this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0]; - } - this.Type.skewY.prototype = new this.Type.SkewBase; - - this.transforms = []; - - this.apply = function(ctx) { - for (var i=0; i<this.transforms.length; i++) { - this.transforms[i].apply(ctx); - } - } - - this.applyToPoint = function(p) { - for (var i=0; i<this.transforms.length; i++) { - this.transforms[i].applyToPoint(p); - } - } - - var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/); - for (var i=0; i<data.length; i++) { - var type = data[i].split('(')[0]; - var s = data[i].split('(')[1].replace(')',''); - var transform = new this.Type[type](s); - this.transforms.push(transform); - } - } - - // aspect ratio - svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) { - // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute - aspectRatio = svg.compressSpaces(aspectRatio); - aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer - var align = aspectRatio.split(' ')[0] || 'xMidYMid'; - var meetOrSlice = aspectRatio.split(' ')[1] || 'meet'; - - // calculate scale - var scaleX = width / desiredWidth; - var scaleY = height / desiredHeight; - var scaleMin = Math.min(scaleX, scaleY); - var scaleMax = Math.max(scaleX, scaleY); - if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; } - if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; } - - refX = new svg.Property('refX', refX); - refY = new svg.Property('refY', refY); - if (refX.hasValue() && refY.hasValue()) { - ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y')); - } - else { - // align - if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0); - if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0); - if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0); - if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight); - } - - // scale - if (align == 'none') ctx.scale(scaleX, scaleY); - else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin); - else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax); - - // translate - ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY); - } - - // elements - svg.Element = {} - - svg.Element.ElementBase = function(node) { - this.attributes = {}; - this.styles = {}; - this.children = []; - - // get or create attribute - this.attribute = function(name, createIfNotExists) { - var a = this.attributes[name]; - if (a != null) return a; - - a = new svg.Property(name, ''); - if (createIfNotExists == true) this.attributes[name] = a; - return a; - } - - // get or create style, crawls up node tree - this.style = function(name, createIfNotExists) { - var s = this.styles[name]; - if (s != null) return s; - - var a = this.attribute(name); - if (a != null && a.hasValue()) { - return a; - } - - var p = this.parent; - if (p != null) { - var ps = p.style(name); - if (ps != null && ps.hasValue()) { - return ps; - } - } - - s = new svg.Property(name, ''); - if (createIfNotExists == true) this.styles[name] = s; - return s; - } - - // base render - this.render = function(ctx) { - // don't render display=none - if (this.style('display').value == 'none') return; - - // don't render visibility=hidden - if (this.attribute('visibility').value == 'hidden') return; - - ctx.save(); - this.setContext(ctx); - // mask - if (this.attribute('mask').hasValue()) { - var mask = this.attribute('mask').Definition.getDefinition(); - if (mask != null) mask.apply(ctx, this); - } - else if (this.style('filter').hasValue()) { - var filter = this.style('filter').Definition.getDefinition(); - if (filter != null) filter.apply(ctx, this); - } - else this.renderChildren(ctx); - this.clearContext(ctx); - ctx.restore(); - } - - // base set context - this.setContext = function(ctx) { - // OVERRIDE ME! - } - - // base clear context - this.clearContext = function(ctx) { - // OVERRIDE ME! - } - - // base render children - this.renderChildren = function(ctx) { - for (var i=0; i<this.children.length; i++) { - this.children[i].render(ctx); - } - } - - this.addChild = function(childNode, create) { - var child = childNode; - if (create) child = svg.CreateElement(childNode); - child.parent = this; - this.children.push(child); - } - - if (node != null && node.nodeType == 1) { //ELEMENT_NODE - // add children - for (var i=0; i<node.childNodes.length; i++) { - var childNode = node.childNodes[i]; - if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE - } - - // add attributes - for (var i=0; i<node.attributes.length; i++) { - var attribute = node.attributes[i]; - this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue); - } - - // add tag styles - var styles = svg.Styles[node.nodeName]; - if (styles != null) { - for (var name in styles) { - this.styles[name] = styles[name]; - } - } - - // add class styles - if (this.attribute('class').hasValue()) { - var classes = svg.compressSpaces(this.attribute('class').value).split(' '); - for (var j=0; j<classes.length; j++) { - styles = svg.Styles['.'+classes[j]]; - if (styles != null) { - for (var name in styles) { - this.styles[name] = styles[name]; - } - } - styles = svg.Styles[node.nodeName+'.'+classes[j]]; - if (styles != null) { - for (var name in styles) { - this.styles[name] = styles[name]; - } - } - } - } - - // add inline styles - if (this.attribute('style').hasValue()) { - var styles = this.attribute('style').value.split(';'); - for (var i=0; i<styles.length; i++) { - if (svg.trim(styles[i]) != '') { - var style = styles[i].split(':'); - var name = svg.trim(style[0]); - var value = svg.trim(style[1]); - this.styles[name] = new svg.Property(name, value); - } - } - } - - // add id - if (this.attribute('id').hasValue()) { - if (svg.Definitions[this.attribute('id').value] == null) { - svg.Definitions[this.attribute('id').value] = this; - } - } - } - } - - svg.Element.RenderedElementBase = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.setContext = function(ctx) { - // fill - if (this.style('fill').Definition.isUrl()) { - var fs = this.style('fill').Definition.getFillStyle(this); - if (fs != null) ctx.fillStyle = fs; - } - else if (this.style('fill').hasValue()) { - var fillStyle = this.style('fill'); - if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value); - ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value); - } - - // stroke - if (this.style('stroke').Definition.isUrl()) { - var fs = this.style('stroke').Definition.getFillStyle(this); - if (fs != null) ctx.strokeStyle = fs; - } - else if (this.style('stroke').hasValue()) { - var strokeStyle = this.style('stroke'); - if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value); - ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value); - } - if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels(); - if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value; - if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value; - if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value; - - // font - if (typeof(ctx.font) != 'undefined') { - ctx.font = svg.Font.CreateFont( - this.style('font-style').value, - this.style('font-variant').value, - this.style('font-weight').value, - this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '', - this.style('font-family').value).toString(); - } - - // transform - if (this.attribute('transform').hasValue()) { - var transform = new svg.Transform(this.attribute('transform').value); - transform.apply(ctx); - } - - // clip - if (this.attribute('clip-path').hasValue()) { - var clip = this.attribute('clip-path').Definition.getDefinition(); - if (clip != null) clip.apply(ctx); - } - - // opacity - if (this.style('opacity').hasValue()) { - ctx.globalAlpha = this.style('opacity').numValue(); - } - } - } - svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase; - - svg.Element.PathElementBase = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.path = function(ctx) { - if (ctx != null) ctx.beginPath(); - return new svg.BoundingBox(); - } - - this.renderChildren = function(ctx) { - this.path(ctx); - svg.Mouse.checkPath(this, ctx); - if (ctx.fillStyle != '') ctx.fill(); - if (ctx.strokeStyle != '') ctx.stroke(); - - var markers = this.getMarkers(); - if (markers != null) { - if (this.style('marker-start').Definition.isUrl()) { - var marker = this.style('marker-start').Definition.getDefinition(); - marker.render(ctx, markers[0][0], markers[0][1]); - } - if (this.style('marker-mid').Definition.isUrl()) { - var marker = this.style('marker-mid').Definition.getDefinition(); - for (var i=1;i<markers.length-1;i++) { - marker.render(ctx, markers[i][0], markers[i][1]); - } - } - if (this.style('marker-end').Definition.isUrl()) { - var marker = this.style('marker-end').Definition.getDefinition(); - marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]); - } - } - } - - this.getBoundingBox = function() { - return this.path(); - } - - this.getMarkers = function() { - return null; - } - } - svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase; - - // svg element - svg.Element.svg = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.baseClearContext = this.clearContext; - this.clearContext = function(ctx) { - this.baseClearContext(ctx); - svg.ViewPort.RemoveCurrent(); - } - - this.baseSetContext = this.setContext; - this.setContext = function(ctx) { - // initial values - ctx.strokeStyle = 'rgba(0,0,0,0)'; - ctx.lineCap = 'butt'; - ctx.lineJoin = 'miter'; - ctx.miterLimit = 4; - - this.baseSetContext(ctx); - - // create new view port - if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) { - ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y')); - } - - var width = svg.ViewPort.width(); - var height = svg.ViewPort.height(); - if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) { - width = this.attribute('width').Length.toPixels('x'); - height = this.attribute('height').Length.toPixels('y'); - - var x = 0; - var y = 0; - if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) { - x = -this.attribute('refX').Length.toPixels('x'); - y = -this.attribute('refY').Length.toPixels('y'); - } - - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(width, y); - ctx.lineTo(width, height); - ctx.lineTo(x, height); - ctx.closePath(); - ctx.clip(); - } - svg.ViewPort.SetCurrent(width, height); - - // viewbox - if (this.attribute('viewBox').hasValue()) { - var viewBox = svg.ToNumberArray(this.attribute('viewBox').value); - var minX = viewBox[0]; - var minY = viewBox[1]; - width = viewBox[2]; - height = viewBox[3]; - - svg.AspectRatio(ctx, - this.attribute('preserveAspectRatio').value, - svg.ViewPort.width(), - width, - svg.ViewPort.height(), - height, - minX, - minY, - this.attribute('refX').value, - this.attribute('refY').value); - - svg.ViewPort.RemoveCurrent(); - svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]); - } - } - } - svg.Element.svg.prototype = new svg.Element.RenderedElementBase; - - // rect element - svg.Element.rect = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - this.path = function(ctx) { - var x = this.attribute('x').Length.toPixels('x'); - var y = this.attribute('y').Length.toPixels('y'); - var width = this.attribute('width').Length.toPixels('x'); - var height = this.attribute('height').Length.toPixels('y'); - var rx = this.attribute('rx').Length.toPixels('x'); - var ry = this.attribute('ry').Length.toPixels('y'); - if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx; - if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry; - - if (ctx != null) { - ctx.beginPath(); - ctx.moveTo(x + rx, y); - ctx.lineTo(x + width - rx, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + ry) - ctx.lineTo(x + width, y + height - ry); - ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height) - ctx.lineTo(x + rx, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - ry) - ctx.lineTo(x, y + ry); - ctx.quadraticCurveTo(x, y, x + rx, y) - ctx.closePath(); - } - - return new svg.BoundingBox(x, y, x + width, y + height); - } - } - svg.Element.rect.prototype = new svg.Element.PathElementBase; - - // circle element - svg.Element.circle = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - this.path = function(ctx) { - var cx = this.attribute('cx').Length.toPixels('x'); - var cy = this.attribute('cy').Length.toPixels('y'); - var r = this.attribute('r').Length.toPixels(); - - if (ctx != null) { - ctx.beginPath(); - ctx.arc(cx, cy, r, 0, Math.PI * 2, true); - ctx.closePath(); - } - - return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r); - } - } - svg.Element.circle.prototype = new svg.Element.PathElementBase; - - // ellipse element - svg.Element.ellipse = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - this.path = function(ctx) { - var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3); - var rx = this.attribute('rx').Length.toPixels('x'); - var ry = this.attribute('ry').Length.toPixels('y'); - var cx = this.attribute('cx').Length.toPixels('x'); - var cy = this.attribute('cy').Length.toPixels('y'); - - if (ctx != null) { - ctx.beginPath(); - ctx.moveTo(cx, cy - ry); - ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy); - ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry); - ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy); - ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry); - ctx.closePath(); - } - - return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry); - } - } - svg.Element.ellipse.prototype = new svg.Element.PathElementBase; - - // line element - svg.Element.line = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - this.getPoints = function() { - return [ - new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')), - new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))]; - } - - this.path = function(ctx) { - var points = this.getPoints(); - - if (ctx != null) { - ctx.beginPath(); - ctx.moveTo(points[0].x, points[0].y); - ctx.lineTo(points[1].x, points[1].y); - } - - return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y); - } - - this.getMarkers = function() { - var points = this.getPoints(); - var a = points[0].angleTo(points[1]); - return [[points[0], a], [points[1], a]]; - } - } - svg.Element.line.prototype = new svg.Element.PathElementBase; - - // polyline element - svg.Element.polyline = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - this.points = svg.CreatePath(this.attribute('points').value); - this.path = function(ctx) { - var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y); - if (ctx != null) { - ctx.beginPath(); - ctx.moveTo(this.points[0].x, this.points[0].y); - } - for (var i=1; i<this.points.length; i++) { - bb.addPoint(this.points[i].x, this.points[i].y); - if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y); - } - return bb; - } - - this.getMarkers = function() { - var markers = []; - for (var i=0; i<this.points.length - 1; i++) { - markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]); - } - markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]); - return markers; - } - } - svg.Element.polyline.prototype = new svg.Element.PathElementBase; - - // polygon element - svg.Element.polygon = function(node) { - this.base = svg.Element.polyline; - this.base(node); - - this.basePath = this.path; - this.path = function(ctx) { - var bb = this.basePath(ctx); - if (ctx != null) { - ctx.lineTo(this.points[0].x, this.points[0].y); - ctx.closePath(); - } - return bb; - } - } - svg.Element.polygon.prototype = new svg.Element.polyline; - - // path element - svg.Element.path = function(node) { - this.base = svg.Element.PathElementBase; - this.base(node); - - var d = this.attribute('d').value; - // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF - d = d.replace(/,/gm,' '); // get rid of all commas - d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands - d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands - d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points - d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points - d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma - d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma - d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax - d = svg.compressSpaces(d); // compress multiple spaces - d = svg.trim(d); - this.PathParser = new (function(d) { - this.tokens = d.split(' '); - - this.reset = function() { - this.i = -1; - this.command = ''; - this.previousCommand = ''; - this.start = new svg.Point(0, 0); - this.control = new svg.Point(0, 0); - this.current = new svg.Point(0, 0); - this.points = []; - this.angles = []; - } - - this.isEnd = function() { - return this.i >= this.tokens.length - 1; - } - - this.isCommandOrEnd = function() { - if (this.isEnd()) return true; - return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null; - } - - this.isRelativeCommand = function() { - return this.command == this.command.toLowerCase(); - } - - this.getToken = function() { - this.i = this.i + 1; - return this.tokens[this.i]; - } - - this.getScalar = function() { - return parseFloat(this.getToken()); - } - - this.nextCommand = function() { - this.previousCommand = this.command; - this.command = this.getToken(); - } - - this.getPoint = function() { - var p = new svg.Point(this.getScalar(), this.getScalar()); - return this.makeAbsolute(p); - } - - this.getAsControlPoint = function() { - var p = this.getPoint(); - this.control = p; - return p; - } - - this.getAsCurrentPoint = function() { - var p = this.getPoint(); - this.current = p; - return p; - } - - this.getReflectedControlPoint = function() { - if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') { - return this.current; - } - - // reflect point - var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y); - return p; - } - - this.makeAbsolute = function(p) { - if (this.isRelativeCommand()) { - p.x = this.current.x + p.x; - p.y = this.current.y + p.y; - } - return p; - } - - this.addMarker = function(p, from, priorTo) { - // if the last angle isn't filled in because we didn't have this point yet ... - if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) { - this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo); - } - this.addMarkerAngle(p, from == null ? null : from.angleTo(p)); - } - - this.addMarkerAngle = function(p, a) { - this.points.push(p); - this.angles.push(a); - } - - this.getMarkerPoints = function() { return this.points; } - this.getMarkerAngles = function() { - for (var i=0; i<this.angles.length; i++) { - if (this.angles[i] == null) { - for (var j=i+1; j<this.angles.length; j++) { - if (this.angles[j] != null) { - this.angles[i] = this.angles[j]; - break; - } - } - } - } - return this.angles; - } - })(d); - - this.path = function(ctx) { - var pp = this.PathParser; - pp.reset(); - - var bb = new svg.BoundingBox(); - if (ctx != null) ctx.beginPath(); - while (!pp.isEnd()) { - pp.nextCommand(); - switch (pp.command.toUpperCase()) { - case 'M': - var p = pp.getAsCurrentPoint(); - pp.addMarker(p); - bb.addPoint(p.x, p.y); - if (ctx != null) ctx.moveTo(p.x, p.y); - pp.start = pp.current; - while (!pp.isCommandOrEnd()) { - var p = pp.getAsCurrentPoint(); - pp.addMarker(p, pp.start); - bb.addPoint(p.x, p.y); - if (ctx != null) ctx.lineTo(p.x, p.y); - } - break; - case 'L': - while (!pp.isCommandOrEnd()) { - var c = pp.current; - var p = pp.getAsCurrentPoint(); - pp.addMarker(p, c); - bb.addPoint(p.x, p.y); - if (ctx != null) ctx.lineTo(p.x, p.y); - } - break; - case 'H': - while (!pp.isCommandOrEnd()) { - var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y); - pp.addMarker(newP, pp.current); - pp.current = newP; - bb.addPoint(pp.current.x, pp.current.y); - if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); - } - break; - case 'V': - while (!pp.isCommandOrEnd()) { - var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar()); - pp.addMarker(newP, pp.current); - pp.current = newP; - bb.addPoint(pp.current.x, pp.current.y); - if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); - } - break; - case 'C': - while (!pp.isCommandOrEnd()) { - var curr = pp.current; - var p1 = pp.getPoint(); - var cntrl = pp.getAsControlPoint(); - var cp = pp.getAsCurrentPoint(); - pp.addMarker(cp, cntrl, p1); - bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - } - break; - case 'S': - while (!pp.isCommandOrEnd()) { - var curr = pp.current; - var p1 = pp.getReflectedControlPoint(); - var cntrl = pp.getAsControlPoint(); - var cp = pp.getAsCurrentPoint(); - pp.addMarker(cp, cntrl, p1); - bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); - } - break; - case 'Q': - while (!pp.isCommandOrEnd()) { - var curr = pp.current; - var cntrl = pp.getAsControlPoint(); - var cp = pp.getAsCurrentPoint(); - pp.addMarker(cp, cntrl, cntrl); - bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); - } - break; - case 'T': - while (!pp.isCommandOrEnd()) { - var curr = pp.current; - var cntrl = pp.getReflectedControlPoint(); - pp.control = cntrl; - var cp = pp.getAsCurrentPoint(); - pp.addMarker(cp, cntrl, cntrl); - bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); - if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); - } - break; - case 'A': - while (!pp.isCommandOrEnd()) { - var curr = pp.current; - var rx = pp.getScalar(); - var ry = pp.getScalar(); - var xAxisRotation = pp.getScalar() * (Math.PI / 180.0); - var largeArcFlag = pp.getScalar(); - var sweepFlag = pp.getScalar(); - var cp = pp.getAsCurrentPoint(); - - // Conversion from endpoint to center parameterization - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // x1', y1' - var currp = new svg.Point( - Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0, - -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0 - ); - // adjust radii - var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2); - if (l > 1) { - rx *= Math.sqrt(l); - ry *= Math.sqrt(l); - } - // cx', cy' - var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt( - ((Math.pow(rx,2)*Math.pow(ry,2))-(Math.pow(rx,2)*Math.pow(currp.y,2))-(Math.pow(ry,2)*Math.pow(currp.x,2))) / - (Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2)) - ); - if (isNaN(s)) s = 0; - var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx); - // cx, cy - var centp = new svg.Point( - (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y, - (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y - ); - // vector magnitude - var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); } - // ratio between two vectors - var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) } - // angle between two vectors - var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); } - // initial angle - var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]); - // angle delta - var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]; - var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry]; - var ad = a(u, v); - if (r(u,v) <= -1) ad = Math.PI; - if (r(u,v) >= 1) ad = 0; - - if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI; - if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI; - - // for markers - var halfWay = new svg.Point( - centp.x - rx * Math.cos((a1 + ad) / 2), - centp.y - ry * Math.sin((a1 + ad) / 2) - ); - pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2); - pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2); - - bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better - if (ctx != null) { - var r = rx > ry ? rx : ry; - var sx = rx > ry ? 1 : rx / ry; - var sy = rx > ry ? ry / rx : 1; - - ctx.translate(centp.x, centp.y); - ctx.rotate(xAxisRotation); - ctx.scale(sx, sy); - ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag); - ctx.scale(1/sx, 1/sy); - ctx.rotate(-xAxisRotation); - ctx.translate(-centp.x, -centp.y); - } - } - break; - case 'Z': - if (ctx != null) ctx.closePath(); - pp.current = pp.start; - } - } - - return bb; - } - - this.getMarkers = function() { - var points = this.PathParser.getMarkerPoints(); - var angles = this.PathParser.getMarkerAngles(); - - var markers = []; - for (var i=0; i<points.length; i++) { - markers.push([points[i], angles[i]]); - } - return markers; - } - } - svg.Element.path.prototype = new svg.Element.PathElementBase; - - // pattern element - svg.Element.pattern = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.createPattern = function(ctx, element) { - // render me using a temporary svg element - var tempSvg = new svg.Element.svg(); - tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value); - tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value); - tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value); - tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value); - tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value); - tempSvg.children = this.children; - - var c = document.createElement('canvas'); - c.width = this.attribute('width').Length.toPixels('x'); - c.height = this.attribute('height').Length.toPixels('y'); - tempSvg.render(c.getContext('2d')); - return ctx.createPattern(c, 'repeat'); - } - } - svg.Element.pattern.prototype = new svg.Element.ElementBase; - - // marker element - svg.Element.marker = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.baseRender = this.render; - this.render = function(ctx, point, angle) { - ctx.translate(point.x, point.y); - if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle); - if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth); - ctx.save(); - - // render me using a temporary svg element - var tempSvg = new svg.Element.svg(); - tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value); - tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value); - tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value); - tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value); - tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value); - tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black')); - tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none')); - tempSvg.children = this.children; - tempSvg.render(ctx); - - ctx.restore(); - if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth); - if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle); - ctx.translate(-point.x, -point.y); - } - } - svg.Element.marker.prototype = new svg.Element.ElementBase; - - // definitions element - svg.Element.defs = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.render = function(ctx) { - // NOOP - } - } - svg.Element.defs.prototype = new svg.Element.ElementBase; - - // base for gradients - svg.Element.GradientBase = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox'); - - this.stops = []; - for (var i=0; i<this.children.length; i++) { - var child = this.children[i]; - this.stops.push(child); - } - - this.getGradient = function() { - // OVERRIDE ME! - } - - this.createGradient = function(ctx, element) { - var stopsContainer = this; - if (this.attribute('xlink:href').hasValue()) { - stopsContainer = this.attribute('xlink:href').Definition.getDefinition(); - } - - var g = this.getGradient(ctx, element); - for (var i=0; i<stopsContainer.stops.length; i++) { - g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color); - } - - if (this.attribute('gradientTransform').hasValue()) { - // render as transformed pattern on temporary canvas - var rootView = svg.ViewPort.viewPorts[0]; - - var rect = new svg.Element.rect(); - rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0); - rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0); - rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS); - rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS); - - var group = new svg.Element.g(); - group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value); - group.children = [ rect ]; - - var tempSvg = new svg.Element.svg(); - tempSvg.attributes['x'] = new svg.Property('x', 0); - tempSvg.attributes['y'] = new svg.Property('y', 0); - tempSvg.attributes['width'] = new svg.Property('width', rootView.width); - tempSvg.attributes['height'] = new svg.Property('height', rootView.height); - tempSvg.children = [ group ]; - - var c = document.createElement('canvas'); - c.width = rootView.width; - c.height = rootView.height; - var tempCtx = c.getContext('2d'); - tempCtx.fillStyle = g; - tempSvg.render(tempCtx); - return tempCtx.createPattern(c, 'no-repeat'); - } - - return g; - } - } - svg.Element.GradientBase.prototype = new svg.Element.ElementBase; - - // linear gradient element - svg.Element.linearGradient = function(node) { - this.base = svg.Element.GradientBase; - this.base(node); - - this.getGradient = function(ctx, element) { - var bb = element.getBoundingBox(); - - var x1 = (this.gradientUnits == 'objectBoundingBox' - ? bb.x() + bb.width() * this.attribute('x1').numValue() - : this.attribute('x1').Length.toPixels('x')); - var y1 = (this.gradientUnits == 'objectBoundingBox' - ? bb.y() + bb.height() * this.attribute('y1').numValue() - : this.attribute('y1').Length.toPixels('y')); - var x2 = (this.gradientUnits == 'objectBoundingBox' - ? bb.x() + bb.width() * this.attribute('x2').numValue() - : this.attribute('x2').Length.toPixels('x')); - var y2 = (this.gradientUnits == 'objectBoundingBox' - ? bb.y() + bb.height() * this.attribute('y2').numValue() - : this.attribute('y2').Length.toPixels('y')); - - return ctx.createLinearGradient(x1, y1, x2, y2); - } - } - svg.Element.linearGradient.prototype = new svg.Element.GradientBase; - - // radial gradient element - svg.Element.radialGradient = function(node) { - this.base = svg.Element.GradientBase; - this.base(node); - - this.getGradient = function(ctx, element) { - var bb = element.getBoundingBox(); - - var cx = (this.gradientUnits == 'objectBoundingBox' - ? bb.x() + bb.width() * this.attribute('cx').numValue() - : this.attribute('cx').Length.toPixels('x')); - var cy = (this.gradientUnits == 'objectBoundingBox' - ? bb.y() + bb.height() * this.attribute('cy').numValue() - : this.attribute('cy').Length.toPixels('y')); - - var fx = cx; - var fy = cy; - if (this.attribute('fx').hasValue()) { - fx = (this.gradientUnits == 'objectBoundingBox' - ? bb.x() + bb.width() * this.attribute('fx').numValue() - : this.attribute('fx').Length.toPixels('x')); - } - if (this.attribute('fy').hasValue()) { - fy = (this.gradientUnits == 'objectBoundingBox' - ? bb.y() + bb.height() * this.attribute('fy').numValue() - : this.attribute('fy').Length.toPixels('y')); - } - - var r = (this.gradientUnits == 'objectBoundingBox' - ? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue() - : this.attribute('r').Length.toPixels()); - - return ctx.createRadialGradient(fx, fy, 0, cx, cy, r); - } - } - svg.Element.radialGradient.prototype = new svg.Element.GradientBase; - - // gradient stop element - svg.Element.stop = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.offset = this.attribute('offset').numValue(); - - var stopColor = this.style('stop-color'); - if (this.style('stop-opacity').hasValue()) stopColor = stopColor.Color.addOpacity(this.style('stop-opacity').value); - this.color = stopColor.value; - } - svg.Element.stop.prototype = new svg.Element.ElementBase; - - // animation base element - svg.Element.AnimateBase = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - svg.Animations.push(this); - - this.duration = 0.0; - this.begin = this.attribute('begin').Time.toMilliseconds(); - this.maxDuration = this.begin + this.attribute('dur').Time.toMilliseconds(); - - this.getProperty = function() { - var attributeType = this.attribute('attributeType').value; - var attributeName = this.attribute('attributeName').value; - - if (attributeType == 'CSS') { - return this.parent.style(attributeName, true); - } - return this.parent.attribute(attributeName, true); - }; - - this.initialValue = null; - this.removed = false; - - this.calcValue = function() { - // OVERRIDE ME! - return ''; - } - - this.update = function(delta) { - // set initial value - if (this.initialValue == null) { - this.initialValue = this.getProperty().value; - } - - // if we're past the end time - if (this.duration > this.maxDuration) { - // loop for indefinitely repeating animations - if (this.attribute('repeatCount').value == 'indefinite') { - this.duration = 0.0 - } - else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) { - this.removed = true; - this.getProperty().value = this.initialValue; - return true; - } - else { - return false; // no updates made - } - } - this.duration = this.duration + delta; - - // if we're past the begin time - var updated = false; - if (this.begin < this.duration) { - var newValue = this.calcValue(); // tween - - if (this.attribute('type').hasValue()) { - // for transform, etc. - var type = this.attribute('type').value; - newValue = type + '(' + newValue + ')'; - } - - this.getProperty().value = newValue; - updated = true; - } - - return updated; - } - - // fraction of duration we've covered - this.progress = function() { - return ((this.duration - this.begin) / (this.maxDuration - this.begin)); - } - } - svg.Element.AnimateBase.prototype = new svg.Element.ElementBase; - - // animate element - svg.Element.animate = function(node) { - this.base = svg.Element.AnimateBase; - this.base(node); - - this.calcValue = function() { - var from = this.attribute('from').numValue(); - var to = this.attribute('to').numValue(); - - // tween value linearly - return from + (to - from) * this.progress(); - }; - } - svg.Element.animate.prototype = new svg.Element.AnimateBase; - - // animate color element - svg.Element.animateColor = function(node) { - this.base = svg.Element.AnimateBase; - this.base(node); - - this.calcValue = function() { - var from = new RGBColor(this.attribute('from').value); - var to = new RGBColor(this.attribute('to').value); - - if (from.ok && to.ok) { - // tween color linearly - var r = from.r + (to.r - from.r) * this.progress(); - var g = from.g + (to.g - from.g) * this.progress(); - var b = from.b + (to.b - from.b) * this.progress(); - return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')'; - } - return this.attribute('from').value; - }; - } - svg.Element.animateColor.prototype = new svg.Element.AnimateBase; - - // animate transform element - svg.Element.animateTransform = function(node) { - this.base = svg.Element.animate; - this.base(node); - } - svg.Element.animateTransform.prototype = new svg.Element.animate; - - // font element - svg.Element.font = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.horizAdvX = this.attribute('horiz-adv-x').numValue(); - - this.isRTL = false; - this.isArabic = false; - this.fontFace = null; - this.missingGlyph = null; - this.glyphs = []; - for (var i=0; i<this.children.length; i++) { - var child = this.children[i]; - if (child.type == 'font-face') { - this.fontFace = child; - if (child.style('font-family').hasValue()) { - svg.Definitions[child.style('font-family').value] = this; - } - } - else if (child.type == 'missing-glyph') this.missingGlyph = child; - else if (child.type == 'glyph') { - if (child.arabicForm != '') { - this.isRTL = true; - this.isArabic = true; - if (typeof(this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = []; - this.glyphs[child.unicode][child.arabicForm] = child; - } - else { - this.glyphs[child.unicode] = child; - } - } - } - } - svg.Element.font.prototype = new svg.Element.ElementBase; - - // font-face element - svg.Element.fontface = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.ascent = this.attribute('ascent').value; - this.descent = this.attribute('descent').value; - this.unitsPerEm = this.attribute('units-per-em').numValue(); - } - svg.Element.fontface.prototype = new svg.Element.ElementBase; - - // missing-glyph element - svg.Element.missingglyph = function(node) { - this.base = svg.Element.path; - this.base(node); - - this.horizAdvX = 0; - } - svg.Element.missingglyph.prototype = new svg.Element.path; - - // glyph element - svg.Element.glyph = function(node) { - this.base = svg.Element.path; - this.base(node); - - this.horizAdvX = this.attribute('horiz-adv-x').numValue(); - this.unicode = this.attribute('unicode').value; - this.arabicForm = this.attribute('arabic-form').value; - } - svg.Element.glyph.prototype = new svg.Element.path; - - // text element - svg.Element.text = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - if (node != null) { - // add children - this.children = []; - for (var i=0; i<node.childNodes.length; i++) { - var childNode = node.childNodes[i]; - if (childNode.nodeType == 1) { // capture tspan and tref nodes - this.addChild(childNode, true); - } - else if (childNode.nodeType == 3) { // capture text - this.addChild(new svg.Element.tspan(childNode), false); - } - } - } - - this.baseSetContext = this.setContext; - this.setContext = function(ctx) { - this.baseSetContext(ctx); - if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value; - if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value; - } - - this.renderChildren = function(ctx) { - var textAnchor = this.style('text-anchor').valueOrDefault('start'); - var x = this.attribute('x').Length.toPixels('x'); - var y = this.attribute('y').Length.toPixels('y'); - for (var i=0; i<this.children.length; i++) { - var child = this.children[i]; - - if (child.attribute('x').hasValue()) { - child.x = child.attribute('x').Length.toPixels('x'); - } - else { - if (child.attribute('dx').hasValue()) x += child.attribute('dx').Length.toPixels('x'); - child.x = x; - } - - var childLength = child.measureText(ctx); - if (textAnchor != 'start' && (i==0 || child.attribute('x').hasValue())) { // new group? - // loop through rest of children - var groupLength = childLength; - for (var j=i+1; j<this.children.length; j++) { - var childInGroup = this.children[j]; - if (childInGroup.attribute('x').hasValue()) break; // new group - groupLength += childInGroup.measureText(ctx); - } - child.x -= (textAnchor == 'end' ? groupLength : groupLength / 2.0); - } - x = child.x + childLength; - - if (child.attribute('y').hasValue()) { - child.y = child.attribute('y').Length.toPixels('y'); - } - else { - if (child.attribute('dy').hasValue()) y += child.attribute('dy').Length.toPixels('y'); - child.y = y; - } - y = child.y; - - child.render(ctx); - } - } - } - svg.Element.text.prototype = new svg.Element.RenderedElementBase; - - // text base - svg.Element.TextElementBase = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.getGlyph = function(font, text, i) { - var c = text[i]; - var glyph = null; - if (font.isArabic) { - var arabicForm = 'isolated'; - if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal'; - if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial'; - if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial'; - if (typeof(font.glyphs[c]) != 'undefined') { - glyph = font.glyphs[c][arabicForm]; - if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c]; - } - } - else { - glyph = font.glyphs[c]; - } - if (glyph == null) glyph = font.missingGlyph; - return glyph; - } - - this.renderChildren = function(ctx) { - var customFont = this.parent.style('font-family').Definition.getDefinition(); - if (customFont != null) { - var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); - var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle); - var text = this.getText(); - if (customFont.isRTL) text = text.split("").reverse().join(""); - - var dx = svg.ToNumberArray(this.parent.attribute('dx').value); - for (var i=0; i<text.length; i++) { - var glyph = this.getGlyph(customFont, text, i); - var scale = fontSize / customFont.fontFace.unitsPerEm; - ctx.translate(this.x, this.y); - ctx.scale(scale, -scale); - var lw = ctx.lineWidth; - ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize; - if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0); - glyph.render(ctx); - if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0); - ctx.lineWidth = lw; - ctx.scale(1/scale, -1/scale); - ctx.translate(-this.x, -this.y); - - this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm; - if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) { - this.x += dx[i]; - } - } - return; - } - - if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y); - if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y); - } - - this.getText = function() { - // OVERRIDE ME - } - - this.measureText = function(ctx) { - var customFont = this.parent.style('font-family').Definition.getDefinition(); - if (customFont != null) { - var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); - var measure = 0; - var text = this.getText(); - if (customFont.isRTL) text = text.split("").reverse().join(""); - var dx = svg.ToNumberArray(this.parent.attribute('dx').value); - for (var i=0; i<text.length; i++) { - var glyph = this.getGlyph(customFont, text, i); - measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm; - if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) { - measure += dx[i]; - } - } - return measure; - } - - var textToMeasure = svg.compressSpaces(this.getText()); - if (!ctx.measureText) return textToMeasure.length * 10; - - ctx.save(); - this.setContext(ctx); - var width = ctx.measureText(textToMeasure).width; - ctx.restore(); - return width; - } - } - svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase; - - // tspan - svg.Element.tspan = function(node) { - this.base = svg.Element.TextElementBase; - this.base(node); - - this.text = node.nodeType == 3 ? node.nodeValue : // text - node.childNodes.length > 0 ? node.childNodes[0].nodeValue : // element - node.text; - this.getText = function() { - return this.text; - } - } - svg.Element.tspan.prototype = new svg.Element.TextElementBase; - - // tref - svg.Element.tref = function(node) { - this.base = svg.Element.TextElementBase; - this.base(node); - - this.getText = function() { - var element = this.attribute('xlink:href').Definition.getDefinition(); - if (element != null) return element.children[0].getText(); - } - } - svg.Element.tref.prototype = new svg.Element.TextElementBase; - - // a element - svg.Element.a = function(node) { - this.base = svg.Element.TextElementBase; - this.base(node); - - this.hasText = true; - for (var i=0; i<node.childNodes.length; i++) { - if (node.childNodes[i].nodeType != 3) this.hasText = false; - } - - // this might contain text - this.text = this.hasText ? node.childNodes[0].nodeValue : ''; - this.getText = function() { - return this.text; - } - - this.baseRenderChildren = this.renderChildren; - this.renderChildren = function(ctx) { - if (this.hasText) { - // render as text element - this.baseRenderChildren(ctx); - var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize); - svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.Length.toPixels('y'), this.x + this.measureText(ctx), this.y)); - } - else { - // render as temporary group - var g = new svg.Element.g(); - g.children = this.children; - g.parent = this; - g.render(ctx); - } - } - - this.onclick = function() { - window.open(this.attribute('xlink:href').value); - } - - this.onmousemove = function() { - svg.ctx.canvas.style.cursor = 'pointer'; - } - } - svg.Element.a.prototype = new svg.Element.TextElementBase; - - // image element - svg.Element.image = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - svg.Images.push(this); - this.img = document.createElement('img'); - this.loaded = false; - var that = this; - this.img.onload = function() { that.loaded = true; } - this.img.src = this.attribute('xlink:href').value; - - this.renderChildren = function(ctx) { - var x = this.attribute('x').Length.toPixels('x'); - var y = this.attribute('y').Length.toPixels('y'); - - var width = this.attribute('width').Length.toPixels('x'); - var height = this.attribute('height').Length.toPixels('y'); - if (width == 0 || height == 0) return; - - ctx.save(); - ctx.translate(x, y); - svg.AspectRatio(ctx, - this.attribute('preserveAspectRatio').value, - width, - this.img.width, - height, - this.img.height, - 0, - 0); - ctx.drawImage(this.img, 0, 0); - ctx.restore(); - } - } - svg.Element.image.prototype = new svg.Element.RenderedElementBase; - - // group element - svg.Element.g = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.getBoundingBox = function() { - var bb = new svg.BoundingBox(); - for (var i=0; i<this.children.length; i++) { - bb.addBoundingBox(this.children[i].getBoundingBox()); - } - return bb; - }; - } - svg.Element.g.prototype = new svg.Element.RenderedElementBase; - - // symbol element - svg.Element.symbol = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.baseSetContext = this.setContext; - this.setContext = function(ctx) { - this.baseSetContext(ctx); - - // viewbox - if (this.attribute('viewBox').hasValue()) { - var viewBox = svg.ToNumberArray(this.attribute('viewBox').value); - var minX = viewBox[0]; - var minY = viewBox[1]; - width = viewBox[2]; - height = viewBox[3]; - - svg.AspectRatio(ctx, - this.attribute('preserveAspectRatio').value, - this.attribute('width').Length.toPixels('x'), - width, - this.attribute('height').Length.toPixels('y'), - height, - minX, - minY); - - svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]); - } - } - } - svg.Element.symbol.prototype = new svg.Element.RenderedElementBase; - - // style element - svg.Element.style = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - // text, or spaces then CDATA - var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : ''); - css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments - css = svg.compressSpaces(css); // replace whitespace - var cssDefs = css.split('}'); - for (var i=0; i<cssDefs.length; i++) { - if (svg.trim(cssDefs[i]) != '') { - var cssDef = cssDefs[i].split('{'); - var cssClasses = cssDef[0].split(','); - var cssProps = cssDef[1].split(';'); - for (var j=0; j<cssClasses.length; j++) { - var cssClass = svg.trim(cssClasses[j]); - if (cssClass != '') { - var props = {}; - for (var k=0; k<cssProps.length; k++) { - var prop = cssProps[k].indexOf(':'); - var name = cssProps[k].substr(0, prop); - var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop); - if (name != null && value != null) { - props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value)); - } - } - svg.Styles[cssClass] = props; - if (cssClass == '@font-face') { - var fontFamily = props['font-family'].value.replace(/"/g,''); - var srcs = props['src'].value.split(','); - for (var s=0; s<srcs.length; s++) { - if (srcs[s].indexOf('format("svg")') > 0) { - var urlStart = srcs[s].indexOf('url'); - var urlEnd = srcs[s].indexOf(')', urlStart); - var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6); - var doc = svg.parseXml(svg.ajax(url)); - var fonts = doc.getElementsByTagName('font'); - for (var f=0; f<fonts.length; f++) { - var font = svg.CreateElement(fonts[f]); - svg.Definitions[fontFamily] = font; - } - } - } - } - } - } - } - } - } - svg.Element.style.prototype = new svg.Element.ElementBase; - - // use element - svg.Element.use = function(node) { - this.base = svg.Element.RenderedElementBase; - this.base(node); - - this.baseSetContext = this.setContext; - this.setContext = function(ctx) { - this.baseSetContext(ctx); - if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0); - if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y')); - } - - this.getDefinition = function() { - var element = this.attribute('xlink:href').Definition.getDefinition(); - if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value; - if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value; - return element; - } - - this.path = function(ctx) { - var element = this.getDefinition(); - if (element != null) element.path(ctx); - } - - this.renderChildren = function(ctx) { - var element = this.getDefinition(); - if (element != null) element.render(ctx); - } - } - svg.Element.use.prototype = new svg.Element.RenderedElementBase; - - // mask element - svg.Element.mask = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.apply = function(ctx, element) { - // render as temp svg - var x = this.attribute('x').Length.toPixels('x'); - var y = this.attribute('y').Length.toPixels('y'); - var width = this.attribute('width').Length.toPixels('x'); - var height = this.attribute('height').Length.toPixels('y'); - - // temporarily remove mask to avoid recursion - var mask = element.attribute('mask').value; - element.attribute('mask').value = ''; - - var cMask = document.createElement('canvas'); - cMask.width = x + width; - cMask.height = y + height; - var maskCtx = cMask.getContext('2d'); - this.renderChildren(maskCtx); - - var c = document.createElement('canvas'); - c.width = x + width; - c.height = y + height; - var tempCtx = c.getContext('2d'); - element.render(tempCtx); - tempCtx.globalCompositeOperation = 'destination-in'; - tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat'); - tempCtx.fillRect(0, 0, x + width, y + height); - - ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat'); - ctx.fillRect(0, 0, x + width, y + height); - - // reassign mask - element.attribute('mask').value = mask; - } - - this.render = function(ctx) { - // NO RENDER - } - } - svg.Element.mask.prototype = new svg.Element.ElementBase; - - // clip element - svg.Element.clipPath = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.apply = function(ctx) { - for (var i=0; i<this.children.length; i++) { - if (this.children[i].path) { - this.children[i].path(ctx); - ctx.clip(); - } - } - } - - this.render = function(ctx) { - // NO RENDER - } - } - svg.Element.clipPath.prototype = new svg.Element.ElementBase; - - // filters - svg.Element.filter = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - this.apply = function(ctx, element) { - // render as temp svg - var bb = element.getBoundingBox(); - var x = this.attribute('x').Length.toPixels('x'); - var y = this.attribute('y').Length.toPixels('y'); - if (x == 0 || y == 0) { - x = bb.x1; - y = bb.y1; - } - var width = this.attribute('width').Length.toPixels('x'); - var height = this.attribute('height').Length.toPixels('y'); - if (width == 0 || height == 0) { - width = bb.width(); - height = bb.height(); - } - - // temporarily remove filter to avoid recursion - var filter = element.style('filter').value; - element.style('filter').value = ''; - - // max filter distance - var extraPercent = .20; - var px = extraPercent * width; - var py = extraPercent * height; - - var c = document.createElement('canvas'); - c.width = width + 2*px; - c.height = height + 2*py; - var tempCtx = c.getContext('2d'); - tempCtx.translate(-x + px, -y + py); - element.render(tempCtx); - - // apply filters - for (var i=0; i<this.children.length; i++) { - this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py); - } - - // render on me - ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py); - - // reassign filter - element.style('filter', true).value = filter; - } - - this.render = function(ctx) { - // NO RENDER - } - } - svg.Element.filter.prototype = new svg.Element.ElementBase; - - svg.Element.feGaussianBlur = function(node) { - this.base = svg.Element.ElementBase; - this.base(node); - - function make_fgauss(sigma) { - sigma = Math.max(sigma, 0.01); - var len = Math.ceil(sigma * 4.0) + 1; - mask = []; - for (var i = 0; i < len; i++) { - mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma)); - } - return mask; - } - - function normalize(mask) { - var sum = 0; - for (var i = 1; i < mask.length; i++) { - sum += Math.abs(mask[i]); - } - sum = 2 * sum + Math.abs(mask[0]); - for (var i = 0; i < mask.length; i++) { - mask[i] /= sum; - } - return mask; - } - - function convolve_even(src, dst, mask, width, height) { - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - var a = imGet(src, x, y, width, height, 3)/255; - for (var rgba = 0; rgba < 4; rgba++) { - var sum = mask[0] * (a==0?255:imGet(src, x, y, width, height, rgba)) * (a==0||rgba==3?1:a); - for (var i = 1; i < mask.length; i++) { - var a1 = imGet(src, Math.max(x-i,0), y, width, height, 3)/255; - var a2 = imGet(src, Math.min(x+i, width-1), y, width, height, 3)/255; - sum += mask[i] * - ((a1==0?255:imGet(src, Math.max(x-i,0), y, width, height, rgba)) * (a1==0||rgba==3?1:a1) + - (a2==0?255:imGet(src, Math.min(x+i, width-1), y, width, height, rgba)) * (a2==0||rgba==3?1:a2)); - } - imSet(dst, y, x, height, width, rgba, sum); - } - } - } - } - - function imGet(img, x, y, width, height, rgba) { - return img[y*width*4 + x*4 + rgba]; - } - - function imSet(img, x, y, width, height, rgba, val) { - img[y*width*4 + x*4 + rgba] = val; - } - - function blur(ctx, width, height, sigma) - { - var srcData = ctx.getImageData(0, 0, width, height); - var mask = make_fgauss(sigma); - mask = normalize(mask); - tmp = []; - convolve_even(srcData.data, tmp, mask, width, height); - convolve_even(tmp, srcData.data, mask, height, width); - ctx.clearRect(0, 0, width, height); - ctx.putImageData(srcData, 0, 0); - } - - this.apply = function(ctx, x, y, width, height) { - // assuming x==0 && y==0 for now - blur(ctx, width, height, this.attribute('stdDeviation').numValue()); - } - } - svg.Element.filter.prototype = new svg.Element.feGaussianBlur; - - // title element, do nothing - svg.Element.title = function(node) { - } - svg.Element.title.prototype = new svg.Element.ElementBase; - - // desc element, do nothing - svg.Element.desc = function(node) { - } - svg.Element.desc.prototype = new svg.Element.ElementBase; - - svg.Element.MISSING = function(node) { - console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.'); - } - svg.Element.MISSING.prototype = new svg.Element.ElementBase; - - // element factory - svg.CreateElement = function(node) { - var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace - className = className.replace(/\-/g,''); // remove dashes - var e = null; - if (typeof(svg.Element[className]) != 'undefined') { - e = new svg.Element[className](node); - } - else { - e = new svg.Element.MISSING(node); - } - - e.type = node.nodeName; - return e; - } - - // load from url - svg.load = function(ctx, url) { - svg.loadXml(ctx, svg.ajax(url)); - } - - // load from xml - svg.loadXml = function(ctx, xml) { - svg.loadXmlDoc(ctx, svg.parseXml(xml)); - } - - svg.loadXmlDoc = function(ctx, dom) { - svg.init(ctx); - - var mapXY = function(p) { - var e = ctx.canvas; - while (e) { - p.x -= e.offsetLeft; - p.y -= e.offsetTop; - e = e.offsetParent; - } - if (window.scrollX) p.x += window.scrollX; - if (window.scrollY) p.y += window.scrollY; - return p; - } - - // bind mouse - if (svg.opts['ignoreMouse'] != true) { - ctx.canvas.onclick = function(e) { - var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY)); - svg.Mouse.onclick(p.x, p.y); - }; - ctx.canvas.onmousemove = function(e) { - var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY)); - svg.Mouse.onmousemove(p.x, p.y); - }; - } - - var e = svg.CreateElement(dom.documentElement); - e.root = true; - - // render loop - var isFirstRender = true; - var draw = function() { - svg.ViewPort.Clear(); - if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight); - - if (svg.opts['ignoreDimensions'] != true) { - // set canvas size - if (e.style('width').hasValue()) { - ctx.canvas.width = e.style('width').Length.toPixels('x'); - ctx.canvas.style.width = ctx.canvas.width + 'px'; - } - if (e.style('height').hasValue()) { - ctx.canvas.height = e.style('height').Length.toPixels('y'); - ctx.canvas.style.height = ctx.canvas.height + 'px'; - } - } - var cWidth = ctx.canvas.clientWidth || ctx.canvas.width; - var cHeight = ctx.canvas.clientHeight || ctx.canvas.height; - svg.ViewPort.SetCurrent(cWidth, cHeight); - - if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX']; - if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY']; - if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) { - var xRatio = 1, yRatio = 1; - if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth']; - if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight']; - - e.attribute('width', true).value = svg.opts['scaleWidth']; - e.attribute('height', true).value = svg.opts['scaleHeight']; - e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio); - e.attribute('preserveAspectRatio', true).value = 'none'; - } - - // clear and render - if (svg.opts['ignoreClear'] != true) { - ctx.clearRect(0, 0, cWidth, cHeight); - } - e.render(ctx); - if (isFirstRender) { - isFirstRender = false; - if (svg.opts != null && typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback'](); - } - } - - var waitingForImages = true; - if (svg.ImagesLoaded()) { - waitingForImages = false; - draw(); - } - svg.intervalID = setInterval(function() { - var needUpdate = false; - - if (waitingForImages && svg.ImagesLoaded()) { - waitingForImages = false; - needUpdate = true; - } - - // need update from mouse events? - if (svg.opts['ignoreMouse'] != true) { - needUpdate = needUpdate | svg.Mouse.hasEvents(); - } - - // need update from animations? - if (svg.opts['ignoreAnimation'] != true) { - for (var i=0; i<svg.Animations.length; i++) { - needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE); - } - } - - // need update from redraw? - if (svg.opts != null && typeof(svg.opts['forceRedraw']) == 'function') { - if (svg.opts['forceRedraw']() == true) needUpdate = true; - } - - // render if needed - if (needUpdate) { - draw(); - svg.Mouse.runEvents(); // run and clear our events - } - }, 1000 / svg.FRAMERATE); - } - - svg.stop = function() { - if (svg.intervalID) { - clearInterval(svg.intervalID); - } - } - - svg.Mouse = new (function() { - this.events = []; - this.hasEvents = function() { return this.events.length != 0; } - - this.onclick = function(x, y) { - this.events.push({ type: 'onclick', x: x, y: y, - run: function(e) { if (e.onclick) e.onclick(); } - }); - } - - this.onmousemove = function(x, y) { - this.events.push({ type: 'onmousemove', x: x, y: y, - run: function(e) { if (e.onmousemove) e.onmousemove(); } - }); - } - - this.eventElements = []; - - this.checkPath = function(element, ctx) { - for (var i=0; i<this.events.length; i++) { - var e = this.events[i]; - if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element; - } - } - - this.checkBoundingBox = function(element, bb) { - for (var i=0; i<this.events.length; i++) { - var e = this.events[i]; - if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element; - } - } - - this.runEvents = function() { - svg.ctx.canvas.style.cursor = ''; - - for (var i=0; i<this.events.length; i++) { - var e = this.events[i]; - var element = this.eventElements[i]; - while (element) { - e.run(element); - element = element.parent; - } - } - - // done running, clear - this.events = []; - this.eventElements = []; - } - }); - - return svg; - } -})(); - -if (CanvasRenderingContext2D) { - CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) { - canvg(this.canvas, s, { - ignoreMouse: true, - ignoreAnimation: true, - ignoreDimensions: true, - ignoreClear: true, - offsetX: dx, - offsetY: dy, - scaleWidth: dw, - scaleHeight: dh - }); - } -}/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * CanVGRenderer Extension module - * - * (c) 2011-2012 Torstein Honsi, Erik Olsson - * - * License: www.highcharts.com/license - */ - -// JSLint options: -/*global Highcharts */ - -(function (Highcharts) { // encapsulate - var UNDEFINED, - DIV = 'div', - ABSOLUTE = 'absolute', - RELATIVE = 'relative', - HIDDEN = 'hidden', - VISIBLE = 'visible', - PX = 'px', - css = Highcharts.css, - CanVGRenderer = Highcharts.CanVGRenderer, - SVGRenderer = Highcharts.SVGRenderer, - extend = Highcharts.extend, - merge = Highcharts.merge, - addEvent = Highcharts.addEvent, - createElement = Highcharts.createElement, - discardElement = Highcharts.discardElement; - - // Extend CanVG renderer on demand, inherit from SVGRenderer - extend(CanVGRenderer.prototype, SVGRenderer.prototype); - - // Add additional functionality: - extend(CanVGRenderer.prototype, { - create: function (chart, container, chartWidth, chartHeight) { - this.setContainer(container, chartWidth, chartHeight); - this.configure(chart); - }, - setContainer: function (container, chartWidth, chartHeight) { - var containerStyle = container.style, - containerParent = container.parentNode, - containerLeft = containerStyle.left, - containerTop = containerStyle.top, - containerOffsetWidth = container.offsetWidth, - containerOffsetHeight = container.offsetHeight, - canvas, - initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE }; - - this.init.apply(this, [container, chartWidth, chartHeight]); - - // add the canvas above it - canvas = createElement('canvas', { - width: containerOffsetWidth, - height: containerOffsetHeight - }, { - position: RELATIVE, - left: containerLeft, - top: containerTop - }, container); - this.canvas = canvas; - - // Create the tooltip line and div, they are placed as siblings to - // the container (and as direct childs to the div specified in the html page) - this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent); - this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent); - this.ttTimer = UNDEFINED; - - // Move away the svg node to a new div inside the container's parent so we can hide it. - var hiddenSvg = createElement(DIV, { - width: containerOffsetWidth, - height: containerOffsetHeight - }, { - visibility: HIDDEN, - left: containerLeft, - top: containerTop - }, containerParent); - this.hiddenSvg = hiddenSvg; - hiddenSvg.appendChild(this.box); - }, - - /** - * Configures the renderer with the chart. Attach a listener to the event tooltipRefresh. - **/ - configure: function (chart) { - var renderer = this, - options = chart.options.tooltip, - borderWidth = options.borderWidth, - tooltipDiv = renderer.ttDiv, - tooltipDivStyle = options.style, - tooltipLine = renderer.ttLine, - padding = parseInt(tooltipDivStyle.padding, 10); - - // Add border styling from options to the style - tooltipDivStyle = merge(tooltipDivStyle, { - padding: padding + PX, - 'background-color': options.backgroundColor, - 'border-style': 'solid', - 'border-width': borderWidth + PX, - 'border-radius': options.borderRadius + PX - }); - - // Optionally add shadow - if (options.shadow) { - tooltipDivStyle = merge(tooltipDivStyle, { - 'box-shadow': '1px 1px 3px gray', // w3c - '-webkit-box-shadow': '1px 1px 3px gray' // webkit - }); - } - css(tooltipDiv, tooltipDivStyle); - - // Set simple style on the line - css(tooltipLine, { - 'border-left': '1px solid darkgray' - }); - - // This event is triggered when a new tooltip should be shown - addEvent(chart, 'tooltipRefresh', function (args) { - var chartContainer = chart.container, - offsetLeft = chartContainer.offsetLeft, - offsetTop = chartContainer.offsetTop, - position; - - // Set the content of the tooltip - tooltipDiv.innerHTML = args.text; - - // Compute the best position for the tooltip based on the divs size and container size. - position = chart.tooltip.getPosition(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, {plotX: args.x, plotY: args.y}); - - css(tooltipDiv, { - visibility: VISIBLE, - left: position.x + PX, - top: position.y + PX, - 'border-color': args.borderColor - }); - - // Position the tooltip line - css(tooltipLine, { - visibility: VISIBLE, - left: offsetLeft + args.x + PX, - top: offsetTop + chart.plotTop + PX, - height: chart.plotHeight + PX - }); - - // This timeout hides the tooltip after 3 seconds - // First clear any existing timer - if (renderer.ttTimer !== UNDEFINED) { - clearTimeout(renderer.ttTimer); - } - - // Start a new timer that hides tooltip and line - renderer.ttTimer = setTimeout(function () { - css(tooltipDiv, { visibility: HIDDEN }); - css(tooltipLine, { visibility: HIDDEN }); - }, 3000); - }); - }, - - /** - * Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer. - */ - destroy: function () { - var renderer = this; - - // Remove the canvas - discardElement(renderer.canvas); - - // Kill the timer - if (renderer.ttTimer !== UNDEFINED) { - clearTimeout(renderer.ttTimer); - } - - // Remove the divs for tooltip and line - discardElement(renderer.ttLine); - discardElement(renderer.ttDiv); - discardElement(renderer.hiddenSvg); - - // Continue with base class - return SVGRenderer.prototype.destroy.apply(renderer); - }, - - /** - * Take a color and return it if it's a string, do not make it a gradient even if it is a - * gradient. Currently canvg cannot render gradients (turns out black), - * see: http://code.google.com/p/canvg/issues/detail?id=104 - * - * @param {Object} color The color or config object - */ - color: function (color, elem, prop) { - if (color && color.linearGradient) { - // Pick the end color and forward to base implementation - color = color.stops[color.stops.length - 1][1]; - } - return SVGRenderer.prototype.color.call(this, color, elem, prop); - }, - - /** - * Draws the SVG on the canvas or adds a draw invokation to the deferred list. - */ - draw: function () { - var renderer = this; - window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML); - } - }); -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/data.js b/pykeg/web/static/highcharts/js/modules/data.js deleted file mode 100644 index fef9b924d..000000000 --- a/pykeg/web/static/highcharts/js/modules/data.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - Data plugin for Highcharts - - (c) 2012-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(j){var m=j.each,o=function(b,a){this.init(b,a)};j.extend(o.prototype,{init:function(b,a){this.options=b;this.chartOptions=a;this.columns=b.columns||this.rowsToColumns(b.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},getColumnDistribution:function(){var b=this.chartOptions,a=b&&b.chart&&b.chart.type,c=[];m(b&&b.series||[],function(b){c.push((j.seriesTypes[b.type||a||"line"].prototype.pointArrayMap||[0]).length)});this.valueCount= -{global:(j.seriesTypes[a||"line"].prototype.pointArrayMap||[0]).length,individual:c}},dataFound:function(){if(this.options.switchRowsAndColumns)this.columns=this.rowsToColumns(this.columns);this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var b=this,a=this.options,c=a.csv,d=this.columns,f=a.startRow||0,h=a.endRow||Number.MAX_VALUE,i=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g,k,j=0;c&&(k=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(a.lineDelimiter|| -"\n"),g=a.itemDelimiter||(c.indexOf("\t")!==-1?"\t":","),m(k,function(a,c){var k=b.trim(a),n=k.indexOf("#")===0;c>=f&&c<=h&&!n&&k!==""&&(k=a.split(g),m(k,function(b,a){a>=i&&a<=e&&(d[a-i]||(d[a-i]=[]),d[a-i][j]=b)}),j+=1)}),this.dataFound())},parseTable:function(){var b=this.options,a=b.table,c=this.columns,d=b.startRow||0,f=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,i=b.endColumn||Number.MAX_VALUE,e;a&&(typeof a==="string"&&(a=document.getElementById(a)),m(a.getElementsByTagName("tr"),function(a, -b){e=0;b>=d&&b<=f&&m(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&e>=h&&e<=i)c[e]||(c[e]=[]),c[e][b-d]=a.innerHTML,e+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var b=this,a=this.options,c=a.googleSpreadsheetKey,d=this.columns,f=a.startRow||0,h=a.endRow||Number.MAX_VALUE,i=a.startColumn||0,e=a.endColumn||Number.MAX_VALUE,g,k;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(a.googleSpreadsheetWorksheet||"od6")+"/public/values?alt=json-in-script&callback=?", -function(a){var a=a.feed.entry,c,j=a.length,m=0,n=0,l;for(l=0;l<j;l++)c=a[l],m=Math.max(m,c.gs$cell.col),n=Math.max(n,c.gs$cell.row);for(l=0;l<m;l++)if(l>=i&&l<=e)d[l-i]=[],d[l-i].length=Math.min(n,h-f);for(l=0;l<j;l++)if(c=a[l],g=c.gs$cell.row-1,k=c.gs$cell.col-1,k>=i&&k<=e&&g>=f&&g<=h)d[k-i][g-f]=c.content.$t;b.dataFound()})},findHeaderRow:function(){m(this.columns,function(){});this.headerRow=0},trim:function(b){return typeof b==="string"?b.replace(/^\s+|\s+$/g,""):b},parseTypes:function(){for(var b= -this.columns,a=b.length,c,d,f,h;a--;)for(c=b[a].length;c--;)d=b[a][c],f=parseFloat(d),h=this.trim(d),h==f?(b[a][c]=f,f>31536E6?b[a].isDatetime=!0:b[a].isNumeric=!0):(d=this.parseDate(d),a===0&&typeof d==="number"&&!isNaN(d)?(b[a][c]=d,b[a].isDatetime=!0):b[a][c]=h===""?null:h)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(b){return Date.UTC(+b[1],b[2]-1,+b[3])}}},parseDate:function(b){var a=this.options.parseDate,c,d,f;a&&(c=a(b));if(typeof b==="string")for(d in this.dateFormats)a= -this.dateFormats[d],(f=b.match(a.regex))&&(c=a.parser(f));return c},rowsToColumns:function(b){var a,c,d,f,h;if(b){h=[];c=b.length;for(a=0;a<c;a++){f=b[a].length;for(d=0;d<f;d++)h[d]||(h[d]=[]),h[d][a]=b[a][d]}}return h},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var b=this.columns,a,c,d=this.options,f,h,i,e,g,k;if(d.complete){this.getColumnDistribution();b.length>1&&(a=b.shift(),this.headerRow===0&&a.shift(),a.isDatetime?c="datetime":a.isNumeric|| -(c="category"));for(e=0;e<b.length;e++)if(this.headerRow===0)b[e].name=b[e].shift();h=[];for(e=0,k=0;e<b.length;k++){f=j.pick(this.valueCount.individual[k],this.valueCount.global);i=[];for(g=0;g<b[e].length;g++)i[g]=[a[g],b[e][g]!==void 0?b[e][g]:null],f>1&&i[g].push(b[e+1][g]!==void 0?b[e+1][g]:null),f>2&&i[g].push(b[e+2][g]!==void 0?b[e+2][g]:null),f>3&&i[g].push(b[e+3][g]!==void 0?b[e+3][g]:null),f>4&&i[g].push(b[e+4][g]!==void 0?b[e+4][g]:null);h[k]={name:b[e].name,data:i};e+=f}d.complete({xAxis:{type:c}, -series:h})}}});j.Data=o;j.data=function(b,a){return new o(b,a)};j.wrap(j.Chart.prototype,"init",function(b,a,c){var d=this;a&&a.data?j.data(j.extend(a.data,{complete:function(f){a.series&&m(a.series,function(b,c){a.series[c]=j.merge(b,f.series[c])});a=j.merge(f,a);b.call(d,a,c)}}),a):b.call(d,a,c)})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/data.src.js b/pykeg/web/static/highcharts/js/modules/data.src.js deleted file mode 100644 index ee79e0e5b..000000000 --- a/pykeg/web/static/highcharts/js/modules/data.src.js +++ /dev/null @@ -1,598 +0,0 @@ -/** - * @license Data plugin for Highcharts - * - * (c) 2012-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -/* - * The Highcharts Data plugin is a utility to ease parsing of input sources like - * CSV, HTML tables or grid views into basic configuration options for use - * directly in the Highcharts constructor. - * - * Demo: http://jsfiddle.net/highcharts/SnLFj/ - * - * --- OPTIONS --- - * - * - columns : Array<Array<Mixed>> - * A two-dimensional array representing the input data on tabular form. This input can - * be used when the data is already parsed, for example from a grid view component. - * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns - * are interpreted as series. See also the rows option. - * - * - complete : Function(chartOptions) - * The callback that is evaluated when the data is finished loading, optionally from an - * external source, and parsed. The first argument passed is a finished chart options - * object, containing series and an xAxis with categories if applicable. Thise options - * can be extended with additional options and passed directly to the chart constructor. - * - * - csv : String - * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn - * and endColumn to delimit what part of the table is used. The lineDelimiter and - * itemDelimiter options define the CSV delimiter formats. - * - * - endColumn : Integer - * In tabular input data, the first row (indexed by 0) to use. Defaults to the last - * column containing data. - * - * - endRow : Integer - * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row - * containing data. - * - * - googleSpreadsheetKey : String - * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample - * for general information on GS. - * - * - googleSpreadsheetWorksheet : String - * The Google Spreadsheet worksheet. The available id's can be read from - * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic - * - * - itemDelimiter : String - * Item or cell delimiter for parsing CSV. Defaults to the tab character "\t" if a tab character - * is found in the CSV string, if not it defaults to ",". - * - * - lineDelimiter : String - * Line delimiter for parsing CSV. Defaults to "\n". - * - * - parsed : Function - * A callback function to access the parsed columns, the two-dimentional input data - * array directly, before they are interpreted into series data and categories. - * - * - parseDate : Function - * A callback function to parse string representations of dates into JavaScript timestamps. - * Return an integer on success. - * - * - rows : Array<Array<Mixed>> - * The same as the columns input option, but defining rows intead of columns. - * - * - startColumn : Integer - * In tabular input data, the first column (indexed by 0) to use. - * - * - startRow : Integer - * In tabular input data, the first row (indexed by 0) to use. - * - * - switchRowsAndColumns : Boolean - * Switch rows and columns of the input data, so that this.columns effectively becomes the - * rows of the data set, and the rows are interpreted as series. - * - * - table : String|HTMLElement - * A HTML table or the id of such to be parsed as input data. Related options ara startRow, - * endRow, startColumn and endColumn to delimit what part of the table is used. - */ - -// JSLint options: -/*global jQuery */ - -(function (Highcharts) { - - // Utilities - var each = Highcharts.each; - - - // The Data constructor - var Data = function (dataOptions, chartOptions) { - this.init(dataOptions, chartOptions); - }; - - // Set the prototype properties - Highcharts.extend(Data.prototype, { - - /** - * Initialize the Data object with the given options - */ - init: function (options, chartOptions) { - this.options = options; - this.chartOptions = chartOptions; - this.columns = options.columns || this.rowsToColumns(options.rows) || []; - - // No need to parse or interpret anything - if (this.columns.length) { - this.dataFound(); - - // Parse and interpret - } else { - - // Parse a CSV string if options.csv is given - this.parseCSV(); - - // Parse a HTML table if options.table is given - this.parseTable(); - - // Parse a Google Spreadsheet - this.parseGoogleSpreadsheet(); - } - - }, - - /** - * Get the column distribution. For example, a line series takes a single column for - * Y values. A range series takes two columns for low and high values respectively, - * and an OHLC series takes four columns. - */ - getColumnDistribution: function () { - var chartOptions = this.chartOptions, - getValueCount = function (type) { - return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length; - }, - globalType = chartOptions && chartOptions.chart && chartOptions.chart.type, - individualCounts = []; - - each((chartOptions && chartOptions.series) || [], function (series) { - individualCounts.push(getValueCount(series.type || globalType)); - }); - - this.valueCount = { - global: getValueCount(globalType), - individual: individualCounts - }; - }, - - /** - * When the data is parsed into columns, either by CSV, table, GS or direct input, - * continue with other operations. - */ - dataFound: function () { - - if (this.options.switchRowsAndColumns) { - this.columns = this.rowsToColumns(this.columns); - } - - // Interpret the values into right types - this.parseTypes(); - - // Use first row for series names? - this.findHeaderRow(); - - // Handle columns if a handleColumns callback is given - this.parsed(); - - // Complete if a complete callback is given - this.complete(); - - }, - - /** - * Parse a CSV input string - */ - parseCSV: function () { - var self = this, - options = this.options, - csv = options.csv, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE, - itemDelimiter, - lines, - activeRowNo = 0; - - if (csv) { - - lines = csv - .replace(/\r\n/g, "\n") // Unix - .replace(/\r/g, "\n") // Mac - .split(options.lineDelimiter || "\n"); - - itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ','); - - each(lines, function (line, rowNo) { - var trimmed = self.trim(line), - isComment = trimmed.indexOf('#') === 0, - isBlank = trimmed === '', - items; - - if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { - items = line.split(itemDelimiter); - each(items, function (item, colNo) { - if (colNo >= startColumn && colNo <= endColumn) { - if (!columns[colNo - startColumn]) { - columns[colNo - startColumn] = []; - } - - columns[colNo - startColumn][activeRowNo] = item; - } - }); - activeRowNo += 1; - } - }); - - this.dataFound(); - } - }, - - /** - * Parse a HTML table - */ - parseTable: function () { - var options = this.options, - table = options.table, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE, - colNo; - - if (table) { - - if (typeof table === 'string') { - table = document.getElementById(table); - } - - each(table.getElementsByTagName('tr'), function (tr, rowNo) { - colNo = 0; - if (rowNo >= startRow && rowNo <= endRow) { - each(tr.childNodes, function (item) { - if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { - if (!columns[colNo]) { - columns[colNo] = []; - } - columns[colNo][rowNo - startRow] = item.innerHTML; - - colNo += 1; - } - }); - } - }); - - this.dataFound(); // continue - } - }, - - /** - */ - parseGoogleSpreadsheet: function () { - var self = this, - options = this.options, - googleSpreadsheetKey = options.googleSpreadsheetKey, - columns = this.columns, - startRow = options.startRow || 0, - endRow = options.endRow || Number.MAX_VALUE, - startColumn = options.startColumn || 0, - endColumn = options.endColumn || Number.MAX_VALUE, - gr, // google row - gc; // google column - - if (googleSpreadsheetKey) { - jQuery.getJSON('https://spreadsheets.google.com/feeds/cells/' + - googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + - '/public/values?alt=json-in-script&callback=?', - function (json) { - - // Prepare the data from the spreadsheat - var cells = json.feed.entry, - cell, - cellCount = cells.length, - colCount = 0, - rowCount = 0, - i; - - // First, find the total number of columns and rows that - // are actually filled with data - for (i = 0; i < cellCount; i++) { - cell = cells[i]; - colCount = Math.max(colCount, cell.gs$cell.col); - rowCount = Math.max(rowCount, cell.gs$cell.row); - } - - // Set up arrays containing the column data - for (i = 0; i < colCount; i++) { - if (i >= startColumn && i <= endColumn) { - // Create new columns with the length of either end-start or rowCount - columns[i - startColumn] = []; - - // Setting the length to avoid jslint warning - columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); - } - } - - // Loop over the cells and assign the value to the right - // place in the column arrays - for (i = 0; i < cellCount; i++) { - cell = cells[i]; - gr = cell.gs$cell.row - 1; // rows start at 1 - gc = cell.gs$cell.col - 1; // columns start at 1 - - // If both row and col falls inside start and end - // set the transposed cell value in the newly created columns - if (gc >= startColumn && gc <= endColumn && - gr >= startRow && gr <= endRow) { - columns[gc - startColumn][gr - startRow] = cell.content.$t; - } - } - self.dataFound(); - }); - } - }, - - /** - * Find the header row. For now, we just check whether the first row contains - * numbers or strings. Later we could loop down and find the first row with - * numbers. - */ - findHeaderRow: function () { - var headerRow = 0; - each(this.columns, function (column) { - if (typeof column[0] !== 'string') { - headerRow = null; - } - }); - this.headerRow = 0; - }, - - /** - * Trim a string from whitespace - */ - trim: function (str) { - return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str; - }, - - /** - * Parse numeric cells in to number types and date types in to true dates. - */ - parseTypes: function () { - var columns = this.columns, - col = columns.length, - row, - val, - floatVal, - trimVal, - dateVal; - - while (col--) { - row = columns[col].length; - while (row--) { - val = columns[col][row]; - floatVal = parseFloat(val); - trimVal = this.trim(val); - - /*jslint eqeq: true*/ - if (trimVal == floatVal) { // is numeric - /*jslint eqeq: false*/ - columns[col][row] = floatVal; - - // If the number is greater than milliseconds in a year, assume datetime - if (floatVal > 365 * 24 * 3600 * 1000) { - columns[col].isDatetime = true; - } else { - columns[col].isNumeric = true; - } - - } else { // string, continue to determine if it is a date string or really a string - dateVal = this.parseDate(val); - - if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date - columns[col][row] = dateVal; - columns[col].isDatetime = true; - - } else { // string - columns[col][row] = trimVal === '' ? null : trimVal; - } - } - - } - } - }, - - /** - * A collection of available date formats, extendable from the outside to support - * custom date formats. - */ - dateFormats: { - 'YYYY-mm-dd': { - regex: '^([0-9]{4})-([0-9]{2})-([0-9]{2})$', - parser: function (match) { - return Date.UTC(+match[1], match[2] - 1, +match[3]); - } - } - }, - - /** - * Parse a date and return it as a number. Overridable through options.parseDate. - */ - parseDate: function (val) { - var parseDate = this.options.parseDate, - ret, - key, - format, - match; - - if (parseDate) { - ret = parseDate(val); - } - - if (typeof val === 'string') { - for (key in this.dateFormats) { - format = this.dateFormats[key]; - match = val.match(format.regex); - if (match) { - ret = format.parser(match); - } - } - } - return ret; - }, - - /** - * Reorganize rows into columns - */ - rowsToColumns: function (rows) { - var row, - rowsLength, - col, - colsLength, - columns; - - if (rows) { - columns = []; - rowsLength = rows.length; - for (row = 0; row < rowsLength; row++) { - colsLength = rows[row].length; - for (col = 0; col < colsLength; col++) { - if (!columns[col]) { - columns[col] = []; - } - columns[col][row] = rows[row][col]; - } - } - } - return columns; - }, - - /** - * A hook for working directly on the parsed columns - */ - parsed: function () { - if (this.options.parsed) { - this.options.parsed.call(this, this.columns); - } - }, - - /** - * If a complete callback function is provided in the options, interpret the - * columns into a Highcharts options object. - */ - complete: function () { - - var columns = this.columns, - firstCol, - type, - options = this.options, - valueCount, - series, - data, - i, - j, - seriesIndex; - - - if (options.complete) { - - this.getColumnDistribution(); - - // Use first column for X data or categories? - if (columns.length > 1) { - firstCol = columns.shift(); - if (this.headerRow === 0) { - firstCol.shift(); // remove the first cell - } - - - if (firstCol.isDatetime) { - type = 'datetime'; - } else if (!firstCol.isNumeric) { - type = 'category'; - } - } - - // Get the names and shift the top row - for (i = 0; i < columns.length; i++) { - if (this.headerRow === 0) { - columns[i].name = columns[i].shift(); - } - } - - // Use the next columns for series - series = []; - for (i = 0, seriesIndex = 0; i < columns.length; seriesIndex++) { - - // This series' value count - valueCount = Highcharts.pick(this.valueCount.individual[seriesIndex], this.valueCount.global); - - // Iterate down the cells of each column and add data to the series - data = []; - for (j = 0; j < columns[i].length; j++) { - data[j] = [ - firstCol[j], - columns[i][j] !== undefined ? columns[i][j] : null - ]; - if (valueCount > 1) { - data[j].push(columns[i + 1][j] !== undefined ? columns[i + 1][j] : null); - } - if (valueCount > 2) { - data[j].push(columns[i + 2][j] !== undefined ? columns[i + 2][j] : null); - } - if (valueCount > 3) { - data[j].push(columns[i + 3][j] !== undefined ? columns[i + 3][j] : null); - } - if (valueCount > 4) { - data[j].push(columns[i + 4][j] !== undefined ? columns[i + 4][j] : null); - } - } - - // Add the series - series[seriesIndex] = { - name: columns[i].name, - data: data - }; - - i += valueCount; - } - - // Do the callback - options.complete({ - xAxis: { - type: type - }, - series: series - }); - } - } - }); - - // Register the Data prototype and data function on Highcharts - Highcharts.Data = Data; - Highcharts.data = function (options, chartOptions) { - return new Data(options, chartOptions); - }; - - // Extend Chart.init so that the Chart constructor accepts a new configuration - // option group, data. - Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) { - var chart = this; - - if (userOptions && userOptions.data) { - Highcharts.data(Highcharts.extend(userOptions.data, { - complete: function (dataOptions) { - - // Merge series configs - if (userOptions.series) { - each(userOptions.series, function (series, i) { - userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); - }); - } - - // Do the merge - userOptions = Highcharts.merge(dataOptions, userOptions); - - proceed.call(chart, userOptions, callback); - } - }), userOptions); - } else { - proceed.call(chart, userOptions, callback); - } - }); - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/drilldown.js b/pykeg/web/static/highcharts/js/modules/drilldown.js deleted file mode 100644 index 5df917b35..000000000 --- a/pykeg/web/static/highcharts/js/modules/drilldown.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(e){function p(b,a,c){return"rgba("+[Math.round(b[0]+(a[0]-b[0])*c),Math.round(b[1]+(a[1]-b[1])*c),Math.round(b[2]+(a[2]-b[2])*c),b[3]+(a[3]-b[3])*c].join(",")+")"}var m=function(){},j=e.getOptions(),g=e.each,q=e.extend,n=e.wrap,h=e.Chart,i=e.seriesTypes,k=i.pie,l=i.column,r=HighchartsAdapter.fireEvent,t=HighchartsAdapter.inArray;q(j.lang,{drillUpText:"◁ Back to {series.name}"});j.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#0d233a",fontWeight:"bold",textDecoration:"underline"}, -activeDataLabelStyle:{cursor:"pointer",color:"#0d233a",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};e.SVGRenderer.prototype.Element.prototype.fadeIn=function(b){this.attr({opacity:0.1,visibility:"visible"}).animate({opacity:1},b||{duration:250})};h.prototype.drilldownLevels=[];h.prototype.addSeriesAsDrilldown=function(b,a){var c=b.series,d=c.xAxis,f=c.yAxis,e;e=b.color||c.color;var g,a=q({color:e},a);g=t(b,c.points);this.drilldownLevels.push({seriesOptions:c.userOptions, -shapeArgs:b.shapeArgs,bBox:b.graphic.getBBox(),color:e,newSeries:a,pointOptions:c.options.data[g],pointIndex:g,oldExtremes:{xMin:d&&d.userMin,xMax:d&&d.userMax,yMin:f&&f.userMin,yMax:f&&f.userMax}});e=this.addSeries(a,!1);if(d)d.oldPos=d.pos,d.userMin=d.userMax=null,f.userMin=f.userMax=null;if(c.type===e.type)e.animate=e.animateDrilldown||m,e.options.animation=!0;c.remove(!1);this.redraw();this.showDrillUpButton()};h.prototype.getDrilldownBackText=function(){return this.options.lang.drillUpText.replace("{series.name}", -this.drilldownLevels[this.drilldownLevels.length-1].seriesOptions.name)};h.prototype.showDrillUpButton=function(){var b=this,a=this.getDrilldownBackText(),c=b.options.drilldown.drillUpButton,d,f;this.drillUpButton?this.drillUpButton.attr({text:a}).align():(f=(d=c.theme)&&d.states,this.drillUpButton=this.renderer.button(a,null,null,function(){b.drillUp()},d,f&&f.hover,f&&f.select).attr({align:c.position.align,zIndex:9}).add().align(c.position,!1,c.relativeTo||"plotBox"))};h.prototype.drillUp=function(){var b= -this.drilldownLevels.pop(),a=this.series[0],c=b.oldExtremes,d=this.addSeries(b.seriesOptions,!1);r(this,"drillup",{seriesOptions:b.seriesOptions});if(d.type===a.type)d.drilldownLevel=b,d.animate=d.animateDrillupTo||m,d.options.animation=!0,a.animateDrillupFrom&&a.animateDrillupFrom(b);a.remove(!1);d.xAxis&&(d.xAxis.setExtremes(c.xMin,c.xMax,!1),d.yAxis.setExtremes(c.yMin,c.yMax,!1));this.redraw();this.drilldownLevels.length===0?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align()}; -k.prototype.animateDrilldown=function(b){var a=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],c=this.chart.options.drilldown.animation,d=a.shapeArgs,f=d.start,s=(d.end-f)/this.points.length,h=e.Color(a.color).rgba;b||g(this.points,function(a,b){var g=e.Color(a.color).rgba;a.graphic.attr(e.merge(d,{start:f+b*s,end:f+(b+1)*s})).animate(a.shapeArgs,e.merge(c,{step:function(a,d){d.prop==="start"&&this.attr({fill:p(h,g,d.pos)})}}))})};k.prototype.animateDrillupTo=l.prototype.animateDrillupTo= -function(b){if(!b){var a=this,c=a.drilldownLevel;g(this.points,function(a){a.graphic.hide();a.dataLabel&&a.dataLabel.hide();a.connector&&a.connector.hide()});setTimeout(function(){g(a.points,function(a,b){var e=b===c.pointIndex?"show":"fadeIn";a.graphic[e]();if(a.dataLabel)a.dataLabel[e]();if(a.connector)a.connector[e]()})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=m}};l.prototype.animateDrilldown=function(b){var a=this.chart.drilldownLevels[this.chart.drilldownLevels.length- -1].shapeArgs,c=this.chart.options.drilldown.animation;b||(a.x+=this.xAxis.oldPos-this.xAxis.pos,g(this.points,function(b){b.graphic.attr(a).animate(b.shapeArgs,c);b.dataLabel&&b.dataLabel.fadeIn(c)}))};l.prototype.animateDrillupFrom=k.prototype.animateDrillupFrom=function(b){var a=this.chart.options.drilldown.animation,c=this.group;delete this.group;g(this.points,function(d){var f=d.graphic,g=e.Color(d.color).rgba;delete d.graphic;f.animate(b.shapeArgs,e.merge(a,{step:function(a,c){c.prop==="start"&& -this.attr({fill:p(g,e.Color(b.color).rgba,c.pos)})},complete:function(){f.destroy();c&&(c=c.destroy())}}))})};e.Point.prototype.doDrilldown=function(){for(var b=this.series.chart,a=b.options.drilldown,c=(a.series||[]).length,d;c--&&!d;)a.series[c].id===this.drilldown&&(d=a.series[c]);r(b,"drilldown",{point:this,seriesOptions:d});d&&b.addSeriesAsDrilldown(this,d)};n(e.Point.prototype,"init",function(b,a,c,d){var f=b.call(this,a,c,d),b=a.chart,a=(a=a.xAxis&&a.xAxis.ticks[d])&&a.label;if(f.drilldown){if(e.addEvent(f, -"click",function(){f.doDrilldown()}),a){if(!a._basicStyle)a._basicStyle=a.element.getAttribute("style");a.addClass("highcharts-drilldown-axis-label").css(b.options.drilldown.activeAxisLabelStyle).on("click",function(){f.doDrilldown&&f.doDrilldown()})}}else a&&a._basicStyle&&a.element.setAttribute("style",a._basicStyle);return f});n(e.Series.prototype,"drawDataLabels",function(b){var a=this.chart.options.drilldown.activeDataLabelStyle;b.call(this);g(this.points,function(b){if(b.drilldown&&b.dataLabel)b.dataLabel.attr({"class":"highcharts-drilldown-data-label"}).css(a).on("click", -function(){b.doDrilldown()})})});l.prototype.supportsDrilldown=!0;k.prototype.supportsDrilldown=!0;var o,j=function(b){b.call(this);g(this.points,function(a){a.drilldown&&a.graphic&&a.graphic.attr({"class":"highcharts-drilldown-point"}).css({cursor:"pointer"})})};for(o in i)i[o].prototype.supportsDrilldown&&n(i[o].prototype,"drawTracker",j)})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/drilldown.src.js b/pykeg/web/static/highcharts/js/modules/drilldown.src.js deleted file mode 100644 index 711a00cec..000000000 --- a/pykeg/web/static/highcharts/js/modules/drilldown.src.js +++ /dev/null @@ -1,459 +0,0 @@ -/** - * Highcharts Drilldown plugin - * - * Author: Torstein Honsi - * License: MIT License - * - * Demo: http://jsfiddle.net/highcharts/Vf3yT/ - */ - -/*global HighchartsAdapter*/ -(function (H) { - - "use strict"; - - var noop = function () {}, - defaultOptions = H.getOptions(), - each = H.each, - extend = H.extend, - wrap = H.wrap, - Chart = H.Chart, - seriesTypes = H.seriesTypes, - PieSeries = seriesTypes.pie, - ColumnSeries = seriesTypes.column, - fireEvent = HighchartsAdapter.fireEvent, - inArray = HighchartsAdapter.inArray; - - // Utilities - function tweenColors(startColor, endColor, pos) { - var rgba = [ - Math.round(startColor[0] + (endColor[0] - startColor[0]) * pos), - Math.round(startColor[1] + (endColor[1] - startColor[1]) * pos), - Math.round(startColor[2] + (endColor[2] - startColor[2]) * pos), - startColor[3] + (endColor[3] - startColor[3]) * pos - ]; - return 'rgba(' + rgba.join(',') + ')'; - } - - // Add language - extend(defaultOptions.lang, { - drillUpText: '◁ Back to {series.name}' - }); - defaultOptions.drilldown = { - activeAxisLabelStyle: { - cursor: 'pointer', - color: '#0d233a', - fontWeight: 'bold', - textDecoration: 'underline' - }, - activeDataLabelStyle: { - cursor: 'pointer', - color: '#0d233a', - fontWeight: 'bold', - textDecoration: 'underline' - }, - animation: { - duration: 500 - }, - drillUpButton: { - position: { - align: 'right', - x: -10, - y: 10 - } - // relativeTo: 'plotBox' - // theme - } - }; - - /** - * A general fadeIn method - */ - H.SVGRenderer.prototype.Element.prototype.fadeIn = function (animation) { - this - .attr({ - opacity: 0.1, - visibility: 'visible' - }) - .animate({ - opacity: 1 - }, animation || { - duration: 250 - }); - }; - - // Extend the Chart prototype - Chart.prototype.drilldownLevels = []; - - Chart.prototype.addSeriesAsDrilldown = function (point, ddOptions) { - var oldSeries = point.series, - xAxis = oldSeries.xAxis, - yAxis = oldSeries.yAxis, - newSeries, - color = point.color || oldSeries.color, - pointIndex, - level; - - ddOptions = extend({ - color: color - }, ddOptions); - pointIndex = inArray(point, oldSeries.points); - - level = { - seriesOptions: oldSeries.userOptions, - shapeArgs: point.shapeArgs, - bBox: point.graphic.getBBox(), - color: color, - newSeries: ddOptions, - pointOptions: oldSeries.options.data[pointIndex], - pointIndex: pointIndex, - oldExtremes: { - xMin: xAxis && xAxis.userMin, - xMax: xAxis && xAxis.userMax, - yMin: yAxis && yAxis.userMin, - yMax: yAxis && yAxis.userMax - } - }; - - this.drilldownLevels.push(level); - - newSeries = this.addSeries(ddOptions, false); - if (xAxis) { - xAxis.oldPos = xAxis.pos; - xAxis.userMin = xAxis.userMax = null; - yAxis.userMin = yAxis.userMax = null; - } - - // Run fancy cross-animation on supported and equal types - if (oldSeries.type === newSeries.type) { - newSeries.animate = newSeries.animateDrilldown || noop; - newSeries.options.animation = true; - } - - oldSeries.remove(false); - - this.redraw(); - this.showDrillUpButton(); - }; - - Chart.prototype.getDrilldownBackText = function () { - var lastLevel = this.drilldownLevels[this.drilldownLevels.length - 1]; - - return this.options.lang.drillUpText.replace('{series.name}', lastLevel.seriesOptions.name); - - }; - - Chart.prototype.showDrillUpButton = function () { - var chart = this, - backText = this.getDrilldownBackText(), - buttonOptions = chart.options.drilldown.drillUpButton, - attr, - states; - - - if (!this.drillUpButton) { - attr = buttonOptions.theme; - states = attr && attr.states; - - this.drillUpButton = this.renderer.button( - backText, - null, - null, - function () { - chart.drillUp(); - }, - attr, - states && states.hover, - states && states.select - ) - .attr({ - align: buttonOptions.position.align, - zIndex: 9 - }) - .add() - .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox'); - } else { - this.drillUpButton.attr({ - text: backText - }) - .align(); - } - }; - - Chart.prototype.drillUp = function () { - var chart = this, - level = chart.drilldownLevels.pop(), - oldSeries = chart.series[0], - oldExtremes = level.oldExtremes, - newSeries = chart.addSeries(level.seriesOptions, false); - - fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions }); - - if (newSeries.type === oldSeries.type) { - newSeries.drilldownLevel = level; - newSeries.animate = newSeries.animateDrillupTo || noop; - newSeries.options.animation = true; - - if (oldSeries.animateDrillupFrom) { - oldSeries.animateDrillupFrom(level); - } - } - - oldSeries.remove(false); - - // Reset the zoom level of the upper series - if (newSeries.xAxis) { - newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false); - newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false); - } - - - this.redraw(); - - if (this.drilldownLevels.length === 0) { - this.drillUpButton = this.drillUpButton.destroy(); - } else { - this.drillUpButton.attr({ - text: this.getDrilldownBackText() - }) - .align(); - } - }; - - PieSeries.prototype.animateDrilldown = function (init) { - var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], - animationOptions = this.chart.options.drilldown.animation, - animateFrom = level.shapeArgs, - start = animateFrom.start, - angle = animateFrom.end - start, - startAngle = angle / this.points.length, - startColor = H.Color(level.color).rgba; - - if (!init) { - each(this.points, function (point, i) { - var endColor = H.Color(point.color).rgba; - - /*jslint unparam: true*/ - point.graphic - .attr(H.merge(animateFrom, { - start: start + i * startAngle, - end: start + (i + 1) * startAngle - })) - .animate(point.shapeArgs, H.merge(animationOptions, { - step: function (val, fx) { - if (fx.prop === 'start') { - this.attr({ - fill: tweenColors(startColor, endColor, fx.pos) - }); - } - } - })); - /*jslint unparam: false*/ - }); - } - }; - - - /** - * When drilling up, keep the upper series invisible until the lower series has - * moved into place - */ - PieSeries.prototype.animateDrillupTo = - ColumnSeries.prototype.animateDrillupTo = function (init) { - if (!init) { - var newSeries = this, - level = newSeries.drilldownLevel; - - each(this.points, function (point) { - point.graphic.hide(); - if (point.dataLabel) { - point.dataLabel.hide(); - } - if (point.connector) { - point.connector.hide(); - } - }); - - - // Do dummy animation on first point to get to complete - setTimeout(function () { - each(newSeries.points, function (point, i) { - // Fade in other points - var verb = i === level.pointIndex ? 'show' : 'fadeIn'; - point.graphic[verb](); - if (point.dataLabel) { - point.dataLabel[verb](); - } - if (point.connector) { - point.connector[verb](); - } - }); - }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0)); - - // Reset - this.animate = noop; - } - - }; - - ColumnSeries.prototype.animateDrilldown = function (init) { - var animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs, - animationOptions = this.chart.options.drilldown.animation; - - if (!init) { - - animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos); - - each(this.points, function (point) { - point.graphic - .attr(animateFrom) - .animate(point.shapeArgs, animationOptions); - if (point.dataLabel) { - point.dataLabel.fadeIn(animationOptions); - } - }); - } - - }; - - /** - * When drilling up, pull out the individual point graphics from the lower series - * and animate them into the origin point in the upper series. - */ - ColumnSeries.prototype.animateDrillupFrom = - PieSeries.prototype.animateDrillupFrom = - function (level) { - var animationOptions = this.chart.options.drilldown.animation, - group = this.group; - - delete this.group; - each(this.points, function (point) { - var graphic = point.graphic, - startColor = H.Color(point.color).rgba; - - delete point.graphic; - - /*jslint unparam: true*/ - graphic.animate(level.shapeArgs, H.merge(animationOptions, { - - step: function (val, fx) { - if (fx.prop === 'start') { - this.attr({ - fill: tweenColors(startColor, H.Color(level.color).rgba, fx.pos) - }); - } - }, - complete: function () { - graphic.destroy(); - if (group) { - group = group.destroy(); - } - } - })); - /*jslint unparam: false*/ - }); - }; - - H.Point.prototype.doDrilldown = function () { - var series = this.series, - chart = series.chart, - drilldown = chart.options.drilldown, - i = (drilldown.series || []).length, - seriesOptions; - - while (i-- && !seriesOptions) { - if (drilldown.series[i].id === this.drilldown) { - seriesOptions = drilldown.series[i]; - } - } - - // Fire the event. If seriesOptions is undefined, the implementer can check for - // seriesOptions, and call addSeriesAsDrilldown async if necessary. - fireEvent(chart, 'drilldown', { - point: this, - seriesOptions: seriesOptions - }); - - if (seriesOptions) { - chart.addSeriesAsDrilldown(this, seriesOptions); - } - - }; - - wrap(H.Point.prototype, 'init', function (proceed, series, options, x) { - var point = proceed.call(this, series, options, x), - chart = series.chart, - tick = series.xAxis && series.xAxis.ticks[x], - tickLabel = tick && tick.label; - - if (point.drilldown) { - - // Add the click event to the point label - H.addEvent(point, 'click', function () { - point.doDrilldown(); - }); - - // Make axis labels clickable - if (tickLabel) { - if (!tickLabel._basicStyle) { - tickLabel._basicStyle = tickLabel.element.getAttribute('style'); - } - tickLabel - .addClass('highcharts-drilldown-axis-label') - .css(chart.options.drilldown.activeAxisLabelStyle) - .on('click', function () { - if (point.doDrilldown) { - point.doDrilldown(); - } - }); - - } - } else if (tickLabel && tickLabel._basicStyle) { - tickLabel.element.setAttribute('style', tickLabel._basicStyle); - } - - return point; - }); - - wrap(H.Series.prototype, 'drawDataLabels', function (proceed) { - var css = this.chart.options.drilldown.activeDataLabelStyle; - - proceed.call(this); - - each(this.points, function (point) { - if (point.drilldown && point.dataLabel) { - point.dataLabel - .attr({ - 'class': 'highcharts-drilldown-data-label' - }) - .css(css) - .on('click', function () { - point.doDrilldown(); - }); - } - }); - }); - - // Mark the trackers with a pointer - ColumnSeries.prototype.supportsDrilldown = true; - PieSeries.prototype.supportsDrilldown = true; - var type, - drawTrackerWrapper = function (proceed) { - proceed.call(this); - each(this.points, function (point) { - if (point.drilldown && point.graphic) { - point.graphic - .attr({ - 'class': 'highcharts-drilldown-point' - }) - .css({ cursor: 'pointer' }); - } - }); - }; - for (type in seriesTypes) { - if (seriesTypes[type].prototype.supportsDrilldown) { - wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper); - } - } - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/exporting.js b/pykeg/web/static/highcharts/js/modules/exporting.js deleted file mode 100644 index 471c38a95..000000000 --- a/pykeg/web/static/highcharts/js/modules/exporting.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - Exporting module - - (c) 2010-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(f){var A=f.Chart,t=f.addEvent,B=f.removeEvent,l=f.createElement,o=f.discardElement,v=f.css,k=f.merge,r=f.each,p=f.extend,D=Math.max,j=document,C=window,E=f.isTouchDevice,F=f.Renderer.prototype.symbols,s=f.getOptions(),y;p(s.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});s.navigation={menuStyle:{border:"1px solid #A0A0A0", -background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:E?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};s.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{menuClassName:"highcharts-contextmenu", -symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(b,a,d){var c,b=l("form",k({method:"post", -action:b,enctype:"multipart/form-data"},d),{display:"none"},j.body);for(c in a)l("input",{type:"hidden",name:c,value:a[c]},null,b);b.submit();o(b)};p(A.prototype,{getSVG:function(b){var a=this,d,c,z,h,g=k(a.options,b);if(!j.createElementNS)j.createElementNS=function(a,b){return j.createElement(b)};b=l("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},j.body);c=a.renderTo.style.width;h=a.renderTo.style.height;c=g.exporting.sourceWidth||g.chart.width|| -/px$/.test(c)&&parseInt(c,10)||600;h=g.exporting.sourceHeight||g.chart.height||/px$/.test(h)&&parseInt(h,10)||400;p(g.chart,{animation:!1,renderTo:b,forExport:!0,width:c,height:h});g.exporting.enabled=!1;g.series=[];r(a.series,function(a){z=k(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});z.isInternal||g.series.push(z)});d=new f.Chart(g,a.callback);r(["xAxis","yAxis"],function(b){r(a[b],function(a,c){var g=d[b][c],f=a.getExtremes(),h=f.userMin,f=f.userMax;g&&(h!==void 0||f!==void 0)&& -g.setExtremes(h,f,!0,!1)})});c=d.container.innerHTML;g=null;d.destroy();o(b);c=c.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/ /g," ").replace(/­/g,"­").replace(/<IMG /g,"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g, -'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});return c=c.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},exportChart:function(b,a){var b=b||{},d=this.options.exporting,d=this.getSVG(k({chart:{borderRadius:0}},d.chartOptions,a,{exporting:{sourceWidth:b.sourceWidth|| -d.sourceWidth,sourceHeight:b.sourceHeight||d.sourceHeight}})),b=k(this.options.exporting,b);f.post(b.url,{filename:b.filename||"chart",type:b.type,width:b.width||0,scale:b.scale||2,svg:d},b.formAttributes)},print:function(){var b=this,a=b.container,d=[],c=a.parentNode,f=j.body,h=f.childNodes;if(!b.isPrinting)b.isPrinting=!0,r(h,function(a,b){if(a.nodeType===1)d[b]=a.style.display,a.style.display="none"}),f.appendChild(a),C.focus(),C.print(),setTimeout(function(){c.appendChild(a);r(h,function(a,b){if(a.nodeType=== -1)a.style.display=d[b]});b.isPrinting=!1},1E3)},contextMenu:function(b,a,d,c,f,h,g){var e=this,k=e.options.navigation,q=k.menuItemStyle,m=e.chartWidth,n=e.chartHeight,j="cache-"+b,i=e[j],u=D(f,h),w,x,o,s=function(a){e.pointer.inClass(a.target,b)||x()};if(!i)e[j]=i=l("div",{className:b},{position:"absolute",zIndex:1E3,padding:u+"px"},e.container),w=l("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},k.menuStyle),i),x=function(){v(i,{display:"none"}); -g&&g.setState(0);e.openMenu=!1},t(i,"mouseleave",function(){o=setTimeout(x,500)}),t(i,"mouseenter",function(){clearTimeout(o)}),t(document,"mouseup",s),t(e,"destroy",function(){B(document,"mouseup",s)}),r(a,function(a){if(a){var b=a.separator?l("hr",null,null,w):l("div",{onmouseover:function(){v(this,k.menuItemHoverStyle)},onmouseout:function(){v(this,q)},onclick:function(){x();a.onclick.apply(e,arguments)},innerHTML:a.text||e.options.lang[a.textKey]},p({cursor:"pointer"},q),w);e.exportDivElements.push(b)}}), -e.exportDivElements.push(w,i),e.exportMenuWidth=i.offsetWidth,e.exportMenuHeight=i.offsetHeight;a={display:"block"};d+e.exportMenuWidth>m?a.right=m-d-f-u+"px":a.left=d-u+"px";c+h+e.exportMenuHeight>n&&g.alignOptions.verticalAlign!=="top"?a.bottom=n-c-u+"px":a.top=c+h-u+"px";v(i,a);e.openMenu=!0},addButton:function(b){var a=this,d=a.renderer,c=k(a.options.navigation.buttonOptions,b),j=c.onclick,h=c.menuItems,g,e,l={stroke:c.symbolStroke,fill:c.symbolFill},q=c.symbolSize||12;if(!a.btnCount)a.btnCount= -0;if(!a.exportDivElements)a.exportDivElements=[],a.exportSVGElements=[];if(c.enabled!==!1){var m=c.theme,n=m.states,o=n&&n.hover,n=n&&n.select,i;delete m.states;j?i=function(){j.apply(a,arguments)}:h&&(i=function(){a.contextMenu(e.menuClassName,h,e.translateX,e.translateY,e.width,e.height,e);e.setState(2)});c.text&&c.symbol?m.paddingLeft=f.pick(m.paddingLeft,25):c.text||p(m,{width:c.width,height:c.height,padding:0});e=d.button(c.text,0,0,i,m,o,n).attr({title:a.options.lang[c._titleKey],"stroke-linecap":"round"}); -e.menuClassName=b.menuClassName||"highcharts-menu-"+a.btnCount++;c.symbol&&(g=d.symbol(c.symbol,c.symbolX-q/2,c.symbolY-q/2,q,q).attr(p(l,{"stroke-width":c.symbolStrokeWidth||1,zIndex:1})).add(e));e.add().align(p(c,{width:e.width,x:f.pick(c.x,y)}),!0,"spacingBox");y+=(e.width+c.buttonSpacing)*(c.align==="right"?-1:1);a.exportSVGElements.push(e,g)}},destroyExport:function(b){var b=b.target,a,d;for(a=0;a<b.exportSVGElements.length;a++)if(d=b.exportSVGElements[a])d.onclick=d.ontouchstart=null,b.exportSVGElements[a]= -d.destroy();for(a=0;a<b.exportDivElements.length;a++)d=b.exportDivElements[a],B(d,"mouseleave"),b.exportDivElements[a]=d.onmouseout=d.onmouseover=d.ontouchstart=d.onclick=null,o(d)}});F.menu=function(b,a,d,c){return["M",b,a+2.5,"L",b+d,a+2.5,"M",b,a+c/2+0.5,"L",b+d,a+c/2+0.5,"M",b,a+c-1.5,"L",b+d,a+c-1.5]};A.prototype.callbacks.push(function(b){var a,d=b.options.exporting,c=d.buttons;y=0;if(d.enabled!==!1){for(a in c)b.addButton(c[a]);t(b,"destroy",b.destroyExport)}})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/exporting.src.js b/pykeg/web/static/highcharts/js/modules/exporting.src.js deleted file mode 100644 index 1c36670db..000000000 --- a/pykeg/web/static/highcharts/js/modules/exporting.src.js +++ /dev/null @@ -1,715 +0,0 @@ -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * Exporting module - * - * (c) 2010-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -// JSLint options: -/*global Highcharts, document, window, Math, setTimeout */ - -(function (Highcharts) { // encapsulate - -// create shortcuts -var Chart = Highcharts.Chart, - addEvent = Highcharts.addEvent, - removeEvent = Highcharts.removeEvent, - createElement = Highcharts.createElement, - discardElement = Highcharts.discardElement, - css = Highcharts.css, - merge = Highcharts.merge, - each = Highcharts.each, - extend = Highcharts.extend, - math = Math, - mathMax = math.max, - doc = document, - win = window, - isTouchDevice = Highcharts.isTouchDevice, - M = 'M', - L = 'L', - DIV = 'div', - HIDDEN = 'hidden', - NONE = 'none', - PREFIX = 'highcharts-', - ABSOLUTE = 'absolute', - PX = 'px', - UNDEFINED, - symbols = Highcharts.Renderer.prototype.symbols, - defaultOptions = Highcharts.getOptions(), - buttonOffset; - - // Add language - extend(defaultOptions.lang, { - printChart: 'Print chart', - downloadPNG: 'Download PNG image', - downloadJPEG: 'Download JPEG image', - downloadPDF: 'Download PDF document', - downloadSVG: 'Download SVG vector image', - contextButtonTitle: 'Chart context menu' - }); - -// Buttons and menus are collected in a separate config option set called 'navigation'. -// This can be extended later to add control buttons like zoom and pan right click menus. -defaultOptions.navigation = { - menuStyle: { - border: '1px solid #A0A0A0', - background: '#FFFFFF', - padding: '5px 0' - }, - menuItemStyle: { - padding: '0 10px', - background: NONE, - color: '#303030', - fontSize: isTouchDevice ? '14px' : '11px' - }, - menuItemHoverStyle: { - background: '#4572A5', - color: '#FFFFFF' - }, - - buttonOptions: { - symbolFill: '#E0E0E0', - symbolSize: 14, - symbolStroke: '#666', - symbolStrokeWidth: 3, - symbolX: 12.5, - symbolY: 10.5, - align: 'right', - buttonSpacing: 3, - height: 22, - // text: null, - theme: { - fill: 'white', // capture hover - stroke: 'none' - }, - verticalAlign: 'top', - width: 24 - } -}; - - - -// Add the export related options -defaultOptions.exporting = { - //enabled: true, - //filename: 'chart', - type: 'image/png', - url: 'http://export.highcharts.com/', - //width: undefined, - //scale: 2 - buttons: { - contextButton: { - menuClassName: PREFIX + 'contextmenu', - //x: -10, - symbol: 'menu', - _titleKey: 'contextButtonTitle', - menuItems: [{ - textKey: 'printChart', - onclick: function () { - this.print(); - } - }, { - separator: true - }, { - textKey: 'downloadPNG', - onclick: function () { - this.exportChart(); - } - }, { - textKey: 'downloadJPEG', - onclick: function () { - this.exportChart({ - type: 'image/jpeg' - }); - } - }, { - textKey: 'downloadPDF', - onclick: function () { - this.exportChart({ - type: 'application/pdf' - }); - } - }, { - textKey: 'downloadSVG', - onclick: function () { - this.exportChart({ - type: 'image/svg+xml' - }); - } - } - // Enable this block to add "View SVG" to the dropdown menu - /* - ,{ - - text: 'View SVG', - onclick: function () { - var svg = this.getSVG() - .replace(/</g, '\n<') - .replace(/>/g, '>'); - - doc.body.innerHTML = '<pre>' + svg + '</pre>'; - } - } // */ - ] - } - } -}; - -// Add the Highcharts.post utility -Highcharts.post = function (url, data, formAttributes) { - var name, - form; - - // create the form - form = createElement('form', merge({ - method: 'post', - action: url, - enctype: 'multipart/form-data' - }, formAttributes), { - display: NONE - }, doc.body); - - // add the data - for (name in data) { - createElement('input', { - type: HIDDEN, - name: name, - value: data[name] - }, null, form); - } - - // submit - form.submit(); - - // clean up - discardElement(form); -}; - -extend(Chart.prototype, { - - /** - * Return an SVG representation of the chart - * - * @param additionalOptions {Object} Additional chart options for the generated SVG representation - */ - getSVG: function (additionalOptions) { - var chart = this, - chartCopy, - sandbox, - svg, - seriesOptions, - sourceWidth, - sourceHeight, - cssWidth, - cssHeight, - options = merge(chart.options, additionalOptions); // copy the options and add extra options - - // IE compatibility hack for generating SVG content that it doesn't really understand - if (!doc.createElementNS) { - /*jslint unparam: true*//* allow unused parameter ns in function below */ - doc.createElementNS = function (ns, tagName) { - return doc.createElement(tagName); - }; - /*jslint unparam: false*/ - } - - // create a sandbox where a new chart will be generated - sandbox = createElement(DIV, null, { - position: ABSOLUTE, - top: '-9999em', - width: chart.chartWidth + PX, - height: chart.chartHeight + PX - }, doc.body); - - // get the source size - cssWidth = chart.renderTo.style.width; - cssHeight = chart.renderTo.style.height; - sourceWidth = options.exporting.sourceWidth || - options.chart.width || - (/px$/.test(cssWidth) && parseInt(cssWidth, 10)) || - 600; - sourceHeight = options.exporting.sourceHeight || - options.chart.height || - (/px$/.test(cssHeight) && parseInt(cssHeight, 10)) || - 400; - - // override some options - extend(options.chart, { - animation: false, - renderTo: sandbox, - forExport: true, - width: sourceWidth, - height: sourceHeight - }); - options.exporting.enabled = false; // hide buttons in print - - // prepare for replicating the chart - options.series = []; - each(chart.series, function (serie) { - seriesOptions = merge(serie.options, { - animation: false, // turn off animation - showCheckbox: false, - visible: serie.visible - }); - - if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set - options.series.push(seriesOptions); - } - }); - - // generate the chart copy - chartCopy = new Highcharts.Chart(options, chart.callback); - - // reflect axis extremes in the export - each(['xAxis', 'yAxis'], function (axisType) { - each(chart[axisType], function (axis, i) { - var axisCopy = chartCopy[axisType][i], - extremes = axis.getExtremes(), - userMin = extremes.userMin, - userMax = extremes.userMax; - - if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) { - axisCopy.setExtremes(userMin, userMax, true, false); - } - }); - }); - - // get the SVG from the container's innerHTML - svg = chartCopy.container.innerHTML; - - // free up memory - options = null; - chartCopy.destroy(); - discardElement(sandbox); - - // sanitize - svg = svg - .replace(/zIndex="[^"]+"/g, '') - .replace(/isShadow="[^"]+"/g, '') - .replace(/symbolName="[^"]+"/g, '') - .replace(/jQuery[0-9]+="[^"]+"/g, '') - .replace(/url\([^#]+#/g, 'url(#') - .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ') - .replace(/ href=/g, ' xlink:href=') - .replace(/\n/, ' ') - .replace(/<\/svg>.*?$/, '</svg>') // any HTML added to the container after the SVG (#894) - /* This fails in IE < 8 - .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight - return s2 +'.'+ s3[0]; - })*/ - - // Replace HTML entities, issue #347 - .replace(/ /g, '\u00A0') // no-break space - .replace(/­/g, '\u00AD') // soft hyphen - - // IE specific - .replace(/<IMG /g, '<image ') - .replace(/height=([^" ]+)/g, 'height="$1"') - .replace(/width=([^" ]+)/g, 'width="$1"') - .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>') - .replace(/id=([^" >]+)/g, 'id="$1"') - .replace(/class=([^" >]+)/g, 'class="$1"') - .replace(/ transform /g, ' ') - .replace(/:(path|rect)/g, '$1') - .replace(/style="([^"]+)"/g, function (s) { - return s.toLowerCase(); - }); - - // IE9 beta bugs with innerHTML. Test again with final IE9. - svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1') - .replace(/"/g, "'"); - - return svg; - }, - - /** - * Submit the SVG representation of the chart to the server - * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes. - * @param {Object} chartOptions Additional chart options for the SVG representation of the chart - */ - exportChart: function (options, chartOptions) { - options = options || {}; - - var chart = this, - chartExportingOptions = chart.options.exporting, - svg = chart.getSVG(merge( - { chart: { borderRadius: 0 } }, - chartExportingOptions.chartOptions, - chartOptions, - { - exporting: { - sourceWidth: options.sourceWidth || chartExportingOptions.sourceWidth, - sourceHeight: options.sourceHeight || chartExportingOptions.sourceHeight - } - } - )); - - // merge the options - options = merge(chart.options.exporting, options); - - // do the post - Highcharts.post(options.url, { - filename: options.filename || 'chart', - type: options.type, - width: options.width || 0, // IE8 fails to post undefined correctly, so use 0 - scale: options.scale || 2, - svg: svg - }, options.formAttributes); - - }, - - /** - * Print the chart - */ - print: function () { - - var chart = this, - container = chart.container, - origDisplay = [], - origParent = container.parentNode, - body = doc.body, - childNodes = body.childNodes; - - if (chart.isPrinting) { // block the button while in printing mode - return; - } - - chart.isPrinting = true; - - // hide all body content - each(childNodes, function (node, i) { - if (node.nodeType === 1) { - origDisplay[i] = node.style.display; - node.style.display = NONE; - } - }); - - // pull out the chart - body.appendChild(container); - - // print - win.focus(); // #1510 - win.print(); - - // allow the browser to prepare before reverting - setTimeout(function () { - - // put the chart back in - origParent.appendChild(container); - - // restore all body content - each(childNodes, function (node, i) { - if (node.nodeType === 1) { - node.style.display = origDisplay[i]; - } - }); - - chart.isPrinting = false; - - }, 1000); - - }, - - /** - * Display a popup menu for choosing the export type - * - * @param {String} className An identifier for the menu - * @param {Array} items A collection with text and onclicks for the items - * @param {Number} x The x position of the opener button - * @param {Number} y The y position of the opener button - * @param {Number} width The width of the opener button - * @param {Number} height The height of the opener button - */ - contextMenu: function (className, items, x, y, width, height, button) { - var chart = this, - navOptions = chart.options.navigation, - menuItemStyle = navOptions.menuItemStyle, - chartWidth = chart.chartWidth, - chartHeight = chart.chartHeight, - cacheName = 'cache-' + className, - menu = chart[cacheName], - menuPadding = mathMax(width, height), // for mouse leave detection - boxShadow = '3px 3px 10px #888', - innerMenu, - hide, - hideTimer, - menuStyle, - docMouseUpHandler = function (e) { - if (!chart.pointer.inClass(e.target, className)) { - hide(); - } - }; - - // create the menu only the first time - if (!menu) { - - // create a HTML element above the SVG - chart[cacheName] = menu = createElement(DIV, { - className: className - }, { - position: ABSOLUTE, - zIndex: 1000, - padding: menuPadding + PX - }, chart.container); - - innerMenu = createElement(DIV, null, - extend({ - MozBoxShadow: boxShadow, - WebkitBoxShadow: boxShadow, - boxShadow: boxShadow - }, navOptions.menuStyle), menu); - - // hide on mouse out - hide = function () { - css(menu, { display: NONE }); - if (button) { - button.setState(0); - } - chart.openMenu = false; - }; - - // Hide the menu some time after mouse leave (#1357) - addEvent(menu, 'mouseleave', function () { - hideTimer = setTimeout(hide, 500); - }); - addEvent(menu, 'mouseenter', function () { - clearTimeout(hideTimer); - }); - - - // Hide it on clicking or touching outside the menu (#2258, #2335, #2407) - addEvent(document, 'mouseup', docMouseUpHandler); - addEvent(chart, 'destroy', function () { - removeEvent(document, 'mouseup', docMouseUpHandler); - }); - - - // create the items - each(items, function (item) { - if (item) { - var element = item.separator ? - createElement('hr', null, null, innerMenu) : - createElement(DIV, { - onmouseover: function () { - css(this, navOptions.menuItemHoverStyle); - }, - onmouseout: function () { - css(this, menuItemStyle); - }, - onclick: function () { - hide(); - item.onclick.apply(chart, arguments); - }, - innerHTML: item.text || chart.options.lang[item.textKey] - }, extend({ - cursor: 'pointer' - }, menuItemStyle), innerMenu); - - - // Keep references to menu divs to be able to destroy them - chart.exportDivElements.push(element); - } - }); - - // Keep references to menu and innerMenu div to be able to destroy them - chart.exportDivElements.push(innerMenu, menu); - - chart.exportMenuWidth = menu.offsetWidth; - chart.exportMenuHeight = menu.offsetHeight; - } - - menuStyle = { display: 'block' }; - - // if outside right, right align it - if (x + chart.exportMenuWidth > chartWidth) { - menuStyle.right = (chartWidth - x - width - menuPadding) + PX; - } else { - menuStyle.left = (x - menuPadding) + PX; - } - // if outside bottom, bottom align it - if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') { - menuStyle.bottom = (chartHeight - y - menuPadding) + PX; - } else { - menuStyle.top = (y + height - menuPadding) + PX; - } - - css(menu, menuStyle); - chart.openMenu = true; - }, - - /** - * Add the export button to the chart - */ - addButton: function (options) { - var chart = this, - renderer = chart.renderer, - btnOptions = merge(chart.options.navigation.buttonOptions, options), - onclick = btnOptions.onclick, - menuItems = btnOptions.menuItems, - symbol, - button, - symbolAttr = { - stroke: btnOptions.symbolStroke, - fill: btnOptions.symbolFill - }, - symbolSize = btnOptions.symbolSize || 12; - if (!chart.btnCount) { - chart.btnCount = 0; - } - - // Keeps references to the button elements - if (!chart.exportDivElements) { - chart.exportDivElements = []; - chart.exportSVGElements = []; - } - - if (btnOptions.enabled === false) { - return; - } - - - var attr = btnOptions.theme, - states = attr.states, - hover = states && states.hover, - select = states && states.select, - callback; - - delete attr.states; - - if (onclick) { - callback = function () { - onclick.apply(chart, arguments); - }; - - } else if (menuItems) { - callback = function () { - chart.contextMenu( - button.menuClassName, - menuItems, - button.translateX, - button.translateY, - button.width, - button.height, - button - ); - button.setState(2); - }; - } - - - if (btnOptions.text && btnOptions.symbol) { - attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25); - - } else if (!btnOptions.text) { - extend(attr, { - width: btnOptions.width, - height: btnOptions.height, - padding: 0 - }); - } - - button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select) - .attr({ - title: chart.options.lang[btnOptions._titleKey], - 'stroke-linecap': 'round' - }); - button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++; - - if (btnOptions.symbol) { - symbol = renderer.symbol( - btnOptions.symbol, - btnOptions.symbolX - (symbolSize / 2), - btnOptions.symbolY - (symbolSize / 2), - symbolSize, - symbolSize - ) - .attr(extend(symbolAttr, { - 'stroke-width': btnOptions.symbolStrokeWidth || 1, - zIndex: 1 - })).add(button); - } - - button.add() - .align(extend(btnOptions, { - width: button.width, - x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654 - }), true, 'spacingBox'); - - buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1); - - chart.exportSVGElements.push(button, symbol); - - }, - - /** - * Destroy the buttons. - */ - destroyExport: function (e) { - var chart = e.target, - i, - elem; - - // Destroy the extra buttons added - for (i = 0; i < chart.exportSVGElements.length; i++) { - elem = chart.exportSVGElements[i]; - - // Destroy and null the svg/vml elements - if (elem) { // #1822 - elem.onclick = elem.ontouchstart = null; - chart.exportSVGElements[i] = elem.destroy(); - } - } - - // Destroy the divs for the menu - for (i = 0; i < chart.exportDivElements.length; i++) { - elem = chart.exportDivElements[i]; - - // Remove the event handler - removeEvent(elem, 'mouseleave'); - - // Remove inline events - chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null; - - // Destroy the div by moving to garbage bin - discardElement(elem); - } - } -}); - - -symbols.menu = function (x, y, width, height) { - var arr = [ - M, x, y + 2.5, - L, x + width, y + 2.5, - M, x, y + height / 2 + 0.5, - L, x + width, y + height / 2 + 0.5, - M, x, y + height - 1.5, - L, x + width, y + height - 1.5 - ]; - return arr; -}; - -// Add the buttons on chart load -Chart.prototype.callbacks.push(function (chart) { - var n, - exportingOptions = chart.options.exporting, - buttons = exportingOptions.buttons; - - buttonOffset = 0; - - if (exportingOptions.enabled !== false) { - - for (n in buttons) { - chart.addButton(buttons[n]); - } - - // Destroy the export elements at chart destroy - addEvent(chart, 'destroy', chart.destroyExport); - } - -}); - - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/funnel.js b/pykeg/web/static/highcharts/js/modules/funnel.js deleted file mode 100644 index 7a7964dd2..000000000 --- a/pykeg/web/static/highcharts/js/modules/funnel.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - - Highcharts funnel module - - (c) 2010-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(d){var u=d.getOptions().plotOptions,p=d.seriesTypes,D=d.merge,z=function(){},A=d.each;u.funnel=D(u.pie,{center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",dataLabels:{connectorWidth:1,connectorColor:"#606060"},size:!0,states:{select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}}});p.funnel=d.extendClass(p.pie,{type:"funnel",animate:z,translate:function(){var a=function(k,a){return/%$/.test(k)?a*parseInt(k,10)/100:parseInt(k,10)},g=0,e=this.chart,f=e.plotWidth, -e=e.plotHeight,h=0,c=this.options,C=c.center,b=a(C[0],f),d=a(C[0],e),p=a(c.width,f),i,q,j=a(c.height,e),r=a(c.neckWidth,f),s=a(c.neckHeight,e),v=j-s,a=this.data,w,x,u=c.dataLabels.position==="left"?1:0,y,m,B,n,l,t,o;this.getWidthAt=q=function(k){return k>j-s||j===s?r:r+(p-r)*((j-s-k)/(j-s))};this.getX=function(k,a){return b+(a?-1:1)*(q(k)/2+c.dataLabels.distance)};this.center=[b,d,j];this.centerX=b;A(a,function(a){g+=a.y});A(a,function(a){o=null;x=g?a.y/g:0;m=d-j/2+h*j;l=m+x*j;i=q(m);y=b-i/2;B=y+ -i;i=q(l);n=b-i/2;t=n+i;m>v?(y=n=b-r/2,B=t=b+r/2):l>v&&(o=l,i=q(v),n=b-i/2,t=n+i,l=v);w=["M",y,m,"L",B,m,t,l];o&&w.push(t,o,n,o);w.push(n,l,"Z");a.shapeType="path";a.shapeArgs={d:w};a.percentage=x*100;a.plotX=b;a.plotY=(m+(o||l))/2;a.tooltipPos=[b,a.plotY];a.slice=z;a.half=u;h+=x});this.setTooltipPoints()},drawPoints:function(){var a=this,g=a.options,e=a.chart.renderer;A(a.data,function(f){var h=f.graphic,c=f.shapeArgs;h?h.animate(c):f.graphic=e.path(c).attr({fill:f.color,stroke:g.borderColor,"stroke-width":g.borderWidth}).add(a.group)})}, -sortByAngle:z,drawDataLabels:function(){var a=this.data,g=this.options.dataLabels.distance,e,f,h,c=a.length,d,b;for(this.center[2]-=2*g;c--;)h=a[c],f=(e=h.half)?1:-1,b=h.plotY,d=this.getX(b,e),h.labelPos=[0,b,d+(g-5)*f,b,d+g*f,b,e?"right":"left",0];p.pie.prototype.drawDataLabels.call(this)}})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/funnel.src.js b/pykeg/web/static/highcharts/js/modules/funnel.src.js deleted file mode 100644 index ec0e2d1e5..000000000 --- a/pykeg/web/static/highcharts/js/modules/funnel.src.js +++ /dev/null @@ -1,289 +0,0 @@ -/** - * @license - * Highcharts funnel module - * - * (c) 2010-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -/*global Highcharts */ -(function (Highcharts) { - -'use strict'; - -// create shortcuts -var defaultOptions = Highcharts.getOptions(), - defaultPlotOptions = defaultOptions.plotOptions, - seriesTypes = Highcharts.seriesTypes, - merge = Highcharts.merge, - noop = function () {}, - each = Highcharts.each; - -// set default options -defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, { - center: ['50%', '50%'], - width: '90%', - neckWidth: '30%', - height: '100%', - neckHeight: '25%', - - dataLabels: { - //position: 'right', - connectorWidth: 1, - connectorColor: '#606060' - }, - size: true, // to avoid adapting to data label size in Pie.drawDataLabels - states: { - select: { - color: '#C0C0C0', - borderColor: '#000000', - shadow: false - } - } -}); - - -seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, { - - type: 'funnel', - animate: noop, - - /** - * Overrides the pie translate method - */ - translate: function () { - - var - // Get positions - either an integer or a percentage string must be given - getLength = function (length, relativeTo) { - return (/%$/).test(length) ? - relativeTo * parseInt(length, 10) / 100 : - parseInt(length, 10); - }, - - sum = 0, - series = this, - chart = series.chart, - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - cumulative = 0, // start at top - options = series.options, - center = options.center, - centerX = getLength(center[0], plotWidth), - centerY = getLength(center[0], plotHeight), - width = getLength(options.width, plotWidth), - tempWidth, - getWidthAt, - height = getLength(options.height, plotHeight), - neckWidth = getLength(options.neckWidth, plotWidth), - neckHeight = getLength(options.neckHeight, plotHeight), - neckY = height - neckHeight, - data = series.data, - path, - fraction, - half = options.dataLabels.position === 'left' ? 1 : 0, - - x1, - y1, - x2, - x3, - y3, - x4, - y5; - - // Return the width at a specific y coordinate - series.getWidthAt = getWidthAt = function (y) { - return y > height - neckHeight || height === neckHeight ? - neckWidth : - neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight)); - }; - series.getX = function (y, half) { - return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance); - }; - - // Expose - series.center = [centerX, centerY, height]; - series.centerX = centerX; - - /* - * Individual point coordinate naming: - * - * x1,y1 _________________ x2,y1 - * \ / - * \ / - * \ / - * \ / - * \ / - * x3,y3 _________ x4,y3 - * - * Additional for the base of the neck: - * - * | | - * | | - * | | - * x3,y5 _________ x4,y5 - */ - - - - - // get the total sum - each(data, function (point) { - sum += point.y; - }); - - each(data, function (point) { - // set start and end positions - y5 = null; - fraction = sum ? point.y / sum : 0; - y1 = centerY - height / 2 + cumulative * height; - y3 = y1 + fraction * height; - //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight)); - tempWidth = getWidthAt(y1); - x1 = centerX - tempWidth / 2; - x2 = x1 + tempWidth; - tempWidth = getWidthAt(y3); - x3 = centerX - tempWidth / 2; - x4 = x3 + tempWidth; - - // the entire point is within the neck - if (y1 > neckY) { - x1 = x3 = centerX - neckWidth / 2; - x2 = x4 = centerX + neckWidth / 2; - - // the base of the neck - } else if (y3 > neckY) { - y5 = y3; - - tempWidth = getWidthAt(neckY); - x3 = centerX - tempWidth / 2; - x4 = x3 + tempWidth; - - y3 = neckY; - } - - // save the path - path = [ - 'M', - x1, y1, - 'L', - x2, y1, - x4, y3 - ]; - if (y5) { - path.push(x4, y5, x3, y5); - } - path.push(x3, y3, 'Z'); - - // prepare for using shared dr - point.shapeType = 'path'; - point.shapeArgs = { d: path }; - - - // for tooltips and data labels - point.percentage = fraction * 100; - point.plotX = centerX; - point.plotY = (y1 + (y5 || y3)) / 2; - - // Placement of tooltips and data labels - point.tooltipPos = [ - centerX, - point.plotY - ]; - - // Slice is a noop on funnel points - point.slice = noop; - - // Mimicking pie data label placement logic - point.half = half; - - cumulative += fraction; - }); - - - series.setTooltipPoints(); - }, - /** - * Draw a single point (wedge) - * @param {Object} point The point object - * @param {Object} color The color of the point - * @param {Number} brightness The brightness relative to the color - */ - drawPoints: function () { - var series = this, - options = series.options, - chart = series.chart, - renderer = chart.renderer; - - each(series.data, function (point) { - - var graphic = point.graphic, - shapeArgs = point.shapeArgs; - - if (!graphic) { // Create the shapes - point.graphic = renderer.path(shapeArgs). - attr({ - fill: point.color, - stroke: options.borderColor, - 'stroke-width': options.borderWidth - }). - add(series.group); - - } else { // Update the shapes - graphic.animate(shapeArgs); - } - }); - }, - - /** - * Funnel items don't have angles (#2289) - */ - sortByAngle: noop, - - /** - * Extend the pie data label method - */ - drawDataLabels: function () { - var data = this.data, - labelDistance = this.options.dataLabels.distance, - leftSide, - sign, - point, - i = data.length, - x, - y; - - // In the original pie label anticollision logic, the slots are distributed - // from one labelDistance above to one labelDistance below the pie. In funnels - // we don't want this. - this.center[2] -= 2 * labelDistance; - - // Set the label position array for each point. - while (i--) { - point = data[i]; - leftSide = point.half; - sign = leftSide ? 1 : -1; - y = point.plotY; - x = this.getX(y, leftSide); - - // set the anchor point for data labels - point.labelPos = [ - 0, // first break of connector - y, // a/a - x + (labelDistance - 5) * sign, // second break, right outside point shape - y, // a/a - x + labelDistance * sign, // landing point for connector - y, // a/a - leftSide ? 'right' : 'left', // alignment - 0 // center angle - ]; - } - - seriesTypes.pie.prototype.drawDataLabels.call(this); - } - -}); - - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/heatmap.js b/pykeg/web/static/highcharts/js/modules/heatmap.js deleted file mode 100644 index 889dd36fa..000000000 --- a/pykeg/web/static/highcharts/js/modules/heatmap.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(a){var c=a.seriesTypes,i=a.each;c.heatmap=a.extendClass(c.map,{useMapGeometry:!1,pointArrayMap:["y","value"],init:function(){c.map.prototype.init.apply(this,arguments);this.pointRange=this.options.colsize||1},translate:function(){var a=this.options,f=this.xAxis,c=this.yAxis;this.generatePoints();i(this.points,function(b){var d=(a.colsize||1)/2,e=(a.rowsize||1)/2,g=Math.round(f.len-f.translate(b.x-d,0,1,0,1)),d=Math.round(f.len-f.translate(b.x+d,0,1,0,1)),h=Math.round(c.translate(b.y-e,0, -1,0,1)),e=Math.round(c.translate(b.y+e,0,1,0,1));b.plotY=1;b.shapeType="rect";b.shapeArgs={x:Math.min(g,d),y:Math.min(h,e),width:Math.abs(d-g),height:Math.abs(e-h)}});this.pointRange=a.colsize||1;this.translateColors()},animate:function(){},getBox:function(){},getExtremes:function(){a.Series.prototype.getExtremes.call(this,this.valueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;a.Series.prototype.getExtremes.call(this)}})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/heatmap.src.js b/pykeg/web/static/highcharts/js/modules/heatmap.src.js deleted file mode 100644 index 9f39e9e99..000000000 --- a/pykeg/web/static/highcharts/js/modules/heatmap.src.js +++ /dev/null @@ -1,60 +0,0 @@ -(function (H) { - var seriesTypes = H.seriesTypes, - each = H.each; - - seriesTypes.heatmap = H.extendClass(seriesTypes.map, { - useMapGeometry: false, - pointArrayMap: ['y', 'value'], - init: function () { - seriesTypes.map.prototype.init.apply(this, arguments); - this.pointRange = this.options.colsize || 1; - // TODO: similar logic for the Y axis - }, - translate: function () { - var series = this, - options = series.options, - xAxis = series.xAxis, - yAxis = series.yAxis; - - series.generatePoints(); - - each(series.points, function (point) { - var xPad = (options.colsize || 1) / 2, - yPad = (options.rowsize || 1) / 2, - x1 = Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)), - x2 = Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)), - y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)), - y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1)); - - - point.plotY = 1; // Pass test in Column.drawPoints - - point.shapeType = 'rect'; - point.shapeArgs = { - x: Math.min(x1, x2), - y: Math.min(y1, y2), - width: Math.abs(x2 - x1), - height: Math.abs(y2 - y1) - }; - }); - - series.pointRange = options.colsize || 1; - series.translateColors(); - }, - - animate: function () {}, - getBox: function () {}, - - getExtremes: function () { - // Get the extremes from the value data - H.Series.prototype.getExtremes.call(this, this.valueData); - this.valueMin = this.dataMin; - this.valueMax = this.dataMax; - - // Get the extremes from the y data - H.Series.prototype.getExtremes.call(this); - } - - }); - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/modules/map.js b/pykeg/web/static/highcharts/js/modules/map.js deleted file mode 100644 index a9df0bcc0..000000000 --- a/pykeg/web/static/highcharts/js/modules/map.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - Map plugin v0.2 for Highcharts - - (c) 2011-2014 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(i){function A(a,b,d){for(var c=4,e,f=[];c--;)e=b.rgba[c]+(a.rgba[c]-b.rgba[c])*(1-d),f[c]=c===3?e:Math.round(e);return"rgba("+f.join(",")+")"}function E(a,b,d,c,e,f,g,h,m){a=a["stroke-width"]%2/2;b-=a;d-=a;return["M",b+f,d,"L",b+c-g,d,"C",b+c-g/2,d,b+c,d+g/2,b+c,d+g,"L",b+c,d+e-h,"C",b+c,d+e-h/2,b+c-h/2,d+e,b+c-h,d+e,"L",b+m,d+e,"C",b+m/2,d+e,b,d+e-m/2,b,d+e-m,"L",b,d+f,"C",b,d+f/2,b+f/2,d,b+f,d,"Z"]}var o=i.Axis,x=i.Chart,v=i.Color,w=i.Point,s=i.Pointer,H=i.Legend,y=i.Series,F=i.VMLRenderer, -B=i.SVGRenderer.prototype.symbols,l=i.each,r=i.extend,t=i.extendClass,n=i.merge,j=i.pick,G=i.numberFormat,C=i.getOptions(),k=i.seriesTypes,p=C.plotOptions,q=i.wrap,u=function(){};r(C.lang,{zoomIn:"Zoom in",zoomOut:"Zoom out"});C.mapNavigation={buttonOptions:{alignTo:"plotBox",align:"left",verticalAlign:"top",x:0,width:18,height:18,style:{fontSize:"15px",fontWeight:"bold",textAlign:"center"},theme:{"stroke-width":1}},buttons:{zoomIn:{onclick:function(){this.mapZoom(0.5)},text:"+",y:0},zoomOut:{onclick:function(){this.mapZoom(2)}, -text:"-",y:28}}};i.splitPath=function(a){var b,a=a.replace(/([A-Za-z])/g," $1 "),a=a.replace(/^\s*/,"").replace(/\s*$/,""),a=a.split(/[ ,]+/);for(b=0;b<a.length;b++)/[a-zA-Z]/.test(a[b])||(a[b]=parseFloat(a[b]));return a};i.maps={};q(o.prototype,"getSeriesExtremes",function(a){var b=this.isXAxis,d,c,e=[];b&&l(this.series,function(a,b){if(a.useMapGeometry)e[b]=a.xData,a.xData=[]});a.call(this);if(b)d=j(this.dataMin,Number.MAX_VALUE),c=j(this.dataMax,Number.MIN_VALUE),l(this.series,function(a,b){if(a.useMapGeometry)d= -Math.min(d,j(a.minX,d)),c=Math.max(c,j(a.maxX,d)),a.xData=e[b]}),this.dataMin=d,this.dataMax=c});q(o.prototype,"setAxisTranslation",function(a){var b=this.chart,d=b.plotWidth/b.plotHeight,c=b.xAxis[0];a.call(this);if(b.options.chart.preserveAspectRatio&&this.coll==="yAxis"&&c.transA!==void 0&&(this.transA=c.transA=Math.min(this.transA,c.transA),a=b.mapRatio=d/((c.max-c.min)/(this.max-this.min)),c=a<1?this:c,a=(c.max-c.min)*c.transA,c.pixelPadding=c.len-a,c.minPixelPadding=c.pixelPadding/2,a=c.fixTo))a= -a[1]-c.toValue(a[0],!0),a*=c.transA,Math.abs(a)>c.minPixelPadding&&(a=0),c.minPixelPadding-=a});q(o.prototype,"render",function(a){a.call(this);this.fixTo=null});r(s.prototype,{onContainerDblClick:function(a){var b=this.chart,a=this.normalize(a);b.options.mapNavigation.enableDoubleClickZoomTo?b.pointer.inClass(a.target,"highcharts-tracker")&&b.hoverPoint.zoomTo():b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&b.mapZoom(0.5,b.xAxis[0].toValue(a.chartX),b.yAxis[0].toValue(a.chartY),a.chartX, -a.chartY)},onContainerMouseWheel:function(a){var b=this.chart,d,a=this.normalize(a);d=a.detail||-(a.wheelDelta/120);b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&b.mapZoom(d>0?2:0.5,b.xAxis[0].toValue(a.chartX),b.yAxis[0].toValue(a.chartY),d>0?void 0:a.chartX,d>0?void 0:a.chartY)}});q(s.prototype,"init",function(a,b,d){a.call(this,b,d);if(j(d.mapNavigation.enableTouchZoom,d.mapNavigation.enabled))this.pinchX=this.pinchHor=this.pinchY=this.pinchVert=!0});q(s.prototype,"pinchTranslate",function(a, -b,d,c,e,f,g,h,m){a.call(this,b,d,c,e,f,g,h,m);this.chart.options.chart.type==="map"&&(a=f.scaleX>f.scaleY,this.pinchTranslateDirection(!a,c,e,f,g,h,m,a?f.scaleX:f.scaleY))});var D=i.ColorAxis=function(){this.init.apply(this,arguments)};r(D.prototype,o.prototype);r(D.prototype,{defaultColorAxisOptions:{lineWidth:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},color:"gray",width:0.01},labels:{overflow:"justify"},minColor:"#EFEFFF",maxColor:"#102d4c"}, -init:function(a,b){var d=a.options.legend.layout!=="vertical",c;c=n(this.defaultColorAxisOptions,{side:d?2:1,reversed:!d},b,{isX:d,opposite:!d,showEmpty:!1,title:null});o.prototype.init.call(this,a,c);b.dataClasses&&this.initDataClasses(b);this.isXAxis=!0;this.horiz=d},initDataClasses:function(a){var b=this.chart,d,c=0,e=this.options;this.dataClasses=d=[];l(a.dataClasses,function(f,g){var h,f=n(f);d.push(f);if(!f.color)e.dataClassColor==="category"?(h=b.options.colors,f.color=h[c++],c===h.length&& -(c=0)):f.color=A(v(e.minColor),v(e.maxColor),g/(a.dataClasses.length-1))})},setOptions:function(a){o.prototype.setOptions.call(this,a);this.options.crosshair=this.options.marker;this.stops=a.stops||[[0,this.options.minColor],[1,this.options.maxColor]];l(this.stops,function(a){a.color=v(a[1])});this.coll="colorAxis"},setAxisSize:function(){var a=this.legendSymbol,b=this.chart;if(a)this.left=a.x,this.top=a.y,this.width=a.width,this.height=a.height,this.right=b.chartWidth-this.left-this.width,this.bottom= -b.chartHeight-this.top-this.height,this.len=this.horiz?this.width:this.height,this.pos=this.horiz?this.left:this.top},toColor:function(a,b){var d,c=this.stops,e,f=this.dataClasses,g,h;if(f)for(h=f.length;h--;){if(g=f[h],e=g.from,c=g.to,(e===void 0||a>=e)&&(c===void 0||a<=c)){d=g.color;if(b)b.dataClass=h;break}}else{this.isLog&&(a=this.val2lin(a));d=1-(this.max-a)/(this.max-this.min);for(h=c.length;h--;)if(d>c[h][0])break;e=c[h]||c[h+1];c=c[h+1]||e;d=1-(c[0]-d)/(c[0]-e[0]||1);d=A(e.color,c.color,d)}return d}, -getOffset:function(){var a=this.legendGroup;if(a&&(o.prototype.getOffset.call(this),!this.axisGroup.parentGroup))this.axisGroup.add(a),this.gridGroup.add(a),this.labelGroup.add(a),this.added=!0},setLegendColor:function(){var a,b=this.options;a=this.horiz?[0,0,1,0]:[0,0,0,1];this.legendColor={linearGradient:{x1:a[0],y1:a[1],x2:a[2],y2:a[3]},stops:b.stops||[[0,b.minColor],[1,b.maxColor]]}},drawLegendSymbol:function(a,b){var d=a.padding,c=a.options,e=this.horiz,f=j(c.symbolWidth,e?200:12),g=j(c.symbolHeight, -e?12:200),c=j(c.labelPadding,e?10:30);this.setLegendColor();b.legendSymbol=this.chart.renderer.rect(0,a.baseline-11,f,g).attr({zIndex:1}).add(b.legendGroup);b.legendSymbol.getBBox();this.legendItemWidth=f+d+(e?0:c);this.legendItemHeight=g+d+(e?c:0)},setState:u,visible:!0,setVisible:u,getSeriesExtremes:function(){var a;if(this.series.length)a=this.series[0],this.dataMin=a.valueMin,this.dataMax=a.valueMax},drawCrosshair:function(a,b){var d=!this.cross,c=b&&b.plotX,e=b&&b.plotY,f,g=this.pos,h=this.len; -if(b)f=this.toPixels(b.value),f<g?f=g-2:f>g+h&&(f=g+h+2),b.plotX=f,b.plotY=this.len-f,o.prototype.drawCrosshair.call(this,a,b),b.plotX=c,b.plotY=e,!d&&this.cross&&this.cross.attr({fill:this.crosshair.color}).add(this.labelGroup)},getPlotLinePath:function(a,b,d,c,e){return e?this.horiz?["M",e-4,this.top-6,"L",e+4,this.top-6,e,this.top,"Z"]:["M",this.left,e,"L",this.left-6,e+6,this.left-6,e-6,"Z"]:o.prototype.getPlotLinePath.call(this,a,b,d,c)},update:function(a,b){o.prototype.update.call(this,a,b); -this.legendItem&&(this.setLegendColor(),this.chart.legend.colorizeItem(this,!0))},getDataClassLegendSymbols:function(){var a=this,b=this.chart,d=[],c=b.options.legend,e=c.valueDecimals,f=c.valueSuffix||"",g;l(this.dataClasses,function(c,m){var z=!0,j=c.from,k=c.to;g="";j===void 0?g="< ":k===void 0&&(g="> ");j!==void 0&&(g+=G(j,e)+f);j!==void 0&&k!==void 0&&(g+=" - ");k!==void 0&&(g+=G(k,e)+f);d.push(i.extend({chart:b,name:g,options:{},drawLegendSymbol:i.LegendSymbolMixin.drawRectangle,visible:!0, -setState:u,setVisible:function(){z=this.visible=!z;l(a.series,function(a){l(a.points,function(a){a.dataClass===m&&a.setVisible(z)})});b.legend.colorizeItem(this,z)}},c))});return d}});q(H.prototype,"getAllItems",function(a){var b=[],d=this.chart.colorAxis[0];d&&(d.options.dataClasses?b=b.concat(d.getDataClassLegendSymbols()):b.push(d),l(d.series,function(a){a.options.showInLegend=!1}));return b.concat(a.call(this))});r(x.prototype,{renderMapNavigation:function(){var a=this,b=this.options.mapNavigation, -d=b.buttons,c,e,f,g,h=function(){this.handler.call(a)};if(j(b.enableButtons,b.enabled)&&!a.renderer.forExport)for(c in d)if(d.hasOwnProperty(c))f=n(b.buttonOptions,d[c]),e=f.theme,g=e.states,e=a.renderer.button(f.text,0,0,h,e,g&&g.hover,g&&g.select,0,c==="zoomIn"?"topbutton":"bottombutton").attr({width:f.width,height:f.height,title:a.options.lang[c],zIndex:5}).css(f.style).add(),e.handler=f.onclick,e.align(r(f,{width:e.width,height:2*e.height}),null,f.alignTo)},fitToBox:function(a,b){l([["x","width"], -["y","height"]],function(d){var c=d[0],d=d[1];a[c]+a[d]>b[c]+b[d]&&(a[d]>b[d]?(a[d]=b[d],a[c]=b[c]):a[c]=b[c]+b[d]-a[d]);a[d]>b[d]&&(a[d]=b[d]);a[c]<b[c]&&(a[c]=b[c])});return a},mapZoom:function(a,b,d,c,e){var f=this.xAxis[0],g=f.max-f.min,h=j(b,f.min+g/2),m=g*a,g=this.yAxis[0],i=g.max-g.min,k=j(d,g.min+i/2);i*=a;h=this.fitToBox({x:h-m*(c?(c-f.pos)/f.len:0.5),y:k-i*(e?(e-g.pos)/g.len:0.5),width:m,height:i},{x:f.dataMin,y:g.dataMin,width:f.dataMax-f.dataMin,height:g.dataMax-g.dataMin});if(c)f.fixTo= -[c-f.pos,b];if(e)g.fixTo=[e-g.pos,d];a!==void 0?(f.setExtremes(h.x,h.x+h.width,!1),g.setExtremes(h.y,h.y+h.height,!1)):(f.setExtremes(void 0,void 0,!1),g.setExtremes(void 0,void 0,!1));this.redraw()}});q(x.prototype,"getAxes",function(a){var b=this.options.colorAxis;a.call(this);this.colorAxis=[];b&&new D(this,b)});q(x.prototype,"render",function(a){var b=this,d=b.options.mapNavigation;a.call(b);b.renderMapNavigation();(j(d.enableDoubleClickZoom,d.enabled)||d.enableDoubleClickZoomTo)&&i.addEvent(b.container, -"dblclick",function(a){b.pointer.onContainerDblClick(a)});j(d.enableMouseWheelZoom,d.enabled)&&i.addEvent(b.container,document.onmousewheel===void 0?"DOMMouseScroll":"mousewheel",function(a){b.pointer.onContainerMouseWheel(a);return!1})});p.map=n(p.scatter,{allAreas:!0,animation:!1,nullColor:"#F8F8F8",borderColor:"silver",borderWidth:1,marker:null,stickyTracking:!1,dataLabels:{format:"{point.value}",verticalAlign:"middle"},turboThreshold:0,tooltip:{followPointer:!0,pointFormat:"{point.name}: {point.value}<br/>"}, -states:{normal:{animation:!0},hover:{brightness:0.2}}});s=t(w,{applyOptions:function(a,b){var d=w.prototype.applyOptions.call(this,a,b),c=this.series,e=c.options,f=e.joinBy;if(e.mapData)if(e=f?c.getMapData(f,d[f]):e.mapData[d.x]){if(c.xyFromShape)d.x=e._midX,d.y=e._midY;r(d,e)}else d.value=d.value||null;return d},setVisible:function(a){var b=this,d=a?"show":"hide";l(["graphic","dataLabel"],function(a){if(b[a])b[a][d]()})},onMouseOver:function(a){clearTimeout(this.colorInterval);w.prototype.onMouseOver.call(this, -a)},onMouseOut:function(){var a=this,b=+new Date,d=v(a.options.color),c=v(a.pointAttr.hover.fill),e=a.series.options.states.normal.animation,f=e&&(e.duration||500);if(f&&d.rgba.length===4&&c.rgba.length===4&&a.state!=="select")delete a.pointAttr[""].fill,clearTimeout(a.colorInterval),a.colorInterval=setInterval(function(){var e=(new Date-b)/f,h=a.graphic;e>1&&(e=1);h&&h.attr("fill",A(c,d,e));e>=1&&clearTimeout(a.colorInterval)},13);w.prototype.onMouseOut.call(a)},zoomTo:function(){var a=this.series; -a.xAxis.setExtremes(this._minX,this._maxX,!1);a.yAxis.setExtremes(this._minY,this._maxY,!1);a.chart.redraw()}});k.map=t(k.scatter,{type:"map",pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",dashstyle:"dashStyle"},pointClass:s,pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],optionalAxis:"colorAxis",trackerGroups:["group","markerGroup","dataLabelsGroup"],getSymbol:u,supportsDrilldown:!0,getExtremesFromAll:!0,useMapGeometry:!0,parallelArrays:["x","y", -"value"],getBox:function(a){var b=Number.MIN_VALUE,d=Number.MAX_VALUE,c=Number.MIN_VALUE,e=Number.MAX_VALUE,f;l(a||[],function(a){if(a.path){if(typeof a.path==="string")a.path=i.splitPath(a.path);var h=a.path||[],m=h.length,k=!1,j=Number.MIN_VALUE,l=Number.MAX_VALUE,o=Number.MIN_VALUE,n=Number.MAX_VALUE;if(!a._foundBox){for(;m--;)typeof h[m]==="number"&&!isNaN(h[m])&&(k?(j=Math.max(j,h[m]),l=Math.min(l,h[m])):(o=Math.max(o,h[m]),n=Math.min(n,h[m])),k=!k);a._midX=l+(j-l)*(a.middleX||0.5);a._midY=n+ -(o-n)*(a.middleY||0.5);a._maxX=j;a._minX=l;a._maxY=o;a._minY=n;a._foundBox=!0}b=Math.max(b,a._maxX);d=Math.min(d,a._minX);c=Math.max(c,a._maxY);e=Math.min(e,a._minY);f=!0}});if(f)this.minY=Math.min(e,j(this.minY,Number.MAX_VALUE)),this.maxY=Math.max(c,j(this.maxY,Number.MIN_VALUE)),this.minX=Math.min(d,j(this.minX,Number.MAX_VALUE)),this.maxX=Math.max(b,j(this.maxX,Number.MIN_VALUE))},getExtremes:function(){y.prototype.getExtremes.call(this,this.valueData);this.chart.hasRendered&&this.isDirtyData&& -this.getBox(this.options.data);this.valueMin=this.dataMin;this.valueMax=this.dataMax;this.dataMin=this.minY;this.dataMax=this.maxY},translatePath:function(a){var b=!1,d=this.xAxis,c=this.yAxis,e=d.min,f=d.transA,d=d.minPixelPadding,g=c.min,h=c.transA,c=c.minPixelPadding,i,j=[];if(a)for(i=a.length;i--;)typeof a[i]==="number"?(j[i]=b?(a[i]-e)*f+d:(a[i]-g)*h+c,b=!b):j[i]=a[i];return j},setData:function(a,b){var d=this.options,c=d.mapData,e=d.joinBy,f=[];this.getBox(a);this.getBox(c);d.allAreas&&c&&(a= -a||[],e&&l(a,function(a){f.push(a[e])}),f="|"+f.join("|")+"|",l(c,function(b){(!e||f.indexOf("|"+b[e]+"|")===-1)&&a.push(n(b,{value:null}))}));y.prototype.setData.call(this,a,b)},getMapData:function(a,b){var d=this.options.mapData,c=this.mapMap,e=d.length;if(!c)c=this.mapMap={};if(c[b]!==void 0)return d[c[b]];else if(b!==void 0)for(;e--;)if(d[e][a]===b)return c[b]=e,d[e]},translateColors:function(){var a=this,b=this.options.nullColor,d=this.colorAxis;l(this.data,function(c){var e=c.value;if(e=e=== -null?b:d?d.toColor(e,c):c.color||a.color)c.color=c.options.color=e})},drawGraph:u,drawDataLabels:u,translate:function(){var a=this,b=a.xAxis,d=a.yAxis;a.generatePoints();l(a.data,function(c){c.plotX=b.toPixels(c._midX,!0);c.plotY=d.toPixels(c._midY,!0);if(a.isDirtyData||a.chart.renderer.isVML)c.shapeType="path",c.shapeArgs={d:a.translatePath(c.path),"vector-effect":"non-scaling-stroke"}});a.translateColors()},drawPoints:function(){var a=this.xAxis,b=this.yAxis,d,c=this.group,e=this.chart,f=e.renderer, -g=function(a,b){var c=a.dataMin,e=a.dataMax;return a.len*(1-d)*((a.min-a.minPixelPadding/a.transA-(c-(e-c)*(b-1)/2))/((e-c-a.max+a.min)*b))};if(!this.transformGroup)this.transformGroup=f.g().attr({scaleX:1,scaleY:1}).add(c);this.isDirtyData||f.isVML?(this.group=this.transformGroup,k.column.prototype.drawPoints.apply(this),this.group=c,l(this.points,function(a){e.hasRendered&&a.graphic&&a.graphic.attr("fill",a.options.color)}),this.transA=a.transA):(d=a.transA/this.transA,d>0.99&&d<1.01?(b=a=0,d=1): -(a=g(a,Math.max(1,this.chart.mapRatio)),b=g(b,1/Math.min(1,this.chart.mapRatio))),this.transformGroup.animate({translateX:a,translateY:b,scaleX:d,scaleY:d}));y.prototype.drawDataLabels.call(this)},render:function(){var a=this,b=y.prototype.render;a.chart.renderer.isVML&&a.data.length>3E3?setTimeout(function(){b.call(a)}):b.call(a)},animate:function(a){var b=this.options.animation,d=this.group,c=this.xAxis,e=this.yAxis,f=c.pos,g=e.pos;if(this.chart.renderer.isSVG)b===!0&&(b={duration:1E3}),a?d.attr({translateX:f+ -c.len/2,translateY:g+e.len/2,scaleX:0.001,scaleY:0.001}):(d.animate({translateX:f,translateY:g,scaleX:1,scaleY:1},b),this.animate=null)},animateDrilldown:function(a){var b=this.chart.plotBox,d=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],c=d.bBox,e=this.chart.options.drilldown.animation;if(!a)a=Math.min(c.width/b.width,c.height/b.height),d.shapeArgs={scaleX:a,scaleY:a,translateX:c.x,translateY:c.y},l(this.points,function(a){a.graphic.attr(d.shapeArgs).animate({scaleX:1,scaleY:1, -translateX:0,translateY:0},e)}),this.animate=null},drawLegendSymbol:i.LegendSymbolMixin.drawRectangle,animateDrillupFrom:function(a){k.column.prototype.animateDrillupFrom.call(this,a)},animateDrillupTo:function(a){k.column.prototype.animateDrillupTo.call(this,a)}});p.mapline=n(p.map,{lineWidth:1,fillColor:"none"});k.mapline=t(k.map,{type:"mapline",pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth",fill:"fillColor"},drawLegendSymbol:k.line.prototype.drawLegendSymbol});p.mappoint=n(p.scatter, -{dataLabels:{enabled:!0,format:"{point.name}",color:"black",style:{textShadow:"0 0 5px white"}}});k.mappoint=t(k.scatter,{type:"mappoint"});if(k.bubble)p.mapbubble=n(p.bubble,{tooltip:{pointFormat:"{point.name}: {point.z}"}}),k.mapbubble=t(k.bubble,{pointClass:t(w,{applyOptions:s.prototype.applyOptions}),xyFromShape:!0,type:"mapbubble",pointArrayMap:["z"],getMapData:k.map.prototype.getMapData,getBox:k.map.prototype.getBox,setData:k.map.prototype.setData});B.topbutton=function(a,b,d,c,e){return E(e, -a,b,d,c,e.r,e.r,0,0)};B.bottombutton=function(a,b,d,c,e){return E(e,a,b,d,c,0,0,e.r,e.r)};i.Renderer===F&&l(["topbutton","bottombutton"],function(a){F.prototype.symbols[a]=B[a]});i.Map=function(a,b){var d={endOnTick:!1,gridLineWidth:0,lineWidth:0,minPadding:0,maxPadding:0,startOnTick:!1,title:null,tickPositions:[]},c;c=a.series;a.series=null;a=n({chart:{panning:"xy",type:"map"},xAxis:d,yAxis:n(d,{reversed:!0})},a,{chart:{inverted:!1,alignTicks:!1,preserveAspectRatio:!0}});a.series=c;return new x(a, -b)}})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/map.src.js b/pykeg/web/static/highcharts/js/modules/map.src.js deleted file mode 100644 index 954aa8d53..000000000 --- a/pykeg/web/static/highcharts/js/modules/map.src.js +++ /dev/null @@ -1,1656 +0,0 @@ -/** - * @license Map plugin v0.2 for Highcharts - * - * (c) 2011-2014 Torstein Honsi - * - * License: www.highcharts.com/license - */ - -/*global HighchartsAdapter*/ -(function (H) { - var UNDEFINED, - Axis = H.Axis, - Chart = H.Chart, - Color = H.Color, - Point = H.Point, - Pointer = H.Pointer, - Legend = H.Legend, - Series = H.Series, - SVGRenderer = H.SVGRenderer, - VMLRenderer = H.VMLRenderer, - - symbols = SVGRenderer.prototype.symbols, - each = H.each, - extend = H.extend, - extendClass = H.extendClass, - merge = H.merge, - pick = H.pick, - numberFormat = H.numberFormat, - defaultOptions = H.getOptions(), - seriesTypes = H.seriesTypes, - plotOptions = defaultOptions.plotOptions, - wrap = H.wrap, - noop = function () {}; - - // Add language - extend(defaultOptions.lang, { - zoomIn: 'Zoom in', - zoomOut: 'Zoom out' - }); - - /* - * Return an intermediate color between two colors, according to pos where 0 - * is the from color and 1 is the to color - */ - function tweenColors(from, to, pos) { - var i = 4, - val, - rgba = []; - - while (i--) { - val = to.rgba[i] + (from.rgba[i] - to.rgba[i]) * (1 - pos); - rgba[i] = i === 3 ? val : Math.round(val); // Do not round opacity - } - return 'rgba(' + rgba.join(',') + ')'; - } - - // Set the default map navigation options - defaultOptions.mapNavigation = { - buttonOptions: { - alignTo: 'plotBox', - align: 'left', - verticalAlign: 'top', - x: 0, - width: 18, - height: 18, - style: { - fontSize: '15px', - fontWeight: 'bold', - textAlign: 'center' - }, - theme: { - 'stroke-width': 1 - } - }, - buttons: { - zoomIn: { - onclick: function () { - this.mapZoom(0.5); - }, - text: '+', - y: 0 - }, - zoomOut: { - onclick: function () { - this.mapZoom(2); - }, - text: '-', - y: 28 - } - } - // enabled: false, - // enableButtons: null, // inherit from enabled - // enableTouchZoom: null, // inherit from enabled - // enableDoubleClickZoom: null, // inherit from enabled - // enableDoubleClickZoomTo: false - // enableMouseWheelZoom: null, // inherit from enabled - }; - - /** - * Utility for reading SVG paths directly. - */ - H.splitPath = function (path) { - var i; - - // Move letters apart - path = path.replace(/([A-Za-z])/g, ' $1 '); - // Trim - path = path.replace(/^\s*/, "").replace(/\s*$/, ""); - - // Split on spaces and commas - path = path.split(/[ ,]+/); - - // Parse numbers - for (i = 0; i < path.length; i++) { - if (!/[a-zA-Z]/.test(path[i])) { - path[i] = parseFloat(path[i]); - } - } - return path; - }; - - // A placeholder for map definitions - H.maps = {}; - - /** - * Override to use the extreme coordinates from the SVG shape, not the - * data values - */ - wrap(Axis.prototype, 'getSeriesExtremes', function (proceed) { - var isXAxis = this.isXAxis, - dataMin, - dataMax, - xData = []; - - // Remove the xData array and cache it locally so that the proceed method doesn't use it - if (isXAxis) { - each(this.series, function (series, i) { - if (series.useMapGeometry) { - xData[i] = series.xData; - series.xData = []; - } - }); - } - - // Call base to reach normal cartesian series (like mappoint) - proceed.call(this); - - // Run extremes logic for map and mapline - if (isXAxis) { - dataMin = pick(this.dataMin, Number.MAX_VALUE); - dataMax = pick(this.dataMax, Number.MIN_VALUE); - each(this.series, function (series, i) { - if (series.useMapGeometry) { - dataMin = Math.min(dataMin, pick(series.minX, dataMin)); - dataMax = Math.max(dataMax, pick(series.maxX, dataMin)); - series.xData = xData[i]; // Reset xData array - } - }); - - this.dataMin = dataMin; - this.dataMax = dataMax; - } - }); - - /** - * Override axis translation to make sure the aspect ratio is always kept - */ - wrap(Axis.prototype, 'setAxisTranslation', function (proceed) { - var chart = this.chart, - mapRatio, - plotRatio = chart.plotWidth / chart.plotHeight, - adjustedAxisLength, - xAxis = chart.xAxis[0], - padAxis, - fixTo, - fixDiff; - - - // Run the parent method - proceed.call(this); - - // On Y axis, handle both - if (chart.options.chart.preserveAspectRatio && this.coll === 'yAxis' && xAxis.transA !== UNDEFINED) { - - // Use the same translation for both axes - this.transA = xAxis.transA = Math.min(this.transA, xAxis.transA); - - mapRatio = chart.mapRatio = plotRatio / ((xAxis.max - xAxis.min) / (this.max - this.min)); - - // What axis to pad to put the map in the middle - padAxis = mapRatio < 1 ? this : xAxis; - - // Pad it - adjustedAxisLength = (padAxis.max - padAxis.min) * padAxis.transA; - padAxis.pixelPadding = padAxis.len - adjustedAxisLength; - padAxis.minPixelPadding = padAxis.pixelPadding / 2; - - fixTo = padAxis.fixTo; - if (fixTo) { - fixDiff = fixTo[1] - padAxis.toValue(fixTo[0], true); - fixDiff *= padAxis.transA; - if (Math.abs(fixDiff) > padAxis.minPixelPadding) { // zooming out again, keep within restricted area - fixDiff = 0; - } - padAxis.minPixelPadding -= fixDiff; - - } - } - }); - - /** - * Override Axis.render in order to delete the fixTo prop - */ - wrap(Axis.prototype, 'render', function (proceed) { - proceed.call(this); - this.fixTo = null; - }); - - // Extend the Pointer - extend(Pointer.prototype, { - - /** - * The event handler for the doubleclick event - */ - onContainerDblClick: function (e) { - var chart = this.chart; - - e = this.normalize(e); - - if (chart.options.mapNavigation.enableDoubleClickZoomTo) { - if (chart.pointer.inClass(e.target, 'highcharts-tracker')) { - chart.hoverPoint.zoomTo(); - } - } else if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - chart.mapZoom( - 0.5, - chart.xAxis[0].toValue(e.chartX), - chart.yAxis[0].toValue(e.chartY), - e.chartX, - e.chartY - ); - } - }, - - /** - * The event handler for the mouse scroll event - */ - onContainerMouseWheel: function (e) { - var chart = this.chart, - delta; - - e = this.normalize(e); - - // Firefox uses e.detail, WebKit and IE uses wheelDelta - delta = e.detail || -(e.wheelDelta / 120); - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - chart.mapZoom( - delta > 0 ? 2 : 1 / 2, - chart.xAxis[0].toValue(e.chartX), - chart.yAxis[0].toValue(e.chartY), - delta > 0 ? undefined : e.chartX, - delta > 0 ? undefined : e.chartY - ); - } - } - }); - - // Implement the pinchType option - wrap(Pointer.prototype, 'init', function (proceed, chart, options) { - - proceed.call(this, chart, options); - - // Pinch status - if (pick(options.mapNavigation.enableTouchZoom, options.mapNavigation.enabled)) { - this.pinchX = this.pinchHor = - this.pinchY = this.pinchVert = true; - } - }); - - // Extend the pinchTranslate method to preserve fixed ratio when zooming - wrap(Pointer.prototype, 'pinchTranslate', function (proceed, zoomHor, zoomVert, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) { - var xBigger; - - proceed.call(this, zoomHor, zoomVert, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); - - // Keep ratio - if (this.chart.options.chart.type === 'map') { - xBigger = transform.scaleX > transform.scaleY; - this.pinchTranslateDirection( - !xBigger, - pinchDown, - touches, - transform, - selectionMarker, - clip, - lastValidTouch, - xBigger ? transform.scaleX : transform.scaleY - ); - } - }); - - - - - /** - * The ColorAxis object for inclusion in gradient legends - */ - var ColorAxis = H.ColorAxis = function () { - this.init.apply(this, arguments); - }; - extend(ColorAxis.prototype, Axis.prototype); - extend(ColorAxis.prototype, { - defaultColorAxisOptions: { - lineWidth: 0, - gridLineWidth: 1, - tickPixelInterval: 72, - startOnTick: true, - endOnTick: true, - offset: 0, - marker: { // docs: use another name? - animation: { - duration: 50 - }, - color: 'gray', - width: 0.01 - }, - labels: { - overflow: 'justify' - }, - minColor: '#EFEFFF', - maxColor: '#102d4c' - }, - init: function (chart, userOptions) { - var horiz = chart.options.legend.layout !== 'vertical', - options; - - // Build the options - options = merge(this.defaultColorAxisOptions, { - side: horiz ? 2 : 1, - reversed: !horiz - }, userOptions, { - isX: horiz, - opposite: !horiz, - showEmpty: false, - title: null - }); - - Axis.prototype.init.call(this, chart, options); - - // Base init() pushes it to the xAxis array, now pop it again - //chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop(); - - // Prepare data classes - if (userOptions.dataClasses) { - this.initDataClasses(userOptions); - } - - // Override original axis properties - this.isXAxis = true; - this.horiz = horiz; - }, - - initDataClasses: function (userOptions) { - var chart = this.chart, - dataClasses, - colorCounter = 0, - options = this.options; - this.dataClasses = dataClasses = []; - - each(userOptions.dataClasses, function (dataClass, i) { - var colors; - - dataClass = merge(dataClass); - dataClasses.push(dataClass); - if (!dataClass.color) { - if (options.dataClassColor === 'category') { - colors = chart.options.colors; - dataClass.color = colors[colorCounter++]; - // loop back to zero - if (colorCounter === colors.length) { - colorCounter = 0; - } - } else { - dataClass.color = tweenColors(Color(options.minColor), Color(options.maxColor), i / (userOptions.dataClasses.length - 1)); - } - } - }); - }, - - /** - * Extend the setOptions method to process extreme colors and color - * stops. - */ - setOptions: function (userOptions) { - Axis.prototype.setOptions.call(this, userOptions); - - this.options.crosshair = this.options.marker; - - this.stops = userOptions.stops || [ - [0, this.options.minColor], - [1, this.options.maxColor] - ]; - each(this.stops, function (stop) { - stop.color = Color(stop[1]); - }); - this.coll = 'colorAxis'; - }, - - setAxisSize: function () { - var symbol = this.legendSymbol, - chart = this.chart; - - if (symbol) { - this.left = symbol.x; - this.top = symbol.y; - this.width = symbol.width; - this.height = symbol.height; - this.right = chart.chartWidth - this.left - this.width; - this.bottom = chart.chartHeight - this.top - this.height; - - this.len = this.horiz ? this.width : this.height; - this.pos = this.horiz ? this.left : this.top; - } - }, - - /** - * Translate from a value to a color - */ - toColor: function (value, point) { - var pos, - stops = this.stops, - from, - to, - color, - dataClasses = this.dataClasses, - dataClass, - i; - - if (dataClasses) { - i = dataClasses.length; - while (i--) { - dataClass = dataClasses[i]; - from = dataClass.from; - to = dataClass.to; - if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) { - color = dataClass.color; - if (point) { - point.dataClass = i; - } - break; - } - } - - } else { - - if (this.isLog) { - value = this.val2lin(value); - } - pos = 1 - ((this.max - value) / (this.max - this.min)); - i = stops.length; - while (i--) { - if (pos > stops[i][0]) { - break; - } - } - from = stops[i] || stops[i + 1]; - to = stops[i + 1] || from; - - // The position within the gradient - pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1); - - color = tweenColors( - from.color, - to.color, - pos - ); - } - return color; - }, - - getOffset: function () { - var group = this.legendGroup; - if (group) { - - Axis.prototype.getOffset.call(this); - - if (!this.axisGroup.parentGroup) { - - // Move the axis elements inside the legend group - this.axisGroup.add(group); - this.gridGroup.add(group); - this.labelGroup.add(group); - - this.added = true; - } - } - }, - - /** - * Create the color gradient - */ - setLegendColor: function () { - var grad, - horiz = this.horiz, - options = this.options; - - grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1]; - this.legendColor = { - linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] }, - stops: options.stops || [ - [0, options.minColor], - [1, options.maxColor] - ] - }; - }, - - /** - * The color axis appears inside the legend and has its own legend symbol - */ - drawLegendSymbol: function (legend, item) { - var padding = legend.padding, - legendOptions = legend.options, - horiz = this.horiz, - box, - width = pick(legendOptions.symbolWidth, horiz ? 200 : 12), - height = pick(legendOptions.symbolHeight, horiz ? 12 : 200), - labelPadding = pick(legendOptions.labelPadding, horiz ? 10 : 30); - - this.setLegendColor(); - - // Create the gradient - item.legendSymbol = this.chart.renderer.rect( - 0, - legend.baseline - 11, - width, - height - ).attr({ - zIndex: 1 - }).add(item.legendGroup); - box = item.legendSymbol.getBBox(); - - // Set how much space this legend item takes up - this.legendItemWidth = width + padding + (horiz ? 0 : labelPadding); - this.legendItemHeight = height + padding + (horiz ? labelPadding : 0); - }, - /** - * Fool the legend - */ - setState: noop, - visible: true, - setVisible: noop, - getSeriesExtremes: function () { - var series; - if (this.series.length) { - series = this.series[0]; - this.dataMin = series.valueMin; - this.dataMax = series.valueMax; - } - }, - drawCrosshair: function (e, point) { - var newCross = !this.cross, - plotX = point && point.plotX, - plotY = point && point.plotY, - crossPos, - axisPos = this.pos, - axisLen = this.len; - - if (point) { - crossPos = this.toPixels(point.value); - if (crossPos < axisPos) { - crossPos = axisPos - 2; - } else if (crossPos > axisPos + axisLen) { - crossPos = axisPos + axisLen + 2; - } - - point.plotX = crossPos; - point.plotY = this.len - crossPos; - Axis.prototype.drawCrosshair.call(this, e, point); - point.plotX = plotX; - point.plotY = plotY; - - if (!newCross && this.cross) { - this.cross - .attr({ - fill: this.crosshair.color - }) - .add(this.labelGroup); - } - } - }, - getPlotLinePath: function (a, b, c, d, pos) { - if (pos) { // crosshairs only - return this.horiz ? - ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] : - ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z']; - } else { - return Axis.prototype.getPlotLinePath.call(this, a, b, c, d); - } - }, - - update: function (newOptions, redraw) { - Axis.prototype.update.call(this, newOptions, redraw); - if (this.legendItem) { - this.setLegendColor(); - this.chart.legend.colorizeItem(this, true); - } - }, - - /** - * Get the legend item symbols for data classes - */ - getDataClassLegendSymbols: function () { - var axis = this, - chart = this.chart, - legendItems = [], - legendOptions = chart.options.legend, - valueDecimals = legendOptions.valueDecimals, - valueSuffix = legendOptions.valueSuffix || '', - name; - - each(this.dataClasses, function (dataClass, i) { - var vis = true, - from = dataClass.from, - to = dataClass.to; - - // Assemble the default name. This can be overridden by legend.options.labelFormatter - name = ''; - if (from === UNDEFINED) { - name = '< '; - } else if (to === UNDEFINED) { - name = '> '; - } - if (from !== UNDEFINED) { - name += numberFormat(from, valueDecimals) + valueSuffix; - } - if (from !== UNDEFINED && to !== UNDEFINED) { - name += ' - '; - } - if (to !== UNDEFINED) { - name += numberFormat(to, valueDecimals) + valueSuffix; - } - - // Add a mock object to the legend items - legendItems.push(H.extend({ - chart: chart, - name: name, - options: {}, - drawLegendSymbol: H.LegendSymbolMixin.drawRectangle, - visible: true, - setState: noop, - setVisible: function () { - vis = this.visible = !vis; - each(axis.series, function (series) { - each(series.points, function (point) { - if (point.dataClass === i) { - point.setVisible(vis); - } - }); - }); - - chart.legend.colorizeItem(this, vis); - } - }, dataClass)); - }); - return legendItems; - } - }); - - /** - * Wrap the legend getAllItems method to add the color axis. This also removes the - * axis' own series to prevent them from showing up individually. - */ - wrap(Legend.prototype, 'getAllItems', function (proceed) { - var allItems = [], - colorAxis = this.chart.colorAxis[0]; - - if (colorAxis) { - - // Data classes - if (colorAxis.options.dataClasses) { - allItems = allItems.concat(colorAxis.getDataClassLegendSymbols()); - // Gradient legend - } else { - // Add this axis on top - allItems.push(colorAxis); - } - - // Don't add the color axis' series - each(colorAxis.series, function (series) { - series.options.showInLegend = false; - }); - } - - return allItems.concat(proceed.call(this)); - }); - - - // Add events to the Chart object itself - extend(Chart.prototype, { - renderMapNavigation: function () { - var chart = this, - options = this.options.mapNavigation, - buttons = options.buttons, - n, - button, - buttonOptions, - attr, - states, - outerHandler = function () { - this.handler.call(chart); - }; - - if (pick(options.enableButtons, options.enabled) && !chart.renderer.forExport) { - for (n in buttons) { - if (buttons.hasOwnProperty(n)) { - buttonOptions = merge(options.buttonOptions, buttons[n]); - attr = buttonOptions.theme; - states = attr.states; - button = chart.renderer.button( - buttonOptions.text, - 0, - 0, - outerHandler, - attr, - states && states.hover, - states && states.select, - 0, - n === 'zoomIn' ? 'topbutton' : 'bottombutton' - ) - .attr({ - width: buttonOptions.width, - height: buttonOptions.height, - title: chart.options.lang[n], - zIndex: 5 - }) - .css(buttonOptions.style) - .add(); - button.handler = buttonOptions.onclick; - button.align(extend(buttonOptions, { width: button.width, height: 2 * button.height }), null, buttonOptions.alignTo); - } - } - } - }, - - /** - * Fit an inner box to an outer. If the inner box overflows left or right, align it to the sides of the - * outer. If it overflows both sides, fit it within the outer. This is a pattern that occurs more places - * in Highcharts, perhaps it should be elevated to a common utility function. - */ - fitToBox: function (inner, outer) { - each([['x', 'width'], ['y', 'height']], function (dim) { - var pos = dim[0], - size = dim[1]; - - if (inner[pos] + inner[size] > outer[pos] + outer[size]) { // right overflow - if (inner[size] > outer[size]) { // the general size is greater, fit fully to outer - inner[size] = outer[size]; - inner[pos] = outer[pos]; - } else { // align right - inner[pos] = outer[pos] + outer[size] - inner[size]; - } - } - if (inner[size] > outer[size]) { - inner[size] = outer[size]; - } - if (inner[pos] < outer[pos]) { - inner[pos] = outer[pos]; - } - }); - - - return inner; - }, - - /** - * Zoom the map in or out by a certain amount. Less than 1 zooms in, greater than 1 zooms out. - */ - mapZoom: function (howMuch, centerXArg, centerYArg, mouseX, mouseY) { - /*if (this.isMapZooming) { - this.mapZoomQueue = arguments; - return; - }*/ - - var chart = this, - xAxis = chart.xAxis[0], - xRange = xAxis.max - xAxis.min, - centerX = pick(centerXArg, xAxis.min + xRange / 2), - newXRange = xRange * howMuch, - yAxis = chart.yAxis[0], - yRange = yAxis.max - yAxis.min, - centerY = pick(centerYArg, yAxis.min + yRange / 2), - newYRange = yRange * howMuch, - fixToX = mouseX ? ((mouseX - xAxis.pos) / xAxis.len) : 0.5, - fixToY = mouseY ? ((mouseY - yAxis.pos) / yAxis.len) : 0.5, - newXMin = centerX - newXRange * fixToX, - newYMin = centerY - newYRange * fixToY, - newExt = chart.fitToBox({ - x: newXMin, - y: newYMin, - width: newXRange, - height: newYRange - }, { - x: xAxis.dataMin, - y: yAxis.dataMin, - width: xAxis.dataMax - xAxis.dataMin, - height: yAxis.dataMax - yAxis.dataMin - }); - - // When mousewheel zooming, fix the point under the mouse - if (mouseX) { - xAxis.fixTo = [mouseX - xAxis.pos, centerXArg]; - } - if (mouseY) { - yAxis.fixTo = [mouseY - yAxis.pos, centerYArg]; - } - - // Zoom - if (howMuch !== undefined) { - xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false); - yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false); - - // Reset zoom - } else { - xAxis.setExtremes(undefined, undefined, false); - yAxis.setExtremes(undefined, undefined, false); - } - - // Prevent zooming until this one is finished animating - /*delay = animation ? animation.duration || 500 : 0; - if (delay) { - chart.isMapZooming = true; - setTimeout(function () { - chart.isMapZooming = false; - if (chart.mapZoomQueue) { - chart.mapZoom.apply(chart, chart.mapZoomQueue); - } - chart.mapZoomQueue = null; - }, delay); - }*/ - - chart.redraw(); - } - }); - - /** - * Extend the chart getAxes method to also get the color axis - */ - wrap(Chart.prototype, 'getAxes', function (proceed) { - - var options = this.options, - colorAxisOptions = options.colorAxis; - - proceed.call(this); - - this.colorAxis = []; - if (colorAxisOptions) { - proceed = new ColorAxis(this, colorAxisOptions); // Fake assignment for jsLint - } - }); - - /** - * Extend the Chart.render method to add zooming and panning - */ - wrap(Chart.prototype, 'render', function (proceed) { - var chart = this, - mapNavigation = chart.options.mapNavigation; - - proceed.call(chart); - - // Render the plus and minus buttons - chart.renderMapNavigation(); - - // Add the double click event - if (pick(mapNavigation.enableDoubleClickZoom, mapNavigation.enabled) || mapNavigation.enableDoubleClickZoomTo) { - H.addEvent(chart.container, 'dblclick', function (e) { - chart.pointer.onContainerDblClick(e); - }); - } - - // Add the mousewheel event - if (pick(mapNavigation.enableMouseWheelZoom, mapNavigation.enabled)) { - H.addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) { - chart.pointer.onContainerMouseWheel(e); - return false; - }); - } - }); - - - - /** - * Extend the default options with map options - */ - plotOptions.map = merge(plotOptions.scatter, { - allAreas: true, - animation: false, // makes the complex shapes slow - nullColor: '#F8F8F8', - borderColor: 'silver', - borderWidth: 1, - marker: null, - stickyTracking: false, - dataLabels: { - format: '{point.value}', - verticalAlign: 'middle' - }, - turboThreshold: 0, - tooltip: { - followPointer: true, - pointFormat: '{point.name}: {point.value}<br/>' - }, - states: { - normal: { - animation: true - }, - hover: { - brightness: 0.2 - } - } - }); - - /** - * The MapAreaPoint object - */ - var MapAreaPoint = extendClass(Point, { - /** - * Extend the Point object to split paths - */ - applyOptions: function (options, x) { - - var point = Point.prototype.applyOptions.call(this, options, x), - series = this.series, - seriesOptions = series.options, - joinBy = seriesOptions.joinBy, - mapPoint; - - if (seriesOptions.mapData) { - mapPoint = joinBy ? - series.getMapData(joinBy, point[joinBy]) : // Join by a string - seriesOptions.mapData[point.x]; // Use array position (faster) - - if (mapPoint) { - // This applies only to bubbles - if (series.xyFromShape) { - point.x = mapPoint._midX; - point.y = mapPoint._midY; - } - extend(point, mapPoint); // copy over properties - } else { - point.value = point.value || null; - } - } - - return point; - }, - - /** - * Set the visibility of a single map area - */ - setVisible: function (vis) { - var point = this, - method = vis ? 'show' : 'hide'; - - // Show and hide associated elements - each(['graphic', 'dataLabel'], function (key) { - if (point[key]) { - point[key][method](); - } - }); - }, - - /** - * Stop the fade-out - */ - onMouseOver: function (e) { - clearTimeout(this.colorInterval); - Point.prototype.onMouseOver.call(this, e); - }, - /** - * Custom animation for tweening out the colors. Animation reduces blinking when hovering - * over islands and coast lines. We run a custom implementation of animation becuase we - * need to be able to run this independently from other animations like zoom redraw. Also, - * adding color animation to the adapters would introduce almost the same amount of code. - */ - onMouseOut: function () { - var point = this, - start = +new Date(), - normalColor = Color(point.options.color), - hoverColor = Color(point.pointAttr.hover.fill), - animation = point.series.options.states.normal.animation, - duration = animation && (animation.duration || 500); - - if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4 && point.state !== 'select') { - delete point.pointAttr[''].fill; // avoid resetting it in Point.setState - - clearTimeout(point.colorInterval); - point.colorInterval = setInterval(function () { - var pos = (new Date() - start) / duration, - graphic = point.graphic; - if (pos > 1) { - pos = 1; - } - if (graphic) { - graphic.attr('fill', tweenColors(hoverColor, normalColor, pos)); - } - if (pos >= 1) { - clearTimeout(point.colorInterval); - } - }, 13); - } - Point.prototype.onMouseOut.call(point); - }, - - /** - * Zoom the chart to view a specific area point - */ - zoomTo: function () { - var point = this, - series = point.series; - - series.xAxis.setExtremes( - point._minX, - point._maxX, - false - ); - series.yAxis.setExtremes( - point._minY, - point._maxY, - false - ); - series.chart.redraw(); - } - }); - - /** - * Add the series type - */ - seriesTypes.map = extendClass(seriesTypes.scatter, { - type: 'map', - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'borderColor', - 'stroke-width': 'borderWidth', - fill: 'color', - dashstyle: 'dashStyle' - }, - pointClass: MapAreaPoint, - pointArrayMap: ['value'], - axisTypes: ['xAxis', 'yAxis', 'colorAxis'], - optionalAxis: 'colorAxis', - trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], - getSymbol: noop, - supportsDrilldown: true, - getExtremesFromAll: true, - useMapGeometry: true, // get axis extremes from paths, not values - parallelArrays: ['x', 'y', 'value'], - - /** - * Get the bounding box of all paths in the map combined. - */ - getBox: function (paths) { - var maxX = Number.MIN_VALUE, - minX = Number.MAX_VALUE, - maxY = Number.MIN_VALUE, - minY = Number.MAX_VALUE, - hasBox; - - // Find the bounding box - each(paths || [], function (point) { - - if (point.path) { - if (typeof point.path === 'string') { - point.path = H.splitPath(point.path); - } - - var path = point.path || [], - i = path.length, - even = false, // while loop reads from the end - pointMaxX = Number.MIN_VALUE, - pointMinX = Number.MAX_VALUE, - pointMaxY = Number.MIN_VALUE, - pointMinY = Number.MAX_VALUE; - - // The first time a map point is used, analyze its box - if (!point._foundBox) { - while (i--) { - if (typeof path[i] === 'number' && !isNaN(path[i])) { - if (even) { // even = x - pointMaxX = Math.max(pointMaxX, path[i]); - pointMinX = Math.min(pointMinX, path[i]); - } else { // odd = Y - pointMaxY = Math.max(pointMaxY, path[i]); - pointMinY = Math.min(pointMinY, path[i]); - } - even = !even; - } - } - // Cache point bounding box for use to position data labels, bubbles etc - point._midX = pointMinX + (pointMaxX - pointMinX) * (point.middleX || 0.5); // pick is slower and very marginally needed - point._midY = pointMinY + (pointMaxY - pointMinY) * (point.middleY || 0.5); - point._maxX = pointMaxX; - point._minX = pointMinX; - point._maxY = pointMaxY; - point._minY = pointMinY; - point._foundBox = true; - } - - maxX = Math.max(maxX, point._maxX); - minX = Math.min(minX, point._minX); - maxY = Math.max(maxY, point._maxY); - minY = Math.min(minY, point._minY); - - hasBox = true; - } - }); - - // Set the box for the whole series - if (hasBox) { - this.minY = Math.min(minY, pick(this.minY, Number.MAX_VALUE)); - this.maxY = Math.max(maxY, pick(this.maxY, Number.MIN_VALUE)); - this.minX = Math.min(minX, pick(this.minX, Number.MAX_VALUE)); - this.maxX = Math.max(maxX, pick(this.maxX, Number.MIN_VALUE)); - } - }, - - getExtremes: function () { - // Get the actual value extremes for colors - Series.prototype.getExtremes.call(this, this.valueData); - - // Recalculate box on updated data - if (this.chart.hasRendered && this.isDirtyData) { - this.getBox(this.options.data); - } - - this.valueMin = this.dataMin; - this.valueMax = this.dataMax; - - // Extremes for the mock Y axis - this.dataMin = this.minY; - this.dataMax = this.maxY; - }, - - /** - * Translate the path so that it automatically fits into the plot area box - * @param {Object} path - */ - translatePath: function (path) { - - var series = this, - even = false, // while loop reads from the end - xAxis = series.xAxis, - yAxis = series.yAxis, - xMin = xAxis.min, - xTransA = xAxis.transA, - xMinPixelPadding = xAxis.minPixelPadding, - yMin = yAxis.min, - yTransA = yAxis.transA, - yMinPixelPadding = yAxis.minPixelPadding, - i, - ret = []; // Preserve the original - - // Do the translation - if (path) { - i = path.length; - while (i--) { - if (typeof path[i] === 'number') { - ret[i] = even ? - (path[i] - xMin) * xTransA + xMinPixelPadding : - (path[i] - yMin) * yTransA + yMinPixelPadding; - even = !even; - } else { - ret[i] = path[i]; - } - } - } - - return ret; - }, - - /** - * Extend setData to join in mapData. If the allAreas option is true, all areas - * from the mapData are used, and those that don't correspond to a data value - * are given null values. - */ - setData: function (data, redraw) { - var options = this.options, - mapData = options.mapData, - joinBy = options.joinBy, - dataUsed = []; - - - this.getBox(data); - this.getBox(mapData); - if (options.allAreas && mapData) { - - data = data || []; - - // Registered the point codes that actually hold data - if (joinBy) { - each(data, function (point) { - dataUsed.push(point[joinBy]); - }); - } - - // Add those map points that don't correspond to data, which will be drawn as null points - dataUsed = '|' + dataUsed.join('|') + '|'; // String search is faster than array.indexOf - each(mapData, function (mapPoint) { - if (!joinBy || dataUsed.indexOf('|' + mapPoint[joinBy] + '|') === -1) { - data.push(merge(mapPoint, { value: null })); - } - }); - } - Series.prototype.setData.call(this, data, redraw); - }, - - /** - * For each point, get the corresponding map data - */ - getMapData: function (key, value) { - var options = this.options, - mapData = options.mapData, - mapMap = this.mapMap, - i = mapData.length; - - // Create a cache for quicker lookup second time - if (!mapMap) { - mapMap = this.mapMap = {}; - } - if (mapMap[value] !== undefined) { - return mapData[mapMap[value]]; - - } else if (value !== undefined) { - while (i--) { - if (mapData[i][key] === value) { - mapMap[value] = i; // cache it - return mapData[i]; - } - } - } - }, - - /** - * In choropleth maps, the color is a result of the value, so this needs translation too - */ - translateColors: function () { - var series = this, - nullColor = this.options.nullColor, - colorAxis = this.colorAxis; - - each(this.data, function (point) { - var value = point.value, - color; - - color = value === null ? nullColor : colorAxis ? colorAxis.toColor(value, point) : (point.color) || series.color; - - if (color) { - point.color = point.options.color = color; - } - }); - }, - - /** - * No graph for the map series - */ - drawGraph: noop, - - /** - * We need the points' bounding boxes in order to draw the data labels, so - * we skip it now and call it from drawPoints instead. - */ - drawDataLabels: noop, - - /** - * Add the path option for data points. Find the max value for color calculation. - */ - translate: function () { - var series = this, - xAxis = series.xAxis, - yAxis = series.yAxis; - - series.generatePoints(); - - each(series.data, function (point) { - - // Record the middle point (loosely based on centroid), determined - // by the middleX and middleY options. - point.plotX = xAxis.toPixels(point._midX, true); - point.plotY = yAxis.toPixels(point._midY, true); - - if (series.isDirtyData || series.chart.renderer.isVML) { - - point.shapeType = 'path'; - point.shapeArgs = { - //d: display ? series.translatePath(point.path) : '' - d: series.translatePath(point.path), - 'vector-effect': 'non-scaling-stroke' - }; - } - }); - - series.translateColors(); - }, - - /** - * Use the drawPoints method of column, that is able to handle simple shapeArgs. - * Extend it by assigning the tooltip position. - */ - drawPoints: function () { - var series = this, - xAxis = series.xAxis, - yAxis = series.yAxis, - scale, - translateX, - group = series.group, - chart = series.chart, - renderer = chart.renderer, - translateY, - getTranslate = function (axis, mapRatio) { - var dataMin = axis.dataMin, - dataMax = axis.dataMax, - fullDataMin = dataMin - ((dataMax - dataMin) * (mapRatio - 1) / 2), - fullMin = axis.min - axis.minPixelPadding / axis.transA, - minOffset = fullMin - fullDataMin, - centerOffset = (dataMax - dataMin - axis.max + axis.min) * mapRatio, - center = minOffset / centerOffset; - return (axis.len * (1 - scale)) * center; - }; - - // Set a group that handles transform during zooming and panning in order to preserve clipping - // on series.group - if (!series.transformGroup) { - series.transformGroup = renderer.g() - .attr({ - scaleX: 1, - scaleY: 1 - }) - .add(group); - } - - // Draw the shapes again - if (series.isDirtyData || renderer.isVML) { - - // Draw them in transformGroup - series.group = series.transformGroup; - seriesTypes.column.prototype.drawPoints.apply(series); - series.group = group; // Reset - - // Individual point actions - each(series.points, function (point) { - - // Reset color on update/redraw - if (chart.hasRendered && point.graphic) { - point.graphic.attr('fill', point.options.color); - } - - }); - - // Set the base for later scale-zooming - this.transA = xAxis.transA; - - // Just update the scale and transform for better performance - } else { - scale = xAxis.transA / this.transA; - if (scale > 0.99 && scale < 1.01) { // rounding errors - translateX = 0; - translateY = 0; - scale = 1; - - } else { - translateX = getTranslate(xAxis, Math.max(1, series.chart.mapRatio)); - translateY = getTranslate(yAxis, 1 / Math.min(1, series.chart.mapRatio)); - } - - this.transformGroup.animate({ - translateX: translateX, - translateY: translateY, - scaleX: scale, - scaleY: scale - }); - - } - - - // Now draw the data labels - Series.prototype.drawDataLabels.call(series); - - }, - - /** - * Override render to throw in an async call in IE8. Otherwise it chokes on the US counties demo. - */ - render: function () { - var series = this, - render = Series.prototype.render; - - // Give IE8 some time to breathe. - if (series.chart.renderer.isVML && series.data.length > 3000) { - setTimeout(function () { - render.call(series); - }); - } else { - render.call(series); - } - }, - - /** - * The initial animation for the map series. By default, animation is disabled. - * Animation of map shapes is not at all supported in VML browsers. - */ - animate: function (init) { - var chart = this.chart, - animation = this.options.animation, - group = this.group, - xAxis = this.xAxis, - yAxis = this.yAxis, - left = xAxis.pos, - top = yAxis.pos; - - if (chart.renderer.isSVG) { - - if (animation === true) { - animation = { - duration: 1000 - }; - } - - // Initialize the animation - if (init) { - - // Scale down the group and place it in the center - group.attr({ - translateX: left + xAxis.len / 2, - translateY: top + yAxis.len / 2, - scaleX: 0.001, // #1499 - scaleY: 0.001 - }); - - // Run the animation - } else { - group.animate({ - translateX: left, - translateY: top, - scaleX: 1, - scaleY: 1 - }, animation); - - // Delete this function to allow it only once - this.animate = null; - } - } - }, - - /** - * Animate in the new series from the clicked point in the old series. - * Depends on the drilldown.js module - */ - animateDrilldown: function (init) { - var toBox = this.chart.plotBox, - level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], - fromBox = level.bBox, - animationOptions = this.chart.options.drilldown.animation, - scale; - - if (!init) { - - scale = Math.min(fromBox.width / toBox.width, fromBox.height / toBox.height); - level.shapeArgs = { - scaleX: scale, - scaleY: scale, - translateX: fromBox.x, - translateY: fromBox.y - }; - - // TODO: Animate this.group instead - each(this.points, function (point) { - - point.graphic - .attr(level.shapeArgs) - .animate({ - scaleX: 1, - scaleY: 1, - translateX: 0, - translateY: 0 - }, animationOptions); - - }); - - this.animate = null; - } - - }, - - drawLegendSymbol: H.LegendSymbolMixin.drawRectangle, - - /** - * When drilling up, pull out the individual point graphics from the lower series - * and animate them into the origin point in the upper series. - */ - animateDrillupFrom: function (level) { - seriesTypes.column.prototype.animateDrillupFrom.call(this, level); - }, - - - /** - * When drilling up, keep the upper series invisible until the lower series has - * moved into place - */ - animateDrillupTo: function (init) { - seriesTypes.column.prototype.animateDrillupTo.call(this, init); - } - }); - - - // The mapline series type - plotOptions.mapline = merge(plotOptions.map, { - lineWidth: 1, - fillColor: 'none' - }); - seriesTypes.mapline = extendClass(seriesTypes.map, { - type: 'mapline', - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'color', - 'stroke-width': 'lineWidth', - fill: 'fillColor' - }, - drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol - }); - - // The mappoint series type - plotOptions.mappoint = merge(plotOptions.scatter, { - dataLabels: { - enabled: true, - format: '{point.name}', - color: 'black', - style: { - textShadow: '0 0 5px white' - } - } - }); - seriesTypes.mappoint = extendClass(seriesTypes.scatter, { - type: 'mappoint' - }); - - // The mapbubble series type - if (seriesTypes.bubble) { - - plotOptions.mapbubble = merge(plotOptions.bubble, { - tooltip: { - pointFormat: '{point.name}: {point.z}' - } - }); - seriesTypes.mapbubble = extendClass(seriesTypes.bubble, { - pointClass: extendClass(Point, { - applyOptions: MapAreaPoint.prototype.applyOptions - }), - xyFromShape: true, - type: 'mapbubble', - pointArrayMap: ['z'], // If one single value is passed, it is interpreted as z - /** - * Return the map area identified by the dataJoinBy option - */ - getMapData: seriesTypes.map.prototype.getMapData, - getBox: seriesTypes.map.prototype.getBox, - setData: seriesTypes.map.prototype.setData - }); - } - - // Create symbols for the zoom buttons - function selectiveRoundedRect(attr, x, y, w, h, rTopLeft, rTopRight, rBottomRight, rBottomLeft) { - var normalize = (attr['stroke-width'] % 2 / 2); - - x -= normalize; - y -= normalize; - - return ['M', x + rTopLeft, y, - // top side - 'L', x + w - rTopRight, y, - // top right corner - 'C', x + w - rTopRight / 2, y, x + w, y + rTopRight / 2, x + w, y + rTopRight, - // right side - 'L', x + w, y + h - rBottomRight, - // bottom right corner - 'C', x + w, y + h - rBottomRight / 2, x + w - rBottomRight / 2, y + h, x + w - rBottomRight, y + h, - // bottom side - 'L', x + rBottomLeft, y + h, - // bottom left corner - 'C', x + rBottomLeft / 2, y + h, x, y + h - rBottomLeft / 2, x, y + h - rBottomLeft, - // left side - 'L', x, y + rTopLeft, - // top left corner - 'C', x, y + rTopLeft / 2, x + rTopLeft / 2, y, x + rTopLeft, y, - 'Z' - ]; - } - symbols.topbutton = function (x, y, w, h, attr) { - return selectiveRoundedRect(attr, x, y, w, h, attr.r, attr.r, 0, 0); - }; - symbols.bottombutton = function (x, y, w, h, attr) { - return selectiveRoundedRect(attr, x, y, w, h, 0, 0, attr.r, attr.r); - }; - // The symbol callbacks are generated on the SVGRenderer object in all browsers. Even - // VML browsers need this in order to generate shapes in export. Now share - // them with the VMLRenderer. - if (H.Renderer === VMLRenderer) { - each(['topbutton', 'bottombutton'], function (shape) { - VMLRenderer.prototype.symbols[shape] = symbols[shape]; - }); - } - - - /** - * A wrapper for Chart with all the default values for a Map - */ - H.Map = function (options, callback) { - - var hiddenAxis = { - endOnTick: false, - gridLineWidth: 0, - lineWidth: 0, - minPadding: 0, - maxPadding: 0, - startOnTick: false, - title: null, - tickPositions: [] - //tickInterval: 500, - //gridZIndex: 10 - }, - seriesOptions; - - // Don't merge the data - seriesOptions = options.series; - options.series = null; - - options = merge({ - chart: { - panning: 'xy', - type: 'map' - }, - xAxis: hiddenAxis, - yAxis: merge(hiddenAxis, { reversed: true }) - }, - options, // user's options - - { // forced options - chart: { - inverted: false, - alignTicks: false, - preserveAspectRatio: true - } - }); - - options.series = seriesOptions; - - - return new Chart(options, callback); - }; -}(Highcharts)); - diff --git a/pykeg/web/static/highcharts/js/modules/no-data-to-display.js b/pykeg/web/static/highcharts/js/modules/no-data-to-display.js deleted file mode 100644 index 6d4e7b098..000000000 --- a/pykeg/web/static/highcharts/js/modules/no-data-to-display.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - Highcharts JS v3.0.9 (2014-01-15) - Plugin for displaying a message when there is no data visible in chart. - - (c) 2010-2014 Highsoft AS - Author: Oystein Moseng - - License: www.highcharts.com/license -*/ -(function(c){function f(){return!!this.points.length}function g(){this.hasData()?this.hideNoData():this.showNoData()}var d=c.seriesTypes,e=c.Chart.prototype,h=c.getOptions(),i=c.extend;i(h.lang,{noData:"No data to display"});h.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"},attr:{},style:{fontWeight:"bold",fontSize:"12px",color:"#60606a"}};d.pie.prototype.hasData=f;if(d.gauge)d.gauge.prototype.hasData=f;if(d.waterfall)d.waterfall.prototype.hasData=f;c.Series.prototype.hasData=function(){return this.dataMax!== -void 0&&this.dataMin!==void 0};e.showNoData=function(a){var b=this.options,a=a||b.lang.noData,b=b.noData;if(!this.noDataLabel)this.noDataLabel=this.renderer.label(a,0,0,null,null,null,null,null,"no-data").attr(b.attr).css(b.style).add(),this.noDataLabel.align(i(this.noDataLabel.getBBox(),b.position),!1,"plotBox")};e.hideNoData=function(){if(this.noDataLabel)this.noDataLabel=this.noDataLabel.destroy()};e.hasData=function(){for(var a=this.series,b=a.length;b--;)if(a[b].hasData()&&!a[b].options.isInternal)return!0; -return!1};e.callbacks.push(function(a){c.addEvent(a,"load",g);c.addEvent(a,"redraw",g)})})(Highcharts); diff --git a/pykeg/web/static/highcharts/js/modules/no-data-to-display.src.js b/pykeg/web/static/highcharts/js/modules/no-data-to-display.src.js deleted file mode 100644 index 4d849781c..000000000 --- a/pykeg/web/static/highcharts/js/modules/no-data-to-display.src.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @license Highcharts JS v3.0.9 (2014-01-15) - * Plugin for displaying a message when there is no data visible in chart. - * - * (c) 2010-2014 Highsoft AS - * Author: Oystein Moseng - * - * License: www.highcharts.com/license - */ - -(function (H) { // docs - - var seriesTypes = H.seriesTypes, - chartPrototype = H.Chart.prototype, - defaultOptions = H.getOptions(), - extend = H.extend; - - // Add language option - extend(defaultOptions.lang, { - noData: 'No data to display' - }); - - // Add default display options for message - defaultOptions.noData = { - position: { - x: 0, - y: 0, - align: 'center', - verticalAlign: 'middle' - }, - attr: { - }, - style: { - fontWeight: 'bold', - fontSize: '12px', - color: '#60606a' - } - }; - - /** - * Define hasData functions for series. These return true if there are data points on this series within the plot area - */ - function hasDataPie() { - return !!this.points.length; /* != 0 */ - } - - seriesTypes.pie.prototype.hasData = hasDataPie; - - if (seriesTypes.gauge) { - seriesTypes.gauge.prototype.hasData = hasDataPie; - } - - if (seriesTypes.waterfall) { - seriesTypes.waterfall.prototype.hasData = hasDataPie; - } - - H.Series.prototype.hasData = function () { - return this.dataMax !== undefined && this.dataMin !== undefined; - }; - - /** - * Display a no-data message. - * - * @param {String} str An optional message to show in place of the default one - */ - chartPrototype.showNoData = function (str) { - var chart = this, - options = chart.options, - text = str || options.lang.noData, - noDataOptions = options.noData; - - if (!chart.noDataLabel) { - chart.noDataLabel = chart.renderer.label(text, 0, 0, null, null, null, null, null, 'no-data') - .attr(noDataOptions.attr) - .css(noDataOptions.style) - .add(); - chart.noDataLabel.align(extend(chart.noDataLabel.getBBox(), noDataOptions.position), false, 'plotBox'); - } - }; - - /** - * Hide no-data message - */ - chartPrototype.hideNoData = function () { - var chart = this; - if (chart.noDataLabel) { - chart.noDataLabel = chart.noDataLabel.destroy(); - } - }; - - /** - * Returns true if there are data points within the plot area now - */ - chartPrototype.hasData = function () { - var chart = this, - series = chart.series, - i = series.length; - - while (i--) { - if (series[i].hasData() && !series[i].options.isInternal) { - return true; - } - } - - return false; - }; - - /** - * Show no-data message if there is no data in sight. Otherwise, hide it. - */ - function handleNoData() { - var chart = this; - if (chart.hasData()) { - chart.hideNoData(); - } else { - chart.showNoData(); - } - } - - /** - * Add event listener to handle automatic display of no-data message - */ - chartPrototype.callbacks.push(function (chart) { - H.addEvent(chart, 'load', handleNoData); - H.addEvent(chart, 'redraw', handleNoData); - }); - -}(Highcharts)); diff --git a/pykeg/web/static/highcharts/js/themes/dark-blue.js b/pykeg/web/static/highcharts/js/themes/dark-blue.js deleted file mode 100644 index f57a67d92..000000000 --- a/pykeg/web/static/highcharts/js/themes/dark-blue.js +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Dark blue theme for Highcharts JS - * @author Torstein Honsi - */ - -Highcharts.theme = { - colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", - "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], - chart: { - backgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, - stops: [ - [0, 'rgb(48, 48, 96)'], - [1, 'rgb(0, 0, 0)'] - ] - }, - borderColor: '#000000', - borderWidth: 2, - className: 'dark-container', - plotBackgroundColor: 'rgba(255, 255, 255, .1)', - plotBorderColor: '#CCCCCC', - plotBorderWidth: 1 - }, - title: { - style: { - color: '#C0C0C0', - font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' - } - }, - subtitle: { - style: { - color: '#666666', - font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' - } - }, - xAxis: { - gridLineColor: '#333333', - gridLineWidth: 1, - labels: { - style: { - color: '#A0A0A0' - } - }, - lineColor: '#A0A0A0', - tickColor: '#A0A0A0', - title: { - style: { - color: '#CCC', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - - } - } - }, - yAxis: { - gridLineColor: '#333333', - labels: { - style: { - color: '#A0A0A0' - } - }, - lineColor: '#A0A0A0', - minorTickInterval: null, - tickColor: '#A0A0A0', - tickWidth: 1, - title: { - style: { - color: '#CCC', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - } - } - }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.75)', - style: { - color: '#F0F0F0' - } - }, - toolbar: { - itemStyle: { - color: 'silver' - } - }, - plotOptions: { - line: { - dataLabels: { - color: '#CCC' - }, - marker: { - lineColor: '#333' - } - }, - spline: { - marker: { - lineColor: '#333' - } - }, - scatter: { - marker: { - lineColor: '#333' - } - }, - candlestick: { - lineColor: 'white' - } - }, - legend: { - itemStyle: { - font: '9pt Trebuchet MS, Verdana, sans-serif', - color: '#A0A0A0' - }, - itemHoverStyle: { - color: '#FFF' - }, - itemHiddenStyle: { - color: '#444' - } - }, - credits: { - style: { - color: '#666' - } - }, - labels: { - style: { - color: '#CCC' - } - }, - - navigation: { - buttonOptions: { - symbolStroke: '#DDDDDD', - hoverSymbolStroke: '#FFFFFF', - theme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - stroke: '#000000' - } - } - }, - - // scroll charts - rangeSelector: { - buttonTheme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - stroke: '#000000', - style: { - color: '#CCC', - fontWeight: 'bold' - }, - states: { - hover: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#BBB'], - [0.6, '#888'] - ] - }, - stroke: '#000000', - style: { - color: 'white' - } - }, - select: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.1, '#000'], - [0.3, '#333'] - ] - }, - stroke: '#000000', - style: { - color: 'yellow' - } - } - } - }, - inputStyle: { - backgroundColor: '#333', - color: 'silver' - }, - labelStyle: { - color: 'silver' - } - }, - - navigator: { - handles: { - backgroundColor: '#666', - borderColor: '#AAA' - }, - outlineColor: '#CCC', - maskFill: 'rgba(16, 16, 16, 0.5)', - series: { - color: '#7798BF', - lineColor: '#A6C7ED' - } - }, - - scrollbar: { - barBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - barBorderColor: '#CCC', - buttonArrowColor: '#CCC', - buttonBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - buttonBorderColor: '#CCC', - rifleColor: '#FFF', - trackBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, '#000'], - [1, '#333'] - ] - }, - trackBorderColor: '#666' - }, - - // special colors for some of the - legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', - legendBackgroundColorSolid: 'rgb(35, 35, 70)', - dataLabelsColor: '#444', - textColor: '#C0C0C0', - maskColor: 'rgba(255,255,255,0.3)' -}; - -// Apply the theme -var highchartsOptions = Highcharts.setOptions(Highcharts.theme); diff --git a/pykeg/web/static/highcharts/js/themes/dark-green.js b/pykeg/web/static/highcharts/js/themes/dark-green.js deleted file mode 100644 index 0461fe4c2..000000000 --- a/pykeg/web/static/highcharts/js/themes/dark-green.js +++ /dev/null @@ -1,255 +0,0 @@ -/** - * Dark blue theme for Highcharts JS - * @author Torstein Honsi - */ - -Highcharts.theme = { - colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", - "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], - chart: { - backgroundColor: { - linearGradient: [0, 0, 250, 500], - stops: [ - [0, 'rgb(48, 96, 48)'], - [1, 'rgb(0, 0, 0)'] - ] - }, - borderColor: '#000000', - borderWidth: 2, - className: 'dark-container', - plotBackgroundColor: 'rgba(255, 255, 255, .1)', - plotBorderColor: '#CCCCCC', - plotBorderWidth: 1 - }, - title: { - style: { - color: '#C0C0C0', - font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' - } - }, - subtitle: { - style: { - color: '#666666', - font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' - } - }, - xAxis: { - gridLineColor: '#333333', - gridLineWidth: 1, - labels: { - style: { - color: '#A0A0A0' - } - }, - lineColor: '#A0A0A0', - tickColor: '#A0A0A0', - title: { - style: { - color: '#CCC', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - - } - } - }, - yAxis: { - gridLineColor: '#333333', - labels: { - style: { - color: '#A0A0A0' - } - }, - lineColor: '#A0A0A0', - minorTickInterval: null, - tickColor: '#A0A0A0', - tickWidth: 1, - title: { - style: { - color: '#CCC', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - } - } - }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.75)', - style: { - color: '#F0F0F0' - } - }, - toolbar: { - itemStyle: { - color: 'silver' - } - }, - plotOptions: { - line: { - dataLabels: { - color: '#CCC' - }, - marker: { - lineColor: '#333' - } - }, - spline: { - marker: { - lineColor: '#333' - } - }, - scatter: { - marker: { - lineColor: '#333' - } - }, - candlestick: { - lineColor: 'white' - } - }, - legend: { - itemStyle: { - font: '9pt Trebuchet MS, Verdana, sans-serif', - color: '#A0A0A0' - }, - itemHoverStyle: { - color: '#FFF' - }, - itemHiddenStyle: { - color: '#444' - } - }, - credits: { - style: { - color: '#666' - } - }, - labels: { - style: { - color: '#CCC' - } - }, - - - navigation: { - buttonOptions: { - symbolStroke: '#DDDDDD', - hoverSymbolStroke: '#FFFFFF', - theme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - stroke: '#000000' - } - } - }, - - // scroll charts - rangeSelector: { - buttonTheme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - stroke: '#000000', - style: { - color: '#CCC', - fontWeight: 'bold' - }, - states: { - hover: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#BBB'], - [0.6, '#888'] - ] - }, - stroke: '#000000', - style: { - color: 'white' - } - }, - select: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.1, '#000'], - [0.3, '#333'] - ] - }, - stroke: '#000000', - style: { - color: 'yellow' - } - } - } - }, - inputStyle: { - backgroundColor: '#333', - color: 'silver' - }, - labelStyle: { - color: 'silver' - } - }, - - navigator: { - handles: { - backgroundColor: '#666', - borderColor: '#AAA' - }, - outlineColor: '#CCC', - maskFill: 'rgba(16, 16, 16, 0.5)', - series: { - color: '#7798BF', - lineColor: '#A6C7ED' - } - }, - - scrollbar: { - barBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - barBorderColor: '#CCC', - buttonArrowColor: '#CCC', - buttonBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - buttonBorderColor: '#CCC', - rifleColor: '#FFF', - trackBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, '#000'], - [1, '#333'] - ] - }, - trackBorderColor: '#666' - }, - - // special colors for some of the - legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', - legendBackgroundColorSolid: 'rgb(35, 35, 70)', - dataLabelsColor: '#444', - textColor: '#C0C0C0', - maskColor: 'rgba(255,255,255,0.3)' -}; - -// Apply the theme -var highchartsOptions = Highcharts.setOptions(Highcharts.theme); diff --git a/pykeg/web/static/highcharts/js/themes/gray.js b/pykeg/web/static/highcharts/js/themes/gray.js deleted file mode 100644 index f0de086d5..000000000 --- a/pykeg/web/static/highcharts/js/themes/gray.js +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Gray theme for Highcharts JS - * @author Torstein Honsi - */ - -Highcharts.theme = { - colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee", - "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], - chart: { - backgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, 'rgb(96, 96, 96)'], - [1, 'rgb(16, 16, 16)'] - ] - }, - borderWidth: 0, - borderRadius: 15, - plotBackgroundColor: null, - plotShadow: false, - plotBorderWidth: 0 - }, - title: { - style: { - color: '#FFF', - font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - }, - subtitle: { - style: { - color: '#DDD', - font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - }, - xAxis: { - gridLineWidth: 0, - lineColor: '#999', - tickColor: '#999', - labels: { - style: { - color: '#999', - fontWeight: 'bold' - } - }, - title: { - style: { - color: '#AAA', - font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - } - }, - yAxis: { - alternateGridColor: null, - minorTickInterval: null, - gridLineColor: 'rgba(255, 255, 255, .1)', - minorGridLineColor: 'rgba(255,255,255,0.07)', - lineWidth: 0, - tickWidth: 0, - labels: { - style: { - color: '#999', - fontWeight: 'bold' - } - }, - title: { - style: { - color: '#AAA', - font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - } - }, - legend: { - itemStyle: { - color: '#CCC' - }, - itemHoverStyle: { - color: '#FFF' - }, - itemHiddenStyle: { - color: '#333' - } - }, - labels: { - style: { - color: '#CCC' - } - }, - tooltip: { - backgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, 'rgba(96, 96, 96, .8)'], - [1, 'rgba(16, 16, 16, .8)'] - ] - }, - borderWidth: 0, - style: { - color: '#FFF' - } - }, - - - plotOptions: { - series: { - shadow: true - }, - line: { - dataLabels: { - color: '#CCC' - }, - marker: { - lineColor: '#333' - } - }, - spline: { - marker: { - lineColor: '#333' - } - }, - scatter: { - marker: { - lineColor: '#333' - } - }, - candlestick: { - lineColor: 'white' - } - }, - - toolbar: { - itemStyle: { - color: '#CCC' - } - }, - - navigation: { - buttonOptions: { - symbolStroke: '#DDDDDD', - hoverSymbolStroke: '#FFFFFF', - theme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - stroke: '#000000' - } - } - }, - - // scroll charts - rangeSelector: { - buttonTheme: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - stroke: '#000000', - style: { - color: '#CCC', - fontWeight: 'bold' - }, - states: { - hover: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#BBB'], - [0.6, '#888'] - ] - }, - stroke: '#000000', - style: { - color: 'white' - } - }, - select: { - fill: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.1, '#000'], - [0.3, '#333'] - ] - }, - stroke: '#000000', - style: { - color: 'yellow' - } - } - } - }, - inputStyle: { - backgroundColor: '#333', - color: 'silver' - }, - labelStyle: { - color: 'silver' - } - }, - - navigator: { - handles: { - backgroundColor: '#666', - borderColor: '#AAA' - }, - outlineColor: '#CCC', - maskFill: 'rgba(16, 16, 16, 0.5)', - series: { - color: '#7798BF', - lineColor: '#A6C7ED' - } - }, - - scrollbar: { - barBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - barBorderColor: '#CCC', - buttonArrowColor: '#CCC', - buttonBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0.4, '#888'], - [0.6, '#555'] - ] - }, - buttonBorderColor: '#CCC', - rifleColor: '#FFF', - trackBackgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, '#000'], - [1, '#333'] - ] - }, - trackBorderColor: '#666' - }, - - // special colors for some of the demo examples - legendBackgroundColor: 'rgba(48, 48, 48, 0.8)', - legendBackgroundColorSolid: 'rgb(70, 70, 70)', - dataLabelsColor: '#444', - textColor: '#E0E0E0', - maskColor: 'rgba(255,255,255,0.3)' -}; - -// Apply the theme -var highchartsOptions = Highcharts.setOptions(Highcharts.theme); diff --git a/pykeg/web/static/highcharts/js/themes/grid.js b/pykeg/web/static/highcharts/js/themes/grid.js deleted file mode 100644 index 70342f5ec..000000000 --- a/pykeg/web/static/highcharts/js/themes/grid.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Grid theme for Highcharts JS - * @author Torstein Honsi - */ - -Highcharts.theme = { - colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'], - chart: { - backgroundColor: { - linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, - stops: [ - [0, 'rgb(255, 255, 255)'], - [1, 'rgb(240, 240, 255)'] - ] - }, - borderWidth: 2, - plotBackgroundColor: 'rgba(255, 255, 255, .9)', - plotShadow: true, - plotBorderWidth: 1 - }, - title: { - style: { - color: '#000', - font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' - } - }, - subtitle: { - style: { - color: '#666666', - font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' - } - }, - xAxis: { - gridLineWidth: 1, - lineColor: '#000', - tickColor: '#000', - labels: { - style: { - color: '#000', - font: '11px Trebuchet MS, Verdana, sans-serif' - } - }, - title: { - style: { - color: '#333', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - - } - } - }, - yAxis: { - minorTickInterval: 'auto', - lineColor: '#000', - lineWidth: 1, - tickWidth: 1, - tickColor: '#000', - labels: { - style: { - color: '#000', - font: '11px Trebuchet MS, Verdana, sans-serif' - } - }, - title: { - style: { - color: '#333', - fontWeight: 'bold', - fontSize: '12px', - fontFamily: 'Trebuchet MS, Verdana, sans-serif' - } - } - }, - legend: { - itemStyle: { - font: '9pt Trebuchet MS, Verdana, sans-serif', - color: 'black' - - }, - itemHoverStyle: { - color: '#039' - }, - itemHiddenStyle: { - color: 'gray' - } - }, - labels: { - style: { - color: '#99b' - } - }, - - navigation: { - buttonOptions: { - theme: { - stroke: '#CCCCCC' - } - } - } -}; - -// Apply the theme -var highchartsOptions = Highcharts.setOptions(Highcharts.theme); diff --git a/pykeg/web/static/highcharts/js/themes/skies.js b/pykeg/web/static/highcharts/js/themes/skies.js deleted file mode 100644 index d58b1f246..000000000 --- a/pykeg/web/static/highcharts/js/themes/skies.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Skies theme for Highcharts JS - * @author Torstein Honsi - */ - -Highcharts.theme = { - colors: ["#514F78", "#42A07B", "#9B5E4A", "#72727F", "#1F949A", "#82914E", "#86777F", "#42A07B"], - chart: { - className: 'skies', - borderWidth: 0, - plotShadow: true, - plotBackgroundImage: 'http://www.highcharts.com/demo/gfx/skies.jpg', - plotBackgroundColor: { - linearGradient: [0, 0, 250, 500], - stops: [ - [0, 'rgba(255, 255, 255, 1)'], - [1, 'rgba(255, 255, 255, 0)'] - ] - }, - plotBorderWidth: 1 - }, - title: { - style: { - color: '#3E576F', - font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - }, - subtitle: { - style: { - color: '#6D869F', - font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - }, - xAxis: { - gridLineWidth: 0, - lineColor: '#C0D0E0', - tickColor: '#C0D0E0', - labels: { - style: { - color: '#666', - fontWeight: 'bold' - } - }, - title: { - style: { - color: '#666', - font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - } - }, - yAxis: { - alternateGridColor: 'rgba(255, 255, 255, .5)', - lineColor: '#C0D0E0', - tickColor: '#C0D0E0', - tickWidth: 1, - labels: { - style: { - color: '#666', - fontWeight: 'bold' - } - }, - title: { - style: { - color: '#666', - font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' - } - } - }, - legend: { - itemStyle: { - font: '9pt Trebuchet MS, Verdana, sans-serif', - color: '#3E576F' - }, - itemHoverStyle: { - color: 'black' - }, - itemHiddenStyle: { - color: 'silver' - } - }, - labels: { - style: { - color: '#3E576F' - } - } -}; - -// Apply the theme -var highchartsOptions = Highcharts.setOptions(Highcharts.theme); diff --git a/pykeg/web/static/images/COPYRIGHT.txt b/pykeg/web/static/images/COPYRIGHT.txt deleted file mode 100644 index cfbde91ed..000000000 --- a/pykeg/web/static/images/COPYRIGHT.txt +++ /dev/null @@ -1,5 +0,0 @@ -All images in this directory are Copyright 2014 Bevbot LLC. -All rights reserved. - -Images may not be reused without the written permission of -Bevbot LLC. For questions, contact info@bevbot.com. diff --git a/pykeg/web/static/images/background.png b/pykeg/web/static/images/background.png deleted file mode 100644 index 130efee5a..000000000 Binary files a/pykeg/web/static/images/background.png and /dev/null differ diff --git a/pykeg/web/static/images/chalkboard-bg.jpg b/pykeg/web/static/images/chalkboard-bg.jpg deleted file mode 100644 index 80d6703dd..000000000 Binary files a/pykeg/web/static/images/chalkboard-bg.jpg and /dev/null differ diff --git a/pykeg/web/static/images/favicon.ico b/pykeg/web/static/images/favicon.ico deleted file mode 100644 index c65e55da2..000000000 Binary files a/pykeg/web/static/images/favicon.ico and /dev/null differ diff --git a/pykeg/web/static/images/info.png b/pykeg/web/static/images/info.png deleted file mode 100644 index 3d92b8efb..000000000 Binary files a/pykeg/web/static/images/info.png and /dev/null differ diff --git a/pykeg/web/static/images/keg-header.png b/pykeg/web/static/images/keg-header.png deleted file mode 100644 index 27ea8a80a..000000000 Binary files a/pykeg/web/static/images/keg-header.png and /dev/null differ diff --git a/pykeg/web/static/images/keg.png b/pykeg/web/static/images/keg.png deleted file mode 100644 index 520005ebd..000000000 Binary files a/pykeg/web/static/images/keg.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-0.png b/pykeg/web/static/images/keg/full/keg-srm14-0.png deleted file mode 100644 index 8ef416e31..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-0.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-1.png b/pykeg/web/static/images/keg/full/keg-srm14-1.png deleted file mode 100644 index 06fbf3f8b..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-1.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-2.png b/pykeg/web/static/images/keg/full/keg-srm14-2.png deleted file mode 100644 index d2204b137..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-2.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-3.png b/pykeg/web/static/images/keg/full/keg-srm14-3.png deleted file mode 100644 index 4df786d2b..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-3.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-4.png b/pykeg/web/static/images/keg/full/keg-srm14-4.png deleted file mode 100644 index 0e567558d..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-4.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/full/keg-srm14-5.png b/pykeg/web/static/images/keg/full/keg-srm14-5.png deleted file mode 100644 index 312c324af..000000000 Binary files a/pykeg/web/static/images/keg/full/keg-srm14-5.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-0.png b/pykeg/web/static/images/keg/thumb/keg-srm14-0.png deleted file mode 100644 index 54e4b683e..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-0.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-1.png b/pykeg/web/static/images/keg/thumb/keg-srm14-1.png deleted file mode 100644 index fef11d91e..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-1.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-2.png b/pykeg/web/static/images/keg/thumb/keg-srm14-2.png deleted file mode 100644 index f65bdc9ad..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-2.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-3.png b/pykeg/web/static/images/keg/thumb/keg-srm14-3.png deleted file mode 100644 index 20613d30e..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-3.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-4.png b/pykeg/web/static/images/keg/thumb/keg-srm14-4.png deleted file mode 100644 index 50314f942..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-4.png and /dev/null differ diff --git a/pykeg/web/static/images/keg/thumb/keg-srm14-5.png b/pykeg/web/static/images/keg/thumb/keg-srm14-5.png deleted file mode 100644 index ecba21537..000000000 Binary files a/pykeg/web/static/images/keg/thumb/keg-srm14-5.png and /dev/null differ diff --git a/pykeg/web/static/images/kegbot-icon-72x72.png b/pykeg/web/static/images/kegbot-icon-72x72.png deleted file mode 100644 index 2ae579fe0..000000000 Binary files a/pykeg/web/static/images/kegbot-icon-72x72.png and /dev/null differ diff --git a/pykeg/web/static/images/kegbot-logo-full-white.png b/pykeg/web/static/images/kegbot-logo-full-white.png deleted file mode 100644 index e4d8696cd..000000000 Binary files a/pykeg/web/static/images/kegbot-logo-full-white.png and /dev/null differ diff --git a/pykeg/web/static/images/kegbot-unknown-square.png b/pykeg/web/static/images/kegbot-unknown-square.png deleted file mode 100644 index efe1e387e..000000000 Binary files a/pykeg/web/static/images/kegbot-unknown-square.png and /dev/null differ diff --git a/pykeg/web/static/images/unknown-drinker.png b/pykeg/web/static/images/unknown-drinker.png deleted file mode 100644 index 4caec7ac8..000000000 Binary files a/pykeg/web/static/images/unknown-drinker.png and /dev/null differ diff --git a/pykeg/web/static/images/whats-on-tap.png b/pykeg/web/static/images/whats-on-tap.png deleted file mode 100644 index e6b3b2b5b..000000000 Binary files a/pykeg/web/static/images/whats-on-tap.png and /dev/null differ diff --git a/pykeg/web/static/js/jquery-1.9.1.min.js b/pykeg/web/static/js/jquery-1.9.1.min.js deleted file mode 100644 index 006e95310..000000000 --- a/pykeg/web/static/js/jquery-1.9.1.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; -return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="<a name='"+x+"'></a><div name='"+x+"'></div>",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) -}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("<div>").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window); \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.autounits.js b/pykeg/web/static/js/jquery.autounits.js deleted file mode 100644 index 2f35c8868..000000000 --- a/pykeg/web/static/js/jquery.autounits.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * autounits: a jQuery plugin, version: 0.1.0 (2011-08-27) - * @requires jQuery v1.2.3 or later - */ -(function($) { - - $.fn.autounits = function(options) { - var settings = { - 'metric': true, - 'humanize': true, - }; - - if (options) { - $.extend(settings, options); - } - - this.each(function() { - refresh(settings, this); - }); - return this; - }; - - function refresh(settings, self) { - var data = prepareData(self); - $(self).text(toHuman(settings, data.volume_ml)); - return this; - } - - function prepareData(self) { - var element = $(self); - if (!element.data("autounits")) { - var text = $.trim(element.text()); - element.data("autounits", { - num: $(self).find("span.num").text(), - unit: $(self).find("span.unit").text(), - volume_ml: $(self).find("span.num").text(), - }); - } - return element.data("autounits"); - } - - function toOunces(amountInMilliliters) { - return 0.0338140227 * amountInMilliliters; - } - - function toHuman(settings, volume_ml) { - volume_ml = parseFloat(volume_ml); - - if (!settings.metric) { - var amt = toOunces(volume_ml); - var units = "oz"; - - if (amt < 10) { - amt = amt.toFixed(2); - } else if (amt < 128) { - amt = amt.toFixed(1); - } else { - amt = amt / 16.0; - amt = amt.toFixed(0); - units = "pints"; - } - - return amt + " " + units; - } else { - var liters = volume_ml / 1000.0; - if (liters < 0.5) { - liters = liters.toFixed(2); - } else if (liters < 100) { - liters = liters.toFixed(1); - } else { - liters = liters.toFixed(0); - } - - return liters + " L"; - } - } - -})(jQuery); diff --git a/pykeg/web/static/js/jquery.cookie.js b/pykeg/web/static/js/jquery.cookie.js deleted file mode 100644 index 6df1faca2..000000000 --- a/pykeg/web/static/js/jquery.cookie.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Cookie plugin - * - * Copyright (c) 2006 Klaus Hartl (stilbuero.de) - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ - -/** - * Create a cookie with the given name and value and other optional parameters. - * - * @example $.cookie('the_cookie', 'the_value'); - * @desc Set the value of a cookie. - * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); - * @desc Create a cookie with all available options. - * @example $.cookie('the_cookie', 'the_value'); - * @desc Create a session cookie. - * @example $.cookie('the_cookie', null); - * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain - * used when the cookie was set. - * - * @param String name The name of the cookie. - * @param String value The value of the cookie. - * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. - * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. - * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. - * If set to null or omitted, the cookie will be a session cookie and will not be retained - * when the the browser exits. - * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). - * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). - * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will - * require a secure protocol (like HTTPS). - * @type undefined - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ - -/** - * Get the value of a cookie with the given name. - * - * @example $.cookie('the_cookie'); - * @desc Get the value of a cookie. - * - * @param String name The name of the cookie. - * @return The value of the cookie. - * @type String - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ -jQuery.cookie = function(name, value, options) { - if (typeof value != 'undefined') { // name and value given, set cookie - options = options || {}; - if (value === null) { - value = ''; - options.expires = -1; - } - var expires = ''; - if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { - var date; - if (typeof options.expires == 'number') { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else { - date = options.expires; - } - expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE - } - // CAUTION: Needed to parenthesize options.path and options.domain - // in the following expressions, otherwise they evaluate to undefined - // in the packed version for some reason... - var path = options.path ? '; path=' + (options.path) : ''; - var domain = options.domain ? '; domain=' + (options.domain) : ''; - var secure = options.secure ? '; secure' : ''; - document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); - } else { // only name given, get cookie - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } -}; \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.cycle2.center.min.js b/pykeg/web/static/js/jquery.cycle2.center.min.js deleted file mode 100644 index cf9033299..000000000 --- a/pykeg/web/static/js/jquery.cycle2.center.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/* Plugin for Cycle2; Copyright (c) 2012 M. Alsup; v20140128 */ -(function(e){"use strict";e.extend(e.fn.cycle.defaults,{centerHorz:!1,centerVert:!1}),e(document).on("cycle-pre-initialize",function(i,t){function n(){clearTimeout(c),c=setTimeout(l,50)}function s(){clearTimeout(c),clearTimeout(a),e(window).off("resize orientationchange",n)}function o(){t.slides.each(r)}function l(){r.apply(t.container.find("."+t.slideActiveClass)),clearTimeout(a),a=setTimeout(o,50)}function r(){var i=e(this),n=t.container.width(),s=t.container.height(),o=i.outerWidth(),l=i.outerHeight();o&&(t.centerHorz&&n>=o&&i.css("marginLeft",(n-o)/2),t.centerVert&&s>=l&&i.css("marginTop",(s-l)/2))}if(t.centerHorz||t.centerVert){var c,a;e(window).on("resize orientationchange load",n),t.container.on("cycle-destroyed",s),t.container.on("cycle-initialized cycle-slide-added cycle-slide-removed",function(){n()}),l()}})})(jQuery); \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.cycle2.min.js b/pykeg/web/static/js/jquery.cycle2.min.js deleted file mode 100644 index 6e31c8250..000000000 --- a/pykeg/web/static/js/jquery.cycle2.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! -* jQuery Cycle2; version: 2.1.1 build: 20140128 -* http://jquery.malsup.com/cycle2/ -* Copyright (c) 2014 M. Alsup; Dual licensed: MIT/GPL -*/ -(function(e){"use strict";function t(e){return(e||"").toLowerCase()}var i="2.1.1";e.fn.cycle=function(i){var n;return 0!==this.length||e.isReady?this.each(function(){var n,s,o,c,l=e(this),r=e.fn.cycle.log;if(!l.data("cycle.opts")){(l.data("cycle-log")===!1||i&&i.log===!1||s&&s.log===!1)&&(r=e.noop),r("--c2 init--"),n=l.data();for(var a in n)n.hasOwnProperty(a)&&/^cycle[A-Z]+/.test(a)&&(c=n[a],o=a.match(/^cycle(.*)/)[1].replace(/^[A-Z]/,t),r(o+":",c,"("+typeof c+")"),n[o]=c);s=e.extend({},e.fn.cycle.defaults,n,i||{}),s.timeoutId=0,s.paused=s.paused||!1,s.container=l,s._maxZ=s.maxZ,s.API=e.extend({_container:l},e.fn.cycle.API),s.API.log=r,s.API.trigger=function(e,t){return s.container.trigger(e,t),s.API},l.data("cycle.opts",s),l.data("cycle.API",s.API),s.API.trigger("cycle-bootstrap",[s,s.API]),s.API.addInitialSlides(),s.API.preInitSlideshow(),s.slides.length&&s.API.initSlideshow()}}):(n={s:this.selector,c:this.context},e.fn.cycle.log("requeuing slideshow (dom not ready)"),e(function(){e(n.s,n.c).cycle(i)}),this)},e.fn.cycle.API={opts:function(){return this._container.data("cycle.opts")},addInitialSlides:function(){var t=this.opts(),i=t.slides;t.slideCount=0,t.slides=e(),i=i.jquery?i:t.container.find(i),t.random&&i.sort(function(){return Math.random()-.5}),t.API.add(i)},preInitSlideshow:function(){var t=this.opts();t.API.trigger("cycle-pre-initialize",[t]);var i=e.fn.cycle.transitions[t.fx];i&&e.isFunction(i.preInit)&&i.preInit(t),t._preInitialized=!0},postInitSlideshow:function(){var t=this.opts();t.API.trigger("cycle-post-initialize",[t]);var i=e.fn.cycle.transitions[t.fx];i&&e.isFunction(i.postInit)&&i.postInit(t)},initSlideshow:function(){var t,i=this.opts(),n=i.container;i.API.calcFirstSlide(),"static"==i.container.css("position")&&i.container.css("position","relative"),e(i.slides[i.currSlide]).css({opacity:1,display:"block",visibility:"visible"}),i.API.stackSlides(i.slides[i.currSlide],i.slides[i.nextSlide],!i.reverse),i.pauseOnHover&&(i.pauseOnHover!==!0&&(n=e(i.pauseOnHover)),n.hover(function(){i.API.pause(!0)},function(){i.API.resume(!0)})),i.timeout&&(t=i.API.getSlideOpts(i.currSlide),i.API.queueTransition(t,t.timeout+i.delay)),i._initialized=!0,i.API.updateView(!0),i.API.trigger("cycle-initialized",[i]),i.API.postInitSlideshow()},pause:function(t){var i=this.opts(),n=i.API.getSlideOpts(),s=i.hoverPaused||i.paused;t?i.hoverPaused=!0:i.paused=!0,s||(i.container.addClass("cycle-paused"),i.API.trigger("cycle-paused",[i]).log("cycle-paused"),n.timeout&&(clearTimeout(i.timeoutId),i.timeoutId=0,i._remainingTimeout-=e.now()-i._lastQueue,(0>i._remainingTimeout||isNaN(i._remainingTimeout))&&(i._remainingTimeout=void 0)))},resume:function(e){var t=this.opts(),i=!t.hoverPaused&&!t.paused;e?t.hoverPaused=!1:t.paused=!1,i||(t.container.removeClass("cycle-paused"),0===t.slides.filter(":animated").length&&t.API.queueTransition(t.API.getSlideOpts(),t._remainingTimeout),t.API.trigger("cycle-resumed",[t,t._remainingTimeout]).log("cycle-resumed"))},add:function(t,i){var n,s=this.opts(),o=s.slideCount,c=!1;"string"==e.type(t)&&(t=e.trim(t)),e(t).each(function(){var t,n=e(this);i?s.container.prepend(n):s.container.append(n),s.slideCount++,t=s.API.buildSlideOpts(n),s.slides=i?e(n).add(s.slides):s.slides.add(n),s.API.initSlide(t,n,--s._maxZ),n.data("cycle.opts",t),s.API.trigger("cycle-slide-added",[s,t,n])}),s.API.updateView(!0),c=s._preInitialized&&2>o&&s.slideCount>=1,c&&(s._initialized?s.timeout&&(n=s.slides.length,s.nextSlide=s.reverse?n-1:1,s.timeoutId||s.API.queueTransition(s)):s.API.initSlideshow())},calcFirstSlide:function(){var e,t=this.opts();e=parseInt(t.startingSlide||0,10),(e>=t.slides.length||0>e)&&(e=0),t.currSlide=e,t.reverse?(t.nextSlide=e-1,0>t.nextSlide&&(t.nextSlide=t.slides.length-1)):(t.nextSlide=e+1,t.nextSlide==t.slides.length&&(t.nextSlide=0))},calcNextSlide:function(){var e,t=this.opts();t.reverse?(e=0>t.nextSlide-1,t.nextSlide=e?t.slideCount-1:t.nextSlide-1,t.currSlide=e?0:t.nextSlide+1):(e=t.nextSlide+1==t.slides.length,t.nextSlide=e?0:t.nextSlide+1,t.currSlide=e?t.slides.length-1:t.nextSlide-1)},calcTx:function(t,i){var n,s=t;return i&&s.manualFx&&(n=e.fn.cycle.transitions[s.manualFx]),n||(n=e.fn.cycle.transitions[s.fx]),n||(n=e.fn.cycle.transitions.fade,s.API.log('Transition "'+s.fx+'" not found. Using fade.')),n},prepareTx:function(e,t){var i,n,s,o,c,l=this.opts();return 2>l.slideCount?(l.timeoutId=0,void 0):(!e||l.busy&&!l.manualTrump||(l.API.stopTransition(),l.busy=!1,clearTimeout(l.timeoutId),l.timeoutId=0),l.busy||(0!==l.timeoutId||e)&&(n=l.slides[l.currSlide],s=l.slides[l.nextSlide],o=l.API.getSlideOpts(l.nextSlide),c=l.API.calcTx(o,e),l._tx=c,e&&void 0!==o.manualSpeed&&(o.speed=o.manualSpeed),l.nextSlide!=l.currSlide&&(e||!l.paused&&!l.hoverPaused&&l.timeout)?(l.API.trigger("cycle-before",[o,n,s,t]),c.before&&c.before(o,n,s,t),i=function(){l.busy=!1,l.container.data("cycle.opts")&&(c.after&&c.after(o,n,s,t),l.API.trigger("cycle-after",[o,n,s,t]),l.API.queueTransition(o),l.API.updateView(!0))},l.busy=!0,c.transition?c.transition(o,n,s,t,i):l.API.doTransition(o,n,s,t,i),l.API.calcNextSlide(),l.API.updateView()):l.API.queueTransition(o)),void 0)},doTransition:function(t,i,n,s,o){var c=t,l=e(i),r=e(n),a=function(){r.animate(c.animIn||{opacity:1},c.speed,c.easeIn||c.easing,o)};r.css(c.cssBefore||{}),l.animate(c.animOut||{},c.speed,c.easeOut||c.easing,function(){l.css(c.cssAfter||{}),c.sync||a()}),c.sync&&a()},queueTransition:function(t,i){var n=this.opts(),s=void 0!==i?i:t.timeout;return 0===n.nextSlide&&0===--n.loop?(n.API.log("terminating; loop=0"),n.timeout=0,s?setTimeout(function(){n.API.trigger("cycle-finished",[n])},s):n.API.trigger("cycle-finished",[n]),n.nextSlide=n.currSlide,void 0):(s&&(n._lastQueue=e.now(),void 0===i&&(n._remainingTimeout=t.timeout),n.paused||n.hoverPaused||(n.timeoutId=setTimeout(function(){n.API.prepareTx(!1,!n.reverse)},s))),void 0)},stopTransition:function(){var e=this.opts();e.slides.filter(":animated").length&&(e.slides.stop(!1,!0),e.API.trigger("cycle-transition-stopped",[e])),e._tx&&e._tx.stopTransition&&e._tx.stopTransition(e)},advanceSlide:function(e){var t=this.opts();return clearTimeout(t.timeoutId),t.timeoutId=0,t.nextSlide=t.currSlide+e,0>t.nextSlide?t.nextSlide=t.slides.length-1:t.nextSlide>=t.slides.length&&(t.nextSlide=0),t.API.prepareTx(!0,e>=0),!1},buildSlideOpts:function(i){var n,s,o=this.opts(),c=i.data()||{};for(var l in c)c.hasOwnProperty(l)&&/^cycle[A-Z]+/.test(l)&&(n=c[l],s=l.match(/^cycle(.*)/)[1].replace(/^[A-Z]/,t),o.API.log("["+(o.slideCount-1)+"]",s+":",n,"("+typeof n+")"),c[s]=n);c=e.extend({},e.fn.cycle.defaults,o,c),c.slideNum=o.slideCount;try{delete c.API,delete c.slideCount,delete c.currSlide,delete c.nextSlide,delete c.slides}catch(r){}return c},getSlideOpts:function(t){var i=this.opts();void 0===t&&(t=i.currSlide);var n=i.slides[t],s=e(n).data("cycle.opts");return e.extend({},i,s)},initSlide:function(t,i,n){var s=this.opts();i.css(t.slideCss||{}),n>0&&i.css("zIndex",n),isNaN(t.speed)&&(t.speed=e.fx.speeds[t.speed]||e.fx.speeds._default),t.sync||(t.speed=t.speed/2),i.addClass(s.slideClass)},updateView:function(e,t){var i=this.opts();if(i._initialized){var n=i.API.getSlideOpts(),s=i.slides[i.currSlide];!e&&t!==!0&&(i.API.trigger("cycle-update-view-before",[i,n,s]),0>i.updateView)||(i.slideActiveClass&&i.slides.removeClass(i.slideActiveClass).eq(i.currSlide).addClass(i.slideActiveClass),e&&i.hideNonActive&&i.slides.filter(":not(."+i.slideActiveClass+")").css("visibility","hidden"),0===i.updateView&&setTimeout(function(){i.API.trigger("cycle-update-view",[i,n,s,e])},n.speed/(i.sync?2:1)),0!==i.updateView&&i.API.trigger("cycle-update-view",[i,n,s,e]),e&&i.API.trigger("cycle-update-view-after",[i,n,s]))}},getComponent:function(t){var i=this.opts(),n=i[t];return"string"==typeof n?/^\s*[\>|\+|~]/.test(n)?i.container.find(n):e(n):n.jquery?n:e(n)},stackSlides:function(t,i,n){var s=this.opts();t||(t=s.slides[s.currSlide],i=s.slides[s.nextSlide],n=!s.reverse),e(t).css("zIndex",s.maxZ);var o,c=s.maxZ-2,l=s.slideCount;if(n){for(o=s.currSlide+1;l>o;o++)e(s.slides[o]).css("zIndex",c--);for(o=0;s.currSlide>o;o++)e(s.slides[o]).css("zIndex",c--)}else{for(o=s.currSlide-1;o>=0;o--)e(s.slides[o]).css("zIndex",c--);for(o=l-1;o>s.currSlide;o--)e(s.slides[o]).css("zIndex",c--)}e(i).css("zIndex",s.maxZ-1)},getSlideIndex:function(e){return this.opts().slides.index(e)}},e.fn.cycle.log=function(){window.console&&console.log&&console.log("[cycle2] "+Array.prototype.join.call(arguments," "))},e.fn.cycle.version=function(){return"Cycle2: "+i},e.fn.cycle.transitions={custom:{},none:{before:function(e,t,i,n){e.API.stackSlides(i,t,n),e.cssBefore={opacity:1,visibility:"visible",display:"block"}}},fade:{before:function(t,i,n,s){var o=t.API.getSlideOpts(t.nextSlide).slideCss||{};t.API.stackSlides(i,n,s),t.cssBefore=e.extend(o,{opacity:0,visibility:"visible",display:"block"}),t.animIn={opacity:1},t.animOut={opacity:0}}},fadeout:{before:function(t,i,n,s){var o=t.API.getSlideOpts(t.nextSlide).slideCss||{};t.API.stackSlides(i,n,s),t.cssBefore=e.extend(o,{opacity:1,visibility:"visible",display:"block"}),t.animOut={opacity:0}}},scrollHorz:{before:function(e,t,i,n){e.API.stackSlides(t,i,n);var s=e.container.css("overflow","hidden").width();e.cssBefore={left:n?s:-s,top:0,opacity:1,visibility:"visible",display:"block"},e.cssAfter={zIndex:e._maxZ-2,left:0},e.animIn={left:0},e.animOut={left:n?-s:s}}}},e.fn.cycle.defaults={allowWrap:!0,autoSelector:".cycle-slideshow[data-cycle-auto-init!=false]",delay:0,easing:null,fx:"fade",hideNonActive:!0,loop:0,manualFx:void 0,manualSpeed:void 0,manualTrump:!0,maxZ:100,pauseOnHover:!1,reverse:!1,slideActiveClass:"cycle-slide-active",slideClass:"cycle-slide",slideCss:{position:"absolute",top:0,left:0},slides:"> img",speed:500,startingSlide:0,sync:!0,timeout:4e3,updateView:0},e(document).ready(function(){e(e.fn.cycle.defaults.autoSelector).cycle()})})(jQuery),/*! Cycle2 autoheight plugin; Copyright (c) M.Alsup, 2012; version: 20130913 */ -function(e){"use strict";function t(t,n){var s,o,c,l=n.autoHeight;if("container"==l)o=e(n.slides[n.currSlide]).outerHeight(),n.container.height(o);else if(n._autoHeightRatio)n.container.height(n.container.width()/n._autoHeightRatio);else if("calc"===l||"number"==e.type(l)&&l>=0){if(c="calc"===l?i(t,n):l>=n.slides.length?0:l,c==n._sentinelIndex)return;n._sentinelIndex=c,n._sentinel&&n._sentinel.remove(),s=e(n.slides[c].cloneNode(!0)),s.removeAttr("id name rel").find("[id],[name],[rel]").removeAttr("id name rel"),s.css({position:"static",visibility:"hidden",display:"block"}).prependTo(n.container).addClass("cycle-sentinel cycle-slide").removeClass("cycle-slide-active"),s.find("*").css("visibility","hidden"),n._sentinel=s}}function i(t,i){var n=0,s=-1;return i.slides.each(function(t){var i=e(this).height();i>s&&(s=i,n=t)}),n}function n(t,i,n,s){var o=e(s).outerHeight();i.container.animate({height:o},i.autoHeightSpeed,i.autoHeightEasing)}function s(i,o){o._autoHeightOnResize&&(e(window).off("resize orientationchange",o._autoHeightOnResize),o._autoHeightOnResize=null),o.container.off("cycle-slide-added cycle-slide-removed",t),o.container.off("cycle-destroyed",s),o.container.off("cycle-before",n),o._sentinel&&(o._sentinel.remove(),o._sentinel=null)}e.extend(e.fn.cycle.defaults,{autoHeight:0,autoHeightSpeed:250,autoHeightEasing:null}),e(document).on("cycle-initialized",function(i,o){function c(){t(i,o)}var l,r=o.autoHeight,a=e.type(r),d=null;("string"===a||"number"===a)&&(o.container.on("cycle-slide-added cycle-slide-removed",t),o.container.on("cycle-destroyed",s),"container"==r?o.container.on("cycle-before",n):"string"===a&&/\d+\:\d+/.test(r)&&(l=r.match(/(\d+)\:(\d+)/),l=l[1]/l[2],o._autoHeightRatio=l),"number"!==a&&(o._autoHeightOnResize=function(){clearTimeout(d),d=setTimeout(c,50)},e(window).on("resize orientationchange",o._autoHeightOnResize)),setTimeout(c,30))})}(jQuery),/*! caption plugin for Cycle2; version: 20130306 */ -function(e){"use strict";e.extend(e.fn.cycle.defaults,{caption:"> .cycle-caption",captionTemplate:"{{slideNum}} / {{slideCount}}",overlay:"> .cycle-overlay",overlayTemplate:"<div>{{title}}</div><div>{{desc}}</div>",captionModule:"caption"}),e(document).on("cycle-update-view",function(t,i,n,s){"caption"===i.captionModule&&e.each(["caption","overlay"],function(){var e=this,t=n[e+"Template"],o=i.API.getComponent(e);o.length&&t?(o.html(i.API.tmpl(t,n,i,s)),o.show()):o.hide()})}),e(document).on("cycle-destroyed",function(t,i){var n;e.each(["caption","overlay"],function(){var e=this,t=i[e+"Template"];i[e]&&t&&(n=i.API.getComponent("caption"),n.empty())})})}(jQuery),/*! command plugin for Cycle2; version: 20130707 */ -function(e){"use strict";var t=e.fn.cycle;e.fn.cycle=function(i){var n,s,o,c=e.makeArray(arguments);return"number"==e.type(i)?this.cycle("goto",i):"string"==e.type(i)?this.each(function(){var l;return n=i,o=e(this).data("cycle.opts"),void 0===o?(t.log('slideshow must be initialized before sending commands; "'+n+'" ignored'),void 0):(n="goto"==n?"jump":n,s=o.API[n],e.isFunction(s)?(l=e.makeArray(c),l.shift(),s.apply(o.API,l)):(t.log("unknown command: ",n),void 0))}):t.apply(this,arguments)},e.extend(e.fn.cycle,t),e.extend(t.API,{next:function(){var e=this.opts();if(!e.busy||e.manualTrump){var t=e.reverse?-1:1;e.allowWrap===!1&&e.currSlide+t>=e.slideCount||(e.API.advanceSlide(t),e.API.trigger("cycle-next",[e]).log("cycle-next"))}},prev:function(){var e=this.opts();if(!e.busy||e.manualTrump){var t=e.reverse?1:-1;e.allowWrap===!1&&0>e.currSlide+t||(e.API.advanceSlide(t),e.API.trigger("cycle-prev",[e]).log("cycle-prev"))}},destroy:function(){this.stop();var t=this.opts(),i=e.isFunction(e._data)?e._data:e.noop;clearTimeout(t.timeoutId),t.timeoutId=0,t.API.stop(),t.API.trigger("cycle-destroyed",[t]).log("cycle-destroyed"),t.container.removeData(),i(t.container[0],"parsedAttrs",!1),t.retainStylesOnDestroy||(t.container.removeAttr("style"),t.slides.removeAttr("style"),t.slides.removeClass(t.slideActiveClass)),t.slides.each(function(){e(this).removeData(),i(this,"parsedAttrs",!1)})},jump:function(e){var t,i=this.opts();if(!i.busy||i.manualTrump){var n=parseInt(e,10);if(isNaN(n)||0>n||n>=i.slides.length)return i.API.log("goto: invalid slide index: "+n),void 0;if(n==i.currSlide)return i.API.log("goto: skipping, already on slide",n),void 0;i.nextSlide=n,clearTimeout(i.timeoutId),i.timeoutId=0,i.API.log("goto: ",n," (zero-index)"),t=i.currSlide<i.nextSlide,i.API.prepareTx(!0,t)}},stop:function(){var t=this.opts(),i=t.container;clearTimeout(t.timeoutId),t.timeoutId=0,t.API.stopTransition(),t.pauseOnHover&&(t.pauseOnHover!==!0&&(i=e(t.pauseOnHover)),i.off("mouseenter mouseleave")),t.API.trigger("cycle-stopped",[t]).log("cycle-stopped")},reinit:function(){var e=this.opts();e.API.destroy(),e.container.cycle()},remove:function(t){for(var i,n,s=this.opts(),o=[],c=1,l=0;s.slides.length>l;l++)i=s.slides[l],l==t?n=i:(o.push(i),e(i).data("cycle.opts").slideNum=c,c++);n&&(s.slides=e(o),s.slideCount--,e(n).remove(),t==s.currSlide?s.API.advanceSlide(1):s.currSlide>t?s.currSlide--:s.currSlide++,s.API.trigger("cycle-slide-removed",[s,t,n]).log("cycle-slide-removed"),s.API.updateView())}}),e(document).on("click.cycle","[data-cycle-cmd]",function(t){t.preventDefault();var i=e(this),n=i.data("cycle-cmd"),s=i.data("cycle-context")||".cycle-slideshow";e(s).cycle(n,i.data("cycle-arg"))})}(jQuery),/*! hash plugin for Cycle2; version: 20130905 */ -function(e){"use strict";function t(t,i){var n;return t._hashFence?(t._hashFence=!1,void 0):(n=window.location.hash.substring(1),t.slides.each(function(s){if(e(this).data("cycle-hash")==n){if(i===!0)t.startingSlide=s;else{var o=s>t.currSlide;t.nextSlide=s,t.API.prepareTx(!0,o)}return!1}}),void 0)}e(document).on("cycle-pre-initialize",function(i,n){t(n,!0),n._onHashChange=function(){t(n,!1)},e(window).on("hashchange",n._onHashChange)}),e(document).on("cycle-update-view",function(e,t,i){i.hash&&"#"+i.hash!=window.location.hash&&(t._hashFence=!0,window.location.hash=i.hash)}),e(document).on("cycle-destroyed",function(t,i){i._onHashChange&&e(window).off("hashchange",i._onHashChange)})}(jQuery),/*! loader plugin for Cycle2; version: 20131121 */ -function(e){"use strict";e.extend(e.fn.cycle.defaults,{loader:!1}),e(document).on("cycle-bootstrap",function(t,i){function n(t,n){function o(t){var o;"wait"==i.loader?(l.push(t),0===a&&(l.sort(c),s.apply(i.API,[l,n]),i.container.removeClass("cycle-loading"))):(o=e(i.slides[i.currSlide]),s.apply(i.API,[t,n]),o.show(),i.container.removeClass("cycle-loading"))}function c(e,t){return e.data("index")-t.data("index")}var l=[];if("string"==e.type(t))t=e.trim(t);else if("array"===e.type(t))for(var r=0;t.length>r;r++)t[r]=e(t[r])[0];t=e(t);var a=t.length;a&&(t.css("visibility","hidden").appendTo("body").each(function(t){function c(){0===--r&&(--a,o(d))}var r=0,d=e(this),u=d.is("img")?d:d.find("img");return d.data("index",t),u=u.filter(":not(.cycle-loader-ignore)").filter(':not([src=""])'),u.length?(r=u.length,u.each(function(){this.complete?c():e(this).load(function(){c()}).on("error",function(){0===--r&&(i.API.log("slide skipped; img not loaded:",this.src),0===--a&&"wait"==i.loader&&s.apply(i.API,[l,n]))})}),void 0):(--a,l.push(d),void 0)}),a&&i.container.addClass("cycle-loading"))}var s;i.loader&&(s=i.API.add,i.API.add=n)})}(jQuery),/*! pager plugin for Cycle2; version: 20130525 */ -function(e){"use strict";function t(t,i,n){var s,o=t.API.getComponent("pager");o.each(function(){var o=e(this);if(i.pagerTemplate){var c=t.API.tmpl(i.pagerTemplate,i,t,n[0]);s=e(c).appendTo(o)}else s=o.children().eq(t.slideCount-1);s.on(t.pagerEvent,function(e){e.preventDefault(),t.API.page(o,e.currentTarget)})})}function i(e,t){var i=this.opts();if(!i.busy||i.manualTrump){var n=e.children().index(t),s=n,o=s>i.currSlide;i.currSlide!=s&&(i.nextSlide=s,i.API.prepareTx(!0,o),i.API.trigger("cycle-pager-activated",[i,e,t]))}}e.extend(e.fn.cycle.defaults,{pager:"> .cycle-pager",pagerActiveClass:"cycle-pager-active",pagerEvent:"click.cycle",pagerTemplate:"<span>•</span>"}),e(document).on("cycle-bootstrap",function(e,i,n){n.buildPagerLink=t}),e(document).on("cycle-slide-added",function(e,t,n,s){t.pager&&(t.API.buildPagerLink(t,n,s),t.API.page=i)}),e(document).on("cycle-slide-removed",function(t,i,n){if(i.pager){var s=i.API.getComponent("pager");s.each(function(){var t=e(this);e(t.children()[n]).remove()})}}),e(document).on("cycle-update-view",function(t,i){var n;i.pager&&(n=i.API.getComponent("pager"),n.each(function(){e(this).children().removeClass(i.pagerActiveClass).eq(i.currSlide).addClass(i.pagerActiveClass)}))}),e(document).on("cycle-destroyed",function(e,t){var i=t.API.getComponent("pager");i&&(i.children().off(t.pagerEvent),t.pagerTemplate&&i.empty())})}(jQuery),/*! prevnext plugin for Cycle2; version: 20130709 */ -function(e){"use strict";e.extend(e.fn.cycle.defaults,{next:"> .cycle-next",nextEvent:"click.cycle",disabledClass:"disabled",prev:"> .cycle-prev",prevEvent:"click.cycle",swipe:!1}),e(document).on("cycle-initialized",function(e,t){if(t.API.getComponent("next").on(t.nextEvent,function(e){e.preventDefault(),t.API.next()}),t.API.getComponent("prev").on(t.prevEvent,function(e){e.preventDefault(),t.API.prev()}),t.swipe){var i=t.swipeVert?"swipeUp.cycle":"swipeLeft.cycle swipeleft.cycle",n=t.swipeVert?"swipeDown.cycle":"swipeRight.cycle swiperight.cycle";t.container.on(i,function(){t.API.next()}),t.container.on(n,function(){t.API.prev()})}}),e(document).on("cycle-update-view",function(e,t){if(!t.allowWrap){var i=t.disabledClass,n=t.API.getComponent("next"),s=t.API.getComponent("prev"),o=t._prevBoundry||0,c=void 0!==t._nextBoundry?t._nextBoundry:t.slideCount-1;t.currSlide==c?n.addClass(i).prop("disabled",!0):n.removeClass(i).prop("disabled",!1),t.currSlide===o?s.addClass(i).prop("disabled",!0):s.removeClass(i).prop("disabled",!1)}}),e(document).on("cycle-destroyed",function(e,t){t.API.getComponent("prev").off(t.nextEvent),t.API.getComponent("next").off(t.prevEvent),t.container.off("swipeleft.cycle swiperight.cycle swipeLeft.cycle swipeRight.cycle swipeUp.cycle swipeDown.cycle")})}(jQuery),/*! progressive loader plugin for Cycle2; version: 20130315 */ -function(e){"use strict";e.extend(e.fn.cycle.defaults,{progressive:!1}),e(document).on("cycle-pre-initialize",function(t,i){if(i.progressive){var n,s,o=i.API,c=o.next,l=o.prev,r=o.prepareTx,a=e.type(i.progressive);if("array"==a)n=i.progressive;else if(e.isFunction(i.progressive))n=i.progressive(i);else if("string"==a){if(s=e(i.progressive),n=e.trim(s.html()),!n)return;if(/^(\[)/.test(n))try{n=e.parseJSON(n)}catch(d){return o.log("error parsing progressive slides",d),void 0}else n=n.split(RegExp(s.data("cycle-split")||"\n")),n[n.length-1]||n.pop()}r&&(o.prepareTx=function(e,t){var s,o;return e||0===n.length?(r.apply(i.API,[e,t]),void 0):(t&&i.currSlide==i.slideCount-1?(o=n[0],n=n.slice(1),i.container.one("cycle-slide-added",function(e,t){setTimeout(function(){t.API.advanceSlide(1)},50)}),i.API.add(o)):t||0!==i.currSlide?r.apply(i.API,[e,t]):(s=n.length-1,o=n[s],n=n.slice(0,s),i.container.one("cycle-slide-added",function(e,t){setTimeout(function(){t.currSlide=1,t.API.advanceSlide(-1)},50)}),i.API.add(o,!0)),void 0)}),c&&(o.next=function(){var e=this.opts();if(n.length&&e.currSlide==e.slideCount-1){var t=n[0];n=n.slice(1),e.container.one("cycle-slide-added",function(e,t){c.apply(t.API),t.container.removeClass("cycle-loading")}),e.container.addClass("cycle-loading"),e.API.add(t)}else c.apply(e.API)}),l&&(o.prev=function(){var e=this.opts();if(n.length&&0===e.currSlide){var t=n.length-1,i=n[t];n=n.slice(0,t),e.container.one("cycle-slide-added",function(e,t){t.currSlide=1,t.API.advanceSlide(-1),t.container.removeClass("cycle-loading")}),e.container.addClass("cycle-loading"),e.API.add(i,!0)}else l.apply(e.API)})}})}(jQuery),/*! tmpl plugin for Cycle2; version: 20121227 */ -function(e){"use strict";e.extend(e.fn.cycle.defaults,{tmplRegex:"{{((.)?.*?)}}"}),e.extend(e.fn.cycle.API,{tmpl:function(t,i){var n=RegExp(i.tmplRegex||e.fn.cycle.defaults.tmplRegex,"g"),s=e.makeArray(arguments);return s.shift(),t.replace(n,function(t,i){var n,o,c,l,r=i.split(".");for(n=0;s.length>n;n++)if(c=s[n]){if(r.length>1)for(l=c,o=0;r.length>o;o++)c=l,l=l[r[o]]||i;else l=c[i];if(e.isFunction(l))return l.apply(c,s);if(void 0!==l&&null!==l&&l!=i)return l}return i})}})}(jQuery); -//@ sourceMappingURL=jquery.cycle2.js.map \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.lazyload.js b/pykeg/web/static/js/jquery.lazyload.js deleted file mode 100644 index 26cc80193..000000000 --- a/pykeg/web/static/js/jquery.lazyload.js +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Lazy Load - jQuery plugin for lazy loading images - * - * Copyright (c) 2007-2013 Mika Tuupola - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Project home: - * http://www.appelsiini.net/projects/lazyload - * - * Version: 1.9.0 - * - */ - -(function($, window, document, undefined) { - var $window = $(window); - - $.fn.lazyload = function(options) { - var elements = this; - var $container; - var settings = { - threshold : 0, - failure_limit : 0, - event : "scroll", - effect : "show", - container : window, - data_attribute : "original", - skip_invisible : true, - appear : null, - load : null, - placeholder : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" - }; - - function update() { - var counter = 0; - - elements.each(function() { - var $this = $(this); - if (settings.skip_invisible && !$this.is(":visible")) { - return; - } - if ($.abovethetop(this, settings) || - $.leftofbegin(this, settings)) { - /* Nothing. */ - } else if (!$.belowthefold(this, settings) && - !$.rightoffold(this, settings)) { - $this.trigger("appear"); - /* if we found an image we'll load, reset the counter */ - counter = 0; - } else { - if (++counter > settings.failure_limit) { - return false; - } - } - }); - - } - - if(options) { - /* Maintain BC for a couple of versions. */ - if (undefined !== options.failurelimit) { - options.failure_limit = options.failurelimit; - delete options.failurelimit; - } - if (undefined !== options.effectspeed) { - options.effect_speed = options.effectspeed; - delete options.effectspeed; - } - - $.extend(settings, options); - } - - /* Cache container as jQuery as object. */ - $container = (settings.container === undefined || - settings.container === window) ? $window : $(settings.container); - - /* Fire one scroll event per scroll. Not one scroll event per image. */ - if (0 === settings.event.indexOf("scroll")) { - $container.bind(settings.event, function() { - return update(); - }); - } - - this.each(function() { - var self = this; - var $self = $(self); - - self.loaded = false; - - /* If no src attribute given use data:uri. */ - if ($self.attr("src") === undefined || $self.attr("src") === false) { - $self.attr("src", settings.placeholder); - } - - /* When appear is triggered load original image. */ - $self.one("appear", function() { - if (!this.loaded) { - if (settings.appear) { - var elements_left = elements.length; - settings.appear.call(self, elements_left, settings); - } - $("<img />") - .bind("load", function() { - var original = $self.data(settings.data_attribute); - $self.hide(); - if ($self.is("img")) { - $self.attr("src", original); - } else { - $self.css("background-image", "url('" + original + "')"); - } - $self[settings.effect](settings.effect_speed); - - self.loaded = true; - - /* Remove image from array so it is not looped next time. */ - var temp = $.grep(elements, function(element) { - return !element.loaded; - }); - elements = $(temp); - - if (settings.load) { - var elements_left = elements.length; - settings.load.call(self, elements_left, settings); - } - }) - .attr("src", $self.data(settings.data_attribute)); - } - }); - - /* When wanted event is triggered load original image */ - /* by triggering appear. */ - if (0 !== settings.event.indexOf("scroll")) { - $self.bind(settings.event, function() { - if (!self.loaded) { - $self.trigger("appear"); - } - }); - } - }); - - /* Check if something appears when window is resized. */ - $window.bind("resize", function() { - update(); - }); - - /* With IOS5 force loading images when navigating with back button. */ - /* Non optimal workaround. */ - if ((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)) { - $window.bind("pageshow", function(event) { - if (event.originalEvent && event.originalEvent.persisted) { - elements.each(function() { - $(this).trigger("appear"); - }); - } - }); - } - - /* Force initial check if images should appear. */ - $(document).ready(function() { - update(); - }); - - return this; - }; - - /* Convenience methods in jQuery namespace. */ - /* Use as $.belowthefold(element, {threshold : 100, container : window}) */ - - $.belowthefold = function(element, settings) { - var fold; - - if (settings.container === undefined || settings.container === window) { - fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop(); - } else { - fold = $(settings.container).offset().top + $(settings.container).height(); - } - - return fold <= $(element).offset().top - settings.threshold; - }; - - $.rightoffold = function(element, settings) { - var fold; - - if (settings.container === undefined || settings.container === window) { - fold = $window.width() + $window.scrollLeft(); - } else { - fold = $(settings.container).offset().left + $(settings.container).width(); - } - - return fold <= $(element).offset().left - settings.threshold; - }; - - $.abovethetop = function(element, settings) { - var fold; - - if (settings.container === undefined || settings.container === window) { - fold = $window.scrollTop(); - } else { - fold = $(settings.container).offset().top; - } - - return fold >= $(element).offset().top + settings.threshold + $(element).height(); - }; - - $.leftofbegin = function(element, settings) { - var fold; - - if (settings.container === undefined || settings.container === window) { - fold = $window.scrollLeft(); - } else { - fold = $(settings.container).offset().left; - } - - return fold >= $(element).offset().left + settings.threshold + $(element).width(); - }; - - $.inviewport = function(element, settings) { - return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) && - !$.belowthefold(element, settings) && !$.abovethetop(element, settings); - }; - - /* Custom selectors for your convenience. */ - /* Use as $("img:below-the-fold").something() or */ - /* $("img").filter(":below-the-fold").something() which is faster */ - - $.extend($.expr[":"], { - "below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); }, - "above-the-top" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, - "right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); }, - "left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); }, - "in-viewport" : function(a) { return $.inviewport(a, {threshold : 0}); }, - /* Maintain BC for couple of versions. */ - "above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, - "right-of-fold" : function(a) { return $.rightoffold(a, {threshold : 0}); }, - "left-of-fold" : function(a) { return !$.rightoffold(a, {threshold : 0}); } - }); - -})(jQuery, window, document); diff --git a/pykeg/web/static/js/jquery.lazyload.min.js b/pykeg/web/static/js/jquery.lazyload.min.js deleted file mode 100644 index 8dd097dc3..000000000 --- a/pykeg/web/static/js/jquery.lazyload.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Lazy Load - jQuery plugin for lazy loading images - * - * Copyright (c) 2007-2013 Mika Tuupola - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Project home: - * http://www.appelsiini.net/projects/lazyload - * - * Version: 1.9.0 - * - */ -!function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("<img />").bind("load",function(){var d=c.data(j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.data(j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/iphone|ipod|ipad.*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document); \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.scrollstop.js b/pykeg/web/static/js/jquery.scrollstop.js deleted file mode 100644 index a0bb63710..000000000 --- a/pykeg/web/static/js/jquery.scrollstop.js +++ /dev/null @@ -1,72 +0,0 @@ -/* http://james.padolsey.com/javascript/special-scroll-events-for-jquery/ */ - -(function(){ - - var special = jQuery.event.special, - uid1 = "D" + (+new Date()), - uid2 = "D" + (+new Date() + 1); - - special.scrollstart = { - setup: function() { - - var timer, - handler = function(evt) { - - var _self = this, - _args = arguments; - - if (timer) { - clearTimeout(timer); - } else { - evt.type = "scrollstart"; - jQuery.event.dispatch.apply(_self, _args); - } - - timer = setTimeout( function(){ - timer = null; - }, special.scrollstop.latency); - - }; - - jQuery(this).bind("scroll", handler).data(uid1, handler); - - }, - teardown: function(){ - jQuery(this).unbind( "scroll", jQuery(this).data(uid1) ); - } - }; - - special.scrollstop = { - latency: 300, - setup: function() { - - var timer, - handler = function(evt) { - - var _self = this, - _args = arguments; - - if (timer) { - clearTimeout(timer); - } - - timer = setTimeout( function(){ - - timer = null; - evt.type = "scrollstop"; - jQuery.event.dispatch.apply(_self, _args); - - - }, special.scrollstop.latency); - - }; - - jQuery(this).bind("scroll", handler).data(uid2, handler); - - }, - teardown: function() { - jQuery(this).unbind( "scroll", jQuery(this).data(uid2) ); - } - }; - -})(); \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.scrollstop.min.js b/pykeg/web/static/js/jquery.scrollstop.min.js deleted file mode 100644 index 7affa2591..000000000 --- a/pykeg/web/static/js/jquery.scrollstop.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Lazy Load - jQuery plugin for lazy loading images - * - * Copyright (c) 2007-2013 Mika Tuupola - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Project home: - * http://www.appelsiini.net/projects/lazyload - * - * Version: 1.9.0 - * - */ -!function(){var a=jQuery.event.special,b="D"+ +new Date,c="D"+(+new Date+1);a.scrollstart={setup:function(){var c,d=function(b){var d=this,e=arguments;c?clearTimeout(c):(b.type="scrollstart",jQuery.event.dispatch.apply(d,e)),c=setTimeout(function(){c=null},a.scrollstop.latency)};jQuery(this).bind("scroll",d).data(b,d)},teardown:function(){jQuery(this).unbind("scroll",jQuery(this).data(b))}},a.scrollstop={latency:300,setup:function(){var b,d=function(c){var d=this,e=arguments;b&&clearTimeout(b),b=setTimeout(function(){b=null,c.type="scrollstop",jQuery.event.dispatch.apply(d,e)},a.scrollstop.latency)};jQuery(this).bind("scroll",d).data(c,d)},teardown:function(){jQuery(this).unbind("scroll",jQuery(this).data(c))}}}(); \ No newline at end of file diff --git a/pykeg/web/static/js/jquery.timeago.js b/pykeg/web/static/js/jquery.timeago.js deleted file mode 100644 index 4d58026ef..000000000 --- a/pykeg/web/static/js/jquery.timeago.js +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Timeago is a jQuery plugin that makes it easy to support automatically - * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). - * - * @name timeago - * @version 1.3.0 - * @requires jQuery v1.2.3+ - * @author Ryan McGeary - * @license MIT License - http://www.opensource.org/licenses/mit-license.php - * - * For usage and examples, visit: - * http://timeago.yarp.com/ - * - * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) - */ - -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - $.timeago = function(timestamp) { - if (timestamp instanceof Date) { - return inWords(timestamp); - } else if (typeof timestamp === "string") { - return inWords($.timeago.parse(timestamp)); - } else if (typeof timestamp === "number") { - return inWords(new Date(timestamp)); - } else { - return inWords($.timeago.datetime(timestamp)); - } - }; - var $t = $.timeago; - - $.extend($.timeago, { - settings: { - refreshMillis: 60000, - allowFuture: false, - localeTitle: false, - cutoff: 0, - strings: { - prefixAgo: null, - prefixFromNow: null, - suffixAgo: "ago", - suffixFromNow: "from now", - seconds: "less than a minute", - minute: "about a minute", - minutes: "%d minutes", - hour: "about an hour", - hours: "about %d hours", - day: "a day", - days: "%d days", - month: "about a month", - months: "%d months", - year: "about a year", - years: "%d years", - wordSeparator: " ", - numbers: [] - } - }, - inWords: function(distanceMillis) { - var $l = this.settings.strings; - var prefix = $l.prefixAgo; - var suffix = $l.suffixAgo; - if (this.settings.allowFuture) { - if (distanceMillis < 0) { - prefix = $l.prefixFromNow; - suffix = $l.suffixFromNow; - } - } - - var seconds = Math.abs(distanceMillis) / 1000; - var minutes = seconds / 60; - var hours = minutes / 60; - var days = hours / 24; - var years = days / 365; - - function substitute(stringOrFunction, number) { - var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; - var value = ($l.numbers && $l.numbers[number]) || number; - return string.replace(/%d/i, value); - } - - var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || - seconds < 90 && substitute($l.minute, 1) || - minutes < 45 && substitute($l.minutes, Math.round(minutes)) || - minutes < 90 && substitute($l.hour, 1) || - hours < 24 && substitute($l.hours, Math.round(hours)) || - hours < 42 && substitute($l.day, 1) || - days < 30 && substitute($l.days, Math.round(days)) || - days < 45 && substitute($l.month, 1) || - days < 365 && substitute($l.months, Math.round(days / 30)) || - years < 1.5 && substitute($l.year, 1) || - substitute($l.years, Math.round(years)); - - var separator = $l.wordSeparator || ""; - if ($l.wordSeparator === undefined) { separator = " "; } - return $.trim([prefix, words, suffix].join(separator)); - }, - parse: function(iso8601) { - var s = $.trim(iso8601); - s = s.replace(/\.\d+/,""); // remove milliseconds - s = s.replace(/-/,"/").replace(/-/,"/"); - s = s.replace(/T/," ").replace(/Z/," UTC"); - s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 - return new Date(s); - }, - datetime: function(elem) { - var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); - return $t.parse(iso8601); - }, - isTime: function(elem) { - // jQuery's `is()` doesn't play well with HTML5 in IE - return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); - } - }); - - // functions that can be called via $(el).timeago('action') - // init is default when no action is given - // functions are called with context of a single element - var functions = { - init: function(){ - var refresh_el = $.proxy(refresh, this); - refresh_el(); - var $s = $t.settings; - if ($s.refreshMillis > 0) { - setInterval(refresh_el, $s.refreshMillis); - } - }, - update: function(time){ - $(this).data('timeago', { datetime: $t.parse(time) }); - refresh.apply(this); - }, - updateFromDOM: function(){ - $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) }); - refresh.apply(this); - } - }; - - $.fn.timeago = function(action, options) { - var fn = action ? functions[action] : functions.init; - if(!fn){ - throw new Error("Unknown function name '"+ action +"' for timeago"); - } - // each over objects here and call the requested function - this.each(function(){ - fn.call(this, options); - }); - return this; - }; - - function refresh() { - var data = prepareData(this); - var $s = $t.settings; - - if (!isNaN(data.datetime)) { - if ( $s.cutoff == 0 || distance(data.datetime) < $s.cutoff) { - $(this).text(inWords(data.datetime)); - } - } - return this; - } - - function prepareData(element) { - element = $(element); - if (!element.data("timeago")) { - element.data("timeago", { datetime: $t.datetime(element) }); - var text = $.trim(element.text()); - if ($t.settings.localeTitle) { - element.attr("title", element.data('timeago').datetime.toLocaleString()); - } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { - element.attr("title", text); - } - } - return element.data("timeago"); - } - - function inWords(date) { - return $t.inWords(distance(date)); - } - - function distance(date) { - return (new Date().getTime() - date.getTime()); - } - - // fix for IE6 suckage - document.createElement("abbr"); - document.createElement("time"); -})); diff --git a/pykeg/web/static/lib/bootstrap-progressbar/LICENSE b/pykeg/web/static/lib/bootstrap-progressbar/LICENSE deleted file mode 100644 index 80aeda4c8..000000000 --- a/pykeg/web/static/lib/bootstrap-progressbar/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2012-2013 Stephan Groß - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/pykeg/web/static/lib/bootstrap-progressbar/bootstrap-progressbar.min.js b/pykeg/web/static/lib/bootstrap-progressbar/bootstrap-progressbar.min.js deleted file mode 100644 index 368393ee2..000000000 --- a/pykeg/web/static/lib/bootstrap-progressbar/bootstrap-progressbar.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! bootstrap-progressbar v0.6.0 | Copyright (c) 2012-2013 Stephan Gross | MIT License | minddust.com */ -!function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.defaults,d)};b.defaults={transition_delay:300,refresh_speed:50,display_text:"none",use_percentage:!0,percent_format:function(a){return a+"%"},amount_format:function(a,b){return a+" / "+b},update:a.noop,done:a.noop,fail:a.noop},b.prototype.transition=function(){var c=this.$element,d=c.parent(),e=this.$back_text,f=this.$front_text,g=this.options,h=c.attr("aria-valuetransitiongoal"),i=c.attr("aria-valuemin")||0,j=c.attr("aria-valuemax")||100,k=d.hasClass("vertical"),l=g.update&&"function"==typeof g.update?g.update:b.defaults.update,m=g.done&&"function"==typeof g.done?g.done:b.defaults.done,n=g.fail&&"function"==typeof g.fail?g.fail:b.defaults.fail;if(!h)return n("aria-valuetransitiongoal not set"),void 0;var o=Math.round(100*(h-i)/(j-i));if("center"===g.display_text&&!e&&!f){this.$back_text=e=a("<span>",{"class":"progressbar-back-text"}).prependTo(d),this.$front_text=f=a("<span>",{"class":"progressbar-front-text"}).prependTo(c);var p;k?(p=d.css("height"),e.css({height:p,"line-height":p}),f.css({height:p,"line-height":p}),a(window).resize(function(){p=d.css("height"),e.css({height:p,"line-height":p}),f.css({height:p,"line-height":p})})):(p=d.css("width"),f.css({width:p}),a(window).resize(function(){p=d.css("width"),f.css({width:p})}))}setTimeout(function(){var a,b,n,p,q;k?c.css("height",o+"%"):c.css("width",o+"%");var r=setInterval(function(){k?(n=c.height(),p=d.height()):(n=c.width(),p=d.width()),a=Math.round(100*n/p),b=Math.round(n/p*(j-i)),a>=o&&(a=o,b=h,m(),clearInterval(r)),"none"!==g.display_text&&(q=g.use_percentage?g.percent_format(a):g.amount_format(b,j),"fill"===g.display_text?c.text(q):"center"===g.display_text&&(e.text(q),f.text(q))),c.attr("aria-valuenow",b),l(a)},g.refresh_speed)},g.transition_delay)};var c=a.fn.progressbar;a.fn.progressbar=function(c){return this.each(function(){var d=a(this),e=d.data("bs.progressbar"),f="object"==typeof c&&c;e||d.data("bs.progressbar",e=new b(this,f)),e.transition()})},a.fn.progressbar.Constructor=b,a.fn.progressbar.noConflict=function(){return a.fn.progressbar=c,this}}(window.jQuery); \ No newline at end of file diff --git a/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.css b/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.css deleted file mode 100644 index a2fc01c86..000000000 --- a/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.css +++ /dev/null @@ -1,419 +0,0 @@ -/*! - * bootstrap-progressbar v0.6.0 by @minddust - * Copyright (c) 2012-2013 Stephan Gross - * - * https://www.minddust.com/bootstrap-progressbar - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -.progress { - position: relative; -} - -.progress .bar { - position: absolute; - overflow: hidden; - line-height: 20px; -} - -.progress .progressbar-back-text { - position: absolute; - width: 100%; - height: 100%; - font-size: 12px; - line-height: 20px; - text-align: center; -} - -.progress .progressbar-front-text { - display: block; - width: 100%; - font-size: 12px; - line-height: 20px; - text-align: center; -} - -.progress.right .bar { - right: 0; -} - -.progress.right .progressbar-front-text { - position: absolute; - right: 0; -} - -.progress.vertical { - float: left; - width: 20px; - height: 100%; - margin-right: 20px; - background-color: #f9f9f9; - background-image: -moz-linear-gradient(left, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(left, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(left, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to right, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=1); -} - -.progress.vertical.bottom { - position: relative; -} - -.progress.vertical.bottom .progressbar-front-text { - position: absolute; - bottom: 0; -} - -.progress.vertical .bar { - width: 100%; - height: 0; - background-color: #0480be; - background-image: -moz-linear-gradient(left, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(left, #149bdf, #0480be); - background-image: -o-linear-gradient(left, #149bdf, #0480be); - background-image: linear-gradient(to right, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=1); - -webkit-transition: height 0.6s ease; - -moz-transition: height 0.6s ease; - -o-transition: height 0.6s ease; - transition: height 0.6s ease; -} - -.progress.vertical.bottom .bar { - position: absolute; - bottom: 0; -} - -.progress-danger.vertical .bar, -.progress.vertical .bar-danger { - background-color: #c43c35; - background-image: -moz-linear-gradient(left, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(left, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(left, #ee5f5b, #c43c35); - background-image: linear-gradient(to right, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=1); -} - -.progress-danger.progress-striped.vertical .bar, -.progress.progress-striped.vertical .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-success.vertical .bar, -.progress.vertical .bar-success { - background-color: #57a957; - background-image: -moz-linear-gradient(left, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(left, #62c462, #57a957); - background-image: -o-linear-gradient(left, #62c462, #57a957); - background-image: linear-gradient(to right, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=1); -} - -.progress-success.progress-striped.vertical .bar, -.progress.progress-striped.vertical .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-info.vertical .bar, -.progress.vertical .bar-info { - background-color: #339bb9; - background-image: -moz-linear-gradient(left, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(left, #5bc0de, #339bb9); - background-image: -o-linear-gradient(left, #5bc0de, #339bb9); - background-image: linear-gradient(to right, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=1); -} - -.progress-info.progress-striped.vertical .bar, -.progress.progress-striped.vertical .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-warning.vertical .bar, -.progress.vertical .bar-warning { - background-color: #f89406; - background-image: -moz-linear-gradient(left, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(left, #fbb450, #f89406); - background-image: -o-linear-gradient(left, #fbb450, #f89406); - background-image: linear-gradient(to right, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=1); -} - -.progress-warning.progress-striped.vertical .bar, -.progress.progress-striped.vertical .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.clearfix { - *zoom: 1; -} - -.clearfix:before, -.clearfix:after { - display: table; - line-height: 0; - content: ""; -} - -.clearfix:after { - clear: both; -} - -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-moz-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-ms-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.progress .bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - color: #ffffff; - text-align: center; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: width 0.6s ease; - -moz-transition: width 0.6s ease; - -o-transition: width 0.6s ease; - transition: width 0.6s ease; -} - -.progress .bar + .bar { - -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); -} - -.progress-striped .bar { - background-color: #149bdf; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - -moz-background-size: 40px 40px; - -o-background-size: 40px 40px; - background-size: 40px 40px; -} - -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} - -.progress-danger .bar, -.progress .bar-danger { - background-color: #dd514c; - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); -} - -.progress-danger.progress-striped .bar, -.progress-striped .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-success .bar, -.progress .bar-success { - background-color: #5eb95e; - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(to bottom, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); -} - -.progress-success.progress-striped .bar, -.progress-striped .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-info .bar, -.progress .bar-info { - background-color: #4bb1cf; - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(to bottom, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); -} - -.progress-info.progress-striped .bar, -.progress-striped .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-warning .bar, -.progress .bar-warning { - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); -} - -.progress-warning.progress-striped .bar, -.progress-striped .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} \ No newline at end of file diff --git a/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.min.css b/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.min.css deleted file mode 100644 index 777e87623..000000000 --- a/pykeg/web/static/lib/bootstrap-progressbar/css/bootstrap-progressbar-2.3.1.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * bootstrap-progressbar v0.6.0 by @minddust - * Copyright (c) 2012-2013 Stephan Gross - * - * https://www.minddust.com/bootstrap-progressbar - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */.progress{position:relative}.progress .bar{position:absolute;overflow:hidden;line-height:20px}.progress .progressbar-back-text{position:absolute;width:100%;height:100%;font-size:12px;line-height:20px;text-align:center}.progress .progressbar-front-text{display:block;width:100%;font-size:12px;line-height:20px;text-align:center}.progress.right .bar{right:0}.progress.right .progressbar-front-text{position:absolute;right:0}.progress.vertical{float:left;width:20px;height:100%;margin-right:20px;background-color:#f9f9f9;background-image:-moz-linear-gradient(left,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,100% 0,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(left,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(left,#f5f5f5,#f9f9f9);background-image:linear-gradient(to right,#f5f5f5,#f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=1)}.progress.vertical.bottom{position:relative}.progress.vertical.bottom .progressbar-front-text{position:absolute;bottom:0}.progress.vertical .bar{width:100%;height:0;background-color:#0480be;background-image:-moz-linear-gradient(left,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,100% 0,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(left,#149bdf,#0480be);background-image:-o-linear-gradient(left,#149bdf,#0480be);background-image:linear-gradient(to right,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=1);-webkit-transition:height .6s ease;-moz-transition:height .6s ease;-o-transition:height .6s ease;transition:height .6s ease}.progress.vertical.bottom .bar{position:absolute;bottom:0}.progress-danger.vertical .bar,.progress.vertical .bar-danger{background-color:#c43c35;background-image:-moz-linear-gradient(left,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,100% 0,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(left,#ee5f5b,#c43c35);background-image:-o-linear-gradient(left,#ee5f5b,#c43c35);background-image:linear-gradient(to right,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=1)}.progress-danger.progress-striped.vertical .bar,.progress.progress-striped.vertical .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success.vertical .bar,.progress.vertical .bar-success{background-color:#57a957;background-image:-moz-linear-gradient(left,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,100% 0,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(left,#62c462,#57a957);background-image:-o-linear-gradient(left,#62c462,#57a957);background-image:linear-gradient(to right,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=1)}.progress-success.progress-striped.vertical .bar,.progress.progress-striped.vertical .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info.vertical .bar,.progress.vertical .bar-info{background-color:#339bb9;background-image:-moz-linear-gradient(left,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,100% 0,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(left,#5bc0de,#339bb9);background-image:-o-linear-gradient(left,#5bc0de,#339bb9);background-image:linear-gradient(to right,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=1)}.progress-info.progress-striped.vertical .bar,.progress.progress-striped.vertical .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning.vertical .bar,.progress.vertical .bar-warning{background-color:#f89406;background-image:-moz-linear-gradient(left,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,100% 0,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(left,#fbb450,#f89406);background-image:-o-linear-gradient(left,#fbb450,#f89406);background-image:linear-gradient(to right,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=1)}.progress-warning.progress-striped.vertical .bar,.progress.progress-striped.vertical .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)} \ No newline at end of file diff --git a/pykeg/web/static/lib/slick/ajax-loader.gif b/pykeg/web/static/lib/slick/ajax-loader.gif deleted file mode 100755 index e0e6e9760..000000000 Binary files a/pykeg/web/static/lib/slick/ajax-loader.gif and /dev/null differ diff --git a/pykeg/web/static/lib/slick/config.rb b/pykeg/web/static/lib/slick/config.rb deleted file mode 100755 index 81f5ae324..000000000 --- a/pykeg/web/static/lib/slick/config.rb +++ /dev/null @@ -1,10 +0,0 @@ -css_dir = "." -sass_dir = "." -images_dir = "." -fonts_dir = "fonts" -relative_assets = true - -output_style = :compact -line_comments = false - -preferred_syntax = :scss \ No newline at end of file diff --git a/pykeg/web/static/lib/slick/fonts/slick.eot b/pykeg/web/static/lib/slick/fonts/slick.eot deleted file mode 100755 index 2cbab9ca9..000000000 Binary files a/pykeg/web/static/lib/slick/fonts/slick.eot and /dev/null differ diff --git a/pykeg/web/static/lib/slick/fonts/slick.svg b/pykeg/web/static/lib/slick/fonts/slick.svg deleted file mode 100755 index b36a66a6c..000000000 --- a/pykeg/web/static/lib/slick/fonts/slick.svg +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg"> -<metadata>Generated by Fontastic.me</metadata> -<defs> -<font id="slick" horiz-adv-x="512"> -<font-face font-family="slick" units-per-em="512" ascent="480" descent="-32"/> -<missing-glyph horiz-adv-x="512" /> - -<glyph unicode="→" d="M241 113l130 130c4 4 6 8 6 13 0 5-2 9-6 13l-130 130c-3 3-7 5-12 5-5 0-10-2-13-5l-29-30c-4-3-6-7-6-12 0-5 2-10 6-13l87-88-87-88c-4-3-6-8-6-13 0-5 2-9 6-12l29-30c3-3 8-5 13-5 5 0 9 2 12 5z m234 143c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> -<glyph unicode="←" d="M296 113l29 30c4 3 6 7 6 12 0 5-2 10-6 13l-87 88 87 88c4 3 6 8 6 13 0 5-2 9-6 12l-29 30c-3 3-8 5-13 5-5 0-9-2-12-5l-130-130c-4-4-6-8-6-13 0-5 2-9 6-13l130-130c3-3 7-5 12-5 5 0 10 2 13 5z m179 143c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> -<glyph unicode="•" d="M475 256c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> -<glyph unicode="a" d="M475 439l0-128c0-5-1-9-5-13-4-4-8-5-13-5l-128 0c-8 0-13 3-17 11-3 7-2 14 4 20l40 39c-28 26-62 39-100 39-20 0-39-4-57-11-18-8-33-18-46-32-14-13-24-28-32-46-7-18-11-37-11-57 0-20 4-39 11-57 8-18 18-33 32-46 13-14 28-24 46-32 18-7 37-11 57-11 23 0 44 5 64 15 20 9 38 23 51 42 2 1 4 3 7 3 3 0 5-1 7-3l39-39c2-2 3-3 3-6 0-2-1-4-2-6-21-25-46-45-76-59-29-14-60-20-93-20-30 0-58 5-85 17-27 12-51 27-70 47-20 19-35 43-47 70-12 27-17 55-17 85 0 30 5 58 17 85 12 27 27 51 47 70 19 20 43 35 70 47 27 12 55 17 85 17 28 0 55-5 81-15 26-11 50-26 70-45l37 37c6 6 12 7 20 4 8-4 11-9 11-17z"/> -</font></defs></svg> diff --git a/pykeg/web/static/lib/slick/fonts/slick.ttf b/pykeg/web/static/lib/slick/fonts/slick.ttf deleted file mode 100755 index 9d03461b6..000000000 Binary files a/pykeg/web/static/lib/slick/fonts/slick.ttf and /dev/null differ diff --git a/pykeg/web/static/lib/slick/fonts/slick.woff b/pykeg/web/static/lib/slick/fonts/slick.woff deleted file mode 100755 index 8ee99721b..000000000 Binary files a/pykeg/web/static/lib/slick/fonts/slick.woff and /dev/null differ diff --git a/pykeg/web/static/lib/slick/slick.css b/pykeg/web/static/lib/slick/slick.css deleted file mode 100755 index fcbbda300..000000000 --- a/pykeg/web/static/lib/slick/slick.css +++ /dev/null @@ -1,55 +0,0 @@ -/* Slider */ -.slick-slider { position: relative; display: block; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -ms-touch-action: none; -webkit-tap-highlight-color: transparent; } - -.slick-list { position: relative; overflow: hidden; display: block; margin: 0; padding: 0; } -.slick-list:focus { outline: none; } -.slick-loading .slick-list { background: white url("./ajax-loader.gif") center center no-repeat; } -.slick-list.dragging { cursor: pointer; cursor: hand; } - -.slick-slider .slick-list, .slick-track, .slick-slide, .slick-slide img { -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -ms-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } - -.slick-track { position: relative; left: 0; top: 0; display: block; zoom: 1; } -.slick-track:before, .slick-track:after { content: ""; display: table; } -.slick-track:after { clear: both; } -.slick-loading .slick-track { visibility: hidden; } - -.slick-slide { float: left; height: 100%; min-height: 1px; display: none; } -.slick-slide img { display: block; } -.slick-slide.slick-loading img { display: none; } -.slick-slide.dragging img { pointer-events: none; } -.slick-initialized .slick-slide { display: block; } -.slick-loading .slick-slide { visibility: hidden; } -.slick-vertical .slick-slide { display: block; height: auto; border: 1px solid transparent; } - -/* Icons */ -@font-face { font-family: "slick"; src: url("./fonts/slick.eot"); src: url("./fonts/slick.eot?#iefix") format("embedded-opentype"), url("./fonts/slick.woff") format("woff"), url("./fonts/slick.ttf") format("truetype"), url("./fonts/slick.svg#slick") format("svg"); font-weight: normal; font-style: normal; } -/* Arrows */ -.slick-prev, .slick-next { position: absolute; display: block; height: 20px; width: 20px; line-height: 0; font-size: 0; cursor: pointer; background: transparent; color: transparent; top: 50%; margin-top: -10px; padding: 0; border: none; outline: none; } -.slick-prev:hover, .slick-prev:focus, .slick-next:hover, .slick-next:focus { outline: none; background: transparent; color: transparent; } -.slick-prev:hover:before, .slick-prev:focus:before, .slick-next:hover:before, .slick-next:focus:before { opacity: 1; } -.slick-prev.slick-disabled:before, .slick-next.slick-disabled:before { opacity: 0.25; } - -.slick-prev:before, .slick-next:before { font-family: "slick"; font-size: 20px; line-height: 1; color: white; opacity: 0.75; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - -.slick-prev { left: -25px; } -.slick-prev:before { content: "\2190"; } - -.slick-next { right: -25px; } -.slick-next:before { content: "\2192"; } - -/* Dots */ -.slick-slider { margin-bottom: 30px; } - -.slick-dots { position: absolute; bottom: -45px; list-style: none; display: block; text-align: center; padding: 0; width: 100%; } -.slick-dots li { position: relative; display: inline-block; height: 20px; width: 20px; margin: 0 5px; padding: 0; cursor: pointer; } -.slick-dots li button { border: 0; background: transparent; display: block; height: 20px; width: 20px; outline: none; line-height: 0; font-size: 0; color: transparent; padding: 5px; cursor: pointer; } -.slick-dots li button:hover, .slick-dots li button:focus { outline: none; } -.slick-dots li button:hover:before, .slick-dots li button:focus:before { opacity: 1; } -.slick-dots li button:before { position: absolute; top: 0; left: 0; content: "\2022"; width: 20px; height: 20px; font-family: "slick"; font-size: 6px; line-height: 20px; text-align: center; color: black; opacity: 0.25; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -.slick-dots li.slick-active button:before { color: black; opacity: 0.75; } - -[dir="rtl"] .slick-next {right: auto;left: -25px;} -[dir="rtl"] .slick-next:before {content: "\2190";} -[dir="rtl"] .slick-prev {right: -25px;left: auto;} -[dir="rtl"] .slick-prev:before {content: "\2192";} -[dir="rtl"] .slick-slide {float: right;} \ No newline at end of file diff --git a/pykeg/web/static/lib/slick/slick.js b/pykeg/web/static/lib/slick/slick.js deleted file mode 100755 index d6ce1d9ce..000000000 --- a/pykeg/web/static/lib/slick/slick.js +++ /dev/null @@ -1,1850 +0,0 @@ -/* - _ _ _ _ - ___| (_) ___| | __ (_)___ -/ __| | |/ __| |/ / | / __| -\__ \ | | (__| < _ | \__ \ -|___/_|_|\___|_|\_(_)/ |___/ - |__/ - - Version: 1.3.7 - Author: Ken Wheeler - Website: http://kenwheeler.github.io - Docs: http://kenwheeler.github.io/slick - Repo: http://github.com/kenwheeler/slick - Issues: http://github.com/kenwheeler/slick/issues - - */ - -/* global window, document, define, jQuery, setInterval, clearInterval */ - -(function(factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else { - factory(jQuery); - } - -}(function($) { - 'use strict'; - var Slick = window.Slick || {}; - - Slick = (function() { - - var instanceUid = 0; - - function Slick(element, settings) { - - var _ = this, - responsiveSettings, breakpoint; - - _.defaults = { - accessibility: true, - appendArrows: $(element), - arrows: true, - asNavFor: null, - prevArrow: '<button type="button" data-role="none" class="slick-prev">Previous</button>', - nextArrow: '<button type="button" data-role="none" class="slick-next">Next</button>', - autoplay: false, - autoplaySpeed: 3000, - centerMode: false, - centerPadding: '50px', - cssEase: 'ease', - customPaging: function(slider, i) { - return '<button type="button" data-role="none">' + (i + 1) + '</button>'; - }, - dots: false, - dotsClass: 'slick-dots', - draggable: true, - easing: 'linear', - fade: false, - focusOnSelect: false, - infinite: true, - lazyLoad: 'ondemand', - onBeforeChange: null, - onAfterChange: null, - onInit: null, - onReInit: null, - pauseOnHover: true, - pauseOnDotsHover: false, - responsive: null, - rtl: false, - slide: 'div', - slidesToShow: 1, - slidesToScroll: 1, - speed: 300, - swipe: true, - touchMove: true, - touchThreshold: 5, - useCSS: true, - vertical: false - }; - - _.initials = { - animating: false, - dragging: false, - autoPlayTimer: null, - currentSlide: 0, - currentLeft: null, - direction: 1, - $dots: null, - listWidth: null, - listHeight: null, - loadIndex: 0, - $nextArrow: null, - $prevArrow: null, - slideCount: null, - slideWidth: null, - $slideTrack: null, - $slides: null, - sliding: false, - slideOffset: 0, - swipeLeft: null, - $list: null, - touchObject: {}, - transformsEnabled: false - }; - - $.extend(_, _.initials); - - _.activeBreakpoint = null; - _.animType = null; - _.animProp = null; - _.breakpoints = []; - _.breakpointSettings = []; - _.cssTransitions = false; - _.paused = false; - _.positionProp = null; - _.$slider = $(element); - _.$slidesCache = null; - _.transformType = null; - _.transitionType = null; - _.windowWidth = 0; - _.windowTimer = null; - - _.options = $.extend({}, _.defaults, settings); - - _.originalSettings = _.options; - responsiveSettings = _.options.responsive || null; - - if (responsiveSettings && responsiveSettings.length > -1) { - for (breakpoint in responsiveSettings) { - if (responsiveSettings.hasOwnProperty(breakpoint)) { - _.breakpoints.push(responsiveSettings[ - breakpoint].breakpoint); - _.breakpointSettings[responsiveSettings[ - breakpoint].breakpoint] = - responsiveSettings[breakpoint].settings; - } - } - _.breakpoints.sort(function(a, b) { - return b - a; - }); - } - - _.autoPlay = $.proxy(_.autoPlay, _); - _.autoPlayClear = $.proxy(_.autoPlayClear, _); - _.changeSlide = $.proxy(_.changeSlide, _); - _.selectHandler = $.proxy(_.selectHandler, _); - _.setPosition = $.proxy(_.setPosition, _); - _.swipeHandler = $.proxy(_.swipeHandler, _); - _.dragHandler = $.proxy(_.dragHandler, _); - _.keyHandler = $.proxy(_.keyHandler, _); - _.autoPlayIterator = $.proxy(_.autoPlayIterator, _); - - _.instanceUid = instanceUid++; - - // A simple way to check for HTML strings - // Strict HTML recognition (must start with <) - // Extracted from jQuery v1.11 source - _.htmlExpr = /^(?:\s*(<[\w\W]+>)[^>]*)$/; - - _.init(); - - } - - return Slick; - - }()); - - Slick.prototype.addSlide = function(markup, index, addBefore) { - - var _ = this; - - if (typeof(index) === 'boolean') { - addBefore = index; - index = null; - } else if (index < 0 || (index >= _.slideCount)) { - return false; - } - - _.unload(); - - if (typeof(index) === 'number') { - if (index === 0 && _.$slides.length === 0) { - $(markup).appendTo(_.$slideTrack); - } else if (addBefore) { - $(markup).insertBefore(_.$slides.eq(index)); - } else { - $(markup).insertAfter(_.$slides.eq(index)); - } - } else { - if (addBefore === true) { - $(markup).prependTo(_.$slideTrack); - } else { - $(markup).appendTo(_.$slideTrack); - } - } - - _.$slides = _.$slideTrack.children(this.options.slide); - - _.$slideTrack.children(this.options.slide).detach(); - - _.$slideTrack.append(_.$slides); - - _.$slides.each(function(index, element) { - $(element).attr("index",index); - }); - - _.$slidesCache = _.$slides; - - _.reinit(); - - }; - - Slick.prototype.animateSlide = function(targetLeft, callback) { - - var animProps = {}, _ = this; - - if (_.options.rtl === true && _.options.vertical === false) { - targetLeft = -targetLeft; - } - if (_.transformsEnabled === false) { - if (_.options.vertical === false) { - _.$slideTrack.animate({ - left: targetLeft - }, _.options.speed, _.options.easing, callback); - } else { - _.$slideTrack.animate({ - top: targetLeft - }, _.options.speed, _.options.easing, callback); - } - - } else { - - if (_.cssTransitions === false) { - - $({ - animStart: _.currentLeft - }).animate({ - animStart: targetLeft - }, { - duration: _.options.speed, - easing: _.options.easing, - step: function(now) { - if (_.options.vertical === false) { - animProps[_.animType] = 'translate(' + - now + 'px, 0px)'; - _.$slideTrack.css(animProps); - } else { - animProps[_.animType] = 'translate(0px,' + - now + 'px)'; - _.$slideTrack.css(animProps); - } - }, - complete: function() { - if (callback) { - callback.call(); - } - } - }); - - } else { - - _.applyTransition(); - - if (_.options.vertical === false) { - animProps[_.animType] = 'translate3d(' + targetLeft + 'px, 0px, 0px)'; - } else { - animProps[_.animType] = 'translate3d(0px,' + targetLeft + 'px, 0px)'; - } - _.$slideTrack.css(animProps); - - if (callback) { - setTimeout(function() { - - _.disableTransition(); - - callback.call(); - }, _.options.speed); - } - - } - - } - - }; - - Slick.prototype.applyTransition = function(slide) { - - var _ = this, - transition = {}; - - if (_.options.fade === false) { - transition[_.transitionType] = _.transformType + ' ' + _.options.speed + 'ms ' + _.options.cssEase; - } else { - transition[_.transitionType] = 'opacity ' + _.options.speed + 'ms ' + _.options.cssEase; - } - - if (_.options.fade === false) { - _.$slideTrack.css(transition); - } else { - _.$slides.eq(slide).css(transition); - } - - }; - - Slick.prototype.autoPlay = function() { - - var _ = this; - - if (_.autoPlayTimer) { - clearInterval(_.autoPlayTimer); - } - - if (_.slideCount > _.options.slidesToShow && _.paused !== true) { - _.autoPlayTimer = setInterval(_.autoPlayIterator, - _.options.autoplaySpeed); - } - - }; - - Slick.prototype.autoPlayClear = function() { - - var _ = this; - - if (_.autoPlayTimer) { - clearInterval(_.autoPlayTimer); - } - - }; - - Slick.prototype.autoPlayIterator = function() { - - var _ = this; - var asNavFor = _.options.asNavFor != null ? $(_.options.asNavFor).getSlick() : null; - - if (_.options.infinite === false) { - - if (_.direction === 1) { - - if ((_.currentSlide + 1) === _.slideCount - - 1) { - _.direction = 0; - } - - _.slideHandler(_.currentSlide + _.options.slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide + asNavFor.options.slidesToScroll); - - } else { - - if ((_.currentSlide - 1 === 0)) { - - _.direction = 1; - - } - - _.slideHandler(_.currentSlide - _.options.slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide - asNavFor.options.slidesToScroll); - - } - - } else { - - _.slideHandler(_.currentSlide + _.options.slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide + asNavFor.options.slidesToScroll); - - } - - }; - - Slick.prototype.buildArrows = function() { - - var _ = this; - - if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { - - _.$prevArrow = $(_.options.prevArrow); - _.$nextArrow = $(_.options.nextArrow); - - if (_.htmlExpr.test(_.options.prevArrow)) { - _.$prevArrow.appendTo(_.options.appendArrows); - } - - if (_.htmlExpr.test(_.options.nextArrow)) { - _.$nextArrow.appendTo(_.options.appendArrows); - } - - if (_.options.infinite !== true) { - _.$prevArrow.addClass('slick-disabled'); - } - - } - - }; - - Slick.prototype.buildDots = function() { - - var _ = this, - i, dotString; - - if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { - - dotString = '<ul class="' + _.options.dotsClass + '">'; - - for (i = 0; i <= _.getDotCount(); i += 1) { - dotString += '<li>' + _.options.customPaging.call(this, _, i) + '</li>'; - } - - dotString += '</ul>'; - - _.$dots = $(dotString).appendTo( - _.$slider); - - _.$dots.find('li').first().addClass( - 'slick-active'); - - } - - }; - - Slick.prototype.buildOut = function() { - - var _ = this; - - _.$slides = _.$slider.children(_.options.slide + - ':not(.slick-cloned)').addClass( - 'slick-slide'); - _.slideCount = _.$slides.length; - - _.$slides.each(function(index, element) { - $(element).attr("index",index); - }); - - _.$slidesCache = _.$slides; - - _.$slider.addClass('slick-slider'); - - _.$slideTrack = (_.slideCount === 0) ? - $('<div class="slick-track"/>').appendTo(_.$slider) : - _.$slides.wrapAll('<div class="slick-track"/>').parent(); - - _.$list = _.$slideTrack.wrap( - '<div class="slick-list"/>').parent(); - _.$slideTrack.css('opacity', 0); - - if (_.options.centerMode === true) { - _.options.slidesToScroll = 1; - if (_.options.slidesToShow % 2 === 0) { - _.options.slidesToShow = 3; - } - } - - $('img[data-lazy]', _.$slider).not('[src]').addClass('slick-loading'); - - _.setupInfinite(); - - _.buildArrows(); - - _.buildDots(); - - _.updateDots(); - - if (_.options.accessibility === true) { - _.$list.prop('tabIndex', 0); - } - - _.setSlideClasses(typeof this.currentSlide === 'number' ? this.currentSlide : 0); - - if (_.options.draggable === true) { - _.$list.addClass('draggable'); - } - - }; - - Slick.prototype.checkResponsive = function() { - - var _ = this, - breakpoint, targetBreakpoint; - - if (_.originalSettings.responsive && _.originalSettings - .responsive.length > -1 && _.originalSettings.responsive !== null) { - - targetBreakpoint = null; - - for (breakpoint in _.breakpoints) { - if (_.breakpoints.hasOwnProperty(breakpoint)) { - if ($(window).width() < _.breakpoints[ - breakpoint]) { - targetBreakpoint = _.breakpoints[ - breakpoint]; - } - } - } - - if (targetBreakpoint !== null) { - if (_.activeBreakpoint !== null) { - if (targetBreakpoint !== _.activeBreakpoint) { - _.activeBreakpoint = - targetBreakpoint; - _.options = $.extend({}, _.options, - _.breakpointSettings[ - targetBreakpoint]); - _.refresh(); - } - } else { - _.activeBreakpoint = targetBreakpoint; - _.options = $.extend({}, _.options, - _.breakpointSettings[ - targetBreakpoint]); - _.refresh(); - } - } else { - if (_.activeBreakpoint !== null) { - _.activeBreakpoint = null; - _.options = $.extend({}, _.options, - _.originalSettings); - _.refresh(); - } - } - - } - - }; - - Slick.prototype.changeSlide = function(event) { - - var _ = this, - $target = $(event.target); - var asNavFor = _.options.asNavFor != null ? $(_.options.asNavFor).getSlick() : null; - - // If target is a link, prevent default action. - $target.is('a') && event.preventDefault(); - - switch (event.data.message) { - - case 'previous': - if (_.slideCount > _.options.slidesToShow) { - _.slideHandler(_.currentSlide - _.options - .slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide - asNavFor.options.slidesToScroll); - } - break; - - case 'next': - if (_.slideCount > _.options.slidesToShow) { - _.slideHandler(_.currentSlide + _.options - .slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide + asNavFor.options.slidesToScroll); - } - break; - - case 'index': - var index = $(event.target).parent().index() * _.options.slidesToScroll; - _.slideHandler(index); - if(asNavFor != null) asNavFor.slideHandler(index); break; - - default: - return false; - } - - }; - - Slick.prototype.destroy = function() { - - var _ = this; - - _.autoPlayClear(); - - _.touchObject = {}; - - $('.slick-cloned', _.$slider).remove(); - if (_.$dots) { - _.$dots.remove(); - } - if (_.$prevArrow) { - _.$prevArrow.remove(); - _.$nextArrow.remove(); - } - if (_.$slides.parent().hasClass('slick-track')) { - _.$slides.unwrap().unwrap(); - } - _.$slides.removeClass( - 'slick-slide slick-active slick-visible').removeAttr('style'); - _.$slider.removeClass('slick-slider'); - _.$slider.removeClass('slick-initialized'); - - _.$list.off('.slick'); - $(window).off('.slick-' + _.instanceUid); - $(document).off('.slick-' + _.instanceUid); - - }; - - Slick.prototype.disableTransition = function(slide) { - - var _ = this, - transition = {}; - - transition[_.transitionType] = ""; - - if (_.options.fade === false) { - _.$slideTrack.css(transition); - } else { - _.$slides.eq(slide).css(transition); - } - - }; - - Slick.prototype.fadeSlide = function(slideIndex, callback) { - - var _ = this; - - if (_.cssTransitions === false) { - - _.$slides.eq(slideIndex).css({ - zIndex: 1000 - }); - - _.$slides.eq(slideIndex).animate({ - opacity: 1 - }, _.options.speed, _.options.easing, callback); - - } else { - - _.applyTransition(slideIndex); - - _.$slides.eq(slideIndex).css({ - opacity: 1, - zIndex: 1000 - }); - - if (callback) { - setTimeout(function() { - - _.disableTransition(slideIndex); - - callback.call(); - }, _.options.speed); - } - - } - - }; - - Slick.prototype.filterSlides = function(filter) { - - var _ = this; - - if (filter !== null) { - - _.unload(); - - _.$slideTrack.children(this.options.slide).detach(); - - _.$slidesCache.filter(filter).appendTo(_.$slideTrack); - - _.reinit(); - - } - - }; - - Slick.prototype.getCurrent = function() { - - var _ = this; - - return _.currentSlide; - - }; - - Slick.prototype.getDotCount = function() { - - var _ = this, - breaker = 0, - dotCounter = 0, - dotCount = 0, - dotLimit; - - dotLimit = _.options.infinite === true ? _.slideCount + _.options.slidesToShow - _.options.slidesToScroll : _.slideCount; - - while (breaker < dotLimit) { - dotCount++; - dotCounter += _.options.slidesToScroll; - breaker = dotCounter + _.options.slidesToShow; - } - - return dotCount; - - }; - - Slick.prototype.getLeft = function(slideIndex) { - - var _ = this, - targetLeft, - verticalHeight, - verticalOffset = 0; - - _.slideOffset = 0; - verticalHeight = _.$slides.first().outerHeight(); - - if (_.options.infinite === true) { - if (_.slideCount > _.options.slidesToShow) { - _.slideOffset = (_.slideWidth * _.options.slidesToShow) * -1; - verticalOffset = (verticalHeight * _.options.slidesToShow) * -1; - } - if (_.slideCount % _.options.slidesToScroll !== 0) { - if (slideIndex + _.options.slidesToScroll > _.slideCount && _.slideCount > _.options.slidesToShow) { - _.slideOffset = ((_.slideCount % _.options.slidesToShow) * _.slideWidth) * -1; - verticalOffset = ((_.slideCount % _.options.slidesToShow) * verticalHeight) * -1; - } - } - } else { - if (_.slideCount % _.options.slidesToShow !== 0) { - if (slideIndex + _.options.slidesToScroll > _.slideCount && _.slideCount > _.options.slidesToShow) { - _.slideOffset = (_.options.slidesToShow * _.slideWidth) - ((_.slideCount % _.options.slidesToShow) * _.slideWidth); - verticalOffset = ((_.slideCount % _.options.slidesToShow) * verticalHeight); - } - } - } - - if (_.options.centerMode === true && _.options.infinite === true) { - _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2) - _.slideWidth; - } else if (_.options.centerMode === true) { - _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2); - } - - if (_.options.vertical === false) { - targetLeft = ((slideIndex * _.slideWidth) * -1) + _.slideOffset; - } else { - targetLeft = ((slideIndex * verticalHeight) * -1) + verticalOffset; - } - - return targetLeft; - - }; - - Slick.prototype.init = function() { - - var _ = this; - - if (!$(_.$slider).hasClass('slick-initialized')) { - - $(_.$slider).addClass('slick-initialized'); - _.buildOut(); - _.setProps(); - _.startLoad(); - _.loadSlider(); - _.initializeEvents(); - _.checkResponsive(); - } - - if (_.options.onInit !== null) { - _.options.onInit.call(this, _); - } - - }; - - Slick.prototype.initArrowEvents = function() { - - var _ = this; - - if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { - _.$prevArrow.on('click.slick', { - message: 'previous' - }, _.changeSlide); - _.$nextArrow.on('click.slick', { - message: 'next' - }, _.changeSlide); - } - - }; - - Slick.prototype.initDotEvents = function() { - - var _ = this; - - if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { - $('li', _.$dots).on('click.slick', { - message: 'index' - }, _.changeSlide); - } - - if (_.options.dots === true && _.options.pauseOnDotsHover === true && _.options.autoplay === true) { - $('li', _.$dots) - .on('mouseenter.slick', _.autoPlayClear) - .on('mouseleave.slick', _.autoPlay); - } - - }; - - Slick.prototype.initializeEvents = function() { - - var _ = this; - - _.initArrowEvents(); - - _.initDotEvents(); - - _.$list.on('touchstart.slick mousedown.slick', { - action: 'start' - }, _.swipeHandler); - _.$list.on('touchmove.slick mousemove.slick', { - action: 'move' - }, _.swipeHandler); - _.$list.on('touchend.slick mouseup.slick', { - action: 'end' - }, _.swipeHandler); - _.$list.on('touchcancel.slick mouseleave.slick', { - action: 'end' - }, _.swipeHandler); - - if (_.options.pauseOnHover === true && _.options.autoplay === true) { - _.$list.on('mouseenter.slick', _.autoPlayClear); - _.$list.on('mouseleave.slick', _.autoPlay); - } - - if(_.options.accessibility === true) { - _.$list.on('keydown.slick', _.keyHandler); - } - - if(_.options.focusOnSelect === true) { - $(_.options.slide, _.$slideTrack).on('click.slick', _.selectHandler); - } - - $(window).on('orientationchange.slick.slick-' + _.instanceUid, function() { - _.checkResponsive(); - _.setPosition(); - }); - - $(window).on('resize.slick.slick-' + _.instanceUid, function() { - if ($(window).width() !== _.windowWidth) { - clearTimeout(_.windowDelay); - _.windowDelay = window.setTimeout(function() { - _.windowWidth = $(window).width(); - _.checkResponsive(); - _.setPosition(); - }, 50); - } - }); - - $(window).on('load.slick.slick-' + _.instanceUid, _.setPosition); - $(document).on('ready.slick.slick-' + _.instanceUid, _.setPosition); - - }; - - Slick.prototype.initUI = function() { - - var _ = this; - - if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { - - _.$prevArrow.show(); - _.$nextArrow.show(); - - } - - if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { - - _.$dots.show(); - - } - - if (_.options.autoplay === true) { - - _.autoPlay(); - - } - - }; - - Slick.prototype.keyHandler = function(event) { - - var _ = this; - - if (event.keyCode === 37) { - _.changeSlide({ - data: { - message: 'previous' - } - }); - } else if (event.keyCode === 39) { - _.changeSlide({ - data: { - message: 'next' - } - }); - } - - }; - - Slick.prototype.lazyLoad = function() { - - var _ = this, - loadRange, cloneRange, rangeStart, rangeEnd; - - function loadImages(imagesScope) { - $('img[data-lazy]', imagesScope).each(function() { - var image = $(this), - imageSource = $(this).attr('data-lazy') + "?" + new Date().getTime(); - - image - .load(function() { image.animate({ opacity: 1 }, 200); }) - .css({ opacity: 0 }) - .attr('src', imageSource) - .removeAttr('data-lazy') - .removeClass('slick-loading'); - }); - } - - if (_.options.centerMode === true) { - if (_.options.infinite === true) { - rangeStart = _.currentSlide + (_.options.slidesToShow/2 + 1); - rangeEnd = rangeStart + _.options.slidesToShow + 2; - } else { - rangeStart = Math.max(0, _.currentSlide - (_.options.slidesToShow/2 + 1)); - rangeEnd = 2 + (_.options.slidesToShow/2 + 1) + _.currentSlide; - } - } else { - rangeStart = _.options.infinite ? _.options.slidesToShow + _.currentSlide : _.currentSlide; - rangeEnd = rangeStart + _.options.slidesToShow; - if (_.options.fade === true ) { - if(rangeStart > 0) rangeStart--; - if(rangeEnd <= _.slideCount) rangeEnd++; - } - } - - loadRange = _.$slider.find('.slick-slide').slice(rangeStart, rangeEnd); - loadImages(loadRange); - - if (_.slideCount == 1){ - cloneRange = _.$slider.find('.slick-slide') - loadImages(cloneRange) - }else - if (_.currentSlide >= _.slideCount - _.options.slidesToShow) { - cloneRange = _.$slider.find('.slick-cloned').slice(0, _.options.slidesToShow); - loadImages(cloneRange) - } else if (_.currentSlide === 0) { - cloneRange = _.$slider.find('.slick-cloned').slice(_.options.slidesToShow * -1); - loadImages(cloneRange); - } - - }; - - Slick.prototype.loadSlider = function() { - - var _ = this; - - _.setPosition(); - - _.$slideTrack.css({ - opacity: 1 - }); - - _.$slider.removeClass('slick-loading'); - - _.initUI(); - - if (_.options.lazyLoad === 'progressive') { - _.progressiveLazyLoad(); - } - - }; - - Slick.prototype.postSlide = function(index) { - - var _ = this; - - if (_.options.onAfterChange !== null) { - _.options.onAfterChange.call(this, _, index); - } - - _.animating = false; - - _.setPosition(); - - _.swipeLeft = null; - - if (_.options.autoplay === true && _.paused === false) { - _.autoPlay(); - } - - }; - - Slick.prototype.progressiveLazyLoad = function() { - - var _ = this, - imgCount, targetImage; - - imgCount = $('img[data-lazy]').length; - - if (imgCount > 0) { - targetImage = $('img[data-lazy]', _.$slider).first(); - targetImage.attr('src', targetImage.attr('data-lazy')).removeClass('slick-loading').load(function() { - targetImage.removeAttr('data-lazy'); - _.progressiveLazyLoad(); - }); - } - - }; - - Slick.prototype.refresh = function() { - - var _ = this, - currentSlide = _.currentSlide; - - _.destroy(); - - $.extend(_, _.initials); - - _.currentSlide = currentSlide; - _.init(); - - }; - - Slick.prototype.reinit = function() { - - var _ = this; - - _.$slides = _.$slideTrack.children(_.options.slide).addClass( - 'slick-slide'); - - _.slideCount = _.$slides.length; - - if (_.currentSlide >= _.slideCount && _.currentSlide !== 0) { - _.currentSlide = _.currentSlide - _.options.slidesToScroll; - } - - _.setProps(); - - _.setupInfinite(); - - _.buildArrows(); - - _.updateArrows(); - - _.initArrowEvents(); - - _.buildDots(); - - _.updateDots(); - - _.initDotEvents(); - - if(_.options.focusOnSelect === true) { - $(_.options.slide, _.$slideTrack).on('click.slick', _.selectHandler); - } - - _.setSlideClasses(0); - - _.setPosition(); - - if (_.options.onReInit !== null) { - _.options.onReInit.call(this, _); - } - - }; - - Slick.prototype.removeSlide = function(index, removeBefore) { - - var _ = this; - - if (typeof(index) === 'boolean') { - removeBefore = index; - index = removeBefore === true ? 0 : _.slideCount - 1; - } else { - index = removeBefore === true ? --index : index; - } - - if (_.slideCount < 1 || index < 0 || index > _.slideCount - 1) { - return false; - } - - _.unload(); - - _.$slideTrack.children(this.options.slide).eq(index).remove(); - - _.$slides = _.$slideTrack.children(this.options.slide); - - _.$slideTrack.children(this.options.slide).detach(); - - _.$slideTrack.append(_.$slides); - - _.$slidesCache = _.$slides; - - _.reinit(); - - }; - - Slick.prototype.setCSS = function(position) { - - var _ = this, - positionProps = {}, x, y; - - if (_.options.rtl === true) { - position = -position; - } - x = _.positionProp == 'left' ? position + 'px' : '0px'; - y = _.positionProp == 'top' ? position + 'px' : '0px'; - - positionProps[_.positionProp] = position; - - if (_.transformsEnabled === false) { - _.$slideTrack.css(positionProps); - } else { - positionProps = {}; - if (_.cssTransitions === false) { - positionProps[_.animType] = 'translate(' + x + ', ' + y + ')'; - _.$slideTrack.css(positionProps); - } else { - positionProps[_.animType] = 'translate3d(' + x + ', ' + y + ', 0px)'; - _.$slideTrack.css(positionProps); - } - } - - }; - - Slick.prototype.setDimensions = function() { - - var _ = this; - - if (_.options.vertical === false) { - if (_.options.centerMode === true) { - _.$list.css({ - padding: ('0px ' + _.options.centerPadding) - }); - } - } else { - _.$list.height(_.$slides.first().outerHeight(true) * _.options.slidesToShow); - if (_.options.centerMode === true) { - _.$list.css({ - padding: (_.options.centerPadding + ' 0px') - }); - } - } - - _.listWidth = _.$list.width(); - _.listHeight = _.$list.height(); - - - if(_.options.vertical === false) { - _.slideWidth = Math.ceil(_.listWidth / _.options.slidesToShow); - _.$slideTrack.width(Math.ceil((_.slideWidth * _.$slideTrack.children('.slick-slide').length))); - - } else { - _.slideWidth = Math.ceil(_.listWidth); - _.$slideTrack.height(Math.ceil((_.$slides.first().outerHeight(true) * _.$slideTrack.children('.slick-slide').length))); - - } - - var offset = _.$slides.first().outerWidth(true) - _.$slides.first().width(); - _.$slideTrack.children('.slick-slide').width(_.slideWidth - offset); - - }; - - Slick.prototype.setFade = function() { - - var _ = this, - targetLeft; - - _.$slides.each(function(index, element) { - targetLeft = (_.slideWidth * index) * -1; - $(element).css({ - position: 'relative', - left: targetLeft, - top: 0, - zIndex: 800, - opacity: 0 - }); - }); - - _.$slides.eq(_.currentSlide).css({ - zIndex: 900, - opacity: 1 - }); - - }; - - Slick.prototype.setPosition = function() { - - var _ = this; - - _.setDimensions(); - - if (_.options.fade === false) { - _.setCSS(_.getLeft(_.currentSlide)); - } else { - _.setFade(); - } - - }; - - Slick.prototype.setProps = function() { - - var _ = this, - bodyStyle = document.body.style; - - _.positionProp = _.options.vertical === true ? 'top' : 'left'; - - if (_.positionProp === 'top') { - _.$slider.addClass('slick-vertical'); - } else { - _.$slider.removeClass('slick-vertical'); - } - - if (bodyStyle.WebkitTransition !== undefined || - bodyStyle.MozTransition !== undefined || - bodyStyle.msTransition !== undefined) { - if(_.options.useCSS === true) { - _.cssTransitions = true; - } - } - - if (bodyStyle.OTransform !== undefined) { - _.animType = 'OTransform'; - _.transformType = "-o-transform"; - _.transitionType = 'OTransition'; - if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; - } - if (bodyStyle.MozTransform !== undefined) { - _.animType = 'MozTransform'; - _.transformType = "-moz-transform"; - _.transitionType = 'MozTransition'; - if (bodyStyle.perspectiveProperty === undefined && bodyStyle.MozPerspective === undefined) _.animType = false; - } - if (bodyStyle.webkitTransform !== undefined) { - _.animType = 'webkitTransform'; - _.transformType = "-webkit-transform"; - _.transitionType = 'webkitTransition'; - if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; - } - if (bodyStyle.msTransform !== undefined) { - _.animType = 'msTransform'; - _.transformType = "-ms-transform"; - _.transitionType = 'msTransition'; - if (bodyStyle.msTransform === undefined) _.animType = false; - } - if (bodyStyle.transform !== undefined && _.animType !== false) { - _.animType = 'transform'; - _.transformType = "transform"; - _.transitionType = 'transition'; - } - _.transformsEnabled = (_.animType !== null && _.animType !== false); - - }; - - - Slick.prototype.setSlideClasses = function(index) { - - var _ = this, - centerOffset, allSlides, indexOffset, remainder; - - _.$slider.find('.slick-slide').removeClass('slick-active').removeClass('slick-center'); - allSlides = _.$slider.find('.slick-slide'); - - if (_.options.centerMode === true) { - - centerOffset = Math.floor(_.options.slidesToShow / 2); - - if(_.options.infinite === true) { - - if (index >= centerOffset && index <= (_.slideCount - 1) - centerOffset) { - _.$slides.slice(index - centerOffset, index + centerOffset + 1).addClass('slick-active'); - } else { - indexOffset = _.options.slidesToShow + index; - allSlides.slice(indexOffset - centerOffset + 1, indexOffset + centerOffset + 2).addClass('slick-active'); - } - - if (index === 0) { - allSlides.eq(allSlides.length - 1 - _.options.slidesToShow).addClass('slick-center'); - } else if (index === _.slideCount - 1) { - allSlides.eq(_.options.slidesToShow).addClass('slick-center'); - } - - } - - _.$slides.eq(index).addClass('slick-center'); - - } else { - - if (index >= 0 && index <= (_.slideCount - _.options.slidesToShow)) { - _.$slides.slice(index, index + _.options.slidesToShow).addClass('slick-active'); - } else if ( allSlides.length <= _.options.slidesToShow ) { - allSlides.addClass('slick-active'); - } else { - remainder = _.slideCount%_.options.slidesToShow; - indexOffset = _.options.infinite === true ? _.options.slidesToShow + index : index; - if(_.options.slidesToShow == _.options.slidesToScroll && (_.slideCount - index) < _.options.slidesToShow) { - allSlides.slice(indexOffset-(_.options.slidesToShow-remainder), indexOffset + remainder).addClass('slick-active'); - } else { - allSlides.slice(indexOffset, indexOffset + _.options.slidesToShow).addClass('slick-active'); - } - } - - } - - if (_.options.lazyLoad === 'ondemand') { - _.lazyLoad(); - } - - }; - - Slick.prototype.setupInfinite = function() { - - var _ = this, - i, slideIndex, infiniteCount; - - if (_.options.fade === true || _.options.vertical === true) { - _.options.centerMode = false; - } - - if (_.options.infinite === true && _.options.fade === false) { - - slideIndex = null; - - if (_.slideCount > _.options.slidesToShow) { - - if (_.options.centerMode === true) { - infiniteCount = _.options.slidesToShow + 1; - } else { - infiniteCount = _.options.slidesToShow; - } - - for (i = _.slideCount; i > (_.slideCount - - infiniteCount); i -= 1) { - slideIndex = i - 1; - $(_.$slides[slideIndex]).clone(true).attr('id', '').prependTo( - _.$slideTrack).addClass('slick-cloned'); - } - for (i = 0; i < infiniteCount; i += 1) { - slideIndex = i; - $(_.$slides[slideIndex]).clone(true).attr('id', '').appendTo( - _.$slideTrack).addClass('slick-cloned'); - } - _.$slideTrack.find('.slick-cloned').find('[id]').each(function() { - $(this).attr('id', ''); - }); - - } - - } - - }; - - Slick.prototype.selectHandler = function(event) { - - var _ = this; - var asNavFor = _.options.asNavFor != null ? $(_.options.asNavFor).getSlick() : null; - var index = parseInt($(event.target).parent().attr("index")); - if(!index) index = 0; - - if(_.slideCount <= _.options.slidesToShow){ - return; - } - _.slideHandler(index); - - if(asNavFor != null){ - if(asNavFor.slideCount <= asNavFor.options.slidesToShow){ - return; - } - asNavFor.slideHandler(index); - } - }; - - Slick.prototype.slideHandler = function(index) { - - var targetSlide, animSlide, slideLeft, unevenOffset, targetLeft = null, - _ = this; - - if (_.animating === true) { - return false; - } - - targetSlide = index; - targetLeft = _.getLeft(targetSlide); - slideLeft = _.getLeft(_.currentSlide); - - unevenOffset = _.slideCount % _.options.slidesToScroll !== 0 ? _.options.slidesToScroll : 0; - - _.currentLeft = _.swipeLeft === null ? slideLeft : _.swipeLeft; - - if (_.options.infinite === false && _.options.centerMode === false && (index < 0 || index > (_.slideCount - _.options.slidesToShow + unevenOffset))) { - if(_.options.fade === false) { - targetSlide = _.currentSlide; - _.animateSlide(slideLeft, function() { - _.postSlide(targetSlide); - }); - } - return false; - } else if (_.options.infinite === false && _.options.centerMode === true && (index < 0 || index > (_.slideCount - _.options.slidesToScroll))) { - if(_.options.fade === false) { - targetSlide = _.currentSlide; - _.animateSlide(slideLeft, function() { - _.postSlide(targetSlide); - }); - } - return false; - } - - if (_.options.autoplay === true) { - clearInterval(_.autoPlayTimer); - } - - if (targetSlide < 0) { - if (_.slideCount % _.options.slidesToScroll !== 0) { - animSlide = _.slideCount - (_.slideCount % _.options.slidesToScroll); - } else { - animSlide = _.slideCount - _.options.slidesToScroll; - } - } else if (targetSlide > (_.slideCount - 1)) { - animSlide = 0; - } else { - animSlide = targetSlide; - } - - _.animating = true; - - if (_.options.onBeforeChange !== null && index !== _.currentSlide) { - _.options.onBeforeChange.call(this, _, _.currentSlide, animSlide); - } - - _.currentSlide = animSlide; - - _.setSlideClasses(_.currentSlide); - - _.updateDots(); - _.updateArrows(); - - if (_.options.fade === true) { - _.fadeSlide(animSlide, function() { - _.postSlide(animSlide); - }); - return false; - } - - _.animateSlide(targetLeft, function() { - _.postSlide(animSlide); - }); - - }; - - Slick.prototype.startLoad = function() { - - var _ = this; - - if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { - - _.$prevArrow.hide(); - _.$nextArrow.hide(); - - } - - if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { - - _.$dots.hide(); - - } - - _.$slider.addClass('slick-loading'); - - }; - - Slick.prototype.swipeDirection = function() { - - var xDist, yDist, r, swipeAngle, _ = this; - - xDist = _.touchObject.startX - _.touchObject.curX; - yDist = _.touchObject.startY - _.touchObject.curY; - r = Math.atan2(yDist, xDist); - - swipeAngle = Math.round(r * 180 / Math.PI); - if (swipeAngle < 0) { - swipeAngle = 360 - Math.abs(swipeAngle); - } - - if ((swipeAngle <= 45) && (swipeAngle >= 0)) { - return 'left'; - } - if ((swipeAngle <= 360) && (swipeAngle >= 315)) { - return 'left'; - } - if ((swipeAngle >= 135) && (swipeAngle <= 225)) { - return 'right'; - } - - return 'vertical'; - - }; - - Slick.prototype.swipeEnd = function(event) { - - var _ = this; - var asNavFor = _.options.asNavFor != null ? $(_.options.asNavFor).getSlick() : null; - - _.dragging = false; - - if (_.touchObject.curX === undefined) { - return false; - } - - if (_.touchObject.swipeLength >= _.touchObject.minSwipe) { - $(event.target).on('click.slick', function(event) { - event.stopImmediatePropagation(); - event.stopPropagation(); - event.preventDefault(); - $(event.target).off('click.slick'); - }); - - switch (_.swipeDirection()) { - case 'left': - _.slideHandler(_.currentSlide + _.options.slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide + asNavFor.options.slidesToScroll); - _.touchObject = {}; - break; - - case 'right': - _.slideHandler(_.currentSlide - _.options.slidesToScroll); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide - asNavFor.options.slidesToScroll); - _.touchObject = {}; - break; - } - } else { - if(_.touchObject.startX !== _.touchObject.curX) { - _.slideHandler(_.currentSlide); - if(asNavFor != null) asNavFor.slideHandler(asNavFor.currentSlide); - _.touchObject = {}; - } - } - - }; - - Slick.prototype.swipeHandler = function(event) { - - var _ = this; - - if ((_.options.swipe === false) || ('ontouchend' in document && _.options.swipe === false)) { - return; - } else if ((_.options.draggable === false) || (_.options.draggable === false && !event.originalEvent.touches)) { - return; - } - - _.touchObject.fingerCount = event.originalEvent && event.originalEvent.touches !== undefined ? - event.originalEvent.touches.length : 1; - - _.touchObject.minSwipe = _.listWidth / _.options - .touchThreshold; - - switch (event.data.action) { - - case 'start': - _.swipeStart(event); - break; - - case 'move': - _.swipeMove(event); - break; - - case 'end': - _.swipeEnd(event); - break; - - } - - }; - - Slick.prototype.swipeMove = function(event) { - - var _ = this, - curLeft, swipeDirection, positionOffset, touches; - - touches = event.originalEvent !== undefined ? event.originalEvent.touches : null; - - curLeft = _.getLeft(_.currentSlide); - - if (!_.dragging || touches && touches.length !== 1) { - return false; - } - - _.touchObject.curX = touches !== undefined ? touches[0].pageX : event.clientX; - _.touchObject.curY = touches !== undefined ? touches[0].pageY : event.clientY; - - _.touchObject.swipeLength = Math.round(Math.sqrt( - Math.pow(_.touchObject.curX - _.touchObject.startX, 2))); - - swipeDirection = _.swipeDirection(); - - if (swipeDirection === 'vertical') { - return; - } - - if (event.originalEvent !== undefined && _.touchObject.swipeLength > 4) { - event.preventDefault(); - } - - positionOffset = _.touchObject.curX > _.touchObject.startX ? 1 : -1; - - if (_.options.vertical === false) { - _.swipeLeft = curLeft + _.touchObject.swipeLength * positionOffset; - } else { - _.swipeLeft = curLeft + (_.touchObject - .swipeLength * (_.$list.height() / _.listWidth)) * positionOffset; - } - - if (_.options.fade === true || _.options.touchMove === false) { - return false; - } - - if (_.animating === true) { - _.swipeLeft = null; - return false; - } - - _.setCSS(_.swipeLeft); - - }; - - Slick.prototype.swipeStart = function(event) { - - var _ = this, - touches; - - if (_.touchObject.fingerCount !== 1 || _.slideCount <= _.options.slidesToShow) { - _.touchObject = {}; - return false; - } - - if (event.originalEvent !== undefined && event.originalEvent.touches !== undefined) { - touches = event.originalEvent.touches[0]; - } - - _.touchObject.startX = _.touchObject.curX = touches !== undefined ? touches.pageX : event.clientX; - _.touchObject.startY = _.touchObject.curY = touches !== undefined ? touches.pageY : event.clientY; - - _.dragging = true; - - }; - - Slick.prototype.unfilterSlides = function() { - - var _ = this; - - if (_.$slidesCache !== null) { - - _.unload(); - - _.$slideTrack.children(this.options.slide).detach(); - - _.$slidesCache.appendTo(_.$slideTrack); - - _.reinit(); - - } - - }; - - Slick.prototype.unload = function() { - - var _ = this; - - $('.slick-cloned', _.$slider).remove(); - if (_.$dots) { - _.$dots.remove(); - } - if (_.$prevArrow) { - _.$prevArrow.remove(); - _.$nextArrow.remove(); - } - _.$slides.removeClass( - 'slick-slide slick-active slick-visible').removeAttr('style'); - - }; - - Slick.prototype.updateArrows = function() { - - var _ = this; - - if (_.options.arrows === true && _.options.infinite !== - true && _.slideCount > _.options.slidesToShow) { - _.$prevArrow.removeClass('slick-disabled'); - _.$nextArrow.removeClass('slick-disabled'); - if (_.currentSlide === 0) { - _.$prevArrow.addClass('slick-disabled'); - _.$nextArrow.removeClass('slick-disabled'); - } else if (_.currentSlide >= _.slideCount - _.options.slidesToShow) { - _.$nextArrow.addClass('slick-disabled'); - _.$prevArrow.removeClass('slick-disabled'); - } - } - - }; - - Slick.prototype.updateDots = function() { - - var _ = this; - - if (_.$dots !== null) { - - _.$dots.find('li').removeClass('slick-active'); - _.$dots.find('li').eq(Math.floor(_.currentSlide / _.options.slidesToScroll)).addClass('slick-active'); - - } - - }; - - $.fn.slick = function(options) { - var _ = this; - return _.each(function(index, element) { - - element.slick = new Slick(element, options); - - }); - }; - - $.fn.slickAdd = function(slide, slideIndex, addBefore) { - var _ = this; - return _.each(function(index, element) { - - element.slick.addSlide(slide, slideIndex, addBefore); - - }); - }; - - $.fn.slickCurrentSlide = function() { - var _ = this; - return _.get(0).slick.getCurrent(); - }; - - $.fn.slickFilter = function(filter) { - var _ = this; - return _.each(function(index, element) { - - element.slick.filterSlides(filter); - - }); - }; - - $.fn.slickGoTo = function(slide) { - var _ = this; - return _.each(function(index, element) { - - var asNavFor = element.slick.options.asNavFor != null ? $(element.slick.options.asNavFor) : null; - if(asNavFor != null) asNavFor.slickGoTo(slide); - element.slick.slideHandler(slide); - - }); - }; - - $.fn.slickNext = function() { - var _ = this; - return _.each(function(index, element) { - - element.slick.changeSlide({ - data: { - message: 'next' - } - }); - - }); - }; - - $.fn.slickPause = function() { - var _ = this; - return _.each(function(index, element) { - - element.slick.autoPlayClear(); - element.slick.paused = true; - - }); - }; - - $.fn.slickPlay = function() { - var _ = this; - return _.each(function(index, element) { - - element.slick.paused = false; - element.slick.autoPlay(); - - }); - }; - - $.fn.slickPrev = function() { - var _ = this; - return _.each(function(index, element) { - - element.slick.changeSlide({ - data: { - message: 'previous' - } - }); - - }); - }; - - $.fn.slickRemove = function(slideIndex, removeBefore) { - var _ = this; - return _.each(function(index, element) { - - element.slick.removeSlide(slideIndex, removeBefore); - - }); - }; - - $.fn.slickGetOption = function(option) { - var _ = this; - return _.get(0).slick.options[option]; - }; - - $.fn.slickSetOption = function(option, value, refresh) { - var _ = this; - return _.each(function(index, element) { - - element.slick.options[option] = value; - - if (refresh === true) { - element.slick.unload(); - element.slick.reinit(); - } - - }); - }; - - $.fn.slickUnfilter = function() { - var _ = this; - return _.each(function(index, element) { - - element.slick.unfilterSlides(); - - }); - }; - - $.fn.unslick = function() { - var _ = this; - return _.each(function(index, element) { - - if (element.slick) { - element.slick.destroy(); - } - - }); - }; - - $.fn.getSlick = function() { - var s = null; - var _ = this; - _.each(function(index, element) { - s = element.slick; - }); - - return s; - }; - -})); diff --git a/pykeg/web/static/lib/slick/slick.min.js b/pykeg/web/static/lib/slick/slick.min.js deleted file mode 100755 index ee4b59ec2..000000000 --- a/pykeg/web/static/lib/slick/slick.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){"use strict";var b=window.Slick||{};b=function(){function c(c,d){var f,g,e=this;if(e.defaults={accessibility:!0,appendArrows:a(c),arrows:!0,asNavFor:null,prevArrow:'<button type="button" data-role="none" class="slick-prev">Previous</button>',nextArrow:'<button type="button" data-role="none" class="slick-next">Next</button>',autoplay:!1,autoplaySpeed:3e3,centerMode:!1,centerPadding:"50px",cssEase:"ease",customPaging:function(a,b){return'<button type="button" data-role="none">'+(b+1)+"</button>"},dots:!1,dotsClass:"slick-dots",draggable:!0,easing:"linear",fade:!1,focusOnSelect:!1,infinite:!0,lazyLoad:"ondemand",onBeforeChange:null,onAfterChange:null,onInit:null,onReInit:null,pauseOnHover:!0,pauseOnDotsHover:!1,responsive:null,rtl:!1,slide:"div",slidesToShow:1,slidesToScroll:1,speed:300,swipe:!0,touchMove:!0,touchThreshold:5,useCSS:!0,vertical:!1},e.initials={animating:!1,dragging:!1,autoPlayTimer:null,currentSlide:0,currentLeft:null,direction:1,$dots:null,listWidth:null,listHeight:null,loadIndex:0,$nextArrow:null,$prevArrow:null,slideCount:null,slideWidth:null,$slideTrack:null,$slides:null,sliding:!1,slideOffset:0,swipeLeft:null,$list:null,touchObject:{},transformsEnabled:!1},a.extend(e,e.initials),e.activeBreakpoint=null,e.animType=null,e.animProp=null,e.breakpoints=[],e.breakpointSettings=[],e.cssTransitions=!1,e.paused=!1,e.positionProp=null,e.$slider=a(c),e.$slidesCache=null,e.transformType=null,e.transitionType=null,e.windowWidth=0,e.windowTimer=null,e.options=a.extend({},e.defaults,d),e.originalSettings=e.options,f=e.options.responsive||null,f&&f.length>-1){for(g in f)f.hasOwnProperty(g)&&(e.breakpoints.push(f[g].breakpoint),e.breakpointSettings[f[g].breakpoint]=f[g].settings);e.breakpoints.sort(function(a,b){return b-a})}e.autoPlay=a.proxy(e.autoPlay,e),e.autoPlayClear=a.proxy(e.autoPlayClear,e),e.changeSlide=a.proxy(e.changeSlide,e),e.selectHandler=a.proxy(e.selectHandler,e),e.setPosition=a.proxy(e.setPosition,e),e.swipeHandler=a.proxy(e.swipeHandler,e),e.dragHandler=a.proxy(e.dragHandler,e),e.keyHandler=a.proxy(e.keyHandler,e),e.autoPlayIterator=a.proxy(e.autoPlayIterator,e),e.instanceUid=b++,e.htmlExpr=/^(?:\s*(<[\w\W]+>)[^>]*)$/,e.init()}var b=0;return c}(),b.prototype.addSlide=function(b,c,d){var e=this;if("boolean"==typeof c)d=c,c=null;else if(0>c||c>=e.slideCount)return!1;e.unload(),"number"==typeof c?0===c&&0===e.$slides.length?a(b).appendTo(e.$slideTrack):d?a(b).insertBefore(e.$slides.eq(c)):a(b).insertAfter(e.$slides.eq(c)):d===!0?a(b).prependTo(e.$slideTrack):a(b).appendTo(e.$slideTrack),e.$slides=e.$slideTrack.children(this.options.slide),e.$slideTrack.children(this.options.slide).detach(),e.$slideTrack.append(e.$slides),e.$slides.each(function(b,c){a(c).attr("index",b)}),e.$slidesCache=e.$slides,e.reinit()},b.prototype.animateSlide=function(b,c){var d={},e=this;e.options.rtl===!0&&e.options.vertical===!1&&(b=-b),e.transformsEnabled===!1?e.options.vertical===!1?e.$slideTrack.animate({left:b},e.options.speed,e.options.easing,c):e.$slideTrack.animate({top:b},e.options.speed,e.options.easing,c):e.cssTransitions===!1?a({animStart:e.currentLeft}).animate({animStart:b},{duration:e.options.speed,easing:e.options.easing,step:function(a){e.options.vertical===!1?(d[e.animType]="translate("+a+"px, 0px)",e.$slideTrack.css(d)):(d[e.animType]="translate(0px,"+a+"px)",e.$slideTrack.css(d))},complete:function(){c&&c.call()}}):(e.applyTransition(),d[e.animType]=e.options.vertical===!1?"translate3d("+b+"px, 0px, 0px)":"translate3d(0px,"+b+"px, 0px)",e.$slideTrack.css(d),c&&setTimeout(function(){e.disableTransition(),c.call()},e.options.speed))},b.prototype.applyTransition=function(a){var b=this,c={};c[b.transitionType]=b.options.fade===!1?b.transformType+" "+b.options.speed+"ms "+b.options.cssEase:"opacity "+b.options.speed+"ms "+b.options.cssEase,b.options.fade===!1?b.$slideTrack.css(c):b.$slides.eq(a).css(c)},b.prototype.autoPlay=function(){var a=this;a.autoPlayTimer&&clearInterval(a.autoPlayTimer),a.slideCount>a.options.slidesToShow&&a.paused!==!0&&(a.autoPlayTimer=setInterval(a.autoPlayIterator,a.options.autoplaySpeed))},b.prototype.autoPlayClear=function(){var a=this;a.autoPlayTimer&&clearInterval(a.autoPlayTimer)},b.prototype.autoPlayIterator=function(){var b=this,c=null!=b.options.asNavFor?a(b.options.asNavFor).getSlick():null;b.options.infinite===!1?1===b.direction?(b.currentSlide+1===b.slideCount-1&&(b.direction=0),b.slideHandler(b.currentSlide+b.options.slidesToScroll),null!=c&&c.slideHandler(c.currentSlide+c.options.slidesToScroll)):(0===b.currentSlide-1&&(b.direction=1),b.slideHandler(b.currentSlide-b.options.slidesToScroll),null!=c&&c.slideHandler(c.currentSlide-c.options.slidesToScroll)):(b.slideHandler(b.currentSlide+b.options.slidesToScroll),null!=c&&c.slideHandler(c.currentSlide+c.options.slidesToScroll))},b.prototype.buildArrows=function(){var b=this;b.options.arrows===!0&&b.slideCount>b.options.slidesToShow&&(b.$prevArrow=a(b.options.prevArrow),b.$nextArrow=a(b.options.nextArrow),b.htmlExpr.test(b.options.prevArrow)&&b.$prevArrow.appendTo(b.options.appendArrows),b.htmlExpr.test(b.options.nextArrow)&&b.$nextArrow.appendTo(b.options.appendArrows),b.options.infinite!==!0&&b.$prevArrow.addClass("slick-disabled"))},b.prototype.buildDots=function(){var c,d,b=this;if(b.options.dots===!0&&b.slideCount>b.options.slidesToShow){for(d='<ul class="'+b.options.dotsClass+'">',c=0;c<=b.getDotCount();c+=1)d+="<li>"+b.options.customPaging.call(this,b,c)+"</li>";d+="</ul>",b.$dots=a(d).appendTo(b.$slider),b.$dots.find("li").first().addClass("slick-active")}},b.prototype.buildOut=function(){var b=this;b.$slides=b.$slider.children(b.options.slide+":not(.slick-cloned)").addClass("slick-slide"),b.slideCount=b.$slides.length,b.$slides.each(function(b,c){a(c).attr("index",b)}),b.$slidesCache=b.$slides,b.$slider.addClass("slick-slider"),b.$slideTrack=0===b.slideCount?a('<div class="slick-track"/>').appendTo(b.$slider):b.$slides.wrapAll('<div class="slick-track"/>').parent(),b.$list=b.$slideTrack.wrap('<div class="slick-list"/>').parent(),b.$slideTrack.css("opacity",0),b.options.centerMode===!0&&(b.options.slidesToScroll=1,0===b.options.slidesToShow%2&&(b.options.slidesToShow=3)),a("img[data-lazy]",b.$slider).not("[src]").addClass("slick-loading"),b.setupInfinite(),b.buildArrows(),b.buildDots(),b.updateDots(),b.options.accessibility===!0&&b.$list.prop("tabIndex",0),b.setSlideClasses("number"==typeof this.currentSlide?this.currentSlide:0),b.options.draggable===!0&&b.$list.addClass("draggable")},b.prototype.checkResponsive=function(){var c,d,b=this;if(b.originalSettings.responsive&&b.originalSettings.responsive.length>-1&&null!==b.originalSettings.responsive){d=null;for(c in b.breakpoints)b.breakpoints.hasOwnProperty(c)&&a(window).width()<b.breakpoints[c]&&(d=b.breakpoints[c]);null!==d?null!==b.activeBreakpoint?d!==b.activeBreakpoint&&(b.activeBreakpoint=d,b.options=a.extend({},b.options,b.breakpointSettings[d]),b.refresh()):(b.activeBreakpoint=d,b.options=a.extend({},b.options,b.breakpointSettings[d]),b.refresh()):null!==b.activeBreakpoint&&(b.activeBreakpoint=null,b.options=a.extend({},b.options,b.originalSettings),b.refresh())}},b.prototype.changeSlide=function(b){var c=this,d=a(b.target),e=null!=c.options.asNavFor?a(c.options.asNavFor).getSlick():null;switch(d.is("a")&&b.preventDefault(),b.data.message){case"previous":c.slideCount>c.options.slidesToShow&&(c.slideHandler(c.currentSlide-c.options.slidesToScroll),null!=e&&e.slideHandler(e.currentSlide-e.options.slidesToScroll));break;case"next":c.slideCount>c.options.slidesToShow&&(c.slideHandler(c.currentSlide+c.options.slidesToScroll),null!=e&&e.slideHandler(e.currentSlide+e.options.slidesToScroll));break;case"index":var f=a(b.target).parent().index()*c.options.slidesToScroll;c.slideHandler(f),null!=e&&e.slideHandler(f);break;default:return!1}},b.prototype.destroy=function(){var b=this;b.autoPlayClear(),b.touchObject={},a(".slick-cloned",b.$slider).remove(),b.$dots&&b.$dots.remove(),b.$prevArrow&&(b.$prevArrow.remove(),b.$nextArrow.remove()),b.$slides.parent().hasClass("slick-track")&&b.$slides.unwrap().unwrap(),b.$slides.removeClass("slick-slide slick-active slick-visible").removeAttr("style"),b.$slider.removeClass("slick-slider"),b.$slider.removeClass("slick-initialized"),b.$list.off(".slick"),a(window).off(".slick-"+b.instanceUid),a(document).off(".slick-"+b.instanceUid)},b.prototype.disableTransition=function(a){var b=this,c={};c[b.transitionType]="",b.options.fade===!1?b.$slideTrack.css(c):b.$slides.eq(a).css(c)},b.prototype.fadeSlide=function(a,b){var c=this;c.cssTransitions===!1?(c.$slides.eq(a).css({zIndex:1e3}),c.$slides.eq(a).animate({opacity:1},c.options.speed,c.options.easing,b)):(c.applyTransition(a),c.$slides.eq(a).css({opacity:1,zIndex:1e3}),b&&setTimeout(function(){c.disableTransition(a),b.call()},c.options.speed))},b.prototype.filterSlides=function(a){var b=this;null!==a&&(b.unload(),b.$slideTrack.children(this.options.slide).detach(),b.$slidesCache.filter(a).appendTo(b.$slideTrack),b.reinit())},b.prototype.getCurrent=function(){var a=this;return a.currentSlide},b.prototype.getDotCount=function(){var e,a=this,b=0,c=0,d=0;for(e=a.options.infinite===!0?a.slideCount+a.options.slidesToShow-a.options.slidesToScroll:a.slideCount;e>b;)d++,c+=a.options.slidesToScroll,b=c+a.options.slidesToShow;return d},b.prototype.getLeft=function(a){var c,d,b=this,e=0;return b.slideOffset=0,d=b.$slides.first().outerHeight(),b.options.infinite===!0?(b.slideCount>b.options.slidesToShow&&(b.slideOffset=-1*b.slideWidth*b.options.slidesToShow,e=-1*d*b.options.slidesToShow),0!==b.slideCount%b.options.slidesToScroll&&a+b.options.slidesToScroll>b.slideCount&&b.slideCount>b.options.slidesToShow&&(b.slideOffset=-1*b.slideCount%b.options.slidesToShow*b.slideWidth,e=-1*b.slideCount%b.options.slidesToShow*d)):0!==b.slideCount%b.options.slidesToShow&&a+b.options.slidesToScroll>b.slideCount&&b.slideCount>b.options.slidesToShow&&(b.slideOffset=b.options.slidesToShow*b.slideWidth-b.slideCount%b.options.slidesToShow*b.slideWidth,e=b.slideCount%b.options.slidesToShow*d),b.options.centerMode===!0&&b.options.infinite===!0?b.slideOffset+=b.slideWidth*Math.floor(b.options.slidesToShow/2)-b.slideWidth:b.options.centerMode===!0&&(b.slideOffset+=b.slideWidth*Math.floor(b.options.slidesToShow/2)),c=b.options.vertical===!1?-1*a*b.slideWidth+b.slideOffset:-1*a*d+e},b.prototype.init=function(){var b=this;a(b.$slider).hasClass("slick-initialized")||(a(b.$slider).addClass("slick-initialized"),b.buildOut(),b.setProps(),b.startLoad(),b.loadSlider(),b.initializeEvents(),b.checkResponsive()),null!==b.options.onInit&&b.options.onInit.call(this,b)},b.prototype.initArrowEvents=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.on("click.slick",{message:"previous"},a.changeSlide),a.$nextArrow.on("click.slick",{message:"next"},a.changeSlide))},b.prototype.initDotEvents=function(){var b=this;b.options.dots===!0&&b.slideCount>b.options.slidesToShow&&a("li",b.$dots).on("click.slick",{message:"index"},b.changeSlide),b.options.dots===!0&&b.options.pauseOnDotsHover===!0&&b.options.autoplay===!0&&a("li",b.$dots).on("mouseenter.slick",b.autoPlayClear).on("mouseleave.slick",b.autoPlay)},b.prototype.initializeEvents=function(){var b=this;b.initArrowEvents(),b.initDotEvents(),b.$list.on("touchstart.slick mousedown.slick",{action:"start"},b.swipeHandler),b.$list.on("touchmove.slick mousemove.slick",{action:"move"},b.swipeHandler),b.$list.on("touchend.slick mouseup.slick",{action:"end"},b.swipeHandler),b.$list.on("touchcancel.slick mouseleave.slick",{action:"end"},b.swipeHandler),b.options.pauseOnHover===!0&&b.options.autoplay===!0&&(b.$list.on("mouseenter.slick",b.autoPlayClear),b.$list.on("mouseleave.slick",b.autoPlay)),b.options.accessibility===!0&&b.$list.on("keydown.slick",b.keyHandler),b.options.focusOnSelect===!0&&a(b.options.slide,b.$slideTrack).on("click.slick",b.selectHandler),a(window).on("orientationchange.slick.slick-"+b.instanceUid,function(){b.checkResponsive(),b.setPosition()}),a(window).on("resize.slick.slick-"+b.instanceUid,function(){a(window).width()!==b.windowWidth&&(clearTimeout(b.windowDelay),b.windowDelay=window.setTimeout(function(){b.windowWidth=a(window).width(),b.checkResponsive(),b.setPosition()},50))}),a(window).on("load.slick.slick-"+b.instanceUid,b.setPosition),a(document).on("ready.slick.slick-"+b.instanceUid,b.setPosition)},b.prototype.initUI=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.show(),a.$nextArrow.show()),a.options.dots===!0&&a.slideCount>a.options.slidesToShow&&a.$dots.show(),a.options.autoplay===!0&&a.autoPlay()},b.prototype.keyHandler=function(a){var b=this;37===a.keyCode?b.changeSlide({data:{message:"previous"}}):39===a.keyCode&&b.changeSlide({data:{message:"next"}})},b.prototype.lazyLoad=function(){function g(b){a("img[data-lazy]",b).each(function(){var b=a(this),c=a(this).attr("data-lazy")+"?"+(new Date).getTime();b.load(function(){b.animate({opacity:1},200)}).css({opacity:0}).attr("src",c).removeAttr("data-lazy").removeClass("slick-loading")})}var c,d,e,f,b=this;b.options.centerMode===!0||b.options.fade===!0?(e=b.options.slidesToShow+b.currentSlide-1,f=e+b.options.slidesToShow+2):(e=b.options.infinite?b.options.slidesToShow+b.currentSlide:b.currentSlide,f=e+b.options.slidesToShow),c=b.$slider.find(".slick-slide").slice(e,f),g(c),1==b.slideCount?(d=b.$slider.find(".slick-slide"),g(d)):b.currentSlide>=b.slideCount-b.options.slidesToShow?(d=b.$slider.find(".slick-cloned").slice(0,b.options.slidesToShow),g(d)):0===b.currentSlide&&(d=b.$slider.find(".slick-cloned").slice(-1*b.options.slidesToShow),g(d))},b.prototype.loadSlider=function(){var a=this;a.setPosition(),a.$slideTrack.css({opacity:1}),a.$slider.removeClass("slick-loading"),a.initUI(),"progressive"===a.options.lazyLoad&&a.progressiveLazyLoad()},b.prototype.postSlide=function(a){var b=this;null!==b.options.onAfterChange&&b.options.onAfterChange.call(this,b,a),b.animating=!1,b.setPosition(),b.swipeLeft=null,b.options.autoplay===!0&&b.paused===!1&&b.autoPlay()},b.prototype.progressiveLazyLoad=function(){var c,d,b=this;c=a("img[data-lazy]").length,c>0&&(d=a("img[data-lazy]",b.$slider).first(),d.attr("src",d.attr("data-lazy")).removeClass("slick-loading").load(function(){d.removeAttr("data-lazy"),b.progressiveLazyLoad()}))},b.prototype.refresh=function(){var b=this,c=b.currentSlide;b.destroy(),a.extend(b,b.initials),b.currentSlide=c,b.init()},b.prototype.reinit=function(){var b=this;b.$slides=b.$slideTrack.children(b.options.slide).addClass("slick-slide"),b.slideCount=b.$slides.length,b.currentSlide>=b.slideCount&&0!==b.currentSlide&&(b.currentSlide=b.currentSlide-b.options.slidesToScroll),b.setProps(),b.setupInfinite(),b.buildArrows(),b.updateArrows(),b.initArrowEvents(),b.buildDots(),b.updateDots(),b.initDotEvents(),b.options.focusOnSelect===!0&&a(b.options.slide,b.$slideTrack).on("click.slick",b.selectHandler),b.setSlideClasses(0),b.setPosition(),null!==b.options.onReInit&&b.options.onReInit.call(this,b)},b.prototype.removeSlide=function(a,b){var c=this;return"boolean"==typeof a?(b=a,a=b===!0?0:c.slideCount-1):a=b===!0?--a:a,c.slideCount<1||0>a||a>c.slideCount-1?!1:(c.unload(),c.$slideTrack.children(this.options.slide).eq(a).remove(),c.$slides=c.$slideTrack.children(this.options.slide),c.$slideTrack.children(this.options.slide).detach(),c.$slideTrack.append(c.$slides),c.$slidesCache=c.$slides,c.reinit(),void 0)},b.prototype.setCSS=function(a){var d,e,b=this,c={};b.options.rtl===!0&&(a=-a),d="left"==b.positionProp?a+"px":"0px",e="top"==b.positionProp?a+"px":"0px",c[b.positionProp]=a,b.transformsEnabled===!1?b.$slideTrack.css(c):(c={},b.cssTransitions===!1?(c[b.animType]="translate("+d+", "+e+")",b.$slideTrack.css(c)):(c[b.animType]="translate3d("+d+", "+e+", 0px)",b.$slideTrack.css(c)))},b.prototype.setDimensions=function(){var a=this;a.options.vertical===!1?a.options.centerMode===!0&&a.$list.css({padding:"0px "+a.options.centerPadding}):(a.$list.height(a.$slides.first().outerHeight(!0)*a.options.slidesToShow),a.options.centerMode===!0&&a.$list.css({padding:a.options.centerPadding+" 0px"})),a.listWidth=a.$list.width(),a.listHeight=a.$list.height(),a.options.vertical===!1?(a.slideWidth=Math.ceil(a.listWidth/a.options.slidesToShow),a.$slideTrack.width(Math.ceil(a.slideWidth*a.$slideTrack.children(".slick-slide").length))):(a.slideWidth=Math.ceil(a.listWidth),a.$slideTrack.height(Math.ceil(a.$slides.first().outerHeight(!0)*a.$slideTrack.children(".slick-slide").length)));var b=a.$slides.first().outerWidth(!0)-a.$slides.first().width();a.$slideTrack.children(".slick-slide").width(a.slideWidth-b)},b.prototype.setFade=function(){var c,b=this;b.$slides.each(function(d,e){c=-1*b.slideWidth*d,a(e).css({position:"relative",left:c,top:0,zIndex:800,opacity:0})}),b.$slides.eq(b.currentSlide).css({zIndex:900,opacity:1})},b.prototype.setPosition=function(){var a=this;a.setDimensions(),a.options.fade===!1?a.setCSS(a.getLeft(a.currentSlide)):a.setFade()},b.prototype.setProps=function(){var a=this;a.positionProp=a.options.vertical===!0?"top":"left","top"===a.positionProp?a.$slider.addClass("slick-vertical"):a.$slider.removeClass("slick-vertical"),(void 0!==document.body.style.WebkitTransition||void 0!==document.body.style.MozTransition||void 0!==document.body.style.msTransition)&&a.options.useCSS===!0&&(a.cssTransitions=!0),void 0!==document.body.style.MozTransform&&(a.animType="MozTransform",a.transformType="-moz-transform",a.transitionType="MozTransition"),void 0!==document.body.style.webkitTransform&&(a.animType="webkitTransform",a.transformType="-webkit-transform",a.transitionType="webkitTransition"),void 0!==document.body.style.msTransform&&(a.animType="msTransform",a.transformType="-ms-transform",a.transitionType="msTransition"),void 0!==document.body.style.transform&&(a.animType="transform",a.transformType="transform",a.transitionType="transition"),a.transformsEnabled=null!==a.animType},b.prototype.setSlideClasses=function(a){var c,d,e,f,b=this;b.$slider.find(".slick-slide").removeClass("slick-active").removeClass("slick-center"),d=b.$slider.find(".slick-slide"),b.options.centerMode===!0?(c=Math.floor(b.options.slidesToShow/2),b.options.infinite===!0&&(a>=c&&a<=b.slideCount-1-c?b.$slides.slice(a-c,a+c+1).addClass("slick-active"):(e=b.options.slidesToShow+a,d.slice(e-c+1,e+c+2).addClass("slick-active")),0===a?d.eq(d.length-1-b.options.slidesToShow).addClass("slick-center"):a===b.slideCount-1&&d.eq(b.options.slidesToShow).addClass("slick-center")),b.$slides.eq(a).addClass("slick-center")):a>=0&&a<=b.slideCount-b.options.slidesToShow?b.$slides.slice(a,a+b.options.slidesToShow).addClass("slick-active"):d.length<=b.options.slidesToShow?d.addClass("slick-active"):(f=b.slideCount%b.options.slidesToShow,e=b.options.infinite===!0?b.options.slidesToShow+a:a,b.options.slidesToShow==b.options.slidesToScroll&&b.slideCount-a<b.options.slidesToShow?d.slice(e-(b.options.slidesToShow-f),e+f).addClass("slick-active"):d.slice(e,e+b.options.slidesToShow).addClass("slick-active")),"ondemand"===b.options.lazyLoad&&b.lazyLoad()},b.prototype.setupInfinite=function(){var c,d,e,b=this;if((b.options.fade===!0||b.options.vertical===!0)&&(b.options.centerMode=!1),b.options.infinite===!0&&b.options.fade===!1&&(d=null,b.slideCount>b.options.slidesToShow)){for(e=b.options.centerMode===!0?b.options.slidesToShow+1:b.options.slidesToShow,c=b.slideCount;c>b.slideCount-e;c-=1)d=c-1,a(b.$slides[d]).clone(!0).attr("id","").prependTo(b.$slideTrack).addClass("slick-cloned");for(c=0;e>c;c+=1)d=c,a(b.$slides[d]).clone(!0).attr("id","").appendTo(b.$slideTrack).addClass("slick-cloned");b.$slideTrack.find(".slick-cloned").find("[id]").each(function(){a(this).attr("id","")})}},b.prototype.selectHandler=function(b){var c=this,d=null!=c.options.asNavFor?a(c.options.asNavFor).getSlick():null,e=parseInt(a(b.target).parent().attr("index"));if(e||(e=0),!(c.slideCount<=c.options.slidesToShow)&&(c.slideHandler(e),null!=d)){if(d.slideCount<=d.options.slidesToShow)return;d.slideHandler(e)}},b.prototype.slideHandler=function(a){var b,c,d,e,f=null,g=this;return g.animating===!0?!1:(b=a,f=g.getLeft(b),d=g.getLeft(g.currentSlide),e=0!==g.slideCount%g.options.slidesToScroll?g.options.slidesToScroll:0,g.currentLeft=null===g.swipeLeft?d:g.swipeLeft,g.options.infinite===!1&&g.options.centerMode===!1&&(0>a||a>g.slideCount-g.options.slidesToShow+e)?(g.options.fade===!1&&(b=g.currentSlide,g.animateSlide(d,function(){g.postSlide(b)})),!1):g.options.infinite===!1&&g.options.centerMode===!0&&(0>a||a>g.slideCount-g.options.slidesToScroll)?(g.options.fade===!1&&(b=g.currentSlide,g.animateSlide(d,function(){g.postSlide(b)})),!1):(g.options.autoplay===!0&&clearInterval(g.autoPlayTimer),c=0>b?0!==g.slideCount%g.options.slidesToScroll?g.slideCount-g.slideCount%g.options.slidesToScroll:g.slideCount-g.options.slidesToScroll:b>g.slideCount-1?0:b,g.animating=!0,null!==g.options.onBeforeChange&&a!==g.currentSlide&&g.options.onBeforeChange.call(this,g,g.currentSlide,c),g.currentSlide=c,g.setSlideClasses(g.currentSlide),g.updateDots(),g.updateArrows(),g.options.fade===!0?(g.fadeSlide(c,function(){g.postSlide(c)}),!1):(g.animateSlide(f,function(){g.postSlide(c)}),void 0)))},b.prototype.startLoad=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.hide(),a.$nextArrow.hide()),a.options.dots===!0&&a.slideCount>a.options.slidesToShow&&a.$dots.hide(),a.$slider.addClass("slick-loading")},b.prototype.swipeDirection=function(){var a,b,c,d,e=this;return a=e.touchObject.startX-e.touchObject.curX,b=e.touchObject.startY-e.touchObject.curY,c=Math.atan2(b,a),d=Math.round(180*c/Math.PI),0>d&&(d=360-Math.abs(d)),45>=d&&d>=0?"left":360>=d&&d>=315?"left":d>=135&&225>=d?"right":"vertical"},b.prototype.swipeEnd=function(b){var c=this,d=null!=c.options.asNavFor?a(c.options.asNavFor).getSlick():null;if(c.dragging=!1,void 0===c.touchObject.curX)return!1;if(c.touchObject.swipeLength>=c.touchObject.minSwipe)switch(a(b.target).on("click.slick",function(b){b.stopImmediatePropagation(),b.stopPropagation(),b.preventDefault(),a(b.target).off("click.slick")}),c.swipeDirection()){case"left":c.slideHandler(c.currentSlide+c.options.slidesToScroll),null!=d&&d.slideHandler(d.currentSlide+d.options.slidesToScroll),c.touchObject={};break;case"right":c.slideHandler(c.currentSlide-c.options.slidesToScroll),null!=d&&d.slideHandler(d.currentSlide-d.options.slidesToScroll),c.touchObject={}}else c.touchObject.startX!==c.touchObject.curX&&(c.slideHandler(c.currentSlide),null!=d&&d.slideHandler(d.currentSlide),c.touchObject={})},b.prototype.swipeHandler=function(a){var b=this;if(!(b.options.swipe===!1||"ontouchend"in document&&b.options.swipe===!1||b.options.draggable===!1||b.options.draggable===!1&&!a.originalEvent.touches))switch(b.touchObject.fingerCount=a.originalEvent&&void 0!==a.originalEvent.touches?a.originalEvent.touches.length:1,b.touchObject.minSwipe=b.listWidth/b.options.touchThreshold,a.data.action){case"start":b.swipeStart(a);break;case"move":b.swipeMove(a);break;case"end":b.swipeEnd(a)}},b.prototype.swipeMove=function(a){var c,d,e,f,b=this;return f=void 0!==a.originalEvent?a.originalEvent.touches:null,c=b.getLeft(b.currentSlide),!b.dragging||f&&1!==f.length?!1:(b.touchObject.curX=void 0!==f?f[0].pageX:a.clientX,b.touchObject.curY=void 0!==f?f[0].pageY:a.clientY,b.touchObject.swipeLength=Math.round(Math.sqrt(Math.pow(b.touchObject.curX-b.touchObject.startX,2))),d=b.swipeDirection(),"vertical"!==d?(void 0!==a.originalEvent&&b.touchObject.swipeLength>4&&a.preventDefault(),e=b.touchObject.curX>b.touchObject.startX?1:-1,b.swipeLeft=b.options.vertical===!1?c+b.touchObject.swipeLength*e:c+b.touchObject.swipeLength*(b.$list.height()/b.listWidth)*e,b.options.fade===!0||b.options.touchMove===!1?!1:b.animating===!0?(b.swipeLeft=null,!1):(b.setCSS(b.swipeLeft),void 0)):void 0)},b.prototype.swipeStart=function(a){var c,b=this;return 1!==b.touchObject.fingerCount||b.slideCount<=b.options.slidesToShow?(b.touchObject={},!1):(void 0!==a.originalEvent&&void 0!==a.originalEvent.touches&&(c=a.originalEvent.touches[0]),b.touchObject.startX=b.touchObject.curX=void 0!==c?c.pageX:a.clientX,b.touchObject.startY=b.touchObject.curY=void 0!==c?c.pageY:a.clientY,b.dragging=!0,void 0)},b.prototype.unfilterSlides=function(){var a=this;null!==a.$slidesCache&&(a.unload(),a.$slideTrack.children(this.options.slide).detach(),a.$slidesCache.appendTo(a.$slideTrack),a.reinit())},b.prototype.unload=function(){var b=this;a(".slick-cloned",b.$slider).remove(),b.$dots&&b.$dots.remove(),b.$prevArrow&&(b.$prevArrow.remove(),b.$nextArrow.remove()),b.$slides.removeClass("slick-slide slick-active slick-visible").removeAttr("style")},b.prototype.updateArrows=function(){var a=this;a.options.arrows===!0&&a.options.infinite!==!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.removeClass("slick-disabled"),a.$nextArrow.removeClass("slick-disabled"),0===a.currentSlide?(a.$prevArrow.addClass("slick-disabled"),a.$nextArrow.removeClass("slick-disabled")):a.currentSlide>=a.slideCount-a.options.slidesToShow&&(a.$nextArrow.addClass("slick-disabled"),a.$prevArrow.removeClass("slick-disabled")))},b.prototype.updateDots=function(){var a=this;null!==a.$dots&&(a.$dots.find("li").removeClass("slick-active"),a.$dots.find("li").eq(Math.floor(a.currentSlide/a.options.slidesToScroll)).addClass("slick-active"))},a.fn.slick=function(a){var c=this;return c.each(function(c,d){d.slick=new b(d,a)})},a.fn.slickAdd=function(a,b,c){var d=this;return d.each(function(d,e){e.slick.addSlide(a,b,c)})},a.fn.slickCurrentSlide=function(){var a=this;return a.get(0).slick.getCurrent()},a.fn.slickFilter=function(a){var b=this;return b.each(function(b,c){c.slick.filterSlides(a)})},a.fn.slickGoTo=function(b){var c=this;return c.each(function(c,d){var e=null!=d.slick.options.asNavFor?a(d.slick.options.asNavFor):null;null!=e&&e.slickGoTo(b),d.slick.slideHandler(b)})},a.fn.slickNext=function(){var a=this;return a.each(function(a,b){b.slick.changeSlide({data:{message:"next"}})})},a.fn.slickPause=function(){var a=this;return a.each(function(a,b){b.slick.autoPlayClear(),b.slick.paused=!0})},a.fn.slickPlay=function(){var a=this;return a.each(function(a,b){b.slick.paused=!1,b.slick.autoPlay()})},a.fn.slickPrev=function(){var a=this;return a.each(function(a,b){b.slick.changeSlide({data:{message:"previous"}})})},a.fn.slickRemove=function(a,b){var c=this;return c.each(function(c,d){d.slick.removeSlide(a,b)})},a.fn.slickGetOption=function(a){var b=this;return b.get(0).slick.options[a]},a.fn.slickSetOption=function(a,b,c){var d=this;return d.each(function(d,e){e.slick.options[a]=b,c===!0&&(e.slick.unload(),e.slick.reinit())})},a.fn.slickUnfilter=function(){var a=this;return a.each(function(a,b){b.slick.unfilterSlides()})},a.fn.unslick=function(){var a=this;return a.each(function(a,b){b.slick&&b.slick.destroy()})},a.fn.getSlick=function(){var a=null,b=this;return b.each(function(b,c){a=c.slick}),a}}); \ No newline at end of file diff --git a/pykeg/web/static/lib/slick/slick.scss b/pykeg/web/static/lib/slick/slick.scss deleted file mode 100755 index c34f6b0e1..000000000 --- a/pykeg/web/static/lib/slick/slick.scss +++ /dev/null @@ -1,286 +0,0 @@ -@charset "UTF-8"; - -// Default Variables - -$slick-font-path: "./fonts/" !default; -$slick-font-family: "slick" !default; -$slick-loader-path: "./" !default; -$slick-arrow-color: white !default; -$slick-dot-color: black !default; -$slick-dot-color-active: $slick-dot-color !default; -$slick-prev-character: '\2190' !default; -$slick-next-character: '\2192' !default; -$slick-dot-character: '\2022' !default; -$slick-dot-size: 6px !default; -$opacity-default: .75; -$opacity-on-hover: 1; -$opacity-not-active: .25; - - -@function slick-image-url($url) { - @if function-exists(image-url) { - @return image-url($url, false, false); - } - @else { - @return url($slick-loader-path + $url); - } -} - -@function slick-font-url($url) { - @if function-exists(font-url) { - @return font-url($url); - } - @else { - @return url($slick-font-path + $url); - } -} - -/* Slider */ - -.slick-slider { - position: relative; - display: block; - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -ms-touch-action: none; - -webkit-tap-highlight-color: transparent; -} -.slick-list { - position: relative; - overflow: hidden; - display: block; - margin: 0; - padding: 0; - - &:focus { - outline: none; - } - - .slick-loading & { - background: #fff slick-image-url("ajax-loader.gif") center center no-repeat; - } - - &.dragging { - cursor: pointer; - cursor: hand; - } -} -.slick-slider .slick-list, -.slick-track, -.slick-slide, -.slick-slide img { - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -ms-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); -} -.slick-track { - position: relative; - left: 0; - top: 0; - display: block; - zoom: 1; - - &:before, - &:after { - content: ""; - display: table; - } - - &:after { - clear: both; - } - - .slick-loading & { - visibility: hidden; - } -} -.slick-slide { - float: left; - height: 100%; - min-height: 1px; - [dir="rtl"] & { - float: right; - } - img { - display: block; - } - &.slick-loading img { - display: none; - } - - display: none; - - &.dragging img { - pointer-events: none; - } - - .slick-initialized & { - display: block; - } - - .slick-loading & { - visibility: hidden; - } - - .slick-vertical & { - display: block; - height: auto; - border: 1px solid transparent; - } -} - -/* Icons */ -@if $slick-font-family == "slick" { - @font-face { - font-family:"slick"; - src: slick-font-url("slick.eot"); - src: slick-font-url("slick.eot?#iefix") format("embedded-opentype"), - slick-font-url("slick.woff") format("woff"), - slick-font-url("slick.ttf") format("truetype"), - slick-font-url("slick.svg#slick") format("svg"); - font-weight: normal; - font-style: normal; - } -} - -/* Arrows */ - -.slick-prev, -.slick-next { - position: absolute; - display: block; - height: 20px; - width: 20px; - line-height: 0; - font-size: 0; - cursor: pointer; - background: transparent; - color: transparent; - top: 50%; - margin-top: -10px; - padding: 0; - border: none; - outline: none; - &:hover, &:focus { - outline: none; - background: transparent; - color: transparent; - &:before { - opacity: $opacity-on-hover; - } - } - &.slick-disabled:before { - opacity: $opacity-not-active; - } -} -.slick-prev:before, .slick-next:before { - font-family: $slick-font-family; - font-size: 20px; - line-height: 1; - color: $slick-arrow-color; - opacity: $opacity-default; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.slick-prev { - left: -25px; - [dir="rtl"] & { - left: auto; - right: -25px; - } - &:before { - content: $slick-prev-character; - [dir="rtl"] & { - content: $slick-next-character; - } - } -} -.slick-next { - right: -25px; - [dir="rtl"] & { - left: -25px; - right: auto; - } - &:before { - content: $slick-next-character; - [dir="rtl"] & { - content: $slick-prev-character; - } - } -} - -/* Dots */ - -.slick-slider { - margin-bottom: 30px; -} -.slick-dots { - position: absolute; - bottom: -45px; - list-style: none; - display: block; - text-align: center; - padding: 0; - width: 100%; - - li { - position: relative; - display: inline-block; - height: 20px; - width: 20px; - margin: 0 5px; - padding: 0; - cursor: pointer; - - button { - border: 0; - background: transparent; - display: block; - height: 20px; - width: 20px; - outline: none; - line-height: 0; - font-size: 0; - color: transparent; - padding: 5px; - cursor: pointer; - &:hover, &:focus { - outline: none; - &:before { - opacity: $opacity-on-hover; - } - } - - &:before { - position: absolute; - top: 0; - left: 0; - content: $slick-dot-character; - width: 20px; - height: 20px; - font-family: $slick-font-family; - font-size: $slick-dot-size; - line-height: 20px; - text-align: center; - color: $slick-dot-color; - opacity: $opacity-not-active; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - } - - &.slick-active button:before { - color: $slick-dot-color-active; - opacity: $opacity-default; - } - } -} diff --git a/pykeg/web/templates/admin/login.html b/pykeg/web/templates/admin/login.html deleted file mode 100644 index 38db84c31..000000000 --- a/pykeg/web/templates/admin/login.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "page-twocol.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Log In to {{kbsite.title}}{% endblock %} -{% block pagetitle %}Log In{% endblock %} - -{% block col-1 %} -<div class="well"> - <form action="" method="POST"> - {% csrf_token %} - {{ form|crispy }} - <button type="submit" class="btn btn-success small" - name="tweet-form-submit">Log In</button> -<p> - <span style="font-size:0.8em;"> - (did you <a href="{% url "password_reset" %}">forget your username or password?</a>) - </span> -</p> - -{% endblock col-1 %} \ No newline at end of file diff --git a/pykeg/web/templates/base.html b/pykeg/web/templates/base.html deleted file mode 100644 index 32676c741..000000000 --- a/pykeg/web/templates/base.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends "skel.html" %} -{% load static humanize kegweblib %} - -{% block title %}{% if kbsite.title %}{{ kbsite.title }}{% else %}Kegbot{% endif %}{% endblock %} - -{% block css %} -{{ block.super }} -<style> - body { -{% if kbsite %}{% if kbsite.background_image %} - background-image: url({{kbsite.background_image.image.url}}); -{% else %} - background-image: url({% static "images/background.png" %}); -{% endif %} {% endif %} - background-repeat: no-repeat; - background-attachment: fixed; - } -</style> - -{% endblock css %} - -{% block body %} - <div class="navbar navbar-inverse navbar-fixed-top"> - <div class="navbar-inner"> - <div class="container"> - {% spaceless %} - {% if kbsite %} - <a class="brand" href="{% url "kb-home" %}"> - {% else %} - <a class="brand" href="/"> - {% endif %} - {{ kbsite.title }} - </a>{% endspaceless %} - - <ul class="nav"> - {% if kbsite %} - {% if HAVE_SESSIONS %}{% navitem "kb-sessions" "Sessions" %}{% endif %} - {% navitem "kb-kegs" "Kegs" %} - {% navitem "kb-stats" "Stats" %} - {% if user.is_authenticated %}{% navitem "kb-account-main" "Account" %}{% endif %} - {% if user.is_staff %} - {% navitem "kegadmin-dashboard" "Admin" %} - {% endif %} - {% endif %} - </ul> - - {% if not user.is_authenticated %} - <ul class="nav pull-right"> - {% if kbsite.registration_mode == 'public' and not SSO_LOGIN_URL %} - <li><a href="{% url "registration_register" %}"><i class="icon-edit icon-white"></i> New Drinker</a></li> - {% endif %} - {% if SSO_LOGIN_URL %} - <li><a href="{{ SSO_LOGIN_URL }}"><i class="icon-user icon-white"></i> Log In</a></li> - {% else %} - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-user icon-white"></i> Sign In <b class="caret"></b></a> - <ul class="dropdown-menu" style="padding: 15px;"> - <form action="/accounts/login/" method="POST"> - {% csrf_token %} - {% with login_form as form %} - {% for hidden in form.hidden_fields %} - {{ hidden }} - {% endfor %} - {% endwith %} - <input class="span2" name="username" type="text" placeholder="Username" > - <input class="span2" name="password" type="password" placeholder="Password"> - <button class="btn" type="submit">Log In <i class="icon-ok"></i></button> - </form> - </ul> - </li> - {% endif %} - </ul> - {% else %} - <ul class="nav pull-right"> - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-user icon-white"></i> {{ user.username}} <b class="caret"></b></a> - <ul class="dropdown-menu"> - {% if SSO_LOGOUT_URL %} - <li><a href="{{ SSO_LOGOUT_URL }}">Logout</a></li> - {% else %} - <li><a href="{% url "logout" %}">Logout</a></li> - {% endif %} - </ul> - </li> - </ul> - {% endif %} - - </div> - </div> - </div> <!-- /.navbar --> - - <div class="container"> - {% block header-margin %} - <div class="page-header"> - <h1>{% block pagetitle %}{% endblock %}</h1> - {% block breadcrumbs %}{% endblock %} - {% block messages %} - {% for message in messages %} - <div class="alert {% for tag in message.tags.split %}alert-{{tag}} {% endfor %}"> - <a class="close" data-dismiss="alert" href="#">×</a> - <p> - {{message}} - </p> - </div> - {% endfor %} - {% endblock messages %} - </div> - {% endblock %} - - <div id="content"> - {% block content %}{% endblock %} - {% block extra-content %}{% endblock %} - </div> - - <footer> - <p class="muted"> - <small> - Powered by - <a href="http://kegbot.org/?utm_source=kbserver">Kegbot™</a>{% if user.is_staff %}, version {{ VERSION }}{% endif %} - — - © 2003-2020 Kegbot Project contributors - <span id="page-settings"></span> - </small> - </p> - </footer> - </div> <!-- /container --> -{% endblock body %} diff --git a/pykeg/web/templates/index.html b/pykeg/web/templates/index.html deleted file mode 100644 index a0b2e5e04..000000000 --- a/pykeg/web/templates/index.html +++ /dev/null @@ -1,74 +0,0 @@ -{% extends "page-twocol.html" %} -{% load kegweblib %} - -{# Suppress page title on homepage. #} -{% block header-margin %}{% endblock %} - -{% block col-1 %} - -{% block messages %} -{% for message in messages %} - <div class="alert {% for tag in message.tags.split %}alert-{{tag}} {% endfor %}"> - <a class="close" data-dismiss="alert" href="#">×</a> - <p> - {{message}} - </p> - </div> -{% endfor %} -{% endblock messages %} - -{% if not current_session and not most_recent_session %} -<h3 class="muted">Fresh Kegbot</h3> -<p>This will be more interesting once some activity is recorded.</p> - -{% else %} - -{% if current_session %} - <h3 class="muted"> - Now Drinking - <small>(<a href="{{ current_session.get_absolute_url}}">details</a>)</small> - </h3> -{% else %} - <h3 class="muted"> - Recent Activity - <small>(last session ended <a href="{{ most_recent_session.get_absolute_url}}"> - {% timeago most_recent_session.end_time %}</a>)</small> - </h3> -{% endif %} - -{% include 'kegweb/includes/timeline.html' %} - -<div class="well"> - See <a href="{% url "kb-sessions" %}">all sessions</a>. -</div> - -{% endif %} - -{% endblock col-1 %} - - -{% block col-2 %} - -<h3 class="muted">Currently On Tap</h3> - -{% if taps %} - -{% for tap in taps %} -{% include 'kegweb/includes/tap_snapshot.html' %} -{% endfor %} - -<p class="muted"> - See all taps in <a href="{% url 'kb-fullscreen' %}">fullscreen mode</a>. -</p> - -{% else %} -<p class="muted"> - No taps are configured. - {% if user.is_staff %} - (Want to <a href="{% url "kegadmin-taps" %}">manage taps</a>?) - {% endif %} -</p> - -{% endif %} - -{% endblock col-2 %} diff --git a/pykeg/web/templates/page-twocol.html b/pykeg/web/templates/page-twocol.html deleted file mode 100644 index 4da47b8d9..000000000 --- a/pykeg/web/templates/page-twocol.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% block content %} -<div class="row"> - <div class="span8"> <!-- left col --> - {% block col-1 %}{% endblock %} - </div> <!-- end center col --> - <div class="span4"> <!-- right col --> - {% block col-2 %}{% endblock %} - </div> <!-- end right col --> -</div> -{% endblock %} diff --git a/pykeg/web/templates/registration/activation_complete.html b/pykeg/web/templates/registration/activation_complete.html deleted file mode 100644 index 4389ed2a8..000000000 --- a/pykeg/web/templates/registration/activation_complete.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Activation Complete{% endblock %} -{% block pagetitle %}Activation Complete{% endblock %} - -{% block content %} -<p> - You may now <a href="{% url "auth_login" %}">log in</a>. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/activation_email.txt b/pykeg/web/templates/registration/activation_email.txt deleted file mode 100644 index 3dc1f753c..000000000 --- a/pykeg/web/templates/registration/activation_email.txt +++ /dev/null @@ -1,8 +0,0 @@ -Hi there! - -Someone, hopefully you, just registered for an account on {{ site_name }}. -To activate your account, please click on the following link: - - {{ base_url }}{% url 'registration_activate' activation_key %} - -This link will expire after {{ expiration_days }} days. \ No newline at end of file diff --git a/pykeg/web/templates/registration/activation_email_subject.txt b/pykeg/web/templates/registration/activation_email_subject.txt deleted file mode 100644 index f87b7d318..000000000 --- a/pykeg/web/templates/registration/activation_email_subject.txt +++ /dev/null @@ -1 +0,0 @@ -[{{ site_name }}] Activate your account \ No newline at end of file diff --git a/pykeg/web/templates/registration/invitation_expired.html b/pykeg/web/templates/registration/invitation_expired.html deleted file mode 100644 index 5f55a2c02..000000000 --- a/pykeg/web/templates/registration/invitation_expired.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Invitation Expired{% endblock %} -{% block pagetitle %}Invitation Expired{% endblock %} - -{% block content %} -<p> - Sorry, your invitation code has expired or is not valid. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/invitation_required.html b/pykeg/web/templates/registration/invitation_required.html deleted file mode 100644 index f19003d92..000000000 --- a/pykeg/web/templates/registration/invitation_required.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Invitation Required{% endblock %} -{% block pagetitle %}Invitation Required{% endblock %} - -{% block content %} -<p> - Sorry, you need an invitation in order to register on this system. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/logged_out.html b/pykeg/web/templates/registration/logged_out.html deleted file mode 100644 index 65cf0ff98..000000000 --- a/pykeg/web/templates/registration/logged_out.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Logged Out{% endblock %} -{% block pagetitle %}Logged Out{% endblock %} - -{% block content %} - You have been logged out. So long! -{% endblock %} diff --git a/pykeg/web/templates/registration/login.html b/pykeg/web/templates/registration/login.html deleted file mode 100644 index 38db84c31..000000000 --- a/pykeg/web/templates/registration/login.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "page-twocol.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Log In to {{kbsite.title}}{% endblock %} -{% block pagetitle %}Log In{% endblock %} - -{% block col-1 %} -<div class="well"> - <form action="" method="POST"> - {% csrf_token %} - {{ form|crispy }} - <button type="submit" class="btn btn-success small" - name="tweet-form-submit">Log In</button> -<p> - <span style="font-size:0.8em;"> - (did you <a href="{% url "password_reset" %}">forget your username or password?</a>) - </span> -</p> - -{% endblock col-1 %} \ No newline at end of file diff --git a/pykeg/web/templates/registration/password_change_done.html b/pykeg/web/templates/registration/password_change_done.html deleted file mode 100644 index 97148ba47..000000000 --- a/pykeg/web/templates/registration/password_change_done.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Account: Change Password | {{ block.super }}{% endblock %} -{% block pagetitle %}Account: Change Password{% endblock %} - -{% block kb-account-main %} -<h2>Password Changed</h2> -<div class="well"> - <p>Your password has been changed.</p> -</div> - -{% endblock %} diff --git a/pykeg/web/templates/registration/password_change_form.html b/pykeg/web/templates/registration/password_change_form.html deleted file mode 100644 index f1bce4dd5..000000000 --- a/pykeg/web/templates/registration/password_change_form.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "account/base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Account: Change Password | {{ block.super }}{% endblock %} -{% block pagetitle %}Account: Change Password{% endblock %} - -{% block kb-account-main %} -<h2>Change Password</h2> -<div class="well"> - <form method="post" action=""> - {% csrf_token %} - {{ form|crispy }} - <div class="actions"> - <button type="submit" class="btn btn-primary">Save Changes</button> - </div> - </form> -</div> - -{% endblock %} diff --git a/pykeg/web/templates/registration/password_reset_complete.html b/pykeg/web/templates/registration/password_reset_complete.html deleted file mode 100644 index c61c2d761..000000000 --- a/pykeg/web/templates/registration/password_reset_complete.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib crispy_forms_tags %} - -{% block title %}Reset Password{% endblock %} - -{% block content %} -<h2>Reset Complete</h2> -<p> - Your account has been updated! -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/password_reset_confirm.html b/pykeg/web/templates/registration/password_reset_confirm.html deleted file mode 100644 index 8b3ed8b22..000000000 --- a/pykeg/web/templates/registration/password_reset_confirm.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib crispy_forms_tags %} - -{% block title %}Reset Password{% endblock %} - -{% block content %} -<h2>Select New Password</h2> -<form action="" method="POST">{% csrf_token %} - {{ form.as_p }} - <input type="submit" class="btn btn-success" name="submit" value="Change Password" /> -</form> -{% endblock %} - diff --git a/pykeg/web/templates/registration/password_reset_done.html b/pykeg/web/templates/registration/password_reset_done.html deleted file mode 100644 index 6f3fda14f..000000000 --- a/pykeg/web/templates/registration/password_reset_done.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib crispy_forms_tags %} - -{% block title %}Reset Password{% endblock %} - -{% block content %} -<h2>E-Mail Sent</h2> -<p> - We've sent password reset instructions to the address provided. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/password_reset_form.html b/pykeg/web/templates/registration/password_reset_form.html deleted file mode 100644 index 4e41e9e1b..000000000 --- a/pykeg/web/templates/registration/password_reset_form.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib crispy_forms_tags %} - -{% block title %}Reset Password{% endblock %} - -{% block content %} -<h2>Reset Password</h2> -<form action="" method="POST">{% csrf_token %} - {{ form.as_p }} - <input type="submit" class="btn btn-success" name="submit" value="Send E-Mail" /> -</form> -{% endblock %} diff --git a/pykeg/web/templates/registration/registration_closed.html b/pykeg/web/templates/registration/registration_closed.html deleted file mode 100644 index a314c44fb..000000000 --- a/pykeg/web/templates/registration/registration_closed.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Registration Closed{% endblock %} -{% block pagetitle %}Registration Closed{% endblock %} - -{% block content %} -<p> - Sorry, registration is closed. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/registration_complete.html b/pykeg/web/templates/registration/registration_complete.html deleted file mode 100644 index f3115bbe0..000000000 --- a/pykeg/web/templates/registration/registration_complete.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} - -{% block title %}Confirm Your E-Mail Address{% endblock %} -{% block pagetitle %}Confirm Your E-Mail Address{% endblock %} - -{% block content %} -<p> - You're almost done! Please click the activation link in your e-mail - to continue. -</p> -{% endblock %} diff --git a/pykeg/web/templates/registration/registration_form.html b/pykeg/web/templates/registration/registration_form.html deleted file mode 100644 index 0951a49db..000000000 --- a/pykeg/web/templates/registration/registration_form.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} -{% load kegweblib %} -{% load crispy_forms_tags %} - -{% block title %}Register New Account{% endblock %} -{% block pagetitle %}Register New Account{% endblock %} - -{% block content %} -<div class="well"> - <form action="" method="POST"> - {% csrf_token %} - {{ form|crispy }} - <button type="submit" class="btn btn-success small">Register</button> - </form> -</div> - -{% endblock %} diff --git a/pykeg/web/templates/skel.html b/pykeg/web/templates/skel.html deleted file mode 100644 index 4065f9a84..000000000 --- a/pykeg/web/templates/skel.html +++ /dev/null @@ -1,157 +0,0 @@ -{# Bare-bones skeletal template. #} -{% load static humanize kegweblib %} -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<title>{% block title %}Kegbot{% endblock %} - - - -{% block css %} - - - -{% endblock %} - -{% block kb-extracss %}{% endblock %} - - - -{% block body %} -{% endblock body %} - -{% block js %} - - - - - - - - - - - - -{% if GOOGLE_ANALYTICS_ID %} - -{% endif %} - - - -{% if kbsite and kbsite.is_setup and user.is_staff %} -{% include 'kegadmin/includes/extrajs.html' %} -{% endif %} - -{% endblock js %} - -{% block kb-extrajs %}{%endblock%} - diff --git a/pykeg/web/templates/spa/index.html b/pykeg/web/templates/spa/index.html new file mode 100644 index 000000000..237c2bf12 --- /dev/null +++ b/pykeg/web/templates/spa/index.html @@ -0,0 +1,13 @@ +{% load static %} + + + + + Kegbot + {% for css in entry_css %}{% endfor %} + + + +
    + + diff --git a/pykeg/web/urls.py b/pykeg/web/urls.py index 3a06006cf..1ccc6e80c 100644 --- a/pykeg/web/urls.py +++ b/pykeg/web/urls.py @@ -1,31 +1,19 @@ from django.conf import settings from django.conf.urls.static import static from django.contrib import admin -from django.urls import include, path +from django.urls import include, path, re_path from pykeg.api import urls as api_urls -from pykeg.web.account import urls as account_urls +from pykeg.web import spa from pykeg.web.api import urls as legacy_api_urls -from pykeg.web.kbregistration import urls as kbregistration_urls -from pykeg.web.kegadmin import urls as kegadmin_urls -from pykeg.web.kegweb import urls as kegweb_urls -from pykeg.web.setup_wizard import urls as setup_wizard_urls urlpatterns = [ # The deprecated legacy api is served only at api/v1/; everything else # under api/ is the current api. path("api/v1/", include(legacy_api_urls)), path("api/", include(api_urls)), - path("account/", include(account_urls)), - path("accounts/", include(kbregistration_urls)), - path("kegadmin/", include(kegadmin_urls)), ] -if "pykeg.web.setup_wizard" in settings.INSTALLED_APPS: - urlpatterns += [ - path("setup/", include(setup_wizard_urls)), - ] - if settings.DEBUG: urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) @@ -38,7 +26,34 @@ path("admin/", admin.site.urls), ] -# main kegweb urls +# Named SPA routes: these exist so server-side code that builds URLs +# (get_absolute_url, e-mail links) keeps working; the SPA handles the +# actual routing client-side. +urlpatterns += [ + path("", spa.spa_index, name="kb-home"), + path("kegs//", spa.spa_index, name="kb-keg"), + path("drinks//", spa.spa_index, name="kb-drink"), + path("d//", spa.spa_index, name="kb-drink-short"), + path("s//", spa.spa_index, name="kb-session-short"), + path( + "sessions/////", + spa.spa_index, + name="kb-session-detail", + ), + path("drinkers//", spa.spa_index, name="kb-drinker"), + path("account/", spa.spa_index, name="kb-account-main"), + path("account/confirm-email/", spa.spa_index, name="account-confirm-email"), + path("account/activate//", spa.spa_index, name="activate-account"), + path("accounts/register/", spa.spa_index, name="registration_register"), + path( + "accounts/password/reset/confirm/-/", + spa.spa_index, + name="password_reset_confirm", + ), +] + +# Everything else (except API, asset, and admin paths, whose 404s stay +# real 404s) is SPA territory. urlpatterns += [ - path("", include(kegweb_urls)), + re_path(r"^(?!api(?:$|/)|media/|static/|admin(?:$|/)).*$", spa.spa_index, name="spa-index"), ] diff --git a/pyproject.toml b/pyproject.toml index 7b10ee324..dd95439dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,12 +10,9 @@ dependencies = [ "coloredlogs", "dj-database-url", "dj-email-url", - "django-cors-headers>=4.9,<5", - "django-crispy-forms>=2.4,<3", - "crispy-bootstrap4>=2024.10", + "django-filter>=25.1", "django-imagekit", "django-redis>=5.4", - "django-registration>=5.1", "django-rq>=3,<5", "djangorestframework>=3.16,<4", "drf-spectacular>=0.28", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..135ff188d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "isolatedModules": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["web-ui/*"] + } + }, + "include": ["web-ui", "vite.config.ts", "openapi-ts.config.ts"] +} diff --git a/uv.lock b/uv.lock index 23ef5c6f1..780445520 100644 --- a/uv.lock +++ b/uv.lock @@ -170,28 +170,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, ] -[[package]] -name = "confusable-homoglyphs" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/10/1358fca1ee2d97d4f2877df9ffbe6d124da666fef3b2f75e771a4c1afee6/confusable_homoglyphs-3.3.1.tar.gz", hash = "sha256:b995001c9b2e1b4cea0cf5f3840a7c79188a8cbbad053d693572bd8c1c1ec460", size = 325480, upload-time = "2024-01-30T10:10:27.47Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/6e/c0fcbb7d341a46cf4241a6aa9e6a737734f0657521fc1bcd074953fe4eea/confusable_homoglyphs-3.3.1-py2.py3-none-any.whl", hash = "sha256:84c92cb79dc7f55aa290d0762b2349abd8dee4c16fbe6f99eac978d394e2e6a1", size = 144755, upload-time = "2024-01-30T10:10:24.857Z" }, -] - -[[package]] -name = "crispy-bootstrap4" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django" }, - { name = "django-crispy-forms" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/cc/638d36595da9fbb2c9d0be98bf6007442a65a521b614cf4645d04311b061/crispy_bootstrap4-2026.2.tar.gz", hash = "sha256:66f8f14bf9c2c16ed94243236ed253a94e5a625afa1ee64022ce29db98c6cd85", size = 34645, upload-time = "2026-02-11T22:45:05.422Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/6d/b90d601ea2449cc6b35b4b08be90fb1f6ca1baf2be383ed195f7bfa91a32/crispy_bootstrap4-2026.2-py3-none-any.whl", hash = "sha256:4b2b99dfe3e3cacb548702159462110901bd38792b650b770e50c62284ac2227", size = 23178, upload-time = "2026-02-11T22:45:04.108Z" }, -] - [[package]] name = "croniter" version = "6.2.4" @@ -262,28 +240,15 @@ wheels = [ ] [[package]] -name = "django-cors-headers" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asgiref" }, - { name = "django" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/39/55822b15b7ec87410f34cd16ce04065ff390e50f9e29f31d6d116fc80456/django_cors_headers-4.9.0.tar.gz", hash = "sha256:fe5d7cb59fdc2c8c646ce84b727ac2bca8912a247e6e68e1fb507372178e59e8", size = 21458, upload-time = "2025-09-18T10:40:52.326Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/d8/19ed1e47badf477d17fb177c1c19b5a21da0fd2d9f093f23be3fb86c5fab/django_cors_headers-4.9.0-py3-none-any.whl", hash = "sha256:15c7f20727f90044dcee2216a9fd7303741a864865f0c3657e28b7056f61b449", size = 12809, upload-time = "2025-09-18T10:40:50.843Z" }, -] - -[[package]] -name = "django-crispy-forms" -version = "2.7" +name = "django-filter" +version = "26.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/f5/b79e3ed7cae871d5d71bf3448d627e1fabab769358bb90808bec056556fe/django_crispy_forms-2.7.tar.gz", hash = "sha256:4c59bed60417375cba26cebb2c67ab350b655934670270b1c89dbcd7e60f1b4c", size = 1097842, upload-time = "2026-07-29T11:12:12.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/3e/563965173d4cbb5fc308087e7b3d11a115b7b67273d093622480b1e31f78/django_filter-26.1.tar.gz", hash = "sha256:66ea04031b068c77c86e1ac26ced7a3f8f13ce797f5795751707e3deefc58054", size = 144299, upload-time = "2026-07-11T09:27:02.767Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/44/50335d09c4deb70affee0c54feef07ba458df015e32e45490615d9dc5b6b/django_crispy_forms-2.7-py3-none-any.whl", hash = "sha256:42a7ecb05ac3fd050d006dfe7aeceb7f318c30e5b5124ff619e2be252f36f096", size = 31478, upload-time = "2026-07-29T11:12:11.052Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/afffed1e3c4540fb75bf550a18b6176a9f6371b5f3e52b69a28995b6480c/django_filter-26.1-py3-none-any.whl", hash = "sha256:7d98ef2899218e6242619b532cb1b95af14e09dfcf74844aecb550ad27b59ff2", size = 94069, upload-time = "2026-07-11T09:27:01.012Z" }, ] [[package]] @@ -312,19 +277,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/f1/63caad7c9222c26a62082f4f777de26389233b7574629996098bf6d25a4d/django_redis-5.4.0-py3-none-any.whl", hash = "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b", size = 31119, upload-time = "2023-10-01T20:21:33.009Z" }, ] -[[package]] -name = "django-registration" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "confusable-homoglyphs" }, - { name = "django" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9a/a0/f6e6d0a59b94eb4ab14983853334cb25401d677c5a3799aaca6819acf2eb/django_registration-5.2.1.tar.gz", hash = "sha256:06864f9da0bc4d7b073fb2da98d95d12428357ab0bc46e33f79c26707ec06bbe", size = 94187, upload-time = "2025-04-07T05:54:48.558Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/5d/aa2f82b3c809db66eb986963db1d4b89de190b4eac2e3021a725112e3b13/django_registration-5.2.1-py3-none-any.whl", hash = "sha256:7079e2364b15fc6bef18b81ae94c5e947b556abdbbfb19c9bcca14feafde6a3d", size = 105277, upload-time = "2025-04-07T05:54:46.981Z" }, -] - [[package]] name = "django-rq" version = "4.1.1" @@ -560,15 +512,12 @@ source = { editable = "." } dependencies = [ { name = "addict" }, { name = "coloredlogs" }, - { name = "crispy-bootstrap4" }, { name = "dj-database-url" }, { name = "dj-email-url" }, { name = "django" }, - { name = "django-cors-headers" }, - { name = "django-crispy-forms" }, + { name = "django-filter" }, { name = "django-imagekit" }, { name = "django-redis" }, - { name = "django-registration" }, { name = "django-rq" }, { name = "djangorestframework" }, { name = "drf-spectacular" }, @@ -608,15 +557,12 @@ docs = [ requires-dist = [ { name = "addict" }, { name = "coloredlogs" }, - { name = "crispy-bootstrap4", specifier = ">=2024.10" }, { name = "dj-database-url" }, { name = "dj-email-url" }, { name = "django", specifier = ">=5.2,<5.3" }, - { name = "django-cors-headers", specifier = ">=4.9,<5" }, - { name = "django-crispy-forms", specifier = ">=2.4,<3" }, + { name = "django-filter", specifier = ">=25.1" }, { name = "django-imagekit" }, { name = "django-redis", specifier = ">=5.4" }, - { name = "django-registration", specifier = ">=5.1" }, { name = "django-rq", specifier = ">=3,<5" }, { name = "djangorestframework", specifier = ">=3.16,<4" }, { name = "drf-spectacular", specifier = ">=0.28" }, diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 000000000..1baa0f9bd --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,42 @@ +import { fileURLToPath } from "node:url"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +// All frontend source lives in web-ui/; tooling configs live at the repo +// root. In development the vite server is the only origin the browser +// talks to — http://localhost:8000 "just works": backend paths are +// proxied to Django (which `kegbot runserver` starts on 8001), so +// requests stay same-origin (no CSRF/CORS special-casing) and hot +// reload just works. +const DJANGO = "http://localhost:8001"; + +export default defineConfig(({ command }) => ({ + root: "web-ui", + // Production assets are collected into Django's static tree and served + // by WhiteNoise under /static/; the dev server serves from the root. + base: command === "build" ? "/static/" : "/", + plugins: [react()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./web-ui", import.meta.url)), + }, + }, + server: { + port: 8000, + // Segment-anchored regexes: a bare "/api" key is a prefix match and + // would also capture source modules under /api-client/. + // + // changeOrigin must stay off (string shorthands turn it on): Django + // must see the browser's Host so its CSRF origin check passes. + proxy: { + "^/api(?:/|$)": { target: DJANGO, changeOrigin: false }, + "^/media(?:/|$)": { target: DJANGO, changeOrigin: false }, + "^/static(?:/|$)": { target: DJANGO, changeOrigin: false }, + }, + }, + build: { + outDir: "dist", + emptyOutDir: true, + manifest: true, + }, +})); diff --git a/web-ui/api-client/client.gen.ts b/web-ui/api-client/client.gen.ts new file mode 100644 index 000000000..163da4e54 --- /dev/null +++ b/web-ui/api-client/client.gen.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { ClientOptions } from './types.gen'; +import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client'; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = (override?: Config) => Config & T>; + +export const client = createClient(createConfig()); \ No newline at end of file diff --git a/web-ui/api-client/client/client.gen.ts b/web-ui/api-client/client/client.gen.ts new file mode 100644 index 000000000..0c606b81c --- /dev/null +++ b/web-ui/api-client/client/client.gen.ts @@ -0,0 +1,199 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Client, Config, ResolvedRequestOptions } from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); + + const request: Client['request'] = async (options) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.serializedBody === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const url = buildUrl(opts); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: opts.serializedBody, + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request._fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response = await _fetch(request); + + for (const fn of interceptors.response._fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + return opts.responseStyle === 'data' + ? {} + : { + data: {}, + ...result, + }; + } + + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error._fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + return { + buildUrl, + connect: (options) => request({ ...options, method: 'CONNECT' }), + delete: (options) => request({ ...options, method: 'DELETE' }), + get: (options) => request({ ...options, method: 'GET' }), + getConfig, + head: (options) => request({ ...options, method: 'HEAD' }), + interceptors, + options: (options) => request({ ...options, method: 'OPTIONS' }), + patch: (options) => request({ ...options, method: 'PATCH' }), + post: (options) => request({ ...options, method: 'POST' }), + put: (options) => request({ ...options, method: 'PUT' }), + request, + setConfig, + trace: (options) => request({ ...options, method: 'TRACE' }), + }; +}; diff --git a/web-ui/api-client/client/index.ts b/web-ui/api-client/client/index.ts new file mode 100644 index 000000000..318a84b6a --- /dev/null +++ b/web-ui/api-client/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + OptionsLegacyParser, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/web-ui/api-client/client/types.gen.ts b/web-ui/api-client/client/types.gen.ts new file mode 100644 index 000000000..2a123be9a --- /dev/null +++ b/web-ui/api-client/client/types.gen.ts @@ -0,0 +1,232 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }> { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick>, 'method'>, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: Pick & Options, +) => string; + +export type Client = CoreClient & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + Omit; + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'body' | 'headers' | 'url' + > & + TData + : OmitKeys, 'body' | 'url'> & + TData & + Pick, 'headers'> + : TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'headers' | 'url' + > & + TData & + Pick, 'body'> + : OmitKeys, 'url'> & TData; diff --git a/web-ui/api-client/client/utils.gen.ts b/web-ui/api-client/client/utils.gen.ts new file mode 100644 index 000000000..1ee09c6db --- /dev/null +++ b/web-ui/api-client/client/utils.gen.ts @@ -0,0 +1,440 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { + QuerySerializer, + QuerySerializerOptions, +} from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +interface PathSerializer { + path: Record; + url: string; +} + +const PATH_PARAM_RE = /\{[^{}]+\}/g; + +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +type ArraySeparatorStyle = ArrayStyle | MatrixStyle; + +const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const createQuerySerializer = ({ + allowReserved, + array, + object, +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: 'form', + value, + ...array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => { + const url = getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header || typeof header !== 'object') { + continue; + } + + const iterator = + header instanceof Headers ? header.entries() : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + _fns: (Interceptor | null)[]; + + constructor() { + this._fns = []; + } + + clear() { + this._fns = []; + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this._fns[id] ? id : -1; + } else { + return this._fns.indexOf(id); + } + } + exists(id: number | Interceptor) { + const index = this.getInterceptorIndex(id); + return !!this._fns[index]; + } + + eject(id: number | Interceptor) { + const index = this.getInterceptorIndex(id); + if (this._fns[index]) { + this._fns[index] = null; + } + } + + update(id: number | Interceptor, fn: Interceptor) { + const index = this.getInterceptorIndex(id); + if (this._fns[index]) { + this._fns[index] = fn; + return id; + } else { + return false; + } + } + + use(fn: Interceptor) { + this._fns = [...this._fns, fn]; + return this._fns.length - 1; + } +} + +// `createInterceptors()` response, meant for external use as it does not +// expose internals +export interface Middleware { + error: Pick< + Interceptors>, + 'eject' | 'use' + >; + request: Pick>, 'eject' | 'use'>; + response: Pick< + Interceptors>, + 'eject' | 'use' + >; +} + +// do not add `Middleware` as return type so we can use _fns internally +export const createInterceptors = () => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/web-ui/api-client/core/auth.gen.ts b/web-ui/api-client/core/auth.gen.ts new file mode 100644 index 000000000..f8a73266f --- /dev/null +++ b/web-ui/api-client/core/auth.gen.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/web-ui/api-client/core/bodySerializer.gen.ts b/web-ui/api-client/core/bodySerializer.gen.ts new file mode 100644 index 000000000..49cd8925e --- /dev/null +++ b/web-ui/api-client/core/bodySerializer.gen.ts @@ -0,0 +1,92 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +export interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/web-ui/api-client/core/params.gen.ts b/web-ui/api-client/core/params.gen.ts new file mode 100644 index 000000000..71c88e852 --- /dev/null +++ b/web-ui/api-client/core/params.gen.ts @@ -0,0 +1,153 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + { + in: Slot; + map?: string; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + (params[field.in] as Record)[name] = arg; + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else { + for (const [slot, allowed] of Object.entries( + config.allowExtra ?? {}, + )) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/web-ui/api-client/core/pathSerializer.gen.ts b/web-ui/api-client/core/pathSerializer.gen.ts new file mode 100644 index 000000000..8d9993104 --- /dev/null +++ b/web-ui/api-client/core/pathSerializer.gen.ts @@ -0,0 +1,181 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/web-ui/api-client/core/types.gen.ts b/web-ui/api-client/core/types.gen.ts new file mode 100644 index 000000000..5bfae35c0 --- /dev/null +++ b/web-ui/api-client/core/types.gen.ts @@ -0,0 +1,120 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; + +export interface Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, +> { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + connect: MethodFn; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; + trace: MethodFn; +} + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: + | 'CONNECT' + | 'DELETE' + | 'GET' + | 'HEAD' + | 'OPTIONS' + | 'PATCH' + | 'POST' + | 'PUT' + | 'TRACE'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; +}; diff --git a/web-ui/api-client/index.ts b/web-ui/api-client/index.ts new file mode 100644 index 000000000..e64537d21 --- /dev/null +++ b/web-ui/api-client/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './types.gen'; +export * from './sdk.gen'; \ No newline at end of file diff --git a/web-ui/api-client/sdk.gen.ts b/web-ui/api-client/sdk.gen.ts new file mode 100644 index 000000000..c2380cec8 --- /dev/null +++ b/web-ui/api-client/sdk.gen.ts @@ -0,0 +1,3539 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer } from './client'; +import type { AccountActivateCreateData, AccountActivateCreateResponses, AccountConfirmEmailCreateData, AccountConfirmEmailCreateResponses, AccountEmailCreateData, AccountEmailCreateResponses, AccountMugshotCreateData, AccountMugshotCreateResponses, AccountPasswordCreateData, AccountPasswordCreateResponses, AccountRegenerateApiKeyCreateData, AccountRegenerateApiKeyCreateResponses, AdminBackupsRetrieveData, AdminBackupsRetrieveResponses, AdminBackupsCreateData, AdminBackupsCreateResponses, AdminBackupsDestroyData, AdminBackupsDestroyResponses, AdminBugreportRetrieveData, AdminBugreportRetrieveResponses, AdminDashboardRetrieveData, AdminDashboardRetrieveResponses, AdminEmailTestCreateData, AdminEmailTestCreateResponses, AdminLogsRetrieveData, AdminLogsRetrieveResponses, AdminPluginsRetrieveData, AdminPluginsRetrieveResponses, AdminPluginsSettingsRetrieveData, AdminPluginsSettingsRetrieveResponses, AdminPluginsSettingsUpdateData, AdminPluginsSettingsUpdateResponses, ApiKeysListData, ApiKeysListResponses, ApiKeysCreateData, ApiKeysCreateResponses, ApiKeysDestroyData, ApiKeysDestroyResponses, ApiKeysRetrieveData, ApiKeysRetrieveResponses, ApiKeysPartialUpdateData, ApiKeysPartialUpdateResponses, ApiKeysUpdateData, ApiKeysUpdateResponses, AuthTokensListData, AuthTokensListResponses, AuthTokensCreateData, AuthTokensCreateResponses, AuthTokensDestroyData, AuthTokensDestroyResponses, AuthTokensRetrieveData, AuthTokensRetrieveResponses, AuthTokensPartialUpdateData, AuthTokensPartialUpdateResponses, AuthTokensUpdateData, AuthTokensUpdateResponses, AuthLoginCreateData, AuthLoginCreateResponses, AuthLogoutCreateData, AuthLogoutCreateResponses, AuthPasswordResetCreateData, AuthPasswordResetCreateResponses, AuthPasswordResetConfirmCreateData, AuthPasswordResetConfirmCreateResponses, AuthRegisterCreateData, AuthRegisterCreateResponses, BeverageProducersListData, BeverageProducersListResponses, BeverageProducersCreateData, BeverageProducersCreateResponses, BeverageProducersDestroyData, BeverageProducersDestroyResponses, BeverageProducersRetrieveData, BeverageProducersRetrieveResponses, BeverageProducersPartialUpdateData, BeverageProducersPartialUpdateResponses, BeverageProducersUpdateData, BeverageProducersUpdateResponses, BeverageProducersPictureCreateData, BeverageProducersPictureCreateResponses, BeveragesListData, BeveragesListResponses, BeveragesCreateData, BeveragesCreateResponses, BeveragesDestroyData, BeveragesDestroyResponses, BeveragesRetrieveData, BeveragesRetrieveResponses, BeveragesPartialUpdateData, BeveragesPartialUpdateResponses, BeveragesUpdateData, BeveragesUpdateResponses, BeveragesPictureCreateData, BeveragesPictureCreateResponses, ControllersListData, ControllersListResponses, ControllersCreateData, ControllersCreateResponses, ControllersDestroyData, ControllersDestroyResponses, ControllersRetrieveData, ControllersRetrieveResponses, ControllersPartialUpdateData, ControllersPartialUpdateResponses, ControllersUpdateData, ControllersUpdateResponses, DevicesListData, DevicesListResponses, DevicesCreateData, DevicesCreateResponses, DevicesDestroyData, DevicesDestroyResponses, DevicesRetrieveData, DevicesRetrieveResponses, DevicesPartialUpdateData, DevicesPartialUpdateResponses, DevicesUpdateData, DevicesUpdateResponses, DrinksListData, DrinksListResponses, DrinksDestroyData, DrinksDestroyResponses, DrinksRetrieveData, DrinksRetrieveResponses, DrinksPartialUpdateData, DrinksPartialUpdateResponses, DrinksPictureDestroyData, DrinksPictureDestroyResponses, DrinksPictureCreateData, DrinksPictureCreateResponses, DrinksReassignCreateData, DrinksReassignCreateResponses, EventsListData, EventsListResponses, EventsRetrieveData, EventsRetrieveResponses, FlowMetersListData, FlowMetersListResponses, FlowMetersCreateData, FlowMetersCreateResponses, FlowMetersDestroyData, FlowMetersDestroyResponses, FlowMetersRetrieveData, FlowMetersRetrieveResponses, FlowMetersPartialUpdateData, FlowMetersPartialUpdateResponses, FlowMetersUpdateData, FlowMetersUpdateResponses, FlowTogglesListData, FlowTogglesListResponses, FlowTogglesCreateData, FlowTogglesCreateResponses, FlowTogglesDestroyData, FlowTogglesDestroyResponses, FlowTogglesRetrieveData, FlowTogglesRetrieveResponses, FlowTogglesPartialUpdateData, FlowTogglesPartialUpdateResponses, FlowTogglesUpdateData, FlowTogglesUpdateResponses, InvitationsListData, InvitationsListResponses, InvitationsCreateData, InvitationsCreateResponses, InvitationsDestroyData, InvitationsDestroyResponses, InvitationsRetrieveData, InvitationsRetrieveResponses, KegsListData, KegsListResponses, KegsCreateData, KegsCreateResponses, KegsDestroyData, KegsDestroyResponses, KegsRetrieveData, KegsRetrieveResponses, KegsPartialUpdateData, KegsPartialUpdateResponses, KegsUpdateData, KegsUpdateResponses, KegsEndCreateData, KegsEndCreateResponses, KegsReactivateCreateData, KegsReactivateCreateResponses, KegsSpillCreateData, KegsSpillCreateResponses, KegsStatsRetrieveData, KegsStatsRetrieveResponses, NotificationSettingsListData, NotificationSettingsListResponses, NotificationSettingsCreateData, NotificationSettingsCreateResponses, NotificationSettingsDestroyData, NotificationSettingsDestroyResponses, NotificationSettingsRetrieveData, NotificationSettingsRetrieveResponses, NotificationSettingsPartialUpdateData, NotificationSettingsPartialUpdateResponses, NotificationSettingsUpdateData, NotificationSettingsUpdateResponses, PluginDataListData, PluginDataListResponses, PluginDataCreateData, PluginDataCreateResponses, PluginDataDestroyData, PluginDataDestroyResponses, PluginDataRetrieveData, PluginDataRetrieveResponses, PluginDataPartialUpdateData, PluginDataPartialUpdateResponses, PluginDataUpdateData, PluginDataUpdateResponses, SessionsListData, SessionsListResponses, SessionsRetrieveData, SessionsRetrieveResponses, SessionsStatsRetrieveData, SessionsStatsRetrieveResponses, SessionsCurrentRetrieveData, SessionsCurrentRetrieveResponses, SessionsDirectoryRetrieveData, SessionsDirectoryRetrieveResponses, SetupAdminUserCreateData, SetupAdminUserCreateResponses, SetupFinishCreateData, SetupFinishCreateResponses, SetupMigrateCreateData, SetupMigrateCreateResponses, SetupSettingsCreateData, SetupSettingsCreateResponses, SetupStatusRetrieveData, SetupStatusRetrieveResponses, SetupUpgradeCreateData, SetupUpgradeCreateResponses, SiteRetrieveData, SiteRetrieveResponses, SitePartialUpdateData, SitePartialUpdateResponses, SiteBackgroundImageCreateData, SiteBackgroundImageCreateResponses, StatsListData, StatsListResponses, StatsRetrieveData, StatsRetrieveResponses, StatsSystemRetrieveData, StatsSystemRetrieveResponses, StatusRetrieveData, StatusRetrieveResponses, TapsListData, TapsListResponses, TapsCreateData, TapsCreateResponses, TapsDestroyData, TapsDestroyResponses, TapsRetrieveData, TapsRetrieveResponses, TapsPartialUpdateData, TapsPartialUpdateResponses, TapsUpdateData, TapsUpdateResponses, TapsAttachKegCreateData, TapsAttachKegCreateResponses, TapsConnectMeterCreateData, TapsConnectMeterCreateResponses, TapsConnectThermoCreateData, TapsConnectThermoCreateResponses, TapsConnectToggleCreateData, TapsConnectToggleCreateResponses, TapsEndKegCreateData, TapsEndKegCreateResponses, TapsRecordDrinkCreateData, TapsRecordDrinkCreateResponses, TapsStartKegCreateData, TapsStartKegCreateResponses, ThermoLogsListData, ThermoLogsListResponses, ThermoLogsRetrieveData, ThermoLogsRetrieveResponses, ThermoSensorsListData, ThermoSensorsListResponses, ThermoSensorsCreateData, ThermoSensorsCreateResponses, ThermoSensorsDestroyData, ThermoSensorsDestroyResponses, ThermoSensorsRetrieveData, ThermoSensorsRetrieveResponses, ThermoSensorsPartialUpdateData, ThermoSensorsPartialUpdateResponses, ThermoSensorsUpdateData, ThermoSensorsUpdateResponses, UsersListData, UsersListResponses, UsersCreateData, UsersCreateResponses, UsersRetrieveData, UsersRetrieveResponses, UsersPartialUpdateData, UsersPartialUpdateResponses, UsersSetPasswordCreateData, UsersSetPasswordCreateResponses, UsersStatsRetrieveData, UsersStatsRetrieveResponses, UsersMeRetrieveData, UsersMeRetrieveResponses, UsersMePartialUpdateData, UsersMePartialUpdateResponses } from './types.gen'; +import { client as _heyApiClient } from './client.gen'; + +export type Options = ClientOptions & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; +}; + +/** + * Activates an invited/created account: sets its password and logs in. + */ +export const accountActivateCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/account/activate', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Applies an email change, given the token from the confirmation mail. + */ +export const accountConfirmEmailCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/account/confirm-email', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Requests an email change; a confirmation link is mailed to the new address. + */ +export const accountEmailCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/account/email', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Sets the current user's mugshot. + */ +export const accountMugshotCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + ...formDataBodySerializer, + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/account/mugshot', + ...options, + headers: { + 'Content-Type': null, + ...options.headers + } + }); +}; + +/** + * Changes the current user's password, keeping the session valid. + */ +export const accountPasswordCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/account/password', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Discards and regenerates the current user's API key. + */ +export const accountRegenerateApiKeyCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/account/regenerate-api-key', + ...options + }); +}; + +/** + * Lists existing backups (GET) or starts building a new one (POST). + */ +export const adminBackupsRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/backups', + ...options + }); +}; + +/** + * Lists existing backups (GET) or starts building a new one (POST). + */ +export const adminBackupsCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/backups', + ...options + }); +}; + +/** + * Deletes a backup archive by filename. + */ +export const adminBackupsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/backups/{filename}', + ...options + }); +}; + +/** + * Generates and returns a bugreport (may contain secrets; admin eyes only). + */ +export const adminBugreportRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/bugreport', + ...options + }); +}; + +/** + * System health summary for the admin dashboard. + */ +export const adminDashboardRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/dashboard', + ...options + }); +}; + +/** + * Sends a test notification email to the given address. + */ +export const adminEmailTestCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/email-test', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Returns recent log records (newest first) from the redis log handler. + */ +export const adminLogsRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/logs', + ...options + }); +}; + +/** + * Lists installed plugins. + */ +export const adminPluginsRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/plugins', + ...options + }); +}; + +/** + * Reads or updates a plugin's site settings. + * + * Writes are validated through the plugin's own settings form; field + * errors come back in standard DRF shape. + */ +export const adminPluginsSettingsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/plugins/{short_name}/settings', + ...options + }); +}; + +/** + * Reads or updates a plugin's site settings. + * + * Writes are validated through the plugin's own settings form; field + * errors come back in standard DRF shape. + */ +export const adminPluginsSettingsUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/admin/plugins/{short_name}/settings', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys', + ...options + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys/{id}', + ...options + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys/{id}', + ...options + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists a user's own api keys. + */ +export const apiKeysUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/api-keys/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens', + ...options + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens/{id}', + ...options + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens/{id}', + ...options + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all AuthenticationTokens in the system. + */ +export const authTokensUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth-tokens/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +export const authLoginCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/auth/login', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +export const authLogoutCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/auth/logout', + ...options + }); +}; + +/** + * Mails a password-reset link. Always succeeds (no account enumeration). + */ +export const authPasswordResetCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/auth/password-reset', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Sets a new password, given the uid/token pair from a reset mail. + */ +export const authPasswordResetConfirmCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/auth/password-reset-confirm', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Registers a new account, honoring the site's registration mode. + */ +export const authRegisterCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/auth/register', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers', + ...options + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers/{id}', + ...options + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers/{id}', + ...options + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all beverage producers in the system. + */ +export const beverageProducersUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Uploads and sets this object's picture. + */ +export const beverageProducersPictureCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + ...formDataBodySerializer, + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverage-producers/{id}/picture', + ...options, + headers: { + 'Content-Type': null, + ...options.headers + } + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages', + ...options + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages/{id}', + ...options + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages/{id}', + ...options + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all beverages in the system. + */ +export const beveragesUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Uploads and sets this object's picture. + */ +export const beveragesPictureCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + ...formDataBodySerializer, + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/beverages/{id}/picture', + ...options, + headers: { + 'Content-Type': null, + ...options.headers + } + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers', + ...options + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers/{id}', + ...options + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers/{id}', + ...options + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all Controllers in the system. + */ +export const controllersUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/controllers/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices', + ...options + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices/{id}', + ...options + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices/{id}', + ...options + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all devices in the system. + * + * Admin-only view. + */ +export const devicesUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/devices/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all Drinks in the system. + * + * Drinks are created by pours (or the tap record-drink endpoint), never + * directly. The drink's owner may edit its shout and manage its picture; + * volume adjustment, reassignment, and deletion are admin operations. + */ +export const drinksList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks', + ...options + }); +}; + +/** + * Lists all Drinks in the system. + * + * Drinks are created by pours (or the tap record-drink endpoint), never + * directly. The drink's owner may edit its shout and manage its picture; + * volume adjustment, reassignment, and deletion are admin operations. + */ +export const drinksDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}', + ...options + }); +}; + +/** + * Lists all Drinks in the system. + * + * Drinks are created by pours (or the tap record-drink endpoint), never + * directly. The drink's owner may edit its shout and manage its picture; + * volume adjustment, reassignment, and deletion are admin operations. + */ +export const drinksRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}', + ...options + }); +}; + +/** + * Lists all Drinks in the system. + * + * Drinks are created by pours (or the tap record-drink endpoint), never + * directly. The drink's owner may edit its shout and manage its picture; + * volume adjustment, reassignment, and deletion are admin operations. + */ +export const drinksPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Attaches (POST) or erases (DELETE) this drink's picture. + */ +export const drinksPictureDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}/picture', + ...options + }); +}; + +/** + * Attaches (POST) or erases (DELETE) this drink's picture. + */ +export const drinksPictureCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + ...formDataBodySerializer, + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}/picture', + ...options, + headers: { + 'Content-Type': null, + ...options.headers + } + }); +}; + +/** + * Reassigns this drink to another user. + */ +export const drinksReassignCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/drinks/{id}/reassign', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all SystemEvents in the system. + */ +export const eventsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/events', + ...options + }); +}; + +/** + * Lists all SystemEvents in the system. + */ +export const eventsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/events/{id}', + ...options + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters', + ...options + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters/{id}', + ...options + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters/{id}', + ...options + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all FlowMeters in the system. + */ +export const flowMetersUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-meters/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles', + ...options + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles/{id}', + ...options + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles/{id}', + ...options + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all FlowToggles in the system. + */ +export const flowTogglesUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/flow-toggles/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all of the *current user's* invitations. + * + * Creating an invitation (when the site's registration mode allows the + * caller to invite) also sends the invitation e-mail. + */ +export const invitationsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/invitations', + ...options + }); +}; + +/** + * Lists all of the *current user's* invitations. + * + * Creating an invitation (when the site's registration mode allows the + * caller to invite) also sends the invitation e-mail. + */ +export const invitationsCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/invitations', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all of the *current user's* invitations. + * + * Creating an invitation (when the site's registration mode allows the + * caller to invite) also sends the invitation e-mail. + */ +export const invitationsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/invitations/{id}', + ...options + }); +}; + +/** + * Lists all of the *current user's* invitations. + * + * Creating an invitation (when the site's registration mode allows the + * caller to invite) also sends the invitation e-mail. + */ +export const invitationsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/invitations/{id}', + ...options + }); +}; + +/** + * Lists all Kegs in the system. + * + * Reads follow site privacy; keg management requires an admin. Deleting + * a keg permanently destroys it and ALL of its drinks. + */ +export const kegsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs', + ...options + }); +}; + +/** + * Adds a new keg to the keg room (unattached). + */ +export const kegsCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Lists all Kegs in the system. + * + * Reads follow site privacy; keg management requires an admin. Deleting + * a keg permanently destroys it and ALL of its drinks. + */ +export const kegsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}', + ...options + }); +}; + +/** + * Lists all Kegs in the system. + * + * Reads follow site privacy; keg management requires an admin. Deleting + * a keg permanently destroys it and ALL of its drinks. + */ +export const kegsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}', + ...options + }); +}; + +/** + * Lists all Kegs in the system. + * + * Reads follow site privacy; keg management requires an admin. Deleting + * a keg permanently destroys it and ALL of its drinks. + */ +export const kegsPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all Kegs in the system. + * + * Reads follow site privacy; keg management requires an admin. Deleting + * a keg permanently destroys it and ALL of its drinks. + */ +export const kegsUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Marks an untapped keg as finished. + */ +export const kegsEndCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}/end', + ...options + }); +}; + +/** + * Returns a finished keg to the available pool. + */ +export const kegsReactivateCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}/reactivate', + ...options + }); +}; + +/** + * Records spilled volume against this keg. + */ +export const kegsSpillCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}/spill', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Returns the latest stats blob for this keg. + */ +export const kegsStatsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/kegs/{id}/stats', + ...options + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings', + ...options + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings/{id}', + ...options + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings/{id}', + ...options + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists the *current user's* notification settings. + */ +export const notificationSettingsUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/notification-settings/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data', + ...options + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data/{id}', + ...options + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data/{id}', + ...options + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all PluginData in the system. + * + * Admin-only: plugin data may contain plugin credentials. + */ +export const pluginDataUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/plugin-data/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all DrinkingSessions in the system. + */ +export const sessionsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/sessions', + ...options + }); +}; + +/** + * Lists all DrinkingSessions in the system. + */ +export const sessionsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/sessions/{id}', + ...options + }); +}; + +/** + * Returns the latest stats blob for this session. + */ +export const sessionsStatsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/sessions/{id}/stats', + ...options + }); +}; + +/** + * Returns the currently-active session, or 404 if there is none. + */ +export const sessionsCurrentRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/sessions/current', + ...options + }); +}; + +/** + * Enumerates the dates that have sessions, newest first. + * + * Buckets use the site's active timezone — the same conversion the + * year/month/day list filters apply — so a directory entry always + * matches the corresponding filtered listing. + */ +export const sessionsDirectoryRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/sessions/directory', + ...options + }); +}; + +/** + * Creates the initial admin account and logs it in. + */ +export const setupAdminUserCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + url: '/api/setup/admin-user', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Marks setup complete. + */ +export const setupFinishCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + url: '/api/setup/finish', + ...options + }); +}; + +/** + * Creates or migrates the database (synchronous). + */ +export const setupMigrateCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + url: '/api/setup/migrate', + ...options + }); +}; + +/** + * Applies initial site settings (after the database is migrated). + */ +export const setupSettingsCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + url: '/api/setup/settings', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Reports whether setup or upgrade is required. + */ +export const setupStatusRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + url: '/api/setup/status', + ...options + }); +}; + +/** + * Migrates the database and stamps the current server version. + */ +export const setupUpgradeCreate = (options?: Options) => { + return (options?.client ?? _heyApiClient).post({ + url: '/api/setup/upgrade', + ...options + }); +}; + +/** + * Reads (GET) or updates (PATCH) the site settings singleton. + */ +export const siteRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/site', + ...options + }); +}; + +/** + * Reads (GET) or updates (PATCH) the site settings singleton. + */ +export const sitePartialUpdate = (options?: Options) => { + return (options?.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/site', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; + +/** + * Uploads and sets the site background image. + */ +export const siteBackgroundImageCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + ...formDataBodySerializer, + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/site/background-image', + ...options, + headers: { + 'Content-Type': null, + ...options.headers + } + }); +}; + +/** + * Lists all stats snapshots in the system. + */ +export const statsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/stats', + ...options + }); +}; + +/** + * Lists all stats snapshots in the system. + */ +export const statsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/stats/{id}', + ...options + }); +}; + +/** + * Returns the latest system-wide (all-time) stats blob. + */ +export const statsSystemRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/stats/system', + ...options + }); +}; + +/** + * The 'current system status' view. + * + * Among other things, the `kegbot-frontend` uses this view to establish + * whether the system privacy permits reading from other APIs (status=200), + * or the user needs to log in (status=4xx), from application of the + * `DashboardViewer` permission + */ +export const statusRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/status', + ...options + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps', + ...options + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}', + ...options + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}', + ...options + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all KegTaps in the system. + * + * Reads follow site privacy; tap management (including the keg and + * hardware-connection operations below) requires an admin. + */ +export const tapsUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Attaches an existing (available) keg to this tap. + */ +export const tapsAttachKegCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/attach-keg', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Assigns a flow meter to this tap (null to disconnect). + */ +export const tapsConnectMeterCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/connect-meter', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Assigns a temperature sensor to this tap (null to disconnect). + */ +export const tapsConnectThermoCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/connect-thermo', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Assigns a flow toggle to this tap (null to disconnect). + */ +export const tapsConnectToggleCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/connect-toggle', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Takes the tap's current keg offline. + */ +export const tapsEndKegCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/end-keg', + ...options + }); +}; + +/** + * Manually records a drink (or spill) against this tap's keg. + * + * Returns the new drink (201), or no content (204) when recorded + * as a spill. + */ +export const tapsRecordDrinkCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/record-drink', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Creates a new keg and attaches it to this tap. + */ +export const tapsStartKegCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/taps/{id}/start-keg', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all Thermologs in the system. + */ +export const thermoLogsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-logs', + ...options + }); +}; + +/** + * Lists all Thermologs in the system. + */ +export const thermoLogsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-logs/{id}', + ...options + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors', + ...options + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsDestroy = (options: Options) => { + return (options.client ?? _heyApiClient).delete({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors/{id}', + ...options + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors/{id}', + ...options + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all ThermoSensors in the system. + */ +export const thermoSensorsUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/thermo-sensors/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all users in the system. + * + * Individual users (and their stats) are viewable by anyone the site + * privacy setting admits, mirroring the public drinker pages; the full + * user listing requires authentication. User management (create, edit, + * enable/disable, staff status, set-password) is admin-only; there is + * no delete — accounts are disabled instead. + */ +export const usersList = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users', + ...options + }); +}; + +/** + * Lists all users in the system. + * + * Individual users (and their stats) are viewable by anyone the site + * privacy setting admits, mirroring the public drinker pages; the full + * user listing requires authentication. User management (create, edit, + * enable/disable, staff status, set-password) is admin-only; there is + * no delete — accounts are disabled instead. + */ +export const usersCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Lists all users in the system. + * + * Individual users (and their stats) are viewable by anyone the site + * privacy setting admits, mirroring the public drinker pages; the full + * user listing requires authentication. User management (create, edit, + * enable/disable, staff status, set-password) is admin-only; there is + * no delete — accounts are disabled instead. + */ +export const usersRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/{username}', + ...options + }); +}; + +/** + * Lists all users in the system. + * + * Individual users (and their stats) are viewable by anyone the site + * privacy setting admits, mirroring the public drinker pages; the full + * user listing requires authentication. User management (create, edit, + * enable/disable, staff status, set-password) is admin-only; there is + * no delete — accounts are disabled instead. + */ +export const usersPartialUpdate = (options: Options) => { + return (options.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/{username}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Sets a new password for this user. + */ +export const usersSetPasswordCreate = (options: Options) => { + return (options.client ?? _heyApiClient).post({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/{username}/set-password', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Returns the latest stats blob for this user. + */ +export const usersStatsRetrieve = (options: Options) => { + return (options.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/{username}/stats', + ...options + }); +}; + +/** + * The frontend boot endpoint. + * + * GET always responds 200, regardless of authentication and site + * privacy: `user` is null for anonymous callers, and the rest of the + * payload is limited to privacy-safe configuration the frontend always + * needs (to render login screens, privacy interstitials, forms, and + * navigation). It also sets the CSRF cookie, so a fresh browser session + * can make authenticated POSTs after calling this. + * + * PATCH updates the current user's profile and returns the same payload. + */ +export const usersMeRetrieve = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/me', + ...options + }); +}; + +/** + * The frontend boot endpoint. + * + * GET always responds 200, regardless of authentication and site + * privacy: `user` is null for anonymous callers, and the rest of the + * payload is limited to privacy-safe configuration the frontend always + * needs (to render login screens, privacy interstitials, forms, and + * navigation). It also sets the CSRF cookie, so a fresh browser session + * can make authenticated POSTs after calling this. + * + * PATCH updates the current user's profile and returns the same payload. + */ +export const usersMePartialUpdate = (options?: Options) => { + return (options?.client ?? _heyApiClient).patch({ + security: [ + { + scheme: 'basic', + type: 'http' + }, + { + in: 'cookie', + name: 'sessionid', + type: 'apiKey' + } + ], + url: '/api/users/me', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); +}; \ No newline at end of file diff --git a/web-ui/api-client/types.gen.ts b/web-ui/api-client/types.gen.ts new file mode 100644 index 000000000..047c1cd9b --- /dev/null +++ b/web-ui/api-client/types.gen.ts @@ -0,0 +1,9921 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ActivateAccountRequestRequest = { + activation_key: string; + password: string; +}; + +export type AdminDashboard = { + email_configured: boolean; + redis_error: string | null; + num_users: number; + num_new_users: number; +}; + +export type AdminUserCreateRequestRequest = { + username: string; + email?: string; + password: string; + is_staff?: boolean; +}; + +export type ApiKey = { + readonly id: number; + /** + * User receiving API access. + */ + readonly user_id: number | null; + /** + * Device this key is associated with. + */ + readonly device_id: number | null; + is_active: boolean; + readonly key: string; + /** + * Information about this key. + */ + description?: string | null; + /** + * Time the key was created. + */ + created_time?: string; +}; + +export type ApiKeyRequest = { + is_active: boolean; + /** + * Information about this key. + */ + description?: string | null; + /** + * Time the key was created. + */ + created_time?: string; +}; + +export type AuthenticationToken = { + readonly id: number; + /** + * Namespace for this token. + */ + auth_device: string; + /** + * Actual value of the token, unique within an auth_device. + */ + token_value: string; + /** + * A human-readable alias for the token, for example "Guest Key". + */ + nice_name?: string | null; + /** + * A secret value necessary to authenticate with this token. + */ + pin?: string | null; + /** + * User in possession of and authenticated by this token. + */ + readonly user_id: number | null; + /** + * Whether this token is considered active. + */ + enabled?: boolean; + /** + * Date token was first added to the system. + */ + readonly created_time: string; + /** + * Date after which token is treated as disabled. + */ + expire_time?: string | null; +}; + +export type AuthenticationTokenRequest = { + /** + * Namespace for this token. + */ + auth_device: string; + /** + * Actual value of the token, unique within an auth_device. + */ + token_value: string; + /** + * A human-readable alias for the token, for example "Guest Key". + */ + nice_name?: string | null; + /** + * A secret value necessary to authenticate with this token. + */ + pin?: string | null; + user?: number | null; + /** + * Whether this token is considered active. + */ + enabled?: boolean; + /** + * Date after which token is treated as disabled. + */ + expire_time?: string | null; +}; + +export type Beverage = { + readonly id: number; + /** + * Name of the beverage, such as "Potrero Pale". + */ + name: string; + producer: BeverageProducer; + beverage_type?: BeverageTypeEnum; + /** + * Beverage style within type, eg "Pale Ale", "Pinot Noir". + */ + style?: string | null; + /** + * Free-form description of the beverage. + */ + description?: string | null; + picture: Picture; + /** + * Date of production, for wines or special/seasonal editions + */ + vintage_year?: string | null; + /** + * ABV Percentage + * Alcohol by volume, as percentage (0.0-100.0). + */ + abv_percent?: number | null; + /** + * Calories per mL of beverage. + */ + calories_per_ml?: number | null; + /** + * Carbohydrates per mL of beverage. + */ + carbs_per_ml?: number | null; + /** + * Color (Hex Value) + * Approximate beverage color + */ + color_hex?: string; + /** + * Original gravity (beer only). + */ + original_gravity?: number | null; + /** + * Final gravity (beer only). + */ + specific_gravity?: number | null; + /** + * SRM Value + * Standard Reference Method value (beer only). + */ + srm?: number | null; + /** + * IBUs + * International Bittering Units value (beer only). + */ + ibu?: number | null; + /** + * Star rating for beverage (0: worst, 5: best) + */ + star_rating?: number | null; + /** + * Untappd.com resource ID (beer only). + */ + untappd_beer_id?: number | null; +}; + +export type BeverageProducer = { + readonly id: number; + /** + * Name of the brewer + */ + name: string; + /** + * Country of origin + * + * * `AFG` - Afghanistan + * * `ALA` - Aland Islands + * * `ALB` - Albania + * * `DZA` - Algeria + * * `ASM` - American Samoa + * * `AND` - Andorra + * * `AGO` - Angola + * * `AIA` - Anguilla + * * `ATG` - Antigua and Barbuda + * * `ARG` - Argentina + * * `ARM` - Armenia + * * `ABW` - Aruba + * * `AUS` - Australia + * * `AUT` - Austria + * * `AZE` - Azerbaijan + * * `BHS` - Bahamas + * * `BHR` - Bahrain + * * `BGD` - Bangladesh + * * `BRB` - Barbados + * * `BLR` - Belarus + * * `BEL` - Belgium + * * `BLZ` - Belize + * * `BEN` - Benin + * * `BMU` - Bermuda + * * `BTN` - Bhutan + * * `BOL` - Bolivia + * * `BIH` - Bosnia and Herzegovina + * * `BWA` - Botswana + * * `BRA` - Brazil + * * `VGB` - British Virgin Islands + * * `BRN` - Brunei Darussalam + * * `BGR` - Bulgaria + * * `BFA` - Burkina Faso + * * `BDI` - Burundi + * * `KHM` - Cambodia + * * `CMR` - Cameroon + * * `CAN` - Canada + * * `CPV` - Cape Verde + * * `CYM` - Cayman Islands + * * `CAF` - Central African Republic + * * `TCD` - Chad + * * `CIL` - Channel Islands + * * `CHL` - Chile + * * `CHN` - China + * * `HKG` - China - Hong Kong + * * `MAC` - China - Macao + * * `COL` - Colombia + * * `COM` - Comoros + * * `COG` - Congo + * * `COK` - Cook Islands + * * `CRI` - Costa Rica + * * `CIV` - Cote d'Ivoire + * * `HRV` - Croatia + * * `CUB` - Cuba + * * `CYP` - Cyprus + * * `CZE` - Czech Republic + * * `PRK` - Democratic People's Republic of Korea + * * `COD` - Democratic Republic of the Congo + * * `DNK` - Denmark + * * `DJI` - Djibouti + * * `DMA` - Dominica + * * `DOM` - Dominican Republic + * * `ECU` - Ecuador + * * `EGY` - Egypt + * * `SLV` - El Salvador + * * `GNQ` - Equatorial Guinea + * * `ERI` - Eritrea + * * `EST` - Estonia + * * `ETH` - Ethiopia + * * `FRO` - Faeroe Islands + * * `FLK` - Falkland Islands (Malvinas) + * * `FJI` - Fiji + * * `FIN` - Finland + * * `FRA` - France + * * `GUF` - French Guiana + * * `PYF` - French Polynesia + * * `GAB` - Gabon + * * `GMB` - Gambia + * * `GEO` - Georgia + * * `DEU` - Germany + * * `GHA` - Ghana + * * `GIB` - Gibraltar + * * `GRC` - Greece + * * `GRL` - Greenland + * * `GRD` - Grenada + * * `GLP` - Guadeloupe + * * `GUM` - Guam + * * `GTM` - Guatemala + * * `GGY` - Guernsey + * * `GIN` - Guinea + * * `GNB` - Guinea-Bissau + * * `GUY` - Guyana + * * `HTI` - Haiti + * * `VAT` - Holy See (Vatican City) + * * `HND` - Honduras + * * `HUN` - Hungary + * * `ISL` - Iceland + * * `IND` - India + * * `IDN` - Indonesia + * * `IRN` - Iran + * * `IRQ` - Iraq + * * `IRL` - Ireland + * * `IMN` - Isle of Man + * * `ISR` - Israel + * * `ITA` - Italy + * * `JAM` - Jamaica + * * `JPN` - Japan + * * `JEY` - Jersey + * * `JOR` - Jordan + * * `KAZ` - Kazakhstan + * * `KEN` - Kenya + * * `KIR` - Kiribati + * * `KWT` - Kuwait + * * `KGZ` - Kyrgyzstan + * * `LAO` - Lao People's Democratic Republic + * * `LVA` - Latvia + * * `LBN` - Lebanon + * * `LSO` - Lesotho + * * `LBR` - Liberia + * * `LBY` - Libyan Arab Jamahiriya + * * `LIE` - Liechtenstein + * * `LTU` - Lithuania + * * `LUX` - Luxembourg + * * `MKD` - Macedonia + * * `MDG` - Madagascar + * * `MWI` - Malawi + * * `MYS` - Malaysia + * * `MDV` - Maldives + * * `MLI` - Mali + * * `MLT` - Malta + * * `MHL` - Marshall Islands + * * `MTQ` - Martinique + * * `MRT` - Mauritania + * * `MUS` - Mauritius + * * `MYT` - Mayotte + * * `MEX` - Mexico + * * `FSM` - Micronesia, Federated States of + * * `MCO` - Monaco + * * `MNG` - Mongolia + * * `MNE` - Montenegro + * * `MSR` - Montserrat + * * `MAR` - Morocco + * * `MOZ` - Mozambique + * * `MMR` - Myanmar + * * `NAM` - Namibia + * * `NRU` - Nauru + * * `NPL` - Nepal + * * `NLD` - Netherlands + * * `ANT` - Netherlands Antilles + * * `NCL` - New Caledonia + * * `NZL` - New Zealand + * * `NIC` - Nicaragua + * * `NER` - Niger + * * `NGA` - Nigeria + * * `NIU` - Niue + * * `NFK` - Norfolk Island + * * `MNP` - Northern Mariana Islands + * * `NOR` - Norway + * * `PSE` - Occupied Palestinian Territory + * * `OMN` - Oman + * * `PAK` - Pakistan + * * `PLW` - Palau + * * `PAN` - Panama + * * `PNG` - Papua New Guinea + * * `PRY` - Paraguay + * * `PER` - Peru + * * `PHL` - Philippines + * * `PCN` - Pitcairn + * * `POL` - Poland + * * `PRT` - Portugal + * * `PRI` - Puerto Rico + * * `QAT` - Qatar + * * `KOR` - Republic of Korea + * * `MDA` - Republic of Moldova + * * `REU` - Reunion + * * `ROU` - Romania + * * `RUS` - Russian Federation + * * `RWA` - Rwanda + * * `BLM` - Saint-Barthelemy + * * `SHN` - Saint Helena + * * `KNA` - Saint Kitts and Nevis + * * `LCA` - Saint Lucia + * * `MAF` - Saint-Martin (French part) + * * `SPM` - Saint Pierre and Miquelon + * * `VCT` - Saint Vincent and the Grenadines + * * `WSM` - Samoa + * * `SMR` - San Marino + * * `STP` - Sao Tome and Principe + * * `SAU` - Saudi Arabia + * * `SEN` - Senegal + * * `SRB` - Serbia + * * `SYC` - Seychelles + * * `SLE` - Sierra Leone + * * `SGP` - Singapore + * * `SVK` - Slovakia + * * `SVN` - Slovenia + * * `SLB` - Solomon Islands + * * `SOM` - Somalia + * * `ZAF` - South Africa + * * `ESP` - Spain + * * `LKA` - Sri Lanka + * * `SDN` - Sudan + * * `SUR` - Suriname + * * `SJM` - Svalbard and Jan Mayen Islands + * * `SWZ` - Swaziland + * * `SWE` - Sweden + * * `CHE` - Switzerland + * * `SYR` - Syrian Arab Republic + * * `TJK` - Tajikistan + * * `THA` - Thailand + * * `TLS` - Timor-Leste + * * `TGO` - Togo + * * `TKL` - Tokelau + * * `TON` - Tonga + * * `TTO` - Trinidad and Tobago + * * `TUN` - Tunisia + * * `TUR` - Turkey + * * `TKM` - Turkmenistan + * * `TCA` - Turks and Caicos Islands + * * `TUV` - Tuvalu + * * `UGA` - Uganda + * * `UKR` - Ukraine + * * `ARE` - United Arab Emirates + * * `GBR` - United Kingdom + * * `TZA` - United Republic of Tanzania + * * `USA` - United States of America + * * `VIR` - United States Virgin Islands + * * `URY` - Uruguay + * * `UZB` - Uzbekistan + * * `VUT` - Vanuatu + * * `VEN` - Venezuela (Bolivarian Republic of) + * * `VNM` - Viet Nam + * * `WLF` - Wallis and Futuna Islands + * * `ESH` - Western Sahara + * * `YEM` - Yemen + * * `ZMB` - Zambia + * * `ZWE` - Zimbabwe + */ + country?: CountryEnum; + /** + * State of origin, if applicable + */ + origin_state?: string | null; + /** + * City of origin, if known + */ + origin_city?: string | null; + is_homebrew?: boolean; + /** + * Brewer's home page + */ + url?: string | null; + /** + * A short description of the brewer + */ + description?: string | null; + picture: Picture; +}; + +export type BeverageProducerRequest = { + /** + * Name of the brewer + */ + name: string; + /** + * Country of origin + * + * * `AFG` - Afghanistan + * * `ALA` - Aland Islands + * * `ALB` - Albania + * * `DZA` - Algeria + * * `ASM` - American Samoa + * * `AND` - Andorra + * * `AGO` - Angola + * * `AIA` - Anguilla + * * `ATG` - Antigua and Barbuda + * * `ARG` - Argentina + * * `ARM` - Armenia + * * `ABW` - Aruba + * * `AUS` - Australia + * * `AUT` - Austria + * * `AZE` - Azerbaijan + * * `BHS` - Bahamas + * * `BHR` - Bahrain + * * `BGD` - Bangladesh + * * `BRB` - Barbados + * * `BLR` - Belarus + * * `BEL` - Belgium + * * `BLZ` - Belize + * * `BEN` - Benin + * * `BMU` - Bermuda + * * `BTN` - Bhutan + * * `BOL` - Bolivia + * * `BIH` - Bosnia and Herzegovina + * * `BWA` - Botswana + * * `BRA` - Brazil + * * `VGB` - British Virgin Islands + * * `BRN` - Brunei Darussalam + * * `BGR` - Bulgaria + * * `BFA` - Burkina Faso + * * `BDI` - Burundi + * * `KHM` - Cambodia + * * `CMR` - Cameroon + * * `CAN` - Canada + * * `CPV` - Cape Verde + * * `CYM` - Cayman Islands + * * `CAF` - Central African Republic + * * `TCD` - Chad + * * `CIL` - Channel Islands + * * `CHL` - Chile + * * `CHN` - China + * * `HKG` - China - Hong Kong + * * `MAC` - China - Macao + * * `COL` - Colombia + * * `COM` - Comoros + * * `COG` - Congo + * * `COK` - Cook Islands + * * `CRI` - Costa Rica + * * `CIV` - Cote d'Ivoire + * * `HRV` - Croatia + * * `CUB` - Cuba + * * `CYP` - Cyprus + * * `CZE` - Czech Republic + * * `PRK` - Democratic People's Republic of Korea + * * `COD` - Democratic Republic of the Congo + * * `DNK` - Denmark + * * `DJI` - Djibouti + * * `DMA` - Dominica + * * `DOM` - Dominican Republic + * * `ECU` - Ecuador + * * `EGY` - Egypt + * * `SLV` - El Salvador + * * `GNQ` - Equatorial Guinea + * * `ERI` - Eritrea + * * `EST` - Estonia + * * `ETH` - Ethiopia + * * `FRO` - Faeroe Islands + * * `FLK` - Falkland Islands (Malvinas) + * * `FJI` - Fiji + * * `FIN` - Finland + * * `FRA` - France + * * `GUF` - French Guiana + * * `PYF` - French Polynesia + * * `GAB` - Gabon + * * `GMB` - Gambia + * * `GEO` - Georgia + * * `DEU` - Germany + * * `GHA` - Ghana + * * `GIB` - Gibraltar + * * `GRC` - Greece + * * `GRL` - Greenland + * * `GRD` - Grenada + * * `GLP` - Guadeloupe + * * `GUM` - Guam + * * `GTM` - Guatemala + * * `GGY` - Guernsey + * * `GIN` - Guinea + * * `GNB` - Guinea-Bissau + * * `GUY` - Guyana + * * `HTI` - Haiti + * * `VAT` - Holy See (Vatican City) + * * `HND` - Honduras + * * `HUN` - Hungary + * * `ISL` - Iceland + * * `IND` - India + * * `IDN` - Indonesia + * * `IRN` - Iran + * * `IRQ` - Iraq + * * `IRL` - Ireland + * * `IMN` - Isle of Man + * * `ISR` - Israel + * * `ITA` - Italy + * * `JAM` - Jamaica + * * `JPN` - Japan + * * `JEY` - Jersey + * * `JOR` - Jordan + * * `KAZ` - Kazakhstan + * * `KEN` - Kenya + * * `KIR` - Kiribati + * * `KWT` - Kuwait + * * `KGZ` - Kyrgyzstan + * * `LAO` - Lao People's Democratic Republic + * * `LVA` - Latvia + * * `LBN` - Lebanon + * * `LSO` - Lesotho + * * `LBR` - Liberia + * * `LBY` - Libyan Arab Jamahiriya + * * `LIE` - Liechtenstein + * * `LTU` - Lithuania + * * `LUX` - Luxembourg + * * `MKD` - Macedonia + * * `MDG` - Madagascar + * * `MWI` - Malawi + * * `MYS` - Malaysia + * * `MDV` - Maldives + * * `MLI` - Mali + * * `MLT` - Malta + * * `MHL` - Marshall Islands + * * `MTQ` - Martinique + * * `MRT` - Mauritania + * * `MUS` - Mauritius + * * `MYT` - Mayotte + * * `MEX` - Mexico + * * `FSM` - Micronesia, Federated States of + * * `MCO` - Monaco + * * `MNG` - Mongolia + * * `MNE` - Montenegro + * * `MSR` - Montserrat + * * `MAR` - Morocco + * * `MOZ` - Mozambique + * * `MMR` - Myanmar + * * `NAM` - Namibia + * * `NRU` - Nauru + * * `NPL` - Nepal + * * `NLD` - Netherlands + * * `ANT` - Netherlands Antilles + * * `NCL` - New Caledonia + * * `NZL` - New Zealand + * * `NIC` - Nicaragua + * * `NER` - Niger + * * `NGA` - Nigeria + * * `NIU` - Niue + * * `NFK` - Norfolk Island + * * `MNP` - Northern Mariana Islands + * * `NOR` - Norway + * * `PSE` - Occupied Palestinian Territory + * * `OMN` - Oman + * * `PAK` - Pakistan + * * `PLW` - Palau + * * `PAN` - Panama + * * `PNG` - Papua New Guinea + * * `PRY` - Paraguay + * * `PER` - Peru + * * `PHL` - Philippines + * * `PCN` - Pitcairn + * * `POL` - Poland + * * `PRT` - Portugal + * * `PRI` - Puerto Rico + * * `QAT` - Qatar + * * `KOR` - Republic of Korea + * * `MDA` - Republic of Moldova + * * `REU` - Reunion + * * `ROU` - Romania + * * `RUS` - Russian Federation + * * `RWA` - Rwanda + * * `BLM` - Saint-Barthelemy + * * `SHN` - Saint Helena + * * `KNA` - Saint Kitts and Nevis + * * `LCA` - Saint Lucia + * * `MAF` - Saint-Martin (French part) + * * `SPM` - Saint Pierre and Miquelon + * * `VCT` - Saint Vincent and the Grenadines + * * `WSM` - Samoa + * * `SMR` - San Marino + * * `STP` - Sao Tome and Principe + * * `SAU` - Saudi Arabia + * * `SEN` - Senegal + * * `SRB` - Serbia + * * `SYC` - Seychelles + * * `SLE` - Sierra Leone + * * `SGP` - Singapore + * * `SVK` - Slovakia + * * `SVN` - Slovenia + * * `SLB` - Solomon Islands + * * `SOM` - Somalia + * * `ZAF` - South Africa + * * `ESP` - Spain + * * `LKA` - Sri Lanka + * * `SDN` - Sudan + * * `SUR` - Suriname + * * `SJM` - Svalbard and Jan Mayen Islands + * * `SWZ` - Swaziland + * * `SWE` - Sweden + * * `CHE` - Switzerland + * * `SYR` - Syrian Arab Republic + * * `TJK` - Tajikistan + * * `THA` - Thailand + * * `TLS` - Timor-Leste + * * `TGO` - Togo + * * `TKL` - Tokelau + * * `TON` - Tonga + * * `TTO` - Trinidad and Tobago + * * `TUN` - Tunisia + * * `TUR` - Turkey + * * `TKM` - Turkmenistan + * * `TCA` - Turks and Caicos Islands + * * `TUV` - Tuvalu + * * `UGA` - Uganda + * * `UKR` - Ukraine + * * `ARE` - United Arab Emirates + * * `GBR` - United Kingdom + * * `TZA` - United Republic of Tanzania + * * `USA` - United States of America + * * `VIR` - United States Virgin Islands + * * `URY` - Uruguay + * * `UZB` - Uzbekistan + * * `VUT` - Vanuatu + * * `VEN` - Venezuela (Bolivarian Republic of) + * * `VNM` - Viet Nam + * * `WLF` - Wallis and Futuna Islands + * * `ESH` - Western Sahara + * * `YEM` - Yemen + * * `ZMB` - Zambia + * * `ZWE` - Zimbabwe + */ + country?: CountryEnum; + /** + * State of origin, if applicable + */ + origin_state?: string | null; + /** + * City of origin, if known + */ + origin_city?: string | null; + is_homebrew?: boolean; + /** + * Brewer's home page + */ + url?: string | null; + /** + * A short description of the brewer + */ + description?: string | null; +}; + +export type BeverageRequest = { + /** + * Name of the beverage, such as "Potrero Pale". + */ + name: string; + producer_id: number; + beverage_type?: BeverageTypeEnum; + /** + * Beverage style within type, eg "Pale Ale", "Pinot Noir". + */ + style?: string | null; + /** + * Free-form description of the beverage. + */ + description?: string | null; + /** + * Date of production, for wines or special/seasonal editions + */ + vintage_year?: string | null; + /** + * ABV Percentage + * Alcohol by volume, as percentage (0.0-100.0). + */ + abv_percent?: number | null; + /** + * Calories per mL of beverage. + */ + calories_per_ml?: number | null; + /** + * Carbohydrates per mL of beverage. + */ + carbs_per_ml?: number | null; + /** + * Color (Hex Value) + * Approximate beverage color + */ + color_hex?: string; + /** + * Original gravity (beer only). + */ + original_gravity?: number | null; + /** + * Final gravity (beer only). + */ + specific_gravity?: number | null; + /** + * SRM Value + * Standard Reference Method value (beer only). + */ + srm?: number | null; + /** + * IBUs + * International Bittering Units value (beer only). + */ + ibu?: number | null; + /** + * Star rating for beverage (0: worst, 5: best) + */ + star_rating?: number | null; + /** + * Untappd.com resource ID (beer only). + */ + untappd_beer_id?: number | null; +}; + +/** + * * `beer` - Beer + * * `wine` - Wine + * * `soda` - Soda + * * `kombucha` - Kombucha + * * `other` - Other/Unknown + */ +export type BeverageTypeEnum = 'beer' | 'wine' | 'soda' | 'kombucha' | 'other'; + +export type ConfirmEmailRequestRequest = { + token: string; +}; + +export type Controller = { + readonly id: number; + /** + * Identifying name for this device; must be unique. + */ + name: string; + /** + * Type of controller (optional). + */ + model_name?: string | null; + /** + * Serial number (optional). + */ + serial_number?: string | null; +}; + +export type ControllerRequest = { + /** + * Identifying name for this device; must be unique. + */ + name: string; + /** + * Type of controller (optional). + */ + model_name?: string | null; + /** + * Serial number (optional). + */ + serial_number?: string | null; +}; + +/** + * * `AFG` - Afghanistan + * * `ALA` - Aland Islands + * * `ALB` - Albania + * * `DZA` - Algeria + * * `ASM` - American Samoa + * * `AND` - Andorra + * * `AGO` - Angola + * * `AIA` - Anguilla + * * `ATG` - Antigua and Barbuda + * * `ARG` - Argentina + * * `ARM` - Armenia + * * `ABW` - Aruba + * * `AUS` - Australia + * * `AUT` - Austria + * * `AZE` - Azerbaijan + * * `BHS` - Bahamas + * * `BHR` - Bahrain + * * `BGD` - Bangladesh + * * `BRB` - Barbados + * * `BLR` - Belarus + * * `BEL` - Belgium + * * `BLZ` - Belize + * * `BEN` - Benin + * * `BMU` - Bermuda + * * `BTN` - Bhutan + * * `BOL` - Bolivia + * * `BIH` - Bosnia and Herzegovina + * * `BWA` - Botswana + * * `BRA` - Brazil + * * `VGB` - British Virgin Islands + * * `BRN` - Brunei Darussalam + * * `BGR` - Bulgaria + * * `BFA` - Burkina Faso + * * `BDI` - Burundi + * * `KHM` - Cambodia + * * `CMR` - Cameroon + * * `CAN` - Canada + * * `CPV` - Cape Verde + * * `CYM` - Cayman Islands + * * `CAF` - Central African Republic + * * `TCD` - Chad + * * `CIL` - Channel Islands + * * `CHL` - Chile + * * `CHN` - China + * * `HKG` - China - Hong Kong + * * `MAC` - China - Macao + * * `COL` - Colombia + * * `COM` - Comoros + * * `COG` - Congo + * * `COK` - Cook Islands + * * `CRI` - Costa Rica + * * `CIV` - Cote d'Ivoire + * * `HRV` - Croatia + * * `CUB` - Cuba + * * `CYP` - Cyprus + * * `CZE` - Czech Republic + * * `PRK` - Democratic People's Republic of Korea + * * `COD` - Democratic Republic of the Congo + * * `DNK` - Denmark + * * `DJI` - Djibouti + * * `DMA` - Dominica + * * `DOM` - Dominican Republic + * * `ECU` - Ecuador + * * `EGY` - Egypt + * * `SLV` - El Salvador + * * `GNQ` - Equatorial Guinea + * * `ERI` - Eritrea + * * `EST` - Estonia + * * `ETH` - Ethiopia + * * `FRO` - Faeroe Islands + * * `FLK` - Falkland Islands (Malvinas) + * * `FJI` - Fiji + * * `FIN` - Finland + * * `FRA` - France + * * `GUF` - French Guiana + * * `PYF` - French Polynesia + * * `GAB` - Gabon + * * `GMB` - Gambia + * * `GEO` - Georgia + * * `DEU` - Germany + * * `GHA` - Ghana + * * `GIB` - Gibraltar + * * `GRC` - Greece + * * `GRL` - Greenland + * * `GRD` - Grenada + * * `GLP` - Guadeloupe + * * `GUM` - Guam + * * `GTM` - Guatemala + * * `GGY` - Guernsey + * * `GIN` - Guinea + * * `GNB` - Guinea-Bissau + * * `GUY` - Guyana + * * `HTI` - Haiti + * * `VAT` - Holy See (Vatican City) + * * `HND` - Honduras + * * `HUN` - Hungary + * * `ISL` - Iceland + * * `IND` - India + * * `IDN` - Indonesia + * * `IRN` - Iran + * * `IRQ` - Iraq + * * `IRL` - Ireland + * * `IMN` - Isle of Man + * * `ISR` - Israel + * * `ITA` - Italy + * * `JAM` - Jamaica + * * `JPN` - Japan + * * `JEY` - Jersey + * * `JOR` - Jordan + * * `KAZ` - Kazakhstan + * * `KEN` - Kenya + * * `KIR` - Kiribati + * * `KWT` - Kuwait + * * `KGZ` - Kyrgyzstan + * * `LAO` - Lao People's Democratic Republic + * * `LVA` - Latvia + * * `LBN` - Lebanon + * * `LSO` - Lesotho + * * `LBR` - Liberia + * * `LBY` - Libyan Arab Jamahiriya + * * `LIE` - Liechtenstein + * * `LTU` - Lithuania + * * `LUX` - Luxembourg + * * `MKD` - Macedonia + * * `MDG` - Madagascar + * * `MWI` - Malawi + * * `MYS` - Malaysia + * * `MDV` - Maldives + * * `MLI` - Mali + * * `MLT` - Malta + * * `MHL` - Marshall Islands + * * `MTQ` - Martinique + * * `MRT` - Mauritania + * * `MUS` - Mauritius + * * `MYT` - Mayotte + * * `MEX` - Mexico + * * `FSM` - Micronesia, Federated States of + * * `MCO` - Monaco + * * `MNG` - Mongolia + * * `MNE` - Montenegro + * * `MSR` - Montserrat + * * `MAR` - Morocco + * * `MOZ` - Mozambique + * * `MMR` - Myanmar + * * `NAM` - Namibia + * * `NRU` - Nauru + * * `NPL` - Nepal + * * `NLD` - Netherlands + * * `ANT` - Netherlands Antilles + * * `NCL` - New Caledonia + * * `NZL` - New Zealand + * * `NIC` - Nicaragua + * * `NER` - Niger + * * `NGA` - Nigeria + * * `NIU` - Niue + * * `NFK` - Norfolk Island + * * `MNP` - Northern Mariana Islands + * * `NOR` - Norway + * * `PSE` - Occupied Palestinian Territory + * * `OMN` - Oman + * * `PAK` - Pakistan + * * `PLW` - Palau + * * `PAN` - Panama + * * `PNG` - Papua New Guinea + * * `PRY` - Paraguay + * * `PER` - Peru + * * `PHL` - Philippines + * * `PCN` - Pitcairn + * * `POL` - Poland + * * `PRT` - Portugal + * * `PRI` - Puerto Rico + * * `QAT` - Qatar + * * `KOR` - Republic of Korea + * * `MDA` - Republic of Moldova + * * `REU` - Reunion + * * `ROU` - Romania + * * `RUS` - Russian Federation + * * `RWA` - Rwanda + * * `BLM` - Saint-Barthelemy + * * `SHN` - Saint Helena + * * `KNA` - Saint Kitts and Nevis + * * `LCA` - Saint Lucia + * * `MAF` - Saint-Martin (French part) + * * `SPM` - Saint Pierre and Miquelon + * * `VCT` - Saint Vincent and the Grenadines + * * `WSM` - Samoa + * * `SMR` - San Marino + * * `STP` - Sao Tome and Principe + * * `SAU` - Saudi Arabia + * * `SEN` - Senegal + * * `SRB` - Serbia + * * `SYC` - Seychelles + * * `SLE` - Sierra Leone + * * `SGP` - Singapore + * * `SVK` - Slovakia + * * `SVN` - Slovenia + * * `SLB` - Solomon Islands + * * `SOM` - Somalia + * * `ZAF` - South Africa + * * `ESP` - Spain + * * `LKA` - Sri Lanka + * * `SDN` - Sudan + * * `SUR` - Suriname + * * `SJM` - Svalbard and Jan Mayen Islands + * * `SWZ` - Swaziland + * * `SWE` - Sweden + * * `CHE` - Switzerland + * * `SYR` - Syrian Arab Republic + * * `TJK` - Tajikistan + * * `THA` - Thailand + * * `TLS` - Timor-Leste + * * `TGO` - Togo + * * `TKL` - Tokelau + * * `TON` - Tonga + * * `TTO` - Trinidad and Tobago + * * `TUN` - Tunisia + * * `TUR` - Turkey + * * `TKM` - Turkmenistan + * * `TCA` - Turks and Caicos Islands + * * `TUV` - Tuvalu + * * `UGA` - Uganda + * * `UKR` - Ukraine + * * `ARE` - United Arab Emirates + * * `GBR` - United Kingdom + * * `TZA` - United Republic of Tanzania + * * `USA` - United States of America + * * `VIR` - United States Virgin Islands + * * `URY` - Uruguay + * * `UZB` - Uzbekistan + * * `VUT` - Vanuatu + * * `VEN` - Venezuela (Bolivarian Republic of) + * * `VNM` - Viet Nam + * * `WLF` - Wallis and Futuna Islands + * * `ESH` - Western Sahara + * * `YEM` - Yemen + * * `ZMB` - Zambia + * * `ZWE` - Zimbabwe + */ +export type CountryEnum = 'AFG' | 'ALA' | 'ALB' | 'DZA' | 'ASM' | 'AND' | 'AGO' | 'AIA' | 'ATG' | 'ARG' | 'ARM' | 'ABW' | 'AUS' | 'AUT' | 'AZE' | 'BHS' | 'BHR' | 'BGD' | 'BRB' | 'BLR' | 'BEL' | 'BLZ' | 'BEN' | 'BMU' | 'BTN' | 'BOL' | 'BIH' | 'BWA' | 'BRA' | 'VGB' | 'BRN' | 'BGR' | 'BFA' | 'BDI' | 'KHM' | 'CMR' | 'CAN' | 'CPV' | 'CYM' | 'CAF' | 'TCD' | 'CIL' | 'CHL' | 'CHN' | 'HKG' | 'MAC' | 'COL' | 'COM' | 'COG' | 'COK' | 'CRI' | 'CIV' | 'HRV' | 'CUB' | 'CYP' | 'CZE' | 'PRK' | 'COD' | 'DNK' | 'DJI' | 'DMA' | 'DOM' | 'ECU' | 'EGY' | 'SLV' | 'GNQ' | 'ERI' | 'EST' | 'ETH' | 'FRO' | 'FLK' | 'FJI' | 'FIN' | 'FRA' | 'GUF' | 'PYF' | 'GAB' | 'GMB' | 'GEO' | 'DEU' | 'GHA' | 'GIB' | 'GRC' | 'GRL' | 'GRD' | 'GLP' | 'GUM' | 'GTM' | 'GGY' | 'GIN' | 'GNB' | 'GUY' | 'HTI' | 'VAT' | 'HND' | 'HUN' | 'ISL' | 'IND' | 'IDN' | 'IRN' | 'IRQ' | 'IRL' | 'IMN' | 'ISR' | 'ITA' | 'JAM' | 'JPN' | 'JEY' | 'JOR' | 'KAZ' | 'KEN' | 'KIR' | 'KWT' | 'KGZ' | 'LAO' | 'LVA' | 'LBN' | 'LSO' | 'LBR' | 'LBY' | 'LIE' | 'LTU' | 'LUX' | 'MKD' | 'MDG' | 'MWI' | 'MYS' | 'MDV' | 'MLI' | 'MLT' | 'MHL' | 'MTQ' | 'MRT' | 'MUS' | 'MYT' | 'MEX' | 'FSM' | 'MCO' | 'MNG' | 'MNE' | 'MSR' | 'MAR' | 'MOZ' | 'MMR' | 'NAM' | 'NRU' | 'NPL' | 'NLD' | 'ANT' | 'NCL' | 'NZL' | 'NIC' | 'NER' | 'NGA' | 'NIU' | 'NFK' | 'MNP' | 'NOR' | 'PSE' | 'OMN' | 'PAK' | 'PLW' | 'PAN' | 'PNG' | 'PRY' | 'PER' | 'PHL' | 'PCN' | 'POL' | 'PRT' | 'PRI' | 'QAT' | 'KOR' | 'MDA' | 'REU' | 'ROU' | 'RUS' | 'RWA' | 'BLM' | 'SHN' | 'KNA' | 'LCA' | 'MAF' | 'SPM' | 'VCT' | 'WSM' | 'SMR' | 'STP' | 'SAU' | 'SEN' | 'SRB' | 'SYC' | 'SLE' | 'SGP' | 'SVK' | 'SVN' | 'SLB' | 'SOM' | 'ZAF' | 'ESP' | 'LKA' | 'SDN' | 'SUR' | 'SJM' | 'SWZ' | 'SWE' | 'CHE' | 'SYR' | 'TJK' | 'THA' | 'TLS' | 'TGO' | 'TKL' | 'TON' | 'TTO' | 'TUN' | 'TUR' | 'TKM' | 'TCA' | 'TUV' | 'UGA' | 'UKR' | 'ARE' | 'GBR' | 'TZA' | 'USA' | 'VIR' | 'URY' | 'UZB' | 'VUT' | 'VEN' | 'VNM' | 'WLF' | 'ESH' | 'YEM' | 'ZMB' | 'ZWE'; + +export type CurrentUser = { + readonly id: number; + /** + * Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters + */ + username: string; + /** + * Email address + */ + readonly email: string; + /** + * Full name, will be shown in some places instead of username + */ + display_name?: string; + /** + * Staff status + * Designates whether the user can log into this admin site. + */ + readonly is_staff: boolean; + /** + * Active + * Designates whether this user should be treated as active. Unselect this instead of deleting accounts. + */ + readonly is_active: boolean; + picture: Picture; +}; + +export type Device = { + readonly id: number; + name?: string; + /** + * Time the device was created. + */ + created_time?: string; +}; + +export type DeviceRequest = { + name?: string; + /** + * Time the device was created. + */ + created_time?: string; +}; + +export type Drink = { + readonly id: number; + /** + * Flow sensor ticks, never changed once recorded. + */ + readonly ticks: number; + /** + * Calculated (or set) Drink volume. + */ + readonly volume_ml: number; + /** + * Date and time of pour. + */ + readonly time: string; + /** + * Time in seconds taken to pour this Drink. + */ + readonly duration: number; + user: User; + keg: Keg; + /** + * Session where this Drink is grouped. + */ + readonly session_id: number | null; + /** + * Comment from the drinker at the time of the pour. + */ + shout?: string | null; + picture: Picture; +}; + +export type DrinkReassignRequestRequest = { + username: string; +}; + +export type DrinkingSession = { + readonly id: number; + start_time: string; + end_time: string; + volume_ml?: number; + timezone?: string; + name?: string | null; + readonly stats: unknown; +}; + +export type EmailChangeRequestRequest = { + email: string; +}; + +export type EmailTestRequestRequest = { + address: string; +}; + +export type FlowMeter = { + readonly id: number; + /** + * Controller that owns this meter. + */ + readonly controller_id: number; + /** + * Controller-specific data port name for this meter. + */ + port_name: string; + /** + * Tap to which this meter is currently bound. + */ + readonly tap_id: number | null; + /** + * Flow meter pulses per mL of fluid. Common values: 2.724 (FT330-RJ), 5.4 (SF800) + */ + ticks_per_ml?: number; +}; + +export type FlowMeterRequest = { + /** + * Controller-specific data port name for this meter. + */ + port_name: string; + /** + * Flow meter pulses per mL of fluid. Common values: 2.724 (FT330-RJ), 5.4 (SF800) + */ + ticks_per_ml?: number; +}; + +export type FlowToggle = { + readonly id: number; + /** + * Controller that owns this toggle. + */ + readonly controller_id: number; + /** + * Controller-specific data port name for this toggle. + */ + port_name: string; + /** + * Tap to which this toggle is currently bound. + */ + readonly tap_id: number | null; +}; + +export type FlowToggleRequest = { + /** + * Controller-specific data port name for this toggle. + */ + port_name: string; +}; + +export type Invitation = { + readonly id: number; + /** + * Address this invitation was sent to. + */ + for_email: string; + /** + * Date invited + * Date and time the invitation was sent + */ + readonly invited_date: string; + /** + * Date expries + * Date and time after which the invitation is considered expired + */ + expires_date?: string; + readonly is_expired: boolean; +}; + +export type InvitationRequest = { + /** + * Address this invitation was sent to. + */ + for_email: string; + /** + * Date expries + * Date and time after which the invitation is considered expired + */ + expires_date?: string; +}; + +export type Keg = { + readonly id: number; + beverage: Beverage; + /** + * Keg container type, used to initialize keg's full volume + * + * * `mini` - Mini Keg (5 L) + * * `corny-2_5-gal` - Corny Keg (2.5 gal) + * * `corny-3-gal` - Corny Keg (3.0 gal) + * * `corny` - Corny Keg (5 gal) + * * `sixth` - Sixth Barrel (5.17 gal) + * * `euro-30-liter` - European DIN (30 L) + * * `euro-half` - European Half Barrel (50 L) + * * `quarter` - Quarter Barrel (7.75 gal) + * * `euro` - European Full Barrel (100 L) + * * `half-barrel` - Half Barrel (15.5 gal) + * * `other` - Other + */ + keg_type?: KegTypeEnum; + /** + * Computed served volume. + */ + readonly served_volume_ml: number; + /** + * Full volume of this Keg; usually set automatically from keg_type. + */ + full_volume_ml?: number; + /** + * Time the Keg was first tapped. + */ + readonly start_time: string; + /** + * Time the Keg was finished or disconnected. + */ + readonly end_time: string; + /** + * Current keg state. + * + * * `available` - Available + * * `on_tap` - On tap + * * `finished` - Finished + */ + status: KegStatusEnum; + /** + * User-visible description of the Keg. + */ + description?: string | null; + /** + * Amount of beverage poured without an associated Drink. + */ + readonly spilled_ml: number; + /** + * Private notes about this keg, viewable only by admins. + */ + notes?: string | null; + readonly illustration: string; + readonly illustration_thumbnail: string; + readonly stats: unknown; +}; + +/** + * Parameters for creating a keg. + * + * The beverage may be given as an existing `beverage_id`, or described by + * the (`beverage_name`, `producer_name`, `style_name`, `beverage_type`) + * tuple, which matches or creates one. + */ +export type KegCreateRequestRequest = { + beverage_id?: number | null; + beverage_name?: string; + beverage_type?: BeverageTypeEnum; + producer_name?: string; + style_name?: string; + keg_type?: KegTypeEnum; + full_volume_ml?: number | null; + description?: string; + notes?: string; +}; + +export type KegRequest = { + /** + * Keg container type, used to initialize keg's full volume + * + * * `mini` - Mini Keg (5 L) + * * `corny-2_5-gal` - Corny Keg (2.5 gal) + * * `corny-3-gal` - Corny Keg (3.0 gal) + * * `corny` - Corny Keg (5 gal) + * * `sixth` - Sixth Barrel (5.17 gal) + * * `euro-30-liter` - European DIN (30 L) + * * `euro-half` - European Half Barrel (50 L) + * * `quarter` - Quarter Barrel (7.75 gal) + * * `euro` - European Full Barrel (100 L) + * * `half-barrel` - Half Barrel (15.5 gal) + * * `other` - Other + */ + keg_type?: KegTypeEnum; + /** + * Full volume of this Keg; usually set automatically from keg_type. + */ + full_volume_ml?: number; + /** + * User-visible description of the Keg. + */ + description?: string | null; + /** + * Private notes about this keg, viewable only by admins. + */ + notes?: string | null; +}; + +export type KegSpillRequestRequest = { + volume_ml: number; +}; + +/** + * * `available` - Available + * * `on_tap` - On tap + * * `finished` - Finished + */ +export type KegStatusEnum = 'available' | 'on_tap' | 'finished'; + +export type KegTap = { + readonly id: number; + /** + * The display name for this tap, for example, "Main Tap". + */ + name: string; + /** + * Private notes about this tap. + */ + notes?: string | null; + readonly current_keg_id: number; + readonly temperature_sensor_id: number; + /** + * Position relative to other taps when sorting (0=first). + */ + sort_order?: number; + current_keg: Keg; +}; + +export type KegTapRequest = { + /** + * The display name for this tap, for example, "Main Tap". + */ + name: string; + /** + * Private notes about this tap. + */ + notes?: string | null; + /** + * Position relative to other taps when sorting (0=first). + */ + sort_order?: number; +}; + +/** + * * `mini` - Mini Keg (5 L) + * * `corny-2_5-gal` - Corny Keg (2.5 gal) + * * `corny-3-gal` - Corny Keg (3.0 gal) + * * `corny` - Corny Keg (5 gal) + * * `sixth` - Sixth Barrel (5.17 gal) + * * `euro-30-liter` - European DIN (30 L) + * * `euro-half` - European Half Barrel (50 L) + * * `quarter` - Quarter Barrel (7.75 gal) + * * `euro` - European Full Barrel (100 L) + * * `half-barrel` - Half Barrel (15.5 gal) + * * `other` - Other + */ +export type KegTypeEnum = 'mini' | 'corny-2_5-gal' | 'corny-3-gal' | 'corny' | 'sixth' | 'euro-30-liter' | 'euro-half' | 'quarter' | 'euro' | 'half-barrel' | 'other'; + +export type KegbotSite = { + readonly name: string; + readonly server_version: string | null; + /** + * True if the site has completed setup. + */ + readonly is_setup: boolean; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * The title of this site. + */ + title?: string; + background_image: Picture; + /** + * Set to your Google Analytics ID to enable tracking. Example: UA-XXXX-y + */ + google_analytics_id?: string | null; + /** + * Maximum time, in minutes, that a session may be idle (no pours) before it is considered to be finished. Recommended value is 180. + */ + session_timeout_minutes?: number; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Who can join this Kegbot from the web site? + * + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ + registration_mode?: RegistrationModeEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; + readonly stats: unknown; +}; + +export type LoginRequest = { + username: string; + password: string; +}; + +/** + * The boot payload: current user plus always-needed site metadata. + * + * Served to every caller with status 200; `user` is null when the caller + * is not authenticated. Static constants (choice lists, keg sizes, and + * similar) are NOT served here: they are baked into the frontend build + * via the `print_constants` management command. + */ +export type Me = { + user: CurrentUser | null; + site: SiteConfig; + can_invite: boolean; + have_sessions: boolean; + sso_login_url: string; + sso_logout_url: string; + plugins: Array; +}; + +/** + * Parameters for creating a keg. + * + * The beverage may be given as an existing `beverage_id`, or described by + * the (`beverage_name`, `producer_name`, `style_name`, `beverage_type`) + * tuple, which matches or creates one. + */ +export type NewKegRequestRequest = { + beverage_id?: number | null; + beverage_name?: string; + beverage_type?: BeverageTypeEnum; + producer_name?: string; + style_name?: string; + keg_type?: KegTypeEnum; + full_volume_ml?: number | null; +}; + +export type NotificationSettings = { + readonly id: number; + /** + * User for these settings. + */ + readonly user_id: number; + /** + * Notification backend (dotted path) for these settings. + */ + backend: string; + /** + * Sent when a keg is activated. + */ + keg_tapped?: boolean; + /** + * Sent when a new drinking session starts. + */ + session_started?: boolean; + /** + * Sent when a keg becomes low. + */ + keg_volume_low?: boolean; + /** + * Sent when a keg has been taken offline. + */ + keg_ended?: boolean; +}; + +export type NotificationSettingsRequest = { + /** + * Notification backend (dotted path) for these settings. + */ + backend: string; + /** + * Sent when a keg is activated. + */ + keg_tapped?: boolean; + /** + * Sent when a new drinking session starts. + */ + session_started?: boolean; + /** + * Sent when a keg becomes low. + */ + keg_volume_low?: boolean; + /** + * Sent when a keg has been taken offline. + */ + keg_ended?: boolean; +}; + +export type PaginatedApiKeyList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedAuthenticationTokenList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedBeverageList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedBeverageProducerList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedControllerList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedDeviceList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedDrinkList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedDrinkingSessionList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedFlowMeterList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedFlowToggleList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedInvitationList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedKegList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedKegTapList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedNotificationSettingsList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedPluginDataList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedStatsList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedSystemEventList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedThermoSensorList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedThermologList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PaginatedUserList = { + next?: string | null; + previous?: string | null; + results: Array; +}; + +export type PasswordChangeRequestRequest = { + current_password: string; + new_password: string; +}; + +export type PasswordResetConfirmRequestRequest = { + uid: string; + token: string; + new_password: string; +}; + +export type PasswordResetRequestRequest = { + email: string; +}; + +export type PatchedAdminUserUpdateRequestRequest = { + email?: string; + display_name?: string; + is_staff?: boolean; + is_active?: boolean; +}; + +export type PatchedApiKeyRequest = { + is_active?: boolean; + /** + * Information about this key. + */ + description?: string | null; + /** + * Time the key was created. + */ + created_time?: string; +}; + +export type PatchedAuthenticationTokenRequest = { + /** + * Namespace for this token. + */ + auth_device?: string; + /** + * Actual value of the token, unique within an auth_device. + */ + token_value?: string; + /** + * A human-readable alias for the token, for example "Guest Key". + */ + nice_name?: string | null; + /** + * A secret value necessary to authenticate with this token. + */ + pin?: string | null; + user?: number | null; + /** + * Whether this token is considered active. + */ + enabled?: boolean; + /** + * Date after which token is treated as disabled. + */ + expire_time?: string | null; +}; + +export type PatchedBeverageProducerRequest = { + /** + * Name of the brewer + */ + name?: string; + /** + * Country of origin + * + * * `AFG` - Afghanistan + * * `ALA` - Aland Islands + * * `ALB` - Albania + * * `DZA` - Algeria + * * `ASM` - American Samoa + * * `AND` - Andorra + * * `AGO` - Angola + * * `AIA` - Anguilla + * * `ATG` - Antigua and Barbuda + * * `ARG` - Argentina + * * `ARM` - Armenia + * * `ABW` - Aruba + * * `AUS` - Australia + * * `AUT` - Austria + * * `AZE` - Azerbaijan + * * `BHS` - Bahamas + * * `BHR` - Bahrain + * * `BGD` - Bangladesh + * * `BRB` - Barbados + * * `BLR` - Belarus + * * `BEL` - Belgium + * * `BLZ` - Belize + * * `BEN` - Benin + * * `BMU` - Bermuda + * * `BTN` - Bhutan + * * `BOL` - Bolivia + * * `BIH` - Bosnia and Herzegovina + * * `BWA` - Botswana + * * `BRA` - Brazil + * * `VGB` - British Virgin Islands + * * `BRN` - Brunei Darussalam + * * `BGR` - Bulgaria + * * `BFA` - Burkina Faso + * * `BDI` - Burundi + * * `KHM` - Cambodia + * * `CMR` - Cameroon + * * `CAN` - Canada + * * `CPV` - Cape Verde + * * `CYM` - Cayman Islands + * * `CAF` - Central African Republic + * * `TCD` - Chad + * * `CIL` - Channel Islands + * * `CHL` - Chile + * * `CHN` - China + * * `HKG` - China - Hong Kong + * * `MAC` - China - Macao + * * `COL` - Colombia + * * `COM` - Comoros + * * `COG` - Congo + * * `COK` - Cook Islands + * * `CRI` - Costa Rica + * * `CIV` - Cote d'Ivoire + * * `HRV` - Croatia + * * `CUB` - Cuba + * * `CYP` - Cyprus + * * `CZE` - Czech Republic + * * `PRK` - Democratic People's Republic of Korea + * * `COD` - Democratic Republic of the Congo + * * `DNK` - Denmark + * * `DJI` - Djibouti + * * `DMA` - Dominica + * * `DOM` - Dominican Republic + * * `ECU` - Ecuador + * * `EGY` - Egypt + * * `SLV` - El Salvador + * * `GNQ` - Equatorial Guinea + * * `ERI` - Eritrea + * * `EST` - Estonia + * * `ETH` - Ethiopia + * * `FRO` - Faeroe Islands + * * `FLK` - Falkland Islands (Malvinas) + * * `FJI` - Fiji + * * `FIN` - Finland + * * `FRA` - France + * * `GUF` - French Guiana + * * `PYF` - French Polynesia + * * `GAB` - Gabon + * * `GMB` - Gambia + * * `GEO` - Georgia + * * `DEU` - Germany + * * `GHA` - Ghana + * * `GIB` - Gibraltar + * * `GRC` - Greece + * * `GRL` - Greenland + * * `GRD` - Grenada + * * `GLP` - Guadeloupe + * * `GUM` - Guam + * * `GTM` - Guatemala + * * `GGY` - Guernsey + * * `GIN` - Guinea + * * `GNB` - Guinea-Bissau + * * `GUY` - Guyana + * * `HTI` - Haiti + * * `VAT` - Holy See (Vatican City) + * * `HND` - Honduras + * * `HUN` - Hungary + * * `ISL` - Iceland + * * `IND` - India + * * `IDN` - Indonesia + * * `IRN` - Iran + * * `IRQ` - Iraq + * * `IRL` - Ireland + * * `IMN` - Isle of Man + * * `ISR` - Israel + * * `ITA` - Italy + * * `JAM` - Jamaica + * * `JPN` - Japan + * * `JEY` - Jersey + * * `JOR` - Jordan + * * `KAZ` - Kazakhstan + * * `KEN` - Kenya + * * `KIR` - Kiribati + * * `KWT` - Kuwait + * * `KGZ` - Kyrgyzstan + * * `LAO` - Lao People's Democratic Republic + * * `LVA` - Latvia + * * `LBN` - Lebanon + * * `LSO` - Lesotho + * * `LBR` - Liberia + * * `LBY` - Libyan Arab Jamahiriya + * * `LIE` - Liechtenstein + * * `LTU` - Lithuania + * * `LUX` - Luxembourg + * * `MKD` - Macedonia + * * `MDG` - Madagascar + * * `MWI` - Malawi + * * `MYS` - Malaysia + * * `MDV` - Maldives + * * `MLI` - Mali + * * `MLT` - Malta + * * `MHL` - Marshall Islands + * * `MTQ` - Martinique + * * `MRT` - Mauritania + * * `MUS` - Mauritius + * * `MYT` - Mayotte + * * `MEX` - Mexico + * * `FSM` - Micronesia, Federated States of + * * `MCO` - Monaco + * * `MNG` - Mongolia + * * `MNE` - Montenegro + * * `MSR` - Montserrat + * * `MAR` - Morocco + * * `MOZ` - Mozambique + * * `MMR` - Myanmar + * * `NAM` - Namibia + * * `NRU` - Nauru + * * `NPL` - Nepal + * * `NLD` - Netherlands + * * `ANT` - Netherlands Antilles + * * `NCL` - New Caledonia + * * `NZL` - New Zealand + * * `NIC` - Nicaragua + * * `NER` - Niger + * * `NGA` - Nigeria + * * `NIU` - Niue + * * `NFK` - Norfolk Island + * * `MNP` - Northern Mariana Islands + * * `NOR` - Norway + * * `PSE` - Occupied Palestinian Territory + * * `OMN` - Oman + * * `PAK` - Pakistan + * * `PLW` - Palau + * * `PAN` - Panama + * * `PNG` - Papua New Guinea + * * `PRY` - Paraguay + * * `PER` - Peru + * * `PHL` - Philippines + * * `PCN` - Pitcairn + * * `POL` - Poland + * * `PRT` - Portugal + * * `PRI` - Puerto Rico + * * `QAT` - Qatar + * * `KOR` - Republic of Korea + * * `MDA` - Republic of Moldova + * * `REU` - Reunion + * * `ROU` - Romania + * * `RUS` - Russian Federation + * * `RWA` - Rwanda + * * `BLM` - Saint-Barthelemy + * * `SHN` - Saint Helena + * * `KNA` - Saint Kitts and Nevis + * * `LCA` - Saint Lucia + * * `MAF` - Saint-Martin (French part) + * * `SPM` - Saint Pierre and Miquelon + * * `VCT` - Saint Vincent and the Grenadines + * * `WSM` - Samoa + * * `SMR` - San Marino + * * `STP` - Sao Tome and Principe + * * `SAU` - Saudi Arabia + * * `SEN` - Senegal + * * `SRB` - Serbia + * * `SYC` - Seychelles + * * `SLE` - Sierra Leone + * * `SGP` - Singapore + * * `SVK` - Slovakia + * * `SVN` - Slovenia + * * `SLB` - Solomon Islands + * * `SOM` - Somalia + * * `ZAF` - South Africa + * * `ESP` - Spain + * * `LKA` - Sri Lanka + * * `SDN` - Sudan + * * `SUR` - Suriname + * * `SJM` - Svalbard and Jan Mayen Islands + * * `SWZ` - Swaziland + * * `SWE` - Sweden + * * `CHE` - Switzerland + * * `SYR` - Syrian Arab Republic + * * `TJK` - Tajikistan + * * `THA` - Thailand + * * `TLS` - Timor-Leste + * * `TGO` - Togo + * * `TKL` - Tokelau + * * `TON` - Tonga + * * `TTO` - Trinidad and Tobago + * * `TUN` - Tunisia + * * `TUR` - Turkey + * * `TKM` - Turkmenistan + * * `TCA` - Turks and Caicos Islands + * * `TUV` - Tuvalu + * * `UGA` - Uganda + * * `UKR` - Ukraine + * * `ARE` - United Arab Emirates + * * `GBR` - United Kingdom + * * `TZA` - United Republic of Tanzania + * * `USA` - United States of America + * * `VIR` - United States Virgin Islands + * * `URY` - Uruguay + * * `UZB` - Uzbekistan + * * `VUT` - Vanuatu + * * `VEN` - Venezuela (Bolivarian Republic of) + * * `VNM` - Viet Nam + * * `WLF` - Wallis and Futuna Islands + * * `ESH` - Western Sahara + * * `YEM` - Yemen + * * `ZMB` - Zambia + * * `ZWE` - Zimbabwe + */ + country?: CountryEnum; + /** + * State of origin, if applicable + */ + origin_state?: string | null; + /** + * City of origin, if known + */ + origin_city?: string | null; + is_homebrew?: boolean; + /** + * Brewer's home page + */ + url?: string | null; + /** + * A short description of the brewer + */ + description?: string | null; +}; + +export type PatchedBeverageRequest = { + /** + * Name of the beverage, such as "Potrero Pale". + */ + name?: string; + producer_id?: number; + beverage_type?: BeverageTypeEnum; + /** + * Beverage style within type, eg "Pale Ale", "Pinot Noir". + */ + style?: string | null; + /** + * Free-form description of the beverage. + */ + description?: string | null; + /** + * Date of production, for wines or special/seasonal editions + */ + vintage_year?: string | null; + /** + * ABV Percentage + * Alcohol by volume, as percentage (0.0-100.0). + */ + abv_percent?: number | null; + /** + * Calories per mL of beverage. + */ + calories_per_ml?: number | null; + /** + * Carbohydrates per mL of beverage. + */ + carbs_per_ml?: number | null; + /** + * Color (Hex Value) + * Approximate beverage color + */ + color_hex?: string; + /** + * Original gravity (beer only). + */ + original_gravity?: number | null; + /** + * Final gravity (beer only). + */ + specific_gravity?: number | null; + /** + * SRM Value + * Standard Reference Method value (beer only). + */ + srm?: number | null; + /** + * IBUs + * International Bittering Units value (beer only). + */ + ibu?: number | null; + /** + * Star rating for beverage (0: worst, 5: best) + */ + star_rating?: number | null; + /** + * Untappd.com resource ID (beer only). + */ + untappd_beer_id?: number | null; +}; + +export type PatchedControllerRequest = { + /** + * Identifying name for this device; must be unique. + */ + name?: string; + /** + * Type of controller (optional). + */ + model_name?: string | null; + /** + * Serial number (optional). + */ + serial_number?: string | null; +}; + +export type PatchedDeviceRequest = { + name?: string; + /** + * Time the device was created. + */ + created_time?: string; +}; + +export type PatchedDrinkUpdateRequestRequest = { + shout?: string; + volume_ml?: number; +}; + +export type PatchedFlowMeterRequest = { + /** + * Controller-specific data port name for this meter. + */ + port_name?: string; + /** + * Flow meter pulses per mL of fluid. Common values: 2.724 (FT330-RJ), 5.4 (SF800) + */ + ticks_per_ml?: number; +}; + +export type PatchedFlowToggleRequest = { + /** + * Controller-specific data port name for this toggle. + */ + port_name?: string; +}; + +export type PatchedKegRequest = { + /** + * Keg container type, used to initialize keg's full volume + * + * * `mini` - Mini Keg (5 L) + * * `corny-2_5-gal` - Corny Keg (2.5 gal) + * * `corny-3-gal` - Corny Keg (3.0 gal) + * * `corny` - Corny Keg (5 gal) + * * `sixth` - Sixth Barrel (5.17 gal) + * * `euro-30-liter` - European DIN (30 L) + * * `euro-half` - European Half Barrel (50 L) + * * `quarter` - Quarter Barrel (7.75 gal) + * * `euro` - European Full Barrel (100 L) + * * `half-barrel` - Half Barrel (15.5 gal) + * * `other` - Other + */ + keg_type?: KegTypeEnum; + /** + * Full volume of this Keg; usually set automatically from keg_type. + */ + full_volume_ml?: number; + /** + * User-visible description of the Keg. + */ + description?: string | null; + /** + * Private notes about this keg, viewable only by admins. + */ + notes?: string | null; +}; + +export type PatchedKegTapRequest = { + /** + * The display name for this tap, for example, "Main Tap". + */ + name?: string; + /** + * Private notes about this tap. + */ + notes?: string | null; + /** + * Position relative to other taps when sorting (0=first). + */ + sort_order?: number; +}; + +export type PatchedNotificationSettingsRequest = { + /** + * Notification backend (dotted path) for these settings. + */ + backend?: string; + /** + * Sent when a keg is activated. + */ + keg_tapped?: boolean; + /** + * Sent when a new drinking session starts. + */ + session_started?: boolean; + /** + * Sent when a keg becomes low. + */ + keg_volume_low?: boolean; + /** + * Sent when a keg has been taken offline. + */ + keg_ended?: boolean; +}; + +export type PatchedPluginDataRequest = { + /** + * Plugin short name + */ + plugin_name?: string; + key?: string; + value?: unknown; +}; + +export type PatchedProfileUpdateRequestRequest = { + display_name?: string; +}; + +/** + * Admin-editable site settings, covering the old settings forms. + */ +export type PatchedSiteSettingsRequest = { + /** + * The title of this site. + */ + title?: string; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Who can join this Kegbot from the web site? + * + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ + registration_mode?: RegistrationModeEnum; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Maximum time, in minutes, that a session may be idle (no pours) before it is considered to be finished. Recommended value is 180. + */ + session_timeout_minutes?: number; + /** + * Set to your Google Analytics ID to enable tracking. Example: UA-XXXX-y + */ + google_analytics_id?: string | null; + /** + * Backend email configuration + */ + email_config?: string; +}; + +export type PatchedThermoSensorRequest = { + raw_name?: string; + nice_name?: string; +}; + +export type Picture = { + readonly id: number; + readonly resized_url: string; + readonly resized_png_url: string; + readonly thumbnail_url: string; + readonly thumbnail_png_url: string; + caption: string; + /** + * User that owns/uploaded this picture + */ + readonly user_id: number | null; + /** + * Keg this picture was taken with, if any. + */ + readonly keg_id: number | null; + /** + * Session this picture was taken with, if any. + */ + readonly session_id: number | null; +}; + +export type PictureRequest = { + caption: string; +}; + +export type PictureUploadRequestRequest = { + image: Blob | File; + caption?: string; +}; + +export type PluginData = { + /** + * Plugin short name + */ + plugin_name: string; + key: string; + value: unknown; +}; + +export type PluginDataRequest = { + /** + * Plugin short name + */ + plugin_name: string; + key: string; + value: unknown; +}; + +export type PluginInfo = { + short_name: string; + name: string; +}; + +/** + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ +export type PrivacyEnum = 'public' | 'members' | 'staff'; + +export type RegisterRequestRequest = { + username: string; + email: string; + password: string; + invite_code?: string; +}; + +/** + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ +export type RegistrationModeEnum = 'public' | 'member-invite-only' | 'staff-invite-only'; + +/** + * The session archive tree: which dates have sessions. + */ +export type SessionDirectory = { + years: Array; +}; + +export type SessionDirectoryMonth = { + month: number; + days: Array; + count: number; +}; + +export type SessionDirectoryYear = { + year: number; + months: Array; + count: number; +}; + +export type SetPasswordRequestRequest = { + password: string; +}; + +export type SetupAdminUserRequestRequest = { + username: string; + email: string; + password: string; +}; + +/** + * The subset of site settings collected during the setup wizard. + */ +export type SetupSiteSettingsRequest = { + /** + * The title of this site. + */ + title?: string; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; +}; + +/** + * The subset of site settings collected during the setup wizard. + */ +export type SetupSiteSettingsRequestRequest = { + /** + * The title of this site. + */ + title?: string; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; +}; + +export type SetupStatus = { + need_setup: boolean; + need_upgrade: boolean; + installed_version: string | null; + current_version: string; +}; + +/** + * The privacy-safe subset of site settings, embedded in the boot payload. + * + * Unlike `KegbotSiteSerializer`, this contains no data derived from pours + * (no stats): it is served to anonymous users regardless of site privacy, + * since the frontend needs it to render the login and interstitial screens. + */ +export type SiteConfig = { + readonly server_version: string | null; + /** + * The title of this site. + */ + title?: string; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Who can join this Kegbot from the web site? + * + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ + registration_mode?: RegistrationModeEnum; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Maximum time, in minutes, that a session may be idle (no pours) before it is considered to be finished. Recommended value is 180. + */ + session_timeout_minutes?: number; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; + /** + * Set to your Google Analytics ID to enable tracking. Example: UA-XXXX-y + */ + google_analytics_id?: string | null; + background_image: Picture; +}; + +/** + * Admin-editable site settings, covering the old settings forms. + */ +export type SiteSettings = { + readonly name: string; + readonly server_version: string | null; + /** + * True if the site has completed setup. + */ + readonly is_setup: boolean; + /** + * The title of this site. + */ + title?: string; + /** + * Who can view Kegbot data? + * + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ + privacy?: PrivacyEnum; + /** + * Who can join this Kegbot from the web site? + * + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ + registration_mode?: RegistrationModeEnum; + /** + * Enable and show features related to volume sensing. + */ + enable_sensing?: boolean; + /** + * Enable user pour tracking. + */ + enable_users?: boolean; + /** + * Unit system to use when displaying volumetric data. + * + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ + volume_display_units?: VolumeDisplayUnitsEnum; + /** + * Unit system to use when displaying temperature data. + * + * * `f` - Fahrenheit + * * `c` - Celsius + */ + temperature_display_units?: TemperatureDisplayUnitsEnum; + /** + * Time zone for this system. + * + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ + timezone?: TimezoneEnum; + /** + * Maximum time, in minutes, that a session may be idle (no pours) before it is considered to be finished. Recommended value is 180. + */ + session_timeout_minutes?: number; + /** + * Set to your Google Analytics ID to enable tracking. Example: UA-XXXX-y + */ + google_analytics_id?: string | null; + /** + * Backend email configuration + */ + email_config?: string; + background_image: Picture; +}; + +export type Stats = { + time?: string; + stats: unknown; + readonly drink_id: number; + readonly user_id: number | null; + readonly keg_id: number | null; + readonly session_id: number | null; +}; + +export type SystemEvent = { + readonly id: number; + /** + * Type of event. + * + * * `drink_poured` - Drink poured + * * `session_started` - Session started + * * `session_joined` - User joined session + * * `keg_tapped` - Keg tapped + * * `keg_volume_low` - Keg volume low + * * `keg_ended` - Keg ended + */ + kind: SystemEventKindEnum; + /** + * Time of the event. + */ + time: string; + drink: Drink; + user: User; + keg: Keg; + session: DrinkingSession; +}; + +/** + * * `drink_poured` - Drink poured + * * `session_started` - Session started + * * `session_joined` - User joined session + * * `keg_tapped` - Keg tapped + * * `keg_volume_low` - Keg volume low + * * `keg_ended` - Keg ended + */ +export type SystemEventKindEnum = 'drink_poured' | 'session_started' | 'session_joined' | 'keg_tapped' | 'keg_volume_low' | 'keg_ended'; + +/** + * A summarized system status status, with the most common "current status" data. + */ +export type SystemStatus = { + site: KegbotSite; + taps: Array; + events: Array; +}; + +export type TapAttachKegRequestRequest = { + keg_id: number; +}; + +export type TapConnectMeterRequestRequest = { + meter_id: number | null; +}; + +export type TapConnectThermoRequestRequest = { + thermo_sensor_id: number | null; +}; + +export type TapConnectToggleRequestRequest = { + toggle_id: number | null; +}; + +export type TapRecordDrinkRequestRequest = { + volume_ml: number; + username?: string; + pour_time?: string | null; + duration?: number; + shout?: string; + spilled?: boolean; +}; + +/** + * * `f` - Fahrenheit + * * `c` - Celsius + */ +export type TemperatureDisplayUnitsEnum = 'f' | 'c'; + +export type ThermoSensor = { + readonly id: number; + raw_name: string; + nice_name: string; +}; + +export type ThermoSensorRequest = { + raw_name: string; + nice_name: string; +}; + +export type Thermolog = { + time: string; + temp: number; + readonly sensor_id: number; +}; + +/** + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ +export type TimezoneEnum = 'Africa/Abidjan' | 'Africa/Accra' | 'Africa/Addis_Ababa' | 'Africa/Algiers' | 'Africa/Asmara' | 'Africa/Asmera' | 'Africa/Bamako' | 'Africa/Bangui' | 'Africa/Banjul' | 'Africa/Bissau' | 'Africa/Blantyre' | 'Africa/Brazzaville' | 'Africa/Bujumbura' | 'Africa/Cairo' | 'Africa/Casablanca' | 'Africa/Ceuta' | 'Africa/Conakry' | 'Africa/Dakar' | 'Africa/Dar_es_Salaam' | 'Africa/Djibouti' | 'Africa/Douala' | 'Africa/El_Aaiun' | 'Africa/Freetown' | 'Africa/Gaborone' | 'Africa/Harare' | 'Africa/Johannesburg' | 'Africa/Juba' | 'Africa/Kampala' | 'Africa/Khartoum' | 'Africa/Kigali' | 'Africa/Kinshasa' | 'Africa/Lagos' | 'Africa/Libreville' | 'Africa/Lome' | 'Africa/Luanda' | 'Africa/Lubumbashi' | 'Africa/Lusaka' | 'Africa/Malabo' | 'Africa/Maputo' | 'Africa/Maseru' | 'Africa/Mbabane' | 'Africa/Mogadishu' | 'Africa/Monrovia' | 'Africa/Nairobi' | 'Africa/Ndjamena' | 'Africa/Niamey' | 'Africa/Nouakchott' | 'Africa/Ouagadougou' | 'Africa/Porto-Novo' | 'Africa/Sao_Tome' | 'Africa/Timbuktu' | 'Africa/Tripoli' | 'Africa/Tunis' | 'Africa/Windhoek' | 'America/Adak' | 'America/Anchorage' | 'America/Anguilla' | 'America/Antigua' | 'America/Araguaina' | 'America/Argentina/Buenos_Aires' | 'America/Argentina/Catamarca' | 'America/Argentina/ComodRivadavia' | 'America/Argentina/Cordoba' | 'America/Argentina/Jujuy' | 'America/Argentina/La_Rioja' | 'America/Argentina/Mendoza' | 'America/Argentina/Rio_Gallegos' | 'America/Argentina/Salta' | 'America/Argentina/San_Juan' | 'America/Argentina/San_Luis' | 'America/Argentina/Tucuman' | 'America/Argentina/Ushuaia' | 'America/Aruba' | 'America/Asuncion' | 'America/Atikokan' | 'America/Atka' | 'America/Bahia' | 'America/Bahia_Banderas' | 'America/Barbados' | 'America/Belem' | 'America/Belize' | 'America/Blanc-Sablon' | 'America/Boa_Vista' | 'America/Bogota' | 'America/Boise' | 'America/Buenos_Aires' | 'America/Cambridge_Bay' | 'America/Campo_Grande' | 'America/Cancun' | 'America/Caracas' | 'America/Catamarca' | 'America/Cayenne' | 'America/Cayman' | 'America/Chicago' | 'America/Chihuahua' | 'America/Ciudad_Juarez' | 'America/Coral_Harbour' | 'America/Cordoba' | 'America/Costa_Rica' | 'America/Coyhaique' | 'America/Creston' | 'America/Cuiaba' | 'America/Curacao' | 'America/Danmarkshavn' | 'America/Dawson' | 'America/Dawson_Creek' | 'America/Denver' | 'America/Detroit' | 'America/Dominica' | 'America/Edmonton' | 'America/Eirunepe' | 'America/El_Salvador' | 'America/Ensenada' | 'America/Fort_Nelson' | 'America/Fort_Wayne' | 'America/Fortaleza' | 'America/Glace_Bay' | 'America/Godthab' | 'America/Goose_Bay' | 'America/Grand_Turk' | 'America/Grenada' | 'America/Guadeloupe' | 'America/Guatemala' | 'America/Guayaquil' | 'America/Guyana' | 'America/Halifax' | 'America/Havana' | 'America/Hermosillo' | 'America/Indiana/Indianapolis' | 'America/Indiana/Knox' | 'America/Indiana/Marengo' | 'America/Indiana/Petersburg' | 'America/Indiana/Tell_City' | 'America/Indiana/Vevay' | 'America/Indiana/Vincennes' | 'America/Indiana/Winamac' | 'America/Indianapolis' | 'America/Inuvik' | 'America/Iqaluit' | 'America/Jamaica' | 'America/Jujuy' | 'America/Juneau' | 'America/Kentucky/Louisville' | 'America/Kentucky/Monticello' | 'America/Knox_IN' | 'America/Kralendijk' | 'America/La_Paz' | 'America/Lima' | 'America/Los_Angeles' | 'America/Louisville' | 'America/Lower_Princes' | 'America/Maceio' | 'America/Managua' | 'America/Manaus' | 'America/Marigot' | 'America/Martinique' | 'America/Matamoros' | 'America/Mazatlan' | 'America/Mendoza' | 'America/Menominee' | 'America/Merida' | 'America/Metlakatla' | 'America/Mexico_City' | 'America/Miquelon' | 'America/Moncton' | 'America/Monterrey' | 'America/Montevideo' | 'America/Montreal' | 'America/Montserrat' | 'America/Nassau' | 'America/New_York' | 'America/Nipigon' | 'America/Nome' | 'America/Noronha' | 'America/North_Dakota/Beulah' | 'America/North_Dakota/Center' | 'America/North_Dakota/New_Salem' | 'America/Nuuk' | 'America/Ojinaga' | 'America/Panama' | 'America/Pangnirtung' | 'America/Paramaribo' | 'America/Phoenix' | 'America/Port-au-Prince' | 'America/Port_of_Spain' | 'America/Porto_Acre' | 'America/Porto_Velho' | 'America/Puerto_Rico' | 'America/Punta_Arenas' | 'America/Rainy_River' | 'America/Rankin_Inlet' | 'America/Recife' | 'America/Regina' | 'America/Resolute' | 'America/Rio_Branco' | 'America/Rosario' | 'America/Santa_Isabel' | 'America/Santarem' | 'America/Santiago' | 'America/Santo_Domingo' | 'America/Sao_Paulo' | 'America/Scoresbysund' | 'America/Shiprock' | 'America/Sitka' | 'America/St_Barthelemy' | 'America/St_Johns' | 'America/St_Kitts' | 'America/St_Lucia' | 'America/St_Thomas' | 'America/St_Vincent' | 'America/Swift_Current' | 'America/Tegucigalpa' | 'America/Thule' | 'America/Thunder_Bay' | 'America/Tijuana' | 'America/Toronto' | 'America/Tortola' | 'America/Vancouver' | 'America/Virgin' | 'America/Whitehorse' | 'America/Winnipeg' | 'America/Yakutat' | 'America/Yellowknife' | 'Antarctica/Casey' | 'Antarctica/Davis' | 'Antarctica/DumontDUrville' | 'Antarctica/Macquarie' | 'Antarctica/Mawson' | 'Antarctica/McMurdo' | 'Antarctica/Palmer' | 'Antarctica/Rothera' | 'Antarctica/South_Pole' | 'Antarctica/Syowa' | 'Antarctica/Troll' | 'Antarctica/Vostok' | 'Arctic/Longyearbyen' | 'Asia/Aden' | 'Asia/Almaty' | 'Asia/Amman' | 'Asia/Anadyr' | 'Asia/Aqtau' | 'Asia/Aqtobe' | 'Asia/Ashgabat' | 'Asia/Ashkhabad' | 'Asia/Atyrau' | 'Asia/Baghdad' | 'Asia/Bahrain' | 'Asia/Baku' | 'Asia/Bangkok' | 'Asia/Barnaul' | 'Asia/Beirut' | 'Asia/Bishkek' | 'Asia/Brunei' | 'Asia/Calcutta' | 'Asia/Chita' | 'Asia/Choibalsan' | 'Asia/Chongqing' | 'Asia/Chungking' | 'Asia/Colombo' | 'Asia/Dacca' | 'Asia/Damascus' | 'Asia/Dhaka' | 'Asia/Dili' | 'Asia/Dubai' | 'Asia/Dushanbe' | 'Asia/Famagusta' | 'Asia/Gaza' | 'Asia/Harbin' | 'Asia/Hebron' | 'Asia/Ho_Chi_Minh' | 'Asia/Hong_Kong' | 'Asia/Hovd' | 'Asia/Irkutsk' | 'Asia/Istanbul' | 'Asia/Jakarta' | 'Asia/Jayapura' | 'Asia/Jerusalem' | 'Asia/Kabul' | 'Asia/Kamchatka' | 'Asia/Karachi' | 'Asia/Kashgar' | 'Asia/Kathmandu' | 'Asia/Katmandu' | 'Asia/Khandyga' | 'Asia/Kolkata' | 'Asia/Krasnoyarsk' | 'Asia/Kuala_Lumpur' | 'Asia/Kuching' | 'Asia/Kuwait' | 'Asia/Macao' | 'Asia/Macau' | 'Asia/Magadan' | 'Asia/Makassar' | 'Asia/Manila' | 'Asia/Muscat' | 'Asia/Nicosia' | 'Asia/Novokuznetsk' | 'Asia/Novosibirsk' | 'Asia/Omsk' | 'Asia/Oral' | 'Asia/Phnom_Penh' | 'Asia/Pontianak' | 'Asia/Pyongyang' | 'Asia/Qatar' | 'Asia/Qostanay' | 'Asia/Qyzylorda' | 'Asia/Rangoon' | 'Asia/Riyadh' | 'Asia/Saigon' | 'Asia/Sakhalin' | 'Asia/Samarkand' | 'Asia/Seoul' | 'Asia/Shanghai' | 'Asia/Singapore' | 'Asia/Srednekolymsk' | 'Asia/Taipei' | 'Asia/Tashkent' | 'Asia/Tbilisi' | 'Asia/Tehran' | 'Asia/Tel_Aviv' | 'Asia/Thimbu' | 'Asia/Thimphu' | 'Asia/Tokyo' | 'Asia/Tomsk' | 'Asia/Ujung_Pandang' | 'Asia/Ulaanbaatar' | 'Asia/Ulan_Bator' | 'Asia/Urumqi' | 'Asia/Ust-Nera' | 'Asia/Vientiane' | 'Asia/Vladivostok' | 'Asia/Yakutsk' | 'Asia/Yangon' | 'Asia/Yekaterinburg' | 'Asia/Yerevan' | 'Atlantic/Azores' | 'Atlantic/Bermuda' | 'Atlantic/Canary' | 'Atlantic/Cape_Verde' | 'Atlantic/Faeroe' | 'Atlantic/Faroe' | 'Atlantic/Jan_Mayen' | 'Atlantic/Madeira' | 'Atlantic/Reykjavik' | 'Atlantic/South_Georgia' | 'Atlantic/St_Helena' | 'Atlantic/Stanley' | 'Australia/ACT' | 'Australia/Adelaide' | 'Australia/Brisbane' | 'Australia/Broken_Hill' | 'Australia/Canberra' | 'Australia/Currie' | 'Australia/Darwin' | 'Australia/Eucla' | 'Australia/Hobart' | 'Australia/LHI' | 'Australia/Lindeman' | 'Australia/Lord_Howe' | 'Australia/Melbourne' | 'Australia/NSW' | 'Australia/North' | 'Australia/Perth' | 'Australia/Queensland' | 'Australia/South' | 'Australia/Sydney' | 'Australia/Tasmania' | 'Australia/Victoria' | 'Australia/West' | 'Australia/Yancowinna' | 'Brazil/Acre' | 'Brazil/DeNoronha' | 'Brazil/East' | 'Brazil/West' | 'Canada/Atlantic' | 'Canada/Central' | 'Canada/Eastern' | 'Canada/Mountain' | 'Canada/Newfoundland' | 'Canada/Pacific' | 'Canada/Saskatchewan' | 'Canada/Yukon' | 'Chile/Continental' | 'Chile/EasterIsland' | 'Europe/Amsterdam' | 'Europe/Andorra' | 'Europe/Astrakhan' | 'Europe/Athens' | 'Europe/Belfast' | 'Europe/Belgrade' | 'Europe/Berlin' | 'Europe/Bratislava' | 'Europe/Brussels' | 'Europe/Bucharest' | 'Europe/Budapest' | 'Europe/Busingen' | 'Europe/Chisinau' | 'Europe/Copenhagen' | 'Europe/Dublin' | 'Europe/Gibraltar' | 'Europe/Guernsey' | 'Europe/Helsinki' | 'Europe/Isle_of_Man' | 'Europe/Istanbul' | 'Europe/Jersey' | 'Europe/Kaliningrad' | 'Europe/Kiev' | 'Europe/Kirov' | 'Europe/Kyiv' | 'Europe/Lisbon' | 'Europe/Ljubljana' | 'Europe/London' | 'Europe/Luxembourg' | 'Europe/Madrid' | 'Europe/Malta' | 'Europe/Mariehamn' | 'Europe/Minsk' | 'Europe/Monaco' | 'Europe/Moscow' | 'Europe/Nicosia' | 'Europe/Oslo' | 'Europe/Paris' | 'Europe/Podgorica' | 'Europe/Prague' | 'Europe/Riga' | 'Europe/Rome' | 'Europe/Samara' | 'Europe/San_Marino' | 'Europe/Sarajevo' | 'Europe/Saratov' | 'Europe/Simferopol' | 'Europe/Skopje' | 'Europe/Sofia' | 'Europe/Stockholm' | 'Europe/Tallinn' | 'Europe/Tirane' | 'Europe/Tiraspol' | 'Europe/Ulyanovsk' | 'Europe/Uzhgorod' | 'Europe/Vaduz' | 'Europe/Vatican' | 'Europe/Vienna' | 'Europe/Vilnius' | 'Europe/Volgograd' | 'Europe/Warsaw' | 'Europe/Zagreb' | 'Europe/Zaporozhye' | 'Europe/Zurich' | 'Indian/Antananarivo' | 'Indian/Chagos' | 'Indian/Christmas' | 'Indian/Cocos' | 'Indian/Comoro' | 'Indian/Kerguelen' | 'Indian/Mahe' | 'Indian/Maldives' | 'Indian/Mauritius' | 'Indian/Mayotte' | 'Indian/Reunion' | 'Mexico/BajaNorte' | 'Mexico/BajaSur' | 'Mexico/General' | 'Pacific/Apia' | 'Pacific/Auckland' | 'Pacific/Bougainville' | 'Pacific/Chatham' | 'Pacific/Chuuk' | 'Pacific/Easter' | 'Pacific/Efate' | 'Pacific/Enderbury' | 'Pacific/Fakaofo' | 'Pacific/Fiji' | 'Pacific/Funafuti' | 'Pacific/Galapagos' | 'Pacific/Gambier' | 'Pacific/Guadalcanal' | 'Pacific/Guam' | 'Pacific/Honolulu' | 'Pacific/Johnston' | 'Pacific/Kanton' | 'Pacific/Kiritimati' | 'Pacific/Kosrae' | 'Pacific/Kwajalein' | 'Pacific/Majuro' | 'Pacific/Marquesas' | 'Pacific/Midway' | 'Pacific/Nauru' | 'Pacific/Niue' | 'Pacific/Norfolk' | 'Pacific/Noumea' | 'Pacific/Pago_Pago' | 'Pacific/Palau' | 'Pacific/Pitcairn' | 'Pacific/Pohnpei' | 'Pacific/Ponape' | 'Pacific/Port_Moresby' | 'Pacific/Rarotonga' | 'Pacific/Saipan' | 'Pacific/Samoa' | 'Pacific/Tahiti' | 'Pacific/Tarawa' | 'Pacific/Tongatapu' | 'Pacific/Truk' | 'Pacific/Wake' | 'Pacific/Wallis' | 'Pacific/Yap' | 'US/Alaska' | 'US/Aleutian' | 'US/Arizona' | 'US/Central' | 'US/East-Indiana' | 'US/Eastern' | 'US/Hawaii' | 'US/Indiana-Starke' | 'US/Michigan' | 'US/Mountain' | 'US/Pacific' | 'US/Samoa' | 'UTC'; + +export type User = { + readonly id: number; + /** + * Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters + */ + username: string; + /** + * Full name, will be shown in some places instead of username + */ + display_name?: string; + /** + * Staff status + * Designates whether the user can log into this admin site. + */ + readonly is_staff: boolean; + /** + * Active + * Designates whether this user should be treated as active. Unselect this instead of deleting accounts. + */ + readonly is_active: boolean; + picture: Picture; +}; + +/** + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ +export type VolumeDisplayUnitsEnum = 'metric' | 'imperial'; + +/** + * * `beer` - Beer + * * `wine` - Wine + * * `soda` - Soda + * * `kombucha` - Kombucha + * * `other` - Other/Unknown + */ +export type BeverageTypeEnumWritable = 'beer' | 'wine' | 'soda' | 'kombucha' | 'other'; + +/** + * * `AFG` - Afghanistan + * * `ALA` - Aland Islands + * * `ALB` - Albania + * * `DZA` - Algeria + * * `ASM` - American Samoa + * * `AND` - Andorra + * * `AGO` - Angola + * * `AIA` - Anguilla + * * `ATG` - Antigua and Barbuda + * * `ARG` - Argentina + * * `ARM` - Armenia + * * `ABW` - Aruba + * * `AUS` - Australia + * * `AUT` - Austria + * * `AZE` - Azerbaijan + * * `BHS` - Bahamas + * * `BHR` - Bahrain + * * `BGD` - Bangladesh + * * `BRB` - Barbados + * * `BLR` - Belarus + * * `BEL` - Belgium + * * `BLZ` - Belize + * * `BEN` - Benin + * * `BMU` - Bermuda + * * `BTN` - Bhutan + * * `BOL` - Bolivia + * * `BIH` - Bosnia and Herzegovina + * * `BWA` - Botswana + * * `BRA` - Brazil + * * `VGB` - British Virgin Islands + * * `BRN` - Brunei Darussalam + * * `BGR` - Bulgaria + * * `BFA` - Burkina Faso + * * `BDI` - Burundi + * * `KHM` - Cambodia + * * `CMR` - Cameroon + * * `CAN` - Canada + * * `CPV` - Cape Verde + * * `CYM` - Cayman Islands + * * `CAF` - Central African Republic + * * `TCD` - Chad + * * `CIL` - Channel Islands + * * `CHL` - Chile + * * `CHN` - China + * * `HKG` - China - Hong Kong + * * `MAC` - China - Macao + * * `COL` - Colombia + * * `COM` - Comoros + * * `COG` - Congo + * * `COK` - Cook Islands + * * `CRI` - Costa Rica + * * `CIV` - Cote d'Ivoire + * * `HRV` - Croatia + * * `CUB` - Cuba + * * `CYP` - Cyprus + * * `CZE` - Czech Republic + * * `PRK` - Democratic People's Republic of Korea + * * `COD` - Democratic Republic of the Congo + * * `DNK` - Denmark + * * `DJI` - Djibouti + * * `DMA` - Dominica + * * `DOM` - Dominican Republic + * * `ECU` - Ecuador + * * `EGY` - Egypt + * * `SLV` - El Salvador + * * `GNQ` - Equatorial Guinea + * * `ERI` - Eritrea + * * `EST` - Estonia + * * `ETH` - Ethiopia + * * `FRO` - Faeroe Islands + * * `FLK` - Falkland Islands (Malvinas) + * * `FJI` - Fiji + * * `FIN` - Finland + * * `FRA` - France + * * `GUF` - French Guiana + * * `PYF` - French Polynesia + * * `GAB` - Gabon + * * `GMB` - Gambia + * * `GEO` - Georgia + * * `DEU` - Germany + * * `GHA` - Ghana + * * `GIB` - Gibraltar + * * `GRC` - Greece + * * `GRL` - Greenland + * * `GRD` - Grenada + * * `GLP` - Guadeloupe + * * `GUM` - Guam + * * `GTM` - Guatemala + * * `GGY` - Guernsey + * * `GIN` - Guinea + * * `GNB` - Guinea-Bissau + * * `GUY` - Guyana + * * `HTI` - Haiti + * * `VAT` - Holy See (Vatican City) + * * `HND` - Honduras + * * `HUN` - Hungary + * * `ISL` - Iceland + * * `IND` - India + * * `IDN` - Indonesia + * * `IRN` - Iran + * * `IRQ` - Iraq + * * `IRL` - Ireland + * * `IMN` - Isle of Man + * * `ISR` - Israel + * * `ITA` - Italy + * * `JAM` - Jamaica + * * `JPN` - Japan + * * `JEY` - Jersey + * * `JOR` - Jordan + * * `KAZ` - Kazakhstan + * * `KEN` - Kenya + * * `KIR` - Kiribati + * * `KWT` - Kuwait + * * `KGZ` - Kyrgyzstan + * * `LAO` - Lao People's Democratic Republic + * * `LVA` - Latvia + * * `LBN` - Lebanon + * * `LSO` - Lesotho + * * `LBR` - Liberia + * * `LBY` - Libyan Arab Jamahiriya + * * `LIE` - Liechtenstein + * * `LTU` - Lithuania + * * `LUX` - Luxembourg + * * `MKD` - Macedonia + * * `MDG` - Madagascar + * * `MWI` - Malawi + * * `MYS` - Malaysia + * * `MDV` - Maldives + * * `MLI` - Mali + * * `MLT` - Malta + * * `MHL` - Marshall Islands + * * `MTQ` - Martinique + * * `MRT` - Mauritania + * * `MUS` - Mauritius + * * `MYT` - Mayotte + * * `MEX` - Mexico + * * `FSM` - Micronesia, Federated States of + * * `MCO` - Monaco + * * `MNG` - Mongolia + * * `MNE` - Montenegro + * * `MSR` - Montserrat + * * `MAR` - Morocco + * * `MOZ` - Mozambique + * * `MMR` - Myanmar + * * `NAM` - Namibia + * * `NRU` - Nauru + * * `NPL` - Nepal + * * `NLD` - Netherlands + * * `ANT` - Netherlands Antilles + * * `NCL` - New Caledonia + * * `NZL` - New Zealand + * * `NIC` - Nicaragua + * * `NER` - Niger + * * `NGA` - Nigeria + * * `NIU` - Niue + * * `NFK` - Norfolk Island + * * `MNP` - Northern Mariana Islands + * * `NOR` - Norway + * * `PSE` - Occupied Palestinian Territory + * * `OMN` - Oman + * * `PAK` - Pakistan + * * `PLW` - Palau + * * `PAN` - Panama + * * `PNG` - Papua New Guinea + * * `PRY` - Paraguay + * * `PER` - Peru + * * `PHL` - Philippines + * * `PCN` - Pitcairn + * * `POL` - Poland + * * `PRT` - Portugal + * * `PRI` - Puerto Rico + * * `QAT` - Qatar + * * `KOR` - Republic of Korea + * * `MDA` - Republic of Moldova + * * `REU` - Reunion + * * `ROU` - Romania + * * `RUS` - Russian Federation + * * `RWA` - Rwanda + * * `BLM` - Saint-Barthelemy + * * `SHN` - Saint Helena + * * `KNA` - Saint Kitts and Nevis + * * `LCA` - Saint Lucia + * * `MAF` - Saint-Martin (French part) + * * `SPM` - Saint Pierre and Miquelon + * * `VCT` - Saint Vincent and the Grenadines + * * `WSM` - Samoa + * * `SMR` - San Marino + * * `STP` - Sao Tome and Principe + * * `SAU` - Saudi Arabia + * * `SEN` - Senegal + * * `SRB` - Serbia + * * `SYC` - Seychelles + * * `SLE` - Sierra Leone + * * `SGP` - Singapore + * * `SVK` - Slovakia + * * `SVN` - Slovenia + * * `SLB` - Solomon Islands + * * `SOM` - Somalia + * * `ZAF` - South Africa + * * `ESP` - Spain + * * `LKA` - Sri Lanka + * * `SDN` - Sudan + * * `SUR` - Suriname + * * `SJM` - Svalbard and Jan Mayen Islands + * * `SWZ` - Swaziland + * * `SWE` - Sweden + * * `CHE` - Switzerland + * * `SYR` - Syrian Arab Republic + * * `TJK` - Tajikistan + * * `THA` - Thailand + * * `TLS` - Timor-Leste + * * `TGO` - Togo + * * `TKL` - Tokelau + * * `TON` - Tonga + * * `TTO` - Trinidad and Tobago + * * `TUN` - Tunisia + * * `TUR` - Turkey + * * `TKM` - Turkmenistan + * * `TCA` - Turks and Caicos Islands + * * `TUV` - Tuvalu + * * `UGA` - Uganda + * * `UKR` - Ukraine + * * `ARE` - United Arab Emirates + * * `GBR` - United Kingdom + * * `TZA` - United Republic of Tanzania + * * `USA` - United States of America + * * `VIR` - United States Virgin Islands + * * `URY` - Uruguay + * * `UZB` - Uzbekistan + * * `VUT` - Vanuatu + * * `VEN` - Venezuela (Bolivarian Republic of) + * * `VNM` - Viet Nam + * * `WLF` - Wallis and Futuna Islands + * * `ESH` - Western Sahara + * * `YEM` - Yemen + * * `ZMB` - Zambia + * * `ZWE` - Zimbabwe + */ +export type CountryEnumWritable = 'AFG' | 'ALA' | 'ALB' | 'DZA' | 'ASM' | 'AND' | 'AGO' | 'AIA' | 'ATG' | 'ARG' | 'ARM' | 'ABW' | 'AUS' | 'AUT' | 'AZE' | 'BHS' | 'BHR' | 'BGD' | 'BRB' | 'BLR' | 'BEL' | 'BLZ' | 'BEN' | 'BMU' | 'BTN' | 'BOL' | 'BIH' | 'BWA' | 'BRA' | 'VGB' | 'BRN' | 'BGR' | 'BFA' | 'BDI' | 'KHM' | 'CMR' | 'CAN' | 'CPV' | 'CYM' | 'CAF' | 'TCD' | 'CIL' | 'CHL' | 'CHN' | 'HKG' | 'MAC' | 'COL' | 'COM' | 'COG' | 'COK' | 'CRI' | 'CIV' | 'HRV' | 'CUB' | 'CYP' | 'CZE' | 'PRK' | 'COD' | 'DNK' | 'DJI' | 'DMA' | 'DOM' | 'ECU' | 'EGY' | 'SLV' | 'GNQ' | 'ERI' | 'EST' | 'ETH' | 'FRO' | 'FLK' | 'FJI' | 'FIN' | 'FRA' | 'GUF' | 'PYF' | 'GAB' | 'GMB' | 'GEO' | 'DEU' | 'GHA' | 'GIB' | 'GRC' | 'GRL' | 'GRD' | 'GLP' | 'GUM' | 'GTM' | 'GGY' | 'GIN' | 'GNB' | 'GUY' | 'HTI' | 'VAT' | 'HND' | 'HUN' | 'ISL' | 'IND' | 'IDN' | 'IRN' | 'IRQ' | 'IRL' | 'IMN' | 'ISR' | 'ITA' | 'JAM' | 'JPN' | 'JEY' | 'JOR' | 'KAZ' | 'KEN' | 'KIR' | 'KWT' | 'KGZ' | 'LAO' | 'LVA' | 'LBN' | 'LSO' | 'LBR' | 'LBY' | 'LIE' | 'LTU' | 'LUX' | 'MKD' | 'MDG' | 'MWI' | 'MYS' | 'MDV' | 'MLI' | 'MLT' | 'MHL' | 'MTQ' | 'MRT' | 'MUS' | 'MYT' | 'MEX' | 'FSM' | 'MCO' | 'MNG' | 'MNE' | 'MSR' | 'MAR' | 'MOZ' | 'MMR' | 'NAM' | 'NRU' | 'NPL' | 'NLD' | 'ANT' | 'NCL' | 'NZL' | 'NIC' | 'NER' | 'NGA' | 'NIU' | 'NFK' | 'MNP' | 'NOR' | 'PSE' | 'OMN' | 'PAK' | 'PLW' | 'PAN' | 'PNG' | 'PRY' | 'PER' | 'PHL' | 'PCN' | 'POL' | 'PRT' | 'PRI' | 'QAT' | 'KOR' | 'MDA' | 'REU' | 'ROU' | 'RUS' | 'RWA' | 'BLM' | 'SHN' | 'KNA' | 'LCA' | 'MAF' | 'SPM' | 'VCT' | 'WSM' | 'SMR' | 'STP' | 'SAU' | 'SEN' | 'SRB' | 'SYC' | 'SLE' | 'SGP' | 'SVK' | 'SVN' | 'SLB' | 'SOM' | 'ZAF' | 'ESP' | 'LKA' | 'SDN' | 'SUR' | 'SJM' | 'SWZ' | 'SWE' | 'CHE' | 'SYR' | 'TJK' | 'THA' | 'TLS' | 'TGO' | 'TKL' | 'TON' | 'TTO' | 'TUN' | 'TUR' | 'TKM' | 'TCA' | 'TUV' | 'UGA' | 'UKR' | 'ARE' | 'GBR' | 'TZA' | 'USA' | 'VIR' | 'URY' | 'UZB' | 'VUT' | 'VEN' | 'VNM' | 'WLF' | 'ESH' | 'YEM' | 'ZMB' | 'ZWE'; + +/** + * * `available` - Available + * * `on_tap` - On tap + * * `finished` - Finished + */ +export type KegStatusEnumWritable = 'available' | 'on_tap' | 'finished'; + +/** + * * `mini` - Mini Keg (5 L) + * * `corny-2_5-gal` - Corny Keg (2.5 gal) + * * `corny-3-gal` - Corny Keg (3.0 gal) + * * `corny` - Corny Keg (5 gal) + * * `sixth` - Sixth Barrel (5.17 gal) + * * `euro-30-liter` - European DIN (30 L) + * * `euro-half` - European Half Barrel (50 L) + * * `quarter` - Quarter Barrel (7.75 gal) + * * `euro` - European Full Barrel (100 L) + * * `half-barrel` - Half Barrel (15.5 gal) + * * `other` - Other + */ +export type KegTypeEnumWritable = 'mini' | 'corny-2_5-gal' | 'corny-3-gal' | 'corny' | 'sixth' | 'euro-30-liter' | 'euro-half' | 'quarter' | 'euro' | 'half-barrel' | 'other'; + +/** + * * `public` - Public: Browsing does not require login + * * `members` - Members only: Must log in to browse + * * `staff` - Staff only: Only logged-in staff accounts may browse + */ +export type PrivacyEnumWritable = 'public' | 'members' | 'staff'; + +/** + * * `public` - Public: Anyone can register. + * * `member-invite-only` - Member Invite: Must be invited by an existing member. + * * `staff-invite-only` - Staff Invite Only: Must be invited by a staff member. + */ +export type RegistrationModeEnumWritable = 'public' | 'member-invite-only' | 'staff-invite-only'; + +/** + * * `drink_poured` - Drink poured + * * `session_started` - Session started + * * `session_joined` - User joined session + * * `keg_tapped` - Keg tapped + * * `keg_volume_low` - Keg volume low + * * `keg_ended` - Keg ended + */ +export type SystemEventKindEnumWritable = 'drink_poured' | 'session_started' | 'session_joined' | 'keg_tapped' | 'keg_volume_low' | 'keg_ended'; + +/** + * * `f` - Fahrenheit + * * `c` - Celsius + */ +export type TemperatureDisplayUnitsEnumWritable = 'f' | 'c'; + +/** + * * `Africa/Abidjan` - Africa/Abidjan + * * `Africa/Accra` - Africa/Accra + * * `Africa/Addis_Ababa` - Africa/Addis_Ababa + * * `Africa/Algiers` - Africa/Algiers + * * `Africa/Asmara` - Africa/Asmara + * * `Africa/Asmera` - Africa/Asmera + * * `Africa/Bamako` - Africa/Bamako + * * `Africa/Bangui` - Africa/Bangui + * * `Africa/Banjul` - Africa/Banjul + * * `Africa/Bissau` - Africa/Bissau + * * `Africa/Blantyre` - Africa/Blantyre + * * `Africa/Brazzaville` - Africa/Brazzaville + * * `Africa/Bujumbura` - Africa/Bujumbura + * * `Africa/Cairo` - Africa/Cairo + * * `Africa/Casablanca` - Africa/Casablanca + * * `Africa/Ceuta` - Africa/Ceuta + * * `Africa/Conakry` - Africa/Conakry + * * `Africa/Dakar` - Africa/Dakar + * * `Africa/Dar_es_Salaam` - Africa/Dar_es_Salaam + * * `Africa/Djibouti` - Africa/Djibouti + * * `Africa/Douala` - Africa/Douala + * * `Africa/El_Aaiun` - Africa/El_Aaiun + * * `Africa/Freetown` - Africa/Freetown + * * `Africa/Gaborone` - Africa/Gaborone + * * `Africa/Harare` - Africa/Harare + * * `Africa/Johannesburg` - Africa/Johannesburg + * * `Africa/Juba` - Africa/Juba + * * `Africa/Kampala` - Africa/Kampala + * * `Africa/Khartoum` - Africa/Khartoum + * * `Africa/Kigali` - Africa/Kigali + * * `Africa/Kinshasa` - Africa/Kinshasa + * * `Africa/Lagos` - Africa/Lagos + * * `Africa/Libreville` - Africa/Libreville + * * `Africa/Lome` - Africa/Lome + * * `Africa/Luanda` - Africa/Luanda + * * `Africa/Lubumbashi` - Africa/Lubumbashi + * * `Africa/Lusaka` - Africa/Lusaka + * * `Africa/Malabo` - Africa/Malabo + * * `Africa/Maputo` - Africa/Maputo + * * `Africa/Maseru` - Africa/Maseru + * * `Africa/Mbabane` - Africa/Mbabane + * * `Africa/Mogadishu` - Africa/Mogadishu + * * `Africa/Monrovia` - Africa/Monrovia + * * `Africa/Nairobi` - Africa/Nairobi + * * `Africa/Ndjamena` - Africa/Ndjamena + * * `Africa/Niamey` - Africa/Niamey + * * `Africa/Nouakchott` - Africa/Nouakchott + * * `Africa/Ouagadougou` - Africa/Ouagadougou + * * `Africa/Porto-Novo` - Africa/Porto-Novo + * * `Africa/Sao_Tome` - Africa/Sao_Tome + * * `Africa/Timbuktu` - Africa/Timbuktu + * * `Africa/Tripoli` - Africa/Tripoli + * * `Africa/Tunis` - Africa/Tunis + * * `Africa/Windhoek` - Africa/Windhoek + * * `America/Adak` - America/Adak + * * `America/Anchorage` - America/Anchorage + * * `America/Anguilla` - America/Anguilla + * * `America/Antigua` - America/Antigua + * * `America/Araguaina` - America/Araguaina + * * `America/Argentina/Buenos_Aires` - America/Argentina/Buenos_Aires + * * `America/Argentina/Catamarca` - America/Argentina/Catamarca + * * `America/Argentina/ComodRivadavia` - America/Argentina/ComodRivadavia + * * `America/Argentina/Cordoba` - America/Argentina/Cordoba + * * `America/Argentina/Jujuy` - America/Argentina/Jujuy + * * `America/Argentina/La_Rioja` - America/Argentina/La_Rioja + * * `America/Argentina/Mendoza` - America/Argentina/Mendoza + * * `America/Argentina/Rio_Gallegos` - America/Argentina/Rio_Gallegos + * * `America/Argentina/Salta` - America/Argentina/Salta + * * `America/Argentina/San_Juan` - America/Argentina/San_Juan + * * `America/Argentina/San_Luis` - America/Argentina/San_Luis + * * `America/Argentina/Tucuman` - America/Argentina/Tucuman + * * `America/Argentina/Ushuaia` - America/Argentina/Ushuaia + * * `America/Aruba` - America/Aruba + * * `America/Asuncion` - America/Asuncion + * * `America/Atikokan` - America/Atikokan + * * `America/Atka` - America/Atka + * * `America/Bahia` - America/Bahia + * * `America/Bahia_Banderas` - America/Bahia_Banderas + * * `America/Barbados` - America/Barbados + * * `America/Belem` - America/Belem + * * `America/Belize` - America/Belize + * * `America/Blanc-Sablon` - America/Blanc-Sablon + * * `America/Boa_Vista` - America/Boa_Vista + * * `America/Bogota` - America/Bogota + * * `America/Boise` - America/Boise + * * `America/Buenos_Aires` - America/Buenos_Aires + * * `America/Cambridge_Bay` - America/Cambridge_Bay + * * `America/Campo_Grande` - America/Campo_Grande + * * `America/Cancun` - America/Cancun + * * `America/Caracas` - America/Caracas + * * `America/Catamarca` - America/Catamarca + * * `America/Cayenne` - America/Cayenne + * * `America/Cayman` - America/Cayman + * * `America/Chicago` - America/Chicago + * * `America/Chihuahua` - America/Chihuahua + * * `America/Ciudad_Juarez` - America/Ciudad_Juarez + * * `America/Coral_Harbour` - America/Coral_Harbour + * * `America/Cordoba` - America/Cordoba + * * `America/Costa_Rica` - America/Costa_Rica + * * `America/Coyhaique` - America/Coyhaique + * * `America/Creston` - America/Creston + * * `America/Cuiaba` - America/Cuiaba + * * `America/Curacao` - America/Curacao + * * `America/Danmarkshavn` - America/Danmarkshavn + * * `America/Dawson` - America/Dawson + * * `America/Dawson_Creek` - America/Dawson_Creek + * * `America/Denver` - America/Denver + * * `America/Detroit` - America/Detroit + * * `America/Dominica` - America/Dominica + * * `America/Edmonton` - America/Edmonton + * * `America/Eirunepe` - America/Eirunepe + * * `America/El_Salvador` - America/El_Salvador + * * `America/Ensenada` - America/Ensenada + * * `America/Fort_Nelson` - America/Fort_Nelson + * * `America/Fort_Wayne` - America/Fort_Wayne + * * `America/Fortaleza` - America/Fortaleza + * * `America/Glace_Bay` - America/Glace_Bay + * * `America/Godthab` - America/Godthab + * * `America/Goose_Bay` - America/Goose_Bay + * * `America/Grand_Turk` - America/Grand_Turk + * * `America/Grenada` - America/Grenada + * * `America/Guadeloupe` - America/Guadeloupe + * * `America/Guatemala` - America/Guatemala + * * `America/Guayaquil` - America/Guayaquil + * * `America/Guyana` - America/Guyana + * * `America/Halifax` - America/Halifax + * * `America/Havana` - America/Havana + * * `America/Hermosillo` - America/Hermosillo + * * `America/Indiana/Indianapolis` - America/Indiana/Indianapolis + * * `America/Indiana/Knox` - America/Indiana/Knox + * * `America/Indiana/Marengo` - America/Indiana/Marengo + * * `America/Indiana/Petersburg` - America/Indiana/Petersburg + * * `America/Indiana/Tell_City` - America/Indiana/Tell_City + * * `America/Indiana/Vevay` - America/Indiana/Vevay + * * `America/Indiana/Vincennes` - America/Indiana/Vincennes + * * `America/Indiana/Winamac` - America/Indiana/Winamac + * * `America/Indianapolis` - America/Indianapolis + * * `America/Inuvik` - America/Inuvik + * * `America/Iqaluit` - America/Iqaluit + * * `America/Jamaica` - America/Jamaica + * * `America/Jujuy` - America/Jujuy + * * `America/Juneau` - America/Juneau + * * `America/Kentucky/Louisville` - America/Kentucky/Louisville + * * `America/Kentucky/Monticello` - America/Kentucky/Monticello + * * `America/Knox_IN` - America/Knox_IN + * * `America/Kralendijk` - America/Kralendijk + * * `America/La_Paz` - America/La_Paz + * * `America/Lima` - America/Lima + * * `America/Los_Angeles` - America/Los_Angeles + * * `America/Louisville` - America/Louisville + * * `America/Lower_Princes` - America/Lower_Princes + * * `America/Maceio` - America/Maceio + * * `America/Managua` - America/Managua + * * `America/Manaus` - America/Manaus + * * `America/Marigot` - America/Marigot + * * `America/Martinique` - America/Martinique + * * `America/Matamoros` - America/Matamoros + * * `America/Mazatlan` - America/Mazatlan + * * `America/Mendoza` - America/Mendoza + * * `America/Menominee` - America/Menominee + * * `America/Merida` - America/Merida + * * `America/Metlakatla` - America/Metlakatla + * * `America/Mexico_City` - America/Mexico_City + * * `America/Miquelon` - America/Miquelon + * * `America/Moncton` - America/Moncton + * * `America/Monterrey` - America/Monterrey + * * `America/Montevideo` - America/Montevideo + * * `America/Montreal` - America/Montreal + * * `America/Montserrat` - America/Montserrat + * * `America/Nassau` - America/Nassau + * * `America/New_York` - America/New_York + * * `America/Nipigon` - America/Nipigon + * * `America/Nome` - America/Nome + * * `America/Noronha` - America/Noronha + * * `America/North_Dakota/Beulah` - America/North_Dakota/Beulah + * * `America/North_Dakota/Center` - America/North_Dakota/Center + * * `America/North_Dakota/New_Salem` - America/North_Dakota/New_Salem + * * `America/Nuuk` - America/Nuuk + * * `America/Ojinaga` - America/Ojinaga + * * `America/Panama` - America/Panama + * * `America/Pangnirtung` - America/Pangnirtung + * * `America/Paramaribo` - America/Paramaribo + * * `America/Phoenix` - America/Phoenix + * * `America/Port-au-Prince` - America/Port-au-Prince + * * `America/Port_of_Spain` - America/Port_of_Spain + * * `America/Porto_Acre` - America/Porto_Acre + * * `America/Porto_Velho` - America/Porto_Velho + * * `America/Puerto_Rico` - America/Puerto_Rico + * * `America/Punta_Arenas` - America/Punta_Arenas + * * `America/Rainy_River` - America/Rainy_River + * * `America/Rankin_Inlet` - America/Rankin_Inlet + * * `America/Recife` - America/Recife + * * `America/Regina` - America/Regina + * * `America/Resolute` - America/Resolute + * * `America/Rio_Branco` - America/Rio_Branco + * * `America/Rosario` - America/Rosario + * * `America/Santa_Isabel` - America/Santa_Isabel + * * `America/Santarem` - America/Santarem + * * `America/Santiago` - America/Santiago + * * `America/Santo_Domingo` - America/Santo_Domingo + * * `America/Sao_Paulo` - America/Sao_Paulo + * * `America/Scoresbysund` - America/Scoresbysund + * * `America/Shiprock` - America/Shiprock + * * `America/Sitka` - America/Sitka + * * `America/St_Barthelemy` - America/St_Barthelemy + * * `America/St_Johns` - America/St_Johns + * * `America/St_Kitts` - America/St_Kitts + * * `America/St_Lucia` - America/St_Lucia + * * `America/St_Thomas` - America/St_Thomas + * * `America/St_Vincent` - America/St_Vincent + * * `America/Swift_Current` - America/Swift_Current + * * `America/Tegucigalpa` - America/Tegucigalpa + * * `America/Thule` - America/Thule + * * `America/Thunder_Bay` - America/Thunder_Bay + * * `America/Tijuana` - America/Tijuana + * * `America/Toronto` - America/Toronto + * * `America/Tortola` - America/Tortola + * * `America/Vancouver` - America/Vancouver + * * `America/Virgin` - America/Virgin + * * `America/Whitehorse` - America/Whitehorse + * * `America/Winnipeg` - America/Winnipeg + * * `America/Yakutat` - America/Yakutat + * * `America/Yellowknife` - America/Yellowknife + * * `Antarctica/Casey` - Antarctica/Casey + * * `Antarctica/Davis` - Antarctica/Davis + * * `Antarctica/DumontDUrville` - Antarctica/DumontDUrville + * * `Antarctica/Macquarie` - Antarctica/Macquarie + * * `Antarctica/Mawson` - Antarctica/Mawson + * * `Antarctica/McMurdo` - Antarctica/McMurdo + * * `Antarctica/Palmer` - Antarctica/Palmer + * * `Antarctica/Rothera` - Antarctica/Rothera + * * `Antarctica/South_Pole` - Antarctica/South_Pole + * * `Antarctica/Syowa` - Antarctica/Syowa + * * `Antarctica/Troll` - Antarctica/Troll + * * `Antarctica/Vostok` - Antarctica/Vostok + * * `Arctic/Longyearbyen` - Arctic/Longyearbyen + * * `Asia/Aden` - Asia/Aden + * * `Asia/Almaty` - Asia/Almaty + * * `Asia/Amman` - Asia/Amman + * * `Asia/Anadyr` - Asia/Anadyr + * * `Asia/Aqtau` - Asia/Aqtau + * * `Asia/Aqtobe` - Asia/Aqtobe + * * `Asia/Ashgabat` - Asia/Ashgabat + * * `Asia/Ashkhabad` - Asia/Ashkhabad + * * `Asia/Atyrau` - Asia/Atyrau + * * `Asia/Baghdad` - Asia/Baghdad + * * `Asia/Bahrain` - Asia/Bahrain + * * `Asia/Baku` - Asia/Baku + * * `Asia/Bangkok` - Asia/Bangkok + * * `Asia/Barnaul` - Asia/Barnaul + * * `Asia/Beirut` - Asia/Beirut + * * `Asia/Bishkek` - Asia/Bishkek + * * `Asia/Brunei` - Asia/Brunei + * * `Asia/Calcutta` - Asia/Calcutta + * * `Asia/Chita` - Asia/Chita + * * `Asia/Choibalsan` - Asia/Choibalsan + * * `Asia/Chongqing` - Asia/Chongqing + * * `Asia/Chungking` - Asia/Chungking + * * `Asia/Colombo` - Asia/Colombo + * * `Asia/Dacca` - Asia/Dacca + * * `Asia/Damascus` - Asia/Damascus + * * `Asia/Dhaka` - Asia/Dhaka + * * `Asia/Dili` - Asia/Dili + * * `Asia/Dubai` - Asia/Dubai + * * `Asia/Dushanbe` - Asia/Dushanbe + * * `Asia/Famagusta` - Asia/Famagusta + * * `Asia/Gaza` - Asia/Gaza + * * `Asia/Harbin` - Asia/Harbin + * * `Asia/Hebron` - Asia/Hebron + * * `Asia/Ho_Chi_Minh` - Asia/Ho_Chi_Minh + * * `Asia/Hong_Kong` - Asia/Hong_Kong + * * `Asia/Hovd` - Asia/Hovd + * * `Asia/Irkutsk` - Asia/Irkutsk + * * `Asia/Istanbul` - Asia/Istanbul + * * `Asia/Jakarta` - Asia/Jakarta + * * `Asia/Jayapura` - Asia/Jayapura + * * `Asia/Jerusalem` - Asia/Jerusalem + * * `Asia/Kabul` - Asia/Kabul + * * `Asia/Kamchatka` - Asia/Kamchatka + * * `Asia/Karachi` - Asia/Karachi + * * `Asia/Kashgar` - Asia/Kashgar + * * `Asia/Kathmandu` - Asia/Kathmandu + * * `Asia/Katmandu` - Asia/Katmandu + * * `Asia/Khandyga` - Asia/Khandyga + * * `Asia/Kolkata` - Asia/Kolkata + * * `Asia/Krasnoyarsk` - Asia/Krasnoyarsk + * * `Asia/Kuala_Lumpur` - Asia/Kuala_Lumpur + * * `Asia/Kuching` - Asia/Kuching + * * `Asia/Kuwait` - Asia/Kuwait + * * `Asia/Macao` - Asia/Macao + * * `Asia/Macau` - Asia/Macau + * * `Asia/Magadan` - Asia/Magadan + * * `Asia/Makassar` - Asia/Makassar + * * `Asia/Manila` - Asia/Manila + * * `Asia/Muscat` - Asia/Muscat + * * `Asia/Nicosia` - Asia/Nicosia + * * `Asia/Novokuznetsk` - Asia/Novokuznetsk + * * `Asia/Novosibirsk` - Asia/Novosibirsk + * * `Asia/Omsk` - Asia/Omsk + * * `Asia/Oral` - Asia/Oral + * * `Asia/Phnom_Penh` - Asia/Phnom_Penh + * * `Asia/Pontianak` - Asia/Pontianak + * * `Asia/Pyongyang` - Asia/Pyongyang + * * `Asia/Qatar` - Asia/Qatar + * * `Asia/Qostanay` - Asia/Qostanay + * * `Asia/Qyzylorda` - Asia/Qyzylorda + * * `Asia/Rangoon` - Asia/Rangoon + * * `Asia/Riyadh` - Asia/Riyadh + * * `Asia/Saigon` - Asia/Saigon + * * `Asia/Sakhalin` - Asia/Sakhalin + * * `Asia/Samarkand` - Asia/Samarkand + * * `Asia/Seoul` - Asia/Seoul + * * `Asia/Shanghai` - Asia/Shanghai + * * `Asia/Singapore` - Asia/Singapore + * * `Asia/Srednekolymsk` - Asia/Srednekolymsk + * * `Asia/Taipei` - Asia/Taipei + * * `Asia/Tashkent` - Asia/Tashkent + * * `Asia/Tbilisi` - Asia/Tbilisi + * * `Asia/Tehran` - Asia/Tehran + * * `Asia/Tel_Aviv` - Asia/Tel_Aviv + * * `Asia/Thimbu` - Asia/Thimbu + * * `Asia/Thimphu` - Asia/Thimphu + * * `Asia/Tokyo` - Asia/Tokyo + * * `Asia/Tomsk` - Asia/Tomsk + * * `Asia/Ujung_Pandang` - Asia/Ujung_Pandang + * * `Asia/Ulaanbaatar` - Asia/Ulaanbaatar + * * `Asia/Ulan_Bator` - Asia/Ulan_Bator + * * `Asia/Urumqi` - Asia/Urumqi + * * `Asia/Ust-Nera` - Asia/Ust-Nera + * * `Asia/Vientiane` - Asia/Vientiane + * * `Asia/Vladivostok` - Asia/Vladivostok + * * `Asia/Yakutsk` - Asia/Yakutsk + * * `Asia/Yangon` - Asia/Yangon + * * `Asia/Yekaterinburg` - Asia/Yekaterinburg + * * `Asia/Yerevan` - Asia/Yerevan + * * `Atlantic/Azores` - Atlantic/Azores + * * `Atlantic/Bermuda` - Atlantic/Bermuda + * * `Atlantic/Canary` - Atlantic/Canary + * * `Atlantic/Cape_Verde` - Atlantic/Cape_Verde + * * `Atlantic/Faeroe` - Atlantic/Faeroe + * * `Atlantic/Faroe` - Atlantic/Faroe + * * `Atlantic/Jan_Mayen` - Atlantic/Jan_Mayen + * * `Atlantic/Madeira` - Atlantic/Madeira + * * `Atlantic/Reykjavik` - Atlantic/Reykjavik + * * `Atlantic/South_Georgia` - Atlantic/South_Georgia + * * `Atlantic/St_Helena` - Atlantic/St_Helena + * * `Atlantic/Stanley` - Atlantic/Stanley + * * `Australia/ACT` - Australia/ACT + * * `Australia/Adelaide` - Australia/Adelaide + * * `Australia/Brisbane` - Australia/Brisbane + * * `Australia/Broken_Hill` - Australia/Broken_Hill + * * `Australia/Canberra` - Australia/Canberra + * * `Australia/Currie` - Australia/Currie + * * `Australia/Darwin` - Australia/Darwin + * * `Australia/Eucla` - Australia/Eucla + * * `Australia/Hobart` - Australia/Hobart + * * `Australia/LHI` - Australia/LHI + * * `Australia/Lindeman` - Australia/Lindeman + * * `Australia/Lord_Howe` - Australia/Lord_Howe + * * `Australia/Melbourne` - Australia/Melbourne + * * `Australia/NSW` - Australia/NSW + * * `Australia/North` - Australia/North + * * `Australia/Perth` - Australia/Perth + * * `Australia/Queensland` - Australia/Queensland + * * `Australia/South` - Australia/South + * * `Australia/Sydney` - Australia/Sydney + * * `Australia/Tasmania` - Australia/Tasmania + * * `Australia/Victoria` - Australia/Victoria + * * `Australia/West` - Australia/West + * * `Australia/Yancowinna` - Australia/Yancowinna + * * `Brazil/Acre` - Brazil/Acre + * * `Brazil/DeNoronha` - Brazil/DeNoronha + * * `Brazil/East` - Brazil/East + * * `Brazil/West` - Brazil/West + * * `Canada/Atlantic` - Canada/Atlantic + * * `Canada/Central` - Canada/Central + * * `Canada/Eastern` - Canada/Eastern + * * `Canada/Mountain` - Canada/Mountain + * * `Canada/Newfoundland` - Canada/Newfoundland + * * `Canada/Pacific` - Canada/Pacific + * * `Canada/Saskatchewan` - Canada/Saskatchewan + * * `Canada/Yukon` - Canada/Yukon + * * `Chile/Continental` - Chile/Continental + * * `Chile/EasterIsland` - Chile/EasterIsland + * * `Europe/Amsterdam` - Europe/Amsterdam + * * `Europe/Andorra` - Europe/Andorra + * * `Europe/Astrakhan` - Europe/Astrakhan + * * `Europe/Athens` - Europe/Athens + * * `Europe/Belfast` - Europe/Belfast + * * `Europe/Belgrade` - Europe/Belgrade + * * `Europe/Berlin` - Europe/Berlin + * * `Europe/Bratislava` - Europe/Bratislava + * * `Europe/Brussels` - Europe/Brussels + * * `Europe/Bucharest` - Europe/Bucharest + * * `Europe/Budapest` - Europe/Budapest + * * `Europe/Busingen` - Europe/Busingen + * * `Europe/Chisinau` - Europe/Chisinau + * * `Europe/Copenhagen` - Europe/Copenhagen + * * `Europe/Dublin` - Europe/Dublin + * * `Europe/Gibraltar` - Europe/Gibraltar + * * `Europe/Guernsey` - Europe/Guernsey + * * `Europe/Helsinki` - Europe/Helsinki + * * `Europe/Isle_of_Man` - Europe/Isle_of_Man + * * `Europe/Istanbul` - Europe/Istanbul + * * `Europe/Jersey` - Europe/Jersey + * * `Europe/Kaliningrad` - Europe/Kaliningrad + * * `Europe/Kiev` - Europe/Kiev + * * `Europe/Kirov` - Europe/Kirov + * * `Europe/Kyiv` - Europe/Kyiv + * * `Europe/Lisbon` - Europe/Lisbon + * * `Europe/Ljubljana` - Europe/Ljubljana + * * `Europe/London` - Europe/London + * * `Europe/Luxembourg` - Europe/Luxembourg + * * `Europe/Madrid` - Europe/Madrid + * * `Europe/Malta` - Europe/Malta + * * `Europe/Mariehamn` - Europe/Mariehamn + * * `Europe/Minsk` - Europe/Minsk + * * `Europe/Monaco` - Europe/Monaco + * * `Europe/Moscow` - Europe/Moscow + * * `Europe/Nicosia` - Europe/Nicosia + * * `Europe/Oslo` - Europe/Oslo + * * `Europe/Paris` - Europe/Paris + * * `Europe/Podgorica` - Europe/Podgorica + * * `Europe/Prague` - Europe/Prague + * * `Europe/Riga` - Europe/Riga + * * `Europe/Rome` - Europe/Rome + * * `Europe/Samara` - Europe/Samara + * * `Europe/San_Marino` - Europe/San_Marino + * * `Europe/Sarajevo` - Europe/Sarajevo + * * `Europe/Saratov` - Europe/Saratov + * * `Europe/Simferopol` - Europe/Simferopol + * * `Europe/Skopje` - Europe/Skopje + * * `Europe/Sofia` - Europe/Sofia + * * `Europe/Stockholm` - Europe/Stockholm + * * `Europe/Tallinn` - Europe/Tallinn + * * `Europe/Tirane` - Europe/Tirane + * * `Europe/Tiraspol` - Europe/Tiraspol + * * `Europe/Ulyanovsk` - Europe/Ulyanovsk + * * `Europe/Uzhgorod` - Europe/Uzhgorod + * * `Europe/Vaduz` - Europe/Vaduz + * * `Europe/Vatican` - Europe/Vatican + * * `Europe/Vienna` - Europe/Vienna + * * `Europe/Vilnius` - Europe/Vilnius + * * `Europe/Volgograd` - Europe/Volgograd + * * `Europe/Warsaw` - Europe/Warsaw + * * `Europe/Zagreb` - Europe/Zagreb + * * `Europe/Zaporozhye` - Europe/Zaporozhye + * * `Europe/Zurich` - Europe/Zurich + * * `Indian/Antananarivo` - Indian/Antananarivo + * * `Indian/Chagos` - Indian/Chagos + * * `Indian/Christmas` - Indian/Christmas + * * `Indian/Cocos` - Indian/Cocos + * * `Indian/Comoro` - Indian/Comoro + * * `Indian/Kerguelen` - Indian/Kerguelen + * * `Indian/Mahe` - Indian/Mahe + * * `Indian/Maldives` - Indian/Maldives + * * `Indian/Mauritius` - Indian/Mauritius + * * `Indian/Mayotte` - Indian/Mayotte + * * `Indian/Reunion` - Indian/Reunion + * * `Mexico/BajaNorte` - Mexico/BajaNorte + * * `Mexico/BajaSur` - Mexico/BajaSur + * * `Mexico/General` - Mexico/General + * * `Pacific/Apia` - Pacific/Apia + * * `Pacific/Auckland` - Pacific/Auckland + * * `Pacific/Bougainville` - Pacific/Bougainville + * * `Pacific/Chatham` - Pacific/Chatham + * * `Pacific/Chuuk` - Pacific/Chuuk + * * `Pacific/Easter` - Pacific/Easter + * * `Pacific/Efate` - Pacific/Efate + * * `Pacific/Enderbury` - Pacific/Enderbury + * * `Pacific/Fakaofo` - Pacific/Fakaofo + * * `Pacific/Fiji` - Pacific/Fiji + * * `Pacific/Funafuti` - Pacific/Funafuti + * * `Pacific/Galapagos` - Pacific/Galapagos + * * `Pacific/Gambier` - Pacific/Gambier + * * `Pacific/Guadalcanal` - Pacific/Guadalcanal + * * `Pacific/Guam` - Pacific/Guam + * * `Pacific/Honolulu` - Pacific/Honolulu + * * `Pacific/Johnston` - Pacific/Johnston + * * `Pacific/Kanton` - Pacific/Kanton + * * `Pacific/Kiritimati` - Pacific/Kiritimati + * * `Pacific/Kosrae` - Pacific/Kosrae + * * `Pacific/Kwajalein` - Pacific/Kwajalein + * * `Pacific/Majuro` - Pacific/Majuro + * * `Pacific/Marquesas` - Pacific/Marquesas + * * `Pacific/Midway` - Pacific/Midway + * * `Pacific/Nauru` - Pacific/Nauru + * * `Pacific/Niue` - Pacific/Niue + * * `Pacific/Norfolk` - Pacific/Norfolk + * * `Pacific/Noumea` - Pacific/Noumea + * * `Pacific/Pago_Pago` - Pacific/Pago_Pago + * * `Pacific/Palau` - Pacific/Palau + * * `Pacific/Pitcairn` - Pacific/Pitcairn + * * `Pacific/Pohnpei` - Pacific/Pohnpei + * * `Pacific/Ponape` - Pacific/Ponape + * * `Pacific/Port_Moresby` - Pacific/Port_Moresby + * * `Pacific/Rarotonga` - Pacific/Rarotonga + * * `Pacific/Saipan` - Pacific/Saipan + * * `Pacific/Samoa` - Pacific/Samoa + * * `Pacific/Tahiti` - Pacific/Tahiti + * * `Pacific/Tarawa` - Pacific/Tarawa + * * `Pacific/Tongatapu` - Pacific/Tongatapu + * * `Pacific/Truk` - Pacific/Truk + * * `Pacific/Wake` - Pacific/Wake + * * `Pacific/Wallis` - Pacific/Wallis + * * `Pacific/Yap` - Pacific/Yap + * * `US/Alaska` - US/Alaska + * * `US/Aleutian` - US/Aleutian + * * `US/Arizona` - US/Arizona + * * `US/Central` - US/Central + * * `US/East-Indiana` - US/East-Indiana + * * `US/Eastern` - US/Eastern + * * `US/Hawaii` - US/Hawaii + * * `US/Indiana-Starke` - US/Indiana-Starke + * * `US/Michigan` - US/Michigan + * * `US/Mountain` - US/Mountain + * * `US/Pacific` - US/Pacific + * * `US/Samoa` - US/Samoa + * * `UTC` - UTC + */ +export type TimezoneEnumWritable = 'Africa/Abidjan' | 'Africa/Accra' | 'Africa/Addis_Ababa' | 'Africa/Algiers' | 'Africa/Asmara' | 'Africa/Asmera' | 'Africa/Bamako' | 'Africa/Bangui' | 'Africa/Banjul' | 'Africa/Bissau' | 'Africa/Blantyre' | 'Africa/Brazzaville' | 'Africa/Bujumbura' | 'Africa/Cairo' | 'Africa/Casablanca' | 'Africa/Ceuta' | 'Africa/Conakry' | 'Africa/Dakar' | 'Africa/Dar_es_Salaam' | 'Africa/Djibouti' | 'Africa/Douala' | 'Africa/El_Aaiun' | 'Africa/Freetown' | 'Africa/Gaborone' | 'Africa/Harare' | 'Africa/Johannesburg' | 'Africa/Juba' | 'Africa/Kampala' | 'Africa/Khartoum' | 'Africa/Kigali' | 'Africa/Kinshasa' | 'Africa/Lagos' | 'Africa/Libreville' | 'Africa/Lome' | 'Africa/Luanda' | 'Africa/Lubumbashi' | 'Africa/Lusaka' | 'Africa/Malabo' | 'Africa/Maputo' | 'Africa/Maseru' | 'Africa/Mbabane' | 'Africa/Mogadishu' | 'Africa/Monrovia' | 'Africa/Nairobi' | 'Africa/Ndjamena' | 'Africa/Niamey' | 'Africa/Nouakchott' | 'Africa/Ouagadougou' | 'Africa/Porto-Novo' | 'Africa/Sao_Tome' | 'Africa/Timbuktu' | 'Africa/Tripoli' | 'Africa/Tunis' | 'Africa/Windhoek' | 'America/Adak' | 'America/Anchorage' | 'America/Anguilla' | 'America/Antigua' | 'America/Araguaina' | 'America/Argentina/Buenos_Aires' | 'America/Argentina/Catamarca' | 'America/Argentina/ComodRivadavia' | 'America/Argentina/Cordoba' | 'America/Argentina/Jujuy' | 'America/Argentina/La_Rioja' | 'America/Argentina/Mendoza' | 'America/Argentina/Rio_Gallegos' | 'America/Argentina/Salta' | 'America/Argentina/San_Juan' | 'America/Argentina/San_Luis' | 'America/Argentina/Tucuman' | 'America/Argentina/Ushuaia' | 'America/Aruba' | 'America/Asuncion' | 'America/Atikokan' | 'America/Atka' | 'America/Bahia' | 'America/Bahia_Banderas' | 'America/Barbados' | 'America/Belem' | 'America/Belize' | 'America/Blanc-Sablon' | 'America/Boa_Vista' | 'America/Bogota' | 'America/Boise' | 'America/Buenos_Aires' | 'America/Cambridge_Bay' | 'America/Campo_Grande' | 'America/Cancun' | 'America/Caracas' | 'America/Catamarca' | 'America/Cayenne' | 'America/Cayman' | 'America/Chicago' | 'America/Chihuahua' | 'America/Ciudad_Juarez' | 'America/Coral_Harbour' | 'America/Cordoba' | 'America/Costa_Rica' | 'America/Coyhaique' | 'America/Creston' | 'America/Cuiaba' | 'America/Curacao' | 'America/Danmarkshavn' | 'America/Dawson' | 'America/Dawson_Creek' | 'America/Denver' | 'America/Detroit' | 'America/Dominica' | 'America/Edmonton' | 'America/Eirunepe' | 'America/El_Salvador' | 'America/Ensenada' | 'America/Fort_Nelson' | 'America/Fort_Wayne' | 'America/Fortaleza' | 'America/Glace_Bay' | 'America/Godthab' | 'America/Goose_Bay' | 'America/Grand_Turk' | 'America/Grenada' | 'America/Guadeloupe' | 'America/Guatemala' | 'America/Guayaquil' | 'America/Guyana' | 'America/Halifax' | 'America/Havana' | 'America/Hermosillo' | 'America/Indiana/Indianapolis' | 'America/Indiana/Knox' | 'America/Indiana/Marengo' | 'America/Indiana/Petersburg' | 'America/Indiana/Tell_City' | 'America/Indiana/Vevay' | 'America/Indiana/Vincennes' | 'America/Indiana/Winamac' | 'America/Indianapolis' | 'America/Inuvik' | 'America/Iqaluit' | 'America/Jamaica' | 'America/Jujuy' | 'America/Juneau' | 'America/Kentucky/Louisville' | 'America/Kentucky/Monticello' | 'America/Knox_IN' | 'America/Kralendijk' | 'America/La_Paz' | 'America/Lima' | 'America/Los_Angeles' | 'America/Louisville' | 'America/Lower_Princes' | 'America/Maceio' | 'America/Managua' | 'America/Manaus' | 'America/Marigot' | 'America/Martinique' | 'America/Matamoros' | 'America/Mazatlan' | 'America/Mendoza' | 'America/Menominee' | 'America/Merida' | 'America/Metlakatla' | 'America/Mexico_City' | 'America/Miquelon' | 'America/Moncton' | 'America/Monterrey' | 'America/Montevideo' | 'America/Montreal' | 'America/Montserrat' | 'America/Nassau' | 'America/New_York' | 'America/Nipigon' | 'America/Nome' | 'America/Noronha' | 'America/North_Dakota/Beulah' | 'America/North_Dakota/Center' | 'America/North_Dakota/New_Salem' | 'America/Nuuk' | 'America/Ojinaga' | 'America/Panama' | 'America/Pangnirtung' | 'America/Paramaribo' | 'America/Phoenix' | 'America/Port-au-Prince' | 'America/Port_of_Spain' | 'America/Porto_Acre' | 'America/Porto_Velho' | 'America/Puerto_Rico' | 'America/Punta_Arenas' | 'America/Rainy_River' | 'America/Rankin_Inlet' | 'America/Recife' | 'America/Regina' | 'America/Resolute' | 'America/Rio_Branco' | 'America/Rosario' | 'America/Santa_Isabel' | 'America/Santarem' | 'America/Santiago' | 'America/Santo_Domingo' | 'America/Sao_Paulo' | 'America/Scoresbysund' | 'America/Shiprock' | 'America/Sitka' | 'America/St_Barthelemy' | 'America/St_Johns' | 'America/St_Kitts' | 'America/St_Lucia' | 'America/St_Thomas' | 'America/St_Vincent' | 'America/Swift_Current' | 'America/Tegucigalpa' | 'America/Thule' | 'America/Thunder_Bay' | 'America/Tijuana' | 'America/Toronto' | 'America/Tortola' | 'America/Vancouver' | 'America/Virgin' | 'America/Whitehorse' | 'America/Winnipeg' | 'America/Yakutat' | 'America/Yellowknife' | 'Antarctica/Casey' | 'Antarctica/Davis' | 'Antarctica/DumontDUrville' | 'Antarctica/Macquarie' | 'Antarctica/Mawson' | 'Antarctica/McMurdo' | 'Antarctica/Palmer' | 'Antarctica/Rothera' | 'Antarctica/South_Pole' | 'Antarctica/Syowa' | 'Antarctica/Troll' | 'Antarctica/Vostok' | 'Arctic/Longyearbyen' | 'Asia/Aden' | 'Asia/Almaty' | 'Asia/Amman' | 'Asia/Anadyr' | 'Asia/Aqtau' | 'Asia/Aqtobe' | 'Asia/Ashgabat' | 'Asia/Ashkhabad' | 'Asia/Atyrau' | 'Asia/Baghdad' | 'Asia/Bahrain' | 'Asia/Baku' | 'Asia/Bangkok' | 'Asia/Barnaul' | 'Asia/Beirut' | 'Asia/Bishkek' | 'Asia/Brunei' | 'Asia/Calcutta' | 'Asia/Chita' | 'Asia/Choibalsan' | 'Asia/Chongqing' | 'Asia/Chungking' | 'Asia/Colombo' | 'Asia/Dacca' | 'Asia/Damascus' | 'Asia/Dhaka' | 'Asia/Dili' | 'Asia/Dubai' | 'Asia/Dushanbe' | 'Asia/Famagusta' | 'Asia/Gaza' | 'Asia/Harbin' | 'Asia/Hebron' | 'Asia/Ho_Chi_Minh' | 'Asia/Hong_Kong' | 'Asia/Hovd' | 'Asia/Irkutsk' | 'Asia/Istanbul' | 'Asia/Jakarta' | 'Asia/Jayapura' | 'Asia/Jerusalem' | 'Asia/Kabul' | 'Asia/Kamchatka' | 'Asia/Karachi' | 'Asia/Kashgar' | 'Asia/Kathmandu' | 'Asia/Katmandu' | 'Asia/Khandyga' | 'Asia/Kolkata' | 'Asia/Krasnoyarsk' | 'Asia/Kuala_Lumpur' | 'Asia/Kuching' | 'Asia/Kuwait' | 'Asia/Macao' | 'Asia/Macau' | 'Asia/Magadan' | 'Asia/Makassar' | 'Asia/Manila' | 'Asia/Muscat' | 'Asia/Nicosia' | 'Asia/Novokuznetsk' | 'Asia/Novosibirsk' | 'Asia/Omsk' | 'Asia/Oral' | 'Asia/Phnom_Penh' | 'Asia/Pontianak' | 'Asia/Pyongyang' | 'Asia/Qatar' | 'Asia/Qostanay' | 'Asia/Qyzylorda' | 'Asia/Rangoon' | 'Asia/Riyadh' | 'Asia/Saigon' | 'Asia/Sakhalin' | 'Asia/Samarkand' | 'Asia/Seoul' | 'Asia/Shanghai' | 'Asia/Singapore' | 'Asia/Srednekolymsk' | 'Asia/Taipei' | 'Asia/Tashkent' | 'Asia/Tbilisi' | 'Asia/Tehran' | 'Asia/Tel_Aviv' | 'Asia/Thimbu' | 'Asia/Thimphu' | 'Asia/Tokyo' | 'Asia/Tomsk' | 'Asia/Ujung_Pandang' | 'Asia/Ulaanbaatar' | 'Asia/Ulan_Bator' | 'Asia/Urumqi' | 'Asia/Ust-Nera' | 'Asia/Vientiane' | 'Asia/Vladivostok' | 'Asia/Yakutsk' | 'Asia/Yangon' | 'Asia/Yekaterinburg' | 'Asia/Yerevan' | 'Atlantic/Azores' | 'Atlantic/Bermuda' | 'Atlantic/Canary' | 'Atlantic/Cape_Verde' | 'Atlantic/Faeroe' | 'Atlantic/Faroe' | 'Atlantic/Jan_Mayen' | 'Atlantic/Madeira' | 'Atlantic/Reykjavik' | 'Atlantic/South_Georgia' | 'Atlantic/St_Helena' | 'Atlantic/Stanley' | 'Australia/ACT' | 'Australia/Adelaide' | 'Australia/Brisbane' | 'Australia/Broken_Hill' | 'Australia/Canberra' | 'Australia/Currie' | 'Australia/Darwin' | 'Australia/Eucla' | 'Australia/Hobart' | 'Australia/LHI' | 'Australia/Lindeman' | 'Australia/Lord_Howe' | 'Australia/Melbourne' | 'Australia/NSW' | 'Australia/North' | 'Australia/Perth' | 'Australia/Queensland' | 'Australia/South' | 'Australia/Sydney' | 'Australia/Tasmania' | 'Australia/Victoria' | 'Australia/West' | 'Australia/Yancowinna' | 'Brazil/Acre' | 'Brazil/DeNoronha' | 'Brazil/East' | 'Brazil/West' | 'Canada/Atlantic' | 'Canada/Central' | 'Canada/Eastern' | 'Canada/Mountain' | 'Canada/Newfoundland' | 'Canada/Pacific' | 'Canada/Saskatchewan' | 'Canada/Yukon' | 'Chile/Continental' | 'Chile/EasterIsland' | 'Europe/Amsterdam' | 'Europe/Andorra' | 'Europe/Astrakhan' | 'Europe/Athens' | 'Europe/Belfast' | 'Europe/Belgrade' | 'Europe/Berlin' | 'Europe/Bratislava' | 'Europe/Brussels' | 'Europe/Bucharest' | 'Europe/Budapest' | 'Europe/Busingen' | 'Europe/Chisinau' | 'Europe/Copenhagen' | 'Europe/Dublin' | 'Europe/Gibraltar' | 'Europe/Guernsey' | 'Europe/Helsinki' | 'Europe/Isle_of_Man' | 'Europe/Istanbul' | 'Europe/Jersey' | 'Europe/Kaliningrad' | 'Europe/Kiev' | 'Europe/Kirov' | 'Europe/Kyiv' | 'Europe/Lisbon' | 'Europe/Ljubljana' | 'Europe/London' | 'Europe/Luxembourg' | 'Europe/Madrid' | 'Europe/Malta' | 'Europe/Mariehamn' | 'Europe/Minsk' | 'Europe/Monaco' | 'Europe/Moscow' | 'Europe/Nicosia' | 'Europe/Oslo' | 'Europe/Paris' | 'Europe/Podgorica' | 'Europe/Prague' | 'Europe/Riga' | 'Europe/Rome' | 'Europe/Samara' | 'Europe/San_Marino' | 'Europe/Sarajevo' | 'Europe/Saratov' | 'Europe/Simferopol' | 'Europe/Skopje' | 'Europe/Sofia' | 'Europe/Stockholm' | 'Europe/Tallinn' | 'Europe/Tirane' | 'Europe/Tiraspol' | 'Europe/Ulyanovsk' | 'Europe/Uzhgorod' | 'Europe/Vaduz' | 'Europe/Vatican' | 'Europe/Vienna' | 'Europe/Vilnius' | 'Europe/Volgograd' | 'Europe/Warsaw' | 'Europe/Zagreb' | 'Europe/Zaporozhye' | 'Europe/Zurich' | 'Indian/Antananarivo' | 'Indian/Chagos' | 'Indian/Christmas' | 'Indian/Cocos' | 'Indian/Comoro' | 'Indian/Kerguelen' | 'Indian/Mahe' | 'Indian/Maldives' | 'Indian/Mauritius' | 'Indian/Mayotte' | 'Indian/Reunion' | 'Mexico/BajaNorte' | 'Mexico/BajaSur' | 'Mexico/General' | 'Pacific/Apia' | 'Pacific/Auckland' | 'Pacific/Bougainville' | 'Pacific/Chatham' | 'Pacific/Chuuk' | 'Pacific/Easter' | 'Pacific/Efate' | 'Pacific/Enderbury' | 'Pacific/Fakaofo' | 'Pacific/Fiji' | 'Pacific/Funafuti' | 'Pacific/Galapagos' | 'Pacific/Gambier' | 'Pacific/Guadalcanal' | 'Pacific/Guam' | 'Pacific/Honolulu' | 'Pacific/Johnston' | 'Pacific/Kanton' | 'Pacific/Kiritimati' | 'Pacific/Kosrae' | 'Pacific/Kwajalein' | 'Pacific/Majuro' | 'Pacific/Marquesas' | 'Pacific/Midway' | 'Pacific/Nauru' | 'Pacific/Niue' | 'Pacific/Norfolk' | 'Pacific/Noumea' | 'Pacific/Pago_Pago' | 'Pacific/Palau' | 'Pacific/Pitcairn' | 'Pacific/Pohnpei' | 'Pacific/Ponape' | 'Pacific/Port_Moresby' | 'Pacific/Rarotonga' | 'Pacific/Saipan' | 'Pacific/Samoa' | 'Pacific/Tahiti' | 'Pacific/Tarawa' | 'Pacific/Tongatapu' | 'Pacific/Truk' | 'Pacific/Wake' | 'Pacific/Wallis' | 'Pacific/Yap' | 'US/Alaska' | 'US/Aleutian' | 'US/Arizona' | 'US/Central' | 'US/East-Indiana' | 'US/Eastern' | 'US/Hawaii' | 'US/Indiana-Starke' | 'US/Michigan' | 'US/Mountain' | 'US/Pacific' | 'US/Samoa' | 'UTC'; + +/** + * * `metric` - Metric (mL, L) + * * `imperial` - Imperial (oz, pint) + */ +export type VolumeDisplayUnitsEnumWritable = 'metric' | 'imperial'; + +export type AccountActivateCreateData = { + body: ActivateAccountRequestRequest; + path?: never; + query?: never; + url: '/api/account/activate'; +}; + +export type AccountActivateCreateResponses = { + 200: CurrentUser; +}; + +export type AccountActivateCreateResponse = AccountActivateCreateResponses[keyof AccountActivateCreateResponses]; + +export type AccountConfirmEmailCreateData = { + body: ConfirmEmailRequestRequest; + path?: never; + query?: never; + url: '/api/account/confirm-email'; +}; + +export type AccountConfirmEmailCreateResponses = { + 200: CurrentUser; +}; + +export type AccountConfirmEmailCreateResponse = AccountConfirmEmailCreateResponses[keyof AccountConfirmEmailCreateResponses]; + +export type AccountEmailCreateData = { + body: EmailChangeRequestRequest; + path?: never; + query?: never; + url: '/api/account/email'; +}; + +export type AccountEmailCreateResponses = { + 200: boolean; +}; + +export type AccountEmailCreateResponse = AccountEmailCreateResponses[keyof AccountEmailCreateResponses]; + +export type AccountMugshotCreateData = { + body: PictureUploadRequestRequest; + path?: never; + query?: never; + url: '/api/account/mugshot'; +}; + +export type AccountMugshotCreateResponses = { + 200: CurrentUser; +}; + +export type AccountMugshotCreateResponse = AccountMugshotCreateResponses[keyof AccountMugshotCreateResponses]; + +export type AccountPasswordCreateData = { + body: PasswordChangeRequestRequest; + path?: never; + query?: never; + url: '/api/account/password'; +}; + +export type AccountPasswordCreateResponses = { + 200: boolean; +}; + +export type AccountPasswordCreateResponse = AccountPasswordCreateResponses[keyof AccountPasswordCreateResponses]; + +export type AccountRegenerateApiKeyCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/account/regenerate-api-key'; +}; + +export type AccountRegenerateApiKeyCreateResponses = { + 200: ApiKey; +}; + +export type AccountRegenerateApiKeyCreateResponse = AccountRegenerateApiKeyCreateResponses[keyof AccountRegenerateApiKeyCreateResponses]; + +export type AdminBackupsRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/backups'; +}; + +export type AdminBackupsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminBackupsRetrieveResponse = AdminBackupsRetrieveResponses[keyof AdminBackupsRetrieveResponses]; + +export type AdminBackupsCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/backups'; +}; + +export type AdminBackupsCreateResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminBackupsCreateResponse = AdminBackupsCreateResponses[keyof AdminBackupsCreateResponses]; + +export type AdminBackupsDestroyData = { + body?: never; + path: { + filename: string; + }; + query?: never; + url: '/api/admin/backups/{filename}'; +}; + +export type AdminBackupsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type AdminBackupsDestroyResponse = AdminBackupsDestroyResponses[keyof AdminBackupsDestroyResponses]; + +export type AdminBugreportRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/bugreport'; +}; + +export type AdminBugreportRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminBugreportRetrieveResponse = AdminBugreportRetrieveResponses[keyof AdminBugreportRetrieveResponses]; + +export type AdminDashboardRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/dashboard'; +}; + +export type AdminDashboardRetrieveResponses = { + 200: AdminDashboard; +}; + +export type AdminDashboardRetrieveResponse = AdminDashboardRetrieveResponses[keyof AdminDashboardRetrieveResponses]; + +export type AdminEmailTestCreateData = { + body: EmailTestRequestRequest; + path?: never; + query?: never; + url: '/api/admin/email-test'; +}; + +export type AdminEmailTestCreateResponses = { + 200: boolean; +}; + +export type AdminEmailTestCreateResponse = AdminEmailTestCreateResponses[keyof AdminEmailTestCreateResponses]; + +export type AdminLogsRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/logs'; +}; + +export type AdminLogsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminLogsRetrieveResponse = AdminLogsRetrieveResponses[keyof AdminLogsRetrieveResponses]; + +export type AdminPluginsRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/admin/plugins'; +}; + +export type AdminPluginsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminPluginsRetrieveResponse = AdminPluginsRetrieveResponses[keyof AdminPluginsRetrieveResponses]; + +export type AdminPluginsSettingsRetrieveData = { + body?: never; + path: { + short_name: string; + }; + query?: never; + url: '/api/admin/plugins/{short_name}/settings'; +}; + +export type AdminPluginsSettingsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminPluginsSettingsRetrieveResponse = AdminPluginsSettingsRetrieveResponses[keyof AdminPluginsSettingsRetrieveResponses]; + +export type AdminPluginsSettingsUpdateData = { + body?: { + [key: string]: unknown; + }; + path: { + short_name: string; + }; + query?: never; + url: '/api/admin/plugins/{short_name}/settings'; +}; + +export type AdminPluginsSettingsUpdateResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AdminPluginsSettingsUpdateResponse = AdminPluginsSettingsUpdateResponses[keyof AdminPluginsSettingsUpdateResponses]; + +export type ApiKeysListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/api-keys'; +}; + +export type ApiKeysListResponses = { + 200: PaginatedApiKeyList; +}; + +export type ApiKeysListResponse = ApiKeysListResponses[keyof ApiKeysListResponses]; + +export type ApiKeysCreateData = { + body: ApiKeyRequest; + path?: never; + query?: never; + url: '/api/api-keys'; +}; + +export type ApiKeysCreateResponses = { + 201: ApiKey; +}; + +export type ApiKeysCreateResponse = ApiKeysCreateResponses[keyof ApiKeysCreateResponses]; + +export type ApiKeysDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this api key. + */ + id: number; + }; + query?: never; + url: '/api/api-keys/{id}'; +}; + +export type ApiKeysDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type ApiKeysDestroyResponse = ApiKeysDestroyResponses[keyof ApiKeysDestroyResponses]; + +export type ApiKeysRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this api key. + */ + id: number; + }; + query?: never; + url: '/api/api-keys/{id}'; +}; + +export type ApiKeysRetrieveResponses = { + 200: ApiKey; +}; + +export type ApiKeysRetrieveResponse = ApiKeysRetrieveResponses[keyof ApiKeysRetrieveResponses]; + +export type ApiKeysPartialUpdateData = { + body?: PatchedApiKeyRequest; + path: { + /** + * A unique integer value identifying this api key. + */ + id: number; + }; + query?: never; + url: '/api/api-keys/{id}'; +}; + +export type ApiKeysPartialUpdateResponses = { + 200: ApiKey; +}; + +export type ApiKeysPartialUpdateResponse = ApiKeysPartialUpdateResponses[keyof ApiKeysPartialUpdateResponses]; + +export type ApiKeysUpdateData = { + body: ApiKeyRequest; + path: { + /** + * A unique integer value identifying this api key. + */ + id: number; + }; + query?: never; + url: '/api/api-keys/{id}'; +}; + +export type ApiKeysUpdateResponses = { + 200: ApiKey; +}; + +export type ApiKeysUpdateResponse = ApiKeysUpdateResponses[keyof ApiKeysUpdateResponses]; + +export type AuthTokensListData = { + body?: never; + path?: never; + query?: { + auth_device?: string; + /** + * The pagination cursor value. + */ + cursor?: string; + enabled?: boolean; + /** + * Number of results to return per page. + */ + page_size?: number; + search?: string; + user?: number; + }; + url: '/api/auth-tokens'; +}; + +export type AuthTokensListResponses = { + 200: PaginatedAuthenticationTokenList; +}; + +export type AuthTokensListResponse = AuthTokensListResponses[keyof AuthTokensListResponses]; + +export type AuthTokensCreateData = { + body: AuthenticationTokenRequest; + path?: never; + query?: never; + url: '/api/auth-tokens'; +}; + +export type AuthTokensCreateResponses = { + 201: AuthenticationToken; +}; + +export type AuthTokensCreateResponse = AuthTokensCreateResponses[keyof AuthTokensCreateResponses]; + +export type AuthTokensDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this authentication token. + */ + id: number; + }; + query?: never; + url: '/api/auth-tokens/{id}'; +}; + +export type AuthTokensDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type AuthTokensDestroyResponse = AuthTokensDestroyResponses[keyof AuthTokensDestroyResponses]; + +export type AuthTokensRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this authentication token. + */ + id: number; + }; + query?: never; + url: '/api/auth-tokens/{id}'; +}; + +export type AuthTokensRetrieveResponses = { + 200: AuthenticationToken; +}; + +export type AuthTokensRetrieveResponse = AuthTokensRetrieveResponses[keyof AuthTokensRetrieveResponses]; + +export type AuthTokensPartialUpdateData = { + body?: PatchedAuthenticationTokenRequest; + path: { + /** + * A unique integer value identifying this authentication token. + */ + id: number; + }; + query?: never; + url: '/api/auth-tokens/{id}'; +}; + +export type AuthTokensPartialUpdateResponses = { + 200: AuthenticationToken; +}; + +export type AuthTokensPartialUpdateResponse = AuthTokensPartialUpdateResponses[keyof AuthTokensPartialUpdateResponses]; + +export type AuthTokensUpdateData = { + body: AuthenticationTokenRequest; + path: { + /** + * A unique integer value identifying this authentication token. + */ + id: number; + }; + query?: never; + url: '/api/auth-tokens/{id}'; +}; + +export type AuthTokensUpdateResponses = { + 200: AuthenticationToken; +}; + +export type AuthTokensUpdateResponse = AuthTokensUpdateResponses[keyof AuthTokensUpdateResponses]; + +export type AuthLoginCreateData = { + body: LoginRequest; + path?: never; + query?: never; + url: '/api/auth/login'; +}; + +export type AuthLoginCreateResponses = { + 200: CurrentUser; +}; + +export type AuthLoginCreateResponse = AuthLoginCreateResponses[keyof AuthLoginCreateResponses]; + +export type AuthLogoutCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/auth/logout'; +}; + +export type AuthLogoutCreateResponses = { + 200: boolean; +}; + +export type AuthLogoutCreateResponse = AuthLogoutCreateResponses[keyof AuthLogoutCreateResponses]; + +export type AuthPasswordResetCreateData = { + body: PasswordResetRequestRequest; + path?: never; + query?: never; + url: '/api/auth/password-reset'; +}; + +export type AuthPasswordResetCreateResponses = { + 200: boolean; +}; + +export type AuthPasswordResetCreateResponse = AuthPasswordResetCreateResponses[keyof AuthPasswordResetCreateResponses]; + +export type AuthPasswordResetConfirmCreateData = { + body: PasswordResetConfirmRequestRequest; + path?: never; + query?: never; + url: '/api/auth/password-reset-confirm'; +}; + +export type AuthPasswordResetConfirmCreateResponses = { + 200: boolean; +}; + +export type AuthPasswordResetConfirmCreateResponse = AuthPasswordResetConfirmCreateResponses[keyof AuthPasswordResetConfirmCreateResponses]; + +export type AuthRegisterCreateData = { + body: RegisterRequestRequest; + path?: never; + query?: never; + url: '/api/auth/register'; +}; + +export type AuthRegisterCreateResponses = { + 200: CurrentUser; +}; + +export type AuthRegisterCreateResponse = AuthRegisterCreateResponses[keyof AuthRegisterCreateResponses]; + +export type BeverageProducersListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/beverage-producers'; +}; + +export type BeverageProducersListResponses = { + 200: PaginatedBeverageProducerList; +}; + +export type BeverageProducersListResponse = BeverageProducersListResponses[keyof BeverageProducersListResponses]; + +export type BeverageProducersCreateData = { + body: BeverageProducerRequest; + path?: never; + query?: never; + url: '/api/beverage-producers'; +}; + +export type BeverageProducersCreateResponses = { + 201: BeverageProducer; +}; + +export type BeverageProducersCreateResponse = BeverageProducersCreateResponses[keyof BeverageProducersCreateResponses]; + +export type BeverageProducersDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this beverage producer. + */ + id: number; + }; + query?: never; + url: '/api/beverage-producers/{id}'; +}; + +export type BeverageProducersDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type BeverageProducersDestroyResponse = BeverageProducersDestroyResponses[keyof BeverageProducersDestroyResponses]; + +export type BeverageProducersRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this beverage producer. + */ + id: number; + }; + query?: never; + url: '/api/beverage-producers/{id}'; +}; + +export type BeverageProducersRetrieveResponses = { + 200: BeverageProducer; +}; + +export type BeverageProducersRetrieveResponse = BeverageProducersRetrieveResponses[keyof BeverageProducersRetrieveResponses]; + +export type BeverageProducersPartialUpdateData = { + body?: PatchedBeverageProducerRequest; + path: { + /** + * A unique integer value identifying this beverage producer. + */ + id: number; + }; + query?: never; + url: '/api/beverage-producers/{id}'; +}; + +export type BeverageProducersPartialUpdateResponses = { + 200: BeverageProducer; +}; + +export type BeverageProducersPartialUpdateResponse = BeverageProducersPartialUpdateResponses[keyof BeverageProducersPartialUpdateResponses]; + +export type BeverageProducersUpdateData = { + body: BeverageProducerRequest; + path: { + /** + * A unique integer value identifying this beverage producer. + */ + id: number; + }; + query?: never; + url: '/api/beverage-producers/{id}'; +}; + +export type BeverageProducersUpdateResponses = { + 200: BeverageProducer; +}; + +export type BeverageProducersUpdateResponse = BeverageProducersUpdateResponses[keyof BeverageProducersUpdateResponses]; + +export type BeverageProducersPictureCreateData = { + body: PictureUploadRequestRequest; + path: { + /** + * A unique integer value identifying this beverage producer. + */ + id: number; + }; + query?: never; + url: '/api/beverage-producers/{id}/picture'; +}; + +export type BeverageProducersPictureCreateResponses = { + 200: BeverageProducer; +}; + +export type BeverageProducersPictureCreateResponse = BeverageProducersPictureCreateResponses[keyof BeverageProducersPictureCreateResponses]; + +export type BeveragesListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/beverages'; +}; + +export type BeveragesListResponses = { + 200: PaginatedBeverageList; +}; + +export type BeveragesListResponse = BeveragesListResponses[keyof BeveragesListResponses]; + +export type BeveragesCreateData = { + body: BeverageRequest; + path?: never; + query?: never; + url: '/api/beverages'; +}; + +export type BeveragesCreateResponses = { + 201: Beverage; +}; + +export type BeveragesCreateResponse = BeveragesCreateResponses[keyof BeveragesCreateResponses]; + +export type BeveragesDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this beverage. + */ + id: number; + }; + query?: never; + url: '/api/beverages/{id}'; +}; + +export type BeveragesDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type BeveragesDestroyResponse = BeveragesDestroyResponses[keyof BeveragesDestroyResponses]; + +export type BeveragesRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this beverage. + */ + id: number; + }; + query?: never; + url: '/api/beverages/{id}'; +}; + +export type BeveragesRetrieveResponses = { + 200: Beverage; +}; + +export type BeveragesRetrieveResponse = BeveragesRetrieveResponses[keyof BeveragesRetrieveResponses]; + +export type BeveragesPartialUpdateData = { + body?: PatchedBeverageRequest; + path: { + /** + * A unique integer value identifying this beverage. + */ + id: number; + }; + query?: never; + url: '/api/beverages/{id}'; +}; + +export type BeveragesPartialUpdateResponses = { + 200: Beverage; +}; + +export type BeveragesPartialUpdateResponse = BeveragesPartialUpdateResponses[keyof BeveragesPartialUpdateResponses]; + +export type BeveragesUpdateData = { + body: BeverageRequest; + path: { + /** + * A unique integer value identifying this beverage. + */ + id: number; + }; + query?: never; + url: '/api/beverages/{id}'; +}; + +export type BeveragesUpdateResponses = { + 200: Beverage; +}; + +export type BeveragesUpdateResponse = BeveragesUpdateResponses[keyof BeveragesUpdateResponses]; + +export type BeveragesPictureCreateData = { + body: PictureUploadRequestRequest; + path: { + /** + * A unique integer value identifying this beverage. + */ + id: number; + }; + query?: never; + url: '/api/beverages/{id}/picture'; +}; + +export type BeveragesPictureCreateResponses = { + 200: Beverage; +}; + +export type BeveragesPictureCreateResponse = BeveragesPictureCreateResponses[keyof BeveragesPictureCreateResponses]; + +export type ControllersListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/controllers'; +}; + +export type ControllersListResponses = { + 200: PaginatedControllerList; +}; + +export type ControllersListResponse = ControllersListResponses[keyof ControllersListResponses]; + +export type ControllersCreateData = { + body: ControllerRequest; + path?: never; + query?: never; + url: '/api/controllers'; +}; + +export type ControllersCreateResponses = { + 201: Controller; +}; + +export type ControllersCreateResponse = ControllersCreateResponses[keyof ControllersCreateResponses]; + +export type ControllersDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this controller. + */ + id: number; + }; + query?: never; + url: '/api/controllers/{id}'; +}; + +export type ControllersDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type ControllersDestroyResponse = ControllersDestroyResponses[keyof ControllersDestroyResponses]; + +export type ControllersRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this controller. + */ + id: number; + }; + query?: never; + url: '/api/controllers/{id}'; +}; + +export type ControllersRetrieveResponses = { + 200: Controller; +}; + +export type ControllersRetrieveResponse = ControllersRetrieveResponses[keyof ControllersRetrieveResponses]; + +export type ControllersPartialUpdateData = { + body?: PatchedControllerRequest; + path: { + /** + * A unique integer value identifying this controller. + */ + id: number; + }; + query?: never; + url: '/api/controllers/{id}'; +}; + +export type ControllersPartialUpdateResponses = { + 200: Controller; +}; + +export type ControllersPartialUpdateResponse = ControllersPartialUpdateResponses[keyof ControllersPartialUpdateResponses]; + +export type ControllersUpdateData = { + body: ControllerRequest; + path: { + /** + * A unique integer value identifying this controller. + */ + id: number; + }; + query?: never; + url: '/api/controllers/{id}'; +}; + +export type ControllersUpdateResponses = { + 200: Controller; +}; + +export type ControllersUpdateResponse = ControllersUpdateResponses[keyof ControllersUpdateResponses]; + +export type DevicesListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/devices'; +}; + +export type DevicesListResponses = { + 200: PaginatedDeviceList; +}; + +export type DevicesListResponse = DevicesListResponses[keyof DevicesListResponses]; + +export type DevicesCreateData = { + body?: DeviceRequest; + path?: never; + query?: never; + url: '/api/devices'; +}; + +export type DevicesCreateResponses = { + 201: Device; +}; + +export type DevicesCreateResponse = DevicesCreateResponses[keyof DevicesCreateResponses]; + +export type DevicesDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this device. + */ + id: number; + }; + query?: never; + url: '/api/devices/{id}'; +}; + +export type DevicesDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type DevicesDestroyResponse = DevicesDestroyResponses[keyof DevicesDestroyResponses]; + +export type DevicesRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this device. + */ + id: number; + }; + query?: never; + url: '/api/devices/{id}'; +}; + +export type DevicesRetrieveResponses = { + 200: Device; +}; + +export type DevicesRetrieveResponse = DevicesRetrieveResponses[keyof DevicesRetrieveResponses]; + +export type DevicesPartialUpdateData = { + body?: PatchedDeviceRequest; + path: { + /** + * A unique integer value identifying this device. + */ + id: number; + }; + query?: never; + url: '/api/devices/{id}'; +}; + +export type DevicesPartialUpdateResponses = { + 200: Device; +}; + +export type DevicesPartialUpdateResponse = DevicesPartialUpdateResponses[keyof DevicesPartialUpdateResponses]; + +export type DevicesUpdateData = { + body?: DeviceRequest; + path: { + /** + * A unique integer value identifying this device. + */ + id: number; + }; + query?: never; + url: '/api/devices/{id}'; +}; + +export type DevicesUpdateResponses = { + 200: Device; +}; + +export type DevicesUpdateResponse = DevicesUpdateResponses[keyof DevicesUpdateResponses]; + +export type DrinksListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + keg?: number; + /** + * Number of results to return per page. + */ + page_size?: number; + session?: number; + user?: number; + username?: string; + }; + url: '/api/drinks'; +}; + +export type DrinksListResponses = { + 200: PaginatedDrinkList; +}; + +export type DrinksListResponse = DrinksListResponses[keyof DrinksListResponses]; + +export type DrinksDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}'; +}; + +export type DrinksDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type DrinksDestroyResponse = DrinksDestroyResponses[keyof DrinksDestroyResponses]; + +export type DrinksRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}'; +}; + +export type DrinksRetrieveResponses = { + 200: Drink; +}; + +export type DrinksRetrieveResponse = DrinksRetrieveResponses[keyof DrinksRetrieveResponses]; + +export type DrinksPartialUpdateData = { + body?: PatchedDrinkUpdateRequestRequest; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}'; +}; + +export type DrinksPartialUpdateResponses = { + 200: Drink; +}; + +export type DrinksPartialUpdateResponse = DrinksPartialUpdateResponses[keyof DrinksPartialUpdateResponses]; + +export type DrinksPictureDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}/picture'; +}; + +export type DrinksPictureDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type DrinksPictureDestroyResponse = DrinksPictureDestroyResponses[keyof DrinksPictureDestroyResponses]; + +export type DrinksPictureCreateData = { + body: PictureUploadRequestRequest; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}/picture'; +}; + +export type DrinksPictureCreateResponses = { + 200: Drink; +}; + +export type DrinksPictureCreateResponse = DrinksPictureCreateResponses[keyof DrinksPictureCreateResponses]; + +export type DrinksReassignCreateData = { + body: DrinkReassignRequestRequest; + path: { + /** + * A unique integer value identifying this drink. + */ + id: number; + }; + query?: never; + url: '/api/drinks/{id}/reassign'; +}; + +export type DrinksReassignCreateResponses = { + 200: Drink; +}; + +export type DrinksReassignCreateResponse = DrinksReassignCreateResponses[keyof DrinksReassignCreateResponses]; + +export type EventsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + keg?: number; + /** + * Type of event. + * + * * `drink_poured` - Drink poured + * * `session_started` - Session started + * * `session_joined` - User joined session + * * `keg_tapped` - Keg tapped + * * `keg_volume_low` - Keg volume low + * * `keg_ended` - Keg ended + */ + kind?: 'drink_poured' | 'keg_ended' | 'keg_tapped' | 'keg_volume_low' | 'session_joined' | 'session_started'; + /** + * Number of results to return per page. + */ + page_size?: number; + session?: number; + since?: number; + user?: number; + username?: string; + }; + url: '/api/events'; +}; + +export type EventsListResponses = { + 200: PaginatedSystemEventList; +}; + +export type EventsListResponse = EventsListResponses[keyof EventsListResponses]; + +export type EventsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this system event. + */ + id: number; + }; + query?: never; + url: '/api/events/{id}'; +}; + +export type EventsRetrieveResponses = { + 200: SystemEvent; +}; + +export type EventsRetrieveResponse = EventsRetrieveResponses[keyof EventsRetrieveResponses]; + +export type FlowMetersListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/flow-meters'; +}; + +export type FlowMetersListResponses = { + 200: PaginatedFlowMeterList; +}; + +export type FlowMetersListResponse = FlowMetersListResponses[keyof FlowMetersListResponses]; + +export type FlowMetersCreateData = { + body: FlowMeterRequest; + path?: never; + query?: never; + url: '/api/flow-meters'; +}; + +export type FlowMetersCreateResponses = { + 201: FlowMeter; +}; + +export type FlowMetersCreateResponse = FlowMetersCreateResponses[keyof FlowMetersCreateResponses]; + +export type FlowMetersDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this flow meter. + */ + id: number; + }; + query?: never; + url: '/api/flow-meters/{id}'; +}; + +export type FlowMetersDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type FlowMetersDestroyResponse = FlowMetersDestroyResponses[keyof FlowMetersDestroyResponses]; + +export type FlowMetersRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this flow meter. + */ + id: number; + }; + query?: never; + url: '/api/flow-meters/{id}'; +}; + +export type FlowMetersRetrieveResponses = { + 200: FlowMeter; +}; + +export type FlowMetersRetrieveResponse = FlowMetersRetrieveResponses[keyof FlowMetersRetrieveResponses]; + +export type FlowMetersPartialUpdateData = { + body?: PatchedFlowMeterRequest; + path: { + /** + * A unique integer value identifying this flow meter. + */ + id: number; + }; + query?: never; + url: '/api/flow-meters/{id}'; +}; + +export type FlowMetersPartialUpdateResponses = { + 200: FlowMeter; +}; + +export type FlowMetersPartialUpdateResponse = FlowMetersPartialUpdateResponses[keyof FlowMetersPartialUpdateResponses]; + +export type FlowMetersUpdateData = { + body: FlowMeterRequest; + path: { + /** + * A unique integer value identifying this flow meter. + */ + id: number; + }; + query?: never; + url: '/api/flow-meters/{id}'; +}; + +export type FlowMetersUpdateResponses = { + 200: FlowMeter; +}; + +export type FlowMetersUpdateResponse = FlowMetersUpdateResponses[keyof FlowMetersUpdateResponses]; + +export type FlowTogglesListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/flow-toggles'; +}; + +export type FlowTogglesListResponses = { + 200: PaginatedFlowToggleList; +}; + +export type FlowTogglesListResponse = FlowTogglesListResponses[keyof FlowTogglesListResponses]; + +export type FlowTogglesCreateData = { + body: FlowToggleRequest; + path?: never; + query?: never; + url: '/api/flow-toggles'; +}; + +export type FlowTogglesCreateResponses = { + 201: FlowToggle; +}; + +export type FlowTogglesCreateResponse = FlowTogglesCreateResponses[keyof FlowTogglesCreateResponses]; + +export type FlowTogglesDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this flow toggle. + */ + id: number; + }; + query?: never; + url: '/api/flow-toggles/{id}'; +}; + +export type FlowTogglesDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type FlowTogglesDestroyResponse = FlowTogglesDestroyResponses[keyof FlowTogglesDestroyResponses]; + +export type FlowTogglesRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this flow toggle. + */ + id: number; + }; + query?: never; + url: '/api/flow-toggles/{id}'; +}; + +export type FlowTogglesRetrieveResponses = { + 200: FlowToggle; +}; + +export type FlowTogglesRetrieveResponse = FlowTogglesRetrieveResponses[keyof FlowTogglesRetrieveResponses]; + +export type FlowTogglesPartialUpdateData = { + body?: PatchedFlowToggleRequest; + path: { + /** + * A unique integer value identifying this flow toggle. + */ + id: number; + }; + query?: never; + url: '/api/flow-toggles/{id}'; +}; + +export type FlowTogglesPartialUpdateResponses = { + 200: FlowToggle; +}; + +export type FlowTogglesPartialUpdateResponse = FlowTogglesPartialUpdateResponses[keyof FlowTogglesPartialUpdateResponses]; + +export type FlowTogglesUpdateData = { + body: FlowToggleRequest; + path: { + /** + * A unique integer value identifying this flow toggle. + */ + id: number; + }; + query?: never; + url: '/api/flow-toggles/{id}'; +}; + +export type FlowTogglesUpdateResponses = { + 200: FlowToggle; +}; + +export type FlowTogglesUpdateResponse = FlowTogglesUpdateResponses[keyof FlowTogglesUpdateResponses]; + +export type InvitationsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/invitations'; +}; + +export type InvitationsListResponses = { + 200: PaginatedInvitationList; +}; + +export type InvitationsListResponse = InvitationsListResponses[keyof InvitationsListResponses]; + +export type InvitationsCreateData = { + body: InvitationRequest; + path?: never; + query?: never; + url: '/api/invitations'; +}; + +export type InvitationsCreateResponses = { + 201: Invitation; +}; + +export type InvitationsCreateResponse = InvitationsCreateResponses[keyof InvitationsCreateResponses]; + +export type InvitationsDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this invitation. + */ + id: number; + }; + query?: never; + url: '/api/invitations/{id}'; +}; + +export type InvitationsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type InvitationsDestroyResponse = InvitationsDestroyResponses[keyof InvitationsDestroyResponses]; + +export type InvitationsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this invitation. + */ + id: number; + }; + query?: never; + url: '/api/invitations/{id}'; +}; + +export type InvitationsRetrieveResponses = { + 200: Invitation; +}; + +export type InvitationsRetrieveResponse = InvitationsRetrieveResponses[keyof InvitationsRetrieveResponses]; + +export type KegsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + /** + * Current keg state. + * + * * `available` - Available + * * `on_tap` - On tap + * * `finished` - Finished + */ + status?: 'available' | 'finished' | 'on_tap'; + }; + url: '/api/kegs'; +}; + +export type KegsListResponses = { + 200: PaginatedKegList; +}; + +export type KegsListResponse = KegsListResponses[keyof KegsListResponses]; + +export type KegsCreateData = { + body?: KegCreateRequestRequest; + path?: never; + query?: never; + url: '/api/kegs'; +}; + +export type KegsCreateResponses = { + 201: Keg; +}; + +export type KegsCreateResponse = KegsCreateResponses[keyof KegsCreateResponses]; + +export type KegsDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}'; +}; + +export type KegsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type KegsDestroyResponse = KegsDestroyResponses[keyof KegsDestroyResponses]; + +export type KegsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}'; +}; + +export type KegsRetrieveResponses = { + 200: Keg; +}; + +export type KegsRetrieveResponse = KegsRetrieveResponses[keyof KegsRetrieveResponses]; + +export type KegsPartialUpdateData = { + body?: PatchedKegRequest; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}'; +}; + +export type KegsPartialUpdateResponses = { + 200: Keg; +}; + +export type KegsPartialUpdateResponse = KegsPartialUpdateResponses[keyof KegsPartialUpdateResponses]; + +export type KegsUpdateData = { + body?: KegRequest; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}'; +}; + +export type KegsUpdateResponses = { + 200: Keg; +}; + +export type KegsUpdateResponse = KegsUpdateResponses[keyof KegsUpdateResponses]; + +export type KegsEndCreateData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}/end'; +}; + +export type KegsEndCreateResponses = { + 200: Keg; +}; + +export type KegsEndCreateResponse = KegsEndCreateResponses[keyof KegsEndCreateResponses]; + +export type KegsReactivateCreateData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}/reactivate'; +}; + +export type KegsReactivateCreateResponses = { + 200: Keg; +}; + +export type KegsReactivateCreateResponse = KegsReactivateCreateResponses[keyof KegsReactivateCreateResponses]; + +export type KegsSpillCreateData = { + body: KegSpillRequestRequest; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}/spill'; +}; + +export type KegsSpillCreateResponses = { + 200: Keg; +}; + +export type KegsSpillCreateResponse = KegsSpillCreateResponses[keyof KegsSpillCreateResponses]; + +export type KegsStatsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg. + */ + id: number; + }; + query?: never; + url: '/api/kegs/{id}/stats'; +}; + +export type KegsStatsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type KegsStatsRetrieveResponse = KegsStatsRetrieveResponses[keyof KegsStatsRetrieveResponses]; + +export type NotificationSettingsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/notification-settings'; +}; + +export type NotificationSettingsListResponses = { + 200: PaginatedNotificationSettingsList; +}; + +export type NotificationSettingsListResponse = NotificationSettingsListResponses[keyof NotificationSettingsListResponses]; + +export type NotificationSettingsCreateData = { + body: NotificationSettingsRequest; + path?: never; + query?: never; + url: '/api/notification-settings'; +}; + +export type NotificationSettingsCreateResponses = { + 201: NotificationSettings; +}; + +export type NotificationSettingsCreateResponse = NotificationSettingsCreateResponses[keyof NotificationSettingsCreateResponses]; + +export type NotificationSettingsDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this notification settings. + */ + id: number; + }; + query?: never; + url: '/api/notification-settings/{id}'; +}; + +export type NotificationSettingsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type NotificationSettingsDestroyResponse = NotificationSettingsDestroyResponses[keyof NotificationSettingsDestroyResponses]; + +export type NotificationSettingsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this notification settings. + */ + id: number; + }; + query?: never; + url: '/api/notification-settings/{id}'; +}; + +export type NotificationSettingsRetrieveResponses = { + 200: NotificationSettings; +}; + +export type NotificationSettingsRetrieveResponse = NotificationSettingsRetrieveResponses[keyof NotificationSettingsRetrieveResponses]; + +export type NotificationSettingsPartialUpdateData = { + body?: PatchedNotificationSettingsRequest; + path: { + /** + * A unique integer value identifying this notification settings. + */ + id: number; + }; + query?: never; + url: '/api/notification-settings/{id}'; +}; + +export type NotificationSettingsPartialUpdateResponses = { + 200: NotificationSettings; +}; + +export type NotificationSettingsPartialUpdateResponse = NotificationSettingsPartialUpdateResponses[keyof NotificationSettingsPartialUpdateResponses]; + +export type NotificationSettingsUpdateData = { + body: NotificationSettingsRequest; + path: { + /** + * A unique integer value identifying this notification settings. + */ + id: number; + }; + query?: never; + url: '/api/notification-settings/{id}'; +}; + +export type NotificationSettingsUpdateResponses = { + 200: NotificationSettings; +}; + +export type NotificationSettingsUpdateResponse = NotificationSettingsUpdateResponses[keyof NotificationSettingsUpdateResponses]; + +export type PluginDataListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/plugin-data'; +}; + +export type PluginDataListResponses = { + 200: PaginatedPluginDataList; +}; + +export type PluginDataListResponse = PluginDataListResponses[keyof PluginDataListResponses]; + +export type PluginDataCreateData = { + body: PluginDataRequest; + path?: never; + query?: never; + url: '/api/plugin-data'; +}; + +export type PluginDataCreateResponses = { + 201: PluginData; +}; + +export type PluginDataCreateResponse = PluginDataCreateResponses[keyof PluginDataCreateResponses]; + +export type PluginDataDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this plugin data. + */ + id: number; + }; + query?: never; + url: '/api/plugin-data/{id}'; +}; + +export type PluginDataDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type PluginDataDestroyResponse = PluginDataDestroyResponses[keyof PluginDataDestroyResponses]; + +export type PluginDataRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this plugin data. + */ + id: number; + }; + query?: never; + url: '/api/plugin-data/{id}'; +}; + +export type PluginDataRetrieveResponses = { + 200: PluginData; +}; + +export type PluginDataRetrieveResponse = PluginDataRetrieveResponses[keyof PluginDataRetrieveResponses]; + +export type PluginDataPartialUpdateData = { + body?: PatchedPluginDataRequest; + path: { + /** + * A unique integer value identifying this plugin data. + */ + id: number; + }; + query?: never; + url: '/api/plugin-data/{id}'; +}; + +export type PluginDataPartialUpdateResponses = { + 200: PluginData; +}; + +export type PluginDataPartialUpdateResponse = PluginDataPartialUpdateResponses[keyof PluginDataPartialUpdateResponses]; + +export type PluginDataUpdateData = { + body: PluginDataRequest; + path: { + /** + * A unique integer value identifying this plugin data. + */ + id: number; + }; + query?: never; + url: '/api/plugin-data/{id}'; +}; + +export type PluginDataUpdateResponses = { + 200: PluginData; +}; + +export type PluginDataUpdateResponse = PluginDataUpdateResponses[keyof PluginDataUpdateResponses]; + +export type SessionsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + day?: number; + month?: number; + /** + * Number of results to return per page. + */ + page_size?: number; + year?: number; + }; + url: '/api/sessions'; +}; + +export type SessionsListResponses = { + 200: PaginatedDrinkingSessionList; +}; + +export type SessionsListResponse = SessionsListResponses[keyof SessionsListResponses]; + +export type SessionsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this drinking session. + */ + id: number; + }; + query?: never; + url: '/api/sessions/{id}'; +}; + +export type SessionsRetrieveResponses = { + 200: DrinkingSession; +}; + +export type SessionsRetrieveResponse = SessionsRetrieveResponses[keyof SessionsRetrieveResponses]; + +export type SessionsStatsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this drinking session. + */ + id: number; + }; + query?: never; + url: '/api/sessions/{id}/stats'; +}; + +export type SessionsStatsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type SessionsStatsRetrieveResponse = SessionsStatsRetrieveResponses[keyof SessionsStatsRetrieveResponses]; + +export type SessionsCurrentRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/sessions/current'; +}; + +export type SessionsCurrentRetrieveResponses = { + 200: DrinkingSession; +}; + +export type SessionsCurrentRetrieveResponse = SessionsCurrentRetrieveResponses[keyof SessionsCurrentRetrieveResponses]; + +export type SessionsDirectoryRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/sessions/directory'; +}; + +export type SessionsDirectoryRetrieveResponses = { + 200: SessionDirectory; +}; + +export type SessionsDirectoryRetrieveResponse = SessionsDirectoryRetrieveResponses[keyof SessionsDirectoryRetrieveResponses]; + +export type SetupAdminUserCreateData = { + body: SetupAdminUserRequestRequest; + path?: never; + query?: never; + url: '/api/setup/admin-user'; +}; + +export type SetupAdminUserCreateResponses = { + 200: CurrentUser; +}; + +export type SetupAdminUserCreateResponse = SetupAdminUserCreateResponses[keyof SetupAdminUserCreateResponses]; + +export type SetupFinishCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/setup/finish'; +}; + +export type SetupFinishCreateResponses = { + 200: boolean; +}; + +export type SetupFinishCreateResponse = SetupFinishCreateResponses[keyof SetupFinishCreateResponses]; + +export type SetupMigrateCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/setup/migrate'; +}; + +export type SetupMigrateCreateResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type SetupMigrateCreateResponse = SetupMigrateCreateResponses[keyof SetupMigrateCreateResponses]; + +export type SetupSettingsCreateData = { + body?: SetupSiteSettingsRequestRequest; + path?: never; + query?: never; + url: '/api/setup/settings'; +}; + +export type SetupSettingsCreateResponses = { + 200: SetupSiteSettingsRequest; +}; + +export type SetupSettingsCreateResponse = SetupSettingsCreateResponses[keyof SetupSettingsCreateResponses]; + +export type SetupStatusRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/setup/status'; +}; + +export type SetupStatusRetrieveResponses = { + 200: SetupStatus; +}; + +export type SetupStatusRetrieveResponse = SetupStatusRetrieveResponses[keyof SetupStatusRetrieveResponses]; + +export type SetupUpgradeCreateData = { + body?: never; + path?: never; + query?: never; + url: '/api/setup/upgrade'; +}; + +export type SetupUpgradeCreateResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type SetupUpgradeCreateResponse = SetupUpgradeCreateResponses[keyof SetupUpgradeCreateResponses]; + +export type SiteRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/site'; +}; + +export type SiteRetrieveResponses = { + 200: SiteSettings; +}; + +export type SiteRetrieveResponse = SiteRetrieveResponses[keyof SiteRetrieveResponses]; + +export type SitePartialUpdateData = { + body?: PatchedSiteSettingsRequest; + path?: never; + query?: never; + url: '/api/site'; +}; + +export type SitePartialUpdateResponses = { + 200: SiteSettings; +}; + +export type SitePartialUpdateResponse = SitePartialUpdateResponses[keyof SitePartialUpdateResponses]; + +export type SiteBackgroundImageCreateData = { + body: PictureUploadRequestRequest; + path?: never; + query?: never; + url: '/api/site/background-image'; +}; + +export type SiteBackgroundImageCreateResponses = { + 200: SiteSettings; +}; + +export type SiteBackgroundImageCreateResponse = SiteBackgroundImageCreateResponses[keyof SiteBackgroundImageCreateResponses]; + +export type StatsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/stats'; +}; + +export type StatsListResponses = { + 200: PaginatedStatsList; +}; + +export type StatsListResponse = StatsListResponses[keyof StatsListResponses]; + +export type StatsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this stats. + */ + id: number; + }; + query?: never; + url: '/api/stats/{id}'; +}; + +export type StatsRetrieveResponses = { + 200: Stats; +}; + +export type StatsRetrieveResponse = StatsRetrieveResponses[keyof StatsRetrieveResponses]; + +export type StatsSystemRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/stats/system'; +}; + +export type StatsSystemRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type StatsSystemRetrieveResponse = StatsSystemRetrieveResponses[keyof StatsSystemRetrieveResponses]; + +export type StatusRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/status'; +}; + +export type StatusRetrieveResponses = { + 200: SystemStatus; +}; + +export type StatusRetrieveResponse = StatusRetrieveResponses[keyof StatusRetrieveResponses]; + +export type TapsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/taps'; +}; + +export type TapsListResponses = { + 200: PaginatedKegTapList; +}; + +export type TapsListResponse = TapsListResponses[keyof TapsListResponses]; + +export type TapsCreateData = { + body: KegTapRequest; + path?: never; + query?: never; + url: '/api/taps'; +}; + +export type TapsCreateResponses = { + 201: KegTap; +}; + +export type TapsCreateResponse = TapsCreateResponses[keyof TapsCreateResponses]; + +export type TapsDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}'; +}; + +export type TapsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type TapsDestroyResponse = TapsDestroyResponses[keyof TapsDestroyResponses]; + +export type TapsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}'; +}; + +export type TapsRetrieveResponses = { + 200: KegTap; +}; + +export type TapsRetrieveResponse = TapsRetrieveResponses[keyof TapsRetrieveResponses]; + +export type TapsPartialUpdateData = { + body?: PatchedKegTapRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}'; +}; + +export type TapsPartialUpdateResponses = { + 200: KegTap; +}; + +export type TapsPartialUpdateResponse = TapsPartialUpdateResponses[keyof TapsPartialUpdateResponses]; + +export type TapsUpdateData = { + body: KegTapRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}'; +}; + +export type TapsUpdateResponses = { + 200: KegTap; +}; + +export type TapsUpdateResponse = TapsUpdateResponses[keyof TapsUpdateResponses]; + +export type TapsAttachKegCreateData = { + body: TapAttachKegRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/attach-keg'; +}; + +export type TapsAttachKegCreateResponses = { + 200: KegTap; +}; + +export type TapsAttachKegCreateResponse = TapsAttachKegCreateResponses[keyof TapsAttachKegCreateResponses]; + +export type TapsConnectMeterCreateData = { + body: TapConnectMeterRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/connect-meter'; +}; + +export type TapsConnectMeterCreateResponses = { + 200: KegTap; +}; + +export type TapsConnectMeterCreateResponse = TapsConnectMeterCreateResponses[keyof TapsConnectMeterCreateResponses]; + +export type TapsConnectThermoCreateData = { + body: TapConnectThermoRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/connect-thermo'; +}; + +export type TapsConnectThermoCreateResponses = { + 200: KegTap; +}; + +export type TapsConnectThermoCreateResponse = TapsConnectThermoCreateResponses[keyof TapsConnectThermoCreateResponses]; + +export type TapsConnectToggleCreateData = { + body: TapConnectToggleRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/connect-toggle'; +}; + +export type TapsConnectToggleCreateResponses = { + 200: KegTap; +}; + +export type TapsConnectToggleCreateResponse = TapsConnectToggleCreateResponses[keyof TapsConnectToggleCreateResponses]; + +export type TapsEndKegCreateData = { + body?: never; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/end-keg'; +}; + +export type TapsEndKegCreateResponses = { + 200: KegTap; +}; + +export type TapsEndKegCreateResponse = TapsEndKegCreateResponses[keyof TapsEndKegCreateResponses]; + +export type TapsRecordDrinkCreateData = { + body: TapRecordDrinkRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/record-drink'; +}; + +export type TapsRecordDrinkCreateResponses = { + 200: Drink; +}; + +export type TapsRecordDrinkCreateResponse = TapsRecordDrinkCreateResponses[keyof TapsRecordDrinkCreateResponses]; + +export type TapsStartKegCreateData = { + body?: NewKegRequestRequest; + path: { + /** + * A unique integer value identifying this keg tap. + */ + id: number; + }; + query?: never; + url: '/api/taps/{id}/start-keg'; +}; + +export type TapsStartKegCreateResponses = { + 200: KegTap; +}; + +export type TapsStartKegCreateResponse = TapsStartKegCreateResponses[keyof TapsStartKegCreateResponses]; + +export type ThermoLogsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + sensor?: number; + since?: string; + until?: string; + }; + url: '/api/thermo-logs'; +}; + +export type ThermoLogsListResponses = { + 200: PaginatedThermologList; +}; + +export type ThermoLogsListResponse = ThermoLogsListResponses[keyof ThermoLogsListResponses]; + +export type ThermoLogsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this thermolog. + */ + id: number; + }; + query?: never; + url: '/api/thermo-logs/{id}'; +}; + +export type ThermoLogsRetrieveResponses = { + 200: Thermolog; +}; + +export type ThermoLogsRetrieveResponse = ThermoLogsRetrieveResponses[keyof ThermoLogsRetrieveResponses]; + +export type ThermoSensorsListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + /** + * Number of results to return per page. + */ + page_size?: number; + }; + url: '/api/thermo-sensors'; +}; + +export type ThermoSensorsListResponses = { + 200: PaginatedThermoSensorList; +}; + +export type ThermoSensorsListResponse = ThermoSensorsListResponses[keyof ThermoSensorsListResponses]; + +export type ThermoSensorsCreateData = { + body: ThermoSensorRequest; + path?: never; + query?: never; + url: '/api/thermo-sensors'; +}; + +export type ThermoSensorsCreateResponses = { + 201: ThermoSensor; +}; + +export type ThermoSensorsCreateResponse = ThermoSensorsCreateResponses[keyof ThermoSensorsCreateResponses]; + +export type ThermoSensorsDestroyData = { + body?: never; + path: { + /** + * A unique integer value identifying this thermo sensor. + */ + id: number; + }; + query?: never; + url: '/api/thermo-sensors/{id}'; +}; + +export type ThermoSensorsDestroyResponses = { + /** + * No response body + */ + 204: void; +}; + +export type ThermoSensorsDestroyResponse = ThermoSensorsDestroyResponses[keyof ThermoSensorsDestroyResponses]; + +export type ThermoSensorsRetrieveData = { + body?: never; + path: { + /** + * A unique integer value identifying this thermo sensor. + */ + id: number; + }; + query?: never; + url: '/api/thermo-sensors/{id}'; +}; + +export type ThermoSensorsRetrieveResponses = { + 200: ThermoSensor; +}; + +export type ThermoSensorsRetrieveResponse = ThermoSensorsRetrieveResponses[keyof ThermoSensorsRetrieveResponses]; + +export type ThermoSensorsPartialUpdateData = { + body?: PatchedThermoSensorRequest; + path: { + /** + * A unique integer value identifying this thermo sensor. + */ + id: number; + }; + query?: never; + url: '/api/thermo-sensors/{id}'; +}; + +export type ThermoSensorsPartialUpdateResponses = { + 200: ThermoSensor; +}; + +export type ThermoSensorsPartialUpdateResponse = ThermoSensorsPartialUpdateResponses[keyof ThermoSensorsPartialUpdateResponses]; + +export type ThermoSensorsUpdateData = { + body: ThermoSensorRequest; + path: { + /** + * A unique integer value identifying this thermo sensor. + */ + id: number; + }; + query?: never; + url: '/api/thermo-sensors/{id}'; +}; + +export type ThermoSensorsUpdateResponses = { + 200: ThermoSensor; +}; + +export type ThermoSensorsUpdateResponse = ThermoSensorsUpdateResponses[keyof ThermoSensorsUpdateResponses]; + +export type UsersListData = { + body?: never; + path?: never; + query?: { + /** + * The pagination cursor value. + */ + cursor?: string; + is_active?: boolean; + is_staff?: boolean; + /** + * Number of results to return per page. + */ + page_size?: number; + search?: string; + }; + url: '/api/users'; +}; + +export type UsersListResponses = { + 200: PaginatedUserList; +}; + +export type UsersListResponse = UsersListResponses[keyof UsersListResponses]; + +export type UsersCreateData = { + body: AdminUserCreateRequestRequest; + path?: never; + query?: never; + url: '/api/users'; +}; + +export type UsersCreateResponses = { + 201: User; +}; + +export type UsersCreateResponse = UsersCreateResponses[keyof UsersCreateResponses]; + +export type UsersRetrieveData = { + body?: never; + path: { + username: string; + }; + query?: never; + url: '/api/users/{username}'; +}; + +export type UsersRetrieveResponses = { + 200: User; +}; + +export type UsersRetrieveResponse = UsersRetrieveResponses[keyof UsersRetrieveResponses]; + +export type UsersPartialUpdateData = { + body?: PatchedAdminUserUpdateRequestRequest; + path: { + username: string; + }; + query?: never; + url: '/api/users/{username}'; +}; + +export type UsersPartialUpdateResponses = { + 200: User; +}; + +export type UsersPartialUpdateResponse = UsersPartialUpdateResponses[keyof UsersPartialUpdateResponses]; + +export type UsersSetPasswordCreateData = { + body: SetPasswordRequestRequest; + path: { + username: string; + }; + query?: never; + url: '/api/users/{username}/set-password'; +}; + +export type UsersSetPasswordCreateResponses = { + 200: boolean; +}; + +export type UsersSetPasswordCreateResponse = UsersSetPasswordCreateResponses[keyof UsersSetPasswordCreateResponses]; + +export type UsersStatsRetrieveData = { + body?: never; + path: { + username: string; + }; + query?: never; + url: '/api/users/{username}/stats'; +}; + +export type UsersStatsRetrieveResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type UsersStatsRetrieveResponse = UsersStatsRetrieveResponses[keyof UsersStatsRetrieveResponses]; + +export type UsersMeRetrieveData = { + body?: never; + path?: never; + query?: never; + url: '/api/users/me'; +}; + +export type UsersMeRetrieveResponses = { + 200: Me; +}; + +export type UsersMeRetrieveResponse = UsersMeRetrieveResponses[keyof UsersMeRetrieveResponses]; + +export type UsersMePartialUpdateData = { + body?: PatchedProfileUpdateRequestRequest; + path?: never; + query?: never; + url: '/api/users/me'; +}; + +export type UsersMePartialUpdateResponses = { + 200: Me; +}; + +export type UsersMePartialUpdateResponse = UsersMePartialUpdateResponses[keyof UsersMePartialUpdateResponses]; + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; \ No newline at end of file diff --git a/web-ui/app.tsx b/web-ui/app.tsx new file mode 100644 index 000000000..f2671df01 --- /dev/null +++ b/web-ui/app.tsx @@ -0,0 +1,36 @@ +import CssBaseline from "@mui/material/CssBaseline"; +import { ThemeProvider } from "@mui/material/styles"; +import { useState } from "react"; +import { createBrowserRouter } from "react-router"; +import { RouterProvider } from "react-router/dom"; +import { ConfigProvider } from "@/components/config-context"; +import { ConfirmProvider } from "@/components/confirm-context"; +import { CurrentUserProvider } from "@/components/current-user-context"; +import { PromptProvider } from "@/components/prompt-context"; +import { SnackbarProvider } from "@/components/snackbar-context"; +import { AppRoutes } from "@/routes"; +import { theme } from "@/theme/theme"; +import { SetupApp } from "@/views/setup/setup-app"; + +export function App() { + // A single splat route delegates to a classic tree; the data + // router wrapper keeps navigation-blocking hooks available later. The + // router is created per mount so it reads the current location. + const [router] = useState(() => createBrowserRouter([{ path: "*", element: }])); + return ( + + + + }> + + + + + + + + + + + ); +} diff --git a/web-ui/components/breadcrumbs.tsx b/web-ui/components/breadcrumbs.tsx new file mode 100644 index 000000000..12ff396c0 --- /dev/null +++ b/web-ui/components/breadcrumbs.tsx @@ -0,0 +1,50 @@ +import MuiBreadcrumbs from "@mui/material/Breadcrumbs"; +import MuiLink from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import { Link } from "react-router"; +import { MONO_FONT } from "@/theme/typography"; + +export interface Crumb { + label: string; + /** Omit on the current (last) crumb. */ + to?: string; +} + +/** Mono breadcrumb trail ("Sessions / 2026 / August / 3"). */ +export function Breadcrumbs({ crumbs }: { crumbs: Crumb[] }) { + return ( + + {crumbs.map((crumb) => + crumb.to ? ( + + {crumb.label} + + ) : ( + + {crumb.label} + + ), + )} + + ); +} diff --git a/web-ui/components/charts/chart-colors.ts b/web-ui/components/charts/chart-colors.ts new file mode 100644 index 000000000..0f2996fdf --- /dev/null +++ b/web-ui/components/charts/chart-colors.ts @@ -0,0 +1,9 @@ +import { useColorScheme } from "@mui/material/styles"; +import { CHART_SERIES_DARK, CHART_SERIES_LIGHT } from "@/theme/palette"; + +/** Validated chart series colors for the active color scheme. */ +export function useChartColors(): string[] { + const { mode, systemMode } = useColorScheme(); + const resolved = (mode === "system" ? systemMode : mode) ?? "light"; + return resolved === "dark" ? CHART_SERIES_DARK : CHART_SERIES_LIGHT; +} diff --git a/web-ui/components/charts/temperature-chart.tsx b/web-ui/components/charts/temperature-chart.tsx new file mode 100644 index 000000000..7b7899576 --- /dev/null +++ b/web-ui/components/charts/temperature-chart.tsx @@ -0,0 +1,42 @@ +import { LineChart } from "@mui/x-charts/LineChart"; +import type { Thermolog } from "@/api-client"; +import { useChartColors } from "@/components/charts/chart-colors"; +import { useConfig } from "@/components/config-context"; +import { useFormatters } from "@/components/use-formatters"; + +/** Line chart of recent temperature readings (oldest to newest). */ +export function TemperatureChart({ logs }: { logs: Thermolog[] }) { + const { temperature } = useFormatters(); + const { me } = useConfig(); + const colors = useChartColors(); + const ordered = [...logs].reverse(); + const useFahrenheit = me.site.temperature_display_units === "f"; + return ( + new Date(log.time)), + scaleType: "time", + disableLine: true, + disableTicks: true, + }, + ]} + yAxis={[{ disableLine: true, disableTicks: true, width: 40 }]} + series={[ + { + data: ordered.map((log) => (useFahrenheit ? (log.temp * 9) / 5 + 32 : log.temp)), + showMark: false, + // The cool series color: temperature reads as the teal line. + color: colors[1], + valueFormatter: (value, context) => { + const original = ordered[context.dataIndex]; + return original ? temperature(original.temp) : String(value); + }, + }, + ]} + grid={{ horizontal: true }} + height={240} + margin={{ top: 8, right: 8 }} + /> + ); +} diff --git a/web-ui/components/charts/volume-by-drinker-chart.tsx b/web-ui/components/charts/volume-by-drinker-chart.tsx new file mode 100644 index 000000000..4c8554f43 --- /dev/null +++ b/web-ui/components/charts/volume-by-drinker-chart.tsx @@ -0,0 +1,50 @@ +import { BarChart } from "@mui/x-charts/BarChart"; +import { useChartColors } from "@/components/charts/chart-colors"; +import { useFormatters } from "@/components/use-formatters"; + +export interface VolumeByDrinkerChartProps { + data: Record; + /** Show at most this many drinkers (largest first). */ + limit?: number; +} + +/** Horizontal bar chart of poured volume by drinker. */ +export function VolumeByDrinkerChart({ data, limit = 10 }: VolumeByDrinkerChartProps) { + const { volume, volumeTick } = useFormatters(); + const colors = useChartColors(); + const entries = Object.entries(data) + .sort(([, a], [, b]) => b - a) + .slice(0, limit); + return ( + name), + scaleType: "band", + width: 120, + disableLine: true, + disableTicks: true, + }, + ]} + xAxis={[ + { + disableLine: true, + disableTicks: true, + valueFormatter: (value: number) => volumeTick(value), + }, + ]} + series={[ + { + data: entries.map(([, value]) => value), + valueFormatter: (v) => (v == null ? "" : volume(v)), + }, + ]} + colors={colors} + borderRadius={4} + grid={{ vertical: true }} + height={Math.max(160, entries.length * 40)} + margin={{ top: 8, right: 8 }} + /> + ); +} diff --git a/web-ui/components/charts/volume-by-weekday-chart.tsx b/web-ui/components/charts/volume-by-weekday-chart.tsx new file mode 100644 index 000000000..6d3ebfb45 --- /dev/null +++ b/web-ui/components/charts/volume-by-weekday-chart.tsx @@ -0,0 +1,34 @@ +import { BarChart } from "@mui/x-charts/BarChart"; +import { useChartColors } from "@/components/charts/chart-colors"; +import { useFormatters } from "@/components/use-formatters"; + +// Blob keys are strftime("%w"): "0" (Sunday) through "6" (Saturday). +// Sunday-first display matches the archive calendars. +const DAY_KEYS = ["0", "1", "2", "3", "4", "5", "6"]; +const LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + +/** Bar chart of poured volume by day of week. */ +export function VolumeByWeekdayChart({ data }: { data: Record }) { + const { volume, volumeTick } = useFormatters(); + const colors = useChartColors(); + const values = DAY_KEYS.map((key) => data[key] ?? 0); + return ( + volumeTick(value), + }, + ]} + series={[{ data: values, valueFormatter: (v) => (v == null ? "" : volume(v)) }]} + colors={colors} + borderRadius={4} + grid={{ horizontal: true }} + height={240} + margin={{ top: 8, right: 8 }} + /> + ); +} diff --git a/web-ui/components/color-mode-toggle.tsx b/web-ui/components/color-mode-toggle.tsx new file mode 100644 index 000000000..380e9c2a1 --- /dev/null +++ b/web-ui/components/color-mode-toggle.tsx @@ -0,0 +1,28 @@ +import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined"; +import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined"; +import IconButton from "@mui/material/IconButton"; +import { useColorScheme } from "@mui/material/styles"; +import Tooltip from "@mui/material/Tooltip"; + +/** Light/dark switch. Follows the system until the user picks a side. */ +export function ColorModeToggle() { + const { mode, systemMode, setMode } = useColorScheme(); + const resolved = (mode === "system" ? systemMode : mode) ?? "light"; + const next = resolved === "dark" ? "light" : "dark"; + + return ( + + setMode(next)} + aria-label={`Switch to ${next} mode`} + > + {resolved === "dark" ? ( + + ) : ( + + )} + + + ); +} diff --git a/web-ui/components/config-context.tsx b/web-ui/components/config-context.tsx new file mode 100644 index 000000000..6d1590a06 --- /dev/null +++ b/web-ui/components/config-context.tsx @@ -0,0 +1,113 @@ +import Alert from "@mui/material/Alert"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import CircularProgress from "@mui/material/CircularProgress"; +import { createContext, type ReactNode, useCallback, useContext, useEffect, useState } from "react"; +import type { Me } from "@/api-client"; +import { usersMeRetrieve } from "@/api-client"; +import { toErrorMessage, unwrap } from "@/lib/api"; + +export interface ConfigValue { + /** The boot payload; always present once the app renders. */ + me: Me; + /** Refetches the boot payload (e.g. after login/logout/profile edit). */ + refresh: () => Promise; +} + +const ConfigContext = createContext(null as unknown as ConfigValue); + +type BootState = + | { status: "loading" } + | { status: "ready"; me: Me } + | { status: "setup"; kind: "setup_required" | "upgrade_required" } + | { status: "error"; message: string }; + +function isSetupError(error: unknown): "setup_required" | "upgrade_required" | null { + if (error && typeof error === "object") { + const kind = (error as { error?: unknown }).error; + if (kind === "setup_required" || kind === "upgrade_required") { + return kind; + } + } + return null; +} + +export interface ConfigProviderProps { + children: ReactNode; + /** Rendered instead of the app when the server needs setup/upgrade. */ + renderSetup?: (kind: "setup_required" | "upgrade_required") => ReactNode; +} + +/** + * Boots the app: fetches /api/users/me (which also sets the CSRF + * cookie) and blocks rendering until it resolves. Children never render + * without a boot payload. + */ +export function ConfigProvider({ children, renderSetup }: ConfigProviderProps) { + const [state, setState] = useState({ status: "loading" }); + + const load = useCallback(async () => { + try { + const me = await unwrap(usersMeRetrieve()); + setState({ status: "ready", me }); + } catch (error) { + const setupKind = isSetupError(error); + if (setupKind) { + setState({ status: "setup", kind: setupKind }); + } else { + setState({ status: "error", message: toErrorMessage(error) }); + } + } + }, []); + + useEffect(() => { + void load(); + }, [load]); + + if (state.status === "loading") { + return ( + + + + ); + } + + if (state.status === "setup") { + return ( + <> + {renderSetup?.(state.kind) ?? ( + + + {state.kind === "setup_required" + ? "This server needs to be set up." + : "This server needs an upgrade."} + + + )} + + ); + } + + if (state.status === "error") { + return ( + + Could not reach the server: {state.message} + + + ); + } + + return ( + + {children} + + ); +} + +export function useConfig(): ConfigValue { + return useContext(ConfigContext); +} diff --git a/web-ui/components/confirm-context.tsx b/web-ui/components/confirm-context.tsx new file mode 100644 index 000000000..2300b0421 --- /dev/null +++ b/web-ui/components/confirm-context.tsx @@ -0,0 +1,72 @@ +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import { createContext, type ReactNode, useCallback, useContext, useRef, useState } from "react"; + +export interface ConfirmOptions { + title: string; + message?: string; + confirmText?: string; + /** Style the confirm button as destructive (red). */ + destructive?: boolean; +} + +type ConfirmFn = (options: ConfirmOptions) => Promise; + +const ConfirmContext = createContext(null as unknown as ConfirmFn); + +/** + * Promise-based confirmation dialogs: + * + * const confirm = useConfirm(); + * if (await confirm({ title: "End keg?", destructive: true })) { ... } + */ +export function ConfirmProvider({ children }: { children: ReactNode }) { + const [options, setOptions] = useState(null); + const resolver = useRef<((result: boolean) => void) | null>(null); + + const confirm = useCallback((opts) => { + setOptions(opts); + return new Promise((resolve) => { + resolver.current = resolve; + }); + }, []); + + const close = (result: boolean) => { + setOptions(null); + resolver.current?.(result); + resolver.current = null; + }; + + return ( + + {children} + close(false)} maxWidth="xs" fullWidth> + {options?.title} + {options?.message && ( + + {options.message} + + )} + + + + + + + ); +} + +export function useConfirm(): ConfirmFn { + return useContext(ConfirmContext); +} diff --git a/web-ui/components/current-user-context.tsx b/web-ui/components/current-user-context.tsx new file mode 100644 index 000000000..826ba1424 --- /dev/null +++ b/web-ui/components/current-user-context.tsx @@ -0,0 +1,44 @@ +import { createContext, type ReactNode, useContext, useMemo } from "react"; +import type { CurrentUser } from "@/api-client"; +import { authLoginCreate, authLogoutCreate } from "@/api-client"; +import { useConfig } from "@/components/config-context"; +import { unwrap } from "@/lib/api"; + +export interface CurrentUserValue { + user: CurrentUser | null; + isLoggedIn: boolean; + isStaff: boolean; + login: (username: string, password: string) => Promise; + logout: () => Promise; + refresh: () => Promise; +} + +const CurrentUserContext = createContext(null as unknown as CurrentUserValue); + +export function CurrentUserProvider({ children }: { children: ReactNode }) { + const { me, refresh } = useConfig(); + + const value = useMemo( + () => ({ + user: me.user, + isLoggedIn: me.user !== null, + isStaff: me.user?.is_staff ?? false, + login: async (username, password) => { + await unwrap(authLoginCreate({ body: { username, password } })); + await refresh(); + }, + logout: async () => { + await unwrap(authLogoutCreate()); + await refresh(); + }, + refresh, + }), + [me.user, refresh], + ); + + return {children}; +} + +export function useCurrentUser(): CurrentUserValue { + return useContext(CurrentUserContext); +} diff --git a/web-ui/components/drink-list.tsx b/web-ui/components/drink-list.tsx new file mode 100644 index 000000000..ec225ac18 --- /dev/null +++ b/web-ui/components/drink-list.tsx @@ -0,0 +1,84 @@ +import MuiLink from "@mui/material/Link"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Tooltip from "@mui/material/Tooltip"; +import { Link } from "react-router"; +import type { Drink } from "@/api-client"; +import { EmptyState } from "@/components/empty-state"; +import { useFormatters } from "@/components/use-formatters"; +import { UserLink } from "@/components/user-link"; +import { formatDateTime } from "@/lib/format"; +import { MONO_FONT } from "@/theme/typography"; + +export interface DrinkListProps { + drinks: Drink[]; + /** Hide the keg column (e.g. on a keg page). */ + hideKeg?: boolean; + /** Hide the user column (e.g. on a drinker page). */ + hideUser?: boolean; +} + +/** + * Tabular list of drinks. Conventions: the "poured" column is the + * row's primary (accent) link; entity links are quiet; numerals and + * times are mono. + */ +export function DrinkList({ drinks, hideKeg, hideUser }: DrinkListProps) { + const { volume, compactRelative } = useFormatters(); + if (drinks.length === 0) { + return ; + } + return ( + + + + + Poured + {!hideUser && Drinker} + Volume + {!hideKeg && Keg} + Shout + + + + {drinks.map((drink) => ( + + + + + {compactRelative(drink.time)} + + + + {!hideUser && ( + + + + )} + + {volume(drink.volume_ml)} + + {!hideKeg && ( + + + {drink.keg.beverage.name} + + + )} + {drink.shout} + + ))} + +
    +
    + ); +} diff --git a/web-ui/components/empty-state.tsx b/web-ui/components/empty-state.tsx new file mode 100644 index 000000000..58b2ad072 --- /dev/null +++ b/web-ui/components/empty-state.tsx @@ -0,0 +1,37 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import type { ReactNode } from "react"; + +export interface EmptyStateProps { + /** What there is none of ("No drinks yet."). */ + title: string; + /** Optional direction: what would put something here. */ + hint?: string; + /** Optional action (a button or link). */ + action?: ReactNode; +} + +/** Quiet, consistent empty state for lists and tables. */ +export function EmptyState({ title, hint, action }: EmptyStateProps) { + return ( + + {title} + {hint && ( + + {hint} + + )} + {action && {action}} + + ); +} diff --git a/web-ui/components/event-timeline.tsx b/web-ui/components/event-timeline.tsx new file mode 100644 index 000000000..1dc04f14c --- /dev/null +++ b/web-ui/components/event-timeline.tsx @@ -0,0 +1,160 @@ +import SportsBarOutlinedIcon from "@mui/icons-material/SportsBarOutlined"; +import Avatar from "@mui/material/Avatar"; +import Box from "@mui/material/Box"; +import MuiLink from "@mui/material/Link"; +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import Typography from "@mui/material/Typography"; +import { Link } from "react-router"; +import type { SystemEvent } from "@/api-client"; +import { EmptyState } from "@/components/empty-state"; +import { useFormatters } from "@/components/use-formatters"; +import { UserLink } from "@/components/user-link"; +import { MONO_FONT } from "@/theme/typography"; + +function KegLink({ event }: { event: SystemEvent }) { + if (!event.keg) { + return <>A keg; + } + return ( + + {event.keg.beverage.name} + + ); +} + +/** One-line sentence for an event ("alice poured 12.0 oz"). */ +function EventSentence({ event }: { event: SystemEvent }) { + const { volume } = useFormatters(); + + switch (event.kind) { + case "drink_poured": + return ( + <> + {" "} + {event.drink ? ( + <> + poured{" "} + + {volume(event.drink.volume_ml)} + + + ) : ( + "poured a drink" + )} + {event.drink?.shout && ( + + {" "} + — “{event.drink.shout}” + + )} + + ); + case "session_started": + return ( + <> + started{" "} + {event.session ? ( + + a new session + + ) : ( + "a new session" + )} + + ); + case "session_joined": + return ( + <> + joined the session + + ); + case "keg_tapped": + return ( + <> + was tapped + + ); + case "keg_volume_low": + return ( + <> + is running low + + ); + case "keg_ended": + return ( + <> + was finished + + ); + default: + return <>{event.kind}; + } +} + +function EventGutter({ event }: { event: SystemEvent }) { + const isKegEvent = + event.kind === "keg_tapped" || event.kind === "keg_volume_low" || event.kind === "keg_ended"; + if (isKegEvent || !event.user) { + return ( + + + + ); + } + const name = event.user.display_name || event.user.username; + return ( + + {name.charAt(0).toUpperCase()} + + ); +} + +/** Feed of recent SystemEvents: gutter · sentence · mono time. */ +export function EventTimeline({ events }: { events: SystemEvent[] }) { + const { compactRelative } = useFormatters(); + if (events.length === 0) { + return ; + } + return ( + + {events.map((event) => ( + + + + + + + {compactRelative(event.time)} + + + ))} + + ); +} diff --git a/web-ui/components/footer.tsx b/web-ui/components/footer.tsx new file mode 100644 index 000000000..3b1e8463c --- /dev/null +++ b/web-ui/components/footer.tsx @@ -0,0 +1,33 @@ +import Box from "@mui/material/Box"; +import MuiLink from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; + +/** Quiet site footer. */ +export function Footer() { + return ( + + + Powered by{" "} + + Kegbot + + + + ); +} diff --git a/web-ui/components/form-error-alert.tsx b/web-ui/components/form-error-alert.tsx new file mode 100644 index 000000000..3b6b8ec0b --- /dev/null +++ b/web-ui/components/form-error-alert.tsx @@ -0,0 +1,26 @@ +import Alert from "@mui/material/Alert"; +import type { FormErrors } from "@/lib/api"; +import { nonFieldErrors } from "@/lib/forms"; + +/** + * Form-level error messages: everything in `errors` not claimed by a + * field the form renders itself (`fields`). Renders nothing when there + * are none. + */ +export function FormErrorAlert({ + errors, + fields = [], +}: { + errors: FormErrors | null; + fields?: string[]; +}) { + return ( + <> + {nonFieldErrors(errors, fields).map((message) => ( + + {message} + + ))} + + ); +} diff --git a/web-ui/components/keg-progress.tsx b/web-ui/components/keg-progress.tsx new file mode 100644 index 000000000..b67532645 --- /dev/null +++ b/web-ui/components/keg-progress.tsx @@ -0,0 +1,74 @@ +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import type { Keg } from "@/api-client"; +import { useFormatters } from "@/components/use-formatters"; +import { kegPercentFull } from "@/lib/format"; +import { MONO_FONT } from "@/theme/typography"; + +/** + * Keg fill gauge: amber fill on a ticked track, with a mono readout. + * Low levels change the readout color, not the fill. + */ +export function KegProgress({ keg }: { keg: Keg }) { + const { volume } = useFormatters(); + const fullMl = keg.full_volume_ml ?? 0; + const spilledMl = keg.spilled_ml ?? 0; + const percent = kegPercentFull(keg.served_volume_ml, spilledMl, fullMl); + const remaining = Math.max(0, fullMl - keg.served_volume_ml - spilledMl); + const readoutColor = percent < 10 ? "error.main" : percent < 25 ? "warning.main" : "text.primary"; + + return ( + + + + {[25, 50, 75].map((tick) => ( + + ))} + + + + {percent.toFixed(0)}% FULL + + + {volume(remaining)} remaining + + + + ); +} diff --git a/web-ui/components/load-more-button.tsx b/web-ui/components/load-more-button.tsx new file mode 100644 index 000000000..3d9f396f1 --- /dev/null +++ b/web-ui/components/load-more-button.tsx @@ -0,0 +1,18 @@ +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import CircularProgress from "@mui/material/CircularProgress"; +import type { CursorList } from "@/lib/use-cursor-list"; + +/** "Load more" footer for cursor-paginated lists. */ +export function LoadMoreButton({ list }: { list: CursorList }) { + if (!list.hasMore) { + return null; + } + return ( + + + + ); +} diff --git a/web-ui/components/loading-zone.tsx b/web-ui/components/loading-zone.tsx new file mode 100644 index 000000000..af7ac7e3c --- /dev/null +++ b/web-ui/components/loading-zone.tsx @@ -0,0 +1,26 @@ +import Alert from "@mui/material/Alert"; +import Box from "@mui/material/Box"; +import CircularProgress from "@mui/material/CircularProgress"; +import type { ReactNode } from "react"; +import { toErrorMessage } from "@/lib/api"; + +export interface LoadingZoneProps { + loading: boolean; + error?: unknown; + children?: ReactNode; +} + +/** Wraps content that depends on async data. */ +export function LoadingZone({ loading, error, children }: LoadingZoneProps) { + if (loading) { + return ( + + + + ); + } + if (error) { + return {toErrorMessage(error)}; + } + return <>{children}; +} diff --git a/web-ui/components/month-calendar.tsx b/web-ui/components/month-calendar.tsx new file mode 100644 index 000000000..22a6d012b --- /dev/null +++ b/web-ui/components/month-calendar.tsx @@ -0,0 +1,133 @@ +import Box from "@mui/material/Box"; +import MuiLink from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import { Link } from "react-router"; +import type { DateParts } from "@/lib/format"; +import { monthName } from "@/lib/format"; +import { MONO_FONT } from "@/theme/typography"; + +const WEEKDAYS = ["S", "M", "T", "W", "T", "F", "S"]; + +export interface MonthCalendarProps { + year: number; + /** 1-12. */ + month: number; + /** Days of this month that have sessions (clickable, highlighted). */ + activeDays: number[]; + /** Smaller cells for the year-overview grid. */ + compact?: boolean; + /** Link the month name to the month's archive page. */ + linkMonth?: boolean; + /** Today's date (in the site timezone) for the today marker. */ + today?: DateParts; +} + +/** + * Mini calendar for the session archive: weekday-aligned day grid + * (weeks start Sunday) with session days highlighted and linked. + */ +export function MonthCalendar({ + year, + month, + activeDays, + compact, + linkMonth, + today, +}: MonthCalendarProps) { + const dayCount = new Date(year, month, 0).getDate(); + const firstWeekday = new Date(year, month - 1, 1).getDay(); + const active = new Set(activeDays); + // Compact calendars have fixed small cells; full-size ones fill their + // container with square cells. + const cellSize = compact ? { height: 26 } : { aspectRatio: "1 / 1", minHeight: 40 }; + const fontSize = compact ? "0.6875rem" : "0.875rem"; + + const title = linkMonth ? ( + 0 ? "text.primary" : "text.secondary"} + > + {monthName(month)} + + ) : ( + monthName(month) + ); + + return ( + + + {title} + + + {WEEKDAYS.map((label, index) => ( + + {label} + + ))} + {Array.from({ length: firstWeekday }, (_, index) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: leading blanks + + ))} + {Array.from({ length: dayCount }, (_, index) => { + const day = index + 1; + const isToday = today?.year === year && today.month === month && today.day === day; + const common = { + ...cellSize, + display: "flex", + alignItems: "center", + justifyContent: "center", + borderRadius: 1, + fontFamily: MONO_FONT, + fontSize, + boxShadow: isToday ? "inset 0 0 0 1px currentColor" : "none", + } as const; + if (active.has(day)) { + return ( + + {day} + + ); + } + return ( + + {day} + + ); + })} + + + ); +} diff --git a/web-ui/components/page.tsx b/web-ui/components/page.tsx new file mode 100644 index 000000000..549f35765 --- /dev/null +++ b/web-ui/components/page.tsx @@ -0,0 +1,114 @@ +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { type ReactNode, useEffect } from "react"; +import { Breadcrumbs, type Crumb } from "@/components/breadcrumbs"; +import { useConfig } from "@/components/config-context"; +import { LoadingZone } from "@/components/loading-zone"; + +const WIDTHS = { + /** Full container width: dashboards, tables. */ + wide: "none", + /** Reading/detail pages. */ + content: "880px", + /** Single-purpose forms. */ + narrow: "560px", +} as const; + +export interface PageProps { + title: string; + /** Hide the visible heading but still set the document title. */ + hideHeading?: boolean; + /** Mono label above the title ("KEG #12"). */ + eyebrow?: string; + /** Breadcrumb trail above the title; replaces the eyebrow slot. */ + breadcrumbs?: Crumb[]; + /** Metadata line under the title; may contain links/chips. */ + meta?: ReactNode; + /** Leading visual beside the title (an Avatar, usually). */ + avatar?: ReactNode; + /** Slot to the right of the heading (actions, filters). */ + headerRight?: ReactNode; + /** Content width intent. */ + width?: keyof typeof WIDTHS; + loading?: boolean; + error?: unknown; + children?: ReactNode; +} + +/** + * Standard page shell: document title, one header anatomy + * (eyebrow / avatar+title / meta / actions), loading state, and a + * declared content width. + */ +export function Page({ + title, + hideHeading, + eyebrow, + breadcrumbs, + meta, + avatar, + headerRight, + width = "wide", + loading, + error, + children, +}: PageProps) { + const { me } = useConfig(); + + useEffect(() => { + document.title = `${title} · ${me.site.title ?? "Kegbot"}`; + }, [title, me.site.title]); + + return ( + + {!hideHeading && ( + + {breadcrumbs ? ( + + + + ) : ( + eyebrow && ( + + {eyebrow} + + ) + )} + + + {avatar} + + + {title} + + {meta && ( + + {meta} + + )} + + + {headerRight} + + + )} + + {children} + + + ); +} diff --git a/web-ui/components/privacy-gate.tsx b/web-ui/components/privacy-gate.tsx new file mode 100644 index 000000000..a789509af --- /dev/null +++ b/web-ui/components/privacy-gate.tsx @@ -0,0 +1,61 @@ +import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; +import Button from "@mui/material/Button"; +import Paper from "@mui/material/Paper"; +import Typography from "@mui/material/Typography"; +import type { ReactNode } from "react"; +import { Link, useLocation } from "react-router"; +import { useConfig } from "@/components/config-context"; +import { useCurrentUser } from "@/components/current-user-context"; + +/** + * Enforces the site privacy setting client-side (the API enforces it + * server-side): members-only sites require login, staff-only sites + * require a staff account. + */ +export function PrivacyGate({ children }: { children: ReactNode }) { + const { me } = useConfig(); + const { user } = useCurrentUser(); + const location = useLocation(); + + const privacy = me.site.privacy; + const allowed = + privacy === "public" || (privacy === "members" && user !== null) || user?.is_staff === true; + + if (allowed) { + return <>{children}; + } + + const needsLogin = user === null; + return ( + + + {privacy === "staff" ? "Staff only" : "Members only"} + + {privacy === "staff" + ? "This site is only viewable by staff accounts." + : "You must log in to view this site."} + + {needsLogin && ( + + )} + + ); +} diff --git a/web-ui/components/prompt-context.tsx b/web-ui/components/prompt-context.tsx new file mode 100644 index 000000000..e71930d1c --- /dev/null +++ b/web-ui/components/prompt-context.tsx @@ -0,0 +1,88 @@ +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogTitle from "@mui/material/DialogTitle"; +import TextField from "@mui/material/TextField"; +import { createContext, type ReactNode, useCallback, useContext, useRef, useState } from "react"; + +export interface PromptOptions { + title: string; + /** Input label ("Volume (mL)"). */ + label: string; + initialValue?: string; + confirmText?: string; + /** HTML input type; "number" and "password" are the common ones. */ + type?: string; + helperText?: string; +} + +type PromptFn = (options: PromptOptions) => Promise; + +const PromptContext = createContext(null as unknown as PromptFn); + +/** + * Promise-based single-input dialogs (the civilized window.prompt): + * + * const prompt = usePrompt(); + * const volume = await prompt({ title: "Record spill", label: "Volume (mL)", type: "number" }); + * if (volume !== null) { ... } + */ +export function PromptProvider({ children }: { children: ReactNode }) { + const [options, setOptions] = useState(null); + const [value, setValue] = useState(""); + const resolver = useRef<((result: string | null) => void) | null>(null); + + const prompt = useCallback((opts) => { + setOptions(opts); + setValue(opts.initialValue ?? ""); + return new Promise((resolve) => { + resolver.current = resolve; + }); + }, []); + + const close = (result: string | null) => { + setOptions(null); + resolver.current?.(result); + resolver.current = null; + }; + + return ( + + {children} + close(null)} maxWidth="xs" fullWidth> + {options?.title} + +
    { + e.preventDefault(); + close(value); + }} + > + setValue(e.target.value)} + /> + +
    + + + + +
    +
    + ); +} + +export function usePrompt(): PromptFn { + return useContext(PromptContext); +} diff --git a/web-ui/components/require-auth.tsx b/web-ui/components/require-auth.tsx new file mode 100644 index 000000000..ed1bcc0a7 --- /dev/null +++ b/web-ui/components/require-auth.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from "react"; +import { Navigate, useLocation } from "react-router"; +import { useCurrentUser } from "@/components/current-user-context"; + +/** Redirects anonymous users to login (with a return path). */ +export function RequireAuth({ children, staff = false }: { children: ReactNode; staff?: boolean }) { + const { user } = useCurrentUser(); + const location = useLocation(); + + if (!user) { + return ( + + ); + } + if (staff && !user.is_staff) { + return ; + } + return <>{children}; +} diff --git a/web-ui/components/section.tsx b/web-ui/components/section.tsx new file mode 100644 index 000000000..d57f56de7 --- /dev/null +++ b/web-ui/components/section.tsx @@ -0,0 +1,36 @@ +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import type { ReactNode } from "react"; + +export interface SectionProps { + /** Eyebrow label; rendered mono-uppercase ("ON TAP"). */ + label: string; + /** Slot on the right of the eyebrow row (actions, filters, counts). */ + action?: ReactNode; + children: ReactNode; +} + +/** Standard content section: mono eyebrow label over the content. */ +export function Section({ label, action, children }: SectionProps) { + return ( + + + + {label} + + {action} + + {children} + + ); +} diff --git a/web-ui/components/session-volume-list.tsx b/web-ui/components/session-volume-list.tsx new file mode 100644 index 000000000..8ac6cd1b5 --- /dev/null +++ b/web-ui/components/session-volume-list.tsx @@ -0,0 +1,46 @@ +import MuiLink from "@mui/material/Link"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableRow from "@mui/material/TableRow"; +import { Link } from "react-router"; +import { EmptyState } from "@/components/empty-state"; +import { useFormatters } from "@/components/use-formatters"; +import { MONO_FONT } from "@/theme/typography"; + +/** + * Session list derived from a stats blob's volume_by_session map + * (newest session id first). + */ +export function SessionVolumeList({ + volumeBySession, +}: { + volumeBySession: Record; +}) { + const { volume } = useFormatters(); + const entries = Object.entries(volumeBySession).sort(([a], [b]) => Number(b) - Number(a)); + if (entries.length === 0) { + return ; + } + return ( + + + + {entries.map(([sessionId, sessionVolume]) => ( + + + + Session #{sessionId} + + + + {volume(sessionVolume)} + + + ))} + +
    +
    + ); +} diff --git a/web-ui/components/snackbar-context.tsx b/web-ui/components/snackbar-context.tsx new file mode 100644 index 000000000..d92e45896 --- /dev/null +++ b/web-ui/components/snackbar-context.tsx @@ -0,0 +1,54 @@ +import Alert, { type AlertColor } from "@mui/material/Alert"; +import Snackbar from "@mui/material/Snackbar"; +import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react"; + +export interface SnackbarValue { + showMessage: (message: string, severity?: AlertColor) => void; +} + +const SnackbarContext = createContext(null as unknown as SnackbarValue); + +interface Message { + id: number; + text: string; + severity: AlertColor; +} + +let nextId = 1; + +export function SnackbarProvider({ children }: { children: ReactNode }) { + const [current, setCurrent] = useState(null); + + const showMessage = useCallback((text: string, severity: AlertColor = "success") => { + setCurrent({ id: nextId++, text, severity }); + }, []); + + const value = useMemo(() => ({ showMessage }), [showMessage]); + + return ( + + {children} + { + if (reason !== "clickaway") { + setCurrent(null); + } + }} + anchorOrigin={{ vertical: "bottom", horizontal: "center" }} + > + {current ? ( + setCurrent(null)} variant="filled"> + {current.text} + + ) : undefined} + + + ); +} + +export function useSnackbar(): SnackbarValue { + return useContext(SnackbarContext); +} diff --git a/web-ui/components/stat-badges.tsx b/web-ui/components/stat-badges.tsx new file mode 100644 index 000000000..2ccbcf48b --- /dev/null +++ b/web-ui/components/stat-badges.tsx @@ -0,0 +1,80 @@ +import Box from "@mui/material/Box"; +import Paper from "@mui/material/Paper"; +import Typography from "@mui/material/Typography"; +import type { ReactNode } from "react"; +import { useFormatters } from "@/components/use-formatters"; +import type { StatsBlob } from "@/lib/stats"; +import { MONO_FONT } from "@/theme/typography"; + +export interface StatCell { + value: ReactNode; + caption: string; +} + +function Cell({ value, caption, index }: StatCell & { index: number }) { + return ( + 0 ? 1 : 0 }, + borderTop: { xs: index >= 2 ? 1 : 0, sm: 0 }, + borderStyle: "solid", + borderRight: 0, + borderBottom: 0, + minWidth: 0, + }} + > + + {value} + + + {caption} + + + ); +} + +/** Headline stat strip: one surface, hairline-divided cells. */ +export function StatStrip({ cells }: { cells: StatCell[] }) { + return ( + + {cells.map((cell, index) => ( + + ))} + + ); +} + +/** Site-wide headline stats derived from a stats blob. */ +export function StatBadges({ stats }: { stats: StatsBlob }) { + const { volume } = useFormatters(); + return ( + + ); +} diff --git a/web-ui/components/tap-card-live.tsx b/web-ui/components/tap-card-live.tsx new file mode 100644 index 000000000..1ac679e13 --- /dev/null +++ b/web-ui/components/tap-card-live.tsx @@ -0,0 +1,29 @@ +import type { KegTap } from "@/api-client"; +import { thermoLogsList } from "@/api-client"; +import { TapCard } from "@/components/tap-card"; +import { useFormatters } from "@/components/use-formatters"; +import { unwrap } from "@/lib/api"; +import { useAsyncData } from "@/lib/use-async-data"; + +/** TapCard that also fetches the latest temperature for the tap's sensor. */ +export function TapCardLive({ tap, large }: { tap: KegTap; large?: boolean }) { + const { temperature } = useFormatters(); + const sensorId = tap.temperature_sensor_id; + const reading = useAsyncData( + async () => { + const page = await unwrap(thermoLogsList({ query: { sensor: sensorId, page_size: 1 } })); + return page.results?.[0] ?? null; + }, + { deps: [sensorId], enabled: sensorId != null, pollMs: 60_000 }, + ); + + const tempC = reading.data?.temp ?? null; + return ( + + ); +} diff --git a/web-ui/components/tap-card.tsx b/web-ui/components/tap-card.tsx new file mode 100644 index 000000000..ab130e5ad --- /dev/null +++ b/web-ui/components/tap-card.tsx @@ -0,0 +1,104 @@ +import Box from "@mui/material/Box"; +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import MuiLink from "@mui/material/Link"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { Link } from "react-router"; +import type { KegTap } from "@/api-client"; +import { KegProgress } from "@/components/keg-progress"; +import { MONO_FONT } from "@/theme/typography"; + +export interface TapCardProps { + tap: KegTap; + /** Latest temperature at this tap, °C. */ + temperatureC?: number | null; + temperatureLabel?: string; + /** Kiosk sizing: larger name and gauge. */ + large?: boolean; +} + +/** + * "On tap" card. Fixed internal grid — eyebrow row, dominant beverage + * name, one metadata line, gauge band bottom-aligned — so a row of taps + * lines up. + */ +export function TapCard({ tap, temperatureC, temperatureLabel, large }: TapCardProps) { + const keg = tap.current_keg; + const beverage = keg?.beverage; + const metadata = beverage + ? [ + beverage.producer.name, + beverage.style, + beverage.abv_percent != null ? `${beverage.abv_percent}% ABV` : null, + ] + .filter(Boolean) + .join(" · ") + : ""; + + return ( + + + + + {tap.name} + + {temperatureC != null && ( + + {temperatureLabel} + + )} + + {keg && beverage ? ( + <> + + + + {beverage.name} + + + {metadata && ( + + {metadata} + + )} + + + + ) : ( + + Tap is empty. + + )} + + + ); +} diff --git a/web-ui/components/use-formatters.ts b/web-ui/components/use-formatters.ts new file mode 100644 index 000000000..50b9b724e --- /dev/null +++ b/web-ui/components/use-formatters.ts @@ -0,0 +1,38 @@ +import { useMemo } from "react"; +import { useConfig } from "@/components/config-context"; +import { + formatCompactRelative, + formatRelativeTime, + formatTemperature, + formatVolume, + formatVolumeTick, + type TemperatureUnits, + type VolumeUnits, +} from "@/lib/format"; + +export interface Formatters { + volume: (volumeMl: number) => string; + /** Terse volume for chart axis ticks: "12 oz", "3 pt", "1.5 L". */ + volumeTick: (volumeMl: number) => string; + temperature: (tempC: number) => string; + relative: (iso: string) => string; + /** Tight-gutter relative time: "now", "4m", "2h". */ + compactRelative: (iso: string) => string; +} + +/** Unit-aware formatters bound to the site's display settings. */ +export function useFormatters(): Formatters { + const { me } = useConfig(); + const volumeUnits = me.site.volume_display_units as VolumeUnits; + const temperatureUnits = me.site.temperature_display_units as TemperatureUnits; + return useMemo( + () => ({ + volume: (volumeMl) => formatVolume(volumeMl, volumeUnits), + volumeTick: (volumeMl) => formatVolumeTick(volumeMl, volumeUnits), + temperature: (tempC) => formatTemperature(tempC, temperatureUnits), + relative: (iso) => formatRelativeTime(iso), + compactRelative: (iso) => formatCompactRelative(iso), + }), + [volumeUnits, temperatureUnits], + ); +} diff --git a/web-ui/components/user-link.tsx b/web-ui/components/user-link.tsx new file mode 100644 index 000000000..add3b601c --- /dev/null +++ b/web-ui/components/user-link.tsx @@ -0,0 +1,50 @@ +import Avatar from "@mui/material/Avatar"; +import MuiLink from "@mui/material/Link"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { Link } from "react-router"; +import type { User } from "@/api-client"; + +export interface UserLinkProps { + user: User | null | undefined; + /** Avatar size in px; 0 hides the avatar. */ + avatarSize?: number; + /** + * Quiet variant for dense tables: link wears the text color (accent + * links are reserved for the row's primary column and prose). + */ + muted?: boolean; +} + +/** Avatar + username, linking to the drinker page. */ +export function UserLink({ user, avatarSize = 24, muted }: UserLinkProps) { + if (!user) { + return guest; + } + const name = user.display_name || user.username; + return ( + + {avatarSize > 0 && ( + + {name.charAt(0).toUpperCase()} + + )} + + {name} + + + ); +} diff --git a/web-ui/components/wordmark.tsx b/web-ui/components/wordmark.tsx new file mode 100644 index 000000000..5027a84d0 --- /dev/null +++ b/web-ui/components/wordmark.tsx @@ -0,0 +1,54 @@ +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { MONO_FONT } from "@/theme/typography"; + +/** + * The Kegbot wordmark: mono, uppercase, tracked out, with an amber + * tap-handle tick. Site title (when customized) rides alongside. + */ +export function Wordmark({ siteTitle }: { siteTitle?: string | null }) { + const showSiteTitle = siteTitle && siteTitle.toLowerCase() !== "kegbot"; + return ( + + + + Kegbot + + {showSiteTitle && ( + + {siteTitle} + + )} + + ); +} diff --git a/web-ui/index.html b/web-ui/index.html new file mode 100644 index 000000000..6d7920076 --- /dev/null +++ b/web-ui/index.html @@ -0,0 +1,12 @@ + + + + + + Kegbot + + +
    + + + diff --git a/web-ui/layout/main-layout.tsx b/web-ui/layout/main-layout.tsx new file mode 100644 index 000000000..88e08a8aa --- /dev/null +++ b/web-ui/layout/main-layout.tsx @@ -0,0 +1,148 @@ +import AccountCircleIcon from "@mui/icons-material/AccountCircle"; +import AppBar from "@mui/material/AppBar"; +import Avatar from "@mui/material/Avatar"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import IconButton from "@mui/material/IconButton"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import Toolbar from "@mui/material/Toolbar"; +import { useState } from "react"; +import { Link, Outlet, useLocation, useNavigate } from "react-router"; +import { ColorModeToggle } from "@/components/color-mode-toggle"; +import { useConfig } from "@/components/config-context"; +import { useCurrentUser } from "@/components/current-user-context"; +import { Footer } from "@/components/footer"; +import { Wordmark } from "@/components/wordmark"; + +function UserMenu() { + const { me } = useConfig(); + const { user } = useCurrentUser(); + const navigate = useNavigate(); + const location = useLocation(); + const [anchor, setAnchor] = useState(null); + + if (!user) { + if (me.sso_login_url) { + const redir = encodeURIComponent(window.location.origin + location.pathname); + return ( + + ); + } + return ( + + ); + } + + const mugshotUrl = user.picture?.thumbnail_url; + return ( + <> + setAnchor(e.currentTarget)} size="large"> + {mugshotUrl ? ( + + ) : ( + + )} + + setAnchor(null)}> + {user.display_name || user.username} + { + setAnchor(null); + navigate("/account"); + }} + > + My account + + {user.is_staff && ( + { + setAnchor(null); + navigate("/kegadmin"); + }} + > + Admin + + )} + { + setAnchor(null); + navigate("/accounts/logout"); + }} + > + Log out + + + + ); +} + +const NAV_ITEMS: Array<{ label: string; to: string }> = [ + { label: "Kegs", to: "/kegs" }, + { label: "Sessions", to: "/sessions" }, + { label: "Stats", to: "/stats" }, +]; + +function NavButton({ label, to }: { label: string; to: string }) { + const location = useLocation(); + const active = location.pathname === to || location.pathname.startsWith(`${to}/`); + return ( + + ); +} + +export function MainLayout() { + const { me } = useConfig(); + + return ( + + + + + + + + {NAV_ITEMS.map((item) => ( + + ))} + + + + + + + + +