diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a76b29 --- /dev/null +++ b/.gitignore @@ -0,0 +1,477 @@ +#### Project specific +config.php + +##### Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +##### Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +##### MacOS +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +##### Backup +*.bak +*.gho +*.ori +*.orig +*.tmp + +##### GPG +secring.* + +##### Dropbox +# Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache + +##### SynopsysVCS +# Waveform formats +*.vcd +*.vpd +*.evcd +*.fsdb + +# Default name of the simulation executable. A different name can be +# specified with this switch (the associated daidir database name is +# also taken from here): -o / +simv + +# Generated for Verilog and VHDL top configs +simv.daidir/ +simv.db.dir/ + +# Infrastructure necessary to co-simulate SystemC models with +# Verilog/VHDL models. An alternate directory may be specified with this +# switch: -Mdir= +csrc/ + +# Log file - the following switch allows to specify the file that will be +# used to write all messages from simulation: -l +*.log + +# Coverage results (generated with urg) and database location. The +# following switch can also be used: urg -dir .vdb +simv.vdb/ +urgReport/ + +# DVE and UCLI related files. +DVEfiles/ +ucli.key + +# When the design is elaborated for DirectC, the following file is created +# with declarations for C/C++ functions. +vc_hdrs.h + +##### SVN +.svn/ + +##### Mercurial +.hg/ +.hgignore +.hgsigs +.hgsub +.hgsubstate +.hgtags + +##### Bazaar +.bzr/ +.bzrignore + +##### CVS +/CVS/* +**/CVS/* +.cvsignore +*/.cvsignore + +##### TortoiseGit +# Project-level settings +/.tgitconfig + +##### PuTTY +# Private key +*.ppk + +##### Vim +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +##### Emacs +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + +##### SublimeText +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +##### Notepad++ +# Notepad++ backups # +*.bak + +##### TextMate +*.tmproj +*.tmproject +tmtags + +##### VisualStudioCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +##### NetBeans +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +##### JetBrains +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +##### Eclipse +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +##### Dreamweaver +# DW Dreamweaver added files +_notes +_compareTemp +configs/ +dwsync.xml +dw_php_codehinting.config +*.mno + +##### CodeKit +# General CodeKit files to ignore +config.codekit +config.codekit3 +/min + +##### Gradle +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +##### Composer +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +composer.lock + +##### PHP CodeSniffer +# gitignore for the PHP Codesniffer framework +# website: https://github.com/squizlabs/PHP_CodeSniffer +# +# Recommended template: PHP.gitignore + +/wpcs/* + +##### SASS +.sass-cache/ +*.css.map +*.sass.map +*.scss.map diff --git a/add2calendar.php b/add2calendar.php new file mode 100644 index 0000000..90a21cd --- /dev/null +++ b/add2calendar.php @@ -0,0 +1,184 @@ + MUST be a time AFTER $startDate +if (isset($_GET["to"])) { + $endDateInput = filter_var($_GET["to"], FILTER_SANITIZE_NUMBER_INT); +} +$duration = false; +// DateInterval format, e.g. PT1H +if (isset($_GET["duration"])) { + $duration = filter_var($_GET["duration"], FILTER_SANITIZE_STRING); +} +if ( + $title === false || $description === false || $address === false || $startDateInput === false || + $allDay === false || ($endDateInput === false && $duration === false) +) { + redirect_to_page('index.html'); +} +$inputDateFormat = 'U'; // input should be UNIX timestamp formatted. e.g. 1612612219 +$durationInterval = null; +try { + $startDate = DateTime::createFromFormat($inputDateFormat, $startDateInput); +} catch (Exception $e) { + echo 'from input date (' . $startDateInput . ') wrong format, please use a unix-timestamp, e.g. 1612612219' . PHP_EOL; + exit(1); +} +if ($endDateInput) { + try { + $endDate = DateTime::createFromFormat($inputDateFormat, $endDateInput); + } catch (Exception $e) { + echo 'to input date (' . $endDateInput . ') wrong format, please use a unix-timestamp, e.g. 1612612219' . PHP_EOL; + exit(1); + } +} else { + if ($duration) { + try { + $durationInterval = new DateInterval($duration); + } catch (Exception $e) { + echo 'duration input (' . $duration . ') wrong format, please use PHP DateInterval format, e.g. PT1H for +1 hour' . PHP_EOL; + exit(1); + } + $endDate = (clone $startDate)->add($durationInterval); + } +} +if ($endDate <= $startDate) { + echo 'the end date/time must be after start date/time' . PHP_EOL; + exit(1); +} +if ($allDay != 'yes' && $allDay != 'no') { + echo 'allday input (' . $allDay . ') wrong format, please use either "yes" or "no" as value' . PHP_EOL; + exit(1); +} +$allDay = $allDay == 'yes' ? true : false; +// ---------------------------------------------------------------- +$dateFormat = 'Ymd'; +$dateTimeFormat = 'Ymd\THis\Z'; +$outlookDateFormat = 'Y-m-d'; +$outlookDateTimeFormat = 'Y-m-d\TH:i:s\Z'; +$IcsDateTimeFormat = 'e:Ymd\THis'; +$dateTimeFormat = $allDay ? $dateFormat : $dateTimeFormat; +// ---------------------------------------------------------------- +// Google Calendar +$googleCalUrl = 'https://calendar.google.com/calendar/render?action=TEMPLATE'; +$googleCalUrl .= '&dates=' . $startDate->format($dateTimeFormat) . '/' . $endDate->format($dateTimeFormat); +$googleCalUrl .= '&text=' . rawurlencode($title); +if ($description) $googleCalUrl .= '&details=' . rawurlencode($description); +if ($address) $googleCalUrl .= '&location=' . rawurlencode($address); +// ---------------------------------------------------------------- +// Outlook365 Calendar +$outlook365Url = 'https://outlook.live.com/calendar/deeplink/compose?path=/calendar/action/compose'; //&rru=addevent causes + signs +$outlook365Url .= '&startdt=' . $startDate->format($outlookDateTimeFormat); +$outlook365Url .= '&enddt=' . $endDate->format($outlookDateTimeFormat); +if ($allDay) $outlook365Url .= '&allday=true'; +$outlook365Url .= '&subject=' . rawurlencode($title); +if ($description) $outlook365Url .= '&body=' . rawurlencode($description); +if ($address) $outlook365Url .= '&location=' . rawurlencode($address); +// ---------------------------------------------------------------- +// Yahoo! Calendar +$yahooUrl = 'https://calendar.yahoo.com/?v=60&view=d&type=20'; +if ($allDay && $startDate->diff($endDate)->days === 1) { + $yahooUrl .= '&st=' . $startDate->format($dateTimeFormat); + $yahooUrl .= '&dur=allday'; +} else { + $yahooUrl .= '&st=' . $startDate->format($dateTimeFormat); + /** + * Yahoo has a bug on parsing end date parameter: it ignores timezone, assuming + * that it's specified in user's tz. In order to bypass it, we can use duration ("dur") + * parameter instead of "et", but this parameter has a limitation cause by it's format HHmm: + * the max duration is 99hours and 59 minutes (dur=9959). + */ + $maxDurationInSecs = (59 * 60 * 60) + (59 * 60); + $canUseDuration = $maxDurationInSecs > ($endDate->getTimestamp() - $startDate->getTimestamp()); + if ($canUseDuration) { + $dateDiff = $startDate->diff($endDate); + $yahooUrl .= '&dur=' . $dateDiff->format('%H%I'); + } else { + $yahooUrl .= '&et=' . $endDate->format($dateTimeFormat); + } +} +$yahooUrl .= '&title=' . rawurlencode($title); +if ($description) $yahooUrl .= '&desc=' . rawurlencode($description); +if ($address) $yahooUrl .= '&in_loc=' . rawurlencode($address); +// ---------------------------------------------------------------- +// ICS Download +// See: https://tools.ietf.org/html/rfc5545#section-3.8.4.7 +$eventUuid = md5(sprintf( + '%s%s%s%s', + $startDate->format(\DateTimeInterface::ATOM), + $endDate->format(\DateTimeInterface::ATOM), + $title, + $address +)); +// See: https://tools.ietf.org/html/rfc5545.html#section-3.3.11 +$ecapedTitle = addcslashes($title, "\r\n,;"); +$IcsUrlParts = array( + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'BEGIN:VEVENT', + 'UID:' . $eventUuid, + 'SUMMARY:' . $ecapedTitle, +); +$dateTimeFormat2Use = $allDay ? $dateFormat : $IcsDateTimeFormat; +if ($allDay) { + $IcsUrlParts[] = 'DTSTART:' . $startDate->format($dateTimeFormat2Use); + $IcsUrlParts[] = 'DURATION:P1D'; +} else { + $IcsUrlParts[] = 'DTSTART;TZID=' . $startDate->format($dateTimeFormat2Use); + $IcsUrlParts[] = 'DTEND;TZID=' . $endDate->format($dateTimeFormat2Use); +} +if ($description) $IcsUrlParts[] = 'DESCRIPTION:' . addcslashes($description, "\r\n,;"); +if ($address) $IcsUrlParts[] = 'LOCATION:' . addcslashes($address, "\r\n,;"); +$IcsUrlParts[] = 'END:VEVENT'; +$IcsUrlParts[] = 'END:VCALENDAR'; +$IcsUrl = 'data:text/calendar;charset=utf8;base64,' . base64_encode(implode("\r\n", $IcsUrlParts)); + +function echoLink($url, $name, $filename = '') +{ + echo '' . $name . '' . PHP_EOL; +} + +?> + + + + + + + + Kaltura Job Application + + + + + + + + + +
+

Add the meeting to your calendar

+
+
    +
  •  
  • +
  •  
  • +
  •  
  • +
  •  
  • +
