From 1811328d5c35a0f61a6b060feb5dfbebf5ddc0e0 Mon Sep 17 00:00:00 2001 From: farhan Date: Tue, 23 Sep 2025 12:42:30 +0500 Subject: [PATCH 1/3] chore: original code. This commit contains original code copied from edx-platform. Purpose of keeping the first commit with original code is to get help in the review process. Reviewer just needs to open the second commit and code changes will be clearly visible. --- xblocks_contrib/video/video.py | 1338 ++++++++++++++++++++++++++++++-- 1 file changed, 1258 insertions(+), 80 deletions(-) diff --git a/xblocks_contrib/video/video.py b/xblocks_contrib/video/video.py index b11baeb4..89718bf2 100644 --- a/xblocks_contrib/video/video.py +++ b/xblocks_contrib/video/video.py @@ -1,105 +1,1283 @@ -"""TO-DO: Write a description of what this XBlock is.""" +"""Video is ungraded Xmodule for support video content. +It's new improved video block, which support additional feature: +- Can play non-YouTube video sources via in-browser HTML5 video player. +- YouTube defaults to HTML5 mode from the start. +- Speed changes in both YouTube and non-YouTube videos happen via +in-browser HTML5 video method (when in HTML5 mode). +- Navigational subtitles can be disabled altogether via an attribute +in XML. +Examples of html5 videos for manual testing: + https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.mp4 + https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.webm + https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv +""" -from importlib.resources import files -from django.utils import translation +import copy +import json +import logging +from collections import OrderedDict, defaultdict +from operator import itemgetter + +from django.conf import settings +from edx_django_utils.cache import RequestCache +from lxml import etree +from opaque_keys.edx.locator import AssetLocator +from organizations.api import get_course_organization from web_fragments.fragment import Fragment +from xblock.completable import XBlockCompletionMode from xblock.core import XBlock -from xblock.fields import Integer, Scope -from xblock.utils.resources import ResourceLoader +from xblock.fields import ScopeIds +from xblock.runtime import KvsFieldData +from xblocks_contrib.video import VideoBlock as _ExtractedVideoBlock + +from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE, ATTR_KEY_USER_ID +from openedx.core.djangoapps.video_config.models import HLSPlaybackEnabledFlag, CourseYoutubeBlockedFlag +from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE, TRANSCRIPT_FEEDBACK +from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE +from openedx.core.lib.cache_utils import request_cached +from openedx.core.lib.courses import get_course_by_id +from openedx.core.lib.license import LicenseMixin +from xmodule.contentstore.content import StaticContent +from xmodule.course_block import ( + COURSE_VIDEO_SHARING_ALL_VIDEOS, + COURSE_VIDEO_SHARING_NONE, +) +from xmodule.editing_block import EditingMixin +from xmodule.exceptions import NotFoundError +from xmodule.mako_block import MakoTemplateBlockBase +from xmodule.modulestore.inheritance import InheritanceKeyValueStore, own_metadata +from xmodule.raw_block import EmptyDataRawMixin +from xmodule.util.builtin_assets import add_css_to_fragment, add_webpack_js_to_fragment +from xmodule.validation import StudioValidation, StudioValidationMessage +from xmodule.video_block import manage_video_subtitles_save +from xmodule.x_module import ( + PUBLIC_VIEW, STUDENT_VIEW, + ResourceTemplates, shim_xmodule_js, + XModuleMixin, XModuleToXBlockMixin, +) +from xmodule.xml_block import XmlMixin, deserialize_field, is_pointer_tag, name_to_pathname +from .bumper_utils import bumperize +from .sharing_sites import sharing_sites_info_for_video +from .transcripts_utils import ( + Transcript, + VideoTranscriptsMixin, + clean_video_id, + get_endonym_or_label, + get_html5_ids, + get_transcript, + subs_filename +) +from .video_handlers import VideoStudentViewHandlers, VideoStudioViewHandlers +from .video_utils import create_youtube_string, format_xml_exception_message, get_poster, rewrite_video_url +from .video_xfields import VideoFields + +# The following import/except block for edxval is temporary measure until +# edxval is a proper XBlock Runtime Service. +# +# Here's the deal: the VideoBlock should be able to take advantage of edx-val +# (https://github.com/openedx/edx-val) to figure out what URL to give for video +# resources that have an edx_video_id specified. edx-val is a Django app, and +# including it causes tests to fail because we run common/lib tests standalone +# without Django dependencies. The alternatives seem to be: +# +# 1. Move VideoBlock out of edx-platform. +# 2. Accept the Django dependency in common/lib. +# 3. Try to import, catch the exception on failure, and check for the existence +# of edxval_api before invoking it in the code. +# 4. Make edxval an XBlock Runtime Service +# +# (1) is a longer term goal. VideoBlock should be made into an XBlock and +# extracted from edx-platform entirely. But that's expensive to do because of +# the various dependencies (like templates). Need to sort this out. +# (2) is explicitly discouraged. +# (3) is what we're doing today. The code is still functional when called within +# the context of the LMS, but does not cause failure on import when running +# standalone tests. Most VideoBlock tests tend to be in the LMS anyway, +# probably for historical reasons, so we're not making things notably worse. +# (4) is one of the next items on the backlog for edxval, and should get rid +# of this particular import silliness. It's just that I haven't made one before, +# and I was worried about trying it with my deadline constraints. +try: + import edxval.api as edxval_api +except ImportError: + edxval_api = None + +try: + from lms.djangoapps.branding.models import BrandingInfoConfig +except ImportError: + BrandingInfoConfig = None -resource_loader = ResourceLoader(__name__) +log = logging.getLogger(__name__) -# This Xblock is just to test the strucutre of xblocks-contrib +# Make '_' a no-op so we can scrape strings. Using lambda instead of +# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file +_ = lambda text: text +EXPORT_IMPORT_COURSE_DIR = 'course' +EXPORT_IMPORT_STATIC_DIR = 'static' -@XBlock.needs("i18n") -class VideoBlock(XBlock): + +@XBlock.wants('settings', 'completion', 'i18n', 'request_cache') +@XBlock.needs('mako', 'user') +class _BuiltInVideoBlock( + VideoFields, VideoTranscriptsMixin, VideoStudioViewHandlers, VideoStudentViewHandlers, + EmptyDataRawMixin, XmlMixin, EditingMixin, XModuleToXBlockMixin, + ResourceTemplates, XModuleMixin, LicenseMixin): """ - TO-DO: document what your XBlock does. + XML source example: + """ + is_extracted = False + has_custom_completion = True + completion_mode = XBlockCompletionMode.COMPLETABLE - # Fields are defined on the class. You can access them in your code as - # self.. + video_time = 0 + icon_class = 'video' - # TO-DO: delete count, and define your own fields. - count = Integer( - default=0, - scope=Scope.user_state, - help="A simple counter, to show something happening", - ) + show_in_read_only_mode = True + + tabs = [ + { + 'name': _("Basic"), + 'template': "video/transcripts.html", + 'current': True + }, + { + 'name': _("Advanced"), + 'template': "tabs/metadata-edit-tab.html" + } + ] + + mako_template = "widgets/tabs-aggregator.html" + js_module_name = "TabsEditingDescriptor" + + uses_xmodule_styles_setup = True + + def get_transcripts_for_student(self, transcripts, dest_lang=None): + """Return transcript information necessary for rendering the XModule student view. + This is more or less a direct extraction from `get_html`. + + Args: + transcripts (dict): A dict with all transcripts and a sub. + + Returns: + Tuple of (track_url, transcript_language, sorted_languages) + track_url -> subtitle download url + transcript_language -> default transcript language + sorted_languages -> dictionary of available transcript languages + """ + track_url = None + sub, other_lang = transcripts["sub"], transcripts["transcripts"] + if self.download_track: + if self.track: + track_url = self.track + elif sub or other_lang: + track_url = self.runtime.handler_url(self, 'transcript', 'download').rstrip('/?') + + transcript_language = self.get_default_transcript_language(transcripts, dest_lang) + languages = {} + for lang_code in other_lang: + try: + label = get_endonym_or_label(lang_code) + languages[lang_code] = label + except NotFoundError: + continue - # Indicates that this XBlock has been extracted from edx-platform. - is_extracted = True + if not other_lang or (other_lang and sub): + languages['en'] = 'English' - def resource_string(self, path): - """Handy helper for getting resources from our kit.""" - return files(__package__).joinpath(path).read_text(encoding="utf-8") + # OrderedDict for easy testing of rendered context in tests + sorted_languages = sorted(list(languages.items()), key=itemgetter(1)) - # TO-DO: change this view to display your data your own way. - def student_view(self, context=None): + sorted_languages = OrderedDict(sorted_languages) + return track_url, transcript_language, sorted_languages + + @property + def youtube_deprecated(self): """ - Create primary view of the VideoBlock, shown to students when viewing courses. + Return True if youtube is deprecated and hls as primary playback is enabled else False """ - if context: - pass # TO-DO: do something based on the context. + # Return False if `hls` playback feature is disabled. + if not HLSPlaybackEnabledFlag.feature_enabled(self.location.course_key): + return False - frag = Fragment() - frag.add_content( - resource_loader.render_django_template( - "templates/video.html", - { - "count": self.count, - }, - i18n_service=self.runtime.service(self, "i18n"), - ) + # check if youtube has been deprecated and hls as primary playback + # is enabled for this course + return DEPRECATE_YOUTUBE.is_enabled(self.location.course_key) + + def youtube_disabled_for_course(self): # lint-amnesty, pylint: disable=missing-function-docstring + if not self.location.context_key.is_course: + return False # Only courses have this flag + request_cache = RequestCache('youtube_disabled_for_course') + cache_response = request_cache.get_cached_response(self.location.context_key) + if cache_response.is_found: + return cache_response.value + + youtube_is_disabled = CourseYoutubeBlockedFlag.feature_enabled(self.location.course_key) + request_cache.set(self.location.context_key, youtube_is_disabled) + return youtube_is_disabled + + def prioritize_hls(self, youtube_streams, html5_sources): + """ + Decide whether hls can be prioritized as primary playback or not. + + If both the youtube and hls sources are present then make decision on flag + If only either youtube or hls is present then play whichever is present + """ + yt_present = bool(youtube_streams.strip()) if youtube_streams else False + hls_present = any(source for source in html5_sources) + + if yt_present and hls_present: + return self.youtube_deprecated + + return False + + def student_view(self, context): + """ + Return the student view. + """ + fragment = Fragment(self.get_html(context=context)) + add_css_to_fragment(fragment, 'VideoBlockDisplay.css') + add_webpack_js_to_fragment(fragment, 'VideoBlockDisplay') + shim_xmodule_js(fragment, 'Video') + return fragment + + def author_view(self, context): + """ + Renders the Studio preview view. + """ + return self.student_view(context) + + def studio_view(self, _context): + """ + Return the studio view. + """ + fragment = Fragment( + self.runtime.service(self, 'mako').render_cms_template(self.mako_template, self.get_context()) ) + add_css_to_fragment(fragment, 'VideoBlockEditor.css') + add_webpack_js_to_fragment(fragment, 'VideoBlockEditor') + shim_xmodule_js(fragment, 'TabsEditingDescriptor') + return fragment - frag.add_css(self.resource_string("static/css/video.css")) - frag.add_javascript(self.resource_string("static/js/src/video.js")) - frag.initialize_js("VideoBlock") - return frag - - # TO-DO: change this handler to perform your own actions. You may need more - # than one handler, or you may not need any handlers at all. - @XBlock.json_handler - def increment_count(self, data, suffix=""): - """ - Increments data. An example handler. - """ - if suffix: - pass # TO-DO: Use the suffix when storing data. - # Just to show data coming in... - assert data["hello"] == "world" - - self.count += 1 - return {"count": self.count} - - # TO-DO: change this to create the scenarios you'd like to see in the - # workbench while developing your XBlock. - @staticmethod - def workbench_scenarios(): - """Create canned scenario for display in the workbench.""" - return [ - ( - "VideoBlock", - """<_video_extracted/> - """, - ), - ( - "Multiple VideoBlock", - """ - <_video_extracted/> - <_video_extracted/> - <_video_extracted/> - - """, + def public_view(self, context): + """ + Returns a fragment that contains the html for the public view + """ + is_embed = context.get("public_video_embed") + + if not is_embed and getattr(self.runtime, 'suppports_state_for_anonymous_users', False): + # The new runtime can support anonymous users as fully as regular users: + return self.student_view(context) + + fragment = Fragment(self.get_html(view=PUBLIC_VIEW, context=context)) + add_css_to_fragment(fragment, 'VideoBlockDisplay.css') + add_webpack_js_to_fragment(fragment, 'VideoBlockDisplay') + shim_xmodule_js(fragment, 'Video') + return fragment + + def get_html(self, view=STUDENT_VIEW, context=None): # lint-amnesty, pylint: disable=arguments-differ, too-many-statements + """ + Return html for a given view of this block. + """ + context = context or {} + track_status = (self.download_track and self.track) + transcript_download_format = self.transcript_download_format if not track_status else None + sources = [source for source in self.html5_sources if source] + + download_video_link = None + branding_info = None + youtube_streams = "" + video_duration = None + video_status = None + + # Determine if there is an alternative source for this video + # based on user locale. This exists to support cases where + # we leverage a geography specific CDN, like China. + default_cdn_url = getattr(settings, 'VIDEO_CDN_URL', {}).get('default') + user_location = self.runtime.service(self, 'user').get_current_user().opt_attrs[ATTR_KEY_REQUEST_COUNTRY_CODE] + cdn_url = getattr(settings, 'VIDEO_CDN_URL', {}).get(user_location, default_cdn_url) + + # If we have an edx_video_id, we prefer its values over what we store + # internally for download links (source, html5_sources) and the youtube + # stream. + if self.edx_video_id and edxval_api: # lint-amnesty, pylint: disable=too-many-nested-blocks + try: + val_profiles = ["youtube", "desktop_webm", "desktop_mp4"] + + if HLSPlaybackEnabledFlag.feature_enabled(self.course_id): + val_profiles.append('hls') + + # strip edx_video_id to prevent ValVideoNotFoundError error if unwanted spaces are there. TNL-5769 + val_video_urls = edxval_api.get_urls_for_profiles(self.edx_video_id.strip(), val_profiles) + + # VAL will always give us the keys for the profiles we asked for, but + # if it doesn't have an encoded video entry for that Video + Profile, the + # value will map to `None` + + # add the non-youtube urls to the list of alternative sources + # use the last non-None non-youtube non-hls url as the link to download the video + for url in [val_video_urls[p] for p in val_profiles if p != "youtube"]: + if url: + if url not in sources: + sources.append(url) + # don't include hls urls for download + if self.download_video and not url.endswith('.m3u8'): + # function returns None when the url cannot be re-written + rewritten_link = rewrite_video_url(cdn_url, url) + if rewritten_link: + download_video_link = rewritten_link + else: + download_video_link = url + + # set the youtube url + if val_video_urls["youtube"]: + youtube_streams = "1.00:{}".format(val_video_urls["youtube"]) + + # get video duration + video_data = edxval_api.get_video_info(self.edx_video_id.strip()) + video_duration = video_data.get('duration') + video_status = video_data.get('status') + + except (edxval_api.ValInternalError, edxval_api.ValVideoNotFoundError): + # VAL raises this exception if it can't find data for the edx video ID. This can happen if the + # course data is ported to a machine that does not have the VAL data. So for now, pass on this + # exception and fallback to whatever we find in the VideoBlock. + log.warning("Could not retrieve information from VAL for edx Video ID: %s.", self.edx_video_id) + + # If the user comes from China use China CDN for html5 videos. + # 'CN' is China ISO 3166-1 country code. + # Video caching is disabled for Studio. User_location is always None in Studio. + # CountryMiddleware disabled for Studio. + if getattr(self, 'video_speed_optimizations', True) and cdn_url: + branding_info = BrandingInfoConfig.get_config().get(user_location) + + if self.edx_video_id and edxval_api and video_status != 'external': + for index, source_url in enumerate(sources): + new_url = rewrite_video_url(cdn_url, source_url) + if new_url: + sources[index] = new_url + + # If there was no edx_video_id, or if there was no download specified + # for it, we fall back on whatever we find in the VideoBlock. + if not download_video_link and self.download_video: + if self.html5_sources: + # If there are multiple html5 sources, we use the first non HLS video urls + download_video_link = next((url for url in self.html5_sources if not url.endswith('.m3u8')), None) + + transcripts = self.get_transcripts_info() + track_url, transcript_language, sorted_languages = self.get_transcripts_for_student( + transcripts=transcripts, + dest_lang=context.get("dest_lang") + ) + + cdn_eval = False + cdn_exp_group = None + + if self.youtube_disabled_for_course(): + self.youtube_streams = '' # lint-amnesty, pylint: disable=attribute-defined-outside-init + else: + self.youtube_streams = youtube_streams or create_youtube_string(self) # pylint: disable=W0201 + + settings_service = self.runtime.service(self, 'settings') # lint-amnesty, pylint: disable=unused-variable + + poster = self._poster() + completion_service = self.runtime.service(self, 'completion') + if completion_service: + completion_enabled = completion_service.completion_tracking_enabled() + else: + completion_enabled = False + + # This is the setting that controls whether the autoadvance button will be visible, not whether the + # video will autoadvance or not. + # For autoadvance controls to be shown, both the feature flag and the course setting must be true. + # This allows to enable the feature for certain courses only. + autoadvance_enabled = settings.FEATURES.get('ENABLE_AUTOADVANCE_VIDEOS', False) and \ + getattr(self, 'video_auto_advance', False) + + # This is the current status of auto-advance (not the control visibility). + # But when controls aren't visible we force it to off. The student might have once set the preference to + # true, but now staff or admin have hidden the autoadvance button and the student won't be able to disable + # it anymore; therefore we force-disable it in this case (when controls aren't visible). + autoadvance_this_video = self.auto_advance and autoadvance_enabled + is_embed = context.get('public_video_embed', False) + is_public_view = view == PUBLIC_VIEW + metadata = { + 'autoAdvance': autoadvance_this_video, + # For now, the option "data-autohide-html5" is hard coded. This option + # either enables or disables autohiding of controls and captions on mouse + # inactivity. If set to true, controls and captions will autohide for + # HTML5 sources (non-YouTube) after a period of mouse inactivity over the + # whole video. When the mouse moves (or a key is pressed while any part of + # the video player is focused), the captions and controls will be shown + # once again. + # + # There is no option in the "Advanced Editor" to set this option. However, + # this option will have an effect if changed to "True". The code on + # front-end exists. + 'autohideHtml5': False, + 'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', False), + # This won't work when we move to data that + # isn't on the filesystem + 'captionDataDir': getattr(self, 'data_dir', None), + 'completionEnabled': completion_enabled, + 'completionPercentage': settings.COMPLETION_VIDEO_COMPLETE_PERCENTAGE, + 'duration': video_duration, + 'end': self.end_time.total_seconds(), # pylint: disable=no-member + 'generalSpeed': self.global_speed, + 'lmsRootURL': settings.LMS_ROOT_URL, + 'poster': poster, + 'prioritizeHls': self.prioritize_hls(self.youtube_streams, sources), + 'publishCompletionUrl': self.runtime.handler_url(self, 'publish_completion', '').rstrip('?'), + # This is the server's guess at whether youtube is available for + # this user, based on what was recorded the last time we saw the + # user, and defaulting to True. + 'recordedYoutubeIsAvailable': self.youtube_is_available, + 'savedVideoPosition': self.saved_video_position.total_seconds(), # pylint: disable=no-member + 'saveStateEnabled': not is_public_view, + 'saveStateUrl': self.ajax_url + '/save_user_state', + # Despite the setting on the block, don't show transcript by default + # if the video is embedded in social media + 'showCaptions': json.dumps(self.show_captions and not is_embed), + 'sources': sources, + 'speed': self.speed, + 'start': self.start_time.total_seconds(), # pylint: disable=no-member + 'streams': self.youtube_streams, + 'transcriptAvailableTranslationsUrl': self.runtime.handler_url( + self, 'transcript', 'available_translations' + ).rstrip('/?'), + 'aiTranslationsUrl': settings.AI_TRANSLATIONS_API_URL, + 'transcriptLanguage': transcript_language, + 'transcriptLanguages': sorted_languages, + 'transcriptTranslationUrl': self.runtime.handler_url( + self, 'transcript', 'translation/__lang__' + ).rstrip('/?'), + 'ytApiUrl': settings.YOUTUBE['API'], + 'ytMetadataEndpoint': ( + # In the new runtime, get YouTube metadata via a handler. The handler supports anonymous users and + # can work in sandboxed iframes. In the old runtime, the JS will call the LMS's yt_video_metadata + # API endpoint directly (not an XBlock handler). + self.runtime.handler_url(self, 'yt_video_metadata') + if getattr(self.runtime, 'suppports_state_for_anonymous_users', False) else '' ), + 'ytTestTimeout': settings.YOUTUBE['TEST_TIMEOUT'], + } + + bumperize(self) + + is_video_from_same_origin = bool(download_video_link and cdn_url and download_video_link.startswith(cdn_url)) + + template_context = { + 'autoadvance_enabled': autoadvance_enabled, + 'branding_info': branding_info, + 'bumper_metadata': json.dumps(self.bumper['metadata']), # pylint: disable=E1101 + 'cdn_eval': cdn_eval, + 'cdn_exp_group': cdn_exp_group, + 'display_name': self.display_name_with_default, + 'download_video_link': download_video_link, + 'is_video_from_same_origin': is_video_from_same_origin, + 'handout': self.handout, + 'hide_downloads': is_public_view or is_embed, + 'id': self.location.html_id(), + 'block_id': str(self.location), + 'course_id': str(self.context_key), + 'video_id': str(self.edx_video_id), + 'user_id': self.get_user_id(), + 'is_embed': is_embed, + 'license': getattr(self, "license", None), + 'metadata': json.dumps(OrderedDict(metadata)), + 'poster': json.dumps(get_poster(self)), + 'track': track_url, + 'transcript_download_format': transcript_download_format, + 'transcript_download_formats_list': self.fields['transcript_download_format'].values, # lint-amnesty, pylint: disable=unsubscriptable-object + 'transcript_feedback_enabled': self.is_transcript_feedback_enabled(), + } + if self.is_public_sharing_enabled(): + public_video_url = self.get_public_video_url() + template_context['public_sharing_enabled'] = True + template_context['public_video_url'] = public_video_url + organization = get_course_organization(self.course_id) + template_context['sharing_sites_info'] = sharing_sites_info_for_video( + public_video_url, + organization=organization + ) + + return self.runtime.service(self, 'mako').render_lms_template('video.html', template_context) + + def get_course_video_sharing_override(self): + """ + Return course video sharing options override or None + """ + if not self.context_key.is_course: + return False # Only courses support this feature at all (not libraries) + try: + course = get_course_by_id(self.context_key) + return getattr(course, 'video_sharing_options', None) + + # In case the course / modulestore does something weird + except Exception as err: # pylint: disable=broad-except + log.exception(f"Error retrieving course for course ID: {self.course_id}") + return None + + def is_public_sharing_enabled(self): + """ + Is public sharing enabled for this video? + """ + if not self.context_key.is_course: + return False # Only courses support this feature at all (not libraries) + try: + # Video share feature must be enabled for sharing settings to take effect + feature_enabled = PUBLIC_VIDEO_SHARE.is_enabled(self.context_key) + except Exception as err: # pylint: disable=broad-except + log.exception(f"Error retrieving course for course ID: {self.context_key}") + return False + if not feature_enabled: + return False + + # Check if the course specifies a general setting + course_video_sharing_option = self.get_course_video_sharing_override() + + # Course can override all videos to be shared + if course_video_sharing_option == COURSE_VIDEO_SHARING_ALL_VIDEOS: + return True + + # ... or no videos to be shared + elif course_video_sharing_option == COURSE_VIDEO_SHARING_NONE: + return False + + # ... or can fall back to per-video setting + # Equivalent to COURSE_VIDEO_SHARING_PER_VIDEO or None / unset + else: + return self.public_access + + def is_transcript_feedback_enabled(self): + """ + Is transcript feedback enabled for this video? + """ + if not self.context_key.is_course: + return False # Only courses support this feature at all (not libraries) + try: + # Video transcript feedback must be enabled in order to show the widget + feature_enabled = TRANSCRIPT_FEEDBACK.is_enabled(self.context_key) + except Exception as err: # pylint: disable=broad-except + log.exception(f"Error retrieving course for course ID: {self.context_key}") + return False + return feature_enabled + + def get_user_id(self): + return self.runtime.service(self, 'user').get_current_user().opt_attrs.get(ATTR_KEY_USER_ID) + + def get_public_video_url(self): + """ + Returns the public video url + """ + return fr'{settings.LMS_ROOT_URL}/videos/{str(self.location)}' + + def validate(self): + """ + Validates the state of this Video XBlock instance. This + is the override of the general XBlock method, and it will also ask + its superclass to validate. + """ + validation = super().validate() + if not isinstance(validation, StudioValidation): + validation = StudioValidation.copy(validation) + + no_transcript_lang = [] + for lang_code, transcript in self.transcripts.items(): + if not transcript: + no_transcript_lang.append([label for code, label in settings.ALL_LANGUAGES if code == lang_code][0]) + + if no_transcript_lang: + ungettext = self.runtime.service(self, "i18n").ungettext + validation.set_summary( + StudioValidationMessage( + StudioValidationMessage.WARNING, + ungettext( + 'There is no transcript file associated with the {lang} language.', + 'There are no transcript files associated with the {lang} languages.', + len(no_transcript_lang) + ).format(lang=', '.join(sorted(no_transcript_lang))) + ) + ) + return validation + + def editor_saved(self, user, old_metadata, old_content): # lint-amnesty, pylint: disable=unused-argument + """ + Used to update video values during `self`:save method from CMS. + old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user. + old_content, same as `old_metadata` but for scope=content. + Due to nature of code flow in block.py::_save_item, before current function is called, + fields of `self` instance have been already updated, but not yet saved. + To obtain values, which were changed by user input, + one should compare own_metadata(self) and old_medatada. + Video player has two tabs, and due to nature of sync between tabs, + metadata from Basic tab is always sent when video player is edited and saved first time, for example: + {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []}, + that's why these fields will always present in old_metadata after first save. This should be fixed. + At consequent save requests html5_sources are always sent too, disregard of their change by user. + That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item). + This should be fixed too. + """ + metadata_was_changed_by_user = old_metadata != own_metadata(self) + + # There is an edge case when old_metadata and own_metadata are same and we are importing transcript from youtube + # then there is a syncing issue where html5_subs are not syncing with youtube sub, We can make sync better by + # checking if transcript is present for the video and if any html5_ids transcript is not present then trigger + # the manage_video_subtitles_save to create the missing transcript with particular html5_id. + if not metadata_was_changed_by_user and self.sub and hasattr(self, 'html5_sources'): + html5_ids = get_html5_ids(self.html5_sources) + for subs_id in html5_ids: + try: + Transcript.asset(self.location, subs_id) + except NotFoundError: + # If a transcript does not not exist with particular html5_id then there is no need to check other + # html5_ids because we have to create a new transcript with this missing html5_id by turning on + # metadata_was_changed_by_user flag. + metadata_was_changed_by_user = True + break + + if metadata_was_changed_by_user: + self.edx_video_id = self.edx_video_id and self.edx_video_id.strip() + + # We want to override `youtube_id_1_0` with val youtube profile in the first place when someone adds/edits + # an `edx_video_id` or its underlying YT val profile. Without this, override will only happen when a user + # saves the video second time. This is because of the syncing of basic and advanced video settings which + # also syncs val youtube id from basic tab's `Video Url` to advanced tab's `Youtube ID`. + if self.edx_video_id and edxval_api: + val_youtube_id = edxval_api.get_url_for_profile(self.edx_video_id, 'youtube') + if val_youtube_id and self.youtube_id_1_0 != val_youtube_id: + self.youtube_id_1_0 = val_youtube_id + + manage_video_subtitles_save( + self, + user, + old_metadata if old_metadata else None, + generate_translation=True + ) + + def save_with_metadata(self, user): + """ + Save block with updated metadata to database." + """ + self.save() + self.runtime.modulestore.update_item(self, user.id) + + @property + def editable_metadata_fields(self): + editable_fields = super().editable_metadata_fields + + settings_service = self.runtime.service(self, 'settings') + if settings_service: + xb_settings = settings_service.get_settings_bucket(self) + if not xb_settings.get("licensing_enabled", False) and "license" in editable_fields: + del editable_fields["license"] + + # Default Timed Transcript a.k.a `sub` has been deprecated and end users shall + # not be able to modify it. + editable_fields.pop('sub') + + languages = [{'label': label, 'code': lang} for lang, label in settings.ALL_LANGUAGES] + languages.sort(key=lambda l: l['label']) + editable_fields['transcripts']['custom'] = True + editable_fields['transcripts']['languages'] = languages + editable_fields['transcripts']['type'] = 'VideoTranslations' + + # We need to send ajax requests to show transcript status + # whenever edx_video_id changes on frontend. Thats why we + # are changing type to `VideoID` so that a specific + # Backbonjs view can handle it. + editable_fields['edx_video_id']['type'] = 'VideoID' + + # `public_access` is a boolean field and by default backbonejs code render it as a dropdown with 2 options + # but in our case we also need to show an input field with dropdown, the input field will show the url to + # be shared with leaners. This is not possible with default rendering logic in backbonjs code, that is why + # we are setting a new type and then do a custom rendering in backbonejs code to render the desired UI. + editable_fields['public_access']['type'] = 'PublicAccess' + editable_fields['public_access']['url'] = self.get_public_video_url() + + # construct transcripts info and also find if `en` subs exist + transcripts_info = self.get_transcripts_info() + possible_sub_ids = [self.sub, self.youtube_id_1_0] + get_html5_ids(self.html5_sources) + for sub_id in possible_sub_ids: + try: + _, sub_id, _ = get_transcript(self, lang='en', output_format=Transcript.TXT) + transcripts_info['transcripts'] = dict(transcripts_info['transcripts'], en=sub_id) + break + except NotFoundError: + continue + + editable_fields['transcripts']['value'] = transcripts_info['transcripts'] + editable_fields['transcripts']['urlRoot'] = self.runtime.handler_url( + self, + 'studio_transcript', + 'translation' + ).rstrip('/?') + editable_fields['handout']['type'] = 'FileUploader' + + return editable_fields + + @classmethod + def parse_xml_new_runtime(cls, node, runtime, keys): + """ + Implement the video block's special XML parsing requirements for the + new runtime only. For all other runtimes, use .parse_xml(). + """ + video_block = runtime.construct_xblock_from_class(cls, keys) + field_data = cls.parse_video_xml(node) + for key, val in field_data.items(): + if key not in cls.fields: # lint-amnesty, pylint: disable=unsupported-membership-test + continue # parse_video_xml returns some old non-fields like 'source' + setattr(video_block, key, cls.fields[key].from_json(val)) # lint-amnesty, pylint: disable=unsubscriptable-object + # Don't use VAL in the new runtime: + video_block.edx_video_id = None + return video_block + + @classmethod + def parse_xml(cls, node, runtime, _keys): + """ + Use `node` to construct a new block. + + See XmlMixin.parse_xml for the detailed description. + """ + url_name = node.get('url_name') + block_type = 'video' + definition_id = runtime.id_generator.create_definition(block_type, url_name) + usage_id = runtime.id_generator.create_usage(definition_id) + aside_children = [] + if is_pointer_tag(node): + filepath = cls._format_filepath(node.tag, name_to_pathname(url_name)) + node = cls.load_file(filepath, runtime.resources_fs, usage_id) + aside_children = runtime.parse_asides(node, definition_id, usage_id, runtime.id_generator) + field_data = cls.parse_video_xml(node, runtime.id_generator) + kvs = InheritanceKeyValueStore(initial_values=field_data) + field_data = KvsFieldData(kvs) + video = runtime.construct_xblock_from_class( + cls, + # We're loading a descriptor, so student_id is meaningless + # We also don't have separate notions of definition and usage ids yet, + # so we use the location for both + ScopeIds(None, block_type, definition_id, usage_id), + field_data, + ) + + # Update VAL with info extracted from `node` + video.edx_video_id = video.import_video_info_into_val( + node, + runtime.resources_fs, + getattr(runtime.id_generator, 'target_course_id', None) + ) + + if aside_children: + cls.add_applicable_asides_to_block(video, runtime, aside_children) + + return video + + def definition_to_xml(self, resource_fs): # lint-amnesty, pylint: disable=too-many-statements + """ + Returns an xml string representing this block. + """ + xml = etree.Element('video') + youtube_string = create_youtube_string(self) + if youtube_string: + xml.set('youtube', str(youtube_string)) + xml.set('url_name', self.url_name) + attrs = [ + ('display_name', self.display_name), + ('show_captions', json.dumps(self.show_captions)), + ('start_time', self.start_time), + ('end_time', self.end_time), + ('sub', self.sub), + ('download_track', json.dumps(self.download_track)), + ('download_video', json.dumps(self.download_video)) + ] + for key, value in attrs: + # Mild workaround to ensure that tests pass -- if a field + # is set to its default value, we don't write it out. + if value: + if key in self.fields and self.fields[key].is_set_on(self): # lint-amnesty, pylint: disable=unsubscriptable-object, unsupported-membership-test + try: + xml.set(key, str(value)) + except UnicodeDecodeError: + exception_message = format_xml_exception_message(self.location, key, value) + log.exception(exception_message) + # If exception is UnicodeDecodeError set value using unicode 'utf-8' scheme. + log.info("Setting xml value using 'utf-8' scheme.") + xml.set(key, str(value, 'utf-8')) + except ValueError: + exception_message = format_xml_exception_message(self.location, key, value) + log.exception(exception_message) + raise + + for source in self.html5_sources: + ele = etree.Element('source') + ele.set('src', source) + xml.append(ele) + + if self.track: + ele = etree.Element('track') + ele.set('src', self.track) + xml.append(ele) + + if self.handout: + ele = etree.Element('handout') + ele.set('src', self.handout) + xml.append(ele) + + transcripts = {} + if self.transcripts is not None: + transcripts.update(self.transcripts) + + edx_video_id = clean_video_id(self.edx_video_id) + if edxval_api and edx_video_id: + try: + # Create static dir if not created earlier. + resource_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True) + + # Backward compatible exports + # edxval exports new transcripts into the course OLX and returns a transcript + # files map so that it can also be rewritten in old transcript metadata fields + # (i.e. `self.transcripts`) on import and older open-releases (<= ginkgo), + # who do not have deprecated contentstore yet, can also import and use new-style + # transcripts into their openedX instances. + exported_metadata = edxval_api.export_to_xml( + video_id=edx_video_id, + resource_fs=resource_fs, + static_dir=EXPORT_IMPORT_STATIC_DIR, + course_id=self.scope_ids.usage_id.context_key.for_branch(None), + ) + # Update xml with edxval metadata + xml.append(exported_metadata['xml']) + + # we don't need sub if english transcript + # is also in new transcripts. + new_transcripts = exported_metadata['transcripts'] + transcripts.update(new_transcripts) + if new_transcripts.get('en'): + xml.set('sub', '') + + except edxval_api.ValVideoNotFoundError: + pass + else: + if transcripts.get('en'): + xml.set('sub', '') + + if transcripts: + # Update `transcripts` attribute in the xml + xml.set('transcripts', json.dumps(transcripts, sort_keys=True)) + + # Sorting transcripts for easy testing of resulting xml + for transcript_language in sorted(transcripts.keys()): + ele = etree.Element('transcript') + ele.set('language', transcript_language) + ele.set('src', transcripts[transcript_language]) + xml.append(ele) + + # handle license specifically + self.add_license_to_xml(xml) + + return xml + + def create_youtube_url(self, youtube_id): + """ + + Args: + youtube_id: The ID of the video to create a link for + + Returns: + A full youtube url to the video whose ID is passed in + """ + if youtube_id: + return f'https://www.youtube.com/watch?v={youtube_id}' + else: + return '' + + def get_context(self): + """ + Extend context by data for transcript basic tab. + """ + _context = MakoTemplateBlockBase.get_context(self) + _context.update({ + 'tabs': self.tabs, + 'html_id': self.location.html_id(), # element_id + 'data': self.data, + }) + + metadata_fields = copy.deepcopy(self.editable_metadata_fields) + + display_name = metadata_fields['display_name'] + video_url = metadata_fields['html5_sources'] + video_id = metadata_fields['edx_video_id'] + youtube_id_1_0 = metadata_fields['youtube_id_1_0'] + + def get_youtube_link(video_id): + """ + Returns the fully-qualified YouTube URL for the given video identifier + """ + # First try a lookup in VAL. If we have a YouTube entry there, it overrides the + # one passed in. + if self.edx_video_id and edxval_api: + val_youtube_id = edxval_api.get_url_for_profile(self.edx_video_id, "youtube") + if val_youtube_id: + video_id = val_youtube_id + + return self.create_youtube_url(video_id) + + _ = self.runtime.service(self, "i18n").ugettext + video_url.update({ + 'help': _('The URL for your video. This can be a YouTube URL or a link to an .mp4, .ogg, or ' + '.webm video file hosted elsewhere on the Internet.'), + 'display_name': _('Default Video URL'), + 'field_name': 'video_url', + 'type': 'VideoList', + 'default_value': [get_youtube_link(youtube_id_1_0['default_value'])] + }) + + source_url = self.create_youtube_url(youtube_id_1_0['value']) + # First try a lookup in VAL. If any video encoding is found given the video id then + # override the source_url with it. + if self.edx_video_id and edxval_api: + + val_profiles = ['youtube', 'desktop_webm', 'desktop_mp4'] + if HLSPlaybackEnabledFlag.feature_enabled(self.scope_ids.usage_id.context_key.for_branch(None)): + val_profiles.append('hls') + + # Get video encodings for val profiles. + val_video_encodings = edxval_api.get_urls_for_profiles(self.edx_video_id, val_profiles) + + # VAL's youtube source has greater priority over external youtube source. + if val_video_encodings.get('youtube'): + source_url = self.create_youtube_url(val_video_encodings['youtube']) + + # If no youtube source is provided externally or in VAl, update source_url in order: hls > mp4 and webm + if not source_url: + if val_video_encodings.get('hls'): + source_url = val_video_encodings['hls'] + elif val_video_encodings.get('desktop_mp4'): + source_url = val_video_encodings['desktop_mp4'] + elif val_video_encodings.get('desktop_webm'): + source_url = val_video_encodings['desktop_webm'] + + # Only add if html5 sources do not already contain source_url. + if source_url and source_url not in video_url['value']: + video_url['value'].insert(0, source_url) + + metadata = { + 'display_name': display_name, + 'video_url': video_url, + 'edx_video_id': video_id + } + + _context.update({'transcripts_basic_tab_metadata': metadata}) + return _context + + @classmethod + def _parse_youtube(cls, data): + """ + Parses a string of Youtube IDs such as "1.0:AXdE34_U,1.5:VO3SxfeD" + into a dictionary. Necessary for backwards compatibility with + XML-based courses. + """ + ret = {'0.75': '', '1.00': '', '1.25': '', '1.50': ''} + + videos = data.split(',') + for video in videos: + pieces = video.split(':') + try: + speed = '%.2f' % float(pieces[0]) # normalize speed + + # Handle the fact that youtube IDs got double-quoted for a period of time. + # Note: we pass in "VideoFields.youtube_id_1_0" so we deserialize as a String-- + # it doesn't matter what the actual speed is for the purposes of deserializing. + youtube_id = deserialize_field(cls.youtube_id_1_0, pieces[1]) + ret[speed] = youtube_id + except (ValueError, IndexError): + log.warning('Invalid YouTube ID: %s', video) + return ret + + @classmethod + def parse_video_xml(cls, xml, id_generator=None): + """ + Parse video fields out of xml_data. The fields are set if they are + present in the XML. + + Arguments: + id_generator is used to generate course-specific urls and identifiers + """ + if isinstance(xml, str): + xml = etree.fromstring(xml) + + field_data = {} + + # Convert between key types for certain attributes -- + # necessary for backwards compatibility. + conversions = { + # example: 'start_time': cls._example_convert_start_time + } + + # Convert between key names for certain attributes -- + # necessary for backwards compatibility. + compat_keys = { + 'from': 'start_time', + 'to': 'end_time' + } + sources = xml.findall('source') + if sources: + field_data['html5_sources'] = [ele.get('src') for ele in sources] + + track = xml.find('track') + if track is not None: + field_data['track'] = track.get('src') + + handout = xml.find('handout') + if handout is not None: + field_data['handout'] = handout.get('src') + + transcripts = xml.findall('transcript') + if transcripts: + field_data['transcripts'] = {tr.get('language'): tr.get('src') for tr in transcripts} + + for attr, value in xml.items(): + if attr in compat_keys: # lint-amnesty, pylint: disable=consider-using-get + attr = compat_keys[attr] + if attr in cls.metadata_to_strip + ('url_name', 'name'): + continue + if attr == 'youtube': + speeds = cls._parse_youtube(value) + for speed, youtube_id in speeds.items(): + # should have made these youtube_id_1_00 for + # cleanliness, but hindsight doesn't need glasses + normalized_speed = speed[:-1] if speed.endswith('0') else speed + # If the user has specified html5 sources, make sure we don't use the default video + if youtube_id != '' or 'html5_sources' in field_data: + field_data['youtube_id_{}'.format(normalized_speed.replace('.', '_'))] = youtube_id + elif attr in conversions: + field_data[attr] = conversions[attr](value) + elif attr not in cls.fields: # lint-amnesty, pylint: disable=unsupported-membership-test + field_data.setdefault('xml_attributes', {})[attr] = value + else: + # We export values with json.dumps (well, except for Strings, but + # for about a month we did it for Strings also). + field_data[attr] = deserialize_field(cls.fields[attr], value) # lint-amnesty, pylint: disable=unsubscriptable-object + + course_id = getattr(id_generator, 'target_course_id', None) + # Update the handout location with current course_id + if 'handout' in field_data and course_id: + handout_location = StaticContent.get_location_from_path(field_data['handout']) + if isinstance(handout_location, AssetLocator): + handout_new_location = StaticContent.compute_location(course_id, handout_location.path) + field_data['handout'] = StaticContent.serialize_asset_key_with_slash(handout_new_location) + + # For backwards compatibility: Add `source` if XML doesn't have `download_video` + # attribute. + if 'download_video' not in field_data and sources: + field_data['source'] = field_data['html5_sources'][0] + + # For backwards compatibility: if XML doesn't have `download_track` attribute, + # it means that it is an old format. So, if `track` has some value, + # `download_track` needs to have value `True`. + if 'download_track' not in field_data and track is not None: + field_data['download_track'] = True + + # load license if it exists + field_data = LicenseMixin.parse_license_from_xml(field_data, xml) + + return field_data + + def import_video_info_into_val(self, xml, resource_fs, course_id): + """ + Import parsed video info from `xml` into edxval. + + Arguments: + xml (lxml object): xml representation of video to be imported. + resource_fs (OSFS): Import file system. + course_id (str): course id + """ + edx_video_id = clean_video_id(self.edx_video_id) + + # Create video_asset is not already present. + video_asset_elem = xml.find('video_asset') + if video_asset_elem is None: + video_asset_elem = etree.Element('video_asset') + + # This will be a dict containing the list of names of the external transcripts. + # Example: + # { + # 'en': ['The_Flash.srt', 'Harry_Potter.srt'], + # 'es': ['Green_Arrow.srt'] + # } + external_transcripts = defaultdict(list) + + # Add trancript from self.sub and self.youtube_id_1_0 fields. + external_transcripts['en'] = [ + subs_filename(transcript, 'en') + for transcript in [self.sub, self.youtube_id_1_0] if transcript ] - @staticmethod - def get_dummy(): + for language_code, transcript in self.transcripts.items(): + external_transcripts[language_code].append(transcript) + + if edxval_api: + edx_video_id = edxval_api.import_from_xml( + video_asset_elem, + edx_video_id, + resource_fs, + EXPORT_IMPORT_STATIC_DIR, + external_transcripts, + course_id=course_id + ) + return edx_video_id + + def index_dictionary(self): + xblock_body = super().index_dictionary() + video_body = { + "display_name": self.display_name, + } + + def _update_transcript_for_index(language=None): + """ Find video transcript - if not found, don't update index """ + try: + transcript = get_transcript(self, lang=language, output_format=Transcript.TXT)[0].replace("\n", " ") + transcript_index_name = f"transcript_{language if language else self.transcript_language}" + video_body.update({transcript_index_name: transcript}) + except NotFoundError: + pass + + if self.sub: + _update_transcript_for_index() + + # Check to see if there are transcripts in other languages besides default transcript + if self.transcripts: + for language in self.transcripts.keys(): + _update_transcript_for_index(language) + + if "content" in xblock_body: + xblock_body["content"].update(video_body) + else: + xblock_body["content"] = video_body + xblock_body["content_type"] = "Video" + + return xblock_body + + @property + def request_cache(self): + """ + Returns the request_cache from the runtime. + """ + return self.runtime.service(self, "request_cache") + + @classmethod + @request_cached( + request_cache_getter=lambda args, kwargs: args[1], + ) + def get_cached_val_data_for_course(cls, request_cache, video_profile_names, course_id): # lint-amnesty, pylint: disable=unused-argument """ - Generate initial i18n with dummy method. + Returns the VAL data for the requested video profiles for the given course. """ - return translation.gettext_noop("Dummy") + return edxval_api.get_video_info_for_course_and_profiles(str(course_id), video_profile_names) + + def student_view_data(self, context=None): + """ + Returns a JSON representation of the student_view of this XModule. + The contract of the JSON content is between the caller and the particular XModule. + """ + context = context or {} + + # If the "only_on_web" field is set on this video, do not return the rest of the video's data + # in this json view, since this video is to be accessed only through its web view." + if self.only_on_web: + return {"only_on_web": True} + + encoded_videos = {} + val_video_data = {} + all_sources = self.html5_sources or [] + + # Check in VAL data first if edx_video_id exists + if self.edx_video_id: + video_profile_names = context.get("profiles", ["mobile_low", 'desktop_mp4', 'desktop_webm', 'mobile_high']) + if HLSPlaybackEnabledFlag.feature_enabled(self.location.course_key) and 'hls' not in video_profile_names: + video_profile_names.append('hls') + + # get and cache bulk VAL data for course + val_course_data = self.get_cached_val_data_for_course( + self.request_cache, + video_profile_names, + self.location.course_key, + ) + val_video_data = val_course_data.get(self.edx_video_id, {}) + + # Get the encoded videos if data from VAL is found + if val_video_data: + encoded_videos = val_video_data.get('profiles', {}) + + # If information for this edx_video_id is not found in the bulk course data, make a + # separate request for this individual edx_video_id, unless cache misses are disabled. + # This is useful/required for videos that don't have a course designated, such as the introductory video + # that is shared across many courses. However, this results in a separate database request so watch + # out for any performance hit if many such videos exist in a course. Set the 'allow_cache_miss' parameter + # to False to disable this fall back. + elif context.get("allow_cache_miss", "True").lower() == "true": + try: + val_video_data = edxval_api.get_video_info(self.edx_video_id) + # Unfortunately, the VAL API is inconsistent in how it returns the encodings, so remap here. + for enc_vid in val_video_data.pop('encoded_videos'): + if enc_vid['profile'] in video_profile_names: + encoded_videos[enc_vid['profile']] = {key: enc_vid[key] for key in ["url", "file_size"]} + except edxval_api.ValVideoNotFoundError: + pass + + # Fall back to other video URLs in the video block if not found in VAL + if not encoded_videos: + if all_sources: + encoded_videos["fallback"] = { + "url": all_sources[0], + "file_size": 0, # File size is unknown for fallback URLs + } + + # Include youtube link if there is no encoding for mobile- ie only a fallback URL or no encodings at all + # We are including a fallback URL for older versions of the mobile app that don't handle Youtube urls + if self.youtube_id_1_0: + encoded_videos["youtube"] = { + "url": self.create_youtube_url(self.youtube_id_1_0), + "file_size": 0, # File size is not relevant for external link + } + + available_translations = self.available_translations(self.get_transcripts_info()) + transcripts = { + lang: self.runtime.handler_url(self, 'transcript', 'download', query="lang=" + lang, thirdparty=True) + for lang in available_translations + } + + return { + "only_on_web": self.only_on_web, + "duration": val_video_data.get('duration', None), + "transcripts": transcripts, + "encoded_videos": encoded_videos, + "all_sources": all_sources, + } + + def _poster(self): + """ + Helper to get poster info from edxval + """ + if edxval_api and self.edx_video_id: + return edxval_api.get_course_video_image_url( + course_id=self.scope_ids.usage_id.context_key.for_branch(None), + edx_video_id=self.edx_video_id.strip() + ) + return None + + +VideoBlock = ( + _ExtractedVideoBlock if settings.USE_EXTRACTED_VIDEO_BLOCK + else _BuiltInVideoBlock +) +VideoBlock.__name__ = "VideoBlock" From 0c0d3e5a0e238b18404ede37185e40a293feb81d Mon Sep 17 00:00:00 2001 From: farhan Date: Tue, 23 Sep 2025 12:49:52 +0500 Subject: [PATCH 2/3] feat: Adds Video XBlock extracted from edx-platform repository --- .gitignore | 4 + requirements/base.in | 4 + xblocks_contrib/utils/__init__.py | 0 xblocks_contrib/utils/utils.py | 26 + xblocks_contrib/video/_transcripts_utils.py | 513 +++ xblocks_contrib/video/ajax_handler_mixin.py | 41 + xblocks_contrib/video/bumper_utils.py | 146 + xblocks_contrib/video/cache_utils.py | 240 ++ xblocks_contrib/video/constants.py | 22 + xblocks_contrib/video/content.py | 36 + xblocks_contrib/video/django.py | 31 + xblocks_contrib/video/exceptions.py | 56 + xblocks_contrib/video/load_static.py | 67 + xblocks_contrib/video/mixin.py | 55 + xblocks_contrib/video/package-lock.json | 2895 +++++++++++++++++ xblocks_contrib/video/package.json | 25 + xblocks_contrib/video/static/css/video.css | 1160 ++++++- .../video/static/js/dist/manifest.json | 4 + .../video/static/js/dist/video-xblock.js | 2 + .../js/dist/video-xblock.js.LICENSE.txt | 159 + .../video/static/js/src/00_async_process.js | 52 + .../video/static/js/src/00_component.js | 81 + .../video/static/js/src/00_i18n.js | 35 + .../video/static/js/src/00_iterator.js | 83 + .../video/static/js/src/00_resizer.js | 236 ++ .../video/static/js/src/00_sjson.js | 108 + .../video/static/js/src/00_video_storage.js | 96 + .../video/static/js/src/01_initialize.js | 845 +++++ .../video/static/js/src/025_focus_grabber.js | 132 + .../video/static/js/src/02_html5_hls_video.js | 145 + .../video/static/js/src/02_html5_video.js | 380 +++ .../js/src/035_video_accessible_menu.js | 65 + .../static/js/src/036_video_social_sharing.js | 85 + .../js/src/037_video_transcript_feedback.js | 240 ++ .../video/static/js/src/03_video_player.js | 911 ++++++ .../video/static/js/src/04_video_control.js | 164 + .../static/js/src/04_video_full_screen.js | 309 ++ .../static/js/src/05_video_quality_control.js | 176 + .../static/js/src/06_video_progress_slider.js | 360 ++ .../static/js/src/07_video_volume_control.js | 554 ++++ .../js/src/08_video_auto_advance_control.js | 126 + .../static/js/src/08_video_speed_control.js | 417 +++ .../static/js/src/095_video_context_menu.js | 699 ++++ .../video/static/js/src/09_bumper.js | 108 + .../video/static/js/src/09_completion.js | 201 ++ .../static/js/src/09_events_bumper_plugin.js | 112 + .../video/static/js/src/09_events_plugin.js | 177 + .../static/js/src/09_play_pause_control.js | 96 + .../static/js/src/09_play_placeholder.js | 84 + .../static/js/src/09_play_skip_control.js | 86 + .../video/static/js/src/09_poster.js | 62 + .../static/js/src/09_save_state_plugin.js | 131 + .../video/static/js/src/09_skip_control.js | 72 + .../video/static/js/src/09_video_caption.js | 1419 ++++++++ .../video/static/js/src/10_commands.js | 108 + .../video/static/js/src/10_main.js | 133 + .../video/static/js/src/utils/time.js | 41 + xblocks_contrib/video/static/js/src/video.js | 38 - xblocks_contrib/video/templates/video.html | 249 +- xblocks_contrib/video/tests/test_toggles.py | 103 + xblocks_contrib/video/toggles.py | 74 + xblocks_contrib/video/transcripts_utils.py | 1228 +++++++ xblocks_contrib/video/utils.py | 156 + xblocks_contrib/video/validation.py | 128 + xblocks_contrib/video/video.py | 1003 +++--- xblocks_contrib/video/video_handlers.py | 613 ++++ xblocks_contrib/video/video_utils.py | 143 + xblocks_contrib/video/video_xfields.py | 345 ++ .../video/webpack.common.config.js | 31 + xblocks_contrib/video/webpack.dev.config.js | 10 + xblocks_contrib/video/webpack.prod.config.js | 9 + xblocks_contrib/word_cloud/word_cloud.py | 2 +- 72 files changed, 18256 insertions(+), 491 deletions(-) create mode 100644 xblocks_contrib/utils/__init__.py create mode 100644 xblocks_contrib/utils/utils.py create mode 100644 xblocks_contrib/video/_transcripts_utils.py create mode 100644 xblocks_contrib/video/ajax_handler_mixin.py create mode 100644 xblocks_contrib/video/bumper_utils.py create mode 100644 xblocks_contrib/video/cache_utils.py create mode 100644 xblocks_contrib/video/constants.py create mode 100644 xblocks_contrib/video/content.py create mode 100644 xblocks_contrib/video/django.py create mode 100644 xblocks_contrib/video/exceptions.py create mode 100644 xblocks_contrib/video/load_static.py create mode 100644 xblocks_contrib/video/mixin.py create mode 100644 xblocks_contrib/video/package-lock.json create mode 100644 xblocks_contrib/video/package.json create mode 100644 xblocks_contrib/video/static/js/dist/manifest.json create mode 100644 xblocks_contrib/video/static/js/dist/video-xblock.js create mode 100644 xblocks_contrib/video/static/js/dist/video-xblock.js.LICENSE.txt create mode 100644 xblocks_contrib/video/static/js/src/00_async_process.js create mode 100644 xblocks_contrib/video/static/js/src/00_component.js create mode 100644 xblocks_contrib/video/static/js/src/00_i18n.js create mode 100644 xblocks_contrib/video/static/js/src/00_iterator.js create mode 100644 xblocks_contrib/video/static/js/src/00_resizer.js create mode 100644 xblocks_contrib/video/static/js/src/00_sjson.js create mode 100644 xblocks_contrib/video/static/js/src/00_video_storage.js create mode 100644 xblocks_contrib/video/static/js/src/01_initialize.js create mode 100644 xblocks_contrib/video/static/js/src/025_focus_grabber.js create mode 100644 xblocks_contrib/video/static/js/src/02_html5_hls_video.js create mode 100644 xblocks_contrib/video/static/js/src/02_html5_video.js create mode 100644 xblocks_contrib/video/static/js/src/035_video_accessible_menu.js create mode 100644 xblocks_contrib/video/static/js/src/036_video_social_sharing.js create mode 100644 xblocks_contrib/video/static/js/src/037_video_transcript_feedback.js create mode 100644 xblocks_contrib/video/static/js/src/03_video_player.js create mode 100644 xblocks_contrib/video/static/js/src/04_video_control.js create mode 100644 xblocks_contrib/video/static/js/src/04_video_full_screen.js create mode 100644 xblocks_contrib/video/static/js/src/05_video_quality_control.js create mode 100644 xblocks_contrib/video/static/js/src/06_video_progress_slider.js create mode 100644 xblocks_contrib/video/static/js/src/07_video_volume_control.js create mode 100644 xblocks_contrib/video/static/js/src/08_video_auto_advance_control.js create mode 100644 xblocks_contrib/video/static/js/src/08_video_speed_control.js create mode 100644 xblocks_contrib/video/static/js/src/095_video_context_menu.js create mode 100644 xblocks_contrib/video/static/js/src/09_bumper.js create mode 100644 xblocks_contrib/video/static/js/src/09_completion.js create mode 100644 xblocks_contrib/video/static/js/src/09_events_bumper_plugin.js create mode 100644 xblocks_contrib/video/static/js/src/09_events_plugin.js create mode 100644 xblocks_contrib/video/static/js/src/09_play_pause_control.js create mode 100644 xblocks_contrib/video/static/js/src/09_play_placeholder.js create mode 100644 xblocks_contrib/video/static/js/src/09_play_skip_control.js create mode 100644 xblocks_contrib/video/static/js/src/09_poster.js create mode 100644 xblocks_contrib/video/static/js/src/09_save_state_plugin.js create mode 100644 xblocks_contrib/video/static/js/src/09_skip_control.js create mode 100644 xblocks_contrib/video/static/js/src/09_video_caption.js create mode 100644 xblocks_contrib/video/static/js/src/10_commands.js create mode 100644 xblocks_contrib/video/static/js/src/10_main.js create mode 100644 xblocks_contrib/video/static/js/src/utils/time.js delete mode 100644 xblocks_contrib/video/static/js/src/video.js create mode 100644 xblocks_contrib/video/tests/test_toggles.py create mode 100644 xblocks_contrib/video/toggles.py create mode 100644 xblocks_contrib/video/transcripts_utils.py create mode 100644 xblocks_contrib/video/utils.py create mode 100644 xblocks_contrib/video/validation.py create mode 100644 xblocks_contrib/video/video_handlers.py create mode 100644 xblocks_contrib/video/video_utils.py create mode 100644 xblocks_contrib/video/video_xfields.py create mode 100644 xblocks_contrib/video/webpack.common.config.js create mode 100644 xblocks_contrib/video/webpack.dev.config.js create mode 100644 xblocks_contrib/video/webpack.prod.config.js diff --git a/.gitignore b/.gitignore index 27632111..af1341cc 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,10 @@ __pycache__ /lib /lib64 +# node +node_modules +npm-debug.log + # Installer logs pip-log.txt diff --git a/requirements/base.in b/requirements/base.in index ed641fdc..da8ef86f 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -8,3 +8,7 @@ nh3 oauthlib openedx-django-pyfs XBlock +# TODO: Run python reqs update +edx-opaque-keys +edx-django-utils>=5.14.1 +edxval diff --git a/xblocks_contrib/utils/__init__.py b/xblocks_contrib/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/xblocks_contrib/utils/utils.py b/xblocks_contrib/utils/utils.py new file mode 100644 index 00000000..ab632570 --- /dev/null +++ b/xblocks_contrib/utils/utils.py @@ -0,0 +1,26 @@ +def display_name_with_default(block): + """ + Calculates the display name for a block. + + Default to the display_name if it isn't None, else fall back to creating + a name based on the URL. + + Unlike the rest of this module's functions, this function takes an entire + course block/overview as a parameter. This is because a few test cases + (specifically, {Text|Image|Video}AnnotationModuleTestCase.test_student_view) + create scenarios where course.display_name is not None but course.location + is None, which causes calling course.url_name to fail. So, although we'd + like to just pass course.display_name and course.url_name as arguments to + this function, we can't do so without breaking those tests. + + Note: This method no longer escapes as it once did, so the caller must + ensure it is properly escaped where necessary. + + Arguments: + block (XModuleMixin|CourseOverview|BlockStructureBlockData): + Block that is being accessed + """ + return ( + block.display_name if block.display_name is not None + else block.scope_ids.usage_id.block_id.replace('_', ' ') + ) diff --git a/xblocks_contrib/video/_transcripts_utils.py b/xblocks_contrib/video/_transcripts_utils.py new file mode 100644 index 00000000..007ebd73 --- /dev/null +++ b/xblocks_contrib/video/_transcripts_utils.py @@ -0,0 +1,513 @@ +# import copy +# import html +# import logging +# import os +# import pathlib +# import re +# from functools import wraps +# +# import requests +# import simplejson as json +# from django.conf import settings +# from django.core.exceptions import ObjectDoesNotExist +# from lxml import etree +# from opaque_keys.edx.keys import UsageKeyV2 +# from pysrt import SubRipFile, SubRipItem, SubRipTime +# from pysrt.srtexc import Error +# +# from xblocks_contrib.video.bumper_utils import get_bumper_settings +# from xblocks_contrib.video.content import StaticContent +# from xblocks_contrib.video.django import contentstore +# from xblocks_contrib.video.exceptions import NotFoundError +# +# try: +# from edxval import api as edxval_api +# except ImportError: +# edxval_api = None +# +# log = logging.getLogger(__name__) +# +# NON_EXISTENT_TRANSCRIPT = 'non_existent_dummy_file_name' +# +# +# class TranscriptException(Exception): +# pass +# +# +# class TranscriptsGenerationException(Exception): +# pass +# +# +# class GetTranscriptsFromYouTubeException(Exception): +# pass +# +# +# class TranscriptsRequestValidationException(Exception): +# pass +# +# +# def generate_subs_from_source(speed_subs, subs_type, subs_filedata, block, language='en'): +# """Generate transcripts from source files (like SubRip format, etc.) +# and save them to assets for `item` module. +# We expect, that speed of source subs equal to 1 +# +# :param speed_subs: dictionary {speed: sub_id, ...} +# :param subs_type: type of source subs: "srt", ... +# :param subs_filedata:unicode, content of source subs. +# :param block: course or block. +# :param language: str, language of translation of transcripts +# :returns: True, if all subs are generated and saved successfully. +# """ +# _ = block.runtime.service(block, "i18n").gettext +# if subs_type.lower() != 'srt': +# raise TranscriptsGenerationException(_("We support only SubRip (*.srt) transcripts format.")) +# try: +# srt_subs_obj = SubRipFile.from_string(subs_filedata) +# except Exception as ex: +# msg = _("Something wrong with SubRip transcripts file during parsing. Inner message is {error_message}").format( +# error_message=str(ex) +# ) +# raise TranscriptsGenerationException(msg) # lint-amnesty, pylint: disable=raise-missing-from +# if not srt_subs_obj: +# raise TranscriptsGenerationException(_("Something wrong with SubRip transcripts file during parsing.")) +# +# sub_starts = [] +# sub_ends = [] +# sub_texts = [] +# +# for sub in srt_subs_obj: +# sub_starts.append(sub.start.ordinal) +# sub_ends.append(sub.end.ordinal) +# sub_texts.append(sub.text.replace('\n', ' ')) +# +# subs = { +# 'start': sub_starts, +# 'end': sub_ends, +# 'text': sub_texts} +# +# for speed, subs_id in speed_subs.items(): +# save_subs_to_store( +# generate_subs(speed, 1, subs), +# subs_id, +# block, +# language +# ) +# +# return subs +# +# +# def generate_sjson_for_all_speeds(block, user_filename, result_subs_dict, lang): +# """ +# Generates sjson from srt for given lang. +# """ +# _ = block.runtime.service(block, "i18n").gettext +# +# try: +# srt_transcripts = contentstore().find(Transcript.asset_location(block.location, user_filename)) +# except NotFoundError as ex: +# raise TranscriptException(_("{exception_message}: Can't find uploaded transcripts: {user_filename}").format( +# # lint-amnesty, pylint: disable=raise-missing-from +# exception_message=str(ex), +# user_filename=user_filename +# )) +# +# if not lang: +# lang = block.transcript_language +# +# # Used utf-8-sig encoding type instead of utf-8 to remove BOM(Byte Order Mark), e.g. U+FEFF +# generate_subs_from_source( +# result_subs_dict, +# os.path.splitext(user_filename)[1][1:], +# srt_transcripts.data.decode('utf-8-sig'), +# block, +# lang +# ) +# +# +# def clean_video_id(edx_video_id): +# """ +# Cleans an edx video ID. +# +# Arguments: +# edx_video_id(unicode): edx-val's video identifier +# """ +# return edx_video_id and edx_video_id.strip() +# +# +# def get_available_transcript_languages(edx_video_id): +# """ +# Gets available transcript languages for a video. +# +# Arguments: +# edx_video_id(unicode): edx-val's video identifier +# +# Returns: +# A list containing distinct transcript language codes against all the passed video ids. +# """ +# available_languages = [] +# edx_video_id = clean_video_id(edx_video_id) +# if edxval_api and edx_video_id: +# available_languages = edxval_api.get_available_transcript_languages(video_id=edx_video_id) +# +# return available_languages +# +# +# class VideoTranscriptsMixin: +# """Mixin class for transcript functionality. +# +# This is necessary for VideoBlock. +# """ +# +# # +# # def available_translations(self, transcripts, verify_assets=None, is_bumper=False): +# # """ +# # Return a list of language codes for which we have transcripts. +# # +# # Arguments: +# # verify_assets (boolean): If True, checks to ensure that the transcripts +# # really exist in the contentstore. If False, we just look at the +# # VideoBlock fields and do not query the contentstore. One reason +# # we might do this is to avoid slamming contentstore() with queries +# # when trying to make a listing of videos and their languages. +# # +# # Defaults to `not FALLBACK_TO_ENGLISH_TRANSCRIPTS`. +# # +# # transcripts (dict): A dict with all transcripts and a sub. +# # include_val_transcripts(boolean): If True, adds the edx-val transcript languages as well. +# # """ +# # translations = [] +# # if verify_assets is None: +# # verify_assets = not settings.FEATURES.get('FALLBACK_TO_ENGLISH_TRANSCRIPTS') +# # +# # sub, other_langs = transcripts["sub"], transcripts["transcripts"] +# # +# # if verify_assets: +# # all_langs = dict(**other_langs) +# # if sub: +# # all_langs.update({'en': sub}) +# # +# # for language, filename in all_langs.items(): +# # try: +# # # for bumper videos, transcripts are stored in content store only +# # if is_bumper: +# # get_transcript_for_video(self.location, filename, filename, language) +# # else: +# # get_transcript(self, language) +# # except NotFoundError: +# # continue +# # +# # translations.append(language) +# # else: +# # # If we're not verifying the assets, we just trust our field values +# # translations = list(other_langs) +# # if not translations or sub: +# # translations += ['en'] +# # +# # # to clean redundant language codes. +# # return list(set(translations)) +# # +# def get_default_transcript_language(self, transcripts, dest_lang=None): +# """ +# Returns the default transcript language for this video block. +# +# Args: +# transcripts (dict): A dict with all transcripts and a sub. +# dest_lang (unicode): language coming from unit translation language selector. +# """ +# sub, other_lang = transcripts["sub"], transcripts["transcripts"] +# +# # language in plugin selector exists as transcript +# if dest_lang and dest_lang in other_lang.keys(): +# transcript_language = dest_lang +# # language in plugin selector is english and empty transcripts or transcripts and sub exists +# elif dest_lang and dest_lang == 'en' and (not other_lang or (other_lang and sub)): +# transcript_language = 'en' +# elif self.transcript_language in other_lang: +# transcript_language = self.transcript_language +# elif sub: +# transcript_language = 'en' +# elif len(other_lang) > 0: +# transcript_language = sorted(other_lang)[0] +# else: +# transcript_language = 'en' +# return transcript_language +# +# def get_transcripts_info(self, is_bumper=False): +# """ +# Returns a transcript dictionary for the video. +# +# Arguments: +# is_bumper(bool): If True, the request is for the bumper transcripts +# include_val_transcripts(bool): If True, include edx-val transcripts as well +# """ +# if is_bumper: +# transcripts = copy.deepcopy(get_bumper_settings(self).get('transcripts', {})) +# sub = transcripts.pop("en", "") +# else: +# transcripts = self.transcripts if self.transcripts else {} +# sub = self.sub +# +# # Only attach transcripts that are not empty. +# transcripts = { +# language_code: transcript_file +# for language_code, transcript_file in transcripts.items() if transcript_file != '' +# } +# +# # bumper transcripts are stored in content store so we don't need to include val transcripts +# if not is_bumper: +# transcript_languages = get_available_transcript_languages(edx_video_id=self.edx_video_id) +# # HACK Warning! this is temporary and will be removed once edx-val take over the +# # transcript module and contentstore will only function as fallback until all the +# # data is migrated to edx-val. +# for language_code in transcript_languages: +# if language_code == 'en' and not sub: +# sub = NON_EXISTENT_TRANSCRIPT +# elif not transcripts.get(language_code): +# transcripts[language_code] = NON_EXISTENT_TRANSCRIPT +# +# return { +# "sub": sub, +# "transcripts": transcripts, +# } +# +# +# def save_subs_to_store(subs, subs_id, item, language='en'): +# """ +# Save transcripts into `StaticContent`. +# +# Args: +# `subs_id`: str, subtitles id +# `item`: video block instance +# `language`: two chars str ('uk'), language of translation of transcripts +# +# Returns: location of saved subtitles. +# """ +# filedata = json.dumps(subs, indent=2).encode('utf-8') +# filename = subs_filename(subs_id, language) +# return save_to_store(filedata, filename, 'application/json', item.location) +# +# +# def generate_subs(speed, source_speed, source_subs): +# """ +# Generate transcripts from one speed to another speed. +# +# Args: +# `speed`: float, for this speed subtitles will be generated, +# `source_speed`: float, speed of source_subs +# `source_subs`: dict, existing subtitles for speed `source_speed`. +# +# Returns: +# `subs`: dict, actual subtitles. +# """ +# if speed == source_speed: +# return source_subs +# +# coefficient = 1.0 * speed / source_speed +# subs = { +# 'start': [ +# int(round(timestamp * coefficient)) for +# timestamp in source_subs['start'] +# ], +# 'end': [ +# int(round(timestamp * coefficient)) for +# timestamp in source_subs['end'] +# ], +# 'text': source_subs['text']} +# return subs +# +# +# def subs_filename(subs_id, lang='en'): +# """ +# Generate proper filename for storage. +# """ +# if lang == 'en': +# return f'subs_{subs_id}.srt.sjson' +# else: +# return f'{lang}_subs_{subs_id}.srt.sjson' +# +# +# def save_to_store(content, name, mime_type, location): +# """ +# Save named content to store by location. +# +# Returns location of saved content. +# """ +# content_location = Transcript.asset_location(location, name) +# content = StaticContent(content_location, name, mime_type, content) +# contentstore().save(content) +# return content_location +# +# +# class Transcript: +# """ +# Container for transcript methods. +# """ +# SRT = 'srt' +# TXT = 'txt' +# SJSON = 'sjson' +# mime_types = { +# SRT: 'application/x-subrip; charset=utf-8', +# TXT: 'text/plain; charset=utf-8', +# SJSON: 'application/json', +# } +# +# @staticmethod +# def convert(content, input_format, output_format): +# """ +# Convert transcript `content` from `input_format` to `output_format`. +# +# Accepted input formats: sjson, srt. +# Accepted output format: srt, txt, sjson. +# +# Raises: +# TranscriptsGenerationException: On parsing the invalid srt content during conversion from srt to sjson. +# """ +# assert input_format in ('srt', 'sjson') +# assert output_format in ('txt', 'srt', 'sjson') +# +# if input_format == output_format: +# return content +# +# if input_format == 'srt': +# # Standardize content into bytes for later decoding. +# if isinstance(content, str): +# content = content.encode('utf-8') +# +# if output_format == 'txt': +# text = SubRipFile.from_string(content.decode('utf-8')).text +# return html.unescape(text) +# +# elif output_format == 'sjson': +# try: +# srt_subs = SubRipFile.from_string( +# # Skip byte order mark(BOM) character +# content.decode('utf-8-sig'), +# error_handling=SubRipFile.ERROR_RAISE +# ) +# except Error as ex: # Base exception from pysrt +# raise TranscriptsGenerationException(str(ex)) from ex +# +# return json.dumps(generate_sjson_from_srt(srt_subs)) +# +# if input_format == 'sjson': +# # If the JSON file content is bytes, try UTF-8, then Latin-1 +# if isinstance(content, bytes): +# try: +# content_str = content.decode('utf-8') +# except UnicodeDecodeError: +# content_str = content.decode('latin-1') +# else: +# content_str = content +# try: +# content_dict = json.loads(content_str) +# except ValueError: +# truncated = content_str[:100].strip() +# log.exception( +# f"Failed to convert {input_format} to {output_format} for {repr(truncated)}..." +# ) +# content_dict = {"start": [1], "end": [2], "text": ["An error occured obtaining the transcript."]} +# if output_format == 'txt': +# text = content_dict['text'] +# text_without_none = [line if line else '' for line in text] +# return html.unescape("\n".join(text_without_none)) +# elif output_format == 'srt': +# return generate_srt_from_sjson(content_dict, speed=1.0) +# +# @staticmethod +# def asset(location, subs_id, lang='en', filename=None): +# """ +# Get asset from contentstore, asset location is built from subs_id and lang. +# +# `location` is block location. +# """ +# # HACK Warning! this is temporary and will be removed once edx-val take over the +# # transcript module and contentstore will only function as fallback until all the +# # data is migrated to edx-val. It will be saving a contentstore hit for a hardcoded +# # dummy-non-existent-transcript name. +# if NON_EXISTENT_TRANSCRIPT in [subs_id, filename]: +# raise NotFoundError +# +# asset_filename = subs_filename(subs_id, lang) if not filename else filename +# return Transcript.get_asset(location, asset_filename) +# +# @staticmethod +# def get_asset(location, filename): +# """ +# Return asset by location and filename. +# """ +# return contentstore().find(Transcript.asset_location(location, filename)) +# +# @staticmethod +# def asset_location(location, filename): +# """ +# Return asset location. `location` is block location. +# """ +# # If user transcript filename is empty, raise `TranscriptException` to avoid `InvalidKeyError`. +# if not filename: +# raise TranscriptException("Transcript not uploaded yet") +# return StaticContent.compute_location(location.course_key, filename) +# +# @staticmethod +# def delete_asset(location, filename): +# """ +# Delete asset by location and filename. +# """ +# try: +# contentstore().delete(Transcript.asset_location(location, filename)) +# log.info("Transcript asset %s was removed from store.", filename) +# except NotFoundError: +# pass +# return StaticContent.compute_location(location.course_key, filename) +# +# +# def generate_srt_from_sjson(sjson_subs, speed): +# """Generate transcripts with speed = 1.0 from sjson to SubRip (*.srt). +# +# :param sjson_subs: "sjson" subs. +# :param speed: speed of `sjson_subs`. +# :returns: "srt" subs. +# """ +# +# output = '' +# +# equal_len = len(sjson_subs['start']) == len(sjson_subs['end']) == len(sjson_subs['text']) +# if not equal_len: +# return output +# +# sjson_speed_1 = generate_subs(speed, 1, sjson_subs) +# +# for i in range(len(sjson_speed_1['start'])): +# item = SubRipItem( +# index=i, +# start=SubRipTime(milliseconds=sjson_speed_1['start'][i]), +# end=SubRipTime(milliseconds=sjson_speed_1['end'][i]), +# text=sjson_speed_1['text'][i] +# ) +# output += (str(item)) +# output += '\n' +# return output +# +# def generate_sjson_from_srt(srt_subs): +# """ +# Generate transcripts from sjson to SubRip (*.srt). +# +# Arguments: +# srt_subs(SubRip): "SRT" subs object +# +# Returns: +# Subs converted to "SJSON" format. +# """ +# sub_starts = [] +# sub_ends = [] +# sub_texts = [] +# for sub in srt_subs: +# sub_starts.append(sub.start.ordinal) +# sub_ends.append(sub.end.ordinal) +# sub_texts.append(sub.text.replace('\n', ' ')) +# +# sjson_subs = { +# 'start': sub_starts, +# 'end': sub_ends, +# 'text': sub_texts +# } +# return sjson_subs diff --git a/xblocks_contrib/video/ajax_handler_mixin.py b/xblocks_contrib/video/ajax_handler_mixin.py new file mode 100644 index 00000000..80ad8e4c --- /dev/null +++ b/xblocks_contrib/video/ajax_handler_mixin.py @@ -0,0 +1,41 @@ +from xblock.core import XBlock +from webob.multidict import MultiDict +from webob import Response + +# TODO: Code has been refactored from https://github.com/openedx/edx-platform/blob/master/xmodule/x_module.py#L739 +class AjaxHandlerMixin: + """ + Mixin that provides AJAX handling for XBlocks, including file upload support. + """ + @property + def ajax_url(self): + """ + Get the AJAX handler URL for this XBlock. + """ + return self.runtime.handler_url(self, 'ajax_handler', '', '').rstrip('/?') + + @XBlock.handler + def ajax_handler(self, request, suffix=None): + """ + Process AJAX requests and handle file uploads by wrapping handle_ajax(). + """ + class FileObjForWebobFiles: + """ + Convert WebOb uploaded files into standard file objects. + """ + def __init__(self, webob_file): + self.file = webob_file.file + self.name = webob_file.filename + + def __getattr__(self, name): + return getattr(self.file, name) + + # WebOb requests have multiple entries for uploaded files. handle_ajax + # expects a single entry as a list. + request_post = MultiDict(request.POST) + for key in set(request.POST.keys()): + if hasattr(request.POST[key], "file"): + request_post[key] = list(map(FileObjForWebobFiles, request.POST.getall(key))) + + response_data = self.handle_ajax(suffix, request_post) + return Response(response_data, content_type='application/json', charset='UTF-8') diff --git a/xblocks_contrib/video/bumper_utils.py b/xblocks_contrib/video/bumper_utils.py new file mode 100644 index 00000000..dd6ac810 --- /dev/null +++ b/xblocks_contrib/video/bumper_utils.py @@ -0,0 +1,146 @@ +""" +Utils for video bumper +""" + +import copy +import json +import logging +from collections import OrderedDict +from datetime import datetime, timedelta + +import pytz +from django.conf import settings + +from .video_utils import set_query_parameter + +try: + import edxval.api as edxval_api +except ImportError: + edxval_api = None + +log = logging.getLogger(__name__) + + +def get_bumper_settings(video): + """ + Get bumper settings from video instance. + """ + bumper_settings = copy.deepcopy(getattr(video, 'video_bumper', {})) + + # clean up /static/ prefix from bumper transcripts + for lang, transcript_url in bumper_settings.get('transcripts', {}).items(): + bumper_settings['transcripts'][lang] = transcript_url.replace("/static/", "") + + return bumper_settings + + +def is_bumper_enabled(video): + """ + Check if bumper enabled. + + - Feature flag ENABLE_VIDEO_BUMPER should be set to True + - Do not show again button should not be clicked by user. + - Current time minus periodicity must be greater that last time viewed + - edxval_api should be presented + + Returns: + bool. + """ + bumper_last_view_date = getattr(video, 'bumper_last_view_date', None) + utc_now = datetime.utcnow().replace(tzinfo=pytz.utc) + periodicity = settings.FEATURES.get('SHOW_BUMPER_PERIODICITY', 0) + has_viewed = any([ + video.bumper_do_not_show_again, + (bumper_last_view_date and bumper_last_view_date + timedelta(seconds=periodicity) > utc_now) + ]) + is_studio = getattr(video.runtime, "is_author_mode", False) + return bool( + not is_studio and + settings.FEATURES.get('ENABLE_VIDEO_BUMPER') and + get_bumper_settings(video) and + edxval_api and + not has_viewed + ) + + +def bumperize(video): + """ + Populate video with bumper settings, if they are presented. + """ + video.bumper = { + 'enabled': False, + 'edx_video_id': "", + 'transcripts': {}, + 'metadata': None, + } + + if not is_bumper_enabled(video): + return + + bumper_settings = get_bumper_settings(video) + + try: + video.bumper['edx_video_id'] = bumper_settings['video_id'] + video.bumper['transcripts'] = bumper_settings['transcripts'] + except (TypeError, KeyError): + log.warning( + "Could not retrieve video bumper information from course settings" + ) + return + + sources = get_bumper_sources(video) + if not sources: + return + + video.bumper.update({ + 'metadata': bumper_metadata(video, sources), + 'enabled': True, # Video poster needs this. + }) + + +def get_bumper_sources(video): + """ + Get bumper sources from edxval. + + Returns list of sources. + """ + try: + val_profiles = ["desktop_webm", "desktop_mp4"] + val_video_urls = edxval_api.get_urls_for_profiles(video.bumper['edx_video_id'], val_profiles) + bumper_sources = [url for url in [val_video_urls[p] for p in val_profiles] if url] + except edxval_api.ValInternalError: + # if no bumper sources, nothing will be showed + log.warning( + "Could not retrieve information from VAL for Bumper edx Video ID: %s.", video.bumper['edx_video_id'] + ) + return [] + + return bumper_sources + + +def bumper_metadata(video, sources): + """ + Generate bumper metadata. + """ + transcripts = video.get_transcripts_info(is_bumper=True) + unused_track_url, bumper_transcript_language, bumper_languages = video.get_transcripts_for_student(transcripts) + + metadata = OrderedDict({ + 'saveStateUrl': video.ajax_url + '/save_user_state', + 'showCaptions': json.dumps(video.show_captions), + 'sources': sources, + 'streams': '', + 'transcriptLanguage': bumper_transcript_language, + 'transcriptLanguages': bumper_languages, + 'transcriptTranslationUrl': set_query_parameter( + video.runtime.handler_url(video, 'transcript', 'translation/__lang__').rstrip('/?'), 'is_bumper', 1 + ), + 'transcriptAvailableTranslationsUrl': set_query_parameter( + video.runtime.handler_url(video, 'transcript', 'available_translations').rstrip('/?'), 'is_bumper', 1 + ), + 'publishCompletionUrl': set_query_parameter( + video.runtime.handler_url(video, 'publish_completion', '').rstrip('?'), 'is_bumper', 1 + ), + }) + + return metadata diff --git a/xblocks_contrib/video/cache_utils.py b/xblocks_contrib/video/cache_utils.py new file mode 100644 index 00000000..858448f5 --- /dev/null +++ b/xblocks_contrib/video/cache_utils.py @@ -0,0 +1,240 @@ +import itertools +import wrapt + +from django.utils.encoding import force_str + +from edx_django_utils.cache import RequestCache + + +def request_cached(namespace=None, arg_map_function=None, request_cache_getter=None): + """ + A function decorator that automatically handles caching its return value for + the duration of the request. It returns the cached value for subsequent + calls to the same function, with the same parameters, within a given request. + + Notes: + - We convert arguments and keyword arguments to their string form to build the cache key. So if you have + args/kwargs that can't be converted to strings, you're gonna have a bad time (don't do it). + - Cache key cardinality depends on the args/kwargs. So if you're caching a function that takes five arguments, + you might have deceptively low cache efficiency. Prefer functions with fewer arguments. + - WATCH OUT: Don't use this decorator for instance methods that take in a "self" argument that changes each + time the method is called. This will result in constant cache misses and not provide the performance benefit + you are looking for. Rather, change your instance method to a class method. + - Benchmark, benchmark, benchmark! If you never measure, how will you know you've improved? or regressed? + + Arguments: + namespace (string): An optional namespace to use for the cache. By default, we use the default request cache, + not a namespaced request cache. Since the code automatically creates a unique cache key with the module and + function's name, storing the cached value in the default cache, you won't usually need to specify a + namespace value. + But you can specify a namespace value here if you need to use your own namespaced cache - for example, + if you want to clear out your own cache by calling RequestCache(namespace=NAMESPACE).clear(). + NOTE: This argument is ignored if you supply a ``request_cache_getter``. + arg_map_function (function: arg->string): Function to use for mapping the wrapped function's arguments to + strings to use in the cache key. If not provided, defaults to force_text, which converts the given + argument to a string. + request_cache_getter (function: args, kwargs->RequestCache): Function that returns the RequestCache to use. + If not provided, defaults to edx_django_utils.cache.RequestCache. If ``request_cache_getter`` returns None, + the function's return values are not cached. + + Returns: + func: a wrapper function which will call the wrapped function, passing in the same args/kwargs, + cache the value it returns, and return that cached value for subsequent calls with the + same args/kwargs within a single request. + """ + @wrapt.decorator + def decorator(wrapped, instance, args, kwargs): + """ + Arguments: + args, kwargs: values passed into the wrapped function + """ + # Check to see if we have a result in cache. If not, invoke our wrapped + # function. Cache and return the result to the caller. + if request_cache_getter: + request_cache = request_cache_getter(args if instance is None else (instance,) + args, kwargs) + else: + request_cache = RequestCache(namespace) + + if request_cache: + cache_key = _func_call_cache_key(wrapped, arg_map_function, *args, **kwargs) + cached_response = request_cache.get_cached_response(cache_key) + if cached_response.is_found: + return cached_response.value + + result = wrapped(*args, **kwargs) + + if request_cache: + request_cache.set(cache_key, result) + + return result + + return decorator + +def _func_call_cache_key(func, arg_map_function, *args, **kwargs): + """ + Returns a cache key based on the function's module, + the function's name, a stringified list of arguments + and a stringified list of keyword arguments. + """ + arg_map_function = arg_map_function or force_str + + converted_args = list(map(arg_map_function, args)) + converted_kwargs = list(map(arg_map_function, _sorted_kwargs_list(kwargs))) + + cache_keys = [func.__module__, func.__name__] + converted_args + converted_kwargs + return '.'.join(cache_keys) + +def _sorted_kwargs_list(kwargs): + """ + Returns a unique and deterministic ordered list from the given kwargs. + """ + sorted_kwargs = sorted(kwargs.items()) + sorted_kwargs_list = list(itertools.chain(*sorted_kwargs)) + return sorted_kwargs_list + + +class process_cached: # pylint: disable=invalid-name + """ + Decorator to cache the result of a function for the life of a process. + + If the return value of the function for the provided arguments has not + yet been cached, the function will be calculated and cached. If called + later with the same arguments, the cached value is returned + (not reevaluated). + https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize + + WARNING: Only use this process_cached decorator for caching data that + is constant throughout the lifetime of a gunicorn worker process, + is costly to compute, and is required often. Otherwise, it can lead to + unwanted memory leakage. + """ + + def __init__(self, func): + self.func = func + self.cache = {} + + def __call__(self, *args): + if not isinstance(args, collections.abc.Hashable): + # uncacheable. a list, for instance. + # better to not cache than blow up. + return self.func(*args) + if args in self.cache: + return self.cache[args] + else: + value = self.func(*args) + self.cache[args] = value + return value + + def __repr__(self): + """ + Return the function's docstring. + """ + return self.func.__doc__ + + def __get__(self, obj, objtype): + """ + Support instance methods. + """ + partial = functools.partial(self.__call__, obj) + # Make the cache accessible on the wrapped object so it can be cleared if needed. + partial.cache = self.cache + return partial + + +class CacheInvalidationManager: + """ + This class provides a decorator for simple functions, which can handle invalidation. + + To use, instantiate with a namespace or django model class: + `manager = CacheInvalidationManager(model=User)` + One of namespace or model should be specified, but not both. + + Then use it as a decorator on functions with no arguments + `@manager + def get_system_user(): + ... + ` + When the User model is saved or deleted, all cache keys used by + the decorator will be cleared. + """ + + def __init__(self, namespace=None, model=None, cache_time=86400): + if model: + post_save.connect(self.invalidate, sender=model) + post_delete.connect(self.invalidate, sender=model) + namespace = f"{model.__module__}.{model.__qualname__}" + self.namespace = namespace + self.cache_time = cache_time + self.keys = set() + + # pylint: disable=unused-argument + def invalidate(self, **kwargs): + """ + Invalidate all keys tracked by the manager. + """ + for key in self.keys: + TieredCache.delete_all_tiers(key) + + def __call__(self, func): + """ + Decorator for functions with no arguments. + """ + cache_key = f'{self.namespace}.{func.__module__}.{func.__name__}' + self.keys.add(cache_key) + + @functools.wraps(func) + def decorator(*args, **kwargs): # pylint: disable=unused-argument,missing-docstring + result = TieredCache.get_cached_response(cache_key) + if result.is_found: + return result.value + result = func() + TieredCache.set_all_tiers(cache_key, result, self.cache_time) + return result + return decorator + + +def zpickle(data): + """Given any data structure, returns a zlib compressed pickled serialization.""" + return zlib.compress(pickle.dumps(data, 4)) + + +def zunpickle(zdata): + """Given a zlib compressed pickled serialization, returns the deserialized data.""" + return pickle.loads(zlib.decompress(zdata), encoding='latin1') + + +def get_cache(name): + """ + Return the request cache named ``name``. + + Arguments: + name (str): The name of the request cache to load + + Returns: dict + """ + assert name is not None + return RequestCache(name).data + + +class CacheService: + """ + An XBlock service which provides a cache. + + Args: + cache(object): provides get/set functions for retrieving/storing key/value pairs. + """ + def __init__(self, cache, **kwargs): + super().__init__(**kwargs) + self._cache = cache + + def get(self, key, *args, **kwargs): + """ + Returns the value cached against the given key, or None. + """ + return self._cache.get(key, *args, **kwargs) + + def set(self, key, value, *args, **kwargs): + """ + Caches the value against the given key. + """ + return self._cache.set(key, value, *args, **kwargs) diff --git a/xblocks_contrib/video/constants.py b/xblocks_contrib/video/constants.py new file mode 100644 index 00000000..3f5fa20b --- /dev/null +++ b/xblocks_contrib/video/constants.py @@ -0,0 +1,22 @@ +""" +Constants used by DjangoXBlockUserService +""" + +# This is the view that will be rendered to display the XBlock in the LMS. +# It will also be used to render the block in "preview" mode in Studio, unless +# the XBlock also implements author_view. +STUDENT_VIEW = 'student_view' + +# This is the view that will be rendered to display the XBlock in the LMS for unenrolled learners. +# Implementations of this view should assume that a user and user data are not available. +PUBLIC_VIEW = 'public_view' + + +# The personally identifiable user ID. +ATTR_KEY_USER_ID = 'edx-platform.user_id' +# The country code determined from the user's request IP address. +ATTR_KEY_REQUEST_COUNTRY_CODE = 'edx-platform.request_country_code' + +COURSE_VIDEO_SHARING_PER_VIDEO = 'per-video' +COURSE_VIDEO_SHARING_ALL_VIDEOS = 'all-on' +COURSE_VIDEO_SHARING_NONE = 'all-off' diff --git a/xblocks_contrib/video/content.py b/xblocks_contrib/video/content.py new file mode 100644 index 00000000..4ddb4a63 --- /dev/null +++ b/xblocks_contrib/video/content.py @@ -0,0 +1,36 @@ +from opaque_keys.edx.locator import AssetLocator + +# TODO: Review this class copied from edx-platform +class StaticContent: # lint-amnesty, pylint: disable=missing-class-docstring + def __init__(self, loc, name, content_type, data, last_modified_at=None, thumbnail_location=None, import_path=None, + length=None, locked=False, content_digest=None): + self.location = loc + self.name = name # a display string which can be edited, and thus not part of the location which needs to be fixed # lint-amnesty, pylint: disable=line-too-long + self.content_type = content_type + self._data = data + self.length = length + self.last_modified_at = last_modified_at + self.thumbnail_location = thumbnail_location + # optional information about where this file was imported from. This is needed to support import/export + # cycles + self.import_path = import_path + self.locked = locked + self.content_digest = content_digest + + @staticmethod + def compute_location(course_key, path, revision=None, + is_thumbnail=False): # lint-amnesty, pylint: disable=unused-argument + """ + Constructs a location object for static content. + + - course_key: the course that this asset belongs to + - path: is the name of the static asset + - revision: is the object's revision information + - is_thumbnail: is whether or not we want the thumbnail version of this + asset + """ + path = path.replace('/', '_') + return course_key.make_asset_key( + 'asset' if not is_thumbnail else 'thumbnail', + AssetLocator.clean_keeping_underscores(path) + ).for_branch(None) diff --git a/xblocks_contrib/video/django.py b/xblocks_contrib/video/django.py new file mode 100644 index 00000000..26766af8 --- /dev/null +++ b/xblocks_contrib/video/django.py @@ -0,0 +1,31 @@ +# TODO: Check the placement of this file +# lint-amnesty, pylint: disable=missing-module-docstring +from importlib import import_module + +from django.conf import settings + +_CONTENTSTORE = {} + + +def load_function(path): + """ + Load a function by name. + + path is a string of the form "path.to.module.function" + returns the imported python object `function` from `path.to.module` + """ + module_path, _, name = path.rpartition('.') + return getattr(import_module(module_path), name) + + +def contentstore(name='default'): # lint-amnesty, pylint: disable=missing-function-docstring + if name not in _CONTENTSTORE: + class_ = load_function(settings.CONTENTSTORE['ENGINE']) + options = {} + options.update(settings.CONTENTSTORE['DOC_STORE_CONFIG']) + if 'ADDITIONAL_OPTIONS' in settings.CONTENTSTORE: + if name in settings.CONTENTSTORE['ADDITIONAL_OPTIONS']: + options.update(settings.CONTENTSTORE['ADDITIONAL_OPTIONS'][name]) + _CONTENTSTORE[name] = class_(**options) + + return _CONTENTSTORE[name] diff --git a/xblocks_contrib/video/exceptions.py b/xblocks_contrib/video/exceptions.py new file mode 100644 index 00000000..b9fa567d --- /dev/null +++ b/xblocks_contrib/video/exceptions.py @@ -0,0 +1,56 @@ +# TODO: We can move it to common utils + +# lint-amnesty, pylint: disable=missing-module-docstring +class InvalidDefinitionError(Exception): + pass + + +class NotFoundError(Exception): + pass + + +class ProcessingError(Exception): + ''' + An error occurred while processing a request to the XModule. + For example: if an exception occurs while checking a capa problem. + ''' + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class InvalidVersionError(Exception): + """ + Tried to save an item with a location that a store cannot support (e.g., draft version + for a non-leaf node) + """ + def __init__(self, location): + super().__init__() + self.location = location + + +class SerializationError(Exception): + """ + Thrown when a module cannot be exported to XML + """ + def __init__(self, location, msg): + super().__init__(msg) + self.location = location + + +class UndefinedContext(Exception): + """ + Tried to access an xmodule field which needs a different context (runtime) to have a value. + """ + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class HeartbeatFailure(Exception): + """ + Raised when heartbeat fails. + """ + + def __init__(self, msg, service): + """ + In addition to a msg, provide the name of the service. + """ + self.service = service + super().__init__(msg) diff --git a/xblocks_contrib/video/load_static.py b/xblocks_contrib/video/load_static.py new file mode 100644 index 00000000..e0f5daf6 --- /dev/null +++ b/xblocks_contrib/video/load_static.py @@ -0,0 +1,67 @@ +import json +import logging +from urllib.parse import urlparse + +from django.conf import settings +from xblock.utils.resources import ResourceLoader + +logger = logging.getLogger(__name__) # pylint: disable=invalid-name + + +def urljoin(*args): + """ + Joining multiple url continuously. The default behavior from os.path.join + would completely reset the path if the second path start with '/'. + """ + beginning_slash = '/' if args[0].startswith('/') else '' + trailing_slash = '/' if args[-1].endswith('/') else '' + joined_path = '/'.join(map(lambda x: str(x).strip('/'), args)) + return beginning_slash + joined_path + trailing_slash + + +class LoadStatic: + """ + Helper class for loading generated file from webpack. + """ + + _manifest = {} + _base_url = '' + _is_loaded = False + + resource_loader = ResourceLoader(__name__) + + @staticmethod + def reload_manifest(): + """ + Reload from manifest file + """ + root_url, base_url = '', '/static/js/dist/' + base_url_override = '' + + if hasattr(settings, 'LMS_ROOT_URL'): + root_url = settings.LMS_ROOT_URL + else: + logger.error('LMS_ROOT_URL is undefined') + + try: + json_data = LoadStatic.resource_loader.load_unicode('static/js/dist/manifest.json') + LoadStatic._manifest = json.loads(json_data) + base_url_override = LoadStatic._manifest.get('base_url', None) + LoadStatic._is_loaded = True + except OSError: + logger.error('Cannot find static/js/dist/manifest.json') + finally: + if base_url_override and urlparse(base_url_override).scheme: + LoadStatic._base_url = base_url_override + else: + LoadStatic._base_url = urljoin(root_url, base_url) + + @staticmethod + def get_url(key): + """ + get url from key + """ + if not LoadStatic._is_loaded: + LoadStatic.reload_manifest() + url = LoadStatic._manifest[key] if key in LoadStatic._manifest else key + return urljoin(LoadStatic._base_url, url) diff --git a/xblocks_contrib/video/mixin.py b/xblocks_contrib/video/mixin.py new file mode 100644 index 00000000..b75b466e --- /dev/null +++ b/xblocks_contrib/video/mixin.py @@ -0,0 +1,55 @@ +""" +License mixin for XBlocks +""" + +from xblock.core import XBlockMixin +from xblock.fields import Scope, String + +# Make '_' a no-op so we can scrape strings. Using lambda instead of +# `django.utils.translation.gettext_noop` because Django cannot be imported in this file +_ = lambda text: text + + +class LicenseMixin(XBlockMixin): + """ + Mixin that allows an author to indicate a license on the contents of an + XBlock. For example, a video could be marked as Creative Commons SA-BY + licensed. You can even indicate the license on an entire course. + + If this mixin is not applied to an XBlock, or if the license field is + blank, then the content is subject to whatever legal licensing terms that + apply to content by default. For example, in the United States, that content + is exclusively owned by the creator of the content by default. Other + countries may have similar laws. + """ + license = String( + display_name=_("License"), + help=_("A license defines how the contents of this block can be shared and reused."), + default=None, + scope=Scope.content, + ) + + @classmethod + def parse_license_from_xml(cls, definition, node): + """ + When importing an XBlock from XML, this method will parse the license + information out of the XML and attach it to the block. + It is defined here so that classes that use this mixin can simply refer + to this method, rather than reimplementing it in their XML import + functions. + """ + license = node.get('license', default=None) # pylint: disable=redefined-builtin + if license: + definition['license'] = license + return definition + + def add_license_to_xml(self, node, default=None): + """ + When generating XML from an XBlock, this method will add the XBlock's + license to the XML representation before it is serialized. + It is defined here so that classes that use this mixin can simply refer + to this method, rather than reimplementing it in their XML export + functions. + """ + if getattr(self, "license", default): + node.set('license', self.license) diff --git a/xblocks_contrib/video/package-lock.json b/xblocks_contrib/video/package-lock.json new file mode 100644 index 00000000..233b1c00 --- /dev/null +++ b/xblocks_contrib/video/package-lock.json @@ -0,0 +1,2895 @@ +{ + "name": "video-xblock", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "video-xblock", + "version": "1.0.0", + "dependencies": { + "draggabilly": "^3.0.0", + "edx-ui-toolkit": "1.8.7", + "hls.js": "0.14.17", + "underscore": "^1.13.7", + "webpack-merge": "^6.0.1" + }, + "devDependencies": { + "clean-webpack-plugin": "^4.0.0", + "webpack": "^5.99.8", + "webpack-cli": "^6.0.1", + "webpack-manifest-plugin": "^5.0.1" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.15.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.19.tgz", + "integrity": "sha512-3vMNr4TzNQyjHcRZadojpRaD9Ofr6LsonZAoQ+HMUa/9ORTPoxVIw0e0mpqWpdjj8xybyCM+oKOUH2vwFu/oEw==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/backbone": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz", + "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==", + "dependencies": { + "underscore": ">=1.8.3" + } + }, + "node_modules/backbone.paginator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.8.tgz", + "integrity": "sha512-8XS2CTbjnwMbJ/3Traa1te2RPecOGbZ9tc52T89pzo6NXlVEJDFnC++dp7CQLBUZpgk3g0veX8mUbEF4wbD2NQ==", + "dependencies": { + "backbone": "1.1.2 || 1.2.3 || ^1.3.2", + "underscore": "^1.8.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "dependencies": { + "del": "^4.1.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.0 <6.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/draggabilly": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", + "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", + "dependencies": { + "get-size": "^3.0.0", + "unidragger": "^3.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/edx-ui-toolkit": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/edx-ui-toolkit/-/edx-ui-toolkit-1.8.7.tgz", + "integrity": "sha512-2GF463dcfKu9u0wFY1u5fX6CUs5V3VQjcTQKt1tLzFzGoK1oGg/lRp0SYsbdcIAzCT8yQAFyrpvAQh1oGuTkiA==", + "dependencies": { + "backbone": "1.6.0", + "backbone.paginator": "2.0.8", + "gulp-shell": "0.8.0", + "jquery": "~3.7.0", + "mini-css-extract-plugin": "^2.7.2", + "moment": "2.30.1", + "moment-timezone": "0.5.46", + "requirejs": "2.1.22", + "requirejs-text": "2.0.16", + "sass": "^1.58.3", + "sinon": "1.17.7", + "underscore": "~1.8.3", + "urijs": "1.19.11" + } + }, + "node_modules/edx-ui-toolkit/node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ev-emitter": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", + "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha512-cPh7is6k3d8tIUh+pnXXuAbD/uhSXGgqLPw0UrYpv5lfdJ+MMMSjx40JNpqP7Top9Nt25YomWEiRmkHbOvkCaA==", + "deprecated": "This package is unmaintained. Use @sinonjs/formatio instead", + "dependencies": { + "samsam": "~1.1" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", + "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gulp-shell": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", + "dependencies": { + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hls.js": { + "version": "0.14.17", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz", + "integrity": "sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==", + "dependencies": { + "eventemitter3": "^4.0.3", + "url-toolkit": "^2.1.6" + } + }, + "node_modules/immutable": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", + "integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha512-YYp8cqz7/8eruZ15L1mzcPkvLYxipfdsWIDESvNdNmQP9o7TsDitRhNuV2xb7aFu2ofZngao1jiVrVZ842x4BQ==" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.46", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz", + "integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requirejs": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "integrity": "sha512-AhZqN7UrWV8R2d1LfGfznskMJNF0Vb6yStwrCn52UbktJg6y5V1I7RoGRyJtACe86d50PyQn0Iw0jYDKMvc4iA==", + "bin": { + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/requirejs-text": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/requirejs-text/-/requirejs-text-2.0.16.tgz", + "integrity": "sha512-XrzjeTb1pwzIWmkz8qnUiM20gENgiwB+66IciNuziwlaPAJsYQsQPSYyQ1kD4tGKGZxTisIfDbOHk02DpI/76Q==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha512-iVL7LibpM3tl4rQPweOXXrmjGegxx27flTOjQEZD3PXe4oZNFzuz6Si4mgleK/JWU/hyCvtV01RUovjvBEpDmw==", + "deprecated": "This package has been deprecated in favour of @sinonjs/samsam" + }, + "node_modules/sass": { + "version": "1.89.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz", + "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "integrity": "sha512-M9rtyQxKfcTTdB64rpPSRaTzOvunb+HHPv/3PxvNPrEDnFSny95Pi6/3VoD471ody0ay0IHyzT3BErfcLXj6NA==", + "deprecated": "16.1.1", + "dependencies": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": ">=0.10.3 <1" + }, + "engines": { + "node": ">=0.1.103" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.39.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz", + "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "node_modules/unidragger": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unidragger/-/unidragger-3.0.1.tgz", + "integrity": "sha512-RngbGSwBFmqGBWjkaH+yB677uzR95blSQyxq6hYbrQCejH3Mx1nm8DVOuh3M9k2fQyTstWUG5qlgCnNqV/9jVw==", + "dependencies": { + "ev-emitter": "^2.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "node_modules/url-toolkit": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz", + "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.99.8", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz", + "integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.1.tgz", + "integrity": "sha512-xTlX7dC3hrASixA2inuWFMz6qHsNi6MT3Uiqw621sJjRTShtpMjbDYhPPZBwWUKdIYKIjSq9em6+uzWayf38aQ==", + "dev": true, + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "webpack": "^5.75.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + } + } +} diff --git a/xblocks_contrib/video/package.json b/xblocks_contrib/video/package.json new file mode 100644 index 00000000..9aed1f59 --- /dev/null +++ b/xblocks_contrib/video/package.json @@ -0,0 +1,25 @@ +{ + "name": "video-xblock", + "version": "1.0.0", + "directories": { + "test": "tests" + }, + "scripts": { + "build": "webpack --config webpack.prod.config.js", + "build-dev": "webpack --config webpack.dev.config.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "draggabilly": "^3.0.0", + "edx-ui-toolkit": "1.8.7", + "hls.js": "0.14.17", + "underscore": "^1.13.7", + "webpack-merge": "^6.0.1" + }, + "devDependencies": { + "clean-webpack-plugin": "^4.0.0", + "webpack": "^5.99.8", + "webpack-cli": "^6.0.1", + "webpack-manifest-plugin": "^5.0.1" + } +} diff --git a/xblocks_contrib/video/static/css/video.css b/xblocks_contrib/video/static/css/video.css index 563fa0d5..a5f0b840 100644 --- a/xblocks_contrib/video/static/css/video.css +++ b/xblocks_contrib/video/static/css/video.css @@ -1,9 +1,1165 @@ /* CSS for VideoBlock */ -.video .count { +@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700"); + +.xblock { + margin-bottom: calc((var(--baseline, 20px) * 1.5)); +} + +.is-hidden, +.video.closed .subtitles { + display: none; +} + +.video { + background: whitesmoke; + display: block; + margin: 0 -12px; + padding: 12px; + border-radius: 5px; + outline: none; +} + +.video:after { + content: ""; + display: table; + clear: both; +} + +.video:focus, +.video:active, +.video:hover { + border: 0; +} + +.video.is-initialized .video-wrapper .spinner { + display: none; +} + +.video.is-pre-roll .slider { + visibility: hidden; +} + +.video.is-pre-roll .video-player { + position: relative; +} + +.video.is-pre-roll .video-player::before { + display: block; + content: ""; + width: 100%; + padding-top: 55%; +} + +.video .tc-wrapper { + position: relative; +} + +.video .tc-wrapper:after { + content: ""; + display: table; + clear: both; +} + +.video .focus_grabber { + position: relative; + display: inline; + width: 0; + height: 0; +} + +.video .downloads-heading { + margin: 1em 0 0; +} + +.video .wrapper-video-bottom-section { + display: flex; + justify-content: space-between; +} + +.video .wrapper-video-bottom-section .wrapper-download-video, +.video .wrapper-video-bottom-section .wrapper-download-transcripts, +.video .wrapper-video-bottom-section .wrapper-handouts, +.video .wrapper-video-bottom-section .branding, +.video .wrapper-video-bottom-section .wrapper-transcript-feedback { + flex: 1; + margin-top: var(--baseline, 20px); + padding-right: var(--baseline, 20px); + vertical-align: top; +} + +@media (min-width: 768px) { + .video .wrapper-downloads { + display: flex; + } +} + +.video .wrapper-downloads .hd { + margin: 0; +} + +.video .wrapper-downloads .wrapper-download-video .video-sources { + margin: 0; +} + +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts { + margin: 0; + padding: 0; + list-style: none; +} + +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option { + margin: 0; +} + +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn, +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn-link { + font-size: 16px !important; + font-weight: unset; +} + +.video .wrapper-downloads .branding { + padding-right: 0; +} + +.video .wrapper-downloads .branding .host-tag { + position: absolute; + left: -9999em; + display: inline-block; + vertical-align: middle; + color: var(--body-color, #313131); +} + +.video .wrapper-downloads .branding .brand-logo { + display: inline-block; + max-width: 100%; + max-height: calc((var(--baseline, 20px) * 2)); + padding: calc((var(--baseline, 20px) / 4)) 0; + vertical-align: middle; +} + +.video .wrapper-transcript-feedback { + display: none; +} + +.video .wrapper-transcript-feedback .transcript-feedback-buttons { + display: flex; +} + +.video .wrapper-transcript-feedback .transcript-feedback-btn-wrapper { + margin-right: 10px; +} + +.video .wrapper-transcript-feedback .thumbs-up-btn, +.video .wrapper-transcript-feedback .thumbs-down-btn { + border: none; + box-shadow: none; + background: transparent; +} + +.video .google-disclaimer { + display: none; + margin-top: var(--baseline, 20px); + padding-right: var(--baseline, 20px); + vertical-align: top; +} + +.video .video-wrapper { + float: left; + margin-right: 2.27273%; + width: 65.90909%; + background-color: black; + position: relative; +} + +.video .video-wrapper:hover .btn-play { + color: #0075b4; +} + +.video .video-wrapper:hover .btn-play::after { + background: #fff; +} + +.video .video-wrapper .video-player-pre, +.video .video-wrapper .video-player-post { + height: 50px; + background-color: #111010; +} + +.video .video-wrapper .spinner { + transform: translate(-50%, -50%); + position: absolute; + z-index: 1; + background: rgba(0, 0, 0, 0.7); + top: 50%; + left: 50%; + padding: 30px; + border-radius: 25%; +} + +.video .video-wrapper .spinner::after { + animation: rotateCW 3s infinite linear; + content: ''; + display: block; + width: 30px; + height: 30px; + border: 7px solid white; + border-top-color: transparent; + border-radius: 100%; + position: relative; +} + +.video .video-wrapper .btn-play { + transform: translate(-50%, -50%); + position: absolute; + z-index: 1; + top: 46%; + left: 50%; + font-size: 4em; + cursor: pointer; + opacity: 0.1; +} + +.video .video-wrapper .btn-play::after { + background: var(--white, #fff); + position: absolute; + width: 50%; + height: 50%; + content: ''; + left: 0; + top: 0; + bottom: 0; + right: 0; + margin: auto; + z-index: -1; +} + +.video .video-wrapper .closed-captions { + left: 5%; + position: absolute; + width: 90%; + box-sizing: border-box; + top: 70%; + text-align: center; +} + +.video .video-wrapper .closed-captions.is-visible { + max-height: calc((var(--baseline, 20px) * 3)); + border-radius: calc((var(--baseline, 20px) / 5)); + padding: 8px calc((var(--baseline, 20px) / 2)) 8px calc((var(--baseline, 20px) * 1.5)); + background: rgba(0, 0, 0, 0.75); + color: var(--yellow, #e2c01f); +} + +.video .video-wrapper .closed-captions.is-visible::before { + position: absolute; + display: inline-block; + top: 50%; + left: var(--baseline, 20px); + margin-top: -0.6em; + font-family: 'FontAwesome'; + content: "\f142"; + color: var(--white, #fff); + opacity: 0.5; +} + +.video .video-wrapper .closed-captions.is-visible:hover, +.video .video-wrapper .closed-captions.is-visible.is-dragging { + background: black; + cursor: move; +} + +.video .video-wrapper .closed-captions.is-visible:hover::before, +.video .video-wrapper .closed-captions.is-visible.is-dragging::before { + opacity: 1; +} + +.video .video-wrapper .video-player { + overflow: hidden; + min-height: 158px; +} + +.video .video-wrapper .video-player > div { + height: 100%; +} + +.video .video-wrapper .video-player > div.hidden { + display: none; +} + +.video .video-wrapper .video-player .video-error, +.video .video-wrapper .video-player .video-hls-error { + padding: calc((var(--baseline, 20px) / 5)); + background: black; + color: white !important; +} + +.video .video-wrapper .video-player object, +.video .video-wrapper .video-player iframe, +.video .video-wrapper .video-player video { + left: 0; + display: block; + border: none; + width: 100%; +} + +.video .video-wrapper .video-player h4 { + text-align: center; + color: white; +} + +.video .video-wrapper .video-player h4.hidden { + display: none; +} + +.video .video-wrapper .video-controls { + position: relative; + border: 0; + background: #282c2e; + color: #f0f3f5; +} + +.video .video-wrapper .video-controls:after { + content: ""; + display: table; + clear: both; +} + +.video .video-wrapper .video-controls:hover ul, +.video .video-wrapper .video-controls:hover div, +.video .video-wrapper .video-controls:focus ul, +.video .video-wrapper .video-controls:focus div { + opacity: 1; +} + +.video .video-wrapper .video-controls .control { + display: inline-block; + vertical-align: middle; + margin: 0; + border: 0; + border-radius: 0; + padding: calc((var(--baseline, 20px) / 2)) calc((var(--baseline, 20px) / 1.5)); + background: #282c2e; + box-shadow: none; + text-shadow: none; + color: #cfd8dc; +} + +.video .video-wrapper .video-controls .control:hover, +.video .video-wrapper .video-controls .control:focus { + background: #171a1b; +} + +.video .video-wrapper .video-controls .control:active, +.video .video-wrapper .video-controls .is-active.control, +.video .video-wrapper .video-controls .active.control { + color: #0ea6ec; +} + +.video .video-wrapper .video-controls .control .icon { + width: 1em; +} + +.video .video-wrapper .video-controls .control .icon.icon-hd { + width: auto; +} + +.video .video-wrapper .video-controls .slider { + transform-origin: bottom left; + transition: height 0.7s ease-in-out 0s; + box-sizing: border-box; + position: absolute; + bottom: 100%; + left: 0; + right: 0; + z-index: 1; + height: calc((var(--baseline, 20px) / 4)); + margin-left: 0; + border: 1px solid #4f595d; + border-radius: 0; + background: #4f595d; +} + +.video .video-wrapper .video-controls .slider:after { + content: ""; + display: table; + clear: both; +} + +.video .video-wrapper .video-controls .slider .ui-widget-header { + background: #8e3e63; + border: 1px solid #8e3e63; + box-shadow: none; + top: -1px; + left: -1px; +} + +.video .video-wrapper .video-controls .slider .ui-corner-all.slider-range { + opacity: 0.3; + background-color: #1e91d3; +} + +.video .video-wrapper .video-controls .slider .ui-slider-handle { + transform-origin: bottom left; + transition: all 0.7s ease-in-out 0s; + box-sizing: border-box; + top: -1px; + height: calc((var(--baseline, 20px) / 4)); + width: calc((var(--baseline, 20px) / 4)); + margin-left: calc(-1 * (var(--baseline, 20px) / 8)); + border: 1px solid #cb598d; + border-radius: calc((var(--baseline, 20px) / 5)); + padding: 0; + background: #cb598d; + box-shadow: none; +} + +.video .video-wrapper .video-controls .slider .ui-slider-handle:focus, +.video .video-wrapper .video-controls .slider .ui-slider-handle:hover { + background-color: #db8baf; + border-color: #db8baf; +} + +.video .video-wrapper .video-controls .vcr { + float: left; + list-style: none; + border-right: 1px solid #282c2e; + padding: 0; +} + +@media (max-width: 1120px) { + .video .video-wrapper .video-controls .vcr { + margin-right: lh(0.5); + font-size: 0.875em; + } +} + +.video .video-wrapper .video-controls .vcr .video_control:focus { + position: relative; +} + +.video .video-wrapper .video-controls .vcr .video_control.skip { + white-space: nowrap; +} + +.video .video-wrapper .video-controls .vcr .vidtime { + padding-left: lh(0.75); + display: inline-block; + color: #cfd8dc; + -webkit-font-smoothing: antialiased; +} + +@media (max-width: 1120px) { + .video .video-wrapper .video-controls .vcr .vidtime { + padding-left: lh(0.5); + } +} + +.video .video-wrapper .video-controls .secondary-controls { + float: right; + border-left: 1px dotted #4f595d; +} + +.video .video-wrapper .video-controls .secondary-controls .volume, +.video .video-wrapper .video-controls .secondary-controls .add-fullscreen, +.video .video-wrapper .video-controls .secondary-controls .grouped-controls, +.video .video-wrapper .video-controls .secondary-controls .auto-advance, +.video .video-wrapper .video-controls .secondary-controls .quality-control { + border-left: 1px dotted #4f595d; +} + +.video .video-wrapper .video-controls .secondary-controls .speed-button:focus, +.video .video-wrapper .video-controls .secondary-controls .volume > .control:focus, +.video .video-wrapper .video-controls .secondary-controls .add-fullscreen:focus, +.video .video-wrapper .video-controls .secondary-controls .auto-advance:focus, +.video .video-wrapper .video-controls .secondary-controls .quality-control:focus, +.video .video-wrapper .video-controls .secondary-controls .toggle-transcript:focus { + position: relative; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container { + position: relative; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu { + transition: none; + position: absolute; + display: none; + bottom: calc((var(--baseline, 20px) * 2)); + right: 0; + width: 120px; + margin: 0; + border: none; + padding: 0; + box-shadow: none; + background-color: #282c2e; + list-style: none; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li { + color: #e7ecee; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang { + text-align: left; + display: block; + width: 100%; + border: 0; + border-radius: 0; + padding: lh(0.5); + background: #282c2e; + box-shadow: none; + color: #e7ecee; + overflow: hidden; + text-shadow: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:hover, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:focus, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:hover, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:focus { + background-color: #4f595d; + color: #fcfcfc; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .speed-option, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .control-lang { + border-left: calc(var(--baseline, 20px) / 10) solid #0ea6ec; + font-weight: var(--font-bold, 700); + color: #0ea6ec; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container.is-opened .menu { + display: block; +} + +.video .video-wrapper .video-controls .secondary-controls .speeds, +.video .video-wrapper .video-controls .secondary-controls .lang, +.video .video-wrapper .video-controls .secondary-controls .grouped-controls { + display: inline-block; +} + +.video .video-wrapper .video-controls .secondary-controls .speeds.is-opened .control .icon { + transform: rotate(-90deg); +} + +.video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { + padding: 0 calc((var(--baseline, 20px) / 3)) 0 0; + font-family: var(--font-family-sans-serif); + color: #e7ecee; +} + +@media (max-width: 1120px) { + .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + } +} + +.video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { + padding: 0 lh(0.5) 0 0; + color: #e7ecee; font-weight: bold; } -.video p { +@media (max-width: 1120px) { + .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { + padding: 0 lh(0.5); + } +} + +.video .video-wrapper .video-controls .secondary-controls .lang .language-menu { + width: var(--baseline, 20px); + padding: calc((var(--baseline, 20px) / 2)) 0; +} + +.video .video-wrapper .video-controls .secondary-controls .lang.is-opened .control .icon { + transform: rotate(90deg); +} + +.video .video-wrapper .video-controls .secondary-controls .volume { + display: inline-block; + position: relative; +} + +.video .video-wrapper .video-controls .secondary-controls .volume.is-opened .volume-slider-container { + display: block; + opacity: 1; +} + +.video .video-wrapper .video-controls .secondary-controls .volume:not(:first-child) > a { + border-left: none; +} + +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { + transition: none; + display: none; + position: absolute; + bottom: calc((var(--baseline, 20px) * 2)); + right: 0; + width: 41px; + height: 120px; + background-color: #282c2e; +} + +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider { + height: 100px; + width: calc((var(--baseline, 20px) / 4)); + margin: 14px auto; + box-sizing: border-box; + border: 1px solid #4f595d; + background: #4f595d; +} + +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle { + transition: height var(--tmg-s2, 2s) ease-in-out 0s, width var(--tmg-s2, 2s) ease-in-out 0s; + left: -5px; + box-sizing: border-box; + height: 13px; + width: 13px; + border: 1px solid #cb598d; + border-radius: calc((var(--baseline, 20px) / 5)); + padding: 0; + background: #cb598d; + box-shadow: none; +} + +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:hover, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:focus { + background: #db8baf; + border-color: #db8baf; +} + +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-range { + background: #8e3e63; + border: 1px solid #8e3e63; + left: -1px; + bottom: -1px; +} + +.video .video-wrapper .video-controls .secondary-controls .quality-control { + font-weight: 700; + letter-spacing: -1px; +} + +.video .video-wrapper .video-controls .secondary-controls .quality-control.active { + color: #0ea6ec; +} + +.video .video-wrapper .video-controls .secondary-controls .quality-control.is-hidden, +.video.closed .video-wrapper .video-controls .secondary-controls .quality-control.subtitles { + display: none !important; +} + +.video .video-wrapper .video-controls .secondary-controls .toggle-transcript.is-active { + color: #0ea6ec; +} + +.video .video-wrapper .video-controls .secondary-controls .lang > .hide-subtitles { + transition: none; +} + +.video .video-wrapper:hover .video-controls .slider { + height: calc((var(--baseline, 20px) / 1.5)); +} + +.video .video-wrapper:hover .video-controls .slider .ui-slider-handle { + height: calc((var(--baseline, 20px) / 1.5)); + width: calc((var(--baseline, 20px) / 1.5)); +} + +.video.video-fullscreen .closed-captions { + width: 65%; +} + +.video.video-fullscreen.closed .closed-captions { + width: 90%; +} + +.video .subtitles { + float: left; + overflow: auto; + max-height: 460px; + width: 31.81818%; + padding: 0; + font-size: 14px; + visibility: visible; +} + +.video .subtitles a { + color: #0074b5; +} + +.video .subtitles .subtitles-menu { + height: 100%; + margin: 0; + padding: 0 3px; + list-style: none; +} + +.video .subtitles .subtitles-menu li { + margin-bottom: 8px; + border: 0; + padding: 0; + color: #0074b5; + line-height: lh(); +} + +.video .subtitles .subtitles-menu li span { + display: block; +} + +.video .subtitles .subtitles-menu li.current { + color: #333; + font-weight: 700; +} + +.video .subtitles .subtitles-menu li.focused { + outline: #000 dotted thin; + outline-offset: -1px; +} + +.video .subtitles .subtitles-menu li:hover, +.video .subtitles .subtitles-menu li:focus { + text-decoration: underline; +} + +.video .subtitles .subtitles-menu li:empty { + margin-bottom: 0; +} + +.video .subtitles .subtitles-menu li.spacing:last-of-type { + position: relative; +} + +.video .subtitles .subtitles-menu li.spacing:last-of-type .transcript-end { + position: absolute; + bottom: 0; +} + +.video.closed .video-wrapper { + width: 100%; + background-color: inherit; +} + +.video.closed .video-wrapper .video-controls.html5 { + bottom: 0; + left: 0; + right: 0; + position: absolute; + z-index: 1; +} + +.video.closed .video-wrapper .video-player-pre, +.video.closed .video-wrapper .video-player-post { + height: 0; +} + +.video.closed .video-wrapper .video-player h3 { + color: black; +} + +.video.closed .subtitles.html5 { + background-color: rgba(243, 243, 243, 0.8); + height: 100%; + position: absolute; + right: 0; + bottom: 0; + top: 0; + width: 275px; + padding: 0 var(--baseline, 20px); + display: none; +} + +.video.video-fullscreen { + background: rgba(0, 0, 0, 0.95); + border: 0; + bottom: 0; + height: 100%; + left: 0; + margin: 0; + padding: 0; + position: fixed; + top: 0; + width: 100%; + vertical-align: middle; + border-radius: 0; +} + +.video.video-fullscreen.closed .tc-wrapper .video-wrapper { + width: 100%; +} + +.video.video-fullscreen .video-wrapper .video-player-pre, +.video.video-fullscreen .video-wrapper .video-player-post { + height: 0; +} + +.video.video-fullscreen .video-wrapper { + position: static; +} + +.video.video-fullscreen .video-wrapper .video-player h3 { + color: white; +} + +.video.video-fullscreen .tc-wrapper { + width: 100%; + height: 100%; + position: static; +} + +.video.video-fullscreen .tc-wrapper:after { + content: ""; + display: table; + clear: both; +} + +.video.video-fullscreen .tc-wrapper .video-wrapper { + height: 100%; + width: 75%; + margin-right: 0; + vertical-align: middle; +} + +.video.video-fullscreen .tc-wrapper .video-wrapper object, +.video.video-fullscreen .tc-wrapper .video-wrapper iframe, +.video.video-fullscreen .tc-wrapper .video-wrapper video { + position: absolute; + width: auto; + height: auto; +} + +.video.video-fullscreen .tc-wrapper .video-controls { + position: absolute; + bottom: 0; + left: 0; + width: 100%; +} + +.video.video-fullscreen .subtitles { + height: 100%; + width: 25%; + padding: lh(); + box-sizing: border-box; + transition: none; + background: var(--black, #000); + visibility: visible; +} + +.video.video-fullscreen .subtitles li { + color: #aaa; +} + +.video.video-fullscreen .subtitles li.current { + color: var(--white, #fff); +} + +.video.is-touch .tc-wrapper .video-wrapper object, +.video.is-touch .tc-wrapper .video-wrapper iframe, +.video.is-touch .tc-wrapper .video-wrapper video { + width: 100%; + height: 100%; +} + +.video .video-pre-roll { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: 100%; + background-color: var(--black, #000); +} + +.video .video-pre-roll.is-html5 { + background-size: 15%; +} + +.video .video-pre-roll .btn-play.btn-pre-roll { + padding: var(--baseline, 20px); + border: none; + border-radius: var(--baseline, 20px); + background: var(--black-t2, rgba(0, 0, 0, 0.5)); + box-shadow: none; +} + +.video .video-pre-roll .btn-play.btn-pre-roll::after { + display: none; +} + +.video .video-pre-roll .btn-play.btn-pre-roll img { + height: calc((var(--baseline, 20px) * 4)); + width: calc((var(--baseline, 20px) * 4)); +} + +.video .video-pre-roll .btn-play.btn-pre-roll:hover, +.video .video-pre-roll .btn-play.btn-pre-roll:focus { + background: var(--blue, #0075b4); +} + +.video .video-wrapper .video-controls .slider .ui-slider-handle, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle, +.video .subtitles .subtitles-menu li, +.a11y-menu-container .a11y-menu-list li { cursor: pointer; } + +.video.closed .subtitles.html5 { + z-index: 0; +} + +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { + z-index: 10; +} + +.video .video-pre-roll, +.a11y-menu-container .a11y-menu-list { + z-index: 1000; +} + +.video.video-fullscreen, +.video.video-fullscreen .tc-wrapper .video-controls, +.overlay { + z-index: 10000; +} + +.contextmenu, +.submenu { + z-index: 100000; +} + +.video-tracks .a11y-menu-container > a::after { + font-family: FontAwesome; + -webkit-font-smoothing: antialiased; + display: inline-block; + speak: none; +} + +.a11y-menu-container { + position: relative; +} + +.a11y-menu-container.open .a11y-menu-list { + display: block; +} + +.a11y-menu-container .a11y-menu-list { + top: 100%; + margin: 0; + padding: 0; + display: none; + position: absolute; + list-style: none; + background-color: var(--white, #fff); + border: 1px solid #eee; +} + +.a11y-menu-container .a11y-menu-list li { + margin: 0; + padding: 0; + border-bottom: 1px solid #eee; + color: var(--white, #fff); +} + +.a11y-menu-container .a11y-menu-list li a { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--gray-l2, #adadad); + font-size: 14px; + line-height: 23px; +} + +.a11y-menu-container .a11y-menu-list li a:hover, +.a11y-menu-container .a11y-menu-list li a:focus { + color: var(--gray-d1, #5e5e5e); +} + +.a11y-menu-container .a11y-menu-list li.active a { + color: #009fe6; +} + +.a11y-menu-container .a11y-menu-list li:last-child { + box-shadow: none; + border-bottom: 0; + margin-top: 0; +} + +.video-tracks .a11y-menu-container { + display: inline-block; + vertical-align: top; + border-left: 1px solid #eee; +} + +.video-tracks .a11y-menu-container.open > a { + background-color: var(--action-primary-active-bg, #0075b4); + color: var(--very-light-text, white); +} + +.video-tracks .a11y-menu-container.open > a::after { + color: var(--very-light-text, white); +} + +.video-tracks .a11y-menu-container > a { + transition: all var(--tmg-f2, 0.25s) ease-in-out 0s; + font-size: 12px; + display: block; + border-radius: 0 3px 3px 0; + background-color: var(--very-light-text, white); + padding: calc((var(--baseline, 20px) * 0.75)) calc((var(--baseline, 20px) * 1.25)) calc((var(--baseline, 20px) * 0.75)) calc((var(--baseline, 20px) * 0.75)); + color: var(--gray-l2, #adadad); + min-width: 1.5em; + line-height: 14px; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; +} + +.video-tracks .a11y-menu-container > a::after { + content: "\f0d7"; + position: absolute; + right: calc((var(--baseline, 20px) * 0.5)); + top: 33%; + color: var(--lighter-base-font-color, #646464); +} + +.video-tracks .a11y-menu-container .a11y-menu-list { + right: 0; +} + +.video-tracks .a11y-menu-container .a11y-menu-list li { + font-size: 0.875em; +} + +.video-tracks .a11y-menu-container .a11y-menu-list li a { + border: 0; + display: block; + padding: 0.70788em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.contextmenu, +.submenu { + border: 1px solid #333; + background: var(--white, #fff); + color: #333; + padding: 0; + margin: 0; + list-style: none; + position: absolute; + top: 0; + display: none; + outline: none; + cursor: default; + white-space: nowrap; +} + +.contextmenu.is-opened, +.submenu.is-opened { + display: block; +} + +.contextmenu .menu-item, +.contextmenu .submenu-item, +.submenu .menu-item, +.submenu .submenu-item { + border-top: 1px solid var(--gray-l3, #c8c8c8); + padding: calc((var(--baseline, 20px) / 4)) calc((var(--baseline, 20px) / 2)); + outline: none; +} + +.contextmenu .menu-item > span, +.contextmenu .submenu-item > span, +.submenu .menu-item > span, +.submenu .submenu-item > span { + color: #333; +} + +.contextmenu .menu-item:first-child, +.contextmenu .submenu-item:first-child, +.submenu .menu-item:first-child, +.submenu .submenu-item:first-child { + border-top: none; +} + +.contextmenu .menu-item:focus, +.contextmenu .submenu-item:focus, +.submenu .menu-item:focus, +.submenu .submenu-item:focus { + background: #333; + color: var(--white, #fff); +} + +.contextmenu .menu-item:focus > span, +.contextmenu .submenu-item:focus > span, +.submenu .menu-item:focus > span, +.submenu .submenu-item:focus > span { + color: var(--white, #fff); +} + +.contextmenu .submenu-item, +.submenu .submenu-item { + position: relative; + padding: calc((var(--baseline, 20px) / 4)) var(--baseline, 20px) calc((var(--baseline, 20px) / 4)) calc((var(--baseline, 20px) / 2)); +} + +.contextmenu .submenu-item::after, +.submenu .submenu-item::after { + content: '\25B6'; + position: absolute; + right: 5px; + line-height: 25px; + font-size: 10px; +} + +.contextmenu .submenu-item .submenu, +.submenu .submenu-item .submenu { + display: none; +} + +.contextmenu .submenu-item.is-opened, +.submenu .submenu-item.is-opened { + background: #333; + color: var(--white, #fff); +} + +.contextmenu .submenu-item.is-opened > span, +.submenu .submenu-item.is-opened > span { + color: var(--white, #fff); +} + +.contextmenu .submenu-item.is-opened > .submenu, +.submenu .submenu-item.is-opened > .submenu { + display: block; +} + +.contextmenu .submenu-item .is-selected, +.submenu .submenu-item .is-selected { + font-weight: bold; +} + +.contextmenu .is-disabled, +.submenu .is-disabled { + pointer-events: none; + color: var(--gray-l3, #c8c8c8); +} + +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: transparent; +} diff --git a/xblocks_contrib/video/static/js/dist/manifest.json b/xblocks_contrib/video/static/js/dist/manifest.json new file mode 100644 index 00000000..518f105a --- /dev/null +++ b/xblocks_contrib/video/static/js/dist/manifest.json @@ -0,0 +1,4 @@ +{ + "base_url": "/static/js/dist", + "video-xblock.js": "/video-xblock.js" +} \ No newline at end of file diff --git a/xblocks_contrib/video/static/js/dist/video-xblock.js b/xblocks_contrib/video/static/js/dist/video-xblock.js new file mode 100644 index 00000000..4fa2ab2b --- /dev/null +++ b/xblocks_contrib/video/static/js/dist/video-xblock.js @@ -0,0 +1,2 @@ +/*! For license information please see video-xblock.js.LICENSE.txt */ +(()=>{var e={119:function(e,t,n){!function(e){"use strict";var t={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},n={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};e.defineLocale("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(e){return/بعد از ظهر/.test(e)},meridiem:function(e,t,n){return e<12?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چند ثانیه",ss:"%d ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[۰-۹]/g,(function(e){return n[e]})).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]})).replace(/,/g,"،")},dayOfMonthOrdinalParse:/\d{1,2}م/,ordinal:"%dم",week:{dow:6,doy:12}})}(n(5093))},158:function(e,t,n){!function(e){"use strict";var t=["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],n=["اتوار","پیر","منگل","بدھ","جمعرات","جمعہ","ہفتہ"];e.defineLocale("ur",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,t,n){return e<12?"صبح":"شام"},calendar:{sameDay:"[آج بوقت] LT",nextDay:"[کل بوقت] LT",nextWeek:"dddd [بوقت] LT",lastDay:"[گذشتہ روز بوقت] LT",lastWeek:"[گذشتہ] dddd [بوقت] LT",sameElse:"L"},relativeTime:{future:"%s بعد",past:"%s قبل",s:"چند سیکنڈ",ss:"%d سیکنڈ",m:"ایک منٹ",mm:"%d منٹ",h:"ایک گھنٹہ",hh:"%d گھنٹے",d:"ایک دن",dd:"%d دن",M:"ایک ماہ",MM:"%d ماہ",y:"ایک سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})}(n(5093))},217:function(e,t,n){!function(e){"use strict";e.defineLocale("gd",{months:["Am Faoilleach","An Gearran","Am Màrt","An Giblean","An Cèitean","An t-Ògmhios","An t-Iuchar","An Lùnastal","An t-Sultain","An Dàmhair","An t-Samhain","An Dùbhlachd"],monthsShort:["Faoi","Gear","Màrt","Gibl","Cèit","Ògmh","Iuch","Lùn","Sult","Dàmh","Samh","Dùbh"],monthsParseExact:!0,weekdays:["Didòmhnaich","Diluain","Dimàirt","Diciadain","Diardaoin","Dihaoine","Disathairne"],weekdaysShort:["Did","Dil","Dim","Dic","Dia","Dih","Dis"],weekdaysMin:["Dò","Lu","Mà","Ci","Ar","Ha","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-màireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-dè aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",ss:"%d diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"mìos",MM:"%d mìosan",y:"bliadhna",yy:"%d bliadhna"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){return e+(1===e?"d":e%10==2?"na":"mh")},week:{dow:1,doy:4}})}(n(5093))},304:function(e,t,n){!function(e){"use strict";e.defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var i=100*e+t;return i<600?"凌晨":i<900?"早上":i<1130?"上午":i<1230?"中午":i<1800?"下午":"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})}(n(5093))},306:function(e,t,n){!function(e){"use strict";e.defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"[Во] dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"за %s",past:"пред %s",s:"неколку секунди",ss:"%d секунди",m:"една минута",mm:"%d минути",h:"еден час",hh:"%d часа",d:"еден ден",dd:"%d дена",M:"еден месец",MM:"%d месеци",y:"една година",yy:"%d години"},dayOfMonthOrdinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var t=e%10,n=e%100;return 0===e?e+"-ев":0===n?e+"-ен":n>10&&n<20?e+"-ти":1===t?e+"-ви":2===t?e+"-ри":7===t||8===t?e+"-ми":e+"-ти"},week:{dow:1,doy:7}})}(n(5093))},527:function(e,t,n){!function(e){"use strict";var t="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),n=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",t[7],t[8],t[9]];function i(e,i,r,a){var s="";switch(r){case"s":return a?"muutaman sekunnin":"muutama sekunti";case"ss":s=a?"sekunnin":"sekuntia";break;case"m":return a?"minuutin":"minuutti";case"mm":s=a?"minuutin":"minuuttia";break;case"h":return a?"tunnin":"tunti";case"hh":s=a?"tunnin":"tuntia";break;case"d":return a?"päivän":"päivä";case"dd":s=a?"päivän":"päivää";break;case"M":return a?"kuukauden":"kuukausi";case"MM":s=a?"kuukauden":"kuukautta";break;case"y":return a?"vuoden":"vuosi";case"yy":s=a?"vuoden":"vuotta"}return function(e,i){return e<10?i?n[e]:t[e]:e}(e,a)+" "+s}e.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:i,ss:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},609:function(e,t,n){!function(e){"use strict";var t="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");function n(e,t,n,i){var r=e;switch(n){case"s":return i||t?"néhány másodperc":"néhány másodperce";case"ss":return r+(i||t)?" másodperc":" másodperce";case"m":return"egy"+(i||t?" perc":" perce");case"mm":return r+(i||t?" perc":" perce");case"h":return"egy"+(i||t?" óra":" órája");case"hh":return r+(i||t?" óra":" órája");case"d":return"egy"+(i||t?" nap":" napja");case"dd":return r+(i||t?" nap":" napja");case"M":return"egy"+(i||t?" hónap":" hónapja");case"MM":return r+(i||t?" hónap":" hónapja");case"y":return"egy"+(i||t?" év":" éve");case"yy":return r+(i||t?" év":" éve")}return""}function i(e){return(e?"":"[múlt] ")+"["+t[this.day()]+"] LT[-kor]"}e.defineLocale("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan._feb._márc._ápr._máj._jún._júl._aug._szept._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,t,n){return e<12?!0===n?"de":"DE":!0===n?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return i.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return i.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},708:function(e,t,n){!function(e){"use strict";var t=["ޖެނުއަރީ","ފެބްރުއަރީ","މާރިޗު","އޭޕްރީލު","މޭ","ޖޫން","ޖުލައި","އޯގަސްޓު","ސެޕްޓެމްބަރު","އޮކްޓޯބަރު","ނޮވެމްބަރު","ޑިސެމްބަރު"],n=["އާދިއްތަ","ހޯމަ","އަންގާރަ","ބުދަ","ބުރާސްފަތި","ހުކުރު","ހޮނިހިރު"];e.defineLocale("dv",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:"އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/މކ|މފ/,isPM:function(e){return"މފ"===e},meridiem:function(e,t,n){return e<12?"މކ":"މފ"},calendar:{sameDay:"[މިއަދު] LT",nextDay:"[މާދަމާ] LT",nextWeek:"dddd LT",lastDay:"[އިއްޔެ] LT",lastWeek:"[ފާއިތުވި] dddd LT",sameElse:"L"},relativeTime:{future:"ތެރޭގައި %s",past:"ކުރިން %s",s:"ސިކުންތުކޮޅެއް",ss:"d% ސިކުންތު",m:"މިނިޓެއް",mm:"މިނިޓު %d",h:"ގަޑިއިރެއް",hh:"ގަޑިއިރު %d",d:"ދުވަހެއް",dd:"ދުވަސް %d",M:"މަހެއް",MM:"މަސް %d",y:"އަހަރެއް",yy:"އަހަރު %d"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:7,doy:12}})}(n(5093))},748:function(e,t,n){!function(e){"use strict";e.defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"hh:mm A",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",ss:"sekunde %d",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"siku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,doy:7}})}(n(5093))},838:function(e,t,n){!function(e){"use strict";var t="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),i=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],r=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;e.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,i){return e?/-MMM-/.test(i)?n[e.month()]:t[e.month()]:t},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})}(n(5093))},1011:function(e,t,n){!function(e){"use strict";e.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"[På] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",ss:"%d sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}(\:e|\:a)/,ordinal:function(e){var t=e%10;return e+(1==~~(e%100/10)?":e":1===t||2===t?":a":":e")},week:{dow:1,doy:4}})}(n(5093))},1025:function(e,t,n){!function(e){"use strict";var t={1:"௧",2:"௨",3:"௩",4:"௪",5:"௫",6:"௬",7:"௭",8:"௮",9:"௯",0:"௦"},n={"௧":"1","௨":"2","௩":"3","௪":"4","௫":"5","௬":"6","௭":"7","௮":"8","௯":"9","௦":"0"};e.defineLocale("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",ss:"%d விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},dayOfMonthOrdinalParse:/\d{1,2}வது/,ordinal:function(e){return e+"வது"},preparse:function(e){return e.replace(/[௧௨௩௪௫௬௭௮௯௦]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},meridiemParse:/யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,meridiem:function(e,t,n){return e<2?" யாமம்":e<6?" வைகறை":e<10?" காலை":e<14?" நண்பகல்":e<18?" எற்பாடு":e<22?" மாலை":" யாமம்"},meridiemHour:function(e,t){return 12===e&&(e=0),"யாமம்"===t?e<2?e:e+12:"வைகறை"===t||"காலை"===t||"நண்பகல்"===t&&e>=10?e:e+12},week:{dow:0,doy:6}})}(n(5093))},1052:function(e,t,n){!function(e){"use strict";var t="pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_");function n(e,n,i,r){var a=function(e){var n=Math.floor(e%1e3/100),i=Math.floor(e%100/10),r=e%10,a="";return n>0&&(a+=t[n]+"vatlh"),i>0&&(a+=(""!==a?" ":"")+t[i]+"maH"),r>0&&(a+=(""!==a?" ":"")+t[r]),""===a?"pagh":a}(e);switch(i){case"ss":return a+" lup";case"mm":return a+" tup";case"hh":return a+" rep";case"dd":return a+" jaj";case"MM":return a+" jar";case"yy":return a+" DIS"}}e.defineLocale("tlh",{months:"tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’".split("_"),monthsShort:"jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’".split("_"),monthsParseExact:!0,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[wa’leS] LT",nextWeek:"LLL",lastDay:"[wa’Hu’] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:function(e){var t=e;return-1!==e.indexOf("jaj")?t.slice(0,-3)+"leS":-1!==e.indexOf("jar")?t.slice(0,-3)+"waQ":-1!==e.indexOf("DIS")?t.slice(0,-3)+"nem":t+" pIq"},past:function(e){var t=e;return-1!==e.indexOf("jaj")?t.slice(0,-3)+"Hu’":-1!==e.indexOf("jar")?t.slice(0,-3)+"wen":-1!==e.indexOf("DIS")?t.slice(0,-3)+"ben":t+" ret"},s:"puS lup",ss:n,m:"wa’ tup",mm:n,h:"wa’ rep",hh:n,d:"wa’ jaj",dd:n,M:"wa’ jar",MM:n,y:"wa’ DIS",yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},1135:function(e,t,n){!function(e){"use strict";e.defineLocale("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Thg 01_Thg 02_Thg 03_Thg 04_Thg 05_Thg 06_Thg 07_Thg 08_Thg 09_Thg 10_Thg 11_Thg 12".split("_"),monthsParseExact:!0,weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:!0,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,t,n){return e<12?n?"sa":"SA":n?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần trước lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",ss:"%d giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",w:"một tuần",ww:"%d tuần",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})}(n(5093))},1221:function(e,t,n){!function(e){"use strict";var t=["جنوري","فيبروري","مارچ","اپريل","مئي","جون","جولاءِ","آگسٽ","سيپٽمبر","آڪٽوبر","نومبر","ڊسمبر"],n=["آچر","سومر","اڱارو","اربع","خميس","جمع","ڇنڇر"];e.defineLocale("sd",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,t,n){return e<12?"صبح":"شام"},calendar:{sameDay:"[اڄ] LT",nextDay:"[سڀاڻي] LT",nextWeek:"dddd [اڳين هفتي تي] LT",lastDay:"[ڪالهه] LT",lastWeek:"[گزريل هفتي] dddd [تي] LT",sameElse:"L"},relativeTime:{future:"%s پوء",past:"%s اڳ",s:"چند سيڪنڊ",ss:"%d سيڪنڊ",m:"هڪ منٽ",mm:"%d منٽ",h:"هڪ ڪلاڪ",hh:"%d ڪلاڪ",d:"هڪ ڏينهن",dd:"%d ڏينهن",M:"هڪ مهينو",MM:"%d مهينا",y:"هڪ سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})}(n(5093))},1290:function(e,t,n){!function(e){"use strict";var t={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},n={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};e.defineLocale("bn",{months:"জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি".split("_"),weekdaysMin:"রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",ss:"%d সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(e){return e.replace(/[১২৩৪৫৬৭৮৯০]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},meridiemParse:/রাত|সকাল|দুপুর|বিকাল|রাত/,meridiemHour:function(e,t){return 12===e&&(e=0),"রাত"===t&&e>=4||"দুপুর"===t&&e<5||"বিকাল"===t?e+12:e},meridiem:function(e,t,n){return e<4?"রাত":e<10?"সকাল":e<17?"দুপুর":e<20?"বিকাল":"রাত"},week:{dow:0,doy:6}})}(n(5093))},1470:function(e,t,n){!function(e){"use strict";function t(e,t,n){return e+" "+function(e,t){return 2===t?function(e){var t={m:"v",b:"v",d:"z"};return void 0===t[e.charAt(0)]?e:t[e.charAt(0)]+e.substring(1)}(e):e}({mm:"munutenn",MM:"miz",dd:"devezh"}[n],e)}function n(e){return e>9?n(e%10):e}var i=[/^gen/i,/^c[ʼ\']hwe/i,/^meu/i,/^ebr/i,/^mae/i,/^(mez|eve)/i,/^gou/i,/^eos/i,/^gwe/i,/^her/i,/^du/i,/^ker/i],r=/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,a=[/^Su/i,/^Lu/i,/^Me([^r]|$)/i,/^Mer/i,/^Ya/i,/^Gw/i,/^Sa/i];e.defineLocale("br",{months:"Genver_Cʼhwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_Cʼhwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Mercʼher_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParse:a,fullWeekdaysParse:[/^sul/i,/^lun/i,/^meurzh/i,/^merc[ʼ\']her/i,/^yaou/i,/^gwener/i,/^sadorn/i],shortWeekdaysParse:[/^Sul/i,/^Lun/i,/^Meu/i,/^Mer/i,/^Yao/i,/^Gwe/i,/^Sad/i],minWeekdaysParse:a,monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,monthsShortStrictRegex:/^(gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY HH:mm",LLLL:"dddd, D [a viz] MMMM YYYY HH:mm"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warcʼhoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Decʼh da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s ʼzo",s:"un nebeud segondennoù",ss:"%d eilenn",m:"ur vunutenn",mm:t,h:"un eur",hh:"%d eur",d:"un devezh",dd:t,M:"ur miz",MM:t,y:"ur bloaz",yy:function(e){switch(n(e)){case 1:case 3:case 4:case 5:case 9:return e+" bloaz";default:return e+" vloaz"}}},dayOfMonthOrdinalParse:/\d{1,2}(añ|vet)/,ordinal:function(e){return e+(1===e?"añ":"vet")},week:{dow:1,doy:4},meridiemParse:/a.m.|g.m./,isPM:function(e){return"g.m."===e},meridiem:function(e,t,n){return e<12?"a.m.":"g.m."}})}(n(5093))},1485:e=>{!function(t,n){e.exports?e.exports=n():t.getSize=n()}(window,(function(){function e(e){let t=parseFloat(e);return-1==e.indexOf("%")&&!isNaN(t)&&t}let t=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"];return t.length,function(n){if("string"==typeof n&&(n=document.querySelector(n)),!n||"object"!=typeof n||!n.nodeType)return;let i=getComputedStyle(n);if("none"==i.display)return function(){let e={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0};return t.forEach((t=>{e[t]=0})),e}();let r={};r.width=n.offsetWidth,r.height=n.offsetHeight;let a=r.isBorderBox="border-box"==i.boxSizing;t.forEach((e=>{let t=i[e],n=parseFloat(t);r[e]=isNaN(n)?0:n}));let s=r.paddingLeft+r.paddingRight,o=r.paddingTop+r.paddingBottom,d=r.marginLeft+r.marginRight,l=r.marginTop+r.marginBottom,u=r.borderLeftWidth+r.borderRightWidth,c=r.borderTopWidth+r.borderBottomWidth,h=e(i.width);!1!==h&&(r.width=h+(a?0:s+u));let f=e(i.height);return!1!==f&&(r.height=f+(a?0:o+c)),r.innerWidth=r.width-(s+u),r.innerHeight=r.height-(o+c),r.outerWidth=r.width+d,r.outerHeight=r.height+l,r}}))},1488:function(e,t,n){!function(e){"use strict";var t=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},n={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},i=function(e){return function(i,r,a,s){var o=t(i),d=n[e][t(i)];return 2===o&&(d=d[r?0:1]),d.replace(/%d/i,i)}},r=["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar-dz",{months:r,monthsShort:r,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:i("s"),ss:i("s"),m:i("m"),mm:i("m"),h:i("h"),hh:i("h"),d:i("d"),dd:i("d"),M:i("M"),MM:i("M"),y:i("y"),yy:i("y")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:0,doy:4}})}(n(5093))},1509:function(e,t,n){!function(e){"use strict";var t={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},i=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},r={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},a=function(e){return function(t,n,a,s){var o=i(t),d=r[e][i(t)];return 2===o&&(d=d[n?0:1]),d.replace(/%d/i,t)}},s=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar",{months:s,monthsShort:s,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:a("s"),ss:a("s"),m:a("m"),mm:a("m"),h:a("h"),hh:a("h"),d:a("d"),dd:a("d"),M:a("M"),MM:a("M"),y:a("y"),yy:a("y")},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,(function(e){return n[e]})).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]})).replace(/,/g,"،")},week:{dow:6,doy:12}})}(n(5093))},1541:function(e,t,n){!function(e){"use strict";e.defineLocale("zh-mo",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"D/M/YYYY",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var i=100*e+t;return i<600?"凌晨":i<900?"早上":i<1130?"上午":i<1230?"中午":i<1800?"下午":"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})}(n(5093))},1545:function(e,t,n){!function(e){"use strict";var t={1:"༡",2:"༢",3:"༣",4:"༤",5:"༥",6:"༦",7:"༧",8:"༨",9:"༩",0:"༠"},n={"༡":"1","༢":"2","༣":"3","༤":"4","༥":"5","༦":"6","༧":"7","༨":"8","༩":"9","༠":"0"};e.defineLocale("bo",{months:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),monthsShort:"ཟླ་1_ཟླ་2_ཟླ་3_ཟླ་4_ཟླ་5_ཟླ་6_ཟླ་7_ཟླ་8_ཟླ་9_ཟླ་10_ཟླ་11_ཟླ་12".split("_"),monthsShortRegex:/^(ཟླ་\d{1,2})/,monthsParseExact:!0,weekdays:"གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་".split("_"),weekdaysShort:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),weekdaysMin:"ཉི_ཟླ_མིག_ལྷག_ཕུར_སངས_སྤེན".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[དི་རིང] LT",nextDay:"[སང་ཉིན] LT",nextWeek:"[བདུན་ཕྲག་རྗེས་མ], LT",lastDay:"[ཁ་སང] LT",lastWeek:"[བདུན་ཕྲག་མཐའ་མ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ལ་",past:"%s སྔན་ལ",s:"ལམ་སང",ss:"%d སྐར་ཆ།",m:"སྐར་མ་གཅིག",mm:"%d སྐར་མ",h:"ཆུ་ཚོད་གཅིག",hh:"%d ཆུ་ཚོད",d:"ཉིན་གཅིག",dd:"%d ཉིན་",M:"ཟླ་བ་གཅིག",MM:"%d ཟླ་བ",y:"ལོ་གཅིག",yy:"%d ལོ"},preparse:function(e){return e.replace(/[༡༢༣༤༥༦༧༨༩༠]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},meridiemParse:/མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,meridiemHour:function(e,t){return 12===e&&(e=0),"མཚན་མོ"===t&&e>=4||"ཉིན་གུང"===t&&e<5||"དགོང་དག"===t?e+12:e},meridiem:function(e,t,n){return e<4?"མཚན་མོ":e<10?"ཞོགས་ཀས":e<17?"ཉིན་གུང":e<20?"དགོང་དག":"མཚན་མོ"},week:{dow:0,doy:6}})}(n(5093))},1632:function(e,t,n){!function(e){"use strict";e.defineLocale("zh-hk",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var i=100*e+t;return i<600?"凌晨":i<900?"早上":i<1200?"上午":1200===i?"中午":i<1800?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})}(n(5093))},1713:function(e,t,n){!function(e){"use strict";e.defineLocale("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",ss:"%d שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(e){return 2===e?"שעתיים":e+" שעות"},d:"יום",dd:function(e){return 2===e?"יומיים":e+" ימים"},M:"חודש",MM:function(e){return 2===e?"חודשיים":e+" חודשים"},y:"שנה",yy:function(e){return 2===e?"שנתיים":e%10==0&&10!==e?e+" שנה":e+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,isPM:function(e){return/^(אחה"צ|אחרי הצהריים|בערב)$/.test(e)},meridiem:function(e,t,n){return e<5?"לפנות בוקר":e<10?"בבוקר":e<12?n?'לפנה"צ':"לפני הצהריים":e<18?n?'אחה"צ':"אחרי הצהריים":"בערב"}})}(n(5093))},1734:function(e,t,n){!function(e){"use strict";e.defineLocale("ga",{months:["Eanáir","Feabhra","Márta","Aibreán","Bealtaine","Meitheamh","Iúil","Lúnasa","Meán Fómhair","Deireadh Fómhair","Samhain","Nollaig"],monthsShort:["Ean","Feabh","Márt","Aib","Beal","Meith","Iúil","Lún","M.F.","D.F.","Samh","Noll"],monthsParseExact:!0,weekdays:["Dé Domhnaigh","Dé Luain","Dé Máirt","Dé Céadaoin","Déardaoin","Dé hAoine","Dé Sathairn"],weekdaysShort:["Domh","Luan","Máirt","Céad","Déar","Aoine","Sath"],weekdaysMin:["Do","Lu","Má","Cé","Dé","A","Sa"],longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Inniu ag] LT",nextDay:"[Amárach ag] LT",nextWeek:"dddd [ag] LT",lastDay:"[Inné ag] LT",lastWeek:"dddd [seo caite] [ag] LT",sameElse:"L"},relativeTime:{future:"i %s",past:"%s ó shin",s:"cúpla soicind",ss:"%d soicind",m:"nóiméad",mm:"%d nóiméad",h:"uair an chloig",hh:"%d uair an chloig",d:"lá",dd:"%d lá",M:"mí",MM:"%d míonna",y:"bliain",yy:"%d bliain"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){return e+(1===e?"d":e%10==2?"na":"mh")},week:{dow:1,doy:4}})}(n(5093))},1765:function(e,t,n){!function(e){"use strict";e.defineLocale("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",ss:"%d ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,doy:12}})}(n(5093))},1794:function(e,t,n){!function(e){"use strict";e.defineLocale("ka",{months:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:{standalone:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),format:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_"),isFormat:/(წინა|შემდეგ)/},weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(e){return e.replace(/(წამ|წუთ|საათ|წელ|დღ|თვ)(ი|ე)/,(function(e,t,n){return"ი"===n?t+"ში":t+n+"ში"}))},past:function(e){return/(წამი|წუთი|საათი|დღე|თვე)/.test(e)?e.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(e)?e.replace(/წელი$/,"წლის წინ"):e},s:"რამდენიმე წამი",ss:"%d წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},dayOfMonthOrdinalParse:/0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,ordinal:function(e){return 0===e?e:1===e?e+"-ლი":e<20||e<=100&&e%20==0||e%100==0?"მე-"+e:e+"-ე"},week:{dow:1,doy:7}})}(n(5093))},1827:function(e,t,n){!function(e){"use strict";e.defineLocale("it-ch",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){return 0===this.day()?"[la scorsa] dddd [alle] LT":"[lo scorso] dddd [alle] LT"},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})}(n(5093))},1848:function(e,t,n){!function(e){"use strict";e.defineLocale("mi",{months:"Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea".split("_"),monthsShort:"Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki".split("_"),monthsRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,weekdays:"Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei".split("_"),weekdaysShort:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),weekdaysMin:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [i] HH:mm",LLLL:"dddd, D MMMM YYYY [i] HH:mm"},calendar:{sameDay:"[i teie mahana, i] LT",nextDay:"[apopo i] LT",nextWeek:"dddd [i] LT",lastDay:"[inanahi i] LT",lastWeek:"dddd [whakamutunga i] LT",sameElse:"L"},relativeTime:{future:"i roto i %s",past:"%s i mua",s:"te hēkona ruarua",ss:"%d hēkona",m:"he meneti",mm:"%d meneti",h:"te haora",hh:"%d haora",d:"he ra",dd:"%d ra",M:"he marama",MM:"%d marama",y:"he tau",yy:"%d tau"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})}(n(5093))},1885:function(e,t,n){!function(e){"use strict";e.defineLocale("te",{months:"జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జులై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్".split("_"),monthsShort:"జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జులై_ఆగ._సెప్._అక్టో._నవ._డిసె.".split("_"),monthsParseExact:!0,weekdays:"ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం".split("_"),weekdaysShort:"ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని".split("_"),weekdaysMin:"ఆ_సో_మం_బు_గు_శు_శ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[నేడు] LT",nextDay:"[రేపు] LT",nextWeek:"dddd, LT",lastDay:"[నిన్న] LT",lastWeek:"[గత] dddd, LT",sameElse:"L"},relativeTime:{future:"%s లో",past:"%s క్రితం",s:"కొన్ని క్షణాలు",ss:"%d సెకన్లు",m:"ఒక నిమిషం",mm:"%d నిమిషాలు",h:"ఒక గంట",hh:"%d గంటలు",d:"ఒక రోజు",dd:"%d రోజులు",M:"ఒక నెల",MM:"%d నెలలు",y:"ఒక సంవత్సరం",yy:"%d సంవత్సరాలు"},dayOfMonthOrdinalParse:/\d{1,2}వ/,ordinal:"%dవ",meridiemParse:/రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/,meridiemHour:function(e,t){return 12===e&&(e=0),"రాత్రి"===t?e<4?e:e+12:"ఉదయం"===t?e:"మధ్యాహ్నం"===t?e>=10?e:e+12:"సాయంత్రం"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"రాత్రి":e<10?"ఉదయం":e<17?"మధ్యాహ్నం":e<20?"సాయంత్రం":"రాత్రి"},week:{dow:0,doy:6}})}(n(5093))},2124:function(e,t,n){!function(e){"use strict";function t(e,t,n,i){var r={s:["थोडया सॅकंडांनी","थोडे सॅकंड"],ss:[e+" सॅकंडांनी",e+" सॅकंड"],m:["एका मिणटान","एक मिनूट"],mm:[e+" मिणटांनी",e+" मिणटां"],h:["एका वरान","एक वर"],hh:[e+" वरांनी",e+" वरां"],d:["एका दिसान","एक दीस"],dd:[e+" दिसांनी",e+" दीस"],M:["एका म्हयन्यान","एक म्हयनो"],MM:[e+" म्हयन्यानी",e+" म्हयने"],y:["एका वर्सान","एक वर्स"],yy:[e+" वर्सांनी",e+" वर्सां"]};return i?r[n][0]:r[n][1]}e.defineLocale("gom-deva",{months:{standalone:"जानेवारी_फेब्रुवारी_मार्च_एप्रील_मे_जून_जुलय_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),format:"जानेवारीच्या_फेब्रुवारीच्या_मार्चाच्या_एप्रीलाच्या_मेयाच्या_जूनाच्या_जुलयाच्या_ऑगस्टाच्या_सप्टेंबराच्या_ऑक्टोबराच्या_नोव्हेंबराच्या_डिसेंबराच्या".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"जाने._फेब्रु._मार्च_एप्री._मे_जून_जुल._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"आयतार_सोमार_मंगळार_बुधवार_बिरेस्तार_सुक्रार_शेनवार".split("_"),weekdaysShort:"आयत._सोम._मंगळ._बुध._ब्रेस्त._सुक्र._शेन.".split("_"),weekdaysMin:"आ_सो_मं_बु_ब्रे_सु_शे".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [वाजतां]",LTS:"A h:mm:ss [वाजतां]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [वाजतां]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [वाजतां]",llll:"ddd, D MMM YYYY, A h:mm [वाजतां]"},calendar:{sameDay:"[आयज] LT",nextDay:"[फाल्यां] LT",nextWeek:"[फुडलो] dddd[,] LT",lastDay:"[काल] LT",lastWeek:"[फाटलो] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s आदीं",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(वेर)/,ordinal:function(e,t){return"D"===t?e+"वेर":e},week:{dow:0,doy:3},meridiemParse:/राती|सकाळीं|दनपारां|सांजे/,meridiemHour:function(e,t){return 12===e&&(e=0),"राती"===t?e<4?e:e+12:"सकाळीं"===t?e:"दनपारां"===t?e>12?e:e+12:"सांजे"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"राती":e<12?"सकाळीं":e<16?"दनपारां":e<20?"सांजे":"राती"}})}(n(5093))},2137:function(e){!function(t,n){e.exports?e.exports=n():t.EvEmitter=n()}("undefined"!=typeof window?window:this,(function(){function e(){}let t=e.prototype;return t.on=function(e,t){if(!e||!t)return this;let n=this._events=this._events||{},i=n[e]=n[e]||[];return i.includes(t)||i.push(t),this},t.once=function(e,t){if(!e||!t)return this;this.on(e,t);let n=this._onceEvents=this._onceEvents||{};return(n[e]=n[e]||{})[t]=!0,this},t.off=function(e,t){let n=this._events&&this._events[e];if(!n||!n.length)return this;let i=n.indexOf(t);return-1!=i&&n.splice(i,1),this},t.emitEvent=function(e,t){let n=this._events&&this._events[e];if(!n||!n.length)return this;n=n.slice(0),t=t||[];let i=this._onceEvents&&this._onceEvents[e];for(let r of n)i&&i[r]&&(this.off(e,r),delete i[r]),r.apply(this,t);return this},t.allOff=function(){return delete this._events,delete this._onceEvents,this},e}))},2218:function(e,t,n){!function(e){"use strict";e.defineLocale("yo",{months:"Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀".split("_"),monthsShort:"Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀".split("_"),weekdays:"Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta".split("_"),weekdaysShort:"Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá".split("_"),weekdaysMin:"Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Ònì ni] LT",nextDay:"[Ọ̀la ni] LT",nextWeek:"dddd [Ọsẹ̀ tón'bọ] [ni] LT",lastDay:"[Àna ni] LT",lastWeek:"dddd [Ọsẹ̀ tólọ́] [ni] LT",sameElse:"L"},relativeTime:{future:"ní %s",past:"%s kọjá",s:"ìsẹjú aayá die",ss:"aayá %d",m:"ìsẹjú kan",mm:"ìsẹjú %d",h:"wákati kan",hh:"wákati %d",d:"ọjọ́ kan",dd:"ọjọ́ %d",M:"osù kan",MM:"osù %d",y:"ọdún kan",yy:"ọdún %d"},dayOfMonthOrdinalParse:/ọjọ́\s\d{1,2}/,ordinal:"ọjọ́ %d",week:{dow:1,doy:4}})}(n(5093))},2264:function(e,t,n){!function(e){"use strict";var t={1:"၁",2:"၂",3:"၃",4:"၄",5:"၅",6:"၆",7:"၇",8:"၈",9:"၉",0:"၀"},n={"၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","၀":"0"};e.defineLocale("my",{months:"ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ".split("_"),monthsShort:"ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ".split("_"),weekdays:"တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ".split("_"),weekdaysShort:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),weekdaysMin:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ယနေ.] LT [မှာ]",nextDay:"[မနက်ဖြန်] LT [မှာ]",nextWeek:"dddd LT [မှာ]",lastDay:"[မနေ.က] LT [မှာ]",lastWeek:"[ပြီးခဲ့သော] dddd LT [မှာ]",sameElse:"L"},relativeTime:{future:"လာမည့် %s မှာ",past:"လွန်ခဲ့သော %s က",s:"စက္ကန်.အနည်းငယ်",ss:"%d စက္ကန့်",m:"တစ်မိနစ်",mm:"%d မိနစ်",h:"တစ်နာရီ",hh:"%d နာရီ",d:"တစ်ရက်",dd:"%d ရက်",M:"တစ်လ",MM:"%d လ",y:"တစ်နှစ်",yy:"%d နှစ်"},preparse:function(e){return e.replace(/[၁၂၃၄၅၆၇၈၉၀]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},week:{dow:1,doy:4}})}(n(5093))},2271:function(e,t,n){!function(e){"use strict";function t(e,t,n){return"m"===n?t?"минута":"минуту":e+" "+(i=+e,r={ss:t?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",ww:"неделя_недели_недель",MM:"месяц_месяца_месяцев",yy:"год_года_лет"}[n].split("_"),i%10==1&&i%100!=11?r[0]:i%10>=2&&i%10<=4&&(i%100<10||i%100>=20)?r[1]:r[2]);var i,r}var n=[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i];e.defineLocale("ru",{months:{format:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_"),standalone:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_")},monthsShort:{format:"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.".split("_"),standalone:"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.".split("_")},weekdays:{standalone:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),format:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?] ?dddd/},weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:n,longMonthsParse:n,shortMonthsParse:n,monthsRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsShortRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsStrictRegex:/^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,monthsShortStrictRegex:/^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., H:mm",LLLL:"dddd, D MMMM YYYY г., H:mm"},calendar:{sameDay:"[Сегодня, в] LT",nextDay:"[Завтра, в] LT",lastDay:"[Вчера, в] LT",nextWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В следующее] dddd, [в] LT";case 1:case 2:case 4:return"[В следующий] dddd, [в] LT";case 3:case 5:case 6:return"[В следующую] dddd, [в] LT"}},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd, [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd, [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd, [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",ss:t,m:t,mm:t,h:"час",hh:t,d:"день",dd:t,w:"неделя",ww:t,M:"месяц",MM:t,y:"год",yy:t},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e,t,n){return e<4?"ночи":e<12?"утра":e<17?"дня":"вечера"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:4}})}(n(5093))},2274:function(e,t,n){!function(e){"use strict";e.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"sø._ma._ti._on._to._fr._lø.".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",ss:"%d sekunder",m:"ett minutt",mm:"%d minutter",h:"én time",hh:"%d timer",d:"én dag",dd:"%d dager",w:"én uke",ww:"%d uker",M:"én måned",MM:"%d måneder",y:"ett år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},2297:function(e,t,n){!function(e){"use strict";e.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){return 12===e&&(e=0),"pagi"===t?e:"tengahari"===t?e>=11?e:e+12:"petang"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(5093))},2353:function(e,t,n){!function(e){"use strict";var t={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},n=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},i={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية"],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة"],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة"],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم"],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر"],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام"]},r=function(e){return function(t,r,a,s){var o=n(t),d=i[e][n(t)];return 2===o&&(d=d[r?0:1]),d.replace(/%d/i,t)}},a=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];e.defineLocale("ar-ly",{months:a,monthsShort:a,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:r("s"),ss:r("s"),m:r("m"),mm:r("m"),h:r("h"),hh:r("h"),d:r("d"),dd:r("d"),M:r("M"),MM:r("M"),y:r("y"),yy:r("y")},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]})).replace(/,/g,"،")},week:{dow:6,doy:12}})}(n(5093))},2475:function(e,t,n){!function(e){"use strict";e.defineLocale("uz-latn",{months:"Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr".split("_"),monthsShort:"Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek".split("_"),weekdays:"Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba".split("_"),weekdaysShort:"Yak_Dush_Sesh_Chor_Pay_Jum_Shan".split("_"),weekdaysMin:"Ya_Du_Se_Cho_Pa_Ju_Sha".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Bugun soat] LT [da]",nextDay:"[Ertaga] LT [da]",nextWeek:"dddd [kuni soat] LT [da]",lastDay:"[Kecha soat] LT [da]",lastWeek:"[O'tgan] dddd [kuni soat] LT [da]",sameElse:"L"},relativeTime:{future:"Yaqin %s ichida",past:"Bir necha %s oldin",s:"soniya",ss:"%d soniya",m:"bir daqiqa",mm:"%d daqiqa",h:"bir soat",hh:"%d soat",d:"bir kun",dd:"%d kun",M:"bir oy",MM:"%d oy",y:"bir yil",yy:"%d yil"},week:{dow:1,doy:7}})}(n(5093))},2477:function(e,t,n){!function(e){"use strict";e.defineLocale("fo",{months:"januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mán_týs_mik_hós_frí_ley".split("_"),weekdaysMin:"su_má_tý_mi_hó_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[Í dag kl.] LT",nextDay:"[Í morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Í gjár kl.] LT",lastWeek:"[síðstu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s síðani",s:"fá sekund",ss:"%d sekundir",m:"ein minuttur",mm:"%d minuttir",h:"ein tími",hh:"%d tímar",d:"ein dagur",dd:"%d dagar",M:"ein mánaður",MM:"%d mánaðir",y:"eitt ár",yy:"%d ár"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},2572:function(e,t,n){!function(e){"use strict";var t="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),n="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),i=[/^jan/i,/^feb/i,/^(maart|mrt\.?)$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;e.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,i){return e?/-MMM-/.test(i)?n[e.month()]:t[e.month()]:t},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",w:"één week",ww:"%d weken",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(5093))},2648:function(e,t,n){!function(e){"use strict";e.defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日Ah点mm分",LLLL:"YYYY年M月D日ddddAh点mm分",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"下午"===t||"晚上"===t?e+12:e>=11?e:e+12},meridiem:function(e,t,n){var i=100*e+t;return i<600?"凌晨":i<900?"早上":i<1130?"上午":i<1230?"中午":i<1800?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:function(e){return e.week()!==this.week()?"[下]dddLT":"[本]dddLT"},lastDay:"[昨天]LT",lastWeek:function(e){return this.week()!==e.week()?"[上]dddLT":"[本]dddLT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s后",past:"%s前",s:"几秒",ss:"%d 秒",m:"1 分钟",mm:"%d 分钟",h:"1 小时",hh:"%d 小时",d:"1 天",dd:"%d 天",w:"1 周",ww:"%d 周",M:"1 个月",MM:"%d 个月",y:"1 年",yy:"%d 年"},week:{dow:1,doy:4}})}(n(5093))},2682:function(e,t,n){!function(e){"use strict";var t={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};e.defineLocale("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,(function(e){return n[e]})).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]})).replace(/,/g,"،")},week:{dow:0,doy:6}})}(n(5093))},2934:function(e,t,n){!function(e){"use strict";e.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mart_apr_maj_jun_jul_aŭg_sept_okt_nov_dec".split("_"),weekdays:"dimanĉo_lundo_mardo_merkredo_ĵaŭdo_vendredo_sabato".split("_"),weekdaysShort:"dim_lun_mard_merk_ĵaŭ_ven_sab".split("_"),weekdaysMin:"di_lu_ma_me_ĵa_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"[la] D[-an de] MMMM, YYYY",LLL:"[la] D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd[n], [la] D[-an de] MMMM, YYYY HH:mm",llll:"ddd, [la] D[-an de] MMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(e){return"p"===e.charAt(0).toLowerCase()},meridiem:function(e,t,n){return e>11?n?"p.t.m.":"P.T.M.":n?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd[n je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasintan] dddd[n je] LT",sameElse:"L"},relativeTime:{future:"post %s",past:"antaŭ %s",s:"kelkaj sekundoj",ss:"%d sekundoj",m:"unu minuto",mm:"%d minutoj",h:"unu horo",hh:"%d horoj",d:"unu tago",dd:"%d tagoj",M:"unu monato",MM:"%d monatoj",y:"unu jaro",yy:"%d jaroj"},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}})}(n(5093))},3004:function(e,t,n){!function(e){"use strict";e.defineLocale("en-sg",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10;return e+(1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")},week:{dow:1,doy:4}})}(n(5093))},3035:function(e,t,n){!function(e){"use strict";function t(e,t,n,i){var r={s:["mõne sekundi","mõni sekund","paar sekundit"],ss:[e+"sekundi",e+"sekundit"],m:["ühe minuti","üks minut"],mm:[e+" minuti",e+" minutit"],h:["ühe tunni","tund aega","üks tund"],hh:[e+" tunni",e+" tundi"],d:["ühe päeva","üks päev"],M:["kuu aja","kuu aega","üks kuu"],MM:[e+" kuu",e+" kuud"],y:["ühe aasta","aasta","üks aasta"],yy:[e+" aasta",e+" aastat"]};return t?r[n][2]?r[n][2]:r[n][1]:i?r[n][0]:r[n][1]}e.defineLocale("et",{months:"jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[Täna,] LT",nextDay:"[Homme,] LT",nextWeek:"[Järgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pärast",past:"%s tagasi",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:"%d päeva",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},3164:function(e,t,n){!function(e){"use strict";e.defineLocale("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 A h:mm",LLLL:"YYYY년 MMMM D일 dddd A h:mm",l:"YYYY.MM.DD.",ll:"YYYY년 MMMM D일",lll:"YYYY년 MMMM D일 A h:mm",llll:"YYYY년 MMMM D일 dddd A h:mm"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇 초",ss:"%d초",m:"1분",mm:"%d분",h:"한 시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한 달",MM:"%d달",y:"일 년",yy:"%d년"},dayOfMonthOrdinalParse:/\d{1,2}(일|월|주)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"일";case"M":return e+"월";case"w":case"W":return e+"주";default:return e}},meridiemParse:/오전|오후/,isPM:function(e){return"오후"===e},meridiem:function(e,t,n){return e<12?"오전":"오후"}})}(n(5093))},3322:function(e,t,n){!function(e){"use strict";var t={words:{ss:["секунда","секунде","секунди"],m:["један минут","једног минута"],mm:["минут","минута","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],d:["један дан","једног дана"],dd:["дан","дана","дана"],M:["један месец","једног месеца"],MM:["месец","месеца","месеци"],y:["једну годину","једне године"],yy:["годину","године","година"]},correctGrammaticalCase:function(e,t){return e%10>=1&&e%10<=4&&(e%100<10||e%100>=20)?e%10==1?t[0]:t[1]:t[2]},translate:function(e,n,i,r){var a,s=t.words[i];return 1===i.length?"y"===i&&n?"једна година":r||n?s[0]:s[1]:(a=t.correctGrammaticalCase(e,s),"yy"===i&&n&&"годину"===a?e+" година":e+" "+a)}};e.defineLocale("sr-cyrl",{months:"јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар".split("_"),monthsShort:"јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.".split("_"),monthsParseExact:!0,weekdays:"недеља_понедељак_уторак_среда_четвртак_петак_субота".split("_"),weekdaysShort:"нед._пон._уто._сре._чет._пет._суб.".split("_"),weekdaysMin:"не_по_ут_ср_че_пе_су".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){return["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"][this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",ss:t.translate,m:t.translate,mm:t.translate,h:t.translate,hh:t.translate,d:t.translate,dd:t.translate,M:t.translate,MM:t.translate,y:t.translate,yy:t.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(5093))},3383:function(e,t,n){!function(e){"use strict";function t(e,t,n,i){var r={s:["thoddea sekondamni","thodde sekond"],ss:[e+" sekondamni",e+" sekond"],m:["eka mintan","ek minut"],mm:[e+" mintamni",e+" mintam"],h:["eka voran","ek vor"],hh:[e+" voramni",e+" voram"],d:["eka disan","ek dis"],dd:[e+" disamni",e+" dis"],M:["eka mhoinean","ek mhoino"],MM:[e+" mhoineamni",e+" mhoine"],y:["eka vorsan","ek voros"],yy:[e+" vorsamni",e+" vorsam"]};return i?r[n][0]:r[n][1]}e.defineLocale("gom-latn",{months:{standalone:"Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr".split("_"),format:"Janerachea_Febrerachea_Marsachea_Abrilachea_Maiachea_Junachea_Julaiachea_Agostachea_Setembrachea_Otubrachea_Novembrachea_Dezembrachea".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Aitar_Somar_Mongllar_Budhvar_Birestar_Sukrar_Son'var".split("_"),weekdaysShort:"Ait._Som._Mon._Bud._Bre._Suk._Son.".split("_"),weekdaysMin:"Ai_Sm_Mo_Bu_Br_Su_Sn".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [vazta]",LTS:"A h:mm:ss [vazta]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [vazta]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [vazta]",llll:"ddd, D MMM YYYY, A h:mm [vazta]"},calendar:{sameDay:"[Aiz] LT",nextDay:"[Faleam] LT",nextWeek:"[Fuddlo] dddd[,] LT",lastDay:"[Kal] LT",lastWeek:"[Fattlo] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s adim",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(er)/,ordinal:function(e,t){return"D"===t?e+"er":e},week:{dow:0,doy:3},meridiemParse:/rati|sokallim|donparam|sanje/,meridiemHour:function(e,t){return 12===e&&(e=0),"rati"===t?e<4?e:e+12:"sokallim"===t?e:"donparam"===t?e>12?e:e+12:"sanje"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"rati":e<12?"sokallim":e<16?"donparam":e<20?"sanje":"rati"}})}(n(5093))},3386:function(e,t,n){!function(e){"use strict";e.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){return 12===e&&(e=0),"pagi"===t?e:"tengahari"===t?e>=11?e:e+12:"petang"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})}(n(5093))},3478:function(e,t,n){!function(e){"use strict";e.defineLocale("se",{months:"ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov".split("_"),weekdays:"sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_láv".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mánnu",MM:"%d mánut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},3508:function(e,t,n){!function(e){"use strict";e.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:!0,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",ss:"%d segundo",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(5093))},3601:function(e,t,n){!function(e){"use strict";e.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"på dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[i] dddd[s kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",ss:"%d sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},3635:function(e,t,n){!function(e){"use strict";e.defineLocale("cv",{months:"кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав".split("_"),monthsShort:"кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кӗҫ_эрн_шӑм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кҫ_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]",LLL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm",LLLL:"dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ӗнер] LT [сехетре]",nextWeek:"[Ҫитес] dddd LT [сехетре]",lastWeek:"[Иртнӗ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(e){return e+(/сехет$/i.exec(e)?"рен":/ҫул$/i.exec(e)?"тан":"ран")},past:"%s каялла",s:"пӗр-ик ҫеккунт",ss:"%d ҫеккунт",m:"пӗр минут",mm:"%d минут",h:"пӗр сехет",hh:"%d сехет",d:"пӗр кун",dd:"%d кун",M:"пӗр уйӑх",MM:"%d уйӑх",y:"пӗр ҫул",yy:"%d ҫул"},dayOfMonthOrdinalParse:/\d{1,2}-мӗш/,ordinal:"%d-мӗш",week:{dow:1,doy:7}})}(n(5093))},3739:function(e,t,n){!function(e){"use strict";e.defineLocale("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),monthsParseExact:!0,weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",LTS:"A h:mm:ss -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -നു",LLLL:"dddd, D MMMM YYYY, A h:mm -നു"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",ss:"%d സെക്കൻഡ്",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiemParse:/രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,meridiemHour:function(e,t){return 12===e&&(e=0),"രാത്രി"===t&&e>=4||"ഉച്ച കഴിഞ്ഞ്"===t||"വൈകുന്നേരം"===t?e+12:e},meridiem:function(e,t,n){return e<4?"രാത്രി":e<12?"രാവിലെ":e<17?"ഉച്ച കഴിഞ്ഞ്":e<20?"വൈകുന്നേരം":"രാത്രി"}})}(n(5093))},3784:function(e,t,n){!function(e){"use strict";var t="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),n="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),i=[/^jan/i,/^feb/i,/^(maart|mrt\.?)$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],r=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;e.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,i){return e?/-MMM-/.test(i)?n[e.month()]:t[e.month()]:t},monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})}(n(5093))},3827:function(e,t,n){!function(e){"use strict";e.defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"㋿",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"平成",narrow:"㍻",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"昭和",narrow:"㍼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"大正",narrow:"㍽",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"明治",narrow:"㍾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"西暦",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"紀元前",narrow:"BC",abbr:"BC"}],eraYearOrdinalRegex:/(元|\d+)年/,eraYearOrdinalParse:function(e,t){return"元"===t[1]?1:parseInt(t[1]||e,10)},months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日 dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e,t,n){return e<12?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:function(e){return e.week()!==this.week()?"[来週]dddd LT":"dddd LT"},lastDay:"[昨日] LT",lastWeek:function(e){return this.week()!==e.week()?"[先週]dddd LT":"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}日/,ordinal:function(e,t){switch(t){case"y":return 1===e?"元年":e+"年";case"d":case"D":case"DDD":return e+"日";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"数秒",ss:"%d秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}})}(n(5093))},3861:function(e,t,n){!function(e){"use strict";var t={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},n={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},i=[/^जन/i,/^फ़र|फर/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सितं|सित/i,/^अक्टू/i,/^नव|नवं/i,/^दिसं|दिस/i];e.defineLocale("hi",{months:{format:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),standalone:"जनवरी_फरवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितंबर_अक्टूबर_नवंबर_दिसंबर".split("_")},monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm बजे",LLLL:"dddd, D MMMM YYYY, A h:mm बजे"},monthsParse:i,longMonthsParse:i,shortMonthsParse:[/^जन/i,/^फ़र/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सित/i,/^अक्टू/i,/^नव/i,/^दिस/i],monthsRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsShortRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsStrictRegex:/^(जनवरी?|फ़रवरी|फरवरी?|मार्च?|अप्रैल?|मई?|जून?|जुलाई?|अगस्त?|सितम्बर|सितंबर|सित?\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर?|दिसम्बर|दिसंबर?)/i,monthsShortStrictRegex:/^(जन\.?|फ़र\.?|मार्च?|अप्रै\.?|मई?|जून?|जुल\.?|अग\.?|सित\.?|अक्टू\.?|नव\.?|दिस\.?)/i,calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",ss:"%d सेकंड",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},meridiemParse:/रात|सुबह|दोपहर|शाम/,meridiemHour:function(e,t){return 12===e&&(e=0),"रात"===t?e<4?e:e+12:"सुबह"===t?e:"दोपहर"===t?e>=10?e:e+12:"शाम"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"रात":e<10?"सुबह":e<17?"दोपहर":e<20?"शाम":"रात"},week:{dow:0,doy:6}})}(n(5093))},3872:function(e,t,n){!function(e){"use strict";e.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10;return e+(1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")},week:{dow:0,doy:4}})}(n(5093))},4051:function(e,t,n){!function(e){"use strict";e.defineLocale("x-pseudo",{months:"J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér".split("_"),monthsShort:"J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc".split("_"),monthsParseExact:!0,weekdays:"S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý".split("_"),weekdaysShort:"S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát".split("_"),weekdaysMin:"S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~ódá~ý át] LT",nextDay:"[T~ómó~rró~w át] LT",nextWeek:"dddd [át] LT",lastDay:"[Ý~ést~érdá~ý át] LT",lastWeek:"[L~ást] dddd [át] LT",sameElse:"L"},relativeTime:{future:"í~ñ %s",past:"%s á~gó",s:"á ~féw ~sécó~ñds",ss:"%d s~écóñ~ds",m:"á ~míñ~úté",mm:"%d m~íñú~tés",h:"á~ñ hó~úr",hh:"%d h~óúrs",d:"á ~dáý",dd:"%d d~áýs",M:"á ~móñ~th",MM:"%d m~óñt~hs",y:"á ~ýéár",yy:"%d ý~éárs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")},week:{dow:1,doy:4}})}(n(5093))},4063:function(e,t,n){!function(e){"use strict";e.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,t){return 12===e&&(e=0),"pagi"===t?e:"siang"===t?e>=11?e:e+12:"sore"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"siang":e<19?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",ss:"%d detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:0,doy:6}})}(n(5093))},4226:function(e,t,n){!function(e){"use strict";e.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn ôl",s:"ychydig eiliadau",ss:"%d eiliad",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},dayOfMonthOrdinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(e){var t="";return e>20?t=40===e||50===e||60===e||80===e||100===e?"fed":"ain":e>0&&(t=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"][e]),e+t},week:{dow:1,doy:4}})}(n(5093))},4418:function(e,t,n){!function(e){"use strict";var t="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),n="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),i=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paź/i,/^lis/i,/^gru/i];function r(e){return e%10<5&&e%10>1&&~~(e/10)%10!=1}function a(e,t,n){var i=e+" ";switch(n){case"ss":return i+(r(e)?"sekundy":"sekund");case"m":return t?"minuta":"minutę";case"mm":return i+(r(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return i+(r(e)?"godziny":"godzin");case"ww":return i+(r(e)?"tygodnie":"tygodni");case"MM":return i+(r(e)?"miesiące":"miesięcy");case"yy":return i+(r(e)?"lata":"lat")}}e.defineLocale("pl",{months:function(e,i){return e?/D MMMM/.test(i)?n[e.month()]:t[e.month()]:t},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:a,m:a,mm:a,h:a,hh:a,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:a,M:"miesiąc",MM:a,y:"rok",yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},4429:function(e,t,n){!function(e){"use strict";function t(e,t,n){var i=e+" ";switch(n){case"ss":return i+(1===e?"sekunda":2===e||3===e||4===e?"sekunde":"sekundi");case"mm":return i+(1===e?"minuta":2===e||3===e||4===e?"minute":"minuta");case"h":return"jedan sat";case"hh":return i+(1===e?"sat":2===e||3===e||4===e?"sata":"sati");case"dd":return i+(1===e?"dan":"dana");case"MM":return i+(1===e?"mjesec":2===e||3===e||4===e?"mjeseca":"mjeseci");case"yy":return i+(1===e?"godina":2===e||3===e||4===e?"godine":"godina")}}e.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:function(e,t,n,i){if("m"===n)return t?"jedna minuta":i?"jednu minutu":"jedne minute"},mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})}(n(5093))},4451:function(e,t,n){!function(e){"use strict";var t={1:"೧",2:"೨",3:"೩",4:"೪",5:"೫",6:"೬",7:"೭",8:"೮",9:"೯",0:"೦"},n={"೧":"1","೨":"2","೩":"3","೪":"4","೫":"5","೬":"6","೭":"7","೮":"8","೯":"9","೦":"0"};e.defineLocale("kn",{months:"ಜನವರಿ_ಫೆಬ್ರವರಿ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬರ್_ಅಕ್ಟೋಬರ್_ನವೆಂಬರ್_ಡಿಸೆಂಬರ್".split("_"),monthsShort:"ಜನ_ಫೆಬ್ರ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂ_ಅಕ್ಟೋ_ನವೆಂ_ಡಿಸೆಂ".split("_"),monthsParseExact:!0,weekdays:"ಭಾನುವಾರ_ಸೋಮವಾರ_ಮಂಗಳವಾರ_ಬುಧವಾರ_ಗುರುವಾರ_ಶುಕ್ರವಾರ_ಶನಿವಾರ".split("_"),weekdaysShort:"ಭಾನು_ಸೋಮ_ಮಂಗಳ_ಬುಧ_ಗುರು_ಶುಕ್ರ_ಶನಿ".split("_"),weekdaysMin:"ಭಾ_ಸೋ_ಮಂ_ಬು_ಗು_ಶು_ಶ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ಇಂದು] LT",nextDay:"[ನಾಳೆ] LT",nextWeek:"dddd, LT",lastDay:"[ನಿನ್ನೆ] LT",lastWeek:"[ಕೊನೆಯ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ನಂತರ",past:"%s ಹಿಂದೆ",s:"ಕೆಲವು ಕ್ಷಣಗಳು",ss:"%d ಸೆಕೆಂಡುಗಳು",m:"ಒಂದು ನಿಮಿಷ",mm:"%d ನಿಮಿಷ",h:"ಒಂದು ಗಂಟೆ",hh:"%d ಗಂಟೆ",d:"ಒಂದು ದಿನ",dd:"%d ದಿನ",M:"ಒಂದು ತಿಂಗಳು",MM:"%d ತಿಂಗಳು",y:"ಒಂದು ವರ್ಷ",yy:"%d ವರ್ಷ"},preparse:function(e){return e.replace(/[೧೨೩೪೫೬೭೮೯೦]/g,(function(e){return n[e]}))},postformat:function(e){return e.replace(/\d/g,(function(e){return t[e]}))},meridiemParse:/ರಾತ್ರಿ|ಬೆಳಿಗ್ಗೆ|ಮಧ್ಯಾಹ್ನ|ಸಂಜೆ/,meridiemHour:function(e,t){return 12===e&&(e=0),"ರಾತ್ರಿ"===t?e<4?e:e+12:"ಬೆಳಿಗ್ಗೆ"===t?e:"ಮಧ್ಯಾಹ್ನ"===t?e>=10?e:e+12:"ಸಂಜೆ"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"ರಾತ್ರಿ":e<10?"ಬೆಳಿಗ್ಗೆ":e<17?"ಮಧ್ಯಾಹ್ನ":e<20?"ಸಂಜೆ":"ರಾತ್ರಿ"},dayOfMonthOrdinalParse:/\d{1,2}(ನೇ)/,ordinal:function(e){return e+"ನೇ"},week:{dow:0,doy:6}})}(n(5093))},4457:function(e,t,n){!function(e){"use strict";function t(e,t,n){var i=" ";return(e%100>=20||e>=100&&e%100==0)&&(i=" de "),e+i+{ss:"secunde",mm:"minute",hh:"ore",dd:"zile",ww:"săptămâni",MM:"luni",yy:"ani"}[n]}e.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._feb._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",ss:t,m:"un minut",mm:t,h:"o oră",hh:t,d:"o zi",dd:t,w:"o săptămână",ww:t,M:"o lună",MM:t,y:"un an",yy:t},week:{dow:1,doy:7}})}(n(5093))},4496:function(e,t,n){!function(e){"use strict";e.defineLocale("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}})}(n(5093))},4566:function(e,t,n){!function(e){"use strict";e.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"su._må._ty._on._to._fr._lau.".split("_"),weekdaysMin:"su_må_ty_on_to_fr_la".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I går klokka] LT",lastWeek:"[Føregåande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",ss:"%d sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",w:"ei veke",ww:"%d veker",M:"ein månad",MM:"%d månader",y:"eit år",yy:"%d år"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(n(5093))},4691:function(e,t,n){!function(e){"use strict";e.defineLocale("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(e,t){return e?"string"==typeof t&&/D/.test(t.substring(0,t.indexOf("MMMM")))?this._monthsGenitiveEl[e.month()]:this._monthsNominativeEl[e.month()]:this._monthsNominativeEl},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(e,t,n){return e>11?n?"μμ":"ΜΜ":n?"πμ":"ΠΜ"},isPM:function(e){return"μ"===(e+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){return 6===this.day()?"[το προηγούμενο] dddd [{}] LT":"[την προηγούμενη] dddd [{}] LT"},sameElse:"L"},calendar:function(e,t){var n,i=this._calendarEl[e],r=t&&t.hours();return n=i,("undefined"!=typeof Function&&n instanceof Function||"[object Function]"===Object.prototype.toString.call(n))&&(i=i.apply(t)),i.replace("{}",r%12==1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",ss:"%d δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},dayOfMonthOrdinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}})}(n(5093))},4692:function(e,t){var n;!function(t,n){"use strict";"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}("undefined"!=typeof window?window:this,(function(i,r){"use strict";var a=[],s=Object.getPrototypeOf,o=a.slice,d=a.flat?function(e){return a.flat.call(e)}:function(e){return a.concat.apply([],e)},l=a.push,u=a.indexOf,c={},h=c.toString,f=c.hasOwnProperty,m=f.toString,_=m.call(Object),p={},g=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},v=i.document,M={type:!0,src:!0,nonce:!0,noModule:!0};function L(e,t,n){var i,r,a=(n=n||v).createElement("script");if(a.text=e,t)for(i in M)(r=t[i]||t.getAttribute&&t.getAttribute(i))&&a.setAttribute(i,r);n.head.appendChild(a).parentNode.removeChild(a)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[h.call(e)]||"object":typeof e}var b="3.7.1",k=/HTML$/i,w=function(e,t){return new w.fn.init(e,t)};function D(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!g(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function S(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}w.fn=w.prototype={jquery:b,constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(w.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(w.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+A+")"+A+"*"),U=new RegExp(A+"|>"),B=new RegExp(j),W=new RegExp("^"+C+"$"),z={ID:new RegExp("^#("+C+")"),CLASS:new RegExp("^\\.("+C+")"),TAG:new RegExp("^("+C+"|[*])"),ATTR:new RegExp("^"+R),PSEUDO:new RegExp("^"+j),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+A+"*(even|odd|(([+-]|)(\\d*)n|)"+A+"*(?:([+-]|)"+A+"*(\\d+)|))"+A+"*\\)|)","i"),bool:new RegExp("^(?:"+D+")$","i"),needsContext:new RegExp("^"+A+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+A+"*((?:-\\d)?\\d*)"+A+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,V=/^h\d$/i,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,q=/[+~]/,$=new RegExp("\\\\[\\da-fA-F]{1,6}"+A+"?|\\\\([^\\r\\n\\f])","g"),J=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},X=function(){de()},Q=he((function(e){return!0===e.disabled&&S(e,"fieldset")}),{dir:"parentNode",next:"legend"});try{_.apply(a=o.call(H.childNodes),H.childNodes),a[H.childNodes.length].nodeType}catch(e){_={apply:function(e,t){O.apply(e,o.call(t))},call:function(e){O.apply(e,o.call(arguments,1))}}}function Z(e,t,n,i){var r,a,s,o,l,u,f,m=t&&t.ownerDocument,y=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==y&&9!==y&&11!==y)return n;if(!i&&(de(t),t=t||d,c)){if(11!==y&&(l=K.exec(e)))if(r=l[1]){if(9===y){if(!(s=t.getElementById(r)))return n;if(s.id===r)return _.call(n,s),n}else if(m&&(s=m.getElementById(r))&&Z.contains(t,s)&&s.id===r)return _.call(n,s),n}else{if(l[2])return _.apply(n,t.getElementsByTagName(e)),n;if((r=l[3])&&t.getElementsByClassName)return _.apply(n,t.getElementsByClassName(r)),n}if(!(b[e+" "]||h&&h.test(e))){if(f=e,m=t,1===y&&(U.test(e)||N.test(e))){for((m=q.test(e)&&oe(t.parentNode)||t)==t&&p.scope||((o=t.getAttribute("id"))?o=w.escapeSelector(o):t.setAttribute("id",o=g)),a=(u=ue(e)).length;a--;)u[a]=(o?"#"+o:":scope")+" "+ce(u[a]);f=u.join(",")}try{return _.apply(n,m.querySelectorAll(f)),n}catch(t){b(e,!0)}finally{o===g&&t.removeAttribute("id")}}}return ye(e.replace(P,"$1"),t,n,i)}function ee(){var e=[];return function n(i,r){return e.push(i+" ")>t.cacheLength&&delete n[e.shift()],n[i+" "]=r}}function te(e){return e[g]=!0,e}function ne(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ie(e){return function(t){return S(t,"input")&&t.type===e}}function re(e){return function(t){return(S(t,"input")||S(t,"button"))&&t.type===e}}function ae(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Q(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function se(e){return te((function(t){return t=+t,te((function(n,i){for(var r,a=e([],n.length,t),s=a.length;s--;)n[r=a[s]]&&(n[r]=!(i[r]=n[r]))}))}))}function oe(e){return e&&void 0!==e.getElementsByTagName&&e}function de(e){var n,i=e?e.ownerDocument||e:H;return i!=d&&9===i.nodeType&&i.documentElement?(l=(d=i).documentElement,c=!w.isXMLDoc(d),m=l.matches||l.webkitMatchesSelector||l.msMatchesSelector,l.msMatchesSelector&&H!=d&&(n=d.defaultView)&&n.top!==n&&n.addEventListener("unload",X),p.getById=ne((function(e){return l.appendChild(e).id=w.expando,!d.getElementsByName||!d.getElementsByName(w.expando).length})),p.disconnectedMatch=ne((function(e){return m.call(e,"*")})),p.scope=ne((function(){return d.querySelectorAll(":scope")})),p.cssHas=ne((function(){try{return d.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}})),p.getById?(t.filter.ID=function(e){var t=e.replace($,J);return function(e){return e.getAttribute("id")===t}},t.find.ID=function(e,t){if(void 0!==t.getElementById&&c){var n=t.getElementById(e);return n?[n]:[]}}):(t.filter.ID=function(e){var t=e.replace($,J);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},t.find.ID=function(e,t){if(void 0!==t.getElementById&&c){var n,i,r,a=t.getElementById(e);if(a){if((n=a.getAttributeNode("id"))&&n.value===e)return[a];for(r=t.getElementsByName(e),i=0;a=r[i++];)if((n=a.getAttributeNode("id"))&&n.value===e)return[a]}return[]}}),t.find.TAG=function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},t.find.CLASS=function(e,t){if(void 0!==t.getElementsByClassName&&c)return t.getElementsByClassName(e)},h=[],ne((function(e){var t;l.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+A+"*(?:value|"+D+")"),e.querySelectorAll("[id~="+g+"-]").length||h.push("~="),e.querySelectorAll("a#"+g+"+*").length||h.push(".#.+[+~]"),e.querySelectorAll(":checked").length||h.push(":checked"),(t=d.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),l.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&h.push(":enabled",":disabled"),(t=d.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||h.push("\\["+A+"*name"+A+"*="+A+"*(?:''|\"\")")})),p.cssHas||h.push(":has"),h=h.length&&new RegExp(h.join("|")),k=function(e,t){if(e===t)return s=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e===d||e.ownerDocument==H&&Z.contains(H,e)?-1:t===d||t.ownerDocument==H&&Z.contains(H,t)?1:r?u.call(r,e)-u.call(r,t):0:4&n?-1:1)},d):d}for(e in Z.matches=function(e,t){return Z(e,null,null,t)},Z.matchesSelector=function(e,t){if(de(e),c&&!b[t+" "]&&(!h||!h.test(t)))try{var n=m.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){b(t,!0)}return Z(t,d,null,[e]).length>0},Z.contains=function(e,t){return(e.ownerDocument||e)!=d&&de(e),w.contains(e,t)},Z.attr=function(e,n){(e.ownerDocument||e)!=d&&de(e);var i=t.attrHandle[n.toLowerCase()],r=i&&f.call(t.attrHandle,n.toLowerCase())?i(e,n,!c):void 0;return void 0!==r?r:e.getAttribute(n)},Z.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},w.uniqueSort=function(e){var t,n=[],i=0,a=0;if(s=!p.sortStable,r=!p.sortStable&&o.call(e,0),E.call(e,k),s){for(;t=e[a++];)t===e[a]&&(i=n.push(a));for(;i--;)x.call(e,n[i],1)}return r=null,e},w.fn.uniqueSort=function(){return this.pushStack(w.uniqueSort(o.apply(this)))},t=w.expr={cacheLength:50,createPseudo:te,match:z,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,J),e[3]=(e[3]||e[4]||e[5]||"").replace($,J),"~="===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]||Z.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]&&Z.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&B.test(n)&&(t=ue(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){var t=e.replace($,J).toLowerCase();return"*"===e?function(){return!0}:function(e){return S(e,t)}},CLASS:function(e){var t=M[e+" "];return t||(t=new RegExp("(^|"+A+")"+e+"("+A+"|$)"))&&M(e,(function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")}))},ATTR:function(e,t,n){return function(i){var r=Z.attr(i,e);return null==r?"!="===t:!t||(r+="","="===t?r===n:"!="===t?r!==n:"^="===t?n&&0===r.indexOf(n):"*="===t?n&&r.indexOf(n)>-1:"$="===t?n&&r.slice(-n.length)===n:"~="===t?(" "+r.replace(F," ")+" ").indexOf(n)>-1:"|="===t&&(r===n||r.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,i,r){var a="nth"!==e.slice(0,3),s="last"!==e.slice(-4),o="of-type"===t;return 1===i&&0===r?function(e){return!!e.parentNode}:function(t,n,d){var l,u,c,h,f,m=a!==s?"nextSibling":"previousSibling",_=t.parentNode,p=o&&t.nodeName.toLowerCase(),v=!d&&!o,M=!1;if(_){if(a){for(;m;){for(c=t;c=c[m];)if(o?S(c,p):1===c.nodeType)return!1;f=m="only"===e&&!f&&"nextSibling"}return!0}if(f=[s?_.firstChild:_.lastChild],s&&v){for(M=(h=(l=(u=_[g]||(_[g]={}))[e]||[])[0]===y&&l[1])&&l[2],c=h&&_.childNodes[h];c=++h&&c&&c[m]||(M=h=0)||f.pop();)if(1===c.nodeType&&++M&&c===t){u[e]=[y,h,M];break}}else if(v&&(M=h=(l=(u=t[g]||(t[g]={}))[e]||[])[0]===y&&l[1]),!1===M)for(;(c=++h&&c&&c[m]||(M=h=0)||f.pop())&&(!(o?S(c,p):1===c.nodeType)||!++M||(v&&((u=c[g]||(c[g]={}))[e]=[y,M]),c!==t)););return(M-=r)===i||M%i===0&&M/i>=0}}},PSEUDO:function(e,n){var i,r=t.pseudos[e]||t.setFilters[e.toLowerCase()]||Z.error("unsupported pseudo: "+e);return r[g]?r(n):r.length>1?(i=[e,e,"",n],t.setFilters.hasOwnProperty(e.toLowerCase())?te((function(e,t){for(var i,a=r(e,n),s=a.length;s--;)e[i=u.call(e,a[s])]=!(t[i]=a[s])})):function(e){return r(e,0,i)}):r}},pseudos:{not:te((function(e){var t=[],n=[],i=ge(e.replace(P,"$1"));return i[g]?te((function(e,t,n,r){for(var a,s=i(e,null,r,[]),o=e.length;o--;)(a=s[o])&&(e[o]=!(t[o]=a))})):function(e,r,a){return t[0]=e,i(t,null,a,n),t[0]=null,!n.pop()}})),has:te((function(e){return function(t){return Z(e,t).length>0}})),contains:te((function(e){return e=e.replace($,J),function(t){return(t.textContent||w.text(t)).indexOf(e)>-1}})),lang:te((function(e){return W.test(e||"")||Z.error("unsupported lang: "+e),e=e.replace($,J).toLowerCase(),function(t){var n;do{if(n=c?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(e){var t=i.location&&i.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===l},focus:function(e){return e===function(){try{return d.activeElement}catch(e){}}()&&d.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:ae(!1),disabled:ae(!0),checked:function(e){return S(e,"input")&&!!e.checked||S(e,"option")&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!t.pseudos.empty(e)},header:function(e){return V.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){return S(e,"input")&&"button"===e.type||S(e,"button")},text:function(e){var t;return S(e,"input")&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:se((function(){return[0]})),last:se((function(e,t){return[t-1]})),eq:se((function(e,t,n){return[n<0?n+t:n]})),even:se((function(e,t){for(var n=0;nt?t:n;--i>=0;)e.push(i);return e})),gt:se((function(e,t,n){for(var i=n<0?n+t:n;++i1?function(t,n,i){for(var r=e.length;r--;)if(!e[r](t,n,i))return!1;return!0}:e[0]}function me(e,t,n,i,r){for(var a,s=[],o=0,d=e.length,l=null!=t;o-1&&(a[l]=!(s[l]=h))}}else f=me(f===s?f.splice(g,f.length):f),r?r(null,s,f,d):_.apply(s,f)}))}function pe(e){for(var i,r,a,s=e.length,o=t.relative[e[0].type],d=o||t.relative[" "],l=o?1:0,c=he((function(e){return e===i}),d,!0),h=he((function(e){return u.call(i,e)>-1}),d,!0),f=[function(e,t,r){var a=!o&&(r||t!=n)||((i=t).nodeType?c(e,t,r):h(e,t,r));return i=null,a}];l1&&fe(f),l>1&&ce(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(P,"$1"),r,l0,a=e.length>0,s=function(s,o,l,u,h){var f,m,p,g=0,v="0",M=s&&[],L=[],T=n,b=s||a&&t.find.TAG("*",h),k=y+=null==T?1:Math.random()||.1,D=b.length;for(h&&(n=o==d||o||h);v!==D&&null!=(f=b[v]);v++){if(a&&f){for(m=0,o||f.ownerDocument==d||(de(f),l=!c);p=e[m++];)if(p(f,o||d,l)){_.call(u,f);break}h&&(y=k)}r&&((f=!p&&f)&&g--,s&&M.push(f))}if(g+=v,r&&v!==g){for(m=0;p=i[m++];)p(M,L,o,l);if(s){if(g>0)for(;v--;)M[v]||L[v]||(L[v]=Y.call(u));L=me(L)}_.apply(u,L),h&&!s&&L.length>0&&g+i.length>1&&w.uniqueSort(u)}return h&&(y=k,n=T),M};return r?te(s):s}(s,a)),o.selector=e}return o}function ye(e,n,i,r){var a,s,o,d,l,u="function"==typeof e&&e,h=!r&&ue(e=u.selector||e);if(i=i||[],1===h.length){if((s=h[0]=h[0].slice(0)).length>2&&"ID"===(o=s[0]).type&&9===n.nodeType&&c&&t.relative[s[1].type]){if(!(n=(t.find.ID(o.matches[0].replace($,J),n)||[])[0]))return i;u&&(n=n.parentNode),e=e.slice(s.shift().value.length)}for(a=z.needsContext.test(e)?0:s.length;a--&&(o=s[a],!t.relative[d=o.type]);)if((l=t.find[d])&&(r=l(o.matches[0].replace($,J),q.test(s[0].type)&&oe(n.parentNode)||n))){if(s.splice(a,1),!(e=r.length&&ce(s)))return _.apply(i,r),i;break}}return(u||ge(e,h))(r,n,!c,i,!n||q.test(e)&&oe(n.parentNode)||n),i}le.prototype=t.filters=t.pseudos,t.setFilters=new le,p.sortStable=g.split("").sort(k).join("")===g,de(),p.sortDetached=ne((function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))})),w.find=Z,w.expr[":"]=w.expr.pseudos,w.unique=w.uniqueSort,Z.compile=ge,Z.select=ye,Z.setDocument=de,Z.tokenize=ue,Z.escape=w.escapeSelector,Z.getText=w.text,Z.isXML=w.isXMLDoc,Z.selectors=w.expr,Z.support=w.support,Z.uniqueSort=w.uniqueSort}();var j=function(e,t,n){for(var i=[],r=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(r&&w(e).is(n))break;i.push(e)}return i},F=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},I=w.expr.match.needsContext,N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function U(e,t,n){return g(t)?w.grep(e,(function(e,i){return!!t.call(e,i,e)!==n})):t.nodeType?w.grep(e,(function(e){return e===t!==n})):"string"!=typeof t?w.grep(e,(function(e){return u.call(t,e)>-1!==n})):w.filter(t,e,n)}w.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?w.find.matchesSelector(i,e)?[i]:[]:w.find.matches(e,w.grep(t,(function(e){return 1===e.nodeType})))},w.fn.extend({find:function(e){var t,n,i=this.length,r=this;if("string"!=typeof e)return this.pushStack(w(e).filter((function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(U(this,e||[],!1))},not:function(e){return this.pushStack(U(this,e||[],!0))},is:function(e){return!!U(this,"string"==typeof e&&I.test(e)?w(e):e||[],!1).length}});var B,W=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||B,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:W.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:v,!0)),N.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(r=v.getElementById(i[2]))&&(this[0]=r,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,B=w(v);var z=/^(?:parents|prev(?:Until|All))/,G={children:!0,contents:!0,next:!0,prev:!0};function V(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter((function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){a.push(n);break}return this.pushStack(a.length>1?w.uniqueSort(a):a)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return j(e,"parentNode")},parentsUntil:function(e,t,n){return j(e,"parentNode",n)},next:function(e){return V(e,"nextSibling")},prev:function(e){return V(e,"previousSibling")},nextAll:function(e){return j(e,"nextSibling")},prevAll:function(e){return j(e,"previousSibling")},nextUntil:function(e,t,n){return j(e,"nextSibling",n)},prevUntil:function(e,t,n){return j(e,"previousSibling",n)},siblings:function(e){return F((e.parentNode||{}).firstChild,e)},children:function(e){return F(e.firstChild)},contents:function(e){return null!=e.contentDocument&&s(e.contentDocument)?e.contentDocument:(S(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},(function(e,t){w.fn[e]=function(n,i){var r=w.map(this,t,n);return"Until"!==e.slice(-5)&&(i=n),i&&"string"==typeof i&&(r=w.filter(i,r)),this.length>1&&(G[e]||w.uniqueSort(r),z.test(e)&&r.reverse()),this.pushStack(r)}}));var K=/[^\x20\t\r\n\f]+/g;function q(e){return e}function $(e){throw e}function J(e,t,n,i){var r;try{e&&g(r=e.promise)?r.call(e).done(t).fail(n):e&&g(r=e.then)?r.call(e,t,n):t.apply(void 0,[e].slice(i))}catch(e){n.apply(void 0,[e])}}w.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return w.each(e.match(K)||[],(function(e,n){t[n]=!0})),t}(e):w.extend({},e);var t,n,i,r,a=[],s=[],o=-1,d=function(){for(r=r||e.once,i=t=!0;s.length;o=-1)for(n=s.shift();++o-1;)a.splice(n,1),n<=o&&o--})),this},has:function(e){return e?w.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return r=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return r=s=[],n||t||(a=n=""),this},locked:function(){return!!r},fireWith:function(e,n){return r||(n=[e,(n=n||[]).slice?n.slice():n],s.push(n),t||d()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!i}};return l},w.extend({Deferred:function(e){var t=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],n="pending",r={state:function(){return n},always:function(){return a.done(arguments).fail(arguments),this},catch:function(e){return r.then(null,e)},pipe:function(){var e=arguments;return w.Deferred((function(n){w.each(t,(function(t,i){var r=g(e[i[4]])&&e[i[4]];a[i[1]]((function(){var e=r&&r.apply(this,arguments);e&&g(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[i[0]+"With"](this,r?[e]:arguments)}))})),e=null})).promise()},then:function(e,n,r){var a=0;function s(e,t,n,r){return function(){var o=this,d=arguments,l=function(){var i,l;if(!(e=a&&(n!==$&&(o=void 0,d=[i]),t.rejectWith(o,d))}};e?u():(w.Deferred.getErrorHook?u.error=w.Deferred.getErrorHook():w.Deferred.getStackHook&&(u.error=w.Deferred.getStackHook()),i.setTimeout(u))}}return w.Deferred((function(i){t[0][3].add(s(0,i,g(r)?r:q,i.notifyWith)),t[1][3].add(s(0,i,g(e)?e:q)),t[2][3].add(s(0,i,g(n)?n:$))})).promise()},promise:function(e){return null!=e?w.extend(e,r):r}},a={};return w.each(t,(function(e,i){var s=i[2],o=i[5];r[i[1]]=s.add,o&&s.add((function(){n=o}),t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),s.add(i[3].fire),a[i[0]]=function(){return a[i[0]+"With"](this===a?void 0:this,arguments),this},a[i[0]+"With"]=s.fireWith})),r.promise(a),e&&e.call(a,a),a},when:function(e){var t=arguments.length,n=t,i=Array(n),r=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){i[e]=this,r[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(i,r)}};if(t<=1&&(J(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(r[n]&&r[n].then)))return a.then();for(;n--;)J(r[n],s(n),a.reject);return a.promise()}});var X=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(e,t){i.console&&i.console.warn&&e&&X.test(e.name)&&i.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},w.readyException=function(e){i.setTimeout((function(){throw e}))};var Q=w.Deferred();function Z(){v.removeEventListener("DOMContentLoaded",Z),i.removeEventListener("load",Z),w.ready()}w.fn.ready=function(e){return Q.then(e).catch((function(e){w.readyException(e)})),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||Q.resolveWith(v,[w]))}}),w.ready.then=Q.then,"complete"===v.readyState||"loading"!==v.readyState&&!v.documentElement.doScroll?i.setTimeout(w.ready):(v.addEventListener("DOMContentLoaded",Z),i.addEventListener("load",Z));var ee=function(e,t,n,i,r,a,s){var o=0,d=e.length,l=null==n;if("object"===T(n))for(o in r=!0,n)ee(e,t,o,n[o],!0,a,s);else if(void 0!==i&&(r=!0,g(i)||(s=!0),l&&(s?(t.call(e,i),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;o1,null,!0)},removeData:function(e){return this.each((function(){de.remove(this,e)}))}}),w.extend({queue:function(e,t,n){var i;if(e)return t=(t||"fx")+"queue",i=oe.get(e,t),n&&(!i||Array.isArray(n)?i=oe.access(e,t,w.makeArray(n)):i.push(n)),i||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),i=n.length,r=n.shift(),a=w._queueHooks(e,t);"inprogress"===r&&(r=n.shift(),i--),r&&("fx"===t&&n.unshift("inprogress"),delete a.stop,r.call(e,(function(){w.dequeue(e,t)}),a)),!i&&a&&a.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return oe.get(e,n)||oe.access(e,n,{empty:w.Callbacks("once memory").add((function(){oe.remove(e,[t+"queue",n])}))})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,Se=/^$|^module$|\/(?:java|ecma)script/i;be=v.createDocumentFragment().appendChild(v.createElement("div")),(ke=v.createElement("input")).setAttribute("type","radio"),ke.setAttribute("checked","checked"),ke.setAttribute("name","t"),be.appendChild(ke),p.checkClone=be.cloneNode(!0).cloneNode(!0).lastChild.checked,be.innerHTML="",p.noCloneChecked=!!be.cloneNode(!0).lastChild.defaultValue,be.innerHTML="",p.option=!!be.lastChild;var Ye={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Ee(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?w.merge([e],n):n}function xe(e,t){for(var n=0,i=e.length;n",""]);var Ae=/<|&#?\w+;/;function Pe(e,t,n,i,r){for(var a,s,o,d,l,u,c=t.createDocumentFragment(),h=[],f=0,m=e.length;f-1)r&&r.push(a);else if(l=pe(a),s=Ee(c.appendChild(a),"script"),l&&xe(s),n)for(u=0;a=s[u++];)Se.test(a.type||"")&&n.push(a);return c}var Ce=/^([^.]*)(?:\.(.+)|)/;function Re(){return!0}function He(){return!1}function Oe(e,t,n,i,r,a){var s,o;if("object"==typeof t){for(o in"string"!=typeof n&&(i=i||n,n=void 0),t)Oe(e,o,n,i,t[o],a);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),!1===r)r=He;else if(!r)return e;return 1===a&&(s=r,r=function(e){return w().off(e),s.apply(this,arguments)},r.guid=s.guid||(s.guid=w.guid++)),e.each((function(){w.event.add(this,t,r,i,n)}))}function je(e,t,n){n?(oe.set(e,t,!1),w.event.add(e,t,{namespace:!1,handler:function(e){var n,i=oe.get(this,t);if(1&e.isTrigger&&this[t]){if(i)(w.event.special[t]||{}).delegateType&&e.stopPropagation();else if(i=o.call(arguments),oe.set(this,t,i),this[t](),n=oe.get(this,t),oe.set(this,t,!1),i!==n)return e.stopImmediatePropagation(),e.preventDefault(),n}else i&&(oe.set(this,t,w.event.trigger(i[0],i.slice(1),this)),e.stopPropagation(),e.isImmediatePropagationStopped=Re)}})):void 0===oe.get(e,t)&&w.event.add(e,t,Re)}w.event={global:{},add:function(e,t,n,i,r){var a,s,o,d,l,u,c,h,f,m,_,p=oe.get(e);if(ae(e))for(n.handler&&(n=(a=n).handler,r=a.selector),r&&w.find.matchesSelector(_e,r),n.guid||(n.guid=w.guid++),(d=p.events)||(d=p.events=Object.create(null)),(s=p.handle)||(s=p.handle=function(t){return void 0!==w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(K)||[""]).length;l--;)f=_=(o=Ce.exec(t[l])||[])[1],m=(o[2]||"").split(".").sort(),f&&(c=w.event.special[f]||{},f=(r?c.delegateType:c.bindType)||f,c=w.event.special[f]||{},u=w.extend({type:f,origType:_,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&w.expr.match.needsContext.test(r),namespace:m.join(".")},a),(h=d[f])||((h=d[f]=[]).delegateCount=0,c.setup&&!1!==c.setup.call(e,i,m,s)||e.addEventListener&&e.addEventListener(f,s)),c.add&&(c.add.call(e,u),u.handler.guid||(u.handler.guid=n.guid)),r?h.splice(h.delegateCount++,0,u):h.push(u),w.event.global[f]=!0)},remove:function(e,t,n,i,r){var a,s,o,d,l,u,c,h,f,m,_,p=oe.hasData(e)&&oe.get(e);if(p&&(d=p.events)){for(l=(t=(t||"").match(K)||[""]).length;l--;)if(f=_=(o=Ce.exec(t[l])||[])[1],m=(o[2]||"").split(".").sort(),f){for(c=w.event.special[f]||{},h=d[f=(i?c.delegateType:c.bindType)||f]||[],o=o[2]&&new RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=a=h.length;a--;)u=h[a],!r&&_!==u.origType||n&&n.guid!==u.guid||o&&!o.test(u.namespace)||i&&i!==u.selector&&("**"!==i||!u.selector)||(h.splice(a,1),u.selector&&h.delegateCount--,c.remove&&c.remove.call(e,u));s&&!h.length&&(c.teardown&&!1!==c.teardown.call(e,m,p.handle)||w.removeEvent(e,f,p.handle),delete d[f])}else for(f in d)w.event.remove(e,f+t[l],n,i,!0);w.isEmptyObject(d)&&oe.remove(e,"handle events")}},dispatch:function(e){var t,n,i,r,a,s,o=new Array(arguments.length),d=w.event.fix(e),l=(oe.get(this,"events")||Object.create(null))[d.type]||[],u=w.event.special[d.type]||{};for(o[0]=d,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(a=[],s={},n=0;n-1:w.find(r,this,null,[l]).length),s[r]&&a.push(i);a.length&&o.push({elem:l,handlers:a})}return l=this,d\s*$/g;function Ue(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&w(e).children("tbody")[0]||e}function Be(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function ze(e,t){var n,i,r,a,s,o;if(1===t.nodeType){if(oe.hasData(e)&&(o=oe.get(e).events))for(r in oe.remove(t,"handle events"),o)for(n=0,i=o[r].length;n1&&"string"==typeof m&&!p.checkClone&&Ie.test(m))return e.each((function(r){var a=e.eq(r);_&&(t[0]=m.call(this,r,a.html())),Ve(a,t,n,i)}));if(h&&(a=(r=Pe(t,e[0].ownerDocument,!1,e,i)).firstChild,1===r.childNodes.length&&(r=a),a||i)){for(o=(s=w.map(Ee(r,"script"),Be)).length;c0&&xe(s,!d&&Ee(e,"script")),o},cleanData:function(e){for(var t,n,i,r=w.event.special,a=0;void 0!==(n=e[a]);a++)if(ae(n)){if(t=n[oe.expando]){if(t.events)for(i in t.events)r[i]?w.event.remove(n,i):w.removeEvent(n,i,t.handle);n[oe.expando]=void 0}n[de.expando]&&(n[de.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ke(this,e,!0)},remove:function(e){return Ke(this,e)},text:function(e){return ee(this,(function(e){return void 0===e?w.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Ve(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Ue(this,e).appendChild(e)}))},prepend:function(){return Ve(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Ue(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Ve(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Ve(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(Ee(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return w.clone(this,e,t)}))},html:function(e){return ee(this,(function(e){var t=this[0]||{},n=0,i=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Fe.test(e)&&!Ye[(De.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(d+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-a-d-o-.5))||0),d+l}function ut(e,t,n){var i=Je(e),r=(!p.boxSizingReliable()||n)&&"border-box"===w.css(e,"boxSizing",!1,i),a=r,s=Ze(e,t,i),o="offset"+t[0].toUpperCase()+t.slice(1);if(qe.test(s)){if(!n)return s;s="auto"}return(!p.boxSizingReliable()&&r||!p.reliableTrDimensions()&&S(e,"tr")||"auto"===s||!parseFloat(s)&&"inline"===w.css(e,"display",!1,i))&&e.getClientRects().length&&(r="border-box"===w.css(e,"boxSizing",!1,i),(a=o in e)&&(s=e[o])),(s=parseFloat(s)||0)+lt(e,t,n||(r?"border":"content"),a,i,s)+"px"}function ct(e,t,n,i,r){return new ct.prototype.init(e,t,n,i,r)}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ze(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var r,a,s,o=re(t),d=$e.test(t),l=e.style;if(d||(t=rt(o)),s=w.cssHooks[t]||w.cssHooks[o],void 0===n)return s&&"get"in s&&void 0!==(r=s.get(e,!1,i))?r:l[t];"string"==(a=typeof n)&&(r=fe.exec(n))&&r[1]&&(n=ve(e,t,r),a="number"),null!=n&&n==n&&("number"!==a||d||(n+=r&&r[3]||(w.cssNumber[o]?"":"px")),p.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),s&&"set"in s&&void 0===(n=s.set(e,n,i))||(d?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,i){var r,a,s,o=re(t);return $e.test(t)||(t=rt(o)),(s=w.cssHooks[t]||w.cssHooks[o])&&"get"in s&&(r=s.get(e,!0,n)),void 0===r&&(r=Ze(e,t,i)),"normal"===r&&t in ot&&(r=ot[t]),""===n||n?(a=parseFloat(r),!0===n||isFinite(a)?a||0:r):r}}),w.each(["height","width"],(function(e,t){w.cssHooks[t]={get:function(e,n,i){if(n)return!at.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?ut(e,t,i):Xe(e,st,(function(){return ut(e,t,i)}))},set:function(e,n,i){var r,a=Je(e),s=!p.scrollboxSize()&&"absolute"===a.position,o=(s||i)&&"border-box"===w.css(e,"boxSizing",!1,a),d=i?lt(e,t,i,o,a):0;return o&&s&&(d-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(a[t])-lt(e,t,"border",!1,a)-.5)),d&&(r=fe.exec(n))&&"px"!==(r[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),dt(0,n,d)}}})),w.cssHooks.marginLeft=et(p.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Ze(e,"marginLeft"))||e.getBoundingClientRect().left-Xe(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+"px"})),w.each({margin:"",padding:"",border:"Width"},(function(e,t){w.cssHooks[e+t]={expand:function(n){for(var i=0,r={},a="string"==typeof n?n.split(" "):[n];i<4;i++)r[e+me[i]+t]=a[i]||a[i-2]||a[0];return r}},"margin"!==e&&(w.cssHooks[e+t].set=dt)})),w.fn.extend({css:function(e,t){return ee(this,(function(e,t,n){var i,r,a={},s=0;if(Array.isArray(t)){for(i=Je(e),r=t.length;s1)}}),w.Tween=ct,ct.prototype={constructor:ct,init:function(e,t,n,i,r,a){this.elem=e,this.prop=n,this.easing=r||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=i,this.unit=a||(w.cssNumber[n]?"":"px")},cur:function(){var e=ct.propHooks[this.prop];return e&&e.get?e.get(this):ct.propHooks._default.get(this)},run:function(e){var t,n=ct.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=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):ct.propHooks._default.set(this),this}},ct.prototype.init.prototype=ct.prototype,ct.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||!w.cssHooks[e.prop]&&null==e.elem.style[rt(e.prop)]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},ct.propHooks.scrollTop=ct.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=ct.prototype.init,w.fx.step={};var ht,ft,mt=/^(?:toggle|show|hide)$/,_t=/queueHooks$/;function pt(){ft&&(!1===v.hidden&&i.requestAnimationFrame?i.requestAnimationFrame(pt):i.setTimeout(pt,w.fx.interval),w.fx.tick())}function gt(){return i.setTimeout((function(){ht=void 0})),ht=Date.now()}function yt(e,t){var n,i=0,r={height:e};for(t=t?1:0;i<4;i+=2-t)r["margin"+(n=me[i])]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function vt(e,t,n){for(var i,r=(Mt.tweeners[t]||[]).concat(Mt.tweeners["*"]),a=0,s=r.length;a1)},removeAttr:function(e){return this.each((function(){w.removeAttr(this,e)}))}}),w.extend({attr:function(e,t,n){var i,r,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return void 0===e.getAttribute?w.prop(e,t,n):(1===a&&w.isXMLDoc(e)||(r=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?Lt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):r&&"get"in r&&null!==(i=r.get(e,t))?i:null==(i=w.find.attr(e,t))?void 0:i)},attrHooks:{type:{set:function(e,t){if(!p.radioValue&&"radio"===t&&S(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,i=0,r=t&&t.match(K);if(r&&1===e.nodeType)for(;n=r[i++];)e.removeAttribute(n)}}),Lt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),(function(e,t){var n=Tt[t]||w.find.attr;Tt[t]=function(e,t,i){var r,a,s=t.toLowerCase();return i||(a=Tt[s],Tt[s]=r,r=null!=n(e,t,i)?s:null,Tt[s]=a),r}}));var bt=/^(?:input|select|textarea|button)$/i,kt=/^(?:a|area)$/i;function wt(e){return(e.match(K)||[]).join(" ")}function Dt(e){return e.getAttribute&&e.getAttribute("class")||""}function St(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(K)||[]}w.fn.extend({prop:function(e,t){return ee(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[w.propFix[e]||e]}))}}),w.extend({prop:function(e,t,n){var i,r,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return 1===a&&w.isXMLDoc(e)||(t=w.propFix[t]||t,r=w.propHooks[t]),void 0!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:e[t]=n:r&&"get"in r&&null!==(i=r.get(e,t))?i:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):bt.test(e.nodeName)||kt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),p.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){w.propFix[this.toLowerCase()]=this})),w.fn.extend({addClass:function(e){var t,n,i,r,a,s;return g(e)?this.each((function(t){w(this).addClass(e.call(this,t,Dt(this)))})):(t=St(e)).length?this.each((function(){if(i=Dt(this),n=1===this.nodeType&&" "+wt(i)+" "){for(a=0;a-1;)n=n.replace(" "+r+" "," ");s=wt(n),i!==s&&this.setAttribute("class",s)}})):this:this.attr("class","")},toggleClass:function(e,t){var n,i,r,a,s=typeof e,o="string"===s||Array.isArray(e);return g(e)?this.each((function(n){w(this).toggleClass(e.call(this,n,Dt(this),t),t)})):"boolean"==typeof t&&o?t?this.addClass(e):this.removeClass(e):(n=St(e),this.each((function(){if(o)for(a=w(this),r=0;r-1)return!0;return!1}});var Yt=/\r/g;w.fn.extend({val:function(e){var t,n,i,r=this[0];return arguments.length?(i=g(e),this.each((function(n){var r;1===this.nodeType&&(null==(r=i?e.call(this,n,w(this).val()):e)?r="":"number"==typeof r?r+="":Array.isArray(r)&&(r=w.map(r,(function(e){return null==e?"":e+""}))),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,r,"value")||(this.value=r))}))):r?(t=w.valHooks[r.type]||w.valHooks[r.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(r,"value"))?n:"string"==typeof(n=r.value)?n.replace(Yt,""):null==n?"":n:void 0}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:wt(w.text(e))}},select:{get:function(e){var t,n,i,r=e.options,a=e.selectedIndex,s="select-one"===e.type,o=s?null:[],d=s?a+1:r.length;for(i=a<0?d:s?a:0;i-1)&&(n=!0);return n||(e.selectedIndex=-1),a}}}}),w.each(["radio","checkbox"],(function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},p.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}));var Et=i.location,xt={guid:Date.now()},At=/\?/;w.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new i.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||w.error("Invalid XML: "+(n?w.map(n.childNodes,(function(e){return e.textContent})).join("\n"):e)),t};var Pt=/^(?:focusinfocus|focusoutblur)$/,Ct=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(e,t,n,r){var a,s,o,d,l,u,c,h,m=[n||v],_=f.call(e,"type")?e.type:e,p=f.call(e,"namespace")?e.namespace.split("."):[];if(s=h=o=n=n||v,3!==n.nodeType&&8!==n.nodeType&&!Pt.test(_+w.event.triggered)&&(_.indexOf(".")>-1&&(p=_.split("."),_=p.shift(),p.sort()),l=_.indexOf(":")<0&&"on"+_,(e=e[w.expando]?e:new w.Event(_,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=p.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:w.makeArray(t,[e]),c=w.event.special[_]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!y(n)){for(d=c.delegateType||_,Pt.test(d+_)||(s=s.parentNode);s;s=s.parentNode)m.push(s),o=s;o===(n.ownerDocument||v)&&m.push(o.defaultView||o.parentWindow||i)}for(a=0;(s=m[a++])&&!e.isPropagationStopped();)h=s,e.type=a>1?d:c.bindType||_,(u=(oe.get(s,"events")||Object.create(null))[e.type]&&oe.get(s,"handle"))&&u.apply(s,t),(u=l&&s[l])&&u.apply&&ae(s)&&(e.result=u.apply(s,t),!1===e.result&&e.preventDefault());return e.type=_,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(m.pop(),t)||!ae(n)||l&&g(n[_])&&!y(n)&&((o=n[l])&&(n[l]=null),w.event.triggered=_,e.isPropagationStopped()&&h.addEventListener(_,Ct),n[_](),e.isPropagationStopped()&&h.removeEventListener(_,Ct),w.event.triggered=void 0,o&&(n[l]=o)),e.result}},simulate:function(e,t,n){var i=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(i,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each((function(){w.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}});var Rt=/\[\]$/,Ht=/\r?\n/g,Ot=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;function Ft(e,t,n,i){var r;if(Array.isArray(t))w.each(t,(function(t,r){n||Rt.test(e)?i(e,r):Ft(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,n,i)}));else if(n||"object"!==T(t))i(e,t);else for(r in t)Ft(e+"["+r+"]",t[r],n,i)}w.param=function(e,t){var n,i=[],r=function(e,t){var n=g(t)?t():t;i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,(function(){r(this.name,this.value)}));else for(n in e)Ft(n,e[n],t,r);return i.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&jt.test(this.nodeName)&&!Ot.test(e)&&(this.checked||!we.test(e))})).map((function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,(function(e){return{name:t.name,value:e.replace(Ht,"\r\n")}})):{name:t.name,value:n.replace(Ht,"\r\n")}})).get()}});var It=/%20/g,Nt=/#.*$/,Ut=/([?&])_=[^&]*/,Bt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Wt=/^(?:GET|HEAD)$/,zt=/^\/\//,Gt={},Vt={},Kt="*/".concat("*"),qt=v.createElement("a");function $t(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var i,r=0,a=t.toLowerCase().match(K)||[];if(g(n))for(;i=a[r++];)"+"===i[0]?(i=i.slice(1)||"*",(e[i]=e[i]||[]).unshift(n)):(e[i]=e[i]||[]).push(n)}}function Jt(e,t,n,i){var r={},a=e===Vt;function s(o){var d;return r[o]=!0,w.each(e[o]||[],(function(e,o){var l=o(t,n,i);return"string"!=typeof l||a||r[l]?a?!(d=l):void 0:(t.dataTypes.unshift(l),s(l),!1)})),d}return s(t.dataTypes[0])||!r["*"]&&s("*")}function Xt(e,t){var n,i,r=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((r[n]?e:i||(i={}))[n]=t[n]);return i&&w.extend(!0,e,i),e}qt.href=Et.href,w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Xt(Xt(e,w.ajaxSettings),t):Xt(w.ajaxSettings,e)},ajaxPrefilter:$t(Gt),ajaxTransport:$t(Vt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var n,r,a,s,o,d,l,u,c,h,f=w.ajaxSetup({},t),m=f.context||f,_=f.context&&(m.nodeType||m.jquery)?w(m):w.event,p=w.Deferred(),g=w.Callbacks("once memory"),y=f.statusCode||{},M={},L={},T="canceled",b={readyState:0,getResponseHeader:function(e){var t;if(l){if(!s)for(s={};t=Bt.exec(a);)s[t[1].toLowerCase()+" "]=(s[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=s[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return l?a:null},setRequestHeader:function(e,t){return null==l&&(e=L[e.toLowerCase()]=L[e.toLowerCase()]||e,M[e]=t),this},overrideMimeType:function(e){return null==l&&(f.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)b.always(e[b.status]);else for(t in e)y[t]=[y[t],e[t]];return this},abort:function(e){var t=e||T;return n&&n.abort(t),k(0,t),this}};if(p.promise(b),f.url=((e||f.url||Et.href)+"").replace(zt,Et.protocol+"//"),f.type=t.method||t.type||f.method||f.type,f.dataTypes=(f.dataType||"*").toLowerCase().match(K)||[""],null==f.crossDomain){d=v.createElement("a");try{d.href=f.url,d.href=d.href,f.crossDomain=qt.protocol+"//"+qt.host!=d.protocol+"//"+d.host}catch(e){f.crossDomain=!0}}if(f.data&&f.processData&&"string"!=typeof f.data&&(f.data=w.param(f.data,f.traditional)),Jt(Gt,f,t,b),l)return b;for(c in(u=w.event&&f.global)&&0===w.active++&&w.event.trigger("ajaxStart"),f.type=f.type.toUpperCase(),f.hasContent=!Wt.test(f.type),r=f.url.replace(Nt,""),f.hasContent?f.data&&f.processData&&0===(f.contentType||"").indexOf("application/x-www-form-urlencoded")&&(f.data=f.data.replace(It,"+")):(h=f.url.slice(r.length),f.data&&(f.processData||"string"==typeof f.data)&&(r+=(At.test(r)?"&":"?")+f.data,delete f.data),!1===f.cache&&(r=r.replace(Ut,"$1"),h=(At.test(r)?"&":"?")+"_="+xt.guid+++h),f.url=r+h),f.ifModified&&(w.lastModified[r]&&b.setRequestHeader("If-Modified-Since",w.lastModified[r]),w.etag[r]&&b.setRequestHeader("If-None-Match",w.etag[r])),(f.data&&f.hasContent&&!1!==f.contentType||t.contentType)&&b.setRequestHeader("Content-Type",f.contentType),b.setRequestHeader("Accept",f.dataTypes[0]&&f.accepts[f.dataTypes[0]]?f.accepts[f.dataTypes[0]]+("*"!==f.dataTypes[0]?", "+Kt+"; q=0.01":""):f.accepts["*"]),f.headers)b.setRequestHeader(c,f.headers[c]);if(f.beforeSend&&(!1===f.beforeSend.call(m,b,f)||l))return b.abort();if(T="abort",g.add(f.complete),b.done(f.success),b.fail(f.error),n=Jt(Vt,f,t,b)){if(b.readyState=1,u&&_.trigger("ajaxSend",[b,f]),l)return b;f.async&&f.timeout>0&&(o=i.setTimeout((function(){b.abort("timeout")}),f.timeout));try{l=!1,n.send(M,k)}catch(e){if(l)throw e;k(-1,e)}}else k(-1,"No Transport");function k(e,t,s,d){var c,h,v,M,L,T=t;l||(l=!0,o&&i.clearTimeout(o),n=void 0,a=d||"",b.readyState=e>0?4:0,c=e>=200&&e<300||304===e,s&&(M=function(e,t,n){for(var i,r,a,s,o=e.contents,d=e.dataTypes;"*"===d[0];)d.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(r in o)if(o[r]&&o[r].test(i)){d.unshift(r);break}if(d[0]in n)a=d[0];else{for(r in n){if(!d[0]||e.converters[r+" "+d[0]]){a=r;break}s||(s=r)}a=a||s}if(a)return a!==d[0]&&d.unshift(a),n[a]}(f,b,s)),!c&&w.inArray("script",f.dataTypes)>-1&&w.inArray("json",f.dataTypes)<0&&(f.converters["text script"]=function(){}),M=function(e,t,n,i){var r,a,s,o,d,l={},u=e.dataTypes.slice();if(u[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];for(a=u.shift();a;)if(e.responseFields[a]&&(n[e.responseFields[a]]=t),!d&&i&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),d=a,a=u.shift())if("*"===a)a=d;else if("*"!==d&&d!==a){if(!(s=l[d+" "+a]||l["* "+a]))for(r in l)if((o=r.split(" "))[1]===a&&(s=l[d+" "+o[0]]||l["* "+o[0]])){!0===s?s=l[r]:!0!==l[r]&&(a=o[0],u.unshift(o[1]));break}if(!0!==s)if(s&&e.throws)t=s(t);else try{t=s(t)}catch(e){return{state:"parsererror",error:s?e:"No conversion from "+d+" to "+a}}}return{state:"success",data:t}}(f,M,b,c),c?(f.ifModified&&((L=b.getResponseHeader("Last-Modified"))&&(w.lastModified[r]=L),(L=b.getResponseHeader("etag"))&&(w.etag[r]=L)),204===e||"HEAD"===f.type?T="nocontent":304===e?T="notmodified":(T=M.state,h=M.data,c=!(v=M.error))):(v=T,!e&&T||(T="error",e<0&&(e=0))),b.status=e,b.statusText=(t||T)+"",c?p.resolveWith(m,[h,T,b]):p.rejectWith(m,[b,T,v]),b.statusCode(y),y=void 0,u&&_.trigger(c?"ajaxSuccess":"ajaxError",[b,f,c?h:v]),g.fireWith(m,[b,T]),u&&(_.trigger("ajaxComplete",[b,f]),--w.active||w.event.trigger("ajaxStop")))}return b},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],(function(e,t){w[t]=function(e,n,i,r){return g(n)&&(r=r||i,i=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:r,data:n,success:i},w.isPlainObject(e)&&e))}})),w.ajaxPrefilter((function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")})),w._evalUrl=function(e,t,n){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){w.globalEval(e,t,n)}})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return g(e)?this.each((function(t){w(this).wrapInner(e.call(this,t))})):this.each((function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=g(e);return this.each((function(n){w(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not("body").each((function(){w(this).replaceWith(this.childNodes)})),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new i.XMLHttpRequest}catch(e){}};var Qt={0:200,1223:204},Zt=w.ajaxSettings.xhr();p.cors=!!Zt&&"withCredentials"in Zt,p.ajax=Zt=!!Zt,w.ajaxTransport((function(e){var t,n;if(p.cors||Zt&&!e.crossDomain)return{send:function(r,a){var s,o=e.xhr();if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(s in e.xhrFields)o[s]=e.xhrFields[s];for(s in e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest"),r)o.setRequestHeader(s,r[s]);t=function(e){return function(){t&&(t=n=o.onload=o.onerror=o.onabort=o.ontimeout=o.onreadystatechange=null,"abort"===e?o.abort():"error"===e?"number"!=typeof o.status?a(0,"error"):a(o.status,o.statusText):a(Qt[o.status]||o.status,o.statusText,"text"!==(o.responseType||"text")||"string"!=typeof o.responseText?{binary:o.response}:{text:o.responseText},o.getAllResponseHeaders()))}},o.onload=t(),n=o.onerror=o.ontimeout=t("error"),void 0!==o.onabort?o.onabort=n:o.onreadystatechange=function(){4===o.readyState&&i.setTimeout((function(){t&&n()}))},t=t("abort");try{o.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}})),w.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")})),w.ajaxTransport("script",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(i,r){t=w(" +{% endif %} diff --git a/xblocks_contrib/video/tests/test_toggles.py b/xblocks_contrib/video/tests/test_toggles.py new file mode 100644 index 00000000..1eef0990 --- /dev/null +++ b/xblocks_contrib/video/tests/test_toggles.py @@ -0,0 +1,103 @@ +""" +Tests for video toggles functions. +""" + +import unittest +from unittest.mock import Mock, MagicMock + +from xblocks_contrib.video.toggles import ( + is_hls_playback_enabled, + get_branding_info, + get_course_by_id, + get_course_organization +) + + +class TestVideoToggles(unittest.TestCase): + """Test cases for video toggles functions.""" + + def setUp(self): + """Set up test fixtures.""" + self.video_block = Mock() + self.video_block.runtime = Mock() + + def test_is_hls_playback_enabled_with_service(self): + """Test HLS playback enabled with service available.""" + mock_service = Mock() + mock_service.is_hls_playback_enabled.return_value = True + self.video_block.runtime.service.return_value = mock_service + + result = is_hls_playback_enabled(self.video_block) + self.assertTrue(result) + self.video_block.runtime.service.assert_called_once_with(self.video_block, 'video_config') + mock_service.is_hls_playback_enabled.assert_called_once() + + def test_is_hls_playback_enabled_without_service(self): + """Test HLS playback enabled without service.""" + self.video_block.runtime.service.return_value = None + + result = is_hls_playback_enabled(self.video_block) + self.assertFalse(result) # Default value from edx-platform + + + def test_get_branding_info_with_service(self): + """Test branding info with service available.""" + mock_service = Mock() + expected_info = {'logo_url': 'test.png', 'logo_alt_text': 'Test', 'favicon_url': 'favicon.ico'} + mock_service.get_branding_info.return_value = expected_info + self.video_block.runtime.service.return_value = mock_service + + result = get_branding_info(self.video_block) + self.assertEqual(result, expected_info) + mock_service.get_branding_info.assert_called_once() + + def test_get_branding_info_without_service(self): + """Test branding info without service.""" + self.video_block.runtime.service.return_value = None + + result = get_branding_info(self.video_block) + expected = { + 'logo_url': None, + 'logo_alt_text': None, + 'favicon_url': None + } + self.assertEqual(result, expected) + + def test_get_course_by_id_with_service(self): + """Test get course by ID with service available.""" + mock_service = Mock() + mock_course = Mock() + mock_service.get_course_by_id.return_value = mock_course + self.video_block.runtime.service.return_value = mock_service + + course_id = Mock() + result = get_course_by_id(self.video_block, course_id) + self.assertEqual(result, mock_course) + mock_service.get_course_by_id.assert_called_once_with(course_id) + + def test_get_course_by_id_without_service(self): + """Test get course by ID without service.""" + self.video_block.runtime.service.return_value = None + + course_id = Mock() + result = get_course_by_id(self.video_block, course_id) + self.assertIsNone(result) + + def test_get_course_organization_with_service(self): + """Test get course organization with service available.""" + mock_service = Mock() + mock_service.get_course_organization.return_value = 'edx' + self.video_block.runtime.service.return_value = mock_service + + course_id = Mock() + result = get_course_organization(self.video_block, course_id) + self.assertEqual(result, 'edx') + mock_service.get_course_organization.assert_called_once_with(course_id) + + def test_get_course_organization_without_service(self): + """Test get course organization without service.""" + self.video_block.runtime.service.return_value = None + + course_id = Mock() + result = get_course_organization(self.video_block, course_id) + self.assertIsNone(result) diff --git a/xblocks_contrib/video/toggles.py b/xblocks_contrib/video/toggles.py new file mode 100644 index 00000000..7902a764 --- /dev/null +++ b/xblocks_contrib/video/toggles.py @@ -0,0 +1,74 @@ +""" +Video toggles and configuration methods for Video XBlock. + +This module contains functions that handle video-related configuration +and feature flags by accessing the VideoConfigService. +""" + + +def is_hls_playback_enabled(video_block, course_id): + """Check if HLS playback is enabled.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.is_hls_playback_enabled(course_id) + return False # TODO: Review it; Default from edx-platform: HLSPlaybackEnabledFlag toggle_default: False + + +def get_branding_info(video_block): + """Get branding information.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.get_branding_info() + return { + 'logo_url': None, + 'logo_alt_text': None, + 'favicon_url': None + } + + +def get_course_by_id(video_block, course_id): + """Get course by ID.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.get_course_by_id(course_id) + return None + + +def get_course_organization(video_block, course_id): + """Get course organization.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.get_course_organization(course_id) + return None + + +def is_youtube_deprecated(video_block, course_id): + """Check if YouTube is deprecated.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.is_youtube_deprecated(course_id) + return False # Default from edx-platform: DEPRECATE_YOUTUBE toggle_default: False + + +def is_youtube_blocked_for_course(video_block, course_id): + """Check if YouTube is blocked for course.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.is_youtube_blocked_for_course(course_id) + return False # Default from edx-platform: CourseYoutubeBlockedFlag default: False + + +def is_public_video_share_enabled(video_block, course_id): + """Check if public video share is enabled.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.is_public_video_share_enabled(course_id) + return False # Default from edx-platform: PUBLIC_VIDEO_SHARE toggle_default: False + + +def is_transcript_feedback_enabled(video_block, course_id): + """Check if transcript feedback is enabled.""" + video_config_service = video_block.runtime.service(video_block, 'video_config') + if video_config_service: + return video_config_service.is_transcript_feedback_enabled(course_id) + return False # Default from edx-platform: TRANSCRIPT_FEEDBACK toggle_default: False diff --git a/xblocks_contrib/video/transcripts_utils.py b/xblocks_contrib/video/transcripts_utils.py new file mode 100644 index 00000000..d96ff604 --- /dev/null +++ b/xblocks_contrib/video/transcripts_utils.py @@ -0,0 +1,1228 @@ +""" +Utility functions for transcripts. +++++++++++++++++++++++++++++++++++ +""" + +import copy +import html +import logging +import os +import pathlib +import re +from functools import wraps + +import requests +import simplejson as json +from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist +from lxml import etree +from opaque_keys.edx.keys import UsageKeyV2 +from pysrt import SubRipFile, SubRipItem, SubRipTime +from pysrt.srtexc import Error + +from .bumper_utils import get_bumper_settings +from .content import StaticContent +from .django import contentstore +from .exceptions import NotFoundError + +try: + from edxval import api as edxval_api +except ImportError: + edxval_api = None + +log = logging.getLogger(__name__) + +NON_EXISTENT_TRANSCRIPT = 'non_existent_dummy_file_name' + + +class TranscriptException(Exception): + pass + + +class TranscriptsGenerationException(Exception): + pass + + +class GetTranscriptsFromYouTubeException(Exception): + pass + + +class TranscriptsRequestValidationException(Exception): + pass + + +def exception_decorator(func): + """ + Generate NotFoundError for TranscriptsGenerationException, UnicodeDecodeError. + + Args: + `func`: Input function + + Returns: + 'wrapper': Decorated function + """ + + @wraps(func) + def wrapper(*args, **kwds): + try: + return func(*args, **kwds) + except (TranscriptsGenerationException, UnicodeDecodeError) as ex: + log.exception(str(ex)) + raise NotFoundError # lint-amnesty, pylint: disable=raise-missing-from + + return wrapper + + +def generate_subs(speed, source_speed, source_subs): + """ + Generate transcripts from one speed to another speed. + + Args: + `speed`: float, for this speed subtitles will be generated, + `source_speed`: float, speed of source_subs + `source_subs`: dict, existing subtitles for speed `source_speed`. + + Returns: + `subs`: dict, actual subtitles. + """ + if speed == source_speed: + return source_subs + + coefficient = 1.0 * speed / source_speed + subs = { + 'start': [ + int(round(timestamp * coefficient)) for + timestamp in source_subs['start'] + ], + 'end': [ + int(round(timestamp * coefficient)) for + timestamp in source_subs['end'] + ], + 'text': source_subs['text']} + return subs + + +def save_to_store(content, name, mime_type, location): + """ + Save named content to store by location. + + Returns location of saved content. + """ + content_location = Transcript.asset_location(location, name) + content = StaticContent(content_location, name, mime_type, content) + contentstore().save(content) + return content_location + + +def save_subs_to_store(subs, subs_id, item, language='en'): + """ + Save transcripts into `StaticContent`. + + Args: + `subs_id`: str, subtitles id + `item`: video block instance + `language`: two chars str ('uk'), language of translation of transcripts + + Returns: location of saved subtitles. + """ + filedata = json.dumps(subs, indent=2).encode('utf-8') + filename = subs_filename(subs_id, language) + return save_to_store(filedata, filename, 'application/json', item.location) + + +def get_transcript_link_from_youtube(youtube_id): + """ + Get the link for YouTube transcript by parsing the source of the YouTube webpage. + Inside the webpage, the details of the transcripts are located in a JSON object. + After prettifying the object, it looks like: + + "captions": { + "playerCaptionsTracklistRenderer": { + "captionTracks": [ + { + "baseUrl": "...", + "name": { + "simpleText": "(Japanese in local language)" + }, + "vssId": ".ja", + "languageCode": "ja", + "isTranslatable": true + }, + { + "baseUrl": "...", + "name": { + "simpleText": "(French in local language)" + }, + "vssId": ".fr", + "languageCode": "fr", + "isTranslatable": true + }, + { + "baseUrl": "...", + "name": { + "simpleText": "(English in local language)" + }, + "vssId": ".en", + "languageCode": "en", + "isTranslatable": true + }, + ... + ], + "audioTracks": [...] + "translationLanguages": ... + }, + ... + } + + So we use a regex to find the captionTracks JavaScript array, and then convert it + to a Python dict and return the link for en caption + """ + youtube_url_base = settings.YOUTUBE['TRANSCRIPTS']['YOUTUBE_URL_BASE'] + try: + youtube_html = requests.get(f"{youtube_url_base}{youtube_id}") + caption_re = settings.YOUTUBE['TRANSCRIPTS']['CAPTION_TRACKS_REGEX'] + caption_matched = re.search(caption_re, youtube_html.content.decode("utf-8")) + if caption_matched: + caption_tracks = json.loads(f'[{caption_matched.group("caption_tracks")}]') + caption_links = {} + for caption in caption_tracks: + language_code = caption.get('languageCode', None) + if language_code and not language_code == 'None': + link = caption.get("baseUrl") + caption_links[language_code] = link + return None if not caption_links else caption_links + return None + except ConnectionError: + return None + + +def get_transcript_links_from_youtube(youtube_id, settings, i18n, + youtube_transcript_name=''): # lint-amnesty, pylint: disable=redefined-outer-name + """ + Gets transcripts from youtube for youtube_id. + + Parses only utf-8 encoded transcripts. + Other encodings are not supported at the moment. + + Returns (status, transcripts): bool, dict. + """ + _ = i18n.gettext + transcript_links = get_transcript_link_from_youtube(youtube_id) + + if not transcript_links: + msg = _("Can't get transcript link from Youtube for {youtube_id}.").format( + youtube_id=youtube_id, + ) + raise GetTranscriptsFromYouTubeException(msg) + + return transcript_links + + +def get_transcript_from_youtube(link, youtube_id, i18n): + """ + Gets transcripts from youtube for youtube_id. + + Parses only utf-8 encoded transcripts. + Other encodings are not supported at the moment. + + Returns (status, transcripts): bool, dict. + """ + _ = i18n.gettext + utf8_parser = etree.XMLParser(encoding='utf-8') + data = requests.get(link) + + if data.status_code != 200 or not data.text: + msg = _("Can't receive transcripts from Youtube for {youtube_id}. Status code: {status_code}.").format( + youtube_id=youtube_id, + status_code=data.status_code + ) + raise GetTranscriptsFromYouTubeException(msg) + + sub_starts, sub_ends, sub_texts = [], [], [] + xmltree = etree.fromstring(data.content, parser=utf8_parser) + for element in xmltree: + if element.tag == "text": + start = float(element.get("start")) + duration = float(element.get("dur", 0)) # dur is not mandatory + text = element.text + end = start + duration + + if text: + # Start and end should be ints representing the millisecond timestamp. + sub_starts.append(int(start * 1000)) + sub_ends.append(int((end + 0.0001) * 1000)) + sub_texts.append(text.replace('\n', ' ')) + + return {'start': sub_starts, 'end': sub_ends, 'text': sub_texts} + + +def download_youtube_subs(youtube_id, video_block, settings): # lint-amnesty, pylint: disable=redefined-outer-name + """ + Download transcripts from Youtube. + + Args: + youtube_id: str, actual youtube_id of the video. + video_block: video block instance. + + We save transcripts for 1.0 speed, as for other speed conversion is done on front-end. + + Returns: + Serialized sjson transcript content, if transcripts were successfully downloaded and saved. + + Raises: + GetTranscriptsFromYouTubeException, if fails. + """ + i18n = video_block.runtime.service(video_block, "i18n") + _ = i18n.gettext + transcript_links = get_transcript_links_from_youtube(youtube_id, settings, i18n) + subs = [] + for (language_code, link) in transcript_links.items(): + sub = get_transcript_from_youtube(link, youtube_id, i18n) + subs.append([language_code, json.dumps(sub, indent=2)]) + return subs + + +def remove_subs_from_store(subs_id, item, lang='en'): + """ + Remove from store, if transcripts content exists. + """ + filename = subs_filename(subs_id, lang) + Transcript.delete_asset(item.location, filename) + + +def generate_subs_from_source(speed_subs, subs_type, subs_filedata, block, language='en'): + """Generate transcripts from source files (like SubRip format, etc.) + and save them to assets for `item` module. + We expect, that speed of source subs equal to 1 + + :param speed_subs: dictionary {speed: sub_id, ...} + :param subs_type: type of source subs: "srt", ... + :param subs_filedata:unicode, content of source subs. + :param block: course or block. + :param language: str, language of translation of transcripts + :returns: True, if all subs are generated and saved successfully. + """ + _ = block.runtime.service(block, "i18n").gettext + if subs_type.lower() != 'srt': + raise TranscriptsGenerationException(_("We support only SubRip (*.srt) transcripts format.")) + try: + srt_subs_obj = SubRipFile.from_string(subs_filedata) + except Exception as ex: + msg = _("Something wrong with SubRip transcripts file during parsing. Inner message is {error_message}").format( + error_message=str(ex) + ) + raise TranscriptsGenerationException(msg) # lint-amnesty, pylint: disable=raise-missing-from + if not srt_subs_obj: + raise TranscriptsGenerationException(_("Something wrong with SubRip transcripts file during parsing.")) + + sub_starts = [] + sub_ends = [] + sub_texts = [] + + for sub in srt_subs_obj: + sub_starts.append(sub.start.ordinal) + sub_ends.append(sub.end.ordinal) + sub_texts.append(sub.text.replace('\n', ' ')) + + subs = { + 'start': sub_starts, + 'end': sub_ends, + 'text': sub_texts} + + for speed, subs_id in speed_subs.items(): + save_subs_to_store( + generate_subs(speed, 1, subs), + subs_id, + block, + language + ) + + return subs + + +def generate_srt_from_sjson(sjson_subs, speed): + """Generate transcripts with speed = 1.0 from sjson to SubRip (*.srt). + + :param sjson_subs: "sjson" subs. + :param speed: speed of `sjson_subs`. + :returns: "srt" subs. + """ + + output = '' + + equal_len = len(sjson_subs['start']) == len(sjson_subs['end']) == len(sjson_subs['text']) + if not equal_len: + return output + + sjson_speed_1 = generate_subs(speed, 1, sjson_subs) + + for i in range(len(sjson_speed_1['start'])): + item = SubRipItem( + index=i, + start=SubRipTime(milliseconds=sjson_speed_1['start'][i]), + end=SubRipTime(milliseconds=sjson_speed_1['end'][i]), + text=sjson_speed_1['text'][i] + ) + output += (str(item)) + output += '\n' + return output + + +def generate_sjson_from_srt(srt_subs): + """ + Generate transcripts from sjson to SubRip (*.srt). + + Arguments: + srt_subs(SubRip): "SRT" subs object + + Returns: + Subs converted to "SJSON" format. + """ + sub_starts = [] + sub_ends = [] + sub_texts = [] + for sub in srt_subs: + sub_starts.append(sub.start.ordinal) + sub_ends.append(sub.end.ordinal) + sub_texts.append(sub.text.replace('\n', ' ')) + + sjson_subs = { + 'start': sub_starts, + 'end': sub_ends, + 'text': sub_texts + } + return sjson_subs + + +def copy_or_rename_transcript(new_name, old_name, item, delete_old=False, user=None): + """ + Renames `old_name` transcript file in storage to `new_name`. + + If `old_name` is not found in storage, raises `NotFoundError`. + If `delete_old` is True, removes `old_name` files from storage. + """ + filename = f'subs_{old_name}.srt.sjson' + content_location = StaticContent.compute_location(item.location.course_key, filename) + transcripts = contentstore().find(content_location).data.decode('utf-8') + save_subs_to_store(json.loads(transcripts), new_name, item) + item.sub = new_name + item.save_with_metadata(user) + if delete_old: + remove_subs_from_store(old_name, item) + + +def get_html5_ids(html5_sources): + """ + Helper method to parse out an HTML5 source into the ideas + NOTE: This assumes that '/' are not in the filename + """ + html5_ids = [x.split('/')[-1].rsplit('.', 1)[0] for x in html5_sources] + return html5_ids + + +def manage_video_subtitles_save(item, user, old_metadata=None, generate_translation=False): + """ + Does some specific things, that can be done only on save. + + Video player item has some video fields: HTML5 ones and Youtube one. + + If value of `sub` field of `new_item` is cleared, transcripts should be removed. + + `item` is video block instance with updated values of fields, + but actually have not been saved to store yet. + + `old_metadata` contains old values of XFields. + + # 1. + If value of `sub` field of `new_item` is different from values of video fields of `new_item`, + and `new_item.sub` file is present, then code in this function creates copies of + `new_item.sub` file with new names. That names are equal to values of video fields of `new_item` + After that `sub` field of `new_item` is changed to one of values of video fields. + This whole action ensures that after user changes video fields, proper `sub` files, corresponding + to new values of video fields, will be presented in system. + + # 2. convert /static/filename.srt to filename.srt in self.transcripts. + (it is done to allow user to enter both /static/filename.srt and filename.srt) + + # 3. Generate transcripts translation only when user clicks `save` button, not while switching tabs. + a) delete sjson translation for those languages, which were removed from `item.transcripts`. + Note: we are not deleting old SRT files to give user more flexibility. + b) For all SRT files in`item.transcripts` regenerate new SJSON files. + (To avoid confusing situation if you attempt to correct a translation by uploading + a new version of the SRT file with same name). + """ + _ = item.runtime.service(item, "i18n").gettext + + # # 1. + # html5_ids = get_html5_ids(item.html5_sources) + + # # Youtube transcript source should always have a higher priority than html5 sources. Appending + # # `youtube_id_1_0` at the end helps achieve this when we read transcripts list. + # possible_video_id_list = html5_ids + [item.youtube_id_1_0] + # sub_name = item.sub + # for video_id in possible_video_id_list: + # if not video_id: + # continue + # if not sub_name: + # remove_subs_from_store(video_id, item) + # continue + # # copy_or_rename_transcript changes item.sub of module + # try: + # # updates item.sub with `video_id`, if it is successful. + # copy_or_rename_transcript(video_id, sub_name, item, user=user) + # except NotFoundError: + # # subtitles file `sub_name` is not presented in the system. Nothing to copy or rename. + # log.debug( + # "Copying %s file content to %s name is failed, " + # "original file does not exist.", + # sub_name, video_id + # ) + + # 2. + if generate_translation: + for lang, filename in item.transcripts.items(): + item.transcripts[lang] = os.path.split(filename)[-1] + + # 3. + if generate_translation: + old_langs = set(old_metadata.get('transcripts', {})) if old_metadata else set() + new_langs = set(item.transcripts) + + html5_ids = get_html5_ids(item.html5_sources) + possible_video_id_list = html5_ids + [item.youtube_id_1_0] + + for lang in old_langs.difference(new_langs): # 3a + for video_id in possible_video_id_list: + if video_id: + remove_subs_from_store(video_id, item, lang) + + reraised_message = '' + for lang in new_langs: # 3b + try: + generate_sjson_for_all_speeds( + item, + item.transcripts[lang], + {speed: subs_id for subs_id, speed in youtube_speed_dict(item).items()}, + lang, + ) + except TranscriptException: + pass + if reraised_message: + item.save_with_metadata(user) + raise TranscriptException(reraised_message) + + +def youtube_speed_dict(item): + """ + Returns {speed: youtube_ids, ...} dict for existing youtube_ids + """ + yt_ids = [item.youtube_id_0_75, item.youtube_id_1_0, item.youtube_id_1_25, item.youtube_id_1_5] + yt_speeds = [0.75, 1.00, 1.25, 1.50] + youtube_ids = {p[0]: p[1] for p in zip(yt_ids, yt_speeds) if p[0]} + return youtube_ids + + +def subs_filename(subs_id, lang='en'): + """ + Generate proper filename for storage. + """ + if lang == 'en': + return f'subs_{subs_id}.srt.sjson' + else: + return f'{lang}_subs_{subs_id}.srt.sjson' + + +def generate_sjson_for_all_speeds(block, user_filename, result_subs_dict, lang): + """ + Generates sjson from srt for given lang. + """ + _ = block.runtime.service(block, "i18n").gettext + + try: + srt_transcripts = contentstore().find(Transcript.asset_location(block.location, user_filename)) + except NotFoundError as ex: + raise TranscriptException(_("{exception_message}: Can't find uploaded transcripts: {user_filename}").format( + # lint-amnesty, pylint: disable=raise-missing-from + exception_message=str(ex), + user_filename=user_filename + )) + + if not lang: + lang = block.transcript_language + + # Used utf-8-sig encoding type instead of utf-8 to remove BOM(Byte Order Mark), e.g. U+FEFF + generate_subs_from_source( + result_subs_dict, + os.path.splitext(user_filename)[1][1:], + srt_transcripts.data.decode('utf-8-sig'), + block, + lang + ) + + +def get_or_create_sjson(block, transcripts): + """ + Get sjson if already exists, otherwise generate it. + + Generate sjson with subs_id name, from user uploaded srt. + Subs_id is extracted from srt filename, which was set by user. + + Args: + transcipts (dict): dictionary of (language: file) pairs. + + Raises: + TranscriptException: when srt subtitles do not exist, + and exceptions from generate_subs_from_source. + """ + user_filename = transcripts[block.transcript_language] + user_subs_id = os.path.splitext(user_filename)[0] + source_subs_id, result_subs_dict = user_subs_id, {1.0: user_subs_id} + try: + sjson_transcript = Transcript.asset(block.location, source_subs_id, block.transcript_language).data + except NotFoundError: # generating sjson from srt + generate_sjson_for_all_speeds(block, user_filename, result_subs_dict, block.transcript_language) + sjson_transcript = Transcript.asset(block.location, source_subs_id, block.transcript_language).data + return sjson_transcript + + +def get_video_ids_info(edx_video_id, youtube_id_1_0, html5_sources): + """ + Returns list internal or external video ids. + + Arguments: + edx_video_id (unicode): edx_video_id + youtube_id_1_0 (unicode): youtube id + html5_sources (list): html5 video ids + + Returns: + tuple: external or internal, video ids list + """ + clean = lambda item: item.strip() if isinstance(item, str) else item + external = not bool(clean(edx_video_id)) + + video_ids = [edx_video_id, youtube_id_1_0] + get_html5_ids(html5_sources) + + # video_ids cleanup + video_ids = [item for item in video_ids if bool(clean(item))] + + return external, video_ids + + +def clean_video_id(edx_video_id): + """ + Cleans an edx video ID. + + Arguments: + edx_video_id(unicode): edx-val's video identifier + """ + return edx_video_id and edx_video_id.strip() + + +def get_video_transcript_content(edx_video_id, language_code): + """ + Gets video transcript content, only if the corresponding feature flag is enabled for the given `course_id`. + + Arguments: + language_code(unicode): Language code of the requested transcript + edx_video_id(unicode): edx-val's video identifier + + Returns: + A dict containing transcript's file name and its sjson content. + """ + transcript = None + edx_video_id = clean_video_id(edx_video_id) + if edxval_api and edx_video_id: + try: + transcript = edxval_api.get_video_transcript_data(edx_video_id, language_code) + except ValueError: + log.exception( + f"Error getting transcript from edx-val id: {edx_video_id}: language code {language_code}" + ) + content = '{"start": [1],"end": [2],"text": ["An error occured obtaining the transcript."]}' + transcript = dict( + file_name='error-{edx_video_id}-{language_code}.srt', + content=Transcript.convert(content, 'sjson', 'srt') + ) + return transcript + + +def get_available_transcript_languages(edx_video_id): + """ + Gets available transcript languages for a video. + + Arguments: + edx_video_id(unicode): edx-val's video identifier + + Returns: + A list containing distinct transcript language codes against all the passed video ids. + """ + available_languages = [] + edx_video_id = clean_video_id(edx_video_id) + if edxval_api and edx_video_id: + available_languages = edxval_api.get_available_transcript_languages(video_id=edx_video_id) + + return available_languages + + +def convert_video_transcript(file_name, content, output_format): + """ + Convert video transcript into desired format + + Arguments: + file_name: name of transcript file along with its extension + content: transcript content stream + output_format: the format in which transcript will be converted + + Returns: + A dict containing the new transcript filename and the content converted into desired format. + """ + name_and_extension = os.path.splitext(file_name) + basename, input_format = name_and_extension[0], name_and_extension[1][1:] + filename = f'{basename}.{output_format}' + converted_transcript = Transcript.convert(content, input_format=input_format, output_format=output_format) + + return dict(filename=filename, content=converted_transcript) + + +class Transcript: + """ + Container for transcript methods. + """ + SRT = 'srt' + TXT = 'txt' + SJSON = 'sjson' + mime_types = { + SRT: 'application/x-subrip; charset=utf-8', + TXT: 'text/plain; charset=utf-8', + SJSON: 'application/json', + } + + @staticmethod + def convert(content, input_format, output_format): + """ + Convert transcript `content` from `input_format` to `output_format`. + + Accepted input formats: sjson, srt. + Accepted output format: srt, txt, sjson. + + Raises: + TranscriptsGenerationException: On parsing the invalid srt content during conversion from srt to sjson. + """ + assert input_format in ('srt', 'sjson') + assert output_format in ('txt', 'srt', 'sjson') + + if input_format == output_format: + return content + + if input_format == 'srt': + # Standardize content into bytes for later decoding. + if isinstance(content, str): + content = content.encode('utf-8') + + if output_format == 'txt': + text = SubRipFile.from_string(content.decode('utf-8')).text + return html.unescape(text) + + elif output_format == 'sjson': + try: + srt_subs = SubRipFile.from_string( + # Skip byte order mark(BOM) character + content.decode('utf-8-sig'), + error_handling=SubRipFile.ERROR_RAISE + ) + except Error as ex: # Base exception from pysrt + raise TranscriptsGenerationException(str(ex)) from ex + + return json.dumps(generate_sjson_from_srt(srt_subs)) + + if input_format == 'sjson': + # If the JSON file content is bytes, try UTF-8, then Latin-1 + if isinstance(content, bytes): + try: + content_str = content.decode('utf-8') + except UnicodeDecodeError: + content_str = content.decode('latin-1') + else: + content_str = content + try: + content_dict = json.loads(content_str) + except ValueError: + truncated = content_str[:100].strip() + log.exception( + f"Failed to convert {input_format} to {output_format} for {repr(truncated)}..." + ) + content_dict = {"start": [1], "end": [2], "text": ["An error occured obtaining the transcript."]} + if output_format == 'txt': + text = content_dict['text'] + text_without_none = [line if line else '' for line in text] + return html.unescape("\n".join(text_without_none)) + elif output_format == 'srt': + return generate_srt_from_sjson(content_dict, speed=1.0) + + @staticmethod + def asset(location, subs_id, lang='en', filename=None): + """ + Get asset from contentstore, asset location is built from subs_id and lang. + + `location` is block location. + """ + # HACK Warning! this is temporary and will be removed once edx-val take over the + # transcript module and contentstore will only function as fallback until all the + # data is migrated to edx-val. It will be saving a contentstore hit for a hardcoded + # dummy-non-existent-transcript name. + if NON_EXISTENT_TRANSCRIPT in [subs_id, filename]: + raise NotFoundError + + asset_filename = subs_filename(subs_id, lang) if not filename else filename + return Transcript.get_asset(location, asset_filename) + + @staticmethod + def get_asset(location, filename): + """ + Return asset by location and filename. + """ + return contentstore().find(Transcript.asset_location(location, filename)) + + @staticmethod + def asset_location(location, filename): + """ + Return asset location. `location` is block location. + """ + # If user transcript filename is empty, raise `TranscriptException` to avoid `InvalidKeyError`. + if not filename: + raise TranscriptException("Transcript not uploaded yet") + return StaticContent.compute_location(location.course_key, filename) + + @staticmethod + def delete_asset(location, filename): + """ + Delete asset by location and filename. + """ + try: + contentstore().delete(Transcript.asset_location(location, filename)) + log.info("Transcript asset %s was removed from store.", filename) + except NotFoundError: + pass + return StaticContent.compute_location(location.course_key, filename) + + +class VideoTranscriptsMixin: + """Mixin class for transcript functionality. + + This is necessary for VideoBlock. + """ + + def available_translations(self, transcripts, verify_assets=None, is_bumper=False): + """ + Return a list of language codes for which we have transcripts. + + Arguments: + verify_assets (boolean): If True, checks to ensure that the transcripts + really exist in the contentstore. If False, we just look at the + VideoBlock fields and do not query the contentstore. One reason + we might do this is to avoid slamming contentstore() with queries + when trying to make a listing of videos and their languages. + + Defaults to `not FALLBACK_TO_ENGLISH_TRANSCRIPTS`. + + transcripts (dict): A dict with all transcripts and a sub. + include_val_transcripts(boolean): If True, adds the edx-val transcript languages as well. + """ + translations = [] + if verify_assets is None: + verify_assets = not settings.FEATURES.get('FALLBACK_TO_ENGLISH_TRANSCRIPTS') + + sub, other_langs = transcripts["sub"], transcripts["transcripts"] + + if verify_assets: + all_langs = dict(**other_langs) + if sub: + all_langs.update({'en': sub}) + + for language, filename in all_langs.items(): + try: + # for bumper videos, transcripts are stored in content store only + if is_bumper: + get_transcript_for_video(self.location, filename, filename, language) + else: + get_transcript(self, language) + except NotFoundError: + continue + + translations.append(language) + else: + # If we're not verifying the assets, we just trust our field values + translations = list(other_langs) + if not translations or sub: + translations += ['en'] + + # to clean redundant language codes. + return list(set(translations)) + + def get_default_transcript_language(self, transcripts, dest_lang=None): + """ + Returns the default transcript language for this video block. + + Args: + transcripts (dict): A dict with all transcripts and a sub. + dest_lang (unicode): language coming from unit translation language selector. + """ + sub, other_lang = transcripts["sub"], transcripts["transcripts"] + + # language in plugin selector exists as transcript + if dest_lang and dest_lang in other_lang.keys(): + transcript_language = dest_lang + # language in plugin selector is english and empty transcripts or transcripts and sub exists + elif dest_lang and dest_lang == 'en' and (not other_lang or (other_lang and sub)): + transcript_language = 'en' + elif self.transcript_language in other_lang: + transcript_language = self.transcript_language + elif sub: + transcript_language = 'en' + elif len(other_lang) > 0: + transcript_language = sorted(other_lang)[0] + else: + transcript_language = 'en' + return transcript_language + + def get_transcripts_info(self, is_bumper=False): + """ + Returns a transcript dictionary for the video. + + Arguments: + is_bumper(bool): If True, the request is for the bumper transcripts + include_val_transcripts(bool): If True, include edx-val transcripts as well + """ + if is_bumper: + transcripts = copy.deepcopy(get_bumper_settings(self).get('transcripts', {})) + sub = transcripts.pop("en", "") + else: + transcripts = self.transcripts if self.transcripts else {} + sub = self.sub + + # Only attach transcripts that are not empty. + transcripts = { + language_code: transcript_file + for language_code, transcript_file in transcripts.items() if transcript_file != '' + } + + # bumper transcripts are stored in content store so we don't need to include val transcripts + if not is_bumper: + transcript_languages = get_available_transcript_languages(edx_video_id=self.edx_video_id) + # HACK Warning! this is temporary and will be removed once edx-val take over the + # transcript module and contentstore will only function as fallback until all the + # data is migrated to edx-val. + for language_code in transcript_languages: + if language_code == 'en' and not sub: + sub = NON_EXISTENT_TRANSCRIPT + elif not transcripts.get(language_code): + transcripts[language_code] = NON_EXISTENT_TRANSCRIPT + + return { + "sub": sub, + "transcripts": transcripts, + } + + +@exception_decorator +def get_transcript_from_val(edx_video_id, lang=None, output_format=Transcript.SRT): + """ + Get video transcript from edx-val. + Arguments: + edx_video_id (unicode): video identifier + lang (unicode): transcript language + output_format (unicode): transcript output format + Returns: + tuple containing content, filename, mimetype + """ + transcript = get_video_transcript_content(edx_video_id, lang) + if not transcript: + raise NotFoundError(f'Transcript not found for {edx_video_id}, lang: {lang}') + + transcript_conversion_props = dict(transcript, output_format=output_format) + transcript = convert_video_transcript(**transcript_conversion_props) + filename = transcript['filename'] + content = transcript['content'] + mimetype = Transcript.mime_types[output_format] + + return content, filename, mimetype + + +def get_transcript_for_video(video_location, subs_id, file_name, language): + """ + Get video transcript from content store. This is a lower level function and is used by + `get_transcript_from_contentstore`. Prefer that function instead where possible. If you + need to support getting transcripts from VAL or Learning Core as well, use the `get_transcript` + function instead. + + NOTE: Transcripts can be searched from content store by two ways: + 1. by an id(a.k.a subs_id) which will be used to construct transcript filename + 2. by providing transcript filename + + Arguments: + video_location (Locator): Video location + subs_id (unicode): id for a transcript in content store + file_name (unicode): file_name for a transcript in content store + language (unicode): transcript language + + Returns: + tuple containing transcript input_format, basename, content + """ + try: + if subs_id is None: + raise NotFoundError + content = Transcript.asset(video_location, subs_id, language).data.decode('utf-8') + base_name = subs_id + input_format = Transcript.SJSON + except NotFoundError: + content = Transcript.asset(video_location, None, language, file_name).data.decode('utf-8') + base_name = os.path.splitext(file_name)[0] + input_format = Transcript.SRT + + return input_format, base_name, content + + +@exception_decorator +def get_transcript_from_contentstore(video, language, output_format, transcripts_info, youtube_id=None): + """ + Get video transcript from content store. + + Arguments: + video (Video block): Video block + language (unicode): transcript language + output_format (unicode): transcript output format + transcripts_info (dict): transcript info for a video + youtube_id (unicode): youtube video id + + Returns: + tuple containing content, filename, mimetype + """ + input_format, base_name, transcript_content = None, None, None + if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT): + raise NotFoundError(f'Invalid transcript format `{output_format}`') + + sub, other_languages = transcripts_info['sub'], transcripts_info['transcripts'] + transcripts = dict(other_languages) + + # this is sent in case of a translation dispatch and we need to use it as our subs_id. + possible_sub_ids = [youtube_id, sub, video.youtube_id_1_0] + get_html5_ids(video.html5_sources) + for sub_id in possible_sub_ids: + try: + transcripts['en'] = sub_id + input_format, base_name, transcript_content = get_transcript_for_video( + video.location, + subs_id=sub_id, + file_name=transcripts[language], + language=language + ) + break + except (KeyError, NotFoundError): + continue + + if transcript_content is None: + raise NotFoundError('No transcript for `{lang}` language'.format( + lang=language + )) + + # add language prefix to transcript file only if language is not None + language_prefix = f'{language}_' if language else '' + transcript_name = f'{language_prefix}{base_name}.{output_format}' + transcript_content = Transcript.convert(transcript_content, input_format=input_format, output_format=output_format) + if not transcript_content.strip(): + raise NotFoundError('No transcript content') + + if youtube_id: + youtube_ids = youtube_speed_dict(video) + transcript_content = json.dumps( + generate_subs(youtube_ids.get(youtube_id, 1), 1, json.loads(transcript_content)) + ) + + return transcript_content, transcript_name, Transcript.mime_types[output_format] + + +def get_transcript_from_learning_core(video_block, language, output_format, transcripts_info): + """ + Get video transcript from Learning Core (used for Content Libraries) + + Limitation: This is only going to grab from the Draft version. + + Learning Core models a VideoBlock's data in a more generic thing it calls a + Component. Each Component has its own virtual space for file-like data. The + OLX for the VideoBlock itself is stored at the root of that space, as + ``block.xml``. Static assets that are meant to be user-downloadable are + placed in a `static/` directory for that Component, and this is where we + expect to store transcript files. + + So if there is a ``video1-en.srt`` file for a particular VideoBlock, we + expect that to be stored as ``static/video1-en.srt`` in the Component. Any + other downloadable files would be here as well, such as thumbnails. + + Video XBlocks in Blockstore must set the 'transcripts' XBlock field to a + JSON dictionary listing the filename of the transcript for each language: +