+
+
+ + + diff --git a/bookmeeting.php b/bookmeeting.php new file mode 100644 index 0000000..6288a84 --- /dev/null +++ b/bookmeeting.php @@ -0,0 +1,120 @@ +description; +} catch (Exception $e) { + $eventDescription = null; +} +if ( + $mediaEntry === null || $eventDescription === null || + $startDate === null || $startDateInput === false || $userks === false || + $entryId === false || $userDisplayName === false || $applicantEmail === false +) { + redirect_to_page('index.html'); +} + +// read more: https://github.com/kaltura-vpaas/virtual-meeting-rooms +// We'll create the video meeting experience room by creating: +// 1. scheduled resource (the room the virtual meeting will take place in) +// 2. scheduled event (indicating the date & time the meeting will happen) +// 3. scheduled event resource (an object mapping the two) + +$ksType = 2; //admin KS +$sessionStartRESTAPIUrl = 'https://cdnapisec.kaltura.com/api_v3/service/session/action/start/format/1/secret/' . $apiAdminSecret . '/partnerId/' . $partnerId . '/type/' . $ksType . '/expiry/' . $expire . '/userId/' . $applicantEmail . '/privileges/editadmintags:*,appid:' . $appName . '-' . $appDomain . ($privacyContext != null ? ',privacycontext:' . $privacyContext : ''); +$adminks = file_get_contents($sessionStartRESTAPIUrl); +$adminks = trim($adminks, '"'); + +// create the scheduled resource for the meeting: +$scheduledResourceUrl = 'https://www.kaltura.com/api_v3/service/schedule_scheduleresource/action/add/format/1?scheduleResource[objectType]=KalturaLocationScheduleResource'; +$scheduledResourceUrl .= '&scheduleResource[name]=' . urlencode('Meeting room for interview with ' . $userDisplayName); +$scheduledResourceUrl .= '&scheduleResource[description]=' . urlencode($eventDescription); +$scheduledResourceUrl .= '&scheduleResource[tags]=vcprovider:newrow,job-application'; +$scheduledResourceUrl .= '&scheduleResource[systemName]=job-application-' . $entryId; +$scheduledResourceUrl .= '&ks=' . $adminks; +try { + $scheduledResource = json_decode(file_get_contents($scheduledResourceUrl)); +} catch (Exception $e) { + var_dump($e); + exit(1); +} + +// create the event for the meeting: +$duration = 'PT1H'; // schedule the event for 1 hour (StartDate was given in the from param, EndDate will be 1 hour from the StartDate) +$durationInterval = new DateInterval($duration); +$endDate = (clone $startDate)->add($durationInterval); + +$scheduledEventUrl = 'https://www.kaltura.com/api_v3/service/schedule_scheduleevent/action/add/format/1?scheduleEvent[objectType]=KalturaRecordScheduleEvent&scheduleEvent[recurrenceType]=0'; +$scheduledEventUrl .= '&scheduleEvent[tags]=' . 'job-application,custom_rec_auto_start:1,custom_rs_show_participant:0,custom_rs_show_invite:0,custom_rs_show_chat:1,custom_rs_class_mode:virtual_classroom,custom_rs_show_chat_moderators:0,custom_rs_show_chat_questions:0,custom_rs_room_version:nr2'; +$scheduledEventUrl .= '&scheduleEvent[summary]=' . urlencode($eventDescription); +$scheduledEventUrl .= '&scheduleEvent[startDate]=' . $startDate->getTimestamp(); +$scheduledEventUrl .= '&scheduleEvent[endDate]=' . $endDate->getTimestamp(); +$scheduledEventUrl .= '&scheduleEvent[entryIds]=' . $entryId; //associate this event with the job applicant's video +$scheduledEventUrl .= '&ks=' . $adminks; +try { + $scheduledEvent = json_decode(file_get_contents($scheduledEventUrl)); +} catch (Exception $e) { + var_dump($e); + exit(1); +} + +// associate the resource (room) with the event +$scheduledEventResourceUrl = 'https://www.kaltura.com/api_v3/service/schedule_scheduleeventresource/action/add/format/1?scheduleEventResource[objectType]=KalturaScheduleEventResource'; +$scheduledEventResourceUrl .= '&scheduleEventResource[eventId]=' . $scheduledEvent->id; +$scheduledEventResourceUrl .= '&scheduleEventResource[resourceId]=' . $scheduledResource->id; +$scheduledEventResourceUrl .= '&ks=' . $adminks; +try { + $scheduledEventResource = json_decode(file_get_contents($scheduledEventResourceUrl)); +} catch (Exception $e) { + var_dump($e); + exit(1); +} + +// create the Kaltura Session for authenticated room participant +// to make sure we give the session enough time to live, we'll set it to when the meeting is supposed to end + 2 hours extra +$participantSessionEndDate = (clone $startDate)->add(new DateInterval('PT2H')); // add 2 hours to meeting end time +$sessionExpiry = $participantSessionEndDate->getTimestamp() - $startDate->getTimestamp(); //unixtimestamps are in seconds +$participantSessionPrivileges = "eventId:$scheduledEvent->id,role:viewerRole,userContextualRole:3,firstName:$userDisplayName"; +$eventParticipantKsUrl = 'https://cdnapisec.kaltura.com/api_v3/service/session/action/start/format/1/secret/' . $apiAdminSecret . '/partnerId/' . $partnerId . '/type/' . $sessionType . '/expiry/' . $sessionExpiry . '/userId/' . $applicantEmail . '/privileges/' . $participantSessionPrivileges; +$eventParticipantKs = file_get_contents($eventParticipantKsUrl); +$eventParticipantKs = trim($eventParticipantKs, '"'); + +// since the room link is rather lengthy with the secure session, we will create a shortlink for it +// construct the room link with the KS +$meetingRoomUrl = get_base_url() . '/meetingroom.php?ks=' . $eventParticipantKs; +// create the short link +$shortLinkAddUrl = 'https://www.kaltura.com/api_v3/service/shortlink_shortlink/action/add/format/1/?shortLink[objectType]=KalturaShortLink&shortLink[status]=2'; +$shortLinkAddUrl .= '&shortLink[fullUrl]=' . $meetingRoomUrl; +$shortLinkAddUrl .= '&shortLink[systemName]=' . 'roomshortlink' . $scheduledEvent->id; +$shortLinkAddUrl .= '&ks=' . $adminks; +try { + $shortLink = json_decode(file_get_contents($shortLinkAddUrl)); +} catch (Exception $e) { + var_dump($e); + exit(1); +} + +$participantRoomShortLink = urlencode($kalturaShortLinkBaseUrl . $shortLink->id); + +//redirect to the add2calendar page: +$desc = urlencode($eventDescription); +$addToCalendarLink = "add2calendar.php?title=$scheduledResource->name&desc=$desc&address=$participantRoomShortLink&allday=no&from=$scheduledEvent->startDate&to=$scheduledEvent->endDate"; +redirect_to_page($addToCalendarLink); +exit(1); diff --git a/config.php.template b/config.php.template new file mode 100644 index 0000000..af4c247 --- /dev/null +++ b/config.php.template @@ -0,0 +1,14 @@ + + + +Generated by IcoMoon + + + + + + + + + + \ No newline at end of file diff --git a/css/fonts/icomoon.ttf b/css/fonts/icomoon.ttf new file mode 100644 index 0000000..afe0e5f Binary files /dev/null and b/css/fonts/icomoon.ttf differ diff --git a/css/fonts/icomoon.woff b/css/fonts/icomoon.woff new file mode 100644 index 0000000..754f6f0 Binary files /dev/null and b/css/fonts/icomoon.woff differ diff --git a/css/images/fav_icon.png b/css/images/fav_icon.png new file mode 100644 index 0000000..2358a1a Binary files /dev/null and b/css/images/fav_icon.png differ diff --git a/css/theme-recruiter.css b/css/theme-recruiter.css new file mode 100644 index 0000000..a9122aa --- /dev/null +++ b/css/theme-recruiter.css @@ -0,0 +1,60 @@ +* { + box-sizing: border-box; +} +body { + background-color: #3498db; + padding: 50px; +} +.container { + margin: 20px auto; + padding: 20px 40px; + width: fit-content; + height: fit-content; + background-color: #fff; + border-radius: 5px; +} +h1 { + color: #777; + margin: 28px auto; + margin-bottom: 20px; + text-align: center; +} +form { + text-align: center; +} +input { + padding: 12px 0; + margin-bottom: 10px; + border-radius: 3px; + border: 2px solid transparent; + text-align: center; + width: 90%; + font-size: 16px; + transition: border 0.2s, background-color 0.2s; +} +form .field { + background-color: #ecf0f1; +} +form .field:focus { + border: 2px solid #3498db; +} +form .btn { + margin-top: 30px; + background-color: #3498db; + color: #fff; + line-height: 25px; + cursor: pointer; +} +form .btn:hover, +form .btn:active { + background-color: #1f78b4; + border: 2px solid #1f78b4; +} +.pass-link { + text-align: center; +} +.pass-link a:link, +.pass-link a:visited { + font-size: 12px; + color: #777; +} diff --git a/css/theme.css b/css/theme.css new file mode 100644 index 0000000..a3be880 --- /dev/null +++ b/css/theme.css @@ -0,0 +1,623 @@ +a, +abbr, +acronym, +address, +area, +b, +big, +blockquote, +body, +br, +button, +caption, +cite, +code, +col, +colgroup, +dd, +del, +dfn, +div, +dl, +dt, +em, +fieldset, +form, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +html, +i, +img, +input, +ins, +label, +legend, +li, +map, +object, +ol, +option, +p, +param, +pre, +q, +samp, +select, +small, +span, +strong, +sub, +table, +tbody, +textarea, +tfoot, +thead, +tr, +tt, +ul, +var { + padding: 0; + margin: 0; + border: none; +} +a, +abbr, +acronym, +address, +applet, +aside, +b, +big, +blockquote, +body, +caption, +center, +cite, +code, +dd, +del, +dfn, +div, +dl, +dt, +em, +fieldset, +figcaption, +figure, +font, +footer, +form, +h1, +h2, +h3, +h4, +h5, +h6, +header, +html, +i, +iframe, +img, +input, +ins, +kbd, +label, +legend, +li, +object, +ol, +p, +pre, +q, +s, +samp, +section, +small, +span, +strike, +strong, +sub, +table, +tbody, +td, +tfoot, +th, +thead, +tr, +tt, +u, +ul, +var { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +ol, +ul { + list-style: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +button, +input[type="button"], +input[type="submit"] { + cursor: pointer; +} +button { + border: 0; +} +:focus { + outline: none; +} +button::-moz-focus-inner, +input[type="button"]::-moz-focus-inner, +input[type="file"] > input[type="button"]::-moz-focus-inner, +input[type="reset"]::-moz-focus-inner, +input[type="submit"]::-moz-focus-inner { + border: none; + outline: none; +} +input::-ms-clear { + width: 0; + height: 0; +} +input, +textarea { + resize: none; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} +::-ms-clear, +::-ms-reveal { + width: 0; + height: 0; + display: none; +} +a { + text-decoration: none; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +input, +select, +textarea { + border-radius: 0; + box-shadow: none; +} +:disabled, +[aria-disabled="true"], +[disabled] { + cursor: not-allowed; +} +video { + background-size: cover; + background-position: 50%; +} +img { + display: block; +} +button { + overflow: visible; +} +button, +textarea { + background: transparent; +} +textarea { + overflow-y: auto; +} +button, +input, +select, +textarea { + -webkit-appearance: none; + border-radius: 0; +} +[type="reset"] { + cursor: pointer; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Hairline.woff) format("woff"), + url(./fonts/CentraNo1-Hairline.ttf) format("truetype"); + font-weight: 100; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Thin.woff) format("woff"), + url(./fonts/CentraNo1-Thin.ttf) format("truetype"); + font-weight: 200; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Light.woff) format("woff"), + url(./fonts/CentraNo1-Light.ttf) format("truetype"); + font-weight: 300; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Book.woff) format("woff"), + url(./fonts/CentraNo1-Book.ttf) format("truetype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Medium.woff) format("woff"), + url(./fonts/CentraNo1-Medium.ttf) format("truetype"); + font-weight: 500; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: CentraNo1; + src: url(./fonts/CentraNo1-Bold.woff) format("woff"), + url(./fonts/CentraNo1-Bold.ttf) format("truetype"); + font-weight: 600; + font-style: normal; + font-display: swap; +} +body, +button, +html, +input, +select, +textarea { + font-family: Source Sans Pro, sans-serif; + font-weight: 400; +} +body, +html { + font-size: 62.5%; +} +img[data-object-fit="contain"] { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + -o-object-fit: contain; + object-fit: contain; +} +img[data-object-fit="cover"] { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + -o-object-fit: cover; + object-fit: cover; +} +.main { + padding: 16px; + text-align: center; + width: 70%; + margin: 0 auto; + padding: 20px; + display: block; + font-family: CentraNo1, Arial, sans-serif; + font-size: 16px; +} +h1, +h2 { + font-size: 58px; + line-height: 1.14; + margin-bottom: 0.4em; + font-family: CentraNo1, Arial, sans-serif; + font-weight: 600; + margin-bottom: 0.6em; + transition: color 0.2s linear; +} +h2 { + font-size: 36px; +} +p, +ol, +ul { + display: block; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-size: 22px; + font-weight: 400; +} +p.left-align-block { + text-align: left; + max-width: 400px; + display: block; + margin: 0 auto; +} +ol, +ul { + padding-left: 24px; +} +section { + max-width: 70%; + margin: 0 auto; + padding: 60px 0; +} +input.submitbtn { + background-color: #006efa; + border-color: #006efa; + color: #fff; + text-align: center; + font-family: CentraNo1, Arial, sans-serif; + font-size: 16px; + font-weight: 600; + border-radius: 6px; + border-style: solid; + border-width: 2px; + padding: 14px 25px; + line-height: normal; + margin: 2em 1em; + min-width: 166px; + display: inline-block; + text-transform: none; + border: 1px solid; + transition: background-color 0.2s ease-out, border-color 0.2s ease-out, + color 0.2s ease-out; +} +input.submitbtn:hover { + background-color: #1b4a97; + border-color: #1b4a97; +} +input[type="text"], +input[type="email"], +input[type="url"], +input[type="password"], +input[type="tel"] { + display: block; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-size: 22px; + font-weight: 400; + width: 100%; + border-radius: 6px; + border: 1px solid gray; + padding: 0.5em; +} +input[type="checkbox"] { + -webkit-appearance: checkbox !important; + -moz-appearance: checkbox !important; + -ms-appearance: checkbox !important; + -o-appearance: checkbox !important; + appearance: checkbox !important; + font-size: 22px; + font-weight: 400; + width: 1em; + height: 1em; + border-radius: 6px; + border: 1px solid gray; +} +label { + font-size: 22px; + font-weight: 400; + width: 100%; + padding-left: 0.5em; + vertical-align: text-bottom; +} +.has-success .form-control { + border-bottom: 2px solid #168b3f; + border-radius: 10px; +} +.has-danger .form-control { + border-bottom: 2px solid #dc1d34; + border-radius: 10px; +} +.form-group .text-help { + color: #dc1d34; +} +.inline-label .pristine-error { + display: inline; +} +.pristine-error { + display: table; + font-size: 12px; + padding: 0; + margin: 0; +} +.ss-main { + width: 103%; +} +.ss-main .ss-multi-selected { + font-size: 22px; + font-weight: 400; + width: 100%; + border-radius: 6px; + border: 1px solid gray; + padding: 7px; +} +.ss-content .ss-list .ss-optgroup .ss-option { + font-size: 14px; +} +.ss-content .ss-list .ss-optgroup .ss-optgroup-label { + font-size: 14px; +} +.ss-main .ss-multi-selected .ss-values .ss-value { + font-size: 22px; +} +.ss-main .ss-multi-selected .ss-values { + width: 100%; +} +.ss-main .ss-multi-selected .ss-values .ss-disabled { + line-height: unset; +} +@font-face { + font-family: "icomoon"; + src: url("fonts/icomoon.eot?jjem8c"); + src: url("fonts/icomoon.eot?jjem8c#iefix") format("embedded-opentype"), + url("fonts/icomoon.ttf?jjem8c") format("truetype"), + url("fonts/icomoon.woff?jjem8c") format("woff"), + url("fonts/icomoon.svg?jjem8c#icomoon") format("svg"); + font-weight: normal; + font-style: normal; + font-display: block; +} +[class^="icon-"], +[class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "icomoon" !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + padding-right: 10px; +} +.icon-calendar:before { + content: "\e903"; +} +.icon-google:before { + content: "\e901"; + color: #4285f4; +} +.icon-microsoftoutlook:before { + content: "\e902"; + color: #0072c6; +} +.icon-yahoo:before { + content: "\e900"; + color: #6001d2; +} +.add2calendar ul { + width: fit-content; + margin: 0 auto; + text-align: left; +} +.add2calendar ul li { + padding: 0 10px 10px 0; +} +.kaltura-player-embed { + position: absolute; + top: 0; + left: 0; + left: 0; + right: 0; + bottom: 0; + border: none; +} +.embed-ratio { + margin-top: 56.25%; +} +.kaltura-player-embed-wrap { + width: 60%; + display: inline-block; + position: relative; +} +.well { + min-height: 20px; + margin-bottom: 20px; +} +.embed-wrap { + border-color: #d9d9d9; + border-radius: 6px; +} +.recorder { + border-radius: 4px; + width: 858px; + height: 483px; + margin: 0 auto; +} +@media (max-width: 979px) { + .recorder { + max-width: 676px; + width: 100%; + height: 380px; + } +} +.responsive-iframe-container { + position: relative; + overflow: hidden; + width: 100%; + padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */ +} +/* Then style the iframe to fit in the container div with full height and width */ +.responsive-iframe { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; +} +table { + width: 100%; + table-layout: fixed; + vertical-align: top; +} +th { + border-left: none; + padding-left: 0; + text-align: left; + padding: 12px 20px; + vertical-align: top; +} +tr { + border-bottom: 1px solid #d9d9d9; + vertical-align: top; +} +td { + border-left: none; + padding-left: 0; + text-align: left; + padding: 12px 20px; + vertical-align: top; +} +div.thumb { + cursor: pointer; +} diff --git a/edit.php b/edit.php new file mode 100644 index 0000000..20f191c --- /dev/null +++ b/edit.php @@ -0,0 +1,52 @@ + + + + + + + + + Kaltura Edit Your Job Application Video + + + + + + + + + + + + +
+

Kaltura Video Job Application

+

You can use the editor below to trim your video or add hotspots with links to important references you'd like to highlight for us.

+

Next step: Schedule a 1 hour interview meeting.

+
+ +
+
+ + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..cd94d8b --- /dev/null +++ b/index.html @@ -0,0 +1,109 @@ + + + + + + + + Kaltura Job Application + + + + + + + + + + + + + + + + + + +
+

Kaltura Video Job Application

+

To apply for a job at Kaltura, please fill the details below, and click next to start recording your application.

+

You can review all available positions on our comeet open positions list.

+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+ + + + + diff --git a/js/array2table.js b/js/array2table.js new file mode 100644 index 0000000..e52e7d6 --- /dev/null +++ b/js/array2table.js @@ -0,0 +1,19 @@ +function renderTableHeader(data) { + const header = Object.keys(data[0]); + const head = header.map((key, index) => `${key}`); + return head.join(''); +} + +function renderTableRows(data) { + const keys = Object.keys(data[0]); + const table = data.map((row, index) => { + return ( + ` + ${keys.map((key, index) => ( + `${row[key]}` + )).join('')} + ` + ); + }); + return table.join(''); +} diff --git a/js/fetch-comeetjobs.js b/js/fetch-comeetjobs.js new file mode 100644 index 0000000..a053653 --- /dev/null +++ b/js/fetch-comeetjobs.js @@ -0,0 +1,49 @@ +populateJobPositionsSelect = function () { + const getComeetJobsListUrl = + "https://www.comeet.co/careers-api/2.0/company/E2.00D/positions?token=2EDEA12ED017688C7147B1A555DA2ED&details=false"; + fetch(getComeetJobsListUrl, { + method: "GET", + }) + .then(function (response) { + // The API call was successful! + if (response.ok) { + return response.json(); + } else { + return Promise.reject(response); + } + }) + .then(function (data) { + // This is the JSON from our response + populateJobPositionsJson(data); + }) + .catch(function (err) { + // There was an error + console.log("Something went wrong.", err); + }); + + var populateJobPositionsJson = function (data) { + //console.log(data); + var dropdown = document.getElementById("jobpos"); + + var jobs = []; + for (var i = 0; i < data.length; i++) { + if (jobs[data[i].department] == undefined) jobs[data[i].department] = []; + var option = document.createElement("option"); + option.text = data[i].name; + option.value = data[i].uid; + jobs[data[i].department].push(option); + } + for (const [key, depJobs] of Object.entries(jobs)) { + var optGroup = document.createElement("optgroup"); + optGroup.label = key; + dropdown.appendChild(optGroup); + for (var i = 0; i < depJobs.length; i++) { + optGroup.appendChild(depJobs[i]); + } + } + + new SlimSelect({ + select: "#jobpos", + }); + }; +}; diff --git a/js/kaltura-editor.js b/js/kaltura-editor.js new file mode 100644 index 0000000..11665ee --- /dev/null +++ b/js/kaltura-editor.js @@ -0,0 +1,284 @@ +function getInitParams( + serviceUrl, + partnerId, + ks, + entryId, + uiConfId, + userDisplayName +) { + return { + messageType: "kea-config", + data: { + /* URL of the Kaltura Server to use */ + service_url: serviceUrl, + /* The Kaltura Account ID the entry we will edit belongs to */ + partner_id: partnerId, + /* The ID the entry we will edit */ + entry_id: entryId, + /* The Kaltura Session to use when editing this entry */ + ks: ks, + /* The Kaltura Session to use when previewing the edited entry */ + preview_ks: ks, + /* id of the player (Universal Studio v2 version player) uiconf to be used in the editor */ + player_uiconf_id: uiConfId, + /* id of the player (Universal Studio v2 version player) uiconf to be used when previewing */ + preview_player_uiconf_id: uiConfId, + /* Boolean; pass true if a KS must be appended to the thumbnails url for access control profile requirements */ + load_thumbnail_with_ks: true, + /* Show a nicer user name (instead of user ID): */ + user_dispaly_name: userDisplayName, + /* language - used by priority: + * 1. Custom locale (locale_url) + * full url of a json file with translations + * 2. Locale code (language_code, one of: + * de, en, es, fr, ja, nl, pt-br, ru, zh-Hans, zh-Hant + * 3. English default locale (fallback). */ + language_code: "en", + //'locale_url': 'URL_OF_CUSTOM_LANGUAGE_FILE', see public/locale_en.json as example + /* URL to be used for "Go to User Manual" in the editor help component */ + help_link: "https://knowledge.kaltura.com/node/1912", + /* the initial tab to load the editor app on (editor, quiz, advertisements or hotspots) */ + tab: "editor", + /* tabs to show in navigation */ + tabs: { + edit: { + name: "edit", + permissions: ["trim"], + userPermissions: ["trim"], + showOnlyExpandedView: true, + //preActivateMessage: 'optional: message to show before activating the tab', + //preSaveMessage: 'optional: message to show before trimming (Save)', + //preSaveAsMessage: 'optional: message to show before clipping (Save As)', + showSaveButton: true, + showSaveAsButton: false, + }, + /*quiz: { + name: "quiz", + permissions: ["quiz", "questions-v2", "questions-v3"], + userPermissions: ["quiz"], + }, + advertisements: { + name: "advertisements", + permissions: ["CUEPOINT_MANAGE", "FEATURE_DISABLE_KMC_KDP_ALERTS"], + showSaveButton: false, + },*/ + hotspots: { + name: "hotspots", + showSaveButton: true, + }, + }, + /* URL of an additional css file to load */ + //'css_url': 'YOUR_ADDITIONAL_CSS_URL', + /* URL to redirect to when the user wishes to leave the editor */ + //'exit_link': "URL TO NAVIGATE TO WHEN EXITING THE APP.", + /* Should the editor hide the navigation bar (the sidebar holding the tabs icons)? */ + hide_navigation_bar: false, + /* Should the editor hide the "go to media" button after quiz creation? */ + hide_quiz_goto_media_button: true, + /* When creating a new quiz, should the editor skip "start" screen and create the new quiz entry automatically upon entering the tab? */ + skip_quiz_start_page: false, + }, + }; +} + +// Initialize the kaltura editor app communication: +var initParamsListener = window.addEventListener("message", function (e) { + // validate postMessage recieved - + var postMessageData; + try { + postMessageData = e.data; + } catch (ex) { + return; + } + + /* This is the initialization request for init params, + * should return a message where messageType = kea-config along with the initialization params (see above) */ + if (postMessageData.messageType === "kea-bootstrap") { + e.source.postMessage(keaInitParams, e.origin); + } + + // attach to the external Save button click: + /*saveBtn.addEventListener("click", function () { + // execute the Save command: + e.source.postMessage({ messageType: "kea-do-save" }, e.origin); + }); + + // attach to the external Save As button click: + saveAsBtn.addEventListener("click", function () { + // execute the Save As command: + // i.e. Start the clipping process (will prompt user) + // And pass optional reference id param to keep reference to the external hosting app entry id. + // useful for cases where the hosting app was closed while clipping. + e.source.postMessage( + { + messageType: "kea-do-save-as", + data: { referenceId: referenceId }, + }, + e.origin + ); + }); +*/ + /* The video editing tab was loaded (clip/trim functionality) + * enable the save and save as buttons: + */ + if (postMessageData.messageType === "kea-editor-tab-loaded") { + //saveAsBtn.removeAttribute("disabled"); + //saveBtn.removeAttribute("disabled"); + } + + // the quiz tab was enabled - disable the external buttons: + if (postMessageData.messageType === "kea-quiz-tab-loaded") { + //saveAsBtn.setAttribute("disabled", true); + //saveBtn.setAttribute("disabled", true); + } + + // the ads or hotspos tabs were enabled, enable only external save button (no save as option): + if ( + postMessageData.messageType === "kea-advertisements-tab-loaded" || + postMessageData.messageType === "kea-hotspots-tab-loaded" + ) { + //saveAsBtn.setAttribute("disabled", true); + //saveBtn.removeAttribute("disabled"); + } + + /* received when a trim action was requested. + * message.data = {entryId} + * should return a message where message.messageType = kea-trim-message + * and message.data is the (localized) text to show the user. + */ + if (postMessageData.messageType === "kea-trimming-started") { + e.source.postMessage( + { + messageType: "kea-trim-message", + data: + "You must approve the media replacement in order to be able to watch the trimmed media", + }, + e.origin + ); + } + + /* received when a trim action is complete. + * message.data = {entryId} + * can be used to clear app cache, for example. + */ + if (postMessageData.messageType === "kea-trimming-done") { + console.log( + "processing of entry with id " + message.data.entryId + " is complete" + ); + } + + /* received when a clip was created. + * postMessageData.data: { + * originalEntryId, + * newEntryId, + * newEntryName + * } + * should return a message where message.messageType = kea-clip-message, + * and message.data is the (localized) text to show the user. + * */ + if (postMessageData.messageType === "kea-clip-created") { + // send a message to editor app which will show up after clip has been created: + var message = + "Thank you for creating a new clip, the new entry ID is: " + + postMessageData.data.newEntryId; + e.source.postMessage( + { + messageType: "kea-clip-message", + data: message, + }, + e.origin + ); + } + + /* request for a KS to pass to the preview player + * message.data = entryId + * may return { + * messageType: kea-preview-ks + * data: ks + * } + * don't include user name or add sview privilege, for example. + * if not provided, the main KS will be used */ + if (postMessageData.messageType === "kea-get-preview-ks") { + e.source.postMessage( + { + messageType: "kea-preview-ks", + data: ks, + }, + e.origin + ); + } + + /* request for a KS. required for entitlements and access control when "switching" entries + (currently when creating a new quiz only). + * message.data = entryId + * may return { + * messageType: kea-ks + * data: ks + * } + * if not provided, the main KS will be used */ + if (postMessageData.messageType === "kea-get-ks") { + e.source.postMessage( + { + messageType: "kea-ks", + data: ks, + }, + e.origin + ); + } + + /* request for user display name. + * message.data = {userId} + * the hosting app can get the userId from: postMessageData.data.userId and then return corresponding display name + * should return a message {messageType:kea-display-name, data: display name} + */ + if (postMessageData.messageType === "kea-get-display-name") { + // in this sample we've simplified and used the config file to set the display name. + var displayName = userDisplayName; + e.source.postMessage( + { + messageType: "kea-display-name", + data: displayName, + }, + e.origin + ); + } + + /* + * Fired when saving quiz's settings. + * message.data = {entryId} + */ + if (postMessageData.messageType === "kea-quiz-updated") { + // do whatever, you can invalidate cache, etc.. + } + + /* + * Fired when creating a new quiz + * message.data = {entryId} + */ + if (postMessageData.messageType === "kea-quiz-created") { + // do whatever, you can invalidate cache, etc.. + } + + /* + * Fired when modifying advertisements (save not performed yet). + * message.data = {entryId} + */ + if (postMessageData.messageType === "kea-advertisements-modified") { + // do whatever, you can invalidate cache, etc.. + } + + /* + * Fired when saving advertisements + * message.data = {entryId} + */ + if (postMessageData.messageType === "kea-advertisements-saved") { + // do whatever, you can invalidate cache, etc.. + } + + /* received when user clicks the "go to media" button after quiz was created/edited + * message.data = entryId + * host should navigate to a page displaying the relevant media */ + if (postMessageData.messageType === "kea-go-to-media") { + console.log("Hosting app should redirect to: " + postMessageData.data); + } +}); // END OF initParamsListener diff --git a/js/modernizer.js b/js/modernizer.js new file mode 100644 index 0000000..66290dd --- /dev/null +++ b/js/modernizer.js @@ -0,0 +1,1471 @@ +/*! + * modernizr v3.6.0 + * Build https://modernizr.com/download?-borderradius-boxsizing-classlist-es6number-es6object-es6string-fetch-flexbox-formvalidation-getusermedia-postmessage-promises-queryselector-requestanimationframe-video-videocrossorigin-videoloop-videopreload-dontmin + * + * Copyright (c) + * Faruk Ates + * Paul Irish + * Alex Sexton + * Ryan Seddon + * Patrick Kettner + * Stu Cox + * Richard Herrera + + * MIT License + */ + +/* + * Modernizr tests which native CSS3 and HTML5 features are available in the + * current UA and makes the results available to you in two ways: as properties on + * a global `Modernizr` object, and as classes on the `` element. This + * information allows you to progressively enhance your pages with a granular level + * of control over the experience. + */ + +(function (window, document, undefined) { + var classes = []; + + var tests = []; + + /** + * + * ModernizrProto is the constructor for Modernizr + * + * @class + * @access public + */ + + var ModernizrProto = { + // The current version, dummy + _version: "3.6.0", + + // Any settings that don't work as separate modules + // can go in here as configuration. + _config: { + classPrefix: "", + enableClasses: true, + enableJSClass: true, + usePrefixes: true, + }, + + // Queue of tests + _q: [], + + // Stub these for people who are listening + on: function (test, cb) { + // I don't really think people should do this, but we can + // safe guard it a bit. + // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. + // This is in case people listen to synchronous tests. I would leave it out, + // but the code to *disallow* sync tests in the real version of this + // function is actually larger than this. + var self = this; + setTimeout(function () { + cb(self[test]); + }, 0); + }, + + addTest: function (name, fn, options) { + tests.push({ name: name, fn: fn, options: options }); + }, + + addAsyncTest: function (fn) { + tests.push({ name: null, fn: fn }); + }, + }; + + // Fake some of Object.create so we can force non test results to be non "own" properties. + var Modernizr = function () {}; + Modernizr.prototype = ModernizrProto; + + // Leak modernizr globally when you `require` it rather than force it here. + // Overwrite name so constructor name is nicer :D + Modernizr = new Modernizr(); + + /*! + { + "name": "postMessage", + "property": "postmessage", + "caniuse": "x-doc-messaging", + "notes": [{ + "name": "W3C Spec", + "href": "http://www.w3.org/TR/html5/comms.html#posting-messages" + }], + "polyfills": ["easyxdm", "postmessage-jquery"] + } + !*/ + /* DOC + Detects support for the `window.postMessage` protocol for cross-document messaging. + */ + + Modernizr.addTest("postmessage", "postMessage" in window); + + /*! + { + "name": "QuerySelector", + "property": "queryselector", + "caniuse": "queryselector", + "tags": ["queryselector"], + "authors": ["Andrew Betts (@triblondon)"], + "notes": [{ + "name" : "W3C Selectors reference", + "href": "https://www.w3.org/TR/selectors-api/#queryselectorall" + }], + "polyfills": ["css-selector-engine"] + } + !*/ + /* DOC + Detects support for querySelector. + */ + + Modernizr.addTest( + "queryselector", + "querySelector" in document && "querySelectorAll" in document + ); + + /*! + { + "name": "ES6 Number", + "property": "es6number", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://people.mozilla.org/~jorendorff/es6-draft.html" + }], + "polyfills": ["es6shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] + } + !*/ + /* DOC + Check if browser implements ECMAScript 6 Number per specification. + */ + + Modernizr.addTest( + "es6number", + !!( + Number.isFinite && + Number.isInteger && + Number.isSafeInteger && + Number.isNaN && + Number.parseInt && + Number.parseFloat && + Number.isInteger(Number.MAX_SAFE_INTEGER) && + Number.isInteger(Number.MIN_SAFE_INTEGER) && + Number.isFinite(Number.EPSILON) + ) + ); + + /*! + { + "name": "ES6 Object", + "property": "es6object", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://people.mozilla.org/~jorendorff/es6-draft.html" + }], + "polyfills": ["es6shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] + } + !*/ + /* DOC + Check if browser implements ECMAScript 6 Object per specification. + */ + + Modernizr.addTest( + "es6object", + !!(Object.assign && Object.is && Object.setPrototypeOf) + ); + + /*! + { + "name": "ES6 Promises", + "property": "promises", + "caniuse": "promises", + "polyfills": ["es6promises"], + "authors": ["Krister Kari", "Jake Archibald"], + "tags": ["es6"], + "notes": [{ + "name": "The ES6 promises spec", + "href": "https://github.com/domenic/promises-unwrapping" + },{ + "name": "Chromium dashboard - ES6 Promises", + "href": "https://www.chromestatus.com/features/5681726336532480" + },{ + "name": "JavaScript Promises: There and back again - HTML5 Rocks", + "href": "http://www.html5rocks.com/en/tutorials/es6/promises/" + }] + } + !*/ + /* DOC + Check if browser implements ECMAScript 6 Promises per specification. + */ + + Modernizr.addTest("promises", function () { + return ( + "Promise" in window && + // Some of these methods are missing from + // Firefox/Chrome experimental implementations + "resolve" in window.Promise && + "reject" in window.Promise && + "all" in window.Promise && + "race" in window.Promise && + // Older version of the spec had a resolver object + // as the arg rather than a function + (function () { + var resolve; + new window.Promise(function (r) { + resolve = r; + }); + return typeof resolve === "function"; + })() + ); + }); + + /*! + { + "name": "ES6 String", + "property": "es6string", + "notes": [{ + "name": "unofficial ECMAScript 6 draft specification", + "href": "https://people.mozilla.org/~jorendorff/es6-draft.html" + }], + "polyfills": ["es6shim"], + "authors": ["Ron Waldon (@jokeyrhyme)"], + "warnings": ["ECMAScript 6 is still a only a draft, so this detect may not match the final specification or implementations."], + "tags": ["es6"] + } + !*/ + /* DOC + Check if browser implements ECMAScript 6 String per specification. + */ + + Modernizr.addTest( + "es6string", + !!( + String.fromCodePoint && + String.raw && + String.prototype.codePointAt && + String.prototype.repeat && + String.prototype.startsWith && + String.prototype.endsWith && + String.prototype.includes + ) + ); + + /*! + { + "name": "Fetch API", + "property": "fetch", + "tags": ["network"], + "caniuse": "fetch", + "notes": [{ + "name": "Fetch Living Standard", + "href": "https://fetch.spec.whatwg.org/" + }], + "polyfills": ["fetch"] + } + !*/ + /* DOC + Detects support for the fetch API, a modern replacement for XMLHttpRequest. + */ + + Modernizr.addTest("fetch", "fetch" in window); + + /*! + { + "name": "getUserMedia", + "property": "getusermedia", + "caniuse": "stream", + "tags": ["webrtc"], + "authors": ["Eric Bidelman", "Masataka Yakura"], + "notes": [{ + "name": "W3C Media Capture and Streams spec", + "href": "http://w3c.github.io/mediacapture-main/#dom-mediadevices-getusermedia" + }] + } + !*/ + /* DOC + Detects support for the new Promise-based `getUserMedia` API. + */ + + Modernizr.addTest( + "getUserMedia", + "mediaDevices" in navigator && "getUserMedia" in navigator.mediaDevices + ); + + /** + * is returns a boolean if the typeof an obj is exactly type. + * + * @access private + * @function is + * @param {*} obj - A thing we want to check the type of + * @param {string} type - A string to compare the typeof against + * @returns {boolean} + */ + + function is(obj, type) { + return typeof obj === type; + } + /** + * Run through all tests and detect their support in the current UA. + * + * @access private + */ + + function testRunner() { + var featureNames; + var feature; + var aliasIdx; + var result; + var nameIdx; + var featureName; + var featureNameSplit; + + for (var featureIdx in tests) { + if (tests.hasOwnProperty(featureIdx)) { + featureNames = []; + feature = tests[featureIdx]; + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + // + // If there is no name, it's an 'async' test that is run, + // but not directly added to the object. That should + // be done with a post-run addTest call. + if (feature.name) { + featureNames.push(feature.name.toLowerCase()); + + if ( + feature.options && + feature.options.aliases && + feature.options.aliases.length + ) { + // Add all the aliases into the names list + for ( + aliasIdx = 0; + aliasIdx < feature.options.aliases.length; + aliasIdx++ + ) { + featureNames.push( + feature.options.aliases[aliasIdx].toLowerCase() + ); + } + } + } + + // Run the test, or use the raw value if it's not a function + result = is(feature.fn, "function") ? feature.fn() : feature.fn; + + // Set each of the names on the Modernizr object + for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { + featureName = featureNames[nameIdx]; + // Support dot properties as sub tests. We don't do checking to make sure + // that the implied parent tests have been added. You must call them in + // order (either in the test, or make the parent test a dependency). + // + // Cap it to TWO to make the logic simple and because who needs that kind of subtesting + // hashtag famous last words + featureNameSplit = featureName.split("."); + + if (featureNameSplit.length === 1) { + Modernizr[featureNameSplit[0]] = result; + } else { + // cast to a Boolean, if not one already + if ( + Modernizr[featureNameSplit[0]] && + !(Modernizr[featureNameSplit[0]] instanceof Boolean) + ) { + Modernizr[featureNameSplit[0]] = new Boolean( + Modernizr[featureNameSplit[0]] + ); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; + } + + classes.push((result ? "" : "no-") + featureNameSplit.join("-")); + } + } + } + } + /** + * docElement is a convenience wrapper to grab the root element of the document + * + * @access private + * @returns {HTMLElement|SVGElement} The root element of the document + */ + + var docElement = document.documentElement; + + /*! + { + "name": "classList", + "caniuse": "classlist", + "property": "classlist", + "tags": ["dom"], + "builderAliases": ["dataview_api"], + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en/DOM/element.classList" + }] + } + !*/ + + Modernizr.addTest("classlist", "classList" in docElement); + + /** + * cssToDOM takes a kebab-case string and converts it to camelCase + * e.g. box-sizing -> boxSizing + * + * @access private + * @function cssToDOM + * @param {string} name - String name of kebab-case prop we want to convert + * @returns {string} The camelCase version of the supplied name + */ + + function cssToDOM(name) { + return name + .replace(/([a-z])-([a-z])/g, function (str, m1, m2) { + return m1 + m2.toUpperCase(); + }) + .replace(/^-/, ""); + } + /** + * A convenience helper to check if the document we are running in is an SVG document + * + * @access private + * @returns {boolean} + */ + + var isSVG = docElement.nodeName.toLowerCase() === "svg"; + + /** + * createElement is a convenience wrapper around document.createElement. Since we + * use createElement all over the place, this allows for (slightly) smaller code + * as well as abstracting away issues with creating elements in contexts other than + * HTML documents (e.g. SVG documents). + * + * @access private + * @function createElement + * @returns {HTMLElement|SVGElement} An HTML or SVG element + */ + + function createElement() { + if (typeof document.createElement !== "function") { + // This is the case in IE7, where the type of createElement is "object". + // For this reason, we cannot call apply() as Object is not a Function. + return document.createElement(arguments[0]); + } else if (isSVG) { + return document.createElementNS.call( + document, + "http://www.w3.org/2000/svg", + arguments[0] + ); + } else { + return document.createElement.apply(document, arguments); + } + } + + /*! + { + "name": "HTML5 Video", + "property": "video", + "caniuse": "video", + "tags": ["html5"], + "knownBugs": [ + "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546" + ], + "polyfills": [ + "html5media", + "mediaelementjs", + "sublimevideo", + "videojs", + "leanbackplayer", + "videoforeverybody" + ] + } + !*/ + /* DOC + Detects support for the video element, as well as testing what types of content it supports. + + Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.: + + ```javascript + Modernizr.video // true + Modernizr.video.ogg // 'probably' + ``` + */ + + // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 + // thx to NielsLeenheer and zcorpan + + // Note: in some older browsers, "no" was a return value instead of empty string. + // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 + // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 + + Modernizr.addTest("video", function () { + var elem = createElement("video"); + var bool = false; + + // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 + try { + bool = !!elem.canPlayType; + if (bool) { + bool = new Boolean(bool); + bool.ogg = elem + .canPlayType('video/ogg; codecs="theora"') + .replace(/^no$/, ""); + + // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 + bool.h264 = elem + .canPlayType('video/mp4; codecs="avc1.42E01E"') + .replace(/^no$/, ""); + + bool.webm = elem + .canPlayType('video/webm; codecs="vp8, vorbis"') + .replace(/^no$/, ""); + + bool.vp9 = elem + .canPlayType('video/webm; codecs="vp9"') + .replace(/^no$/, ""); + + bool.hls = elem + .canPlayType('application/x-mpegURL; codecs="avc1.42E01E"') + .replace(/^no$/, ""); + } + } catch (e) {} + + return bool; + }); + + /*! + { + "name": "Video crossOrigin", + "property": "videocrossorigin", + "caniuse": "cors", + "authors": ["Florian Mailliet"], + "notes": [{ + "name": "MDN documentation", + "href": "https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes" + }] + } + !*/ + /* DOC + Detects support for the crossOrigin attribute on video tag + */ + + Modernizr.addTest( + "videocrossorigin", + "crossOrigin" in createElement("video") + ); + + /*! + { + "name": "Video Loop Attribute", + "property": "videoloop", + "tags": ["video", "media"] + } + !*/ + + Modernizr.addTest("videoloop", "loop" in createElement("video")); + + /*! + { + "name": "Video Preload Attribute", + "property": "videopreload", + "tags": ["video", "media"] + } + !*/ + + Modernizr.addTest("videopreload", "preload" in createElement("video")); + + /** + * getBody returns the body of a document, or an element that can stand in for + * the body if a real body does not exist + * + * @access private + * @function getBody + * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an + * artificially created element that stands in for the body + */ + + function getBody() { + // After page load injecting a fake body doesn't work so check if body exists + var body = document.body; + + if (!body) { + // Can't use the real body create a fake one. + body = createElement(isSVG ? "svg" : "body"); + body.fake = true; + } + + return body; + } + + /** + * injectElementWithStyles injects an element with style element and some CSS rules + * + * @access private + * @function injectElementWithStyles + * @param {string} rule - String representing a css rule + * @param {function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + */ + + function injectElementWithStyles(rule, callback, nodes, testnames) { + var mod = "modernizr"; + var style; + var ret; + var node; + var docOverflow; + var div = createElement("div"); + var body = getBody(); + + if (parseInt(nodes, 10)) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while (nodes--) { + node = createElement("div"); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + style = createElement("style"); + style.type = "text/css"; + style.id = "s" + mod; + + // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (!body.fake ? div : body).appendChild(style); + body.appendChild(div); + + if (style.styleSheet) { + style.styleSheet.cssText = rule; + } else { + style.appendChild(document.createTextNode(rule)); + } + div.id = mod; + + if (body.fake) { + //avoid crashing IE8, if background image is used + body.style.background = ""; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + body.style.overflow = "hidden"; + docOverflow = docElement.style.overflow; + docElement.style.overflow = "hidden"; + docElement.appendChild(body); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if (body.fake) { + body.parentNode.removeChild(body); + docElement.style.overflow = docOverflow; + // Trigger layout so kinetic scrolling isn't disabled in iOS6+ + // eslint-disable-next-line + docElement.offsetHeight; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + } + + /** + * testStyles injects an element with style element and some CSS rules + * + * @memberof Modernizr + * @name Modernizr.testStyles + * @optionName Modernizr.testStyles() + * @optionProp testStyles + * @access public + * @function testStyles + * @param {string} rule - String representing a css rule + * @param {function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + * @example + * + * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page + * along with (possibly multiple) DOM elements. This lets you check for features + * that can not be detected by simply checking the [IDL](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Interface_development_guide/IDL_interface_rules). + * + * ```js + * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { + * // elem is the first DOM node in the page (by default #modernizr) + * // rule is the first argument you supplied - the CSS rule in string form + * + * addTest('widthworks', elem.style.width === '9px') + * }); + * ``` + * + * If your test requires multiple nodes, you can include a third argument + * indicating how many additional div elements to include on the page. The + * additional nodes are injected as children of the `elem` that is returned as + * the first argument to the callback. + * + * ```js + * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { + * document.getElementById('modernizr').style.width === '1px'; // true + * document.getElementById('modernizr2').style.width === '2px'; // true + * elem.firstChild === document.getElementById('modernizr2'); // true + * }, 1); + * ``` + * + * By default, all of the additional elements have an ID of `modernizr[n]`, where + * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, + * the second additional is `#modernizr3`, etc.). + * If you want to have more meaningful IDs for your function, you can provide + * them as the fourth argument, as an array of strings + * + * ```js + * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { + * elem.firstChild === document.getElementById('foo'); // true + * elem.lastChild === document.getElementById('bar'); // true + * }, 2, ['foo', 'bar']); + * ``` + * + */ + + var testStyles = (ModernizrProto.testStyles = injectElementWithStyles); + + /*! + { + "name": "Form Validation", + "property": "formvalidation", + "tags": ["forms", "validation", "attribute"], + "builderAliases": ["forms_validation"] + } + !*/ + /* DOC + This implementation only tests support for interactive form validation. + To check validation for a specific type or a specific other constraint, + the test can be combined: + + - `Modernizr.inputtypes.number && Modernizr.formvalidation` (browser supports rangeOverflow, typeMismatch etc. for type=number) + - `Modernizr.input.required && Modernizr.formvalidation` (browser supports valueMissing) + */ + + Modernizr.addTest("formvalidation", function () { + var form = createElement("form"); + if (!("checkValidity" in form) || !("addEventListener" in form)) { + return false; + } + if ("reportValidity" in form) { + return true; + } + var invalidFired = false; + var input; + + Modernizr.formvalidationapi = true; + + // Prevent form from being submitted + form.addEventListener( + "submit", + function (e) { + // Old Presto based Opera does not validate form, if submit is prevented + // although Opera Mini servers use newer Presto. + if (!window.opera || window.operamini) { + e.preventDefault(); + } + e.stopPropagation(); + }, + false + ); + + // Calling form.submit() doesn't trigger interactive validation, + // use a submit button instead + //older opera browsers need a name attribute + form.innerHTML = + ''; + + testStyles( + "#modernizr form{position:absolute;top:-99999em}", + function (node) { + node.appendChild(form); + + input = form.getElementsByTagName("input")[0]; + + // Record whether "invalid" event is fired + input.addEventListener( + "invalid", + function (e) { + invalidFired = true; + e.preventDefault(); + e.stopPropagation(); + }, + false + ); + + //Opera does not fully support the validationMessage property + Modernizr.formvalidationmessage = !!input.validationMessage; + + // Submit form by clicking submit button + form.getElementsByTagName("button")[0].click(); + } + ); + + return invalidFired; + }); + + /** + * If the browsers follow the spec, then they would expose vendor-specific styles as: + * elem.style.WebkitBorderRadius + * instead of something like the following (which is technically incorrect): + * elem.style.webkitBorderRadius + + * WebKit ghosts their properties in lowercase but Opera & Moz do not. + * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ + * erik.eae.net/archives/2008/03/10/21.48.10/ + + * More here: github.com/Modernizr/Modernizr/issues/issue/21 + * + * @access private + * @returns {string} The string representing the vendor-specific style properties + */ + + var omPrefixes = "Moz O ms Webkit"; + + var cssomPrefixes = ModernizrProto._config.usePrefixes + ? omPrefixes.split(" ") + : []; + ModernizrProto._cssomPrefixes = cssomPrefixes; + + /** + * atRule returns a given CSS property at-rule (eg @keyframes), possibly in + * some prefixed form, or false, in the case of an unsupported rule + * + * @memberof Modernizr + * @name Modernizr.atRule + * @optionName Modernizr.atRule() + * @optionProp atRule + * @access public + * @function atRule + * @param {string} prop - String name of the @-rule to test for + * @returns {string|boolean} The string representing the (possibly prefixed) + * valid version of the @-rule, or `false` when it is unsupported. + * @example + * ```js + * var keyframes = Modernizr.atRule('@keyframes'); + * + * if (keyframes) { + * // keyframes are supported + * // could be `@-webkit-keyframes` or `@keyframes` + * } else { + * // keyframes === `false` + * } + * ``` + * + */ + + var atRule = function (prop) { + var length = prefixes.length; + var cssrule = window.CSSRule; + var rule; + + if (typeof cssrule === "undefined") { + return undefined; + } + + if (!prop) { + return false; + } + + // remove literal @ from beginning of provided property + prop = prop.replace(/^@/, ""); + + // CSSRules use underscores instead of dashes + rule = prop.replace(/-/g, "_").toUpperCase() + "_RULE"; + + if (rule in cssrule) { + return "@" + prop; + } + + for (var i = 0; i < length; i++) { + // prefixes gives us something like -o-, and we want O_ + var prefix = prefixes[i]; + var thisRule = prefix.toUpperCase() + "_" + rule; + + if (thisRule in cssrule) { + return "@-" + prefix.toLowerCase() + "-" + prop; + } + } + + return false; + }; + + ModernizrProto.atRule = atRule; + + /** + * List of JavaScript DOM values used for tests + * + * @memberof Modernizr + * @name Modernizr._domPrefixes + * @optionName Modernizr._domPrefixes + * @optionProp domPrefixes + * @access public + * @example + * + * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather + * than kebab-case properties, all properties are their Capitalized variant + * + * ```js + * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; + * ``` + */ + + var domPrefixes = ModernizrProto._config.usePrefixes + ? omPrefixes.toLowerCase().split(" ") + : []; + ModernizrProto._domPrefixes = domPrefixes; + + /** + * contains checks to see if a string contains another string + * + * @access private + * @function contains + * @param {string} str - The string we want to check for substrings + * @param {string} substr - The substring we want to search the first string for + * @returns {boolean} + */ + + function contains(str, substr) { + return !!~("" + str).indexOf(substr); + } + + /** + * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. + * + * @access private + * @function fnBind + * @param {function} fn - a function you want to change `this` reference to + * @param {object} that - the `this` you want to call the function with + * @returns {function} The wrapped version of the supplied function + */ + + function fnBind(fn, that) { + return function () { + return fn.apply(that, arguments); + }; + } + + /** + * testDOMProps is a generic DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + * + * @access private + * @function testDOMProps + * @param {array.} props - An array of properties to test for + * @param {object} obj - An object or Element you want to use to test the parameters again + * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check + * @returns {false|*} returns false if the prop is unsupported, otherwise the value that is supported + */ + function testDOMProps(props, obj, elem) { + var item; + + for (var i in props) { + if (props[i] in obj) { + // return the property name as a string + if (elem === false) { + return props[i]; + } + + item = obj[props[i]]; + + // let's bind a function + if (is(item, "function")) { + // bind to obj unless overriden + return fnBind(item, elem || obj); + } + + // return the unbound function or obj or value + return item; + } + } + return false; + } + + /** + * Create our "modernizr" element that we do most feature tests on. + * + * @access private + */ + + var modElem = { + elem: createElement("modernizr"), + }; + + // Clean up this element + Modernizr._q.push(function () { + delete modElem.elem; + }); + + var mStyle = { + style: modElem.elem.style, + }; + + // kill ref for gc, must happen before mod.elem is removed, so we unshift on to + // the front of the queue. + Modernizr._q.unshift(function () { + delete mStyle.style; + }); + + /** + * domToCSS takes a camelCase string and converts it to kebab-case + * e.g. boxSizing -> box-sizing + * + * @access private + * @function domToCSS + * @param {string} name - String name of camelCase prop we want to convert + * @returns {string} The kebab-case version of the supplied name + */ + + function domToCSS(name) { + return name + .replace(/([A-Z])/g, function (str, m1) { + return "-" + m1.toLowerCase(); + }) + .replace(/^ms-/, "-ms-"); + } + /** + * wrapper around getComputedStyle, to fix issues with Firefox returning null when + * called inside of a hidden iframe + * + * @access private + * @function computedStyle + * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of + * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none + * @returns {CSSStyleDeclaration} + */ + + function computedStyle(elem, pseudo, prop) { + var result; + + if ("getComputedStyle" in window) { + result = getComputedStyle.call(window, elem, pseudo); + var console = window.console; + + if (result !== null) { + if (prop) { + result = result.getPropertyValue(prop); + } + } else { + if (console) { + var method = console.error ? "error" : "log"; + console[method].call( + console, + "getComputedStyle returning null, its possible modernizr test results are inaccurate" + ); + } + } + } else { + result = !pseudo && elem.currentStyle && elem.currentStyle[prop]; + } + + return result; + } + + /** + * nativeTestProps allows for us to use native feature detection functionality if available. + * some prefixed form, or false, in the case of an unsupported rule + * + * @access private + * @function nativeTestProps + * @param {array} props - An array of property names + * @param {string} value - A string representing the value we want to check via @supports + * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise + */ + + // Accepts a list of property names and a single value + // Returns `undefined` if native detection not available + function nativeTestProps(props, value) { + var i = props.length; + // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface + if ("CSS" in window && "supports" in window.CSS) { + // Try every prefixed variant of the property + while (i--) { + if (window.CSS.supports(domToCSS(props[i]), value)) { + return true; + } + } + return false; + } + // Otherwise fall back to at-rule (for Opera 12.x) + else if ("CSSSupportsRule" in window) { + // Build a condition string for every prefixed variant + var conditionText = []; + while (i--) { + conditionText.push("(" + domToCSS(props[i]) + ":" + value + ")"); + } + conditionText = conditionText.join(" or "); + return injectElementWithStyles( + "@supports (" + + conditionText + + ") { #modernizr { position: absolute; } }", + function (node) { + return computedStyle(node, null, "position") == "absolute"; + } + ); + } + return undefined; + } + // testProps is a generic CSS / DOM property test. + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + // Property names can be provided in either camelCase or kebab-case. + + function testProps(props, prefixed, value, skipValueTest) { + skipValueTest = is(skipValueTest, "undefined") ? false : skipValueTest; + + // Try native detect first + if (!is(value, "undefined")) { + var result = nativeTestProps(props, value); + if (!is(result, "undefined")) { + return result; + } + } + + // Otherwise do it properly + var afterInit, i, propsLength, prop, before; + + // If we don't have a style element, that means we're running async or after + // the core tests, so we'll need to create our own elements to use + + // inside of an SVG element, in certain browsers, the `style` element is only + // defined for valid tags. Therefore, if `modernizr` does not have one, we + // fall back to a less used element and hope for the best. + // for strict XHTML browsers the hardly used samp element is used + var elems = ["modernizr", "tspan", "samp"]; + while (!mStyle.style && elems.length) { + afterInit = true; + mStyle.modElem = createElement(elems.shift()); + mStyle.style = mStyle.modElem.style; + } + + // Delete the objects if we created them. + function cleanElems() { + if (afterInit) { + delete mStyle.style; + delete mStyle.modElem; + } + } + + propsLength = props.length; + for (i = 0; i < propsLength; i++) { + prop = props[i]; + before = mStyle.style[prop]; + + if (contains(prop, "-")) { + prop = cssToDOM(prop); + } + + if (mStyle.style[prop] !== undefined) { + // If value to test has been passed in, do a set-and-check test. + // 0 (integer) is a valid property value, so check that `value` isn't + // undefined, rather than just checking it's truthy. + if (!skipValueTest && !is(value, "undefined")) { + // Needs a try catch block because of old IE. This is slow, but will + // be avoided in most cases because `skipValueTest` will be used. + try { + mStyle.style[prop] = value; + } catch (e) {} + + // If the property value has changed, we assume the value used is + // supported. If `value` is empty string, it'll fail here (because + // it hasn't changed), which matches how browsers have implemented + // CSS.supports() + if (mStyle.style[prop] != before) { + cleanElems(); + return prefixed == "pfx" ? prop : true; + } + } + // Otherwise just return true, or the property name if this is a + // `prefixed()` call + else { + cleanElems(); + return prefixed == "pfx" ? prop : true; + } + } + } + cleanElems(); + return false; + } + + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + * + * @access private + * @function testPropsAll + * @param {string} prop - A string of the property to test for + * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip + * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against + * @param {string} [value] - A string of a css value + * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set + * @returns {false|string} returns the string version of the property, or false if it is unsupported + */ + function testPropsAll(prop, prefixed, elem, value, skipValueTest) { + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + " " + cssomPrefixes.join(ucProp + " ") + ucProp).split( + " " + ); + + // did they call .prefixed('boxSizing') or are we just testing a prop? + if (is(prefixed, "string") || is(prefixed, "undefined")) { + return testProps(props, prefixed, value, skipValueTest); + + // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) + } else { + props = (prop + " " + domPrefixes.join(ucProp + " ") + ucProp).split(" "); + return testDOMProps(props, prefixed, elem); + } + } + + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + ModernizrProto.testAllProps = testPropsAll; + + /** + * testAllProps determines whether a given CSS property is supported in the browser + * + * @memberof Modernizr + * @name Modernizr.testAllProps + * @optionName Modernizr.testAllProps() + * @optionProp testAllProps + * @access public + * @function testAllProps + * @param {string} prop - String naming the property to test (either camelCase or kebab-case) + * @param {string} [value] - String of the value to test + * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection + * @example + * + * testAllProps determines whether a given CSS property, in some prefixed form, + * is supported by the browser. + * + * ```js + * testAllProps('boxSizing') // true + * ``` + * + * It can optionally be given a CSS value in string form to test if a property + * value is valid + * + * ```js + * testAllProps('display', 'block') // true + * testAllProps('display', 'penguin') // false + * ``` + * + * A boolean can be passed as a third parameter to skip the value check when + * native detection (@supports) isn't available. + * + * ```js + * testAllProps('shapeOutside', 'content-box', true); + * ``` + */ + + function testAllProps(prop, value, skipValueTest) { + return testPropsAll(prop, undefined, undefined, value, skipValueTest); + } + ModernizrProto.testAllProps = testAllProps; + + /*! + { + "name": "Border Radius", + "property": "borderradius", + "caniuse": "border-radius", + "polyfills": ["css3pie"], + "tags": ["css"], + "notes": [{ + "name": "Comprehensive Compat Chart", + "href": "https://muddledramblings.com/table-of-css3-border-radius-compliance" + }] + } + !*/ + + Modernizr.addTest("borderradius", testAllProps("borderRadius", "0px", true)); + + /*! + { + "name": "Box Sizing", + "property": "boxsizing", + "caniuse": "css3-boxsizing", + "polyfills": ["borderboxmodel", "boxsizingpolyfill", "borderbox"], + "tags": ["css"], + "builderAliases": ["css_boxsizing"], + "notes": [{ + "name": "MDN Docs", + "href": "https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing" + },{ + "name": "Related Github Issue", + "href": "https://github.com/Modernizr/Modernizr/issues/248" + }] + } + !*/ + + Modernizr.addTest( + "boxsizing", + testAllProps("boxSizing", "border-box", true) && + (document.documentMode === undefined || document.documentMode > 7) + ); + + /*! + { + "name": "Flexbox", + "property": "flexbox", + "caniuse": "flexbox", + "tags": ["css"], + "notes": [{ + "name": "The _new_ flexbox", + "href": "http://dev.w3.org/csswg/css3-flexbox" + }], + "warnings": [ + "A `true` result for this detect does not imply that the `flex-wrap` property is supported; see the `flexwrap` detect." + ] + } + !*/ + /* DOC + Detects support for the Flexible Box Layout model, a.k.a. Flexbox, which allows easy manipulation of layout order and sizing within a container. + */ + + Modernizr.addTest("flexbox", testAllProps("flexBasis", "1px", true)); + + /** + * prefixed returns the prefixed or nonprefixed property name variant of your input + * + * @memberof Modernizr + * @name Modernizr.prefixed + * @optionName Modernizr.prefixed() + * @optionProp prefixed + * @access public + * @function prefixed + * @param {string} prop - String name of the property to test for + * @param {object} [obj] - An object to test for the prefixed properties on + * @param {HTMLElement} [elem] - An element used to test specific properties against + * @returns {string|false} The string representing the (possibly prefixed) valid + * version of the property, or `false` when it is unsupported. + * @example + * + * Modernizr.prefixed takes a string css value in the DOM style camelCase (as + * opposed to the css style kebab-case) form and returns the (possibly prefixed) + * version of that property that the browser actually supports. + * + * For example, in older Firefox... + * ```js + * prefixed('boxSizing') + * ``` + * returns 'MozBoxSizing' + * + * In newer Firefox, as well as any other browser that support the unprefixed + * version would simply return `boxSizing`. Any browser that does not support + * the property at all, it will return `false`. + * + * By default, prefixed is checked against a DOM element. If you want to check + * for a property on another object, just pass it as a second argument + * + * ```js + * var rAF = prefixed('requestAnimationFrame', window); + * + * raf(function() { + * renderFunction(); + * }) + * ``` + * + * Note that this will return _the actual function_ - not the name of the function. + * If you need the actual name of the property, pass in `false` as a third argument + * + * ```js + * var rAFProp = prefixed('requestAnimationFrame', window, false); + * + * rafProp === 'WebkitRequestAnimationFrame' // in older webkit + * ``` + * + * One common use case for prefixed is if you're trying to determine which transition + * end event to bind to, you might do something like... + * ```js + * var transEndEventNames = { + * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser + * 'MozTransition' : 'transitionend', * only for FF < 15 + * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ + * }; + * + * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; + * ``` + * + * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). + */ + + var prefixed = (ModernizrProto.prefixed = function (prop, obj, elem) { + if (prop.indexOf("@") === 0) { + return atRule(prop); + } + + if (prop.indexOf("-") != -1) { + // Convert kebab-case to camelCase + prop = cssToDOM(prop); + } + if (!obj) { + return testPropsAll(prop, "pfx"); + } else { + // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' + return testPropsAll(prop, obj, elem); + } + }); + + /*! + { + "name": "requestAnimationFrame", + "property": "requestanimationframe", + "aliases": ["raf"], + "caniuse": "requestanimationframe", + "tags": ["animation"], + "authors": ["Addy Osmani"], + "notes": [{ + "name": "W3C spec", + "href": "https://www.w3.org/TR/animation-timing/" + }], + "polyfills": ["raf"] + } + !*/ + /* DOC + Detects support for the `window.requestAnimationFrame` API, for offloading animation repainting to the browser for optimized performance. + */ + + Modernizr.addTest( + "requestanimationframe", + !!prefixed("requestAnimationFrame", window), + { aliases: ["raf"] } + ); + + // Run each test + testRunner(); + + delete ModernizrProto.addTest; + delete ModernizrProto.addAsyncTest; + + // Run the things that are supposed to run after the tests + for (var i = 0; i < Modernizr._q.length; i++) { + Modernizr._q[i](); + } + + // Leak Modernizr namespace + window.Modernizr = Modernizr; +})(window, document); diff --git a/js/playkit-js-hotspots.js b/js/playkit-js-hotspots.js new file mode 100644 index 0000000..d750ee3 --- /dev/null +++ b/js/playkit-js-hotspots.js @@ -0,0 +1,17 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("hotspots",[],t):"object"==typeof exports?exports.hotspots=t():(e.KalturaPlayer=e.KalturaPlayer||{},e.KalturaPlayer.plugins=e.KalturaPlayer.plugins||{},e.KalturaPlayer.plugins.hotspots=t())}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=21)}([function(e,t,n){"use strict";n.r(t),n.d(t,"__extends",function(){return o}),n.d(t,"__assign",function(){return i}),n.d(t,"__rest",function(){return a}),n.d(t,"__decorate",function(){return s}),n.d(t,"__param",function(){return u}),n.d(t,"__metadata",function(){return l}),n.d(t,"__awaiter",function(){return p}),n.d(t,"__generator",function(){return c}),n.d(t,"__exportStar",function(){return d}),n.d(t,"__values",function(){return f}),n.d(t,"__read",function(){return y}),n.d(t,"__spread",function(){return h}),n.d(t,"__await",function(){return _}),n.d(t,"__asyncGenerator",function(){return v}),n.d(t,"__asyncDelegator",function(){return g}),n.d(t,"__asyncValues",function(){return b}),n.d(t,"__makeTemplateObject",function(){return m}),n.d(t,"__importStar",function(){return O}),n.d(t,"__importDefault",function(){return w}); +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function o(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}function u(e,t){return function(n,r){t(n,r,e)}}function l(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function p(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}u((r=r.apply(e,t||[])).next())})}function c(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}function y(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a}function h(){for(var e=[],t=0;t1||s(e,t)})})}function s(e,t){try{(n=o[e](t)).value instanceof _?Promise.resolve(n.value.v).then(u,l):p(i[0][2],n)}catch(e){p(i[0][3],e)}var n}function u(e){s("next",e)}function l(e){s("throw",e)}function p(e,t){e(t),i.shift(),i.length&&s(i[0][0],i[0][1])}}function g(e){var t,n;return t={},r("next"),r("throw",function(e){throw e}),r("return"),t[Symbol.iterator]=function(){return this},t;function r(r,o){t[r]=e[r]?function(t){return(n=!n)?{value:_(e[r](t)),done:"return"===r}:o?o(t):t}:o}}function b(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=f(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise(function(r,o){(function(e,t,n,r){Promise.resolve(r).then(function(t){e({value:t,done:n})},t)})(r,o,(t=e[n](t)).done,t.value)})}}}function m(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}function O(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function w(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(2),o={},i=function(){function e(){}return e.registerType=function(e,t){o[e]=t},e.createObject=function(e){var t="";e instanceof r.KalturaObjectBase?t=e.getTypeName():"string"==typeof e&&(t=e);var n=t?o[t]:null;return n?new n:null},e}();t.KalturaTypesFactory=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(28),o=n(1),i=new(n(29).KalturaLogger)("KalturaObjectBase"),a=function(){function e(e){this._allowedEmptyArray=[],this._dependentProperties={},e&&Object.assign(this,e),void 0===this.relatedObjects&&(this.relatedObjects={})}return e.prototype.allowEmptyArray=function(){for(var e=[],t=0;t0){var l={};if(u.forEach(function(t){var n=a[t];n instanceof e&&(l[t]=n.toRequestObject())}),u.length!==Object.keys(l).length)throw new Error("failed to parse map. Expected all '"+t+" items to be kaltura object");o={status:"exists",value:l}}break;case"d":if(!(a instanceof Date))throw new Error("failed to parse property. Expected '"+t+" to be date");o={status:"exists",value:r.KalturaClientUtils.toServerDate(a)};break;case"es":o={status:"exists",value:"string"==typeof a?a:void 0};break;case"f":a instanceof FormData&&(o={status:"exists",value:a})}}return o},e.prototype.setDependency=function(){for(var e=[],t=0;t0?this._executors.splice(0,1)[0]:null;if(r)try{if(t instanceof Error?r.reject?n=r.reject(t):this._handleErrorOriginatedFromExecuter(t):r.resolve&&(n=r.resolve(t)),n instanceof e)return void n.then(this._onResolve.bind(this),this._onReject.bind(this));if(n instanceof Promise)return void n.then(this._onResolve.bind(this),this._onReject.bind(this));n=void 0===n?t:n,this._notifyExecutor(n)}catch(e){this._handleErrorOriginatedFromExecuter(e)}},e.prototype._handleErrorOriginatedFromExecuter=function(e){if(!(this._executors.length>0))throw e;this._notifyExecutor(e)},e.prototype.cancel=function(){this._onCancel&&this._onCancel()},e.prototype.then=function(e,t){return this._executors.push({resolve:e,reject:t}),this},e.prototype.catch=function(e){return this._executors.push({resolve:null,reject:e}),this},e}();t.CancelableAction=r},function(e,t,n){"use strict";n.r(t),n.d(t,"h",function(){return s}),n.d(t,"createElement",function(){return s}),n.d(t,"cloneElement",function(){return c}),n.d(t,"createRef",function(){return N}),n.d(t,"Component",function(){return F}),n.d(t,"render",function(){return I}),n.d(t,"rerender",function(){return h}),n.d(t,"options",function(){return o});var r=function(){},o={},i=[],a=[];function s(e,t){var n,s,u,l,p=a;for(l=arguments.length;l-- >2;)i.push(arguments[l]);for(t&&null!=t.children&&(i.length||i.push(t.children),delete t.children);i.length;)if((s=i.pop())&&void 0!==s.pop)for(l=s.length;l--;)i.push(s[l]);else"boolean"==typeof s&&(s=null),(u="function"!=typeof e)&&(null==s?s="":"number"==typeof s?s=String(s):"string"!=typeof s&&(u=!1)),u&&n?p[p.length-1]+=s:p===a?p=[s]:p.push(s),n=u;var c=new r;return c.nodeName=e,c.children=p,c.attributes=null==t?void 0:t,c.key=null==t?void 0:t.key,void 0!==o.vnode&&o.vnode(c),c}function u(e,t){for(var n in t)e[n]=t[n];return e}function l(e,t){null!=e&&("function"==typeof e?e(t):e.current=t)}var p="function"==typeof Promise?Promise.resolve().then.bind(Promise.resolve()):setTimeout;function c(e,t){return s(e.nodeName,u(u({},e.attributes),t),arguments.length>2?[].slice.call(arguments,2):e.children)}var d=/acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i,f=[];function y(e){!e._dirty&&(e._dirty=!0)&&1==f.push(e)&&(o.debounceRendering||p)(h)}function h(){for(var e;e=f.pop();)e._dirty&&q(e)}function _(e,t){return e.normalizedNodeName===t||e.nodeName.toLowerCase()===t.toLowerCase()}function v(e){var t=u({},e.attributes);t.children=e.children;var n=e.nodeName.defaultProps;if(void 0!==n)for(var r in n)void 0===t[r]&&(t[r]=n[r]);return t}function g(e){var t=e.parentNode;t&&t.removeChild(e)}function b(e,t,n,r,o){if("className"===t&&(t="class"),"key"===t);else if("ref"===t)l(n,null),l(r,e);else if("class"!==t||o)if("style"===t){if(r&&"string"!=typeof r&&"string"!=typeof n||(e.style.cssText=r||""),r&&"object"==typeof r){if("string"!=typeof n)for(var i in n)i in r||(e.style[i]="");for(var i in r)e.style[i]="number"==typeof r[i]&&!1===d.test(i)?r[i]+"px":r[i]}}else if("dangerouslySetInnerHTML"===t)r&&(e.innerHTML=r.__html||"");else if("o"==t[0]&&"n"==t[1]){var a=t!==(t=t.replace(/Capture$/,""));t=t.toLowerCase().substring(2),r?n||e.addEventListener(t,m,a):e.removeEventListener(t,m,a),(e._listeners||(e._listeners={}))[t]=r}else if("list"!==t&&"type"!==t&&!o&&t in e){try{e[t]=null==r?"":r}catch(e){}null!=r&&!1!==r||"spellcheck"==t||e.removeAttribute(t)}else{var s=o&&t!==(t=t.replace(/^xlink:?/,""));null==r||!1===r?s?e.removeAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase()):e.removeAttribute(t):"function"!=typeof r&&(s?e.setAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase(),r):e.setAttribute(t,r))}else e.className=r||""}function m(e){return this._listeners[e.type](o.event&&o.event(e)||e)}var O=[],w=0,C=!1,P=!1;function T(){for(var e;e=O.shift();)o.afterMount&&o.afterMount(e),e.componentDidMount&&e.componentDidMount()}function j(e,t,n,r,o,i){w++||(C=null!=o&&void 0!==o.ownerSVGElement,P=null!=e&&!("__preactattr_"in e));var a=K(e,t,n,r,i);return o&&a.parentNode!==o&&o.appendChild(a),--w||(P=!1,i||T()),a}function K(e,t,n,r,o){var i=e,a=C;if(null!=t&&"boolean"!=typeof t||(t=""),"string"==typeof t||"number"==typeof t)return e&&void 0!==e.splitText&&e.parentNode&&(!e._component||o)?e.nodeValue!=t&&(e.nodeValue=t):(i=document.createTextNode(t),e&&(e.parentNode&&e.parentNode.replaceChild(i,e),x(e,!0))),i.__preactattr_=!0,i;var s,u,l=t.nodeName;if("function"==typeof l)return function(e,t,n,r){var o=e&&e._component,i=o,a=e,s=o&&e._componentConstructor===t.nodeName,u=s,l=v(t);for(;o&&!u&&(o=o._parentComponent);)u=o.constructor===t.nodeName;o&&u&&(!r||o._component)?(k(o,l,3,n,r),e=o.base):(i&&!s&&(A(i),e=a=null),o=S(t.nodeName,l,n),e&&!o.nextBase&&(o.nextBase=e,a=null),k(o,l,1,n,r),e=o.base,a&&e!==a&&(a._component=null,x(a,!1)));return e}(e,t,n,r);if(C="svg"===l||"foreignObject"!==l&&C,l=String(l),(!e||!_(e,l))&&(s=l,(u=C?document.createElementNS("http://www.w3.org/2000/svg",s):document.createElement(s)).normalizedNodeName=s,i=u,e)){for(;e.firstChild;)i.appendChild(e.firstChild);e.parentNode&&e.parentNode.replaceChild(i,e),x(e,!0)}var p=i.firstChild,c=i.__preactattr_,d=t.children;if(null==c){c=i.__preactattr_={};for(var f=i.attributes,y=f.length;y--;)c[f[y].name]=f[y].value}return!P&&d&&1===d.length&&"string"==typeof d[0]&&null!=p&&void 0!==p.splitText&&null==p.nextSibling?p.nodeValue!=d[0]&&(p.nodeValue=d[0]):(d&&d.length||null!=p)&&function(e,t,n,r,o){var i,a,s,u,l,p=e.childNodes,c=[],d={},f=0,y=0,h=p.length,v=0,b=t?t.length:0;if(0!==h)for(var m=0;m10?console.warn("cannot set network tag longer than 10 characters. ignoring tag '"+e):this._networkTag=e,this},t.prototype.getNetworkTag=function(){return this._networkTag},t}(n(2).KalturaObjectBase);t.KalturaRequestBase=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.environment={request:{apiVersion:"14.16.0",avoidQueryString:!1,fileFormatValue:1},response:{nestedResponse:!1}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(30),i=function(e){function t(t){var n=e.call(this,t)||this;return void 0===n.acceptedTypes&&(n.acceptedTypes=[]),n}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{partnerId:{type:"n"},ks:{type:"s"},responseProfile:{type:"o",subTypeConstructor:o.KalturaBaseResponseProfile,subType:"KalturaBaseResponseProfile"}}),t},t}(n(2).KalturaObjectBase);t.KalturaRequestOptions=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){e.ad="adCuePoint.Ad",e.annotation="annotation.Annotation",e.code="codeCuePoint.Code",e.event="eventCuePoint.Event",e.quizAnswer="quiz.QUIZ_ANSWER",e.quizQuestion="quiz.QUIZ_QUESTION",e.thumb="thumbCuePoint.Thumb"}(t.KalturaCuePointType||(t.KalturaCuePointType={}))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(10),i=n(15),a=n(3),s=n(11),u=function(e){function t(){for(var t=[],n=0;n0&&r.push.apply(r,t),r}return r.__extends(t,e),t.prototype.hasErrors=function(){return this.filter(function(e){return e.error}).length>0},t.prototype.getFirstError=function(){for(var e=null,t=0;te||e-i>2e3),s=n||null!==this.lastHandledTime&&this.lastHandledTime>e||null!=this.nextTimeToHandle&&e>=this.nextTimeToHandle,u=this.findClosestLastIndexByTime(e),l=u<0?0:this.cuepointChanges[u].time;if(!s)return t?{snapshot:this.createCuepointSnapshot(u)}:{delta:this.createEmptyDelta()};if(o.log("debug","CuepointEngine:updateTime","has changes to handle. check if need to return snapshot instead of delta based on provided new time",{currentTime:e,closestChangeIndex:u,closestChangeTime:l,lastHandledTime:r,nextTimeToHandle:i,isFirstTime:n}),n||t||a){o.log("debug","CuepointEngine:updateTime","some conditions doesn't allow returning delta, return snapshot instead",{isFirstTime:n,userSeeked:a,forceSnapshot:t});var p=this.createCuepointSnapshot(u);return this.updateInternals(l,u),{snapshot:p}}var c=this.createCuepointDelta(u);return this.updateInternals(l,u),{delta:c}},e.prototype.getCurrentCuepointSnapshot=function(){return this.lastHandledTimeIndex?this.createCuepointSnapshot(this.lastHandledTimeIndex):[]},e.prototype.createCuepointSnapshot=function(e){if(!this.enabled||e<0||!this.cuepointChanges||0===this.cuepointChanges.length)return o.log("log","CuepointEngine:createCuepointSnapshot","resulted with empty snapshot",{targetIndex:e,enabled:this.enabled,cuepointCount:(this.cuepointChanges||[]).length}),[];for(var t=[],n=0;n<=e;n++){var i=this.cuepointChanges[n],a=t.indexOf(i.cuePoint);i.type===r.Show?-1===a&&t.push(i.cuePoint):-1!==a&&t.splice(a,1)}return o.log("log","CuepointEngine:createCuepointSnapshot","resulted snapshot",{snapshot:t}),t},e.prototype.createCuepointDelta=function(e){if(!this.enabled||!this.cuepointChanges||0===this.cuepointChanges.length)return o.log("log","CuepointEngine:createCuepointDelta","resulted with empty delta",{enabled:this.enabled,cuepointCount:(this.cuepointChanges||[]).length}),this.createEmptyDelta();var t=this.lastHandledTimeIndex;if(null===t)return o.log("log","CuepointEngine:createCuepointDelta","invalid internal state. resulted with empty delta"),this.createEmptyDelta();var n=[],i=[];o.log("log","CuepointEngine:createCuepointDelta","find cuepoint that were added or removed");for(var a=t+1;a<=e;a++){var s=this.cuepointChanges[a],u=n.indexOf(s.cuePoint);s.type===r.Show?-1===u&&n.push(s.cuePoint):-1!==u?(o.log("log","CuepointEngine:createCuepointDelta","cuepoint was marked with type "+s.type+" at "+s.time+". remove from new cuepoint list as it wasn't visible yet",{cuepoint:s.cuePoint}),n.splice(u,1)):-1===i.indexOf(s.cuePoint)&&(o.log("log","CuepointEngine:createCuepointDelta","cuepoint was marked with type "+s.type+" at "+s.time+". add to removed cuepoint list",{cuepoint:s.cuePoint}),i.push(s.cuePoint))}return o.log("log","CuepointEngine:createCuepointDelta","resulted delta",{newCuepoint:n,removedCuepoint:i}),{show:n,hide:i}},e.prototype.updateInternals=function(e,t){var n=this.cuepointChanges;if(n&&0!==n.length){var r=t>=n.length-1,i=null===t;this.lastHandledTime=e,this.lastHandledTimeIndex=t,this.nextTimeToHandle=i?n[0].time:r?n[n.length-1].time:n[t+1].time,this.isFirstTime=!1,o.log("debug","CuepointEngine:updateInternals","update inner state with new time and index",{lastHandledTime:this.lastHandledTime,lastHandledTimeIndex:this.lastHandledTimeIndex,nextTimeToHandle:this.nextTimeToHandle})}},e.prototype.createEmptyDelta=function(){return{show:[],hide:[]}},e.prototype.binarySearch=function(e,t){if(!e||0===e.length)return null;if(te[e.length-1].time)return e.length-1;for(var n=0,r=e.length-1;n<=r;){var o=Math.floor((r+n+1)/2);if(te[o].time))return o;n=o+1}}return Math.min(n,r)},e.prototype.findClosestLastIndexByTime=function(e){var t=this.cuepointChanges,n=this.binarySearch(t,e);if(null===n)return-1;for(var r=t.length;n=0&&e.cuepointChanges.push({time:t.startTime,type:r.Show,cuePoint:t}),null!==t.endTime&&void 0!==t.endTime&&t.endTime>=0&&e.cuepointChanges.push({time:t.endTime,type:r.Hide,cuePoint:t})}),this.cuepointChanges.sort(function(e,t){return e.timen;return(p=p?o:!o)?(i.width=Math.abs(a),i.height=Math.abs(s),i.scaleToTargetWidth=!0):(i.width=Math.abs(u),i.height=Math.abs(l),i.scaleToTargetWidth=!1),i.left=Math.abs((n-i.width)/2),i.top=Math.abs((r-i.height)/2),i}},function(e,t,n){"use strict";function r(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}Object.defineProperty(t,"__esModule",{value:!0}),r(n(1)),r(n(4)),r(n(2)),r(n(0))},function(e,t,n){"use strict";var r,o=this&&this.__extends||(r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)});Object.defineProperty(t,"__esModule",{value:!0});var i,a=n(0),s=n(2),u=n(1);!function(e){e.Show="show",e.Hide="hide"}(i||(i={}));var l=function(e){function t(t){var n=e.call(this,t)||this;return n.playerSize=null,n.videoSize=null,n}return o(t,e),t.prototype.updateLayout=function(e,t){return this.videoSize=t,this.playerSize=e,this.recalculateCuepointLayout(),this.getCurrentCuepointSnapshot()},t.prototype._calculateLayout=function(e,t){var n=e.rawLayout;return{x:t.left+n.relativeX*t.width,y:t.top+n.relativeY*t.height,width:n.relativeWidth*t.width,height:n.relativeHeight*t.height}},t.prototype.recalculateCuepointLayout=function(){var e=this;if(a.log("debug","CuepointLayoutEngine::recalculateCuepointLayout","calculating cuepoint layout based on video/player sizes"),!this.playerSize||!this.videoSize)return a.log("debug","CuepointLayoutEngine::recalculateCuepointLayout","missing video/player sizes, hide all cuepoint"),void(this.enabled=!1);var t=this.playerSize,n=t.width,r=t.height,o=this.videoSize,i=o.width,u=o.height;if(!(n&&r&&i&&u))return a.log("debug","CuepointLayoutEngine::recalculateCuepointLayout","missing video/player sizes, hide all cuepoint"),void(this.enabled=!1);var l=s.scaleVideo(i,u,n,r,!0);a.log("debug","CuepointLayoutEngine::recalculateCuepointLayout","recalculate cuepoint layout based on new sizes",l),(this.cuepoints||[]).forEach(function(t){t.layout=e._calculateLayout(t,l)}),this.enabled=!0},t}(u.CuepointEngine);t.CuepointLayoutEngine=l}])},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0);r.__exportStar(n(27),t),r.__exportStar(n(6),t),r.__exportStar(n(31),t)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(4);t.KalturaClientException=o.KalturaClientException;var i=n(3);t.KalturaAPIException=i.KalturaAPIException;var a=n(14);t.KalturaMultiRequest=a.KalturaMultiRequest;var s=n(15);t.KalturaMultiResponse=s.KalturaMultiResponse;var u=n(5);t.KalturaRequest=u.KalturaRequest;var l=n(10);t.KalturaRequestBase=l.KalturaRequestBase;var p=n(17);t.KalturaUploadRequest=p.KalturaUploadRequest;var c=n(16);t.KalturaResponse=c.KalturaResponse;var d=n(1);t.KalturaTypesFactory=d.KalturaTypesFactory,r.__exportStar(n(12),t)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(){}return e.fromServerDate=function(e){return e?new Date(1e3*e):null},e.toServerDate=function(e){return e?Math.round(e.getTime()/1e3):null},e.getStartDateValue=function(e){return e?(e.setHours(0),e.setMinutes(0),e.setSeconds(0),e):null},e.getEndDateValue=function(e){return e?(e.setHours(23),e.setMinutes(59),e.setSeconds(59),e):null},e}();t.KalturaClientUtils=r},function(e,t,n){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),function(e){e[e.info=0]="info",e[e.warn=1]="warn",e[e.error=2]="error",e[e.none=100]="none"}(r=t.LogLevels||(t.LogLevels={})),t.LoggerSettings={logLevel:r.warn};var o=function(){function e(e){this._name=e}return e.prototype.warn=function(e){t.LoggerSettings.logLevel<=r.warn&&console.warn("[kaltura-client/"+this._name+"]: "+e)},e.prototype.info=function(e){t.LoggerSettings.logLevel<=r.info&&console.info("[kaltura-client/"+this._name+"]: "+e)},e.prototype.error=function(e){t.LoggerSettings.logLevel<=r.error&&console.error("[kaltura-client/"+this._name+"]: "+e)},e}();t.KalturaLogger=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaBaseResponseProfile"}}),t},t}(n(2).KalturaObjectBase);t.KalturaBaseResponseProfile=i,o.KalturaTypesFactory.registerType("KalturaBaseResponseProfile",i)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(5),o=n(14),i=n(32),a=n(17),s=n(33),u=n(34),l=n(35),p=n(4),c=n(36),d=n(12),f=n(6),y=function(){function e(e,t){this._options=e,this._defaultRequestOptions=new d.KalturaRequestOptions(t||{})}return e.prototype.appendOptions=function(e){if(!e)throw new p.KalturaClientException("client::append_options","missing required argument 'options'");this._options=Object.assign(this._options||{},e)},e.prototype.setOptions=function(e){if(!e)throw new p.KalturaClientException("client::set_options","missing required argument 'options'");this._options=e},e.prototype.appendDefaultRequestOptions=function(e){if(!e)throw new p.KalturaClientException("client::append_default_request_options","missing required argument 'args'");this._defaultRequestOptions=Object.assign(this._defaultRequestOptions||new d.KalturaRequestOptions,new d.KalturaRequestOptions(e))},e.prototype.setDefaultRequestOptions=function(e){if(!e)throw new p.KalturaClientException("client::set_default_request_options","missing required argument 'args'");this._defaultRequestOptions=new d.KalturaRequestOptions(e)},e.prototype._validateOptions=function(){return this._options?this._options.endpointUrl?this._options.clientTag?null:new p.KalturaClientException("client::missing_options","cannot transmit request, missing 'clientTag' in client options"):new p.KalturaClientException("client::missing_options","cannot transmit request, missing 'endpointUrl' in client options"):new p.KalturaClientException("client::missing_options","cannot transmit request, missing client options (did you forgot to provide options manually?)")},e.prototype.request=function(e){var t=this._validateOptions();return t?f.CancelableAction.reject(t):e instanceof i.KalturaFileRequest?(new u.KalturaFileRequestAdapter).transmit(e,this._options,this._defaultRequestOptions):e instanceof a.KalturaUploadRequest?new c.KalturaUploadRequestAdapter(this._options,this._defaultRequestOptions).transmit(e):e instanceof r.KalturaRequest?(new s.KalturaRequestAdapter).transmit(e,this._options,this._defaultRequestOptions):f.CancelableAction.reject(new p.KalturaClientException("client::request_type_error","unsupported request type requested"))},e.prototype.multiRequest=function(e){var t=this._validateOptions();if(t)return f.CancelableAction.reject(t);var n=e instanceof o.KalturaMultiRequest?e:e instanceof Array?new(o.KalturaMultiRequest.bind.apply(o.KalturaMultiRequest,[void 0].concat(e))):null;return n?n.requests.some(function(e){return e instanceof i.KalturaFileRequest})?f.CancelableAction.reject(new p.KalturaClientException("client::invalid_request","multi-request not support requests of type 'KalturaFileRequest', use regular request instead")):(new l.KalturaMultiRequestAdapter).transmit(n,this._options,this._defaultRequestOptions):f.CancelableAction.reject(new p.KalturaClientException("client::invalid_request","Expected argument of type Array or KalturaMultiRequest"))},e}();t.KalturaClient=y},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){function t(t){return e.call(this,t,{responseType:"v",responseSubType:"",responseConstructor:null})||this}return r.__extends(t,e),t}(n(5).KalturaRequest);t.KalturaFileRequest=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(3),o=n(4),i=n(8),a=function(){function e(){}return e.prototype.transmit=function(e,t,n){var a=i.prepareParameters(e,t,n),s=i.createEndpoint(e,t,a.service,a.action);return delete a.service,delete a.action,i.createCancelableAction({endpoint:s,headers:i.getHeaders(),body:a}).then(function(t){try{var n=e.handleResponse(t);if(n.error)throw n.error;return n.result}catch(e){if(e instanceof o.KalturaClientException||e instanceof r.KalturaAPIException)throw e;var i=e instanceof Error?e.message:"string"==typeof e?e:null;throw new o.KalturaClientException("client::response-unknown-error",i||"Failed to parse response")}},function(e){var t=e instanceof Error?e.message:"string"==typeof e?e:null;throw new o.KalturaClientException("client::request-network-error",t||"Error connecting to server")})},e}();t.KalturaRequestAdapter=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(8),o=n(6),i=function(){function e(){}return e.prototype.transmit=function(e,t,n){var i=r.prepareParameters(e,t,n),a=r.createEndpoint(e,t,i.service,i.action);return delete i.service,delete i.action,o.CancelableAction.resolve({url:a+"?"+r.buildQuerystring(i)})},e}();t.KalturaFileRequestAdapter=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(8),o=n(3),i=n(4),a=function(){function e(){}return e.prototype.transmit=function(e,t,n){var a=r.prepareParameters(e,t,n),s=r.createEndpoint(e,t,a.service,a.action);return delete a.service,delete a.action,r.createCancelableAction({endpoint:s,headers:r.getHeaders(),body:a}).then(function(t){try{return e.handleResponse(t)}catch(e){if(e instanceof i.KalturaClientException||e instanceof o.KalturaAPIException)throw e;var n=e instanceof Error?e.message:"string"==typeof e?e:null;throw new i.KalturaClientException("client::multi-response-unknown-error",n||"Failed to parse response")}},function(e){var t=e instanceof Error?e.message:"string"==typeof e?e:null;throw new i.KalturaClientException("client::multi-request-network-error",t||"Error connecting to server")})},e}();t.KalturaMultiRequestAdapter=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(8),o=n(4),i=n(3),a=n(6),s=function(){function e(e,t){this.clientOptions=e,this.defaultRequestOptions=t}return e.prototype._chunkUploadSupported=function(e){var t=!("undefined"==typeof File||"undefined"==typeof Blob||"undefined"==typeof FileList||!Blob.prototype.webkitSlice&&!Blob.prototype.mozSlice&&!Blob.prototype.slice),n=e.supportChunkUpload();return!this.clientOptions.chunkFileDisabled&&t&&n},e.prototype.transmit=function(e){var t=this;return new a.CancelableAction(function(n,r,a){var s,u=!isNaN(e.uploadedFileSize)&&isFinite(e.uploadedFileSize)&&e.uploadedFileSize>0?e.uploadedFileSize:0,l={enabled:t._chunkUploadSupported(e),resume:!!u,finalChunk:!1,resumeAt:u},p=function(e){s=null,r(e)},c=function(a){if(!l.enabled||l.finalChunk){s=null;try{var u=e.handleResponse(a);u.error?r(u.error):n(u.result)}catch(e){if(e instanceof o.KalturaClientException||e instanceof i.KalturaAPIException)r(e);else{var d=e instanceof Error?e.message:"string"==typeof e?e:null;r(new o.KalturaClientException("client::response-unknown-error",d||"Failed to parse response"))}}}else s=t._chunkUpload(e,l).then(c,p)};return s=t._chunkUpload(e,l).then(c,p),function(){s&&(s.cancel(),s=null)}})},e.prototype._getFormData=function(e,t,n){var r=new FormData;return r.append("fileName",t),r.append(e,n),r},e.prototype._chunkUpload=function(e,t){var n=this;return new a.CancelableAction(function(i,a){var s=r.prepareParameters(e,n.clientOptions,n.defaultRequestOptions),u=!1,l=e.getFileInfo(),p=l.propertyName,c=l.file,d=n._getFormData(p,c.name,c),f=0;if(t.enabled){var y=null,h=n.clientOptions?n.clientOptions.chunkFileSize:null;h&&Number.isFinite(h)&&!Number.isNaN(h)?h<1e5?(console.warn("user requested for invalid upload chunk size '"+h+"'. minimal value 100Kb. using minimal value 100Kb instead"),y=1e5):(console.log("using user requetsed chunk size '"+h+"'"),y=h):(console.log("using default chunk size 5Mb"),y=5e6),t.finalChunk=c.size-t.resumeAt<=y,f=t.resumeAt;var _=t.finalChunk?c.size:f+y;d=n._getFormData(p,c.name,c.slice(f,_,c.type)),s.resume=t.resume,s.resumeAt=t.resumeAt,s.finalChunk=t.finalChunk}else console.log("chunk upload not supported by browser or by request. Uploading the file as-is");var v=r.createEndpoint(e,n.clientOptions,s.service,s.action);delete s.service,delete s.action,v=v+"?"+r.buildQuerystring(s);var g=new XMLHttpRequest;g.onreadystatechange=function(){if(4===g.readyState){if(u)return;u=!0;var e=void 0;try{e=200===g.status?JSON.parse(g.response):new o.KalturaClientException("client::upload-failure",g.responseText||"failed to upload file")}catch(t){e=new o.KalturaClientException("client::upload-failure",t.message||"failed to upload file")}if(e instanceof Error)a(e);else{if(t.enabled){if(void 0===e.uploadedFileSize||null===e.uploadedFileSize)return void a(new o.KalturaClientException("client::upload-failure","uploaded chunk of file failed, expected response with property 'uploadedFileSize'"));t.finalChunk||(t.resumeAt=Number(e.uploadedFileSize),t.resume=!0)}i(e)}}};var b=e._getProgressCallback();return b&&g.upload.addEventListener("progress",function(t){t.lengthComputable&&b.apply(e,[t.loaded+f,c.size])},!1),g.open("POST",v),g.send(d),function(){u||(u=!0,g.abort())}})},e}();t.KalturaUploadRequestAdapter=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(38),i=n(20),a=n(44),s=function(e){function t(t){return e.call(this,t,{responseType:"o",responseSubType:"KalturaCuePointListResponse",responseConstructor:o.KalturaCuePointListResponse})||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{service:{type:"c",default:"cuepoint_cuepoint"},action:{type:"c",default:"list"},filter:{type:"o",subTypeConstructor:i.KalturaCuePointFilter,subType:"KalturaCuePointFilter"},pager:{type:"o",subTypeConstructor:a.KalturaFilterPager,subType:"KalturaFilterPager"}}),t},t}(n(5).KalturaRequest);t.CuePointListAction=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=n(18),a=function(e){function t(t){var n=e.call(this,t)||this;return void 0===n.objects&&(n.objects=[]),n}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaCuePointListResponse"},objects:{type:"a",readOnly:!0,subTypeConstructor:i.KalturaCuePoint,subType:"KalturaCuePoint"}}),t},t}(n(39).KalturaListResponse);t.KalturaCuePointListResponse=a,o.KalturaTypesFactory.registerType("KalturaCuePointListResponse",a)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaListResponse"},totalCount:{type:"n",readOnly:!0}}),t},t}(n(2).KalturaObjectBase);t.KalturaListResponse=i,o.KalturaTypesFactory.registerType("KalturaListResponse",i)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=n(13),a=n(19),s=n(9),u=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaCuePointBaseFilter"},idEqual:{type:"s"},idIn:{type:"s"},cuePointTypeEqual:{type:"es",subTypeConstructor:i.KalturaCuePointType,subType:"KalturaCuePointType"},cuePointTypeIn:{type:"s"},statusEqual:{type:"en",subTypeConstructor:a.KalturaCuePointStatus,subType:"KalturaCuePointStatus"},statusIn:{type:"s"},entryIdEqual:{type:"s"},entryIdIn:{type:"s"},createdAtGreaterThanOrEqual:{type:"d"},createdAtLessThanOrEqual:{type:"d"},updatedAtGreaterThanOrEqual:{type:"d"},updatedAtLessThanOrEqual:{type:"d"},triggeredAtGreaterThanOrEqual:{type:"d"},triggeredAtLessThanOrEqual:{type:"d"},tagsLike:{type:"s"},tagsMultiLikeOr:{type:"s"},tagsMultiLikeAnd:{type:"s"},startTimeGreaterThanOrEqual:{type:"n"},startTimeLessThanOrEqual:{type:"n"},userIdEqual:{type:"s"},userIdIn:{type:"s"},partnerSortValueEqual:{type:"n"},partnerSortValueIn:{type:"s"},partnerSortValueGreaterThanOrEqual:{type:"n"},partnerSortValueLessThanOrEqual:{type:"n"},forceStopEqual:{type:"en",subTypeConstructor:s.KalturaNullableBoolean,subType:"KalturaNullableBoolean"},systemNameEqual:{type:"s"},systemNameIn:{type:"s"}}),t},t}(n(41).KalturaRelatedFilter);t.KalturaCuePointBaseFilter=u,o.KalturaTypesFactory.registerType("KalturaCuePointBaseFilter",u)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaRelatedFilter"}}),t},t}(n(42).KalturaFilter);t.KalturaRelatedFilter=i,o.KalturaTypesFactory.registerType("KalturaRelatedFilter",i)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=n(43),a=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaFilter"},orderBy:{type:"s"},advancedSearch:{type:"o",subTypeConstructor:i.KalturaSearchItem,subType:"KalturaSearchItem"}}),t},t}(n(2).KalturaObjectBase);t.KalturaFilter=a,o.KalturaTypesFactory.registerType("KalturaFilter",a)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaSearchItem"}}),t},t}(n(2).KalturaObjectBase);t.KalturaSearchItem=i,o.KalturaTypesFactory.registerType("KalturaSearchItem",i)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaFilterPager"}}),t},t}(n(45).KalturaPager);t.KalturaFilterPager=i,o.KalturaTypesFactory.registerType("KalturaFilterPager",i)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(1),i=function(e){function t(t){return e.call(this,t)||this}return r.__extends(t,e),t.prototype._getMetadata=function(){var t=e.prototype._getMetadata.call(this);return Object.assign(t.properties,{objectType:{type:"c",default:"KalturaPager"},pageSize:{type:"n"},pageIndex:{type:"n"}}),t},t}(n(2).KalturaObjectBase);t.KalturaPager=i,o.KalturaTypesFactory.registerType("KalturaPager",i)},function(e,t,n){"use strict";var r=this&&this.__assign||function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n ${1}", + min: "Minimum value for this field is ${1}", + max: "Maximum value for this field is ${1}", + pattern: "Please match the requested format", + equals: "The two fields do not match" + } + }; + + function findAncestor(el, cls) { + while ((el = el.parentElement) && !el.classList.contains(cls)) {} + return el; + } + + function tmpl(o) { + var _arguments = arguments; + + return this.replace(/\${([^{}]*)}/g, function (a, b) { + return _arguments[b]; + }); + } + + function groupedElemCount(input) { + return input.pristine.self.form.querySelectorAll('input[name="' + input.getAttribute('name') + '"]:checked').length; + } + + function mergeConfig(obj1, obj2) { + for (var attr in obj2) { + if (!(attr in obj1)) { + obj1[attr] = obj2[attr]; + } + } + return obj1; + } + + var defaultConfig = { + classTo: 'form-group', + errorClass: 'has-danger', + successClass: 'has-success', + errorTextParent: 'form-group', + errorTextTag: 'div', + errorTextClass: 'text-help' + }; + + var PRISTINE_ERROR = 'pristine-error'; + var SELECTOR = "input:not([type^=hidden]):not([type^=submit]), select, textarea"; + var ALLOWED_ATTRIBUTES = ["required", "min", "max", 'minlength', 'maxlength', 'pattern']; + var EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + var MESSAGE_REGEX = /-message(?:-([a-z]{2}(?:_[A-Z]{2})?))?/; // matches, -message, -message-en, -message-en_US + var currentLocale = 'en'; + var validators = {}; + + var _ = function _(name, validator) { + validator.name = name; + if (validator.priority === undefined) validator.priority = 1; + validators[name] = validator; + }; + + _('text', { fn: function fn(val) { + return true; + }, priority: 0 }); + _('required', { fn: function fn(val) { + return this.type === 'radio' || this.type === 'checkbox' ? groupedElemCount(this) : val !== undefined && val !== ''; + }, priority: 99, halt: true }); + _('email', { fn: function fn(val) { + return !val || EMAIL_REGEX.test(val); + } }); + _('number', { fn: function fn(val) { + return !val || !isNaN(parseFloat(val)); + }, priority: 2 }); + _('integer', { fn: function fn(val) { + return !val || /^\d+$/.test(val); + } }); + _('minlength', { fn: function fn(val, length) { + return !val || val.length >= parseInt(length); + } }); + _('maxlength', { fn: function fn(val, length) { + return !val || val.length <= parseInt(length); + } }); + _('min', { fn: function fn(val, limit) { + return !val || (this.type === 'checkbox' ? groupedElemCount(this) >= parseInt(limit) : parseFloat(val) >= parseFloat(limit)); + } }); + _('max', { fn: function fn(val, limit) { + return !val || (this.type === 'checkbox' ? groupedElemCount(this) <= parseInt(limit) : parseFloat(val) <= parseFloat(limit)); + } }); + _('pattern', { fn: function fn(val, pattern) { + var m = pattern.match(new RegExp('^/(.*?)/([gimy]*)$'));return !val || new RegExp(m[1], m[2]).test(val); + } }); + _('equals', { fn: function fn(val, otherFieldSelector) { + var other = document.querySelector(otherFieldSelector);return other && (!val && !other.value || other.value === val); + } }); + + function Pristine(form, config, live) { + + var self = this; + + init(form, config, live); + + function init(form, config, live) { + + form.setAttribute("novalidate", "true"); + + self.form = form; + self.config = mergeConfig(config || {}, defaultConfig); + self.live = !(live === false); + self.fields = Array.from(form.querySelectorAll(SELECTOR)).map(function (input) { + + var fns = []; + var params = {}; + var messages = {}; + + [].forEach.call(input.attributes, function (attr) { + if (/^data-pristine-/.test(attr.name)) { + var name = attr.name.substr(14); + var messageMatch = name.match(MESSAGE_REGEX); + if (messageMatch !== null) { + var locale = messageMatch[1] === undefined ? 'en' : messageMatch[1]; + if (!messages.hasOwnProperty(locale)) messages[locale] = {}; + messages[locale][name.slice(0, name.length - messageMatch[0].length)] = attr.value; + return; + } + if (name === 'type') name = attr.value; + _addValidatorToField(fns, params, name, attr.value); + } else if (~ALLOWED_ATTRIBUTES.indexOf(attr.name)) { + _addValidatorToField(fns, params, attr.name, attr.value); + } else if (attr.name === 'type') { + _addValidatorToField(fns, params, attr.value); + } + }); + + fns.sort(function (a, b) { + return b.priority - a.priority; + }); + + self.live && input.addEventListener(!~['radio', 'checkbox'].indexOf(input.getAttribute('type')) ? 'input' : 'change', function (e) { + self.validate(e.target); + }.bind(self)); + + return input.pristine = { input: input, validators: fns, params: params, messages: messages, self: self }; + }.bind(self)); + } + + function _addValidatorToField(fns, params, name, value) { + var validator = validators[name]; + if (validator) { + fns.push(validator); + if (value) { + var valueParams = name === "pattern" ? [value] : value.split(','); + valueParams.unshift(null); // placeholder for input's value + params[name] = valueParams; + } + } + } + + /*** + * Checks whether the form/input elements are valid + * @param input => input element(s) or a jquery selector, null for full form validation + * @param silent => do not show error messages, just return true/false + * @returns {boolean} return true when valid false otherwise + */ + self.validate = function (input, silent) { + silent = input && silent === true || input === true; + var fields = self.fields; + if (input !== true && input !== false) { + if (input instanceof HTMLElement) { + fields = [input.pristine]; + } else if (input instanceof NodeList || input instanceof (window.$ || Array) || input instanceof Array) { + fields = Array.from(input).map(function (el) { + return el.pristine; + }); + } + } + + var valid = true; + + for (var i = 0; fields[i]; i++) { + var field = fields[i]; + if (_validateField(field)) { + !silent && _showSuccess(field); + } else { + valid = false; + !silent && _showError(field); + } + } + return valid; + }; + + /*** + * Get errors of a specific field or the whole form + * @param input + * @returns {Array|*} + */ + self.getErrors = function (input) { + if (!input) { + var erroneousFields = []; + for (var i = 0; i < self.fields.length; i++) { + var field = self.fields[i]; + if (field.errors.length) { + erroneousFields.push({ input: field.input, errors: field.errors }); + } + } + return erroneousFields; + } + if (input.tagName && input.tagName.toLowerCase() === "select") { + return input.pristine.errors; + } + return input.length ? input[0].pristine.errors : input.pristine.errors; + }; + + /*** + * Validates a single field, all validator functions are called and error messages are generated + * when a validator fails + * @param field + * @returns {boolean} + * @private + */ + function _validateField(field) { + var errors = []; + var valid = true; + for (var i = 0; field.validators[i]; i++) { + var validator = field.validators[i]; + var params = field.params[validator.name] ? field.params[validator.name] : []; + params[0] = field.input.value; + if (!validator.fn.apply(field.input, params)) { + valid = false; + + if (typeof validator.msg === "function") { + errors.push(validator.msg(field.input.value, params)); + } else if (typeof validator.msg === "string") { + errors.push(tmpl.apply(validator.msg, params)); + } else if (validator.msg === Object(validator.msg) && validator.msg[currentLocale]) { + // typeof generates unnecessary babel code + errors.push(tmpl.apply(validator.msg[currentLocale], params)); + } else if (field.messages[currentLocale] && field.messages[currentLocale][validator.name]) { + errors.push(tmpl.apply(field.messages[currentLocale][validator.name], params)); + } else if (lang[currentLocale] && lang[currentLocale][validator.name]) { + errors.push(tmpl.apply(lang[currentLocale][validator.name], params)); + } + + if (validator.halt === true) { + break; + } + } + } + field.errors = errors; + return valid; + } + + /*** + * Add a validator to a specific dom element in a form + * @param elem => The dom element where the validator is applied to + * @param fn => validator function + * @param msg => message to show when validation fails. Supports templating. ${0} for the input's value, ${1} and + * so on are for the attribute values + * @param priority => priority of the validator function, higher valued function gets called first. + * @param halt => whether validation should stop for this field after current validation function + */ + self.addValidator = function (elem, fn, msg, priority, halt) { + if (elem instanceof HTMLElement) { + elem.pristine.validators.push({ fn: fn, msg: msg, priority: priority, halt: halt }); + elem.pristine.validators.sort(function (a, b) { + return b.priority - a.priority; + }); + } else { + console.warn("The parameter elem must be a dom element"); + } + }; + + /*** + * An utility function that returns a 2-element array, first one is the element where error/success class is + * applied. 2nd one is the element where error message is displayed. 2nd element is created if doesn't exist and cached. + * @param field + * @returns {*} + * @private + */ + function _getErrorElements(field) { + if (field.errorElements) { + return field.errorElements; + } + var errorClassElement = findAncestor(field.input, self.config.classTo); + var errorTextParent = null, + errorTextElement = null; + if (self.config.classTo === self.config.errorTextParent) { + errorTextParent = errorClassElement; + } else { + errorTextParent = errorClassElement.querySelector('.' + self.config.errorTextParent); + } + if (errorTextParent) { + errorTextElement = errorTextParent.querySelector('.' + PRISTINE_ERROR); + if (!errorTextElement) { + errorTextElement = document.createElement(self.config.errorTextTag); + errorTextElement.className = PRISTINE_ERROR + ' ' + self.config.errorTextClass; + errorTextParent.appendChild(errorTextElement); + errorTextElement.pristineDisplay = errorTextElement.style.display; + } + } + return field.errorElements = [errorClassElement, errorTextElement]; + } + + function _showError(field) { + var errorElements = _getErrorElements(field); + var errorClassElement = errorElements[0], + errorTextElement = errorElements[1]; + + if (errorClassElement) { + errorClassElement.classList.remove(self.config.successClass); + errorClassElement.classList.add(self.config.errorClass); + } + if (errorTextElement) { + errorTextElement.innerHTML = field.errors.join('
'); + errorTextElement.style.display = errorTextElement.pristineDisplay || ''; + } + } + + /*** + * Adds error to a specific field + * @param input + * @param error + */ + self.addError = function (input, error) { + input = input.length ? input[0] : input; + input.pristine.errors.push(error); + _showError(input.pristine); + }; + + function _removeError(field) { + var errorElements = _getErrorElements(field); + var errorClassElement = errorElements[0], + errorTextElement = errorElements[1]; + if (errorClassElement) { + // IE > 9 doesn't support multiple class removal + errorClassElement.classList.remove(self.config.errorClass); + errorClassElement.classList.remove(self.config.successClass); + } + if (errorTextElement) { + errorTextElement.innerHTML = ''; + errorTextElement.style.display = 'none'; + } + return errorElements; + } + + function _showSuccess(field) { + var errorClassElement = _removeError(field)[0]; + errorClassElement && errorClassElement.classList.add(self.config.successClass); + } + + /*** + * Resets the errors + */ + self.reset = function () { + for (var i = 0; self.fields[i]; i++) { + self.fields[i].errorElements = null; + } + Array.from(self.form.querySelectorAll('.' + PRISTINE_ERROR)).map(function (elem) { + elem.parentNode.removeChild(elem); + }); + Array.from(self.form.querySelectorAll('.' + self.config.classTo)).map(function (elem) { + elem.classList.remove(self.config.successClass); + elem.classList.remove(self.config.errorClass); + }); + }; + + /*** + * Resets the errors and deletes all pristine fields + */ + self.destroy = function () { + self.reset(); + self.fields.forEach(function (field) { + delete field.input.pristine; + }); + self.fields = []; + }; + + self.setGlobalConfig = function (config) { + defaultConfig = config; + }; + + return self; + } + + /*** + * + * @param name => Name of the global validator + * @param fn => validator function + * @param msg => message to show when validation fails. Supports templating. ${0} for the input's value, ${1} and + * so on are for the attribute values + * @param priority => priority of the validator function, higher valued function gets called first. + * @param halt => whether validation should stop for this field after current validation function + */ + Pristine.addValidator = function (name, fn, msg, priority, halt) { + _(name, { fn: fn, msg: msg, priority: priority, halt: halt }); + }; + + Pristine.addMessages = function (locale, messages) { + var langObj = lang.hasOwnProperty(locale) ? lang[locale] : lang[locale] = {}; + + Object.keys(messages).forEach(function (key, index) { + langObj[key] = messages[key]; + }); + }; + + Pristine.setLocale = function (locale) { + currentLocale = locale; + }; + + return Pristine; + +}))); diff --git a/meetingroom.php b/meetingroom.php new file mode 100644 index 0000000..0b357c3 --- /dev/null +++ b/meetingroom.php @@ -0,0 +1,33 @@ + + + + + + + + + Kaltura Job Application Video Interview Room + + + + + + + + + +
+

Kaltura Job Application Video Interview

+ +
+ + + \ No newline at end of file diff --git a/record.php b/record.php new file mode 100644 index 0000000..1a35992 --- /dev/null +++ b/record.php @@ -0,0 +1,152 @@ + + + + + + + + + Kaltura Record Job Application + + + + + + + + + + + +
+

Kaltura Video Job Application

+
+

Please record a brief video below.

+

🤓 We love quirky so make it fun!
we'd like to know you, and why you'd make an awesome Kalturian!

+
+ +
+
+
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/recruiter.php b/recruiter.php new file mode 100644 index 0000000..21f92de --- /dev/null +++ b/recruiter.php @@ -0,0 +1,200 @@ +objects as $scheduledEvent) { + if (verify_entry_exist($scheduledEvent->entryIds, $recruiterKs) == true) { + $roomlink = create_room_link($scheduledEvent, $uid, $userName, $apiAdminSecret, $partnerId, $sessionType); + $events[] = array( + 'Video' => null, + 'Applicantion ID' => $scheduledEvent->entryIds, + 'Details' => nl2br($scheduledEvent->summary), + 'Start Date' => $scheduledEvent->startDate, + 'Room Url' => wrap_as_link($roomlink, '👩‍💻 Enter interview room') + ); + } +} + +function verify_entry_exist($eid, $ks) +{ + $mediaGetUrl = 'https://cdnapisec.kaltura.com/api_v3/service/media/action/get/format/1/entryId/' . $eid . '/ks/' . $ks; + try { + $mediaEntry = json_decode(file_get_contents($mediaGetUrl)); + } catch (Exception $e) { + var_dump($e); + exit(1); + } + return isset($mediaEntry->id); +} + +function wrap_as_link($link, $txt) +{ + return '' . $txt . ''; +} + +function create_room_link($schedEvent, $userId, $userName, $apiAdminSecret, $partnerId, $sessionType) +{ + $startDate = DateTime::createFromFormat('U', $schedEvent->startDate); + // create the Kaltura Session for authenticated room participant + // to make sure we give the session enough time to live, we'll set it to when the meeting is supposed to end + 2 hours extra + $participantSessionEndDate = (clone $startDate)->add(new DateInterval('PT2H')); // add 2 hours to meeting end time + $sessionExpiry = $participantSessionEndDate->getTimestamp() - $startDate->getTimestamp(); //unixtimestamps are in seconds + $participantSessionPrivileges = "eventId:$schedEvent->id,role:viewerRole,userContextualRole:3,firstName:$userName"; + $eventParticipantKsUrl = 'https://cdnapisec.kaltura.com/api_v3/service/session/action/start/format/1/secret/' . $apiAdminSecret . '/partnerId/' . $partnerId . '/type/' . $sessionType . '/expiry/' . $sessionExpiry . '/userId/' . $userId . '/privileges/' . $participantSessionPrivileges; + $eventParticipantKs = file_get_contents($eventParticipantKsUrl); + $eventParticipantKs = trim($eventParticipantKs, '"'); + + // since the room link is rather lengthy with the secure session, we will create a shortlink for it + // construct the room link with the KS + $meetingRoomUrl = get_base_url() . '/meetingroom.php?ks=' . $eventParticipantKs; + + return $meetingRoomUrl; +} + +?> + + + + + + + + Kaltura Record Job Application + + + + + + + + + + + + + +
+

Available Video Job Applications

+ + + + +
+
+ + + + + diff --git a/recruiterlogin.php b/recruiterlogin.php new file mode 100644 index 0000000..d9180f4 --- /dev/null +++ b/recruiterlogin.php @@ -0,0 +1,115 @@ + + + + + + + + + Kaltura Record Job Application + + + + + + + + + + + + +
+

Recruiter Login

+
+
+ +
+
+ +
+ +
+ +
+ + + + diff --git a/review.php b/review.php new file mode 100644 index 0000000..a384a91 --- /dev/null +++ b/review.php @@ -0,0 +1,127 @@ + + + + + + + + + Kaltura Job Application Review + + + + + + + + + + + +
+

Kaltura Video Job Application

+

 Fetching available meeting times...

+ +
+
+
+
+
+
+
+

+ Your application ID: id; ?>
+ description); ?> +

+
+ +
+
+ + + \ No newline at end of file diff --git a/utils.php b/utils.php new file mode 100644 index 0000000..eefcf28 --- /dev/null +++ b/utils.php @@ -0,0 +1,20 @@ +