diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..cc7ae09
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: radio-station
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..dd84ea7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/developer_task.md b/.github/ISSUE_TEMPLATE/developer_task.md
new file mode 100644
index 0000000..b4cafa0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/developer_task.md
@@ -0,0 +1,20 @@
+---
+name: Developer task
+about: Suggest an specific task for a developer
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your developer task related to a problem or a requirement? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..bbcbbe7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae2f4a5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+.DS_Store*
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+**/.idea/*
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..477cda5
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
new file mode 100644
index 0000000..a44c6b2
--- /dev/null
+++ b/.idea/php.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/radio-station.iml b/.idea/radio-station.iml
new file mode 100644
index 0000000..3b9e703
--- /dev/null
+++ b/.idea/radio-station.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..6bcae0b
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $PROJECT_DIR$/composer.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1657688918584
+
+
+ 1657688918584
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\!WP\InstantWP_4.5\iwpserver\htdocs\wordpress
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..3f13059
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,717 @@
+# Radio Station Changelog
+
+***
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+= 2.5.18 =
+* Updated: Freemius SDK (2.13.0)
+* Updated: Plugin Panel (1.3.7) for delayed translations
+* Updated: Radio Player (1.0.4)
+* Changed: split station content and settings Admin menus
+* Changed: display label of Overrides to Specials
+* Changed: handle of radio-player assets to stream-player
+* Improved: Related Show and Linked Show select Show ordering
+* Improved: handle Special/Override meta in Archive shortcode
+* Improved: edit Special/Override permissions for linked Shows
+* Improved: Timezone and Clock Timezone display formatting
+* Improved: Special override interface change highlighting
+* Added: Dashboard overview widget and Content Dashboard page
+* Added: Station Tagline, Callsign, Frequency, Location options
+* Added: Default Player display for Station Meta
+* Added: Special Overrides data routes/feed to Data API
+* Added: Solid and Solid with Transparent Outline Player Buttons
+* Added: Link main language term to filtered show archive
+* Added: Channel input support for Shifts and Overrides
+* Added: Genre and Description filters for admin Show list
+* Fixed: Player Amplitude script pause/unpause source bug
+* Fixed: Special/Override (non-linked) Host/Producer data
+* Fixed: Radio Player (other developer) plugin conflicts
+* Fixed: Override Drafts without dates not listing in Admin
+* Fixed: Block asset paths for script/style file versions
+* Fixed: Display user timezone for timezone shortcode
+* Removed: Freemius Pricing v2 filter (merged in Freemius)
+
+= 2.5.17 =
+* Fixed: Current Show widget reloading when Shift display is off
+
+= 2.5.16 =
+* Fixed: Player Block control color style preview / loading
+
+= 2.5.15 =
+* Improved: AJAX player style output moved to footer
+* Option: Use Stream Player for latest Show file playback
+* Option: Use Stream Player for Episode file playback (Pro)
+
+= 2.5.14 =
+* Fixed: Player resume (play/pause/play) glitch for Amplitude
+* Fixed: Keep active day when changing schedule weeks on Grid view
+* Fixed: Schedule Override list Override Date column display
+* Fixed: jPlayer change old fallback SWF path to empty string
+
+= 2.5.13 =
+* Updated: Freemius SDK (2.12.0)
+* Fixed: load text domain too early notice
+* Fixed: added nonce checks to offer dismissals
+
+= 2.5.12 =
+* Updated: Block translation and element functions
+* Fixed: preload player settings when Block editing
+
+= 2.5.11 =
+* Added: AJAX load of Schedule views for cachebusting
+* Fixed: instance validation for schedule loader shortcode
+* Fixed: mismatched function name in popup player
+
+= 2.5.10 =
+* Updated: Freemius SDK (2.11.0)
+* Updated: reader.php with prefixed reader functions
+* Updated: Plugin Panel (1.3.5) with new reader function
+* Updated: Color Picker Alpha Library (3.0.4)
+* Updated: Howler Library (2.2.4)
+* Improved: use wp_kses on player widget output
+* Improved: player volume slider background sync/hover
+* Fixed: Freemius optin image URL path
+
+= 2.5.9 =
+* Fixed: Missing use of prepare method on some database queries
+* Added: Vulnerability disclosure program link to FAQ
+* Added: Edit Host/Producer link to admin Show List column
+
+= 2.5.8 =
+* Fixed: Template display for themes showing excerpts on singular pages
+* Fixed: Possible next show duplicates current show in Data API
+* Fixed: Save multiple Show related post values
+* Fixed: Count bug on Override Archive shortcode
+* Fixed: Date/time display on Override Archive shortcode
+* Fixed: Show file Disable Download checkbox saving
+* Added: Use automatic Embeds on external Show file URLs
+* Added: Nonce check to notice dismissal AJAX
+* Changed: Removed player state saving iframe
+
+= 2.5.7 =
+* Updated: Freemius SDK (2.6.0)
+* Disabled: Howler Player Script (browser compatibility issues)
+* Improved: Schedule Engine Weekdate calculations
+* Fixed: AJAX shortcode/widget loading for current time
+* Fixed: Widget title display duplication
+
+= 2.5.6 =
+* Updated: Freemius SDK (2.5.11)
+* Updated: Plugin Panel (1.3.0)
+* Added: Filter for query and meta for show post list shortcode
+* Updated: Language translations file (.pot)
+* Updated: Bundled Dutch translation
+* Fixed: check linked override shifts before displaying
+* Fixed: hide empty widgets to work in AJAX loading mode
+* Fixed: remove direct usages of date function
+* Fixed: display of show posts on show page (query)
+* Fixed: Data API next_show data field
+* Fixed: minor schedule engine issues
+* Improved: more consistent sanitization and escaping
+
+= 2.5.5 =
+* Updated: Freemius SDK (2.5.10)
+* Added: RSS Posts Feed: Related Show node
+* Added: RSS Show Feed: Host/Producer node
+* Fixed: Prefix Block element JS constant to prevent conflict (EventOn)
+* Fixed: RSS Posts Feed: filter by Show conflict
+
+= 2.5.4 =
+* Updated: Freemius SDK (2.5.9)
+* Fixed: Missing player back-compat arguments
+
+= 2.5.3 =
+* Fixed: Bug in Admin Override Timeslot List
+
+= 2.5.2 =
+* Fixed: Bug retrieving show data for linked overrides
+
+= 2.5.1 =
+* Fixed: Widget Countdown Timer Display Bug
+* Fixed: Pro Player Backwards Compatibility
+
+= 2.5.0 =
+* Added: Radio Station Blocks! (converted Widgets)
+* Updated: Freemius SDK (2.5.7)
+* Updated: Plugin Panel (1.2.9)
+* Updated: AmplitudeJS (5.3.2)
+* Updated: Howler (2.2.3)
+* Updated: Moment JS (2.29.4) with WP Loading
+* Improved: Refactored Schedule Engine Class
+* Improved: Redesigned higher resolution player buttons
+* Improved: Standardized Widget Input Fields
+* Improved: WordPress Coding Standards
+* Improved: Sanitization using KSES
+* Improved: Translation Implementation
+* Improved: use WP JSON functions for data endpoints
+* Improved: Schedule Templates to use Classes and Instances
+* Improved: Tab Schedule default date display on
+* Improved: use wp_send_json for feed endpoints
+* Added: Freemius Pricing Page v2
+* Added: assign Playlist to a specific Show Shift
+* Added: Quick Edit of Playlist to assign to Show
+* Added: Volume Control options to Player widget
+* Fixed: Countdowns with multiple widget instances
+* Fixed: Radio Player iOS no volume control detection
+* Fixed: Mobile detection (via any pointer type)
+* Fixed: Genre/Language Archive Pagination
+* Fixed: Adjacent Post Links (where show has one shift)
+* Fixed: Workaround Amplitude pause event not firing
+* Fixed: inline scripts when main script in head tag
+* Security Fix: Escape all debug output content
+
+### 2.4.0.9
+* Update: Sysend (1.11.1) for Radio Player
+* Fixed: missing register REST routes permission_callback argument
+* Fixed: added property_exists checks for PHP8 TypeErrors
+
+### 2.4.0.8
+* Update: Plugin Panel (1.2.2)
+* Added: filter plugin icon for Freemius activation screen
+* Updated: clear plugin updates transient on activation/deactivation
+* Fixed: filter plugin updates to prevent Pro ever overwriting Free
+* Changed: plugin options array moved to a separate file
+
+### 2.4.0.7
+* Fix: remove debug output breaking redirects/data endpoints
+* Updated: main language translation file
+* Added: list of Pro filters to documentation
+
+### 2.4.0.6
+* Update: Freemius SDK (2.4.3)
+* Updated: documentation links to new demo site address
+* Fixed: remove duplicate Related Show box in Post Quick Edit
+* Fixed: multiple attributes for automatic pages shortcodes
+* Fixed: hide inactive tab shortcode section on tab click
+* Fixed: undefined warning for debugshifts
+* Fixed: current show in schedule when on exact start second
+* Added: filters for time and date separators
+* Added: description/excerpt to single show data endpoint
+
+### 2.4.0.5
+* Fixed: plugin conflicts causing fatal errors
+
+### 2.4.0.4
+* Improved: clear cache on show/override status transitions
+* Fixed: DJ / Host can edit own/others Show permissions
+* Fixed: Override link to show dropdown query
+* Fixed: Fallback scripts and fallback stream URLs
+* Fixed: Radio Clock responsive width display
+* Fixed: Collapse descriptions for non-show pages
+* Fixed: Deduplicate dates in week (daylight saving fix)
+
+### 2.4.0.3
+* Update: Plugin Panel (1.2.1) with zero value save and tab fixes
+* Added: option to disable player audio fallback scripts
+* Added: option to hide various volume controls
+* Improved: lazy load player audio fallback scripts
+* Improved: added author support to post types for quick edit
+* Refix: missing fix to active day tab on pageload
+* Fixed: player volume slider background position (cross-browser)
+* Fixed: missing title value for adjacent post links
+* Fixed: Fallback scripts and fallback stream URLs
+
+### 2.4.0.2
+* Fixed: Multiple Player instance IDs
+* Fixed: Player loading button glow animation
+* Added: Enabled Pro Pricing plans page
+* Added: Widget type specific classes
+* Added: Alternative text positions in Player
+* Added: Pause button graphics to Player
+
+### 2.4.0.1
+* Fixed: Rounded player play button background corner style
+* Fixed: Tabbed schedule active day tab on pageload
+* Improved: Radio Clock Widget layout
+
+### 2.4.0
+* Added: Radio Stream Player!
+* Fixed: Shows archive shortcode with no Shows selected
+
+### 2.3.3.9
+* Update: Plugin Panel (1.1.8) with Number Step Min/Max fix
+* Update: Freemius SDK (2.4.2)
+* Improved: Allow for Multiple Override Times (with AJAX Saving)
+* Improved: Markdown Extra Compatibility for PHP 7.4+
+* Added: Link Override to Show Data with selectable Show Fields
+* Added: Language Archive Shortcode (similar to Genre Archive)
+* Added: Display Linked Override Date List on Show Pages
+* Added: Automatic user showtime conversion and display
+* Fixed: Show Schedule sometimes starting on previous week
+* Fixed: Current Show highlighting timer interval cycling
+* Fixed: Before and After Show classes when no current Show
+* Fixed: Shows Data Endpoint 24 Hour Shift Format and Encore Switch
+* Fixed: Multiple host separator display in Current Show Widget
+* Fixed: Playlist Widget playlist ended label when no next playlist
+* Fixed: Conflicting duplicate filter name for Show Avatar
+* Fixed: Time conversions where start/finish Show/Override is equal
+* Fixed: Show page subarchive lists pagination button arrow display
+* Fixed: Show Shifts with same start time overwriting bug
+
+### 2.3.3.8
+* Update: Plugin Panel (1.1.7) with Image and Color Picker fields
+& Documentation: Full Plugin Filter List added to docs/Filters.md
+* Added: Stream Format and Fallback/Format selection setting
+* Added: Station Image and Station Title for future Player Display
+* Added: Station Email Address setting with default display option
+* Added: Section order filtering for Master Schedule Views
+* Added: Section display filtering for Master Schedule Views
+* Added: Section display filtering for Widget sections
+* Added: Show image alignment attribute to Schedule Tabs View
+* Added: Show Description/Excerpt to Show Data Endpoint (via querystring)
+* Added: Reduced opacity for past Shows on Schedule Tab/Table Views
+* Added: Screen Reader text for Show icons on Show Page
+* Fixed: Display Widget Countdown when no Current Show/Playlist
+* Fixed: Check for explicit singular.php template usage setting
+* Fixed: Access to Shows Data via querystring of Show ID/name
+* Fixed: Shows Data for Genres/Languages querystring of ID/name
+* Fixed: Override Display order output for Tab/List Views
+
+### 2.3.3.7
+* Fixed: Schedule Overrides overlapping multiple Show shifts
+* Fixed: Bulk Edit field repetition and possible jQuery conflict
+* Fixed: Related Posts check producing error output
+* Fixed: WordPress Readme Parser deprecated errors for PHP7
+
+### 2.3.3.6
+* Update: Freemius SDK (2.4.1)
+* Update: Plugin Loader (1.1.6) with phone number and CSV validation
+* Added: Station phone number setting with default display option
+* Added: Schedule classes for Shows before and after current Show
+* Improved: current Show highlighting on Schedule for overnight shifts
+* Improved: info section reordering filters on single Show template
+* Fixed: Edit permissions checks for Related to Show post assignments
+* Fixed: Main Language option value for WordPress Setting
+* Fixed: make Date on Tab clickable on Tabbed Schedule View
+* Fixed: prevent possible conflicts with changes not saved reload message
+* Fixed: do not conflict check Shift against itself for last shift check
+* Fixed: link back to Show posts for related Show posts (allow multiple)
+* Fixed: filter next/previous post link for (multiple) related Show posts
+* Fixed: automatic pages conflict where themes filter the_content early
+
+### 2.3.3.5
+* Fixed: use schedule based on start_day if specified for Schedule view
+* Fixed: day left/right shifting on Schedule table/tab mobile views
+* Added: past/today/future filter for Schedule Override List
+* Added: filter for Schedule display start day (and to accept today)
+* Added: current playlist (if any) to Broadcast Data endpoint
+
+### 2.3.3.4
+* Improved: auto-match show description to info height on Show pages
+* Improved: allow multiple Related Show selection for single post
+* Improved: ability to assign Post to relate to multiple Shows
+* Added: Related Show Post List column and Quick Edit field
+* Added: Related Show selection Bulk Edit Action for Post List
+* Added: filters for label texts and title attributes on Show Page
+* Added: filter for label text above Show Player (default empty)
+
+### 2.3.3.3
+* Fixed: improved Current Show and Upcoming Shows calculations
+* (Display showtimes when show starts before and ends after midnight)
+
+### 2.3.3.2
+* Update: Freemius SDK (2.4.0)
+* Update: Plugin Loader (1.1.4) with weird isset glitch fix
+* Fixed: Current Show for Shows ending at midnight
+* Fixed: incorrect AJAX Widget plugin setting value
+* Fixed: use pageload data for schedules before transients
+
+### 2.3.3
+* Update: Plugin Loader (1.1.3) with non-strict select match fix
+* Improved: width responsiveness for table/tabbed Schedule views
+* Improved: show shifts interface background colors
+* Added: navigate away from page on shift change check
+* Added: default time format option to Widgets
+* Removed: current show transients (intermittant unreliability)
+* Fixed: AJAX call causing plugin conflicts via save_post action
+* Fixed: calculation of Upcoming Shows near end of the week
+* Fixed: remove and duplicate actions on new shifts
+
+### 2.3.2
+* Update: Plugin Loader (1.1.2) with settings link fix
+* Improved: use plugin timezone setting for all times
+* Improved: show shift conflict checker logic
+* Added: Radio Clock Widget for user/server time display
+* Added: AJAX widget load option (to bypass page caches)
+* Added: automated show schedule highlighting (table/tabs/list)
+* Added: playlist track arrows for re-ordering tracks
+* Added: AJAX save of show shifts and playlist tracks
+* Added: post type editing metabox position filtering
+* Added: more display attributes to Master Schedule shortcode
+* Added: time format filters for time output displays
+* Added: javascript user timezone display on Master Schedule
+* Fixed: handling of UTC only timezone settings
+* Fixed: added check for empty role capabilities
+* Fixed: added settings submenu redirection fix
+* Fixed: show and override midnight end conflict
+* Fixed: calculate next shows at end of schedule week
+* Fixed: metaboxes disappearing on position sorting
+* Fixed: move tracks marked New to end of Playlist on update
+* Fixed: override shift array output showing above schedule
+* Fixed: master schedule specify days attribute bug
+* Fixed: display real end time of overnight split shifts
+* Fixed: master schedule display with days attribute
+* Fixed: logic for Affected Shifts in override list
+* Fixed: removed auto-tab selection change on tab view resize
+* Fixed: Current Show widget schedule/countdown for Overrides
+* Fixed: multiple overrides in schedule range variable conflict
+
+### 2.3.1
+* Update: Plugin Loader (1.1.1) with Freemius first path fix
+* Fixed: conditions for Schedule Override time calculations
+* Fixed: schedule table view - 12 hour format with translations
+* Fixed: schedule table view hour column width style
+* Fixed: javascript table/tab arrows to prevent default click
+* Fixed: undefined index warning when saving show with no shifts
+* Fixed: append not echo override date to shortcode archive list
+* Fixed: compatibility with multiple the_content calls (Yoast)
+* Fixed: reset to showcontinued flag in Schedule (table view)
+* Added: option to clear transients on every pageload
+* Added: show avatar and featured image URLs to Data API output
+* Added: option to ping Netmix directory on show updates
+* Added: filters for widget section display order
+
+### 2.3.0
+* Include: Plugin Loader (1.1.0) with plugin options and settings
+* Include: Freemius SDK (2.3.0) and Freemius integration
+* Feature: assign new Producer role to a Show for Show displays
+* Feature: internal Schedule Show Shift Conflict checking
+* Feature: Show Shift saving completeness and conflict checking
+* Feature: added Data Endpoints API via WordPress REST and Feeds
+* Feature: options to set Page and default View for Master Schedule
+* Feature: post type Archive Shortcodes and Show-related Shortcodes
+* Feature: display Radio Timezone on Master Schedule table view
+* Feature: added Show Header image to Shows for single Show display
+* Feature: added Show Language Taxonomy to Shows (and Overrides)
+* Feature: added Countdown clock for Show and Playlists Widgets
+* Improved: new Data Model and Schedule (with Override) Calculation
+* Improved: new Show Content Template layout display method
+* Improved: new Playlist Content Template layout display method
+* Improved: added multiple Genre highlight selection on Master Schedule
+* Improved: added Custom Field and Revision support to post types
+* Improved: missing output sanitization throughout the plugin
+* Improved: added file hierarchy fallbacks for CSS, JS and Templates
+* Improved: enqueue conditional scripts inline instead of echoing
+* Improved: Master Schedule displays enhancements and styling
+* Improved: add Responsiveness to Master Schedule Table and Tab View
+* Improved: add View/Edit links for editing custom post types
+* Improved: load Datepicker styles locally instead of via Google
+* Improved: add debug function for debug display and logging
+* Improved: add links from Show Posts back to Show Page
+* Improved: added Duplicate Shift button to Show Shift Editing
+* Roles: new Show Producer role (same capabilities as DJ / Host)
+* Roles: new Show Editor role (edit permissions but not Admin)
+* Roles: Changed DJ role Label to DJ / Host (for talk show usage)
+* Admin: Added Plugin Settings Admin Page (via Plugin Loader)
+* Admin: Added plugin Upgrade / Updated details admin notices
+* Admin: Schedule conflict notice and Show conflicts in Shift column
+* Admin: Show/Override content indicator columns to Admin Show list
+* Admin: Show Description helper text metabox on Show edit screen
+* Admin: Fix to restore Admin Bar New/Edit links for plugin post types
+* Admin: Store installed version for future updates and announcements
+* Disabled: automatic loading of old templates (non theme agnostic)
+
+### 2.2.8
+* Fix to remove strict type checking from in_array (introduced 2.2.6)
+* Fix to mismatched flush rewrite rules flag function name
+* Fix to undefined index warnings for new Schedule Overrides
+* Fix to not 404 author pages for DJs without blog posts
+* Fix to implode blog array for Show blog post listing
+
+### 2.2.7
+* Dutch translation added (Thank you to André Dortmont for the file!)
+* Added Tabbed Display for Master Schedule Shortcode (via Tutorial)
+* Add Show list columns with active, shift, DJs and show image displays
+* Add Schedule Override list columns with date sorting and filtering
+* Add playlist track information labels to Now Playing Widget
+* Added meridiem (am/pm) translations via WP Locale class
+* Added star rating link to plugin announcement box
+* Added update subscription form to plugin Help page
+* Fix to checkbox value saving for On Air/Upcoming Widgets
+* Fix 12 hour show time display in Upcoming Widget
+* Fix PM 12 hour shot time display in On Air Widget
+* Fix to schedule override date picker value visibility
+* Fix to weekday and month translations to use WP Locale
+* Fix to checkbox value saving in Upcoming Widget
+* Split Plugin Admin Functions into separate file
+* Split Post Type Admin Functions into separate include
+* Revert anonymous function use in widget registrations
+
+### 2.2.6
+* Reorganize master-list shortcode into templates
+* Add constant for plugin directory
+* Use WP_Query instead of get_posts
+* New posts_per_page and tax_query
+* Fixes for undefined indexes
+* Fixes for raw mysql queries
+* Typecasting to support strict comparisons
+
+### 2.2.5
+* WordPress coding standards and best practices (thanks to Mike Garrett @mikengarrett)
+
+### 2.2.4
+* added title position and avatar width options to widgets
+* added missing DJ author links as new option to widgets
+* cleanup, improve and fix enqueued Widget CSS (on air/upcoming)
+* improved to show Encore Presentation in show widget displays
+* fix to Show shift Encore Presentation checkbox saving
+
+### 2.2.3
+* added flush rewrite rules on plugin activation/deactivation
+* added show_admin_column and show_in_quick_edit for Genres
+* added show metadata and schedule value sanitization
+* fix to 00 minute validation for Schedule Override
+* convert span tags to div tags in Widgets to fix line breaks
+
+### 2.2.2
+* shift main playlist and show metaboxes above editor
+* set plugin custom post types editor to Classic Editor
+* add high priority to side metaboxes for plugin post types
+* added dismissable development changeover admin notice
+* added simple Patreon supporter image button and blurb
+* added filter for DJ Avatar size on Author page template
+* fix to Schedule Override metabox value saving
+* fix to Playlist track list items overflowing metabox
+* fix to shift up time row on Master Schedule table view
+* fix to missing weekday headings in Master Schedule table
+* fix to weekday display for Upcoming DJ Widget
+* fix to user display labels on select DJ metabox
+* fix to file_exists check for DJ on Air stylesheet path
+* fix to make DJ multi-select input full metabox width
+* fix to expand admin menu when on genre taxonomy page
+* fix to expand admin menu when editing plugin post types
+* fix to genre submenu item link for current page
+* added GitHub URI to plugin header for GitHub updater
+
+### 2.2.1
+* Re-commit all missing files via SVN
+
+### 2.2.0
+* WordPress coding standards refactoring for WP 5 (thanks to Tony Hayes @majick777)
+* fixed the protocol in jQuery UI style Google URL
+* reprefixed all functions for consistency (radio_station_)
+* updated all the widget constructor methods
+* merged the menu items into a single main menu
+* updated the capability checks for the menu items
+* moved the help and export pages to /templates/
+* moved all the css files to /css/
+* enqeued the djonair css from within the widget
+* use plugins_url for all resource URLs
+* added $wpdb->prepare to sanitize a query
+* added some sanization for metabox save values
+* added a week and month translation helper
+* added a radio station antenna icon
+
+### 2.1.3
+* Added method for displaying schedule for only a single day (see readme section for the master-schedule shortcode for details).
+
+### 2.1.2
+* Compatibility fix for Wordpress 4.3.x - Updated the widgets to use PHP5 constructors instead of the deprecated PHP4 constructors.
+* Catalan translation added (Thank you to Victor Riera for the file!)
+
+### 2.1.1
+* Bug fix - Fixed day of the week language translation issue in master schedule shortcode
+* Bug fix - Added some error checking in the sidebar widgets
+* New Feature - Added ability to give schedule overrides a featured image
+* New Feature - Added built-in help page
+
+### 2.1
+* General code cleanup, 4.1 compatibility testing, and changes for better efficiency.
+* Bug fix - Fixed issue with early morning shows spanning entire column in the programming grid shortcode
+* New Feature - Master programming grid can now be displayed in div format, as well as the original table and list formats.
+
+### 2.0.16
+* Minor revisions to German translation.
+* Fixed a bug that was resetting custom-sert role capabilities for the DJ role.
+
+### 2.0.15
+* German translation added (Thank you to Ian Hook for the file!)
+
+### 2.0.14
+* Fixed issue on the master schedule where genres containing more than one work wouldn't highlight when clicked
+* Added ability to display DJ names on the master schedule.
+* Fixed bug in the Upcoming widget. Override Schedule no longer display as upcoming when they are on-air.
+* Verified compatibility woth WordPress 4.0
+
+### 2.0.13
+* Added the ability to display show avatars on the program grid.
+* Added the ability to display show description in the now on-air widget and short code.
+
+### 2.0.12
+* Fixed a bug in the master schedule shortcode
+
+### 2.0.11
+* Russian translation added (Thank you to Alexander Esin for the file!)
+
+### 2.0.10
+* Fixed role/capability conflict with WP User Avatar plugin.
+* Added the missing leading zero to 24-hour time format on the master schedule.
+* Fixed dj_get_current function so that it no longer returns shows that have been moved to the trash.
+* Fixed dj_get_next function so that it no longer ignores the "Active" checkbox on a show.
+* Added some CSS ids and classes to the master program schedule list format to make it more useful
+
+### 2.0.9
+* Fixed broken upcoming show shortcode.
+* Added ability to display DJ names along with the show title in the widgets.
+
+### 2.0.8
+* Fixed the display of schedules for upcoming shows in the widget and shortcode.
+* Fixed a bug in the dj_get_next function that was causing it to ignore the beginning of the next week at the end of the current week.
+
+### 2.0.7
+* Fixed scheduling bug in shortcode function
+
+### 2.0.6
+* Master Schedule now displays days starting with the start_of_week option set in the WordPress General Settings panel.
+* Fixed issue with shows that have been unplublished still showing up on the master schedule.
+* Fixed missing am/pm text on shows that run overnight on the master schedule.
+* Fixed an issue with shows that run overnight not spanning the correct number of hours on the second day on the master schedule.
+* Fixed problem in Upcoming DJ Widget that wasn't displaying the correct upcoming shift.
+
+### 2.0.5
+* Fixed an issue with some shows displaying in 24 hour time on master schedule grid even though 12-hour time is specified
+* Fixed a bug in the On-Air widget that was preventing shows spanning two day from displaying
+* Added code to enable theme support for post-thumbnails on the "show" post-type so users don't have to add it to their theme's functions.php file anymore.
+
+### 2.0.4
+* Master Schedule bug for shows that start at midnight and end before the hour is up fixed.
+
+### 2.0.3
+* Compatibility fix: Fixed a jquery conflict in the backend that was occuring in certain themes
+
+### 2.0.2
+* Bug fix: Scheduling issue with overnight shows fixed
+
+### 2.0.1
+* Bug fix: Fixed PHP error in Playlist save function that was triggered during preview
+* Bug fix: Fixed PHP notice in playlist template file
+* Bug fix: Fixed PHP error in dj-widget shortcode
+
+### 2.0.0
+* Major code reorganization for better future development
+* PHP warning fix
+* Enabled option to add comments on Shows and Playlists
+* Added option to show either single or multiple schedules in the On Air widget
+
+### 1.6.2
+* Minor PHP warning fixes
+
+### 1.6.1
+* Bug fix: Some of the code added in the previous update uses the array_replace() function that is only available in PHP 5.3+. Added a fallback for older PHP versions.
+
+### 1.6.0
+* Added the ability to override the weekly schedule to allow one-off events to be scheduled
+* Added a list format option to the master schedule shortcode
+* Added Italian translation (it_IT) (thank you to Cristofaro Giuseppe!)
+
+### 1.5.4
+* Fixed some PHP notices that were being generated when there were no playlist entries in the system.
+
+### 1.5.3
+* Added Serbian translation (sr_RS) (thank you to Miodarag Zivkovic!)
+
+### 1.5.2.1
+* Removed some debug code from one of the template files
+
+### 1.5.2
+* Fixed some localization bugs.
+* Added Albanian translation (sq_AL) (thank you to Lorenc!)
+
+### 1.5.1
+* Fixed some localization bugs.
+* Added French translation (fr_FR) (a big thank you to Dan over at [BuddyPress France](http://bp-fr.net/).
+
+### 1.5.0
+* Plugin modified to allow for internationalization.
+* Spanish translation (es_ES) added.
+
+### 1.4.6
+* Fixed a bug with shows that start at midnight not displaying in the on-air sidebar widget.
+* Switched DJ/Show avatars in the widgets to use the featured image of the show instead of gravatar.
+* Updated show template to get rid of a PHP warning that appeared if the show had no schedules.
+* Fixed some other areas of the code that were generating PHP notices in WordPress 3.6
+* Added CSS classes to master program schedule output so CSS rules can be applied to specific shows
+* Added new attribute to the list-shows shortcode to allow only specified genres to be displayed
+
+### 1.4.5
+* Fixed master-schedule shortcode bug that was preventing display of 12 hour time
+
+### 1.4.4
+* Compatibility fix for Wordpress 3.6 - fixed problem with giving alternative roles DJ capabilities
+* Fixed some areas of the code that were generating PHP notices in WordPress 3.6
+
+### 1.4.3
+* Master schedule shortcode now displays indiviual shows in both 24 and 12 hour time
+* Fixed some areas of the code that were generating PHP notices in WordPress 3.6
+* Added example of how to display show schedule to single-show.php template
+* Added more options to the plugin's widgets
+* Added new options to the master-schedule shortcode
+
+### 1.4.2
+* Fixed a bug in the CSS file override from theme directory
+
+### 1.4.1
+* Fixed issue with templates copied to the theme directory not overriding the defaults correctly
+* Fixed incorrectly implemented wp_enqueue_styles()
+* Removed deprecated escape_attribute() function from the plugin widgets
+* Fixed some areas of the code that were generating PHP notices
+
+### 1.4.0
+* Compatibility fix for WordPress 3.6
+
+### 1.3.9
+* Fixed a bug that was preventing sites using a non-default table prefix from seeing the list of DJs on the add/edit show pages
+
+### 1.3.8
+* Changes to fix the incorrect list of available shows on the Add Playlist page
+* Removing Add Show links from admin menu for DJs, since they don't have permission to use them anyway.
+
+### 1.3.7
+* Fixed a scheduling bug in the upcoming shows widget
+* By popular request, switched the order of artist and song in the now playing widget
+
+### 1.3.6
+* Fixed issue with shows that run overnight not showing up correctly in the sidebar widgets
+
+### 1.3.5
+* Fixed a time display bug in the DJ On-Air sidebar widget
+* Fixed a display bug on the master schedule with overnight shows
+
+### 1.3.4
+* By request, added as 24-hour time format option to the master schedule and sidebar widgets.
+
+### 1.3.3
+* Added the ability to assign any user with the edit_shows capability as a DJ, to accomodate custom and edited roles.
+
+### 1.3.2
+* Fixed a bug in the DJ-on-air widget
+
+### 1.3.1
+* Fixed a major bug in the master schedule output
+
+### 1.3
+* Fixed some minor compatibility issues with WordPress 3.5
+* Fixed Shows icon in Dashboard
+
+### 1.2
+* Fixed thumbnail bug in sidebar widgets
+* Added new widget to display upcoming shows
+* Added pagination options for playlists and show blogs
+
+### 1.1
+* Fixed playlist edit screen so that queued songs fall to the bottom of the list to maintain play order
+* Reduced the size of the content field in the playlist post type
+* Some minor formatting changes to default templates
+* Added genre highlighter to the master programming schedule page
+* Added a second Update button on the bottom of the playlist edit page for convinience.
+* Added sample template for DJ user pages
+* Fixed a bug in the master schedule shortcode that messed up the table for shows that are more than two hours in duration
+* Fixed a bug in the master schedule shortcode to accomodate shows that run from late night into the following morning.
+* Added new field to associate blog posts with shows
+
+### 1.0
+* Initial release
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e72bfdd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
\ No newline at end of file
diff --git a/assets/banner-1544x500.jpg b/assets/banner-1544x500.jpg
new file mode 100644
index 0000000..0698ec7
Binary files /dev/null and b/assets/banner-1544x500.jpg differ
diff --git "a/assets/banner-772\303\227250.jpg" "b/assets/banner-772\303\227250.jpg"
new file mode 100644
index 0000000..616d56f
Binary files /dev/null and "b/assets/banner-772\303\227250.jpg" differ
diff --git a/assets/icon-128x128.png b/assets/icon-128x128.png
new file mode 100644
index 0000000..7275039
Binary files /dev/null and b/assets/icon-128x128.png differ
diff --git a/assets/icon-256x256.png b/assets/icon-256x256.png
new file mode 100644
index 0000000..888e20b
Binary files /dev/null and b/assets/icon-256x256.png differ
diff --git a/assets/screenshot-1.png b/assets/screenshot-1.png
new file mode 100644
index 0000000..d9a8a3b
Binary files /dev/null and b/assets/screenshot-1.png differ
diff --git a/assets/screenshot-2.png b/assets/screenshot-2.png
new file mode 100644
index 0000000..b1a4844
Binary files /dev/null and b/assets/screenshot-2.png differ
diff --git a/assets/screenshot-3.png b/assets/screenshot-3.png
new file mode 100644
index 0000000..da8b98d
Binary files /dev/null and b/assets/screenshot-3.png differ
diff --git a/assets/screenshot-4.png b/assets/screenshot-4.png
new file mode 100644
index 0000000..aef0697
Binary files /dev/null and b/assets/screenshot-4.png differ
diff --git a/assets/screenshot-5.png b/assets/screenshot-5.png
new file mode 100644
index 0000000..92992c4
Binary files /dev/null and b/assets/screenshot-5.png differ
diff --git a/assets/screenshot-6.png b/assets/screenshot-6.png
new file mode 100644
index 0000000..63d778a
Binary files /dev/null and b/assets/screenshot-6.png differ
diff --git a/assets/screenshot-7.png b/assets/screenshot-7.png
new file mode 100644
index 0000000..d06dcfa
Binary files /dev/null and b/assets/screenshot-7.png differ
diff --git a/assets/screenshot-8.png b/assets/screenshot-8.png
new file mode 100644
index 0000000..74da81b
Binary files /dev/null and b/assets/screenshot-8.png differ
diff --git a/blocks/archive.js b/blocks/archive.js
new file mode 100644
index 0000000..fad5189
--- /dev/null
+++ b/blocks/archive.js
@@ -0,0 +1,259 @@
+/**
+ * === Radio Archive Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ archive_options = [
+ { label: rs__( 'Shows', 'radio-station' ), value: 'shows' },
+ { label: rs__( 'Overrides', 'radio-station' ), value: 'overrides' },
+ { label: rs__( 'Playlists', 'radio-station' ), value: 'playlists' },
+ { label: rs__( 'Shows by Genre', 'radio-station' ), value: 'genres' },
+ { label: rs__( 'Shows by Language', 'radio-station' ), value: 'languages' },
+ ];
+ pro_archive_options = archive_options;
+ pro_archive_options[5] = { label: rs__( 'Episodes', 'radio-station' ), value: 'episodes' };
+ pro_archive_options[6] = { label: rs__( 'Hosts', 'radio-station' ), value: 'hosts' };
+ pro_archive_options[7] = { label: rs__( 'Producers', 'radio-station' ), value: 'producers' };
+ /* pro_archive_options[8] = { label: rs__( 'Team', 'radio-station' ), value: 'team' }; */
+
+ registerBlockType( 'radio-station/archive', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Archive List',
+ description: rs__( 'Archive list for Radio Station record types.', 'radio-station' ),
+ icon: 'media-audio',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Archive List Details --- */
+ archive_type: { type: 'string', default: 'shows' },
+ view: { type: 'string', default: 'list' },
+ perpage: { type: 'number', default: 10 },
+ pagination: { type: 'boolean', default: true },
+ hide_empty: { type: 'boolean', default: false },
+
+ /* --- Archive Record Query --- */
+ orderby: { type: 'string', default: 'title' },
+ order: { type: 'string', default: 'ASC' },
+ status: { type: 'string', default: 'publish' },
+ genre: { type: 'string', default: '' },
+ language: { type: 'string', default: '' },
+
+ /* === Archive Record Display === */
+ description: { type: 'string', default: 'excerpt' },
+ time_format: { type: 'string', default: '' },
+ show_avatars: { type: 'boolean', default: true }, /* shows and overrides only */
+ with_shifts: { type: 'boolean', default: true }, /* shows only */
+ show_dates: { type: 'boolean', default: true }, /* overrides only */
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Controls
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ if ( atts.pro ) {
+ archive_type_options = pro_archive_options;
+ archive_type_help = rs__( 'Which type of records to display.', 'radio-station' );
+ } else {
+ archive_type_options = archive_options;
+ archive_type_help = rs__( 'Episodes, Hosts and Producer archives available in Pro version.', 'radio-station' );
+ }
+
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/archive', className: 'radio-archive-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+ /* === Archive List Details === */
+ rs_el( PanelBody, { title: rs__( 'Archive List Details', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Archive Type --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Archive Type', 'radio-station' ),
+ help: archive_type_help,
+ options: archive_type_options,
+ onChange: ( value ) => {
+ props.setAttributes( { archive_type: value } );
+ },
+ value: atts.archive_type,
+ })
+ ),
+ /* --- Archive View --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Archive View', 'radio-station' ),
+ options : [
+ { label: rs__( 'List View', 'radio-station' ), value: 'list' },
+ { label: rs__( 'Grid View', 'radio-station' ), value: 'grid' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { view: value } );
+ },
+ value: atts.view
+ })
+ ),
+ /* --- Per Page --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Records Per Page', 'radio-station' ),
+ help: rs__( 'Use 0 for all records.', 'radio-station' ),
+ min: 0,
+ max: 100,
+ onChange: ( value ) => {
+ props.setAttributes( { perpage: value } );
+ },
+ value: atts.perpage
+ })
+ ),
+ /* --- Pagination --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Pagination?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { pagination: value } );
+ },
+ checked: atts.pagination,
+ })
+ ),
+ /* --- Hide if Empty --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Hide if Empty?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { hide_empty: value } );
+ },
+ checked: atts.hide_empty,
+ })
+ ),
+ ),
+
+ /* === Archive Record Query === */
+ rs_el( PanelBody, { title: rs__( 'Archive Record Query', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ rs_el( SelectControl, {
+ label: rs__( 'Order By', 'radio-station' ),
+ options: [
+ { label: rs__( 'Title', 'radio-station' ), value: 'title' },
+ { label: rs__( 'Publish Date', 'radio-station' ), value: 'date' },
+ { label: rs__( 'Modified Date', 'radio-station' ), value: 'modified' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { orderby: value } );
+ },
+ value: atts.orderby
+ }),
+ rs_el( RadioControl, {
+ label: rs__( 'Order', 'radio-station' ),
+ options: [
+ { label: rs__( 'Ascending', 'radio-station' ), value: 'ASC' },
+ { label: rs__( 'Descending', 'radio-station' ), value: 'DESC' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { order: value } );
+ },
+ selected: atts.order
+ }),
+ /* TODO: --- Status Picker ? --- */
+ /* TODO: --- Genre Picker ? --- */
+ /* TODO: --- Language Picker ? --- */
+ ),
+
+ /* === Archive Record Display === */
+ rs_el( PanelBody, { title: rs__( 'Archive Record Display', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Time Format --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Time Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( '12 Hour', 'radio-station' ), value: '12' },
+ { label: rs__( '24 Hour', 'radio-station' ), value: '24' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_format: value } );
+ },
+ value: atts.time_format
+ })
+ ),
+ /* --- Description --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Description Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'View Default', 'radio-station' ), value: '' },
+ { label: rs__( 'None', 'radio-station' ), value: 'none' },
+ { label: rs__( 'Excerpt', 'radio-station' ), value: 'excerpt' },
+ { label: rs__( 'Full', 'radio-station' ), value: 'full' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { description: value } );
+ },
+ value: atts.description
+ })
+ ),
+ /* --- Image Display (conditional) --- */
+ ( ( atts.archive_type == 'shows' || atts.archive_type == 'overrides' ) &&
+ rs_el( PanelRow, { className: 'shows-only overrides-only' },
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Image?', 'radio-station' ),
+ help: rs__( 'This setting is for Shows and Overrides.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_avatars: value } );
+ },
+ checked: atts.show_avatars
+ })
+ )
+ ),
+ /* --- With Shifts Only (conditional) --- */
+ ( ( atts.archive_type == 'shows' ) &&
+ rs_el( PanelRow, { className: 'shows-only' },
+ rs_el( ToggleControl, {
+ label: rs__( 'Only Shows with Shifts?', 'radio-station' ),
+ help: rs__( 'This setting is for Shows only.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { with_shifts: value } );
+ },
+ checked: atts.with_shifts
+ })
+ )
+ ),
+ /* --- Override Dates (conditional) --- */
+ ( ( atts.archive_type == 'overrides' ) &&
+ rs_el( PanelRow, { className: 'overrides-only' },
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Override Dates?', 'radio-station' ),
+ help: rs__( 'This setting is for Overrides only.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_dates: value } );
+ },
+ checked: atts.show_dates
+ })
+ )
+ )
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/blocks/clock.js b/blocks/clock.js
new file mode 100644
index 0000000..32453f1
--- /dev/null
+++ b/blocks/clock.js
@@ -0,0 +1,143 @@
+/**
+ * === Radio Clock Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ registerBlockType( 'radio-station/clock', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Radio Clock',
+ description: rs__( 'Radio Station Clock time display.', 'radio-station' ),
+ icon: 'clock',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Clock Display Options --- */
+ time_format: { type: 'string', default: '' },
+ day: { type: 'string', default: 'full' },
+ date: { type: 'boolean', default: true },
+ month: { type: 'string', default: 'full' },
+ zone: { type: 'boolean', default: true },
+ seconds: { type: 'boolean', default: true },
+
+ /* TODO: add extended clock shortcode attributes
+ code: { type: 'boolean', default: true },
+ region: { type: 'boolean', default: true },
+ location: { type: 'boolean', default: true },
+ offset: { type: 'boolean', default: true },
+ timezone: { type: 'string', default: '' }, */
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/clock', className: 'radio-clock-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+ rs_el( PanelBody, { title: rs__( 'Clock Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* Time Display Format */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Time Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( '12 Hour', 'radio-station' ), value: '12' },
+ { label: rs__( '24 Hour', 'radio-station' ), value: '24' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_format: value } );
+ },
+ value: atts.time_format
+ })
+ ),
+ /* Day Display Format */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Day Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Full', 'radio-station' ), value: 'full' },
+ { label: rs__( 'Short', 'radio-station' ), value: 'short' },
+ { label: rs__( 'None', 'radio-station' ), value: 'none' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { day: value } );
+ },
+ value: atts.day
+ })
+ ),
+ /* Date Display */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Date?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { date: value } );
+ },
+ checked: atts.date,
+ })
+ ),
+ /* Month Display Format */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: 'Month Display Format',
+ options: [
+ { label: rs__( 'Full', 'radio-station' ), value: 'full' },
+ { label: rs__( 'Short', 'radio-station' ), value: 'short' },
+ { label: rs__( 'None', 'radio-station' ), value: 'none' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { month: value } );
+ },
+ value: atts.month
+ })
+ ),
+ /* Timezone Display */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Timezone?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { zone: value } );
+ },
+ checked: atts.zone,
+ })
+ ),
+ /* Seconds Display */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Seconds?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { seconds: value } );
+ },
+ checked: atts.seconds,
+ })
+ ),
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/blocks/current-playlist.js b/blocks/current-playlist.js
new file mode 100644
index 0000000..85451d7
--- /dev/null
+++ b/blocks/current-playlist.js
@@ -0,0 +1,220 @@
+/**
+ * === Radio Current Playlist Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ registerBlockType( 'radio-station/current-playlist', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Current Playlist',
+ description: rs__( 'Radio Station current playlist block.', 'radio-station' ),
+ icon: 'playlist-audio',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Loading Options --- */
+ ajax: { type: 'string', default: '' },
+ /* dynamic: { type: 'string', default: '' }, */
+ hide_empty: { type: 'boolean', default: false },
+
+ /* --- Playlist Display Options --- */
+ playlist_title: { type: 'boolean', default: false },
+ link: { type: 'boolean', default: true },
+ countdown: { type: 'boolean', default: true },
+ no_playlist: { type: 'string', default: '' },
+
+ /* --- Track Display Options --- */
+ song: { type: 'boolean', default: true },
+ artist: { type: 'boolean', default: true },
+ album: { type: 'boolean', default: false },
+ label: { type: 'boolean', default: false },
+ comments: { type: 'boolean', default: false },
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/current-playlist', className: 'radio-playlist-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+
+ // === Loading Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'AJAX Load Block', 'radio-station' ),
+ help: rs__( 'To bypass page caching.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { ajax: value } );
+ },
+ value: atts.ajax
+ })
+ ),
+ /* --- [Pro] Dynamic Reloading --- */
+ rs_el( PanelRow, {},
+ ( ( atts.pro ) &&
+ rs_el( SelectControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Reloads at show changeover times.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { dynamic: value } );
+ },
+ value: atts.dynamic
+ })
+ ), ( ( !atts.pro ) &&
+ rs_el( BaseControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Show changeover reloading available in Pro.', 'radio-station' ),
+ })
+ )
+ ),
+ /* --- Hide if Empty --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Hide if Empty?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { hide_empty: value } );
+ },
+ checked: atts.hide_empty,
+ })
+ ),
+ ),
+
+ /* === Playlist Display Panel === */
+ rs_el( PanelBody, { title: rs__( 'Extra Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Playlist Title --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Playlist Title', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { playlist_title: value } );
+ },
+ checked: atts.playlist_title,
+ })
+ ),
+ /* --- Link Playlist --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Playlist Page', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link: value } );
+ },
+ checked: atts.link,
+ })
+ ),
+ /* --- No Playlist Text --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'No Current Playlist Text', 'radio-station' ),
+ help: rs__( 'Blank for default. 0 for none.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { no_playlist: value } );
+ },
+ value: atts.no_playlist
+ })
+ ),
+ /* --- Countdown --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Playlist Countdown', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { countdown: value } );
+ },
+ checked: atts.countdown,
+ })
+ ),
+ ),
+
+ /* === Track Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Track Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Song Display --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Song Title', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { song: value } );
+ },
+ checked: atts.song,
+ })
+ ),
+ /* --- Artist Display --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Artist', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { artist: value } );
+ },
+ checked: atts.artist,
+ })
+ ),
+ /* --- Display Album --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Album', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { album: value } );
+ },
+ checked: atts.album,
+ })
+ ),
+ /* --- Display Record Label --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Record Label', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { label: value } );
+ },
+ checked: atts.label,
+ })
+ ),
+ /* --- Display Comments --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Track Comments', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { comments: value } );
+ },
+ checked: atts.comments,
+ })
+ ),
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/blocks/current-show.js b/blocks/current-show.js
new file mode 100644
index 0000000..b9d6e79
--- /dev/null
+++ b/blocks/current-show.js
@@ -0,0 +1,336 @@
+/**
+ * === Radio Current Show Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ /* --- create image size options --- */
+ image_size_options = [];
+ image_sizes = wp.data.select( 'core/block-editor' ).getSettings().imageSizes;
+ for ( i = 0; i < image_sizes.length; i++ ) {
+ image_size_options[i] = { label: image_sizes[i].name, value: image_sizes[i].slug };
+ }
+
+ registerBlockType( 'radio-station/current-show', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Current Show',
+ description: rs__( 'Radio Station current show block.', 'radio-station' ),
+ icon: 'controls-play',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Loading Options --- */
+ ajax: { type: 'string', default: '' },
+ /* dynamic: { type: 'string', default: '' }, */
+ no_shows: { type: 'string', default: '' },
+ hide_empty: { type: 'boolean', default: false },
+
+ /* --- Show Display Options --- */
+ show_link: { type: 'boolean', default: true },
+ title_position: { type: 'string', default: 'right' },
+ show_avatar: { type: 'boolean', default: true },
+ avatar_size: { type: 'string', default: 'thumbnail' },
+ avatar_width: { type: 'number', default: 0 },
+
+ /* --- Show Time Display Options --- */
+ show_sched: { type: 'boolean', default: true },
+ show_all_sched: { type: 'boolean', default: false },
+ countdown: { type: 'boolean', default: true },
+ time_format: { type: 'string', default: '' },
+
+ /* --- Extra Display Options --- */
+ display_hosts: { type: 'boolean', default: false },
+ link_hosts: { type: 'boolean', default: true },
+ /* display_producers: { type: 'boolean', default: false }, */
+ /* link_producers: { type: 'boolean', default: false }, */
+ show_desc: { type: 'boolean', default: false },
+ show_playlist: { type: 'boolean', default: false },
+ show_encore: { type: 'boolean', default: true },
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/current-show', className: 'radio-current-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+
+ // === Loading Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'AJAX Load Block', 'radio-station' ),
+ help: rs__( 'To bypass page caching.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { ajax: value } );
+ },
+ value: atts.ajax
+ })
+ ),
+ /* --- [Pro] Dynamic Reloading --- */
+ rs_el( PanelRow, {},
+ ( ( atts.pro ) &&
+ rs_el( SelectControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Reloads at show changeover times.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { dynamic: value } );
+ },
+ value: atts.dynamic
+ })
+ ), ( ( !atts.pro ) &&
+ rs_el( BaseControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Show changeover reloading available in Pro.', 'radio-station' ),
+ })
+ )
+ ),
+ /* --- No Shows Text --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'No Current Show Text', 'radio-station' ),
+ help: rs__( 'Blank for default. 0 for none.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { no_shows: value } );
+ },
+ value: atts.no_shows
+ })
+ ),
+ /* --- Hide if Empty --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Hide if Empty?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { hide_empty: value } );
+ },
+ checked: atts.hide_empty,
+ })
+ ),
+ ),
+
+ /* === Show Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Show Link --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Show Page', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_link: value } );
+ },
+ checked: atts.show_link,
+ })
+ ),
+ /* --- Title Position --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Title Position', 'radio-station' ),
+ options : [
+ { label: rs__( 'Above Image', 'radio-station' ), value: 'above' },
+ { label: rs__( 'Left of Image', 'radio-station' ), value: 'left' },
+ { label: rs__( 'Right of Image', 'radio-station' ), value: 'right' },
+ { label: rs__( 'Below Image', 'radio-station' ), value: 'below' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { title_position: value } );
+ },
+ value: atts.title_position
+ })
+ ),
+ /* --- Show Avatar --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Image', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_avatar: value } );
+ },
+ checked: atts.show_avatar,
+ })
+ ),
+ /* --- Avatar Size --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Image Size', 'radio-station' ),
+ options: image_size_options,
+ onChange: ( value ) => {
+ props.setAttributes( { avatar_size: value } );
+ },
+ selected: atts.avatar_size
+ })
+ ),
+ /* --- Avatar Width --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Image Width Override', 'radio-station' ),
+ help: rs__( '0 for default.', 'radio-station' ),
+ min: 0,
+ max: 1000,
+ onChange: ( value ) => {
+ props.setAttributes( { avatar_width: value } );
+ },
+ value: atts.avatar_width
+ })
+ ),
+ ),
+
+ /* === Show Time Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Time Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Show Time --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Time', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_sched: value } );
+ },
+ checked: atts.show_sched,
+ })
+ ),
+ /* --- Show Schedule --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display All Show Times', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_all_sched: value } );
+ },
+ checked: atts.show_all_sched,
+ })
+ ),
+ /* --- Countdown --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Remaining Time Countdown', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { countdown: value } );
+ },
+ checked: atts.countdown,
+ })
+ ),
+ /* --- Time Format --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Time Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( '12 Hour', 'radio-station' ), value: '12' },
+ { label: rs__( '24 Hour', 'radio-station' ), value: '24' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_format: value } );
+ },
+ value: atts.time_format
+ })
+ ),
+ ),
+
+ /* === Extra Displays Panel === */
+ rs_el( PanelBody, { title: rs__( 'Extra Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Display Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Hosts', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { display_hosts: value } );
+ },
+ checked: atts.display_hosts,
+ })
+ ),
+ /* --- Link Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Host Profile', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link_hosts: value } );
+ },
+ checked: atts.link_hosts,
+ })
+ ),
+ /* --- Display Producers --- */
+ /* rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Producers', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { display_hosts: value } );
+ },
+ checked: atts.display_hosts,
+ })
+ ), */
+ /* --- Link Producers --- */
+ /* rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Producer Profile', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link_producers: value } );
+ },
+ checked: atts.link_producers,
+ })
+ ), */
+ /* --- Show Description Excerpt --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Show Description Excerpt', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_desc: value } );
+ },
+ checked: atts.show_desc,
+ })
+ ),
+ /* --- Show Playlist --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Playlist', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_playlist: value } );
+ },
+ checked: atts.show_playlist,
+ })
+ ),
+ /* --- Show Encore --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display if Encore Airing', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_encore: value } );
+ },
+ checked: atts.show_encore,
+ })
+ ),
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/blocks/editor.js b/blocks/editor.js
new file mode 100644
index 0000000..009aac0
--- /dev/null
+++ b/blocks/editor.js
@@ -0,0 +1,115 @@
+
+/* === Radio Station Block Editor Scripts === */
+
+const { Icon } = wp.components;
+
+/* --- Subscribe to Block State --- */
+/* ref: https://wordpress.stackexchange.com/a/358256/76440 */
+( () => {
+ let blocksState = wp.data.select( 'core/block-editor' ).getBlocks();
+ wp.data.subscribe( _.debounce( ()=> {
+ newBlocksState = wp.data.select( 'core/block-editor' ).getBlocks();
+ if ( blocksState.length !== newBlocksState.length ) {
+
+ /* --- recheck for needed scripts --- */
+ schedule = player = archive = clock = false;
+ s_multi = s_table = s_tabs = s_list = s_grid = s_calendar = false;
+ for ( i = 0; i < newBlocksState.length; i++ ) {
+ block = newBlocksState[i];
+ if ( block.name == 'radio-station/clock' ) {
+ clock = true;
+ } else if ( block.name == 'radio-station/schedule' ) {
+
+ if ( block.attributes.clock ) {clock = true;}
+
+ /* --- Schedule Views --- */
+ schedule = true;
+ if ( !block.attributes.pro ) {
+ if ( block.attributes.view == 'table' ) {s_table = true;}
+ else if ( block.attributes.view == 'tabs' ) {s_tabs = true;}
+ else if ( block.attributes.view == 'list' ) {s_list = true;}
+ else if ( block.attributes.view == 'grid' ) {s_grid = true;}
+ else if ( block.attributes.view == 'calendar' ) {s_calendar = true;}
+ }
+
+ /* --- [Pro] Multiple Views --- */
+ if ( block.attributes.pro ) {
+ s_multi = true;
+ if ( block.attributes.views.includes( 'table' ) ) {s_table = true;}
+ if ( block.attributes.views.includes( 'tabs' ) ) {s_tabs = true;}
+ if ( block.attributes.views.includes( 'grid' ) ) {s_grid = true;}
+ if ( block.attributes.views.includes( 'calendar' ) ) {s_calendar = true;}
+ if ( block.attributes.views.includes( 'list' ) ) {s_list = true;}
+ }
+
+ } else if ( block.name == 'radio-station/player' ) {
+ player = true;
+ } else if ( block.name == 'radio-station/archive' ) {
+ archive = true;
+ if ( block.attributes.pagination ) {
+ /* TODO: check pagination type */
+ }
+ }
+ }
+ if (clock && !jQuery('#radio-clock-js').length) {
+ radio_station_load_block_script('clock');
+ }
+ if (schedule) {
+ /* --- schedule view scripts --- */
+ if (s_multi && !jQuery('#radio-schedule-multiview-js').length) {
+ radio_station_load_block_script('schedule-multiview');
+ }
+ if (s_table && !jQuery('#radio-schedule-table-js').length) {
+ radio_station_load_block_script('schedule-table');
+ var radio_load_table = setInterval(function() { if (typeof radio_table_initialize == 'function') {
+ radio_table_initialize(); clearInterval(radio_load_table);
+ } }, 1000);
+ }
+ if (s_tabs && !jQuery('#radio-schedule-tabs-js').length) {
+ radio_station_load_block_script('schedule-tabs');
+ var radio_load_tabs = setInterval(function() { if (typeof radio_tabs_initialize == 'function') {
+ radio_tabs_init = false; radio_tabs_initialize(); clearInterval(radio_load_tabs);
+ } }, 1000);
+ }
+ if (s_list && !jQuery('#radio-schedule-list-js').length) {
+ radio_station_load_block_script('schedule-list');
+ var radio_load_list = setInterval(function() { if (typeof radio_list_hightlight == 'function') {
+ radio_list_hightlight(); clearInterval(radio_load_list);
+ } }, 1000);
+ }
+ if (s_grid && !jQuery('#radio-schedule-grid-js').length) {
+ radio_station_load_block_script('schedule-grid');
+ var radio_load_grid = setInterval(function() { if (typeof radio_grid_initialize == 'function') {
+ radio_grid_init = false; radio_grid_initialize(); radio_grid_time_spacing(); clearInterval(radio_load_grid);
+ } }, 1000);
+ }
+ if (s_calendar && !jQuery('#radio-schedule-calendar-js').length) {
+ radio_station_load_block_script('schedule-calendar');
+ var radio_load_calendar = setInterval(function() { if (typeof radio_calendar_initialize == 'function') {
+ radio_calendar_init = false; radio_calendar_initialize(); radio_sliders_check(); clearInterval(radio_load_calendar);
+ } }, 1000);
+ }
+ }
+ if (player) {
+ radio_station_load_block_script('player');
+ }
+ if (archive) {
+ /* TODO: check for archive pagination type */
+ }
+ }
+ blocksState = newBlocksState;
+ }, 300 ) );
+} )();
+
+/* --- Load Block Script --- */
+function radio_station_load_block_script(handle) {
+ id = 'radio-'+handle+'-js';
+ if (!document.getElementById(id)) {
+ url = radio.ajax_url+'?action=radio_station_block_script&handle='+handle;
+ jQuery('html head').append('');
+ }
+ if (typeof radio_station_pro_block_scripts == 'function') {
+ radio_station_pro_load_block_scripts(handle);
+ }
+}
+
diff --git a/blocks/player.js b/blocks/player.js
new file mode 100644
index 0000000..c64ad9b
--- /dev/null
+++ b/blocks/player.js
@@ -0,0 +1,671 @@
+/**
+ * === Radio Player Block ===
+ */
+(() => {
+
+ /* --- Import Modules/Components --- */
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType, getBlockType } = window.wp.blocks;
+ const { InspectorControls, useBlockProps } = window.wp.blockEditor;
+ const { Fragment, useEffect, createRef, useRef } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, ColorPicker, Dropdown, Button, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ /* --- Register Block --- */
+ if ( !getBlockType('radio-station/player' ) ) {
+ registerBlockType( 'radio-station/player', {
+
+ /* --- Block Settings --- */
+ title: rs__( '[Radio Station] Stream Player', 'radio-station' ),
+ description: rs__( 'Audio stream player block.', 'radio-station' ),
+ icon: 'controls-volumeon',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Player Content --- */
+ url: { type: 'string', default: '' },
+ title: { type: 'string', default: '' },
+ image: { type: 'string', default: 'default' },
+ /* --- Player Options --- */
+ script: { type: 'string', default: 'default' },
+ volume: { type: 'number', default: 77 },
+ volumes: { type: 'array', default: ['slider'] },
+ default: { type: 'boolean', default: false },
+ /* --- Player Styles --- */
+ layout: { type: 'string', default: 'horizontal' },
+ theme: { type: 'string', default: 'default' },
+ buttons: { type: 'string', default: 'default' },
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ let blockRef = createRef(null);
+ const blockProps = useBlockProps({ ref: blockRef });
+
+ /* load control colors on render */
+ useEffect(() => {
+ /* console.log('Player block rendered.', atts, blockProps); */
+ radio_player_control_colors(blockProps.id, atts);
+ }, [atts.text_color, atts.background_color, atts.playing_color, atts.button_color, atts.track_color, atts.thumb_color]);
+
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/player', className: 'radio-player-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+ /* === Player Content === */
+ rs_el( PanelBody, { title: rs__( 'Player Content', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Stream URL --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'Stream URL', 'radio-station' ),
+ help: rs__( 'Leave blank to use default stream.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { url: value } );
+ },
+ value: atts.url,
+ })
+ ),
+ /* --- Player Title Text --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'Player Title Text', 'radio-station' ),
+ help: rs__( 'Empty for default, 0 for none.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { title: value } );
+ },
+ value: atts.title
+ })
+ ),
+ /* --- Image --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Player Image', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'Display Station Image', 'radio-station' ), value: '1' },
+ { label: rs__( 'Do Not Display Station Image', 'radio-station' ), value: '0' },
+ /* { label: rs__( 'Display Custom Image', 'radio-station' ), value: 'custom' }, */
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { image: value } );
+ },
+ value: atts.image
+ })
+ )
+ ),
+
+ /* === Player Options === */
+ rs_el( PanelBody, { title: rs__( 'Player Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Script --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Player Script', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'Amplitude', 'radio-station' ), value: 'amplitude' },
+ { label: rs__( 'Howler', 'radio-station' ), value: 'howler' },
+ { label: rs__( 'jPlayer', 'radio-station' ), value: 'jplayer' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { script: value } );
+ },
+ value: atts.script
+ })
+ ),
+ /* --- Volume --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Initial Volume', 'radio-station' ),
+ min: 0,
+ max: 100,
+ onChange: ( value ) => {
+ props.setAttributes( { volume: value } );
+ },
+ value: atts.volume
+ })
+ ),
+ /* --- Volume controls --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ multiple: true,
+ label: rs__( 'Volume Controls', 'radio-station' ),
+ help: rs__( 'Ctrl-Click to select multiple controls.', 'radio-station' ),
+ options: [
+ { label: rs__( 'Volume Slider', 'radio-station' ), value: 'slider' },
+ { label: rs__( 'Up and Down Buttons', 'radio-station' ), value: 'updown' },
+ { label: rs__( 'Mute Button', 'radio-station' ), value: 'mute' },
+ { label: rs__( 'Maximize Button', 'radio-station' ), value: 'max' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { volumes: value } );
+ },
+ value: atts.volumes
+ })
+ ),
+ /* --- Default Player --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Use as Default Player', 'radio-station' ),
+ help: rs__( 'Make this the default player on this page.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { default: value } );
+ },
+ checked: atts.default,
+ })
+ ),
+ /* --- Popup Player Button --- */
+ rs_el( PanelRow, {},
+ ( ( atts.pro ) &&
+ rs_el( SelectControl, {
+ label: rs__( 'Popup Player', 'radio-station' ),
+ help: rs__( 'Enables button to open Player in separate window.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { popup: value } );
+ },
+ value: atts.popup
+ })
+ ), ( ( !atts.pro ) &&
+ rs_el( BaseControl, {
+ label: rs__( 'Popup Player', 'radio-station' ),
+ help: rs__( 'Popup Player Button available in Pro.', 'radio-station' ),
+ })
+ )
+ ),
+ ),
+
+ /* === Player Styles === */
+ rs_el( PanelBody, { title: rs__( 'Player Design', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Player Layout --- */
+ rs_el( PanelRow, {},
+ rs_el( RadioControl, {
+ label: rs__( 'Player Layout', 'radio-station' ),
+ options : [
+ { label: rs__( 'Vertical (Stacked)', 'radio-station' ), value: 'vertical' },
+ { label: rs__( 'Horizontal (Inline)', 'radio-station' ), value: 'horizontal' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { layout: value } );
+ },
+ checked: atts.layout
+ })
+ ),
+ /* --- Player Theme --- */
+ ( ( !atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Player Theme', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'Light', 'radio-station' ), value: 'light' },
+ { label: rs__( 'Dark', 'radio-station' ), value: 'dark' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { theme: value } );
+ },
+ value: atts.theme
+ })
+ )
+ ),
+ /* [Pro] Extra Theme Color Options */
+ ( ( atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Player Theme', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'Light', 'radio-station' ), value: 'light' },
+ { label: rs__( 'Dark', 'radio-station' ), value: 'dark' },
+ { label: rs__( 'Red', 'radio-station' ), value: 'red' },
+ { label: rs__( 'Orange', 'radio-station' ), value: 'orange' },
+ { label: rs__( 'Yellow', 'radio-station' ), value: 'yellow' },
+ { label: rs__( 'Light Green', 'radio-station' ), value: 'light-green' },
+ { label: rs__( 'Green', 'radio-station' ), value: 'green' },
+ { label: rs__( 'Cyan', 'radio-station' ), value: 'cyan' },
+ { label: rs__( 'Light Blue', 'radio-station' ), value: 'light-blue' },
+ { label: rs__( 'Blue', 'radio-station' ), value: 'blue' },
+ { label: rs__( 'Purple', 'radio-station' ), value: 'purple' },
+ { label: rs__( 'Magenta', 'radio-station' ), value: 'magenta' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { theme: value } );
+ },
+ value: atts.theme
+ })
+ )
+ ),
+ /* --- Player Buttons --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Player Buttons', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'Circular', 'radio-station' ), value: 'circular' },
+ { label: rs__( 'Rounded', 'radio-station' ), value: 'rounded' },
+ { label: rs__( 'Square', 'radio-station' ), value: 'square' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { buttons: value } );
+ },
+ value: atts.buttons
+ })
+ )
+ ),
+
+ /* === [Pro] Player Colors === */
+ ( ( atts.pro ) &&
+ rs_el( PanelBody, { title: rs__( 'Player Colors', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+
+ /* --- Text Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Text Color', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ disableAlpha: true,
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {text_color: color.hex} );
+ },
+ color: atts.text_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-text_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Text Color', 'radio-station' )
+ },
+ ( ('' != atts.text_color) ? atts.text_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {text_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Text Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.text_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-text_color {background-color:'+atts.text_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+
+ /* --- Background Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Background Color', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {background_color: color.hex} );
+ },
+ color: atts.background_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-background_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Background Color', 'radio-station' )
+ },
+ ( ('' != atts.background_color) ? atts.background_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {background_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Background Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.background_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-background_color {background-color:'+atts.background_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+
+ /* --- Playing Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Playing Highlight', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {playing_color: color.hex} );
+ },
+ color: atts.playing_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-playing_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Playing Highlight Color', 'radio-station' )
+ },
+ ( ('' != atts.playing_color) ? atts.playing_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {playing_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Playing Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.playing_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-playing_color {background-color:'+atts.playing_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+
+ /* --- Buttons Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Buttons Highlight', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {buttons_color: color.hex} );
+ },
+ color: atts.buttons_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-buttons_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Button Highlight Color', 'radio-station' )
+ },
+ ( ('' != atts.buttons_color) ? atts.buttons_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {buttons_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Button Highlight Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.buttons_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-buttons_color {background-color:'+atts.buttons_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+
+ /* --- Track Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Volume Track', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {track_color: color.hex} );
+ },
+ color: atts.track_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-track_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Volume Track Color', 'radio-station' )
+ },
+ ( ('' != atts.track_color) ? atts.track_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {track_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Volume Track Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.track_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-track_color {background-color:'+atts.track_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+
+ /* --- Thumb Color --- */
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'Volume Thumb', 'radio-station' ),
+ className: 'color-dropdown-control'
+ },
+ rs_el( Dropdown, {
+ renderContent: () => (
+ rs_el( ColorPicker, {
+ defaultValue: '',
+ onChangeComplete: color => {
+ props.setAttributes( {thumb_color: color.hex} );
+ },
+ color: atts.thumb_color
+ })
+ ),
+ renderToggle: (args) => (
+ rs_el( 'div', {className: 'color-dropdown-buttons'},
+ rs_el( Button, {
+ className: 'color-dropdown-thumb_color',
+ onClick: args.onToggle,
+ variant: 'secondary',
+ 'aria-expanded': args.isOpen,
+ 'aria-haspopup': 'true',
+ 'aria-label': rs__( 'Select Volume Thumb Color', 'radio-station' )
+ },
+ ( ('' != atts.thumb_color) ? atts.thumb_color : rs__( 'Select', 'radio-station' ) )
+ ),
+ rs_el( Button, {
+ onClick: () => {
+ props.setAttributes( {thumb_color: ''} );
+ args.onClose();
+ },
+ isSmall: true,
+ variant: 'tertiary',
+ 'aria-label': rs__( 'Clear Volume Thumb Color Selection', 'radio-station' )
+ },
+ rs__( 'Clear', 'radio-station' )
+ ),
+ ( ( '' != atts.thumb_color ) &&
+ rs_el( 'style', {}, '.components-button.is-secondary.color-dropdown-thumb_color {background-color:'+atts.thumb_color+'}' )
+ )
+ )
+ )
+ } )
+ )
+ ),
+ /* --- end color options --- */
+ )
+ ),
+
+ /* === Advanced Options === */
+ ( ( atts.pro ) &&
+ rs_el( PanelBody, { title: rs__( 'Advanced Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Current Show Display --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Current Show Display', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { currentshow: value } );
+ },
+ value: atts.currentshow
+ })
+ ),
+ /* ---Now Playing Display --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Now Playing Track Display', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { nowplaying: value } );
+ },
+ value: atts.nowplaying
+ })
+ ),
+ /* --- Track Animation --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Track Animation', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: 'default' },
+ { label: rs__( 'No Animation', 'radio-station' ), value: 'none' },
+ { label: rs__( 'Left to Right Ticker', 'radio-station' ), value: 'lefttoright' },
+ { label: rs__( 'Right to Left Ticker', 'radio-station' ), value: 'righttoleft' },
+ { label: rs__( 'Back and Forth', 'radio-station' ), value: 'backandforth' },
+ { label: rs__( '', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { animation: value } );
+ },
+ value: atts.animation
+ })
+ ),
+ /* --- Metadata URL --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'Metadata Source URL', 'radio-station' ),
+ help: rs__( 'Defaults to Stream URL.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { metadata: value } );
+ },
+ value: atts.metadata
+ })
+ ),
+ )
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+ }
+})();
+
+/* Load Player Controls Colors */
+function radio_player_control_colors(id, atts) {
+ container = jQuery('#'+id+' .radio-container'); /* console.log(container); */
+ if (!container.length) { setTimeout(function() {radio_player_control_colors(id,atts);}, 1000); return; }
+ instance = container.attr('id').replace('radio_container_','');
+ url = radio_player.settings.ajaxurl+'?action=player_control_styles&instance='+instance+'&text='+encodeURIComponent(atts.text_color)+'&background='+encodeURIComponent(atts.background_color)+'&playing='+encodeURIComponent(atts.playing_color)+'&buttons='+encodeURIComponent(atts.buttons_color)+'&track='+encodeURIComponent(atts.track_color)+'&thumb='+encodeURIComponent(atts.thumb_color);
+ jQuery.ajax({
+ type: 'GET',
+ url: url,
+ data: {'action':'player_control_styles', 'instance':instance, 'text':atts.text_color, 'background':atts.background_color, 'playing':atts.playing_color, 'buttons':atts.buttons_color, 'track':atts.track_color, 'thumb':atts.thumb_color},
+ processData: false,
+ beforeSend: function(request, settings) {
+ request._data = settings.data;
+ },
+ success: function(data, success, request) {
+ if (data.success) {
+ console.log('Load Control Styles Success: '+data.message);
+ if (jQuery('#radio-player-control-styles-'+data.instance).length) {jQuery('#radio-player-control-styles-'+data.instance).remove();}
+ jQuery('body').append('');
+ } else {console.log('Load Control Styles Failed: '+data.message); console.log(request);}
+ },
+ fail: function(request, textStatus, errorThrown) {
+ console.log(request); console.log(textStatus); console.log(errorThrown);
+ }
+ }).catch(function(error) {
+ console.log(error); console.log(jQuery(this));
+ });
+}
+
diff --git a/blocks/schedule.js b/blocks/schedule.js
new file mode 100644
index 0000000..b005c5b
--- /dev/null
+++ b/blocks/schedule.js
@@ -0,0 +1,428 @@
+/**
+ * === Radio Schedule Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ /* --- set schedule view options --- */
+ schedule_views = [
+ { label: rs__( 'Table', 'radio-station' ), value: 'table' },
+ { label: rs__( 'Tabbed', 'radio-station' ), value: 'tabs' },
+ { label: rs__( 'List', 'radio-station' ), value: 'list' },
+ ];
+ pro_views = [
+ { label: rs__( 'Table', 'radio-station' ), value: 'table' },
+ { label: rs__( 'Tabbed', 'radio-station' ), value: 'tabs' },
+ { label: rs__( 'Grid', 'radio-station' ), value: 'grid' },
+ { label: rs__( 'Calendar', 'radio-station' ), value: 'calendar' },
+ { label: rs__( 'List', 'radio-station' ), value: 'list' },
+ ];
+ default_setting = [ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' } ];
+ default_views = default_setting.concat(pro_views);
+
+ registerBlockType( 'radio-station/schedule', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Program Schedule',
+ description: rs__( 'Radio Station Schedule block.', 'radio-station' ),
+ icon: 'calendar-alt',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+
+ /* --- Schedule Display --- */
+ view: { type: 'string', default: 'table' },
+ image_position: { type: 'string', default: 'left' },
+ hide_past_shows: { type: 'boolean', default: false },
+
+ /* --- Header Displays --- */
+ time_header: { type: 'string', default: 'clock' },
+ /* clock: { type: 'boolean', default: true }, */
+ /* timezone: { type: 'boolean', default: true }, */
+ selector: { type: 'boolean', default: true },
+
+ /* --- Times Display --- */
+ display_day: { type: 'string', default: 'short' },
+ display_month: { type: 'string', default: 'short' },
+ start_day: { type: 'string', default: '' },
+ time_format: { type: 'string', default: '' },
+ /* days: { type: '', default: false },*/
+ /* start_date: { type: '', default: false }, */
+ /* active_date: { type: '', default: false }, */
+ /* display_date: { type: 'string', default: 'jS' }, */
+
+ /* --- Show Display --- */
+ show_times: { type: 'boolean', default: true },
+ show_link: { type: 'boolean', default: true },
+ show_image: { type: 'boolean', default: false },
+ show_desc: { type: 'boolean', default: false },
+ show_hosts: { type: 'boolean', default: false },
+ link_hosts: { type: 'boolean', default: false },
+ show_genres: { type: 'boolean', default: false },
+ show_encore: { type: 'boolean', default: true },
+ // show_file: { type: 'boolean', default: false },
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/schedule', className: 'radio-schedule-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+ /* === Schedule Display Panel === */
+ rs_el( PanelBody, { title: rs__( 'Schedule Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- View Selection --- */
+ ( ( !atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Schedule View', 'radio-station' ),
+ help: rs__( 'Grid and Calendar Views available in Pro version.', 'radio-station' ),
+ options: schedule_views,
+ onChange: ( value ) => {
+ props.setAttributes( { view: value } );
+ },
+ value: atts.view
+ })
+ )
+ ),
+ ( ( !atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( BaseControl, {
+ label: rs__( 'View Switching', 'radio-station' ),
+ help: rs__( 'Multiple view switching available in Pro version.', 'radio-station' ),
+ })
+ )
+ ),
+ /* --- [Pro] Multiple View Selection --- */
+ /* ( ( atts.pro && atts.multi_view ) && */
+ ( ( atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ multiple: true,
+ label: rs__( 'Select Schedule Views', 'radio-station' ),
+ help: rs__( 'Ctrl-Click to select multiple views.', 'radio-station' ),
+ options: pro_views,
+ onChange: ( value ) => {
+ props.setAttributes( { views: value } );
+ },
+ value: atts.views
+ })
+ )
+ ),
+ /* --- [Pro] Default View --- */
+ ( ( atts.pro ) &&
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Default View', 'radio-station' ),
+ options: default_views,
+ onChange: ( value ) => {
+ props.setAttributes( { default_view: value } );
+ },
+ value: atts.default_view
+ })
+ )
+ ),
+ /* --- Tab View Options */
+ ( ( ( !atts.pro && ( atts.view == 'tabs' ) )
+ || ( atts.pro && atts.views.includes('tabs') ) ) &&
+ /* --- Image Position --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Image Position', 'radio-station' ),
+ help: rs__( 'Affects Tabbed View only.', 'radio-station' ),
+ options: [
+ { label: rs__( 'Left', 'radio-station' ), value: 'left' },
+ { label: rs__( 'Right', 'radio-station' ), value: 'right' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { image_position: value } );
+ },
+ value: atts.image_position
+ })
+ )
+ ),
+ ( ( ( !atts.pro && ( atts.view == 'tabs' ) )
+ || ( atts.pro && atts.views.includes('tabs') ) ) &&
+ /* --- Hide Past Shows */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Hide Past Shows', 'radio-station' ),
+ help: rs__( 'Affects Tabbed View only.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { hide_past_shows: value } );
+ },
+ checked: atts.hide_past_shows,
+ })
+ )
+ ),
+ /* --- [Pro] Grid View Options --- */
+ ( ( atts.pro && atts.views.includes('grid') ) &&
+ /* --- Grid Width --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Grid Width', 'radio-station' ),
+ help: rs__( 'Grid view Show column width in pixels.', 'radio-station' ),
+ min: 0,
+ max: 1000,
+ onChange: ( value ) => {
+ props.setAttributes( { gridwith: value } );
+ },
+ value: atts.gridwidth
+ })
+ )
+ ),
+ ( ( atts.pro && atts.views.includes('grid') ) &&
+ /* --- Time Spaced Grid --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Time Spaced Grid', 'radio-station' ),
+ help: rs__( 'Line up Shows by times in Grid view.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { time_spaced: value } );
+ },
+ checked: atts.time_spaced,
+ })
+ )
+ ),
+ /* --- [Pro] Calendar View Options --- */
+ ( ( atts.pro && atts.views.includes('calendar') ) &&
+ /* --- Calendar Weeks --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Calendar Weeks', 'radio-station' ),
+ help: rs__( 'Week rows to display in view.', 'radio-station' ),
+ min: 1,
+ max: 8,
+ onChange: ( value ) => {
+ props.setAttributes( { weeks: value } );
+ },
+ value: atts.weeks
+ })
+ )
+ ),
+ ( ( atts.pro && atts.views.includes('calendar') ) &&
+ /* --- Previous Weeks --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Previous Weeks', 'radio-station' ),
+ help: rs__( 'Previous Weeks Display', 'radio-station' ),
+ min: 0,
+ max: 4,
+ onChange: ( value ) => {
+ props.setAttributes( { previous_weeks: value } );
+ },
+ value: atts.previous_weeks,
+ })
+ )
+ )
+ ),
+
+ /* === Header Displays Panel === */
+ rs_el( PanelBody, { title: rs__( 'Header Display Options', 'radio-station' ), initialOpen: false },
+ /* --- Clock/Timezone Header --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Radio Time Header', 'radio-station' ),
+ options: [
+ { label: rs__( 'Display Radio Clock', 'radio-station' ), value: 'clock' },
+ { label: rs__( 'Display Radio Timezone', 'radio-station' ), value: 'timezone' },
+ { label: rs__( 'No Time Header Display', 'radio-station' ), value: 'none' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_header: value } );
+ },
+ value: atts.time_header
+ })
+ ),
+ /* --- Genre Highlighter --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Genre Highlighter', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { selector: value } );
+ },
+ checked: atts.selector,
+ })
+ ),
+ ),
+
+ /* === Time Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Time Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Day Display --- */
+ rs_el( PanelRow, {},
+ rs_el( RadioControl, {
+ label: rs__( 'Day Display Format', 'radio-station' ),
+ options : [
+ { label: 'Abbreviated', value: 'short' },
+ { label: 'Full Name', value: 'full' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { display_day: value } );
+ },
+ selected: atts.display_day
+ })
+ ),
+ /* --- Month Display --- */
+ rs_el( PanelRow, {},
+ rs_el( RadioControl, {
+ label: rs__( 'Month Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Abbreviated', 'radio-station' ), value: 'short' },
+ { label: rs__( 'Full Name', 'radio-station' ), value: 'full' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { display_month: value } );
+ },
+ selected: atts.display_month
+ })
+ ),
+ /* --- Schedule Start Day --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Schedule Start Day', 'radio-station' ),
+ options: [
+ { label: rs__( 'WP Start of Week', 'radio-station' ), value: '' },
+ { label: rs__( 'Today', 'radio-station' ), value: 'today' },
+ { label: rs__( 'Monday', 'radio-station' ), value: 'Monday' },
+ { label: rs__( 'Tuesday', 'radio-station' ), value: 'Tuesday' },
+ { label: rs__( 'Wednesday', 'radio-station' ), value: 'Wednesday' },
+ { label: rs__( 'Thursday', 'radio-station' ), value: 'Thursday' },
+ { label: rs__( 'Friday', 'radio-station' ), value: 'Friday' },
+ { label: rs__( 'Saturday', 'radio-station' ), value: 'Saturday' },
+ { label: rs__( 'Sunday', 'radio-station' ), value: 'Sunday' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { start_day: value } );
+ },
+ value: atts.start_day
+ })
+ ),
+ /* --- Time Format --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Time Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( '12 Hour', 'radio-station' ), value: '12' },
+ { label: rs__( '24 Hour', 'radio-station' ), value: '24' }
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_format: value } );
+ },
+ value: atts.time_format
+ })
+ )
+ ),
+
+ /* === Show Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Show Times --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Time', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_times: value } );
+ },
+ checked: atts.show_times,
+ })
+ ),
+ /* --- Show Link --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Shows', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_link: value } );
+ },
+ checked: atts.show_link,
+ })
+ ),
+ /* --- Show Image --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Image', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_image: value } );
+ },
+ checked: atts.show_image,
+ })
+ ),
+ /* --- Show Description --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Description', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_desc: value } );
+ },
+ checked: atts.show_desc,
+ })
+ ),
+ /* --- Show Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Hosts', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_hosts: value } );
+ },
+ checked: atts.show_hosts,
+ })
+ ),
+ /* --- Link Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Hosts', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link_hosts: value } );
+ },
+ checked: atts.link_hosts,
+ })
+ ),
+ /* --- Show Genres --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Genres', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_genres: value } );
+ },
+ checked: atts.show_genres,
+ })
+ ),
+ /* --- Show Encore --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Encore', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_encore: value } );
+ },
+ checked: atts.show_encore,
+ })
+ ),
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/blocks/upcoming-shows.js b/blocks/upcoming-shows.js
new file mode 100644
index 0000000..e5c9e22
--- /dev/null
+++ b/blocks/upcoming-shows.js
@@ -0,0 +1,317 @@
+/**
+ * === Radio Upcoming Shows Block ===
+ */
+(() => {
+
+ const rs_el = window.wp.element.createElement;
+ const rs__ = window.wp.i18n.__;
+ const { serverSideRender: ServerSideRender } = window.wp;
+ const { registerBlockType } = window.wp.blocks;
+ const { InspectorControls } = window.wp.blockEditor;
+ const { Fragment } = window.wp.element;
+ const { BaseControl, TextControl, SelectControl, RadioControl, RangeControl, ToggleControl, Panel, PanelBody, PanelRow } = window.wp.components;
+
+ /* --- create image size options --- */
+ image_size_options = [];
+ image_sizes = wp.data.select( 'core/block-editor' ).getSettings().imageSizes;
+ for ( i = 0; i < image_sizes.length; i++ ) {
+ image_size_options[i] = { label: image_sizes[i].name, value: image_sizes[i].slug };
+ }
+
+ registerBlockType( 'radio-station/upcoming-shows', {
+
+ /* --- Block Settings --- */
+ title: '[Radio Station] Upcoming Shows',
+ description: rs__( 'Radio Station upcoming shows block.', 'radio-station' ),
+ icon: 'controls-forward',
+ category: 'radio-station',
+ example: {},
+ attributes: {
+ /* --- Loading Options --- */
+ limit: { type: 'number', default: 1 },
+ ajax: { type: 'string', default: '' },
+ /* dynamic: { type: 'string', default: '' }, */
+ no_shows: { type: 'string', default: '' },
+ hide_empty: { type: 'boolean', default: false },
+
+ /* --- Show Display Options --- */
+ show_link: { type: 'boolean', default: true },
+ title_position: { type: 'string', default: 'right' },
+ show_avatar: { type: 'boolean', default: true },
+ avatar_size: { type: 'string', default: 'thumbnail' },
+ avatar_width: { type: 'number', default: 0 },
+
+ /* --- Show Time Display Options --- */
+ show_sched: { type: 'boolean', default: true },
+ countdown: { type: 'boolean', default: true },
+ time_format: { type: 'string', default: '' },
+
+ /* --- Extra Display Options --- */
+ display_hosts: { type: 'boolean', default: false },
+ link_hosts: { type: 'boolean', default: true },
+ /* display_producers: { type: 'boolean', default: false }, */
+ /* link_producers: { type: 'boolean', default: false }, */
+ show_encore: { type: 'boolean', default: true },
+
+ /* --- Hidden Switches --- */
+ channel: { type: 'number', default: 0 },
+ block: { type: 'boolean', default: true },
+ pro: { type: 'boolean', default: false }
+ },
+
+ /**
+ * Edit Block Control
+ */
+ edit: (props) => {
+ const atts = props.attributes;
+ return (
+ rs_el( Fragment, {},
+ rs_el( ServerSideRender, { block: 'radio-station/upcoming-shows', className: 'radio-upcoming-block', attributes: atts } ),
+ rs_el( InspectorControls, {},
+ rs_el( Panel, {},
+
+ // === Loading Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Shows to Display --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Upcoming Shows to Display', 'radio-station' ),
+ min: 1,
+ max: 10,
+ onChange: ( value ) => {
+ props.setAttributes( { limit: value } );
+ },
+ value: atts.limit
+ })
+ ),
+ /* --- AJAX Load --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'AJAX Load Block', 'radio-station' ),
+ help: rs__( 'To bypass page caching.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { ajax: value } );
+ },
+ value: atts.ajax
+ })
+ ),
+ /* --- [Pro] Dynamic Reloading --- */
+ rs_el( PanelRow, {},
+ ( ( atts.pro ) &&
+ rs_el( SelectControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Reloads at show changeover times.', 'radio-station' ),
+ options : [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( 'On', 'radio-station' ), value: 'on' },
+ { label: rs__( 'Off', 'radio-station' ), value: 'off' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { dynamic: value } );
+ },
+ value: atts.dynamic
+ })
+ ), ( ( !atts.pro ) &&
+ rs_el( BaseControl, {
+ label: rs__( 'Dynamic Reloading', 'radio-station' ),
+ help: rs__( 'Show changeover reloading available in Pro.', 'radio-station' ),
+ })
+ )
+ ),
+ /* --- No Shows Text --- */
+ rs_el( PanelRow, {},
+ rs_el( TextControl, {
+ label: rs__( 'No Upcoming Shows Text', 'radio-station' ),
+ help: rs__( 'Blank for default. 0 for none.', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { no_shows: value } );
+ },
+ value: atts.no_shows
+ })
+ ),
+ /* --- Hide if Empty --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Hide if Empty?', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { hide_empty: value } );
+ },
+ checked: atts.hide_empty,
+ })
+ ),
+ ),
+
+ /* === Show Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: true },
+ /* --- Show Link --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Show Page', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_title: value } );
+ },
+ checked: atts.show_link,
+ })
+ ),
+ /* --- Title Position --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Show Title Position', 'radio-station' ),
+ options : [
+ { label: rs__( 'Above Image', 'radio-station' ), value: 'above' },
+ { label: rs__( 'Left of Image', 'radio-station' ), value: 'left' },
+ { label: rs__( 'Right of Image', 'radio-station' ), value: 'right' },
+ { label: rs__( 'Below Image', 'radio-station' ), value: 'below' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { title_position: value } );
+ },
+ value: atts.title_position
+ })
+ ),
+ /* --- Show Avatar --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Image', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_avatar: value } );
+ },
+ checked: atts.show_avatar,
+ })
+ ),
+ /* --- Avatar Size --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Image Size', 'radio-station' ),
+ options: image_size_options,
+ onChange: ( value ) => {
+ props.setAttributes( { avatar_size: value } );
+ },
+ selected: atts.avatar_size
+ })
+ ),
+ /* --- Avatar Width --- */
+ rs_el( PanelRow, {},
+ rs_el( RangeControl, {
+ label: rs__( 'Image Width Override', 'radio-station' ),
+ help: rs__( '0 for default.', 'radio-station' ),
+ min: 0,
+ max: 1000,
+ onChange: ( value ) => {
+ props.setAttributes( { avatar_width: value } );
+ },
+ value: atts.avatar_width
+ })
+ ),
+ ),
+
+ /* === Show Time Display Options === */
+ rs_el( PanelBody, { title: rs__( 'Show Time Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Show Time --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Time', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_sched: value } );
+ },
+ checked: atts.show_sched,
+ })
+ ),
+ /* --- Countdown --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Remaining Time Countdown', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { countdown: value } );
+ },
+ checked: atts.countdown,
+ })
+ ),
+ /* --- Time Format --- */
+ rs_el( PanelRow, {},
+ rs_el( SelectControl, {
+ label: rs__( 'Time Display Format', 'radio-station' ),
+ options: [
+ { label: rs__( 'Plugin Setting', 'radio-station' ), value: '' },
+ { label: rs__( '12 Hour', 'radio-station' ), value: '12' },
+ { label: rs__( '24 Hour', 'radio-station' ), value: '24' },
+ ],
+ onChange: ( value ) => {
+ props.setAttributes( { time_format: value } );
+ },
+ value: atts.time_format
+ })
+ ),
+ ),
+
+ /* === Extra Displays Panel === */
+ rs_el( PanelBody, { title: rs__( 'Extra Display Options', 'radio-station' ), className: 'radio-block-controls', initialOpen: false },
+ /* --- Display Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Hosts', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { display_hosts: value } );
+ },
+ checked: atts.display_hosts,
+ })
+ ),
+ /* --- Link Hosts --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Host Profile', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link_hosts: value } );
+ },
+ checked: atts.link_hosts,
+ })
+ ),
+ /* --- Display Producers --- */
+ /* rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display Show Producers', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { display_hosts: value } );
+ },
+ checked: atts.display_hosts,
+ })
+ ), */
+ /* --- Link Producers --- */
+ /* rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Link to Producer Profile', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { link_producers: value } );
+ },
+ checked: atts.link_producers,
+ })
+ ), */
+ /* --- Show Encore --- */
+ rs_el( PanelRow, {},
+ rs_el( ToggleControl, {
+ label: rs__( 'Display if Encore Airing', 'radio-station' ),
+ onChange: ( value ) => {
+ props.setAttributes( { show_encore: value } );
+ },
+ checked: atts.show_encore,
+ })
+ ),
+ )
+ /* end panels */
+ )
+ )
+ )
+ );
+ },
+
+ /**
+ * Returns nothing because this is a dynamic block rendered via PHP
+ */
+ save: () => null,
+ });
+})();
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..b9fbfe3
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,6 @@
+{
+ "require": {
+ "symfony/yaml": "^5.0",
+ "semantic/ui": "^2.4"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..20fb4cc
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,172 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "d3b0e53293ccc443539489556bce75ac",
+ "packages": [
+ {
+ "name": "semantic/ui",
+ "version": "2.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Semantic-Org/Semantic-UI.git",
+ "reference": "a753d5b7841ab6adbe6d8a3b40086b452f0ba715"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Semantic-Org/Semantic-UI/zipball/a753d5b7841ab6adbe6d8a3b40086b452f0ba715",
+ "reference": "a753d5b7841ab6adbe6d8a3b40086b452f0ba715",
+ "shasum": ""
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jack Lukic",
+ "email": "jacklukic@gmail.com",
+ "homepage": "http://www.jacklukic.com",
+ "role": "Creator"
+ }
+ ],
+ "description": "Semantic empowers designers and developers by creating a shared vocabulary for UI.",
+ "homepage": "http://www.semantic-ui.com",
+ "keywords": [
+ "css",
+ "framework",
+ "semantic",
+ "ui"
+ ],
+ "time": "2018-10-13T22:51:35+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.13.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
+ "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.13-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "time": "2019-11-27T13:56:44+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "847661e77afa48d99ecfa508e8b60f0b029a19c0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/847661e77afa48d99ecfa508e8b60f0b029a19c0",
+ "reference": "847661e77afa48d99ecfa508e8b60f0b029a19c0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "symfony/console": "<4.4"
+ },
+ "require-dev": {
+ "symfony/console": "^4.4|^5.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2019-12-10T11:06:55+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/css/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 0000000..6b1f24d
Binary files /dev/null and b/css/images/ui-bg_glass_55_fbf9ee_1x400.png differ
diff --git a/css/images/ui-bg_glass_65_ffffff_1x400.png b/css/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 0000000..5f6fc57
Binary files /dev/null and b/css/images/ui-bg_glass_65_ffffff_1x400.png differ
diff --git a/css/images/ui-bg_glass_75_dadada_1x400.png b/css/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 0000000..8bc2c8b
Binary files /dev/null and b/css/images/ui-bg_glass_75_dadada_1x400.png differ
diff --git a/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/css/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 0000000..be993d5
Binary files /dev/null and b/css/images/ui-bg_glass_75_e6e6e6_1x400.png differ
diff --git a/css/images/ui-bg_glass_95_fef1ec_1x400.png b/css/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 0000000..b9356a2
Binary files /dev/null and b/css/images/ui-bg_glass_95_fef1ec_1x400.png differ
diff --git a/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 0000000..9e3afb5
Binary files /dev/null and b/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ
diff --git a/css/images/ui-icons_222222_256x240.png b/css/images/ui-icons_222222_256x240.png
new file mode 100644
index 0000000..6ba300b
Binary files /dev/null and b/css/images/ui-icons_222222_256x240.png differ
diff --git a/css/images/ui-icons_2e83ff_256x240.png b/css/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 0000000..1c7b633
Binary files /dev/null and b/css/images/ui-icons_2e83ff_256x240.png differ
diff --git a/css/images/ui-icons_454545_256x240.png b/css/images/ui-icons_454545_256x240.png
new file mode 100644
index 0000000..bd90b26
Binary files /dev/null and b/css/images/ui-icons_454545_256x240.png differ
diff --git a/css/images/ui-icons_888888_256x240.png b/css/images/ui-icons_888888_256x240.png
new file mode 100644
index 0000000..813f18d
Binary files /dev/null and b/css/images/ui-icons_888888_256x240.png differ
diff --git a/css/images/ui-icons_cd0a0a_256x240.png b/css/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 0000000..cfb3d44
Binary files /dev/null and b/css/images/ui-icons_cd0a0a_256x240.png differ
diff --git a/css/index.php b/css/index.php
new file mode 100644
index 0000000..6220032
--- /dev/null
+++ b/css/index.php
@@ -0,0 +1,2 @@
+ .ui-controlgroup-item {
+ float: left;
+ margin-left: 0;
+ margin-right: 0;
+}
+.ui-controlgroup > .ui-controlgroup-item:focus,
+.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus {
+ z-index: 9999;
+}
+.ui-controlgroup-vertical > .ui-controlgroup-item {
+ display: block;
+ float: none;
+ width: 100%;
+ margin-top: 0;
+ margin-bottom: 0;
+ text-align: left;
+}
+.ui-controlgroup-vertical .ui-controlgroup-item {
+ box-sizing: border-box;
+}
+.ui-controlgroup .ui-controlgroup-label {
+ padding: .4em 1em;
+}
+.ui-controlgroup .ui-controlgroup-label span {
+ font-size: 80%;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item {
+ border-left: none;
+}
+.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item {
+ border-top: none;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content {
+ border-right: none;
+}
+.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content {
+ border-bottom: none;
+}
+
+/* Spinner specific style fixes */
+.ui-controlgroup-vertical .ui-spinner-input {
+
+ /* Support: IE8 only, Android < 4.4 only */
+ width: 75%;
+ width: calc( 100% - 2.4em );
+}
+.ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
+ border-top-style: solid;
+}
+
+.ui-checkboxradio-label .ui-icon-background {
+ box-shadow: inset 1px 1px 1px #ccc;
+ border-radius: .12em;
+ border: none;
+}
+.ui-checkboxradio-radio-label .ui-icon-background {
+ width: 16px;
+ height: 16px;
+ border-radius: 1em;
+ overflow: visible;
+ border: none;
+}
+.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
+.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
+ background-image: none;
+ width: 8px;
+ height: 8px;
+ border-width: 4px;
+ border-style: solid;
+}
+.ui-checkboxradio-disabled {
+ pointer-events: none;
+}
+.ui-datepicker {
+ width: 17em;
+ padding: .2em .2em 0;
+ display: none;
+}
+.ui-datepicker .ui-datepicker-header {
+ position: relative;
+ padding: .2em 0;
+}
+.ui-datepicker .ui-datepicker-prev,
+.ui-datepicker .ui-datepicker-next {
+ position: absolute;
+ top: 2px;
+ width: 1.8em;
+ height: 1.8em;
+}
+.ui-datepicker .ui-datepicker-prev-hover,
+.ui-datepicker .ui-datepicker-next-hover {
+ top: 1px;
+}
+.ui-datepicker .ui-datepicker-prev {
+ left: 2px;
+}
+.ui-datepicker .ui-datepicker-next {
+ right: 2px;
+}
+.ui-datepicker .ui-datepicker-prev-hover {
+ left: 1px;
+}
+.ui-datepicker .ui-datepicker-next-hover {
+ right: 1px;
+}
+.ui-datepicker .ui-datepicker-prev span,
+.ui-datepicker .ui-datepicker-next span {
+ display: block;
+ position: absolute;
+ left: 50%;
+ margin-left: -8px;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-datepicker .ui-datepicker-title {
+ margin: 0 2.3em;
+ line-height: 1.8em;
+ text-align: center;
+}
+.ui-datepicker .ui-datepicker-title select {
+ font-size: 1em;
+ margin: 1px 0;
+}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year {
+ width: 45%;
+}
+.ui-datepicker table {
+ width: 100%;
+ font-size: .9em;
+ border-collapse: collapse;
+ margin: 0 0 .4em;
+}
+.ui-datepicker th {
+ padding: .7em .3em;
+ text-align: center;
+ font-weight: bold;
+ border: 0;
+}
+.ui-datepicker td {
+ border: 0;
+ padding: 1px;
+}
+.ui-datepicker td span,
+.ui-datepicker td a {
+ display: block;
+ padding: .2em;
+ text-align: right;
+ text-decoration: none;
+}
+.ui-datepicker .ui-datepicker-buttonpane {
+ background-image: none;
+ margin: .7em 0 0 0;
+ padding: 0 .2em;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+}
+.ui-datepicker .ui-datepicker-buttonpane button {
+ float: right;
+ margin: .5em .2em .4em;
+ cursor: pointer;
+ padding: .2em .6em .3em .6em;
+ width: auto;
+ overflow: visible;
+}
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
+ float: left;
+}
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi {
+ width: auto;
+}
+.ui-datepicker-multi .ui-datepicker-group {
+ float: left;
+}
+.ui-datepicker-multi .ui-datepicker-group table {
+ width: 95%;
+ margin: 0 auto .4em;
+}
+.ui-datepicker-multi-2 .ui-datepicker-group {
+ width: 50%;
+}
+.ui-datepicker-multi-3 .ui-datepicker-group {
+ width: 33.3%;
+}
+.ui-datepicker-multi-4 .ui-datepicker-group {
+ width: 25%;
+}
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
+ border-left-width: 0;
+}
+.ui-datepicker-multi .ui-datepicker-buttonpane {
+ clear: left;
+}
+.ui-datepicker-row-break {
+ clear: both;
+ width: 100%;
+ font-size: 0;
+}
+
+/* RTL support */
+.ui-datepicker-rtl {
+ direction: rtl;
+}
+.ui-datepicker-rtl .ui-datepicker-prev {
+ right: 2px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next {
+ left: 2px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-prev:hover {
+ right: 1px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next:hover {
+ left: 1px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane {
+ clear: right;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button {
+ float: left;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
+.ui-datepicker-rtl .ui-datepicker-group {
+ float: right;
+}
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
+ border-right-width: 0;
+ border-left-width: 1px;
+}
+
+/* Icons */
+.ui-datepicker .ui-icon {
+ display: block;
+ text-indent: -99999px;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ left: .5em;
+ top: .3em;
+}
+.ui-dialog {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: .2em;
+ outline: 0;
+}
+.ui-dialog .ui-dialog-titlebar {
+ padding: .4em 1em;
+ position: relative;
+}
+.ui-dialog .ui-dialog-title {
+ float: left;
+ margin: .1em 0;
+ white-space: nowrap;
+ width: 90%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+ position: absolute;
+ right: .3em;
+ top: 50%;
+ width: 20px;
+ margin: -10px 0 0 0;
+ padding: 1px;
+ height: 20px;
+}
+.ui-dialog .ui-dialog-content {
+ position: relative;
+ border: 0;
+ padding: .5em 1em;
+ background: none;
+ overflow: auto;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ text-align: left;
+ border-width: 1px 0 0 0;
+ background-image: none;
+ margin-top: .5em;
+ padding: .3em 1em .5em .4em;
+}
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+ float: right;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+ margin: .5em .4em .5em 0;
+ cursor: pointer;
+}
+.ui-dialog .ui-resizable-n {
+ height: 2px;
+ top: 0;
+}
+.ui-dialog .ui-resizable-e {
+ width: 2px;
+ right: 0;
+}
+.ui-dialog .ui-resizable-s {
+ height: 2px;
+ bottom: 0;
+}
+.ui-dialog .ui-resizable-w {
+ width: 2px;
+ left: 0;
+}
+.ui-dialog .ui-resizable-se,
+.ui-dialog .ui-resizable-sw,
+.ui-dialog .ui-resizable-ne,
+.ui-dialog .ui-resizable-nw {
+ width: 7px;
+ height: 7px;
+}
+.ui-dialog .ui-resizable-se {
+ right: 0;
+ bottom: 0;
+}
+.ui-dialog .ui-resizable-sw {
+ left: 0;
+ bottom: 0;
+}
+.ui-dialog .ui-resizable-ne {
+ right: 0;
+ top: 0;
+}
+.ui-dialog .ui-resizable-nw {
+ left: 0;
+ top: 0;
+}
+.ui-draggable .ui-dialog-titlebar {
+ cursor: move;
+}
+.ui-draggable-handle {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.ui-resizable {
+ position: relative;
+}
+.ui-resizable-handle {
+ position: absolute;
+ font-size: 0.1px;
+ display: block;
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.ui-resizable-disabled .ui-resizable-handle,
+.ui-resizable-autohide .ui-resizable-handle {
+ display: none;
+}
+.ui-resizable-n {
+ cursor: n-resize;
+ height: 7px;
+ width: 100%;
+ top: -5px;
+ left: 0;
+}
+.ui-resizable-s {
+ cursor: s-resize;
+ height: 7px;
+ width: 100%;
+ bottom: -5px;
+ left: 0;
+}
+.ui-resizable-e {
+ cursor: e-resize;
+ width: 7px;
+ right: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-w {
+ cursor: w-resize;
+ width: 7px;
+ left: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-se {
+ cursor: se-resize;
+ width: 12px;
+ height: 12px;
+ right: 1px;
+ bottom: 1px;
+}
+.ui-resizable-sw {
+ cursor: sw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ bottom: -5px;
+}
+.ui-resizable-nw {
+ cursor: nw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ top: -5px;
+}
+.ui-resizable-ne {
+ cursor: ne-resize;
+ width: 9px;
+ height: 9px;
+ right: -5px;
+ top: -5px;
+}
+.ui-progressbar {
+ height: 2em;
+ text-align: left;
+ overflow: hidden;
+}
+.ui-progressbar .ui-progressbar-value {
+ margin: -1px;
+ height: 100%;
+}
+.ui-progressbar .ui-progressbar-overlay {
+ background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
+ height: 100%;
+ filter: alpha(opacity=25); /* support: IE8 */
+ opacity: 0.25;
+}
+.ui-progressbar-indeterminate .ui-progressbar-value {
+ background-image: none;
+}
+.ui-selectable {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.ui-selectable-helper {
+ position: absolute;
+ z-index: 100;
+ border: 1px dotted black;
+}
+.ui-selectmenu-menu {
+ padding: 0;
+ margin: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ display: none;
+}
+.ui-selectmenu-menu .ui-menu {
+ overflow: auto;
+ overflow-x: hidden;
+ padding-bottom: 1px;
+}
+.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
+ font-size: 1em;
+ font-weight: bold;
+ line-height: 1.5;
+ padding: 2px 0.4em;
+ margin: 0.5em 0 0 0;
+ height: auto;
+ border: 0;
+}
+.ui-selectmenu-open {
+ display: block;
+}
+.ui-selectmenu-text {
+ display: block;
+ margin-right: 20px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui-selectmenu-button.ui-button {
+ text-align: left;
+ white-space: nowrap;
+ width: 14em;
+}
+.ui-selectmenu-icon.ui-icon {
+ float: right;
+ margin-top: 0;
+}
+.ui-slider {
+ position: relative;
+ text-align: left;
+}
+.ui-slider .ui-slider-handle {
+ position: absolute;
+ z-index: 2;
+ width: 1.2em;
+ height: 1.2em;
+ cursor: default;
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.ui-slider .ui-slider-range {
+ position: absolute;
+ z-index: 1;
+ font-size: .7em;
+ display: block;
+ border: 0;
+ background-position: 0 0;
+}
+
+/* support: IE8 - See #6727 */
+.ui-slider.ui-state-disabled .ui-slider-handle,
+.ui-slider.ui-state-disabled .ui-slider-range {
+ filter: inherit;
+}
+
+.ui-slider-horizontal {
+ height: .8em;
+}
+.ui-slider-horizontal .ui-slider-handle {
+ top: -.3em;
+ margin-left: -.6em;
+}
+.ui-slider-horizontal .ui-slider-range {
+ top: 0;
+ height: 100%;
+}
+.ui-slider-horizontal .ui-slider-range-min {
+ left: 0;
+}
+.ui-slider-horizontal .ui-slider-range-max {
+ right: 0;
+}
+
+.ui-slider-vertical {
+ width: .8em;
+ height: 100px;
+}
+.ui-slider-vertical .ui-slider-handle {
+ left: -.3em;
+ margin-left: 0;
+ margin-bottom: -.6em;
+}
+.ui-slider-vertical .ui-slider-range {
+ left: 0;
+ width: 100%;
+}
+.ui-slider-vertical .ui-slider-range-min {
+ bottom: 0;
+}
+.ui-slider-vertical .ui-slider-range-max {
+ top: 0;
+}
+.ui-sortable-handle {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.ui-spinner {
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ padding: 0;
+ vertical-align: middle;
+}
+.ui-spinner-input {
+ border: none;
+ background: none;
+ color: inherit;
+ padding: .222em 0;
+ margin: .2em 0;
+ vertical-align: middle;
+ margin-left: .4em;
+ margin-right: 2em;
+}
+.ui-spinner-button {
+ width: 1.6em;
+ height: 50%;
+ font-size: .5em;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ position: absolute;
+ cursor: default;
+ display: block;
+ overflow: hidden;
+ right: 0;
+}
+/* more specificity required here to override default borders */
+.ui-spinner a.ui-spinner-button {
+ border-top-style: none;
+ border-bottom-style: none;
+ border-right-style: none;
+}
+.ui-spinner-up {
+ top: 0;
+}
+.ui-spinner-down {
+ bottom: 0;
+}
+.ui-tabs {
+ position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+ padding: .2em;
+}
+.ui-tabs .ui-tabs-nav {
+ margin: 0;
+ padding: .2em .2em 0;
+}
+.ui-tabs .ui-tabs-nav li {
+ list-style: none;
+ float: left;
+ position: relative;
+ top: 0;
+ margin: 1px .2em 0 0;
+ border-bottom-width: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
+ float: left;
+ padding: .5em 1em;
+ text-decoration: none;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active {
+ margin-bottom: -1px;
+ padding-bottom: 1px;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
+ cursor: text;
+}
+.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
+ cursor: pointer;
+}
+.ui-tabs .ui-tabs-panel {
+ display: block;
+ border-width: 0;
+ padding: 1em 1.4em;
+ background: none;
+}
+.ui-tooltip {
+ padding: 8px;
+ position: absolute;
+ z-index: 9999;
+ max-width: 300px;
+}
+body .ui-tooltip {
+ border-width: 2px;
+}
+/* Component containers
+----------------------------------*/
+.ui-widget {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1.1em;
+}
+.ui-widget .ui-widget {
+ font-size: 1em;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1em;
+}
+.ui-widget.ui-widget-content {
+ border: 1px solid #d3d3d3;
+}
+.ui-widget-content {
+ border: 1px solid #aaaaaa;
+ background: #ffffff;
+ color: #222222;
+}
+.ui-widget-content a {
+ color: #222222;
+}
+.ui-widget-header {
+ border: 1px solid #aaaaaa;
+ background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
+ color: #222222;
+ font-weight: bold;
+}
+.ui-widget-header a {
+ color: #222222;
+}
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default,
+.ui-widget-content .ui-state-default,
+.ui-widget-header .ui-state-default,
+.ui-button,
+
+ /* We use html here because we need a greater specificity to make sure disabled
+ works properly when clicked or hovered */
+html .ui-button.ui-state-disabled:hover,
+html .ui-button.ui-state-disabled:active {
+ border: 1px solid #d3d3d3;
+ background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #555555;
+}
+.ui-state-default a,
+.ui-state-default a:link,
+.ui-state-default a:visited,
+a.ui-button,
+a:link.ui-button,
+a:visited.ui-button,
+.ui-button {
+ color: #555555;
+ text-decoration: none;
+}
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-widget-header .ui-state-hover,
+.ui-state-focus,
+.ui-widget-content .ui-state-focus,
+.ui-widget-header .ui-state-focus,
+.ui-button:hover,
+.ui-button:focus {
+ border: 1px solid #999999;
+ background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
+}
+.ui-state-hover a,
+.ui-state-hover a:hover,
+.ui-state-hover a:link,
+.ui-state-hover a:visited,
+.ui-state-focus a,
+.ui-state-focus a:hover,
+.ui-state-focus a:link,
+.ui-state-focus a:visited,
+a.ui-button:hover,
+a.ui-button:focus {
+ color: #212121;
+ text-decoration: none;
+}
+
+.ui-visual-focus {
+ box-shadow: 0 0 3px 1px rgb(94, 158, 214);
+}
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active,
+a.ui-button:active,
+.ui-button:active,
+.ui-button.ui-state-active:hover {
+ border: 1px solid #aaaaaa;
+ background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
+}
+.ui-icon-background,
+.ui-state-active .ui-icon-background {
+ border: #aaaaaa;
+ background-color: #212121;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+ color: #212121;
+ text-decoration: none;
+}
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight,
+.ui-widget-content .ui-state-highlight,
+.ui-widget-header .ui-state-highlight {
+ border: 1px solid #fcefa1;
+ background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
+ color: #363636;
+}
+.ui-state-checked {
+ border: 1px solid #fcefa1;
+ background: #fbf9ee;
+}
+.ui-state-highlight a,
+.ui-widget-content .ui-state-highlight a,
+.ui-widget-header .ui-state-highlight a {
+ color: #363636;
+}
+.ui-state-error,
+.ui-widget-content .ui-state-error,
+.ui-widget-header .ui-state-error {
+ border: 1px solid #cd0a0a;
+ background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
+ color: #cd0a0a;
+}
+.ui-state-error a,
+.ui-widget-content .ui-state-error a,
+.ui-widget-header .ui-state-error a {
+ color: #cd0a0a;
+}
+.ui-state-error-text,
+.ui-widget-content .ui-state-error-text,
+.ui-widget-header .ui-state-error-text {
+ color: #cd0a0a;
+}
+.ui-priority-primary,
+.ui-widget-content .ui-priority-primary,
+.ui-widget-header .ui-priority-primary {
+ font-weight: bold;
+}
+.ui-priority-secondary,
+.ui-widget-content .ui-priority-secondary,
+.ui-widget-header .ui-priority-secondary {
+ opacity: .7;
+ filter:Alpha(Opacity=70); /* support: IE8 */
+ font-weight: normal;
+}
+.ui-state-disabled,
+.ui-widget-content .ui-state-disabled,
+.ui-widget-header .ui-state-disabled {
+ opacity: .35;
+ filter:Alpha(Opacity=35); /* support: IE8 */
+ background-image: none;
+}
+.ui-state-disabled .ui-icon {
+ filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
+}
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ width: 16px;
+ height: 16px;
+}
+.ui-icon,
+.ui-widget-content .ui-icon {
+ background-image: url("images/ui-icons_222222_256x240.png");
+}
+.ui-widget-header .ui-icon {
+ background-image: url("images/ui-icons_222222_256x240.png");
+}
+.ui-state-hover .ui-icon,
+.ui-state-focus .ui-icon,
+.ui-button:hover .ui-icon,
+.ui-button:focus .ui-icon {
+ background-image: url("images/ui-icons_454545_256x240.png");
+}
+.ui-state-active .ui-icon,
+.ui-button:active .ui-icon {
+ background-image: url("images/ui-icons_454545_256x240.png");
+}
+.ui-state-highlight .ui-icon,
+.ui-button .ui-state-highlight.ui-icon {
+ background-image: url("images/ui-icons_2e83ff_256x240.png");
+}
+.ui-state-error .ui-icon,
+.ui-state-error-text .ui-icon {
+ background-image: url("images/ui-icons_cd0a0a_256x240.png");
+}
+.ui-button .ui-icon {
+ background-image: url("images/ui-icons_888888_256x240.png");
+}
+
+/* positioning */
+.ui-icon-blank { background-position: 16px 16px; }
+.ui-icon-caret-1-n { background-position: 0 0; }
+.ui-icon-caret-1-ne { background-position: -16px 0; }
+.ui-icon-caret-1-e { background-position: -32px 0; }
+.ui-icon-caret-1-se { background-position: -48px 0; }
+.ui-icon-caret-1-s { background-position: -65px 0; }
+.ui-icon-caret-1-sw { background-position: -80px 0; }
+.ui-icon-caret-1-w { background-position: -96px 0; }
+.ui-icon-caret-1-nw { background-position: -112px 0; }
+.ui-icon-caret-2-n-s { background-position: -128px 0; }
+.ui-icon-caret-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -65px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -65px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 1px -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-on { background-position: -96px -144px; }
+.ui-icon-radio-off { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-left,
+.ui-corner-tl {
+ border-top-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-right,
+.ui-corner-tr {
+ border-top-right-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-left,
+.ui-corner-bl {
+ border-bottom-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-br {
+ border-bottom-right-radius: 4px;
+}
+
+/* Overlays */
+.ui-widget-overlay {
+ background: #aaaaaa;
+ opacity: .3;
+ filter: Alpha(Opacity=30); /* support: IE8 */
+}
+.ui-widget-shadow {
+ -webkit-box-shadow: -8px -8px 8px #aaaaaa;
+ box-shadow: -8px -8px 8px #aaaaaa;
+}
\ No newline at end of file
diff --git a/css/rs-admin.css b/css/rs-admin.css
new file mode 100644
index 0000000..1c8ca26
--- /dev/null
+++ b/css/rs-admin.css
@@ -0,0 +1,2 @@
+/* Radio Station Styles used on admin pages */
+/* ---------------------------------------- */
diff --git a/css/rs-block-editor.css b/css/rs-block-editor.css
new file mode 100644
index 0000000..5d3ba33
--- /dev/null
+++ b/css/rs-block-editor.css
@@ -0,0 +1,3 @@
+.components-panel .components-panel__body.radio-block-controls .components-panel__row label {
+ width: 100%; max-width: 100%; min-width: 150px; overflow: visible;
+}
diff --git a/css/rs-blocks.css b/css/rs-blocks.css
new file mode 100644
index 0000000..e69de29
diff --git a/css/rs-mailchimp.css b/css/rs-mailchimp.css
new file mode 100644
index 0000000..14a4d79
--- /dev/null
+++ b/css/rs-mailchimp.css
@@ -0,0 +1,30 @@
+/* MailChimp Form Embed Code - Slim - 12/15/2015 v10.7 */
+#mc_embed_signup {background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; max-width: 1000px;}
+#mc_embed_signup form {display:block; position:relative; text-align:left; padding:10px 0 10px 3%}
+#mc_embed_signup h2 {font-weight:bold; padding:0; margin:15px 0; font-size:1.4em;}
+#mc_embed_signup input {border:1px solid #999; -webkit-appearance:none;}
+#mc_embed_signup input[type=checkbox]{-webkit-appearance:checkbox;}
+#mc_embed_signup input[type=radio]{-webkit-appearance:radio;}
+#mc_embed_signup input:focus {border-color:#333;}
+#mc_embed_signup .button {clear:both; background-color: #00bb33; border: 0 none; border-radius:4px; letter-spacing:.03em; color: #FFFFFF; cursor: pointer; display: inline-block; font-size:15px; height: 32px; line-height: 32px; margin: 0 5px 10px 0; padding:0; text-align: center; text-decoration: none; vertical-align: top; white-space: nowrap; width: auto; transition: all 0.23s ease-in-out 0s;}
+#mc_embed_signup .button:hover {background-color:#00cc33;}
+#mc_embed_signup .small-meta {font-size: 11px;}
+#mc_embed_signup .nowrap {white-space:nowrap;}
+#mc_embed_signup .clear {clear:none; display:inline;}
+
+#mc_embed_signup label {display:block; font-size:16px; padding-bottom:10px; font-weight:bold;}
+#mc_embed_signup input.email {font-family:"Open Sans","Helvetica Neue",Arial,Helvetica,Verdana,sans-serif; font-size: 15px; display:block; padding:0 0.4em; margin:0 4% 10px 0; min-height:32px; width:58%; min-width:130px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px;}
+#mc_embed_signup input.button {display:block; width:35%; margin:0 0 10px 0; min-width:90px;}
+
+#mc_embed_signup div#mce-responses {float:left; top:-1.4em; padding:0 .5em 0 .5em; overflow:hidden; width:90%;margin: 0 5%; clear: both;}
+#mc_embed_signup div.response {margin:1em 0; padding:1em .5em .5em 0; font-weight:bold; float:left; top:-1.5em; z-index:1; width:80%;}
+#mc_embed_signup #mce-error-response {display:none;}
+#mc_embed_signup #mce-success-response {color:#529214; display:none;}
+#mc_embed_signup label.error {display:block; float:none; width:auto; margin-left:1.05em; text-align:left; padding:.5em 0;}
+
+/* Custom */
+#mc_embed_signup #plugin-icon {display:inline-block; vertical-align:middle; margin-right:40px;}
+#mc_embed_signup #plugin-icon img {width:40px; height:40px;}
+#mc_embed_signup #signup-label {display:inline-block; vertical-align:middle; margin-right:40px; font-size:16px; line-height:24px;}
+#mc_embed_signup input[type=email] {display:inline-block; vertical-align:middle; width:300px;}
+#mc_embed_signup .subscribe {display:inline-block; vertical-align:middle;}
diff --git a/css/rs-schedule.css b/css/rs-schedule.css
new file mode 100644
index 0000000..ddeff56
--- /dev/null
+++ b/css/rs-schedule.css
@@ -0,0 +1,663 @@
+/* ============================= */
+/* Radio Station Schedule Styles */
+/* ============================= */
+
+.radio-clear {
+ display: block;
+ clear: both;
+}
+
+.show-user-time {
+ display: none;
+ font-style: italic;
+ font-size: 0.95em;
+}
+
+/* Edit / Add Links */
+/* ---------------- */
+.show-title, .show-edit-link {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.show-edit-link, .show-add-link, .shift-edit-link {
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.show-edit-link span, .show-add-link span {
+ color: #777777;
+ opacity: 0.9;
+}
+
+.show-edit-link span {
+ font-size: 18px;
+}
+
+.show-add-link span {
+ font-size: 16px;
+}
+
+.show-edit-link:hover span, .show-add-link:hover span {
+ opacity: 1;
+}
+
+/* Schedule Controls */
+/* ----------------- */
+.master-schedule-controls-wrapper {
+ width: 100%;
+}
+
+.master-schedule-clock-wrapper, .master-schedule-timezone-wrapper, .master-schedule-selector-wrapper {
+ font-size: 0.8em;
+ vertical-align: top;
+ text-align: left;
+ margin-top: 10px;
+}
+
+
+/* Genre Selector */
+/* -------------- */
+.master-genre-list {
+ font-size: 1em;
+ float: left;
+}
+
+.master-genre-list span.heading {
+ font-weight: bold;
+}
+
+.master-genre-list .genre-highlight, .master-genre-list .genre-highlight:hover {
+ text-decoration: none;
+}
+
+.master-genre-list .genre-highlight.highlighted {
+ font-weight: bold;
+ background-color: rgba( 235, 235, 0, 0.4 );
+ border: 1px dashed orange;
+ padding-left: 4px;
+ padding-right: 2px;
+}
+
+/* Table View Styles */
+/* ----------------- */
+.master-program-schedule {
+ width: 100%;
+ height: 100%;
+}
+
+.master-program-schedule tr {
+ height: 100%;
+}
+
+.master-program-schedule .master-program-hour-heading {
+ max-width: 120px;
+}
+
+.master-program-schedule .master-program-hour-heading, .master-program-schedule .master-program-hour-row {
+ width: 100px;
+}
+
+.master-program-schedule th {
+ width: auto;
+ text-align: center;
+ vertical-align: top;
+ padding: 0.5em 0;
+ min-width: 80px;
+}
+
+.master-program-schedule .master-program-day {
+ min-width: 125px;
+ max-width: 250px;
+}
+
+.master-program-schedule th.master-program-hour {
+ min-width: 80px;
+}
+
+.master-program-schedule .master-program-day.current-day {
+ border: 1px solid #F00000;
+}
+
+#master-program-schedule th .headings {
+ display: inline-block;
+}
+
+.master-program-schedule th .day-heading {
+ font-size: 1em;
+}
+
+.master-program-schedule th .date-subheading {
+ font-size: 0.75em;
+}
+
+.master-program-schedule th a {
+ text-decoration: none;
+}
+
+.master-schedule-table-arrow-left, .master-schedule-table-arrow-right {
+ display: inline-block;
+ font-size: 2em;
+ line-height: 1em;
+ vertical-align: top;
+ cursor: pointer;
+}
+
+.master-schedule-table-arrow-left .master-schedule-table-arrow,
+.master-schedule-table-arrow-right .master-schedule-table-arrow {
+ width: 1em;
+}
+
+.master-schedule-table-arrow-left .master-schedule-table-arrow:hover,
+.master-schedule-table-arrow-right .master-schedule-table-arrow:hover {
+ background-color: rgba(128,128,128,0.5);
+ border-radius: 1em;
+}
+
+.master-program-schedule th .shift-left-arrow {
+ float: left;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 0.5em;
+}
+
+.master-program-schedule th .shift-right-arrow {
+ float: right;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 0.5em;
+}
+
+.master-program-schedule th .shift-left-arrow, .master-program-schedule th .shift-right-arrow {
+ display: none;
+}
+
+.master-program-schedule th.first-column .shift-left-arrow, .master-program-schedule th.last-column .shift-right-arrow {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.master-program-schedule .master-program-day .headings {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.master-program-schedule .master-program-hour-row th {
+ padding-top: 0;
+}
+
+.master-program-schedule td {
+ vertical-align: top;
+ font-size: 1em;
+ text-align: center;
+ padding: 0;
+ height: 100%;
+}
+
+.master-program-schedule td {
+ border: 1px solid #222222;
+}
+
+.master-program-schedule td.show-info {
+ padding: 0;
+}
+
+.master-program-schedule td div {
+ border-top: 1px solid #EEEEEE;
+ font-size: 0.9em;
+}
+
+.master-program-schedule .master-program-hour.current-hour {
+ border: 1px solid #F00000;
+}
+
+.master-program-schedule .master-program-hour div {
+ padding-bottom: 1em;
+}
+
+.master-program-schedule .show-wrap {
+ position: relative;
+ display: -ms-grid;
+ display: grid;
+ min-height: 50px;
+ height: 100%;
+}
+
+.master-program-schedule .show-title, .master-program-schedule .show-desc {
+ max-width: 120px;
+ text-align: center;
+}
+
+.master-program-schedule .show-image {
+ max-width: 120px;
+ margin: 0 auto;
+}
+
+.master-program-schedule .show-info.overflow {
+ border-bottom: transparent !important;
+ position: relative;
+}
+
+.master-program-schedule .show-info.continued {
+ border-top: transparent !important;
+}
+
+.master-program-schedule .show-info .master-show-entry {
+ position: relative;
+ display: block;
+ /* height: 100%; */
+}
+
+.master-program-schedule .show-info .master-show-entry.finished {
+ height: 99%;
+}
+
+.master-program-schedule .show-info .master-show-entry.nowplaying {
+ border: 2px solid #F00000;
+}
+
+.master-program-schedule .show-info .master-show-entry.overflow,
+.master-program-schedule .show-info .master-show-entry.nowplaying.overflow {
+ border-bottom: 1px solid transparent;
+}
+
+.master-program-schedule .show-info .master-show-entry.continued,
+.master-program-schedule .show-info .master-show-entry.nowplaying.continued {
+ border-top: 1px solid transparent;
+}
+
+.master-program-schedule .show-info .master-show-entry.continued.overflow,
+.master-program-schedule .show-info .master-show-entry.nowplaying.continued.overflow {
+ border-bottom: 1px solid transparent;
+}
+
+.master-program-schedule .master-show-entry.highlighted {
+ border: 1px dashed orange;
+ background-color: rgba(235,235,0,0.4);
+}
+
+.master-program-schedule .master-show-entry.before-current {
+ opacity: 0.8;
+}
+
+.master-program-schedule span.show-title,
+.master-program-schedule span.show-file,
+.master-program-schedule span.show-time,
+.master-program-schedule span.show-encore {
+ display: block;
+}
+
+.master-program-schedule .show-host-names {
+ font-size: 0.8em;
+}
+
+.master-program-schedule .show-time, .master-program-schedule .show-user-time {
+ font-size: 0.8em;
+}
+
+.master-program-schedule .show-encore {
+ font-size: 0.8em;
+ color: #EE8E66;
+}
+
+.master-program-schedule .show-file {
+ margin-bottom: 5px;
+ margin-top: 5px;
+ font-size: 0.6em;
+}
+
+.master-program-schedule .show-file a {
+ width: 95%;
+ height: 20px;
+ background-color: #931B25;
+ padding: 3px;
+ text-decoration: none;
+ color: #ffffff;
+ margin-bottom: 3px;
+}
+
+.master-program-schedule .show-file a:hover {
+ background-color: #C51D2E;
+ color: #ffffff;
+}
+
+.master-program-schedule .show-desc {
+ font-size: 0.6em;
+}
+
+
+/* List View Styles */
+/* ---------------- */
+.master-list .master-list-day-item.highlighted {
+ border: 1px dashed orange;
+ background-color: rgba(235,235,0,0.4);
+}
+
+
+/* Tabbed View Styles */
+/* ------------------ */
+
+.master-schedule-tabs {
+ border: none;
+ padding: 0;
+ margin: 0;
+ width: 100%;
+}
+
+.master-schedule-tabs .shift-left-arrow {
+ float: left;
+}
+
+.master-schedule-tabs .shift-right-arrow {
+ float: right;
+}
+
+.master-schedule-tabs .shift-left-arrow, .master-schedule-tabs .shift-right-arrow,
+.master-schedule-tabs .first-tab.day-0 .shift-left-arrow, .master-schedule-tabs .last-tab.day-6 .shift-right-arrow {
+ display: none;
+ padding: 0;
+}
+
+.master-schedule-tabs .shift-left-arrow a, .master-schedule-tabs .shift-right-arrow a {
+ text-decoration: none;
+}
+
+.master-schedule-tabs .first-tab .shift-left-arrow, .master-schedule-tabs .last-tab .shift-right-arrow {
+ display: inline-block;
+ font-size: 2em;
+ font-weight: bold;
+ line-height: 0.5em;
+ padding: 5px;
+}
+
+.master-schedule-tabs-loader {
+ list-style: none;
+ display: inline-block;
+ margin: 0;
+ padding: 0;
+ font-size: 50px;
+ line-height: 35px;
+ vertical-align: top;
+ cursor: pointer;
+}
+
+.master-schedule-tabs-loader .master-schedule-tabs-arrow {
+ width: 50px;
+ text-align: center;
+ padding-bottom: 15px;
+}
+
+.master-schedule-tabs-arrow-right {
+ margin-left: 5px;
+}
+
+.master-schedule-tabs-loader .master-schedule-tabs-arrow:hover {
+ background-color: rgba(128,128,128,0.5);
+ border-radius: 25px;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day {
+ display: inline-block;
+ position: relative;
+ min-width: 100px;
+ margin: 0px;
+ cursor: pointer;
+ text-align: center;
+ background-color: #eeeeee;
+ color: #444444;
+ font-size: 1em;
+ font-weight: bold;
+ border-radius: 7px 7px 0 0;
+ border: 1px solid #000000;
+ list-style: none;
+ margin-left: 20px;
+ border-bottom: 0;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day.start-tab,
+.master-schedule-tabs .master-schedule-tabs-day.first-tab {
+ margin-left: 0;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day.current-day {
+ border: 1px solid #F00000;
+ border-bottom: 0;
+}
+
+.master-schedule-tabs .master-schedule-tabs-headings {
+ display: inline-block;
+ padding: 5px 10px 5px 10px;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day-name {
+ /* display: inline-block;
+ padding: 5px 10px 5px 10px; */
+ display: block;
+ font-size: 1em;
+}
+
+.master-schedule-tabs .master-schedule-tabs-date {
+ display: block;
+ font-size: 0.75em;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day.active-day-tab {
+ font-weight: bold;
+ background-color: #ffffff;
+ color: #000000;
+ /* border-bottom: 0; */
+}
+
+.master-schedule-tabs .master-schedule-tabs-day .master-schedule-tab-bottom {
+ display: none;
+}
+
+.master-schedule-tabs .master-schedule-tabs-day.active-day-tab .master-schedule-tab-bottom {
+ position: absolute;
+ display: block;
+ bottom: 0;
+ height: 1px;
+ width: 100%;
+ background-color: #FFF;
+ margin-bottom: -1px;
+ z-index: 999;
+}
+
+.master-schedule-tab-panels {
+ min-width: 300px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-selected {
+ display: none;
+ padding: 10px 10px 0 10px;
+ margin: 0;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel {
+ display: none;
+ float: left;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ text-align: left;
+ width: 100%;
+ list-style: none;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel .master-schedule-tabs-show {
+ padding: 10px 10px;
+ margin: 0 10px;
+ clear: both;
+ list-style: none;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel .master-schedule-tabs-show.nowplaying {
+ border: 1px solid #F00000;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel .master-schedule-tabs-show.first-show {
+ margin-top: 20px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel .master-schedule-tabs-show.last-show {
+ margin-bottom: 20px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel .master-schedule-tabs-no-shows {
+ padding-top: 30px; padding-bottom: 30px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-panel.active-day-panel {
+ display: block;
+ border: 1px solid #333333;
+ background-color: #ffffff;
+ /* border-top: 0; */
+ border-radius: 7px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show.before-current {
+ opacity: 0.8;
+}
+
+.master-schedule-tab-panels.hide-past-shows .master-show-entry.before-current {
+ display: none;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-info,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-image,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-desc {
+ display: inline-block;
+ vertical-align: top;
+ font-size: 1em;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-image {
+ margin-right: 10px;
+ width: 180px;
+ text-align: center;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-image.right-image {
+ margin-right: 0;
+ margin-left: 10px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-info {
+ min-width: 260px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-info.has-show-desc {
+ width: 260px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-info.left-image {
+ text-align: left;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-info.right-image {
+ text-align: right;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-desc {
+ max-width: 400px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-time,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-user-time,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-host-names {
+ font-size: 0.9em;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-encore,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-file,
+.master-schedule-tab-panels .master-schedule-tabs-show .show-genres {
+ font-size: 0.8em;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show .show-desc {
+ font-size: 0.75em;
+ margin-left: 20px;
+}
+
+.master-schedule-tab-panels .master-schedule-tabs-show.highlighted {
+ border: 1px dashed orange;
+ background-color: rgba(235,235,0,0.4);
+}
+
+
+/* Div View Styles */
+/* --------------- */
+#master-schedule-divs {
+ width: 100%;
+}
+
+#master-schedule-divs .master-schedule-hour {
+ width: 100%;
+ clear: both;
+}
+
+#master-schedule-divs .master-schedule-hour-header {
+ width: 12%;
+ float: left;
+ text-align: center;
+ font-weight: bold;
+ font-size: 0.7em;
+}
+
+#master-schedule-divs .master-schedule-weekday {
+ width: 10%;
+ float: left;
+ border: 1px solid #dddddd;
+}
+
+#master-schedule-divs .master-schedule-weekday-header {
+ text-align: center;
+ font-weight: bold;
+ font-size: 0.7em;
+ display: block;
+}
+
+#master-schedule-divs .master-show-entry {
+ padding: 8px 5px 5px 5px;
+ position: relative;
+}
+
+#master-schedule-divs .show-dj-names,
+#master-schedule-divs .show-time,
+#master-schedule-divs .show-title {
+ display: block;
+ font-size: 0.6em;
+ line-height: 1em;
+}
+
+
+#master-schedule-divs .show-image img {
+ width: 100%;
+ height: auto;
+}
+
+#master-schedule-divs .rowspan {
+ background-color: #dddddd;
+ margin-left: -1px;
+ position: absolute;
+ width: 50px;
+ z-index: 5;
+}
+
+/* Media Queries */
+@media screen and (max-width: 782px) {
+ .master-schedule-tab-panels .master-schedule-tabs-show .show-image {width: 120px;}
+}
+
+@media screen and (max-width: 599px) {
+ .master-program-schedule .show-image,
+ .master-schedule-tab-panels .master-schedule-tabs-show .show-image {width: 90px;}
+ .master-program-schedule .master-program-hour-heading, .master-program-schedule .master-program-hour-row {width: 75px;}
+ #master-schedule-controls-wrapper .radio-station-server-clock,
+ #master-schedule-controls-wrapper .radio-station-user-clock {margin-bottom: 10px;}
+ #master-schedule-controls-wrapper .radio-clock-title {display: block; min-height: auto; margin-bottom: 5px;}
+}
+
+@media screen and (max-width: 299px) {
+ .master-program-schedule .master-program-hour-heading, .master-program-schedule .master-program-hour-row {width: 50px;}
+}
diff --git a/css/rs-shortcodes.css b/css/rs-shortcodes.css
new file mode 100644
index 0000000..04cb08a
--- /dev/null
+++ b/css/rs-shortcodes.css
@@ -0,0 +1,343 @@
+/* ============================== */
+/* Radio Station Shortcode Styles */
+/* ============================== */
+
+.radio-clear {
+ display: block;
+ clear: both;
+}
+
+.rs-countdown {
+ font-size: 0.8em;
+}
+
+.show-user-time {
+ display: none;
+ font-style: italic;
+}
+
+
+/* Timezone Shortcode */
+/* ------------------ */
+.radio-timezone-title, .radio-timezone {
+ display: inline-block;
+}
+
+.radio-timezone-title, .radio-user-timezone-title, .radio-clock-title {
+ font-weight: bold;
+}
+
+.radio-user-timezone-title, .radio-user-timezone {
+ display: none;
+}
+
+.radio-user-timezone {
+ font-style: italic;
+}
+
+
+/* Clock Shortcode */
+/* --------------- */
+.radio-server-time, .radio-server-date, .radio-server-zone,
+.radio-user-time, .radio-user-date, .radio-user-zone {
+ display: inline-block;
+}
+
+.widget .radio-clock-title {
+ float: left;
+}
+
+.radio-clock-title {
+ min-width: 100px;
+ min-height: 40px;
+ display: inline-block;
+}
+
+.radio-timezone-title, .radio-user-timezone-title {
+ min-width: 120px;
+}
+
+.radio-server-time, .radio-user-time, .radio-server-date, .radio-user-date {
+ margin-left: 10px;
+}
+
+.radio-server-zone, .radio-user-zone {
+ margin-left: 20px;
+}
+
+.radio-user-time, .radio-user-date, .radio-user-zone {
+ font-style: italic;
+}
+
+.radio-station-clock-widget .radio-station-server-clock, .radio-station-clock-widget .radio-station-user-clock {
+ font-size: 0.9em;
+ margin-top: 10px;
+ display: inline-block;
+}
+
+.radio-station-clock-widget .radio-server-zone, .radio-station-clock-widget .radio-user-zone {
+ display: block;
+ margin-left: 110px;
+ font-size: 0.9em;
+}
+
+.user-timezone-change, .user-timezone-select {
+ display: inline-block;
+}
+
+/* Archive Shortcodes */
+/* ------------------ */
+
+.genre-archive, .language-archive {
+ clear: both;
+}
+
+.show-archive-list, .playlist-archive-list, .override-archive-list {
+ list-style: none;
+ padding-left: 0;
+}
+
+.show-archive-item, .playlist-archive-item, .override-archive-item {
+ list-style: none;
+ clear: both;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.show-archive-item-thumbnail, .playlist-archive-item-thumbnail, .override-archive-item-thumbnail {
+ float: left;
+ padding-right: 20px;
+}
+
+.genre-archive .show-archive-item-title, .language-archive .show-archive-item-title {
+ font-size: 1em;
+ margin-bottom: 5px;
+}
+
+.genre-archive .show-archive-item-thumbnail, .language-archive .show-archive-item-thumbnail {
+ width: 120px;
+ height: auto;
+}
+
+.show-archive-item-title, .playlist-archive-item-title, .override-archive-item-title {
+ font-size: 1.25em;
+ margin-bottom: 10px;
+}
+
+.show-archive-item-content, .playlist-archive-item-content, .override-archive-item-content,
+.show-archive-item-excerpt, .playlist-archive-item-excerpt, .override-archive-item-excerpt {
+ font-size: 0.8em;
+}
+
+.show-archives.grid .show-archive-item, .override-archives.grid .override-archive-item,
+.genres-archive.grid .show-archive-item, .languages-archive.grid .show-archive-item,
+.playlist-archives.grid .playlist-archive-item {
+ display: inline-block;
+ vertical-align: top;
+ text-align: center;
+ width: 220px;
+}
+
+.show-archives.grid .show-archive-item-thumbnail, .override-archives.grid .override-archive-item-thumbnail,
+.genres-archive.grid .show-archive-item-thumbnail, .languages-archive.grid .show-archive-item-thumbnail,
+.playlist-archives.grid .playlist-archive-item-thumbnail {
+ float: none;
+}
+
+.show-archive-item-description, .override-archive-item-description {
+ overflow-wrap: break-word; word-break: normal;
+}
+
+/* Show Subarchive Shortcodes */
+/* -------------------------- */
+.show-post, .show-override, .show-playlist, .show-host, .show-producer, .show-episode {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.show-post-thumbnail, .show-override-thumbnail, show-playlist-thumbnail, .show-host-thumbnail, .show-producer-thumbnail, .show-episode-thumbnail,
+.show-post-info, .show-override-info, .show-playlist-info, .show-host-info, .show-producer-info, .show-episode-info {
+ display: table-cell;
+ vertical-align: top;
+}
+
+.show-post-thumbnail, .show-override-thumbnail, .show-playlist-thumbnail, .show-host-thumbnail, .show-producer-thumbnail, .show-episode-thumbnail {
+ padding-right: 30px;
+ margin-top: 10px;
+ min-width: 150px;
+}
+
+.show-post-info, .show-override-info, .show-playlist-info, .show-host-info, .show-producer-info, .show-episode-info {
+ max-width: 600px;
+}
+
+.show-post-title, .show-override-title, .show-playlist-title, .show-host-title, .show-producer-title, .show-episode-title,
+.show-post-meta, .show-override-meta, .show-playlist-meta, .show-host-meta, .show-producer-meta, .show-episode-meta {
+ margin-bottom: 10px;
+}
+
+/* Archive Pagination */
+/* ------------------ */
+.show-pagination-buttons, .show-pagination-buttons::after, .archive-pagination-buttons, .archive-pagination-buttons::after {
+ clear: both;
+ content: "";
+ display: block;
+}
+
+.show-pagination-button, .archive-pagination-button {
+ float: left;
+ display: inline-block;
+ width: 30px;
+ margin-right: 7px;
+ cursor: pointer;
+ text-align: center;
+ background-color: #eeeeee;
+ border-radius: 7px;
+ border: 1px solid #777777;
+ font-size: 1em;
+ color: #444444;
+ padding: 5px;
+}
+
+.show-pagination-button a, .show-pagination-button a:hover,
+.archive-pagination-button a, .archive-pagination a:hover {
+ text-decoration: none;
+}
+
+.show-pagination-button.arrow-button, .archive-pagination-button.arrow-button {
+ padding-top: 4px;
+ padding-bottom: 6px;
+}
+
+.show-pagination-button.active, .archive-pagination-button.active {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+.show-pagination-button:hover, .archive-pagination-button:hover {
+ background-color: #f5f5f5;
+}
+
+/* Widget/Shortcode Styles */
+/* ----------------------- */
+
+ul.current-show-list, ul.current-show-list li, ul.upcoming-shows-list, ul.upcoming-shows-list li {
+ list-style: none;
+ padding-left: 0;
+}
+
+.current-show-avatar, .upcoming-show-avatar, .on-air-dj-avatar {
+ padding-bottom: 5px;
+ display: block;
+ max-width: 90%;
+}
+
+.current-show-avatar img, .upcoming-show-avatar img, .on-air-dj-avatar img {
+ width: 100%;
+ height: auto;
+}
+
+.current-show-avatar.float-left, .upcoming-show-avatar.float-left, .on-air-dj-avatar.float-left {
+ float: left;
+ margin-right: 15px;
+}
+
+.current-show-avatar.float-right, .upcoming-show-avatar.float-right, .on-air-dj-avatar.float-right {
+ float: right;
+ margin-left: 15px;
+}
+
+.current-show-list .current-show-title, .on-air-list .on-air-dj-title {
+ font-size: 1.25em;
+}
+
+.current-show-encore, .upcoming-show-encore, .on-air-dj-encore {
+ font-style: italic;
+}
+
+.current-show-encore, .current-show-hosts, .on-air-dj-encore, .on-air-dj-names {
+ font-size: 0.85em;
+}
+
+.current-show-hosts, .upcoming-show-hosts, .on-air-dj-names {
+ overflow: hidden;
+}
+
+.current-show-list, .upcoming-shows-list,
+.on-air-list, .widget .on-air-list, .on-air-upcoming-list, .widget .on-air-upcoming-list {
+ list-style: none;
+}
+
+.current-show-countdown .rs-label, .upcoming-show-countdown .rs-label {
+ font-weight: bold;
+}
+
+.current-show-countdown .rs-hours, .current-show-countdown .rs-minutes, .current-show-countdown .rs-seconds,
+.upcoming-show-countdown .rs-hours, .upcoming-show-countdown .rs-minutes, .upcoming-show-countdown .rs-seconds,
+.show-playlist-countdown .rs-hours, .show-playlist-countdown .rs-minutes, .show-playlist-countdown .rs-seconds {
+ background-color: #F0F0F0;
+ padding: 1px 3px;
+}
+
+.current-show-countdown, .upcoming-show-countdown, .show-playlist-countdown {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.current-show-desc, .on-air-dj-desc,
+.current-show-playlist, .on-air-dj-playlist {
+ font-size: 0.8em;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.current-show-schedule, .upcoming-show-schedule, .on-air-dj-schedule {
+ font-size: 0.85em;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ overflow: hidden;
+}
+
+.current-show-shifts, .upcoming-show-shifts, .on-air-dj-sched {
+ font-size: 0.85em;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.current-show .show-user-time, .upcoming-show-schedule .show-user-time {
+ font-size: 0.85em;
+}
+
+.current-shift-list, .current-shift-list-item {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+.current-shift-list .current-shift-list-item {
+ list-style: disc;
+ text-indent: 20px;
+}
+
+.current-show-schedule .current-show-shifts.current-shift {
+ font-style: italic;
+}
+
+.upcoming-shows-list .upcoming-show-title, .on-air-upcoming-list .on-air-dj-title {
+ font-size: 1em;
+}
+
+.upcoming-shows-list .upcoming-show-encore, .upcoming-shows-list .upcoming-show-hosts,
+.on-air-upcoming-list .on-air-dj-encore, .on-air-upcoming-list .on-air-dj-names {
+ font-size: 0.75em;
+}
+
+
+/* Current Playlist Widget */
+.show-playlist-tracks {
+ line-height: 1.6em;
+}
+
+.show-playlist-track {
+ margin-bottom: 10px;
+ font-size: 0.75em;
+}
\ No newline at end of file
diff --git a/css/rs-templates.css b/css/rs-templates.css
new file mode 100644
index 0000000..0041de2
--- /dev/null
+++ b/css/rs-templates.css
@@ -0,0 +1,352 @@
+/* ============================= */
+/* Radio Station Template Styles */
+/* ============================= */
+
+.radio-clear {
+ display: block;
+ clear: both;
+}
+
+.show-user-time {
+ display: none;
+ font-style: italic;
+}
+
+.rs-start-date, .rs-end-date {
+ margin-right: 0.5em;
+}
+
+/* Show Page Styles */
+/* ---------------- */
+
+#show-posts, #show-playlists, #show-episodes {
+ overflow: hidden;
+}
+
+#show-content .show-sections {
+ min-width: 250px;
+}
+
+#show-content .show-sections h3, #show-content .show-sections h4, #show-content .show-sections h5 {
+ clear: none;
+}
+
+/* Show Info */
+/* --------- */
+#show-content .show-info {
+ float: left;
+}
+
+#show-content .show-info .show-block {
+ vertical-align: top;
+ line-height: 1.8em;
+ max-width: 250px;
+ margin-top: 20px;
+}
+
+#show-content .show-info .show-block.first-block {
+ margin-top: 0;
+}
+
+#show-content .show-info .show-block.last-block {
+ margin-bottom: 20px;
+}
+
+.show-label {
+ font-weight: bold;
+}
+
+#show-content .show-info .show-avatar {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 10px;
+ text-align: center;
+}
+
+#show-content .show-avatar img {
+ width: 200px;
+ height: 200px;
+}
+
+#show-content .show-embed {
+ display: inline-block;
+ vertical-align: middle;
+ width: 100%;
+ min-width: 150px;
+ max-width: 200px;
+}
+
+#show-content .show-download {
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 10px;
+ margin-top: 5px;
+}
+
+#show-content .show-icon span:hover, #show-content .show-download span:hover {
+ opacity: 0.8;
+ text-decoration: none;
+}
+
+#show-content .show-icon .show-download a, #show-content .show-icon .show-download a:visited {
+ text-decoration: none;
+}
+
+#show-content .show-embed .mejs-container {
+ border-radius: 15px;
+ overflow: hidden;
+}
+
+#show-content .show-embed .mejs-container,
+#show-content .show-embed .mejs-controls,
+#show-content .show-embed .mejs-embed,
+#show-content .show-embed .mejs-embed body {
+ background-color: #AAAAAA;
+}
+
+#show-content .show-embed .mejs-horizontal-volume-total {
+ width: 100%;
+}
+
+#show-content .show-icons {
+ vertical-align: top;
+ margin-top: 12px;
+}
+
+#show-content .show-icons .show-icon {
+ display: inline-block;
+ margin-right: 24px;
+ margin-bottom: 12px;
+}
+
+#show-content .show-icons .show-icon span,
+#show-content .show-download span {
+ font-size: 32px;
+}
+
+#show-content .show-icon a {
+ text-decoration: none;
+}
+
+/* Show Schedule */
+/* ------------- */
+#show-content .show-info .show-block h3, #show-content .show-info .show-block h4 {
+ margin-top: 0;
+}
+
+#show-content .show-times .show-offset {
+ font-size: 16px;
+}
+
+#show-content .show-times .show-encore {
+ color: #CC0000;
+}
+
+#show-content .show-times .show-encore-label {
+ font-size: 14px;
+}
+
+#show-content .show-times table {
+ padding: 0;
+ margin: 0;
+}
+
+#show-content .show-times table td {
+ vertical-align: top;
+}
+
+#show-content .show-times .show-schedule-link {
+ font-size: 0.8em;
+}
+
+#show-content .show-times .show-day-time, #show-content .show-times .show-time {
+ font-size: 16px;
+}
+
+#show-content .show-latest-title {
+ margin-bottom: 10px;
+}
+
+/* Show Description */
+/* ---------------- */
+#show-content .show-sections .show-jump-links {
+ font-size: 12px;
+}
+
+#show-content .show-description {
+ position: relative;
+ max-height: 800px;
+ overflow: hidden;
+}
+
+#show-content .show-description.expanded,
+#show-content .show-description.narrow.expanded {
+ max-height: none !important;
+ overflow: visible;
+}
+
+#show-content #show-desc-buttons {
+ width: 100%;
+ text-align: right;
+ padding-bottom: 20px;
+}
+
+#show-content #show-desc-buttons #show-desc-less {
+ display: none;
+}
+
+#show-content .show-description {
+ -webkit-mask-image: linear-gradient(to bottom, black calc(100% - 6em), rgba(0,0,0,0.5) calc(100% - 3em), transparent 100%);
+ mask-image: linear-gradient(to bottom, black calc(100% - 6em), rgba(0,0,0,0.5) calc(100% - 3em), transparent 100%);
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ -webkit-mask-size: 100% 100%;
+ mask-size: 100% 100%;
+}
+
+#show-content .show-description.expanded {
+ -webkit-mask-image: none;
+ mask-image: none;
+}
+
+/* #show-content #show-more-overlay {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 6em;
+ background: -webkit-linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
+ background-image: -moz-linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
+ background-image: -o-linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
+ background-image: linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
+ background-image: -ms-linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
+}
+
+#show-content .show-description.expanded #show-more-overlay {
+ display: none;
+} */
+
+
+/* Show Tabs */
+/* --------- */
+#show-content .show-tabs {
+ font-size: 0;
+}
+
+#show-content .show-tabs .show-tab {
+ display: inline-block;
+ min-width: 100px;
+ line-height: 40px;
+ font-size: 18px;
+ color: #444444;
+ margin: 0;
+ cursor: pointer;
+ text-align: center;
+ background-color: #eeeeee;
+ border-radius: 7px 7px 0 0;
+ border: 1px solid #000000;
+ padding: 6px 14px;
+}
+
+#show-content .show-tabs .show-tab-spacer {
+ display: inline-block;
+ width: 50px;
+ line-height: 40px;
+ font-size: 18px;
+ border-bottom: 1px solid #000000;
+ border-top: 1px solid transparent;
+ padding: 6px 0;
+}
+
+#show-content .show-tabs .show-tab.tab-active {
+ font-weight: bold;
+ background-color: transparent;
+ color: #000000;
+ border-bottom: 0;
+}
+
+#show-content .show-tabs .show-tab.tab-inactive {
+
+}
+
+#show-content .show-tabs .show-tab.tab-inactive:hover {
+ background-color: #f5f5f5;
+}
+
+#show-content .show-section .show-tab.tab-inactive {
+ display: none;
+}
+
+/* Alternative Layouts */
+/* ------------------- */
+#show-content.left-blocks .show-info {
+ margin-right: 30px;
+}
+
+#show-content.right-blocks .show-info {
+ float: right;
+ margin-left: 30px;
+}
+
+#show-content.top-blocks .show-info {
+ float: none;
+}
+
+#show-content.top-blocks .show-info .show-block {
+ display: inline-block;
+ border-left: 1px solid #AAAAAA;
+ padding-left: 20px;
+ margin-left: 20px;
+ margin-top: 0;
+}
+
+#show-content.top-blocks .show-info .show-block.first-block {
+ border-left: none;
+ padding-left: 0;
+ margin-left: 0;
+}
+
+#show-content.top-blocks .show-info .show-block.last-block {
+ margin-bottom: 0;
+}
+
+/* Mobile Screens */
+/* -------------- */
+#show-content.narrow .show-sections {
+ overflow: hidden;
+}
+
+#show-content.narrow .show-description {
+ max-height:600px;
+}
+
+#show-content.narrow .show-tabs .show-tab-spacer {
+ width: 20px;
+}
+
+#show-content.right-blocks.narrow .show-info {
+ margin-left: 0;
+}
+
+#show-content.narrow .show-info .show-block {
+ max-width: 95%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+#show-content.narrow .show-info .show-avatar, #show-content.narrow .show-info .show-controls {
+ display: inline-block;
+}
+
+#show-content.narrow .show-info .show-images {
+ margin-right: 20px;
+ margin-left: 0;
+}
+
+#show-content.narrow .show-info .show-avatar img {
+ width: 150px;
+ height: 150px;
+}
+
+#show-content.narrow .show-info .show-download {
+ margin-left: 0;
+}
diff --git a/docs/API.md b/docs/API.md
new file mode 100644
index 0000000..9706b0b
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,128 @@
+Radio Station Plugin API
+
+***
+
+Since 2.3.0, Radio Station includes an API for all of your Station data.
+
+## Data Endpoints
+
+Data Endpoints for Schedule and Show Data are available via WordPress REST API Routes (if option enabled.)
+Alternatively, they are also available via WordPress Feed Links (again if that option is enabled.)
+Note the default output format for all data endpoints is JSON.
+
+#### Station Data
+
+All Endpoints add the following top level Station data to the output:
+
+* *timezone*: Radio Timezone (via Plugin Settings)
+* *stream_url*: Streaming URL (via Plugin Settings)
+* *station_url*: Station Home URL (via `site_url()`)
+* *schedule_url*: Schedule Page URL (via Plugin Settings)
+* *language*: Main Station Language Code (eg. en_US)
+* *timestamp*: UTC Generated Timestamp for the Request
+* *date_time*: Generated Date Time Stamp (YY-mm-dd H:i:s)
+* *endpoints*: List of data endpoints and URLs (array) Note this list will be REST API Routes or Feed URLs depending on which is used to make the request.
+* *success*: set to true (1) on request success and false (0) on failure
+
+#### Show Data
+
+Each `[Show]` data array referenced in the below Endpoints has the following keys:
+
+* *id*: Show ID
+* *name*: Show Title
+* *slug*: Show Slug
+* *url*: Show Permalink URL
+* *latest*: Latest Audio URL
+* *website*: Show Website URL
+* *hosts*: Array of Show Hosts (display *name* and profile *url*)
+* *producers*: Array of Show Producers (display *name* and profile *url*)
+* *genres*: Array of Show Genres
+* *languages*: Array of Show Languages
+
+
+### Endpoint List
+
+#### Radio Discovery Endpoint
+
+**/wp-json/radio/** and/or **/feed/radio/**
+
+This is the base discovery Endpoint for all the other Endpoints. If REST URL is used, this will list the REST Routes, and if Feed URL is used this will list the Feed Endpoints.
+
+* *namespace*: The base namespace for all Endpoints (default is */radio*)
+* *routes*: Data array of all the available Endpoints URLs
+
+
+
+
+#### Station Endpoint
+
+**/wp-json/radio/station/** and/or **/feed/radio/station/**
+
+An Endpoint for retrieving *ALL* available Station data at once.
+
+* *station*: Data array containing the Data from every other Endpoint below.
+
+The Station data has the following keys, each containing the data for that Endpoint:
+
+* *broadcast*: see Broadcast Endpoint
+* *schedule*: see Schedule Endpoint
+* *shows*: see Shows Endpoint
+* *genres*: see Genres Endpoint
+* *languages*: see Languages Endpoint
+
+#### Broadcast Endpoint
+
+**/wp-json/radio/broadcast/** and/or **/feed/radio/broadcast/**
+
+Lists the Current Show and Next Scheduled Show.
+
+* *current_show*: Currently Scheduled `[Show]` Details
+* *next_show*: Next Scheduled `[Show]` Details
+
+#### Schedule Endpoint
+
+**/wp-json/radio/schedule/** and/or **/feed/radio/schedule/**
+
+* *schedule*: Data array of weekly Show Schedule by Day (ordered by Shift Times)
+
+Each Scheduled Shift has the following keys:
+
+* *day*: Scheduled Shift Day
+* *start*: Scheduled Shift Start Time
+* *end*: Scheduled Show End Time
+* *updated*: Last Updated Timestamp for the Show
+* *encore*: Whether the Scheduled Shift is an encore (repeat) airing. 0 or 1
+* *split*: Whether the Scheduled Shift is split overnight. 0 or 1
+* *override*: Whether the Shift is a Schedule Override. 0 or 1
+* *show*: Data array of `[Show]` Details for this Shift
+
+#### Shows Endpoint
+
+**/wp-json/radio/shows/** and/or **/feed/radio/shows/**
+
+Optionally, a single or comma-separated `show` argument will retrieve specific Show information.
+
+* *shows*: Data array of all `[Shows]` (ordered by Show Name)
+
+#### Genres Endpoint
+
+**/wp-json/radio/genres/** and/or **/feed/radio/genres/**
+
+Optionally, a single or comma-separated `genre` argument will retrieve specific Genre information.
+
+* *genres*: Data array of all Genres with all the `[Shows]` for each Genre.
+
+#### Languages Endpoint
+
+**/wp-json/radio/languages/** and/or **/feed/radio/languages/**
+
+Optionally, a single or comma-separated `language` argument will retrieve specific Language information.
+
+* *languages*: Data array of all Languages with all the `[Shows]` for each Language.
+
+#### [Pro] Episodes Endpoint
+
+#### [Pro] Hosts Endpoint
+
+#### [Pro] Producers Endpoint
+
diff --git a/docs/Data.md b/docs/Data.md
new file mode 100644
index 0000000..8c5a7f3
--- /dev/null
+++ b/docs/Data.md
@@ -0,0 +1,58 @@
+# Radio Station Plugin Data
+
+***
+
+## Custom Post Types
+
+### Shows
+
+The main Post Type for Radio Station with repeater field for assignable Show Shifts. Hosts, Producers, Genres and Languages terms can also be assigned. A Featured Image, Show Avatar Image and (if turned on in plugin Settings) an alternative Show Header can be added to the Show via the Show Images metabox (all images upload to the WordPress Media Library.) Each Show also has the following meta data fields:
+
+* *show_link*: optional URL to the Show's external website
+* *show_file*: optional URL to the Show's latest audio file
+* *show_email*: optional email contact for the Show
+* *show_patreon*: option Patreon ID for the Show's Patreon page
+
+### Schedule Overrides
+
+Similar to Shows but use a date and time block instead of Shifts. A Datepicker field is used to specify the Override start date, and dropdowns for the start and end times. Hosts, Producers, Genres and Languages terms can also be assigned. A Featured Image and Override Avatar Image can also be added via the Show Images metabox (all images upload to the WordPress Media Library.)
+
+### Playlists
+
+A Track list assignable to a Show, with a repeater field for track information. A Playlist can be assigned to a particular Show via the Edit Playlist screen. The most recently published Playlist will be considered current for a Show when that Show is playing and display in the Current Playlist Widget (or Widget Shortcode)
+
+For each Track the following can be specified:
+
+* Artist (Text Field)
+* Song Title (Text Field)
+* Album (Text Field)
+* Label (Text Field)
+* Comments (Text Field)
+* New Track (Checkbox)
+* Queued or Played (Status Dropdown)
+
+### Show Posts
+
+Standard WordPress Posts are assignable to a Show via a metabox on the Post Edit screen. This is good for allowing listeners to find news and other announcements for a particular Show, for when assigned to a Show they will display in a paginated list on that Show's page.
+
+### [Pro] Show Episodes
+
+[Radio Statio Pro](https://radiostation.pro) will include Show Episodes. These will be assignable to a Show to create and archive of Episodes for specific dates that will display on the Show page.
+
+### [Pro] Host and Producer Profiles
+
+[Radio Station Pro](https://radiostation.pro) will include Show Host and Producers Profile pages. These will display a profile template rather than the standard Author template for Hosts and Producers.
+
+
+## Taxonomies
+
+### Genre Taxonomy
+
+A flexible taxonomy allowing for the addition of Genre terms that can be assigned to a Show (or Override.) A Genre does not have to be music, as it can also be used to assign topics for talk shows for example. Having a Genre assigned allows for user highlighting on the Program Schedule, and discoverable via the plugin's data [API](./API.md) Genres Endpoint. They are displayed on the Show's page, and can also be displayed in widgets and other shortcodes. There is also a [Genres Archive Shortcode](./Shortcodes.md#genres-archive-shortcode) that lists Shows sorted by their assigned Genre terms.
+
+### Language Taxonomy
+
+A fixed taxonomy allowing for assigning of Language terms to a Show (or Override.) In a Show does not have a Language assigned, it is assumed to be in the main Language selected in the Plugin Settings. Languages are displayed on a Show's page, and discoverable via the plugin's data [API](./API.md) Languages Endpoint, and in future will be displayable in widgets and other shortcodes also. There will also be an addition of a Languages Archive Shortcode that will work similar to the Genre Archive Shortcode.
+
+
+
diff --git a/docs/Display.md b/docs/Display.md
new file mode 100644
index 0000000..48cf428
--- /dev/null
+++ b/docs/Display.md
@@ -0,0 +1,150 @@
+# Radio Station Plugin Displays
+
+***
+
+For live display examples see [Radio Station Demo Site](https://demo.radiostation.pro).
+
+## Styling
+
+#### Default Styles
+
+You can find the base styles in the `/css` directory of the plugin, prefixed with `rs-`. There is a CSS file for Schedule Views, Shortcodes (also used for Widgets) and Page Templates. The default styles have intionally kept fairly minimal so as to be compatible with most themes.
+
+#### Custom Styling
+
+You may wish to add your own styles to suit your site's look and feel. One easy way to do this is to add your own `rs-custom.css` to your Child Theme's directory, and add more specific style rules that modify or override the existing styles. Radio Station will automatically detect the presence of this file and enqueue it. This is preferable to modifying the base style files directly, as your changes will be overwritten in a plugin update.
+
+
+## Timezone Conversions
+
+#### Automatic Display
+
+Radio Station will now display a shift time converted to the users timezone under every shift time displayed. This conversion is based on the selected Timezone in your plugin settings and the default timezone detected in the users browser. This feature is on by default, but can also be disabled in the Plugin Settings.
+
+#### [Pro] User Timezone Switching
+
+In [Radio Station Pro](https://radiostation.pro), there is an additional interface displayed in the Clock/Timezone shortcode (automatically displayed above Schedules and also in the Radio Clock widget.) This allows your visitors/listeners to select a specific timezone other than what is detected in their browser. This can be useful for travellers especially, for keeping up with their home Shows, or tuning into a new local station without needing to update their computer clock.
+
+
+## Automatic Pages
+
+### Master Schedule Page
+
+Since one of the main purposes of this plugin is to provide your listeners with a useable Schedule for your Shows, having the ability to easily assign a page and display a selected schedule View on it is a natural option to have. To do this, simply create a new WordPress Page and title it as desired, then go to the Radio Station Plugin Settings page and select that Page for this Option and Save. This will also make your Schedule page discoverable via the plugin's Data Endpoints.
+
+The following Views are available for the Master Schedule:
+
+* Table - [default] responsive program in table form
+* Tabbed - responsive styled list view with day selection tabs
+* List - unstyled plain list view for custom development use
+* Divs - [display issues] legacy unstyled div based view
+* Legacy - [deprecated] legacy table grid view
+
+Table or Tabbed are recommended for the best ready display result. By enabling the "Automatic Display" option for this page, the selected Master Schedule View will automatically replace the content of that page, using with default attributes. If you want to use other display options to customize what displays in the Schedule - or wish to combine it with other content on that page - then disable the Automatic Display option and instead manually place the [Master Schedule Shortcode](./Shortcodes.md#master-schedule-shortcode) `[master-schedule]` in the page content instead.
+
+#### [Pro] Extra Master Schedule Views
+
+In [Radio Station Pro](https://radiostation.pro), two additional schedule views are available for displaying your station schedule:
+
+* Grid - vertically stacked responsive show grid based on days
+* Calendar - responsive weekly schedule view with horizontal day show slider
+
+You can set these are the default view in the settings, or display them via the master schedule shortcode. If using the Grid view there is also an extra option to line up the grid according to the show time slots.
+
+### Archive Pages
+
+Since Shows, Playlists and Genre Archives are automatically generated via the WordPress archive template specific to a Theme, this makes modifying them consistently an impossible task. Instead, the option has been creating to assign Archive Pages for these that can then use plugin shortcodes to display these Archives in a different and more meaningful way. Of course doing this is entirely optional, and not always a priority, but at least the option is there for when it is needed.
+
+
+## Page Templates
+
+**If you are have upgraded to 2.3.0, it is recommended you read this section carefully in case you need to take action!** (New users to the plugin on the other hand probably do not need to know these details unless wanting to customize something with the way the templates operate.)
+
+Since 2.3.0, Radio Station now uses a content filter method instead of single page templates to display the content for it's Post Types. This is because the page templates used previously were based on the default twenty-something series of WordPress themes, and so unfortunately these templates do not always play well with other themes. So to become theme-agnostic - to have a stable display output independent of the active theme - the content filtering method is now used instead.
+
+#### Page Template Selection
+
+In order to do this though, a standard page template (such as `page.php`, `single.php` or `singular.php`) still needs to be used, and the content is displayed within it. You can choose which template to use for a Post Type in the Plugin Options page. The main reason this option has been made available is that it is just not possible to determine whether a Theme displays the Featured Image for a Show in the standard template or not. So having this as an option solves that problem. See [Images](#show-images) below for more details on this.
+
+#### WordPress Template Hierarchy
+
+Obviously, Radio Station still has to honour the WordPress Template Hierarchy. So for example, if you create your own custom `single-show.php` template for Shows and place it in your Child Theme, as expected it will be used instead of the standard page template selected. Additionally, to ensure this override really takes precedence, it should be noted that the content will *not* be filtered by default when a custom template is found and used (read on...)
+
+#### Important Note on Legacy Templates!
+
+What this means is also that *if you have previously copied a legacy template into your Child Theme, you will need to take action to see the updated display*. And since it is indicated you might want to do this previous version's documentation, it is quite possible that you have! In this case, there are two main options. If you have already customized a template to match your Theme, you can enable the "Combined Method" for that template on the Plugin Settings page, which will then use both the page template and the content filtering method together.
+
+If you have *not* customized templates to match your Theme, and yet they are present, then it is recommended that you *remove the old templates from your Child Theme entirely*. This will allow the plugin to work via the new content filter method with the standard template within your theme. The Legacy templates names that you should look for and remove are: `single-show.php`, `single-playlist.php`, `archive-playlist.php`, `playlist-archive-template.php`, `show-blog-archive-template.php`
+
+We recognize this is may be a rather confusing and complex situation for existing users transitioning to 2.3.0, and if this is the case suggest rereading these section until it becomes clear. We are however confident that our solution going forward here is the best one, as it maintains backwards compatibility, honours the WordPress Template Hierarchy and enables implementing the revamped content displays. Fortunately, the majority of users will not be affected by these changes at all, but if you are the above options should easily resolve this.
+
+### Show Page Content
+
+The Show Page Content display has undergone the biggest of changes so far within the plugin, with an entirely new responsive layout for Shows that is displayed within the content area. This has required a lot of thought to allow for in-built flexibility, because it has to account for variations of what information is supplied for a Show and what is not. In other words, whether it is bare or full it, still needs to look good! Note also that there are some Plugin Options relating to this layout, namely where the info blocks are floated, and whether the content sections below are tabbed or display in full in a series.
+
+### Override Page Content
+
+Previous to 2.3.0, Schedule Overrides did not have their own page display enabled. We decided to change this, so in most ways they are now closer to Shows and handled in the same way. The main difference being that they have a single specific date and time block for the scheduled override timeslot instead of Shifts.
+
+### Playlist Page Content
+
+Currently this is a simple table of Tracks for the specified Playlist, taken from the Legacy template. This will receive further attention in the future to allow for a more customizable display.
+
+
+## Images
+
+### Show Images
+
+Since 2.3.0, Featured Images and Show Avatars have been split into two different images. This was done so the Featured Image could be treated in a more standard way like in a WordPress post or page - as the wide image at the top of a post. Whereas, a Show Avatar is more likely to be smaller and square like a logo. Note than existing Show Featured Images prior to 2.3.0 are automatically transferred to Show Avatars as that was their only usage in previous versions, allowing for the new dual usage, so there is no need manually make any changes to those.
+
+#### Show Featured Image
+
+A Show Featured Image is intended to display at the top of a single Show Page, just as it might on a standard post or page. However, depending on the page template you are using for Shows, it *might not* automatically display there if the template does not include it. This is one reason why there is an option to choose between the `page.php`, `single.php` and `singular.php` templates for the Show Page Template on the Plugin Settings Page. And if for some reason your chosen template does not display the Featured Image, you can use the Show Content Header option below.
+
+#### Show Avatar
+
+Primarily displayed on the single Show Page content layout in the first information box. Also may be displayed in the Current Show and Upcoming Show Widgets when that widget option is checked (or in the corresponding Widget Shortcodes) - with an optional display width option. There are also contextual filters available throughout the plugin which enable adjusting the image size of the Show Avatar display. Show Avatars also display (by default) in the Show Archive Shortcode and Genre Archive Shortcode. They can also be shown in the Master Schedule Shortcode if desired (the default there is to display for the Tabbed and List Views only.)
+
+#### Show Content Header
+
+If your chosen Show template (eg. `page.php`) does not display the Featured Image above the Show title automatically, you can enable this option via Plugin Settings Page. This will provide an additional Content Header Image selection on the Show edit screen image box (below the Show Avatar.) Adding this image to a Show will mean it is displayed as a Header on the Show page - the only difference being it will be below the Show title and not above it like a Featured Image would.
+
+### Schedule Override Images
+
+Since 2.3.0, image support has been added to Schedule Overrides so that they behave just like Shows in this respect. So the above sections on Show Images also now apply to Schedule Overrides.
+
+### [Pro] Profile Images
+
+In [Radio Station Pro](https://radiostation.pro), Show Host and Producer Profiles also support having images added, which then display on the public profile pages for the Host or Producer.
+
+### [Pro] Episode Images
+
+In [Radio Station Pro](https://radiostation.pro), Episodes also support having images added, which then display on the public page for that Show Episode.
+
+### [Pro] Genre Images and Colors
+
+In [Radio Station Pro](https://radiostation.pro), images and colors can also be assigned to Genre taxonomy terms via the WordPress Admin Taxonomy Editing interface. Images may then optionally be displayed in the [Genre Archive Shortcode](./Shortcodes.md#genre-archives-shortcode) to provide another visual level to that Show list display. Genre colors are used in the Master Schedule display when the user highlights particular Genres.
+
+
+## Translations
+
+#### Using WordPress Translations
+
+Please note that the "Main Broadcast Language" Setting on the plugin page does NOT translate any strings, it is for assigning the default Language for Shows, which is displayed to visitors on the Show page etc. See the [Language Taxonomy](./Data.md) for more details.
+
+Radio Station is fully translation ready. This means all the text strings within the plugin are wrapped in translation functions so that WordPress can handle the automatic translation of these strings. If your site language is set through WordPress Admin -> Settings page (under Site Language) then you will see the translated string output changed in your selected language - but only for any strings where a translation exists.
+
+This is because some strings have never been translated into certain languages, but also because some have been translated in the past and some added to newer versions have not been translated yet. You can add to the existing translations in any Language via the [Radio Station Translation Project on WordPress.Org](https://translate.wordpress.org/projects/wp-plugins/radio-station/) By logging in to WordPress.org you can now add these string translations directly, and they will be used by the WordPress to translate the plugin.
+
+#### Time Related Translations
+
+Unlike other word strings, those involving dates and times are automatically translated by the plugin into your WordPress language using the `WP_Locale` class. This means you do not have to retranslate common strings for days of the week, month names, or am/pm meridian strings.
+
+#### Using a Translation Plugin
+
+If you wish to use a translation plugin instead, you can find the relevant language filed in the `/languages/` directory of the plugin. Follow the translation plugin documentation on how to use these files. We welcome submissions of updated language files. Please contact us via email or submit a pull request via the [Github repository](https://github.com/netmix/radio-station/).
+
+
+
+
+
diff --git a/docs/FAQ.md b/docs/FAQ.md
new file mode 100644
index 0000000..e3663f0
--- /dev/null
+++ b/docs/FAQ.md
@@ -0,0 +1,223 @@
+# Radio Station Plugin FAQ
+
+***
+### Getting Started
+
+#### How do I get started with Radio Station (Free or PRO)?
+
+Read the [Quickstart Guide](./index.md#quickstart-guide) for an introduction to the plugin, what features are available and how to set them up.
+
+#### Where can I find the full Radio Station documentation (Free or PRO)?
+
+The latest documentation [can be found online here](https://radiostation.pro/docs/). Documentation is also included with the currently installed version via the Radio Station Help menu item located under the Radio Station admin menu. You can find the Markdown-formatted files in the `/docs` folder of the [GitHub Repository](https://github.com/netmix/radio-station/docs/) and in the `/docs` folder of the plugin directory.
+
+#### How do I get support for Radio Station (Free or PRO)?
+
+For Radio Station customers using the free, open-source version of our plugin, you can contact us via [our support channel in the WordPress support forums here](https://wordpress.org/plugins/support/radio-station). If you have any bug reports or feature suggestions please [open an issue on our Github repository](https://github.com/netmix/radio-station/) For Radio Station PRO subscribers, you can email us at support@radiostation.pro and someone will respond to your inquiry within 12 to 124 hours. All support inquiries will be handled in the order they are received. Before contacting support or opening an issue, make sure you check for conflicts by disabling all of your plugins and re-enabling them one at a time to ascertain which plugin is conflicting with Radio Station. Note that Radio Station PRO works as an addon to Radio Station, so deactivating it will disable the PRO features until you reactivate it.
+
+#### Can I try Radio Station PRO before I purchase the plugin?
+
+Yes, you can trial Radio Station PRO for up to 14 days. You are required to set a credit or debit card when you sign up for the free trial and can cancel any time before the trial ends. The credit or debit card on file will be charged automatically once the trial expires 14 days from the date your free trial began. [Click here to start your trial.](https://radiostation.pro/pricing)
+
+
+### Account and Billing
+
+#### How do I access my Radio Station PRO account?
+
+We have partnered with Freemius who provide an integrated subscription and upgrade system for WordPress plugin developers. When you purchase Radio Station PRO, you will receive email instructions to create your account, which contain a link to the [Freemius User Dashboard here](https://users.freemius.com/login). While you can see your account details by navigating to WordPress Dashboard > Radio Station > Account, logging into the Freemius Dashboard will give you full control over your account.
+
+#### Can I request a refund for Radio Station PRO?
+
+We offer a 30 day, moneyback guarantee. If you are not satisfied with Radio Station PRO within the 30 day period, we will issue a full refund, no questions asked. Once the 30 day period is exhausted refunds are not available.
+
+#### How do I cancel my Radio Station PRO subscription?
+
+Login to your [Freemius User Dashboard](https://users.freemius.com/login) and navigate to Renewals & Billing to cancel your Radio Station PRO subscription. When you cancel your subscription, your account will stay active for the remainder of the billing period. Once your subscription is cancelled, you will lose access to PRO customer support and any future upgrades, bug fixes and feature additions. Radio Station (free) will continue to operate normally.
+
+
+### Plugin Usage
+
+#### How do I schedule a Show?
+
+Simply create a new show via Add Show in the Radio Station plugin menu in the Admin area. You will be able to assign Shift timeslots to it on the Show edit page, as well as add the Show description and other meta fields, including Show images.
+In order to schedule a Show, a Show must be added and available to accept schedule entries. If this is your first time using Radio Station, create a new show via the Add Show item in the Radio Station menu in your WordPress Admin screen. You can assign Shift timeslots to your new Show or pre-existing Show on the Show edit page, as well as add a Show description and other meta fields, including Show images.
+
+#### How do I display a full schedule of my Station's shows?
+
+Navigate to the plugin Settings page via the Radio Station menu in your WordPress Admin screen and then click the Pages tab. There you can select the Page on which to automatically display the schedule, as well as which View to display (a Table grid by default.) Alternatively, you can use the shortcode `[master-schedule]` on any page (or post.) This option allows you to use additional shortcode attributes to control what is displayed in your Schedule (see [Master Schedule Shortcode Docs](./Shortcodes.md#master-schedule-shortcode) )
+
+#### What if I want to schedule a special event or one-off schedule change?
+
+If you have a one-off event that you need to show up in the Schedule and Widgets, you can create a Schedule Override by navigating to WordPress Dashboard > Radio Station > Schedule Overrides > Add New. This will allow you to set aside a block of time on a specific date, and when the Schedule or Widget is displaying that date, the override will be used instead of the normally scheduled Show. You can also link an Override to an existing Show, and partially update any Show information to be overridden. (Note that Schedule Overrides will not display in the old Legacy Table/Div Views of the Master Schedule.) In the Free version, if an Override needs to apply to multiple dates, you must schedule each time slot individually. In the PRO version, you can repeat the Override via day periods or monthly recurrences. To enable recurring overrides, [Upgrade to Pro here](https://radiostation.pro/pricing/)
+
+#### How do I change the Show Image displayed in the widgets and schedule?
+
+The schedule, widgets and show page will display whichever avatar is assigned to the show on the Show Edit screen. Navigate in the WordPress Dashboard to Radio Station -> Shows and locate the Show you want to add/edit the Avatar for in the Shows list. Edit the Show then simply set a new image for the Show.
+
+#### How do I style how the plugin displays content on the front end of my site?
+
+The default styles for Radio Station have intentionally kept fairly minimal so as to be compatible with most themes, so you may wish to add your own CSS styles to suit your site's look and feel. You can add these styles via Theme Customizer's Additional CSS setting. You can also add your own `rs-custom.css` file to your Child Theme's directory, and Radio Station will automatically detect the presence of this file and enqueue it. Either way you can add more specific selectors with rules that modify or override the existing styles. You can find the base styles in the `/css/` directory of the plugin.
+
+
+### Widgets, Blocks and Shortcodes
+
+#### What Widgets/Blocks are available with this plugin?
+
+The following Widgets are available to add via the WordPress Appearance -> Widgets page:
+
+- Streaming Player
+- Current Show
+- Upcoming Shows
+- Current Playlist
+- Radio Clock
+
+Since 2.5.0, these widgets are now also available as Gutenberg Blocks. See the [Widget Documentation](./Widgets.md) for more details on these Widgets.
+
+#### Do the Widgets reload automatically?
+
+In the free version of Radio Station, the Current Show, Upcoming Shows, and Current Playlist widgets can display a countdown, but do not refresh automatically. To enable auto-reloading of these widgets, so that they refresh exactly at a Show’s changeover time, [upgrade to Radio Station PRO](https://radiostation.pro/pricing/).
+
+Current Show, Upcoming Shows and Current Playlist widgets do not refresh automatically in the Free version of Radio Station. This functionality is only available in our Pro version so widgets refresh exactly at a Show's changeover time. To enable s refresh exactly at Show changeover times. To enable auto-refresh widgets [upgrade to Radio Station PRO](https://radiostation.pro)
+
+#### What Shortcodes are available in Radio Station?
+
+See the [Shortcode Documentation](./Shortcodes.md) for more details and a full list of possible Attributes for these Shortcodes:
+
+* `[master-schedule]` - Master Program Schedule Display
+* `[current-show]` - Current Show Widget
+* `[upcoming-shows]` - Upcoming Shows Widget
+* `[current-playlist]` - Current Playlist Widget
+* `[shows-archive]` - Archive List of Shows
+* `[genres-archive]` - Archive List of Shows sorted by Genre
+* `[languages-archive]` - Archive List of Shows sorted by Language
+* `[overrides-archive]` - Archive List of Schedule overrides
+* `[playlists-archive]` - Archive List of Show Playlists
+
+(Note old shortcode aliases will still work in current and future versions to prevent breakage.)
+
+#### Do you include Page Builder support?
+
+The free version of Radio Station includes support for classic Widgets and Gutenberg Blocks. The same widgets are available as modules for Elementor and Beaver Builder in the Pro version, along with additional styling options for each module. To enable these page builder modules, [upgrade to PRO here](https://radiostation.pro/pricing/).
+
+
+### User Plugin Roles
+
+#### How do I assign a User as the Host or Producer to one or more Shows?
+
+You can to assign the Host or Producer roles to any WordPress User by accessing the User editor located under WordPress Dashboard -> Users. Search for or navigate to the User you want to assign as a Host or Producer, then click Edit to open the Edit screen for that User. Find the Roles dropdown menu and select from the Role options provided. Choosing either Host or Producer grants the User that role. You can then assign that User to single or multiple Shows via the Show Edit page. A Host or Producer only has permissions to Edit the Show(s) they are assigned to. The Pro version includes an additional Role Editor interface where you can assign the plugin Roles to any number of users at once. To enable the Role Editor interface, [upgrade to PRO here](https://radiostation.pro/pricing/).
+
+#### How do I grant users other than Administrator and DJ roles permission to edit Shows and Playlists?
+
+There are a number of different options depending on what your goals are. Assigning a user as a Host or Producer to a Show will allow that User to edit it (and it's Playlists.) You could also assign a single User as the Author of a Show/Playlist. If you’d like to give a user that isn't a site Administrator permissions for all Radio Station records, you can assign them the Show Editor role that was created for this purpose. This may help keep clear lines of separation for editorial responsibility over your content. You can find more information on roles in the [Roles Documentation](https://radiostation.pro/docs/Roles/).
+
+#### How do I use a different image from the Gravatar for a Host/Producer?
+
+If you prefer not to use WordPress-owned, Gravatar.com for your User profile images, you'll need to install a plugin that allows you to add a different image to your Host/Producer's user account. You can search for a free plugin in the WordPress plugin repository at WordPress.org. As there are a number of plugins that do this already, it's mostly out of the scope of this plugin. However, in our Pro version, you can create separate Profile pages to showcase each of your Hosts and Producers, to which you can assign profile images that appear on those. To enable Profile pages, [upgrade to PRO here](https://radiostation.pro/pricing/).
+
+
+### Languages and Translations
+
+#### What languages other than English is the plugin available in?
+
+As of April 1st, 2023, known languages include the following:
+
+* Albanian (sq_AL)
+* Dutch (nl_NL)
+* French (fr_FR)
+* German (de_DE)
+* Italian (it_IT)
+* Russian (ru_RU)
+* Serbian (sr_RS)
+* Spanish (es_ES)
+* Catalan (ca)
+
+Note that for ease of translation the Free version contains any extra text strings from the PRO version. The auto-generated `radio-station.pot` file is located in the `/languages` directory of the plugin. If you do add a translation for your preferred language, please send or notify us of the completed translation to `info@netmix.com`. We'd love to include it.
+
+#### Can Radio Station be translated into my language?
+
+You may translate the plugin into any other language supported by the WordPress translation engine (GlotPress.) Here are some instructions on how to do that:
+
+- Go to https://translate.wordpress.org/ and sign in with a WordPress.org account (or create one).
+- In the search box, enter "Radio Station" plugin name or `radio-station` text domain, and click the project result to open its translation page. Here's the link: [Radio Station Translate Project Page](https://translate.wordpress.org/projects/wp-plugins/radio-station/)
+- On the project page, choose the language from the language list.
+- Choose the latest stable branch version number (note this may change as the plugin is updated so always select the latest version as older translations will automatically be transferred to it.)
+- Use the filter/search to find a string.
+- Click a string to open the editor.
+- In the "Translation" box enter the German text (keep placeholders like %s, %d, intact).
+- If unsure, add a comment in the discussion box for other translators/reviewers.
+- For contributors without commit rights: click "Suggest new translation".
+- If a translation is uncertain, mark it as "Needs work" or leave a note; do not publish incomplete translations.
+- Check the right‑hand panel for translation suggestions and previous translations to stay consistent.
+- Reviews are handled by language moderators. If the customer has reviewer role, they can approve suggestions from others.
+
+
+### Troubleshooting
+
+#### Why aren't all my Shows displaying in the Schedule?
+
+Did you remember to check the "Active" checkbox for each Show? If a Show is not marked active, the plugin assumes that it's not currently in production and it is not shown on the Schedule. A Show will also not be shown if it has a Draft status or has no active Shifts assigned to it.
+
+#### I'm seeing a 404 Not Found error when I click on the link for a Show!
+
+Try re-saving your site's permalink settings via Settings > Permalinks. WordPress sometimes gets confused with a new custom post type is added. Permalink rewrites are automatically flushed on plugin activation, so you can also just deactivate and reactivate the plugin to regenerate your site's permalinks.
+
+#### Where is my data stored? Can I export my data?
+
+Radio Station is stores your site's settings and all post type data in your WordPress MySQL database on your webhost. You can export your data using WordPress Dashboard -> Tools -> Export feature, or use Radio Station PRO’s Export feature located at WordPress Dashboard -> Import/Export. Our import/export feature works with YML and not XML, which is the standard WordPress format.
+
+#### Why can't Show Hosts or Producers can't edit a Show page?
+
+The only Hosts and Producers that may Edit a Show are the ones assigned as Host(s) or Producer(s) to that specific Show in the respective user selection menus. This is to prevent Hosts/Producers from editing other Shows managed by different Hosts/Producers without permission. If you need a user other than Administrator to be able to edit all Shows you can assign them a Show Editor role.
+
+
+### Integrations
+
+#### Can I use this plugin for Podcasts?
+
+While the plugin is not specifically geared toward Podcasting, which is not live programming, some podcaster's have used Radio Station to let their subscribers know when they publish new shows.
+
+#### Can I use this plugin for TwitchTV, Facebook Live, YouTube or Clubhouse shows?
+
+Sure, there's no reason why you couldn't use the plugin to display a show schedule on a WordPress site for those services. Unfortunately, we are not currently syncing events from these platforms, but may do so in the future. While there may be APIs available from the larger services, Clubhouse does not yet have a public API, so scheduled rooms can't be automated to the Radio Station show scheduling system.
+
+#### I use Google Calendar to print a show schedule online. Can I import/sync my Google Calendar with Radio Station?
+
+We haven't built an interface between Google Calendar and Radio Station just yet, but it's on our radar to do so in the foreseeable future.
+
+#### Can I import Show data from Pro.Radio or the JOAN (Jock on Air Now) plugin?
+
+We do not have a method of importing data directly from JOAN or Pro.Radio
+
+
+### Development Versions
+
+#### How do I install the latest Development version for testing?
+
+If you are having issues with the plugin, we may recommend you install the development version for further bugfix testing, as it may contain fixes that are not yet released into the next stable WordPress version. It is recommended you do this on a staging site. Instructions:
+
+1. Visit the `develop` branch of the Radio Station Github repository at:
+`https://github.com/netmix/radio-station/tree/develop/`
+2. Click on the green "Code" button and select Download a ZIP.
+3. Unzip the downloaded file on your computer and upload it via FTP to the subdirectory of your WordPress install on your web server: `/wp-content/plugins/radio-station-develop/`
+4. Rename the subdirectory `/wp-content/plugins/radio-station/` to `/wp-content/plugins/radio-station-old/`
+5. Rename the subdirectory `/wp-content/plugins/radio-station-develop/` to `/wp-content/plugins/radio-station/`
+
+Then upload to WordPress via the plugin installer as normal.
+Note that it will install to /wp-content/plugins/radio-station-develop/, and because of this won't overwrite your existing installation, so you'll need to deactivate that before activating the development version.
+
+You can now visit your site to make sure nothing is broken. If you experience issues you can reverse the folder renaming process to activate the old copy of the plugin. If the new development version works fine, at your convenience you can delete the `/wp-content/plugins/radio-station-old/` directory.
+
+Alternatively, if you want to do this from your WordPress Plugin area, you can upload the development Zip file from your Plugins -> Upload page. This will install it to `/wp-content/plugins/radio-station-develop/`. You can then deactivate the existing Radio Station plugin from you Plugins page and then activate the development version. (You can tell them apart on the plugins page via their version numbers. Official releases are 2.x.x, only development releases have the extra digit 2.x.x.x) Again, if you experience issues, you can deactivate the development version and reactivate the old version.
+
+#### What about Pro Beta Version Testing?
+
+We are constantly improving and adding new features to [Radio Station Pro](https://radiostation.pro/pricing/). Periodically we will release a Beta version to test out a new feature (or fix) out before it is officially released. If you have a Pro license, you can access these cutting edge Pro Beta version releases in two ways:
+
+1. Download the Beta versions by logging in to your [Freemius User Dashboard](https://users.freemius.com/login). and navigating to the "Downloads" section. You will see a dropdown list of all the Radio Station Pro releases, including beta ones.
+2. Enable the Beta program option from your Radio Station Account page in your WordPress site's Admin area, and the latest Beta version will then be available as an update.
+
+**Important Note**: As we are developing the Free and Pro versions in tandem, the latest Pro Beta may require you to install a development version of the Free plugin for it to work. Please see the previous section for how you can install the development version from Github (if the required version is not yet available via the WordPress repository.)
+
+We recommend you test these on a Staging site (or a development copy of your live site.) This way you can make sure there are no significant bugs before using it on a production site. Of course, please be willing to [report any bugs](https://github.com/netmix/radio-station/issues) that you do find so we can ensure they are not present in the next official release.
+
+
diff --git a/docs/Filters.md b/docs/Filters.md
new file mode 100644
index 0000000..258c437
--- /dev/null
+++ b/docs/Filters.md
@@ -0,0 +1,692 @@
+# Radio Station Plugin Filters
+
+***
+
+## Settings Filters
+
+To programmatically override any of the Plugin Settings available from the Settings Page, see [Options Documentation](./Options.md)
+
+## Data Filters
+
+There are filters throughout the plugin that allow you to override data values and plugin output. We employ the practice of adding as many of these as possible to allow users of the plugin to customize it's behaviour without needing to modify the plugin's code - as these kind of modifications are overwritten with plugin updates.
+
+You can add your own custom filters via a Code Snippets plugin (which has the advantage of checking syntax for you), or in your Child Theme's `functions.php`, or in any file with a PHP extension in your `/wp-content/mu-plugins/` directory.
+
+## Finding Filters
+
+You can find these filters by searching any of the PHP plugin files for: `apply_filters( 'radio_`
+
+## Filter Values and Arguments
+
+Note the first argument passed to `apply_filters` is the name of the filter, the second argument is the value to be filtered. Additional arguments may also be provided to the filter so that you can match changes to specific contexts.
+
+## Filter Examples
+
+You can find many examples and tutorials of how to use WordPress filters online. Here is a generic filter example to help you get started with filters. This one will add custom HTML to the bottom of the Current Show Widget, regardless of which Show is playing:
+
+```
+add_filter( 'radio_station_current_show_custom_display', 'my_custom_function_name' );
+function my_custom_function_name( $html ) {
+ $html .= "
Now taking phone requests!
";
+ return $html;
+}
+```
+
+Note if a filter has additional arguments, and you wish to check them, you need to specify the number of arguments. To do this you must also include a filter priority. Here `10` is the (default) priority of when to run the filter and `3` is the number of arguments passed to the filter function. This example will add custom HTML to the bottom of the Current Show widget only if the Show ID is 20:
+
+```
+add_filter( 'radio_station_current_show_custom_display', 'my_custom_function_name', 10, 3 );
+function my_custom_function_name( $html, $show_id, $atts ) {
+ if ( 20 == $show_id ) {
+ $html .= "
Welcoming our newest DJ!
";
+ }
+ return $html;
+}
+```
+
+## Filter List
+
+Here is a full list of available filters within the plugin, grouped by file and function for ease of reference.
+
+| File / *Function* | Filter | Value | Extra Args |
+| - | - |
+|**radio-station.php**||||
+|*radio_station_localize_script*|`radio_station_time_separator` | ` ':'` | `'javascript'`|
+|*radio_station_streaming_data*|`radio_station_localization_script` | ` $js` | |
+| |`radio_station_streaming_data` | ` $data` | `$station`|
+|*radio_station_doing_template*|`radio_station_player_allowed_origins` | ` $allowed` | |
+|*radio_station_phone_number*|`radio_station_template_dir_hierarchy` | ` $dirs` | `$template`, `$paths`|
+|*radio_station_automatic_pages_content_get*|`radio_station_automatic_schedule_atts` | ` $atts` | |
+| |`radio_station_automatic_show_archive_atts` | ` $atts` | |
+| |`radio_station_automatic_override_archive_atts` | ` $atts` | |
+| |`radio_station_automatic_playlist_archive_atts` | ` $atts` | |
+| |`radio_station_automatic_genre_archive_atts` | ` $atts` | |
+| |`radio_station_automatic_languagee_archive_atts` | ` $atts` | |
+| |`radio_station_'.$post_type.'_content_templates` | ` $templates` | `$post_type`|
+| |`radio_station_single_template_post_data` | ` $post` | `$post_type`|
+| |`radio_station_content_'.$post_type` | ` $output` | `$post_id`|
+|*radio_station_override_content_template*|`radio_station_host_templates` | ` $templates` | |
+| |`radio_station_producer_templates` | ` $templates` | |
+|*radio_station_archive_template_hierarchy*|`radio_station_show_related_post_types` | ` $post_types` | |
+| |`radio_station_link_to_show_positions` | ` $positions` | `$post_type`, `$post`|
+| |`radio_station_link_to_show_before` | ` $before` | `$post`, `$related_shows`|
+| |`radio_station_link_to_show_after` | ` $after` | `$post`, `$related_shows`|
+|**radio-station-admin.php**||||
+|*radio_station_license_activation_link*|`radio_station_settings_capability` | ` 'manage_options'` | |
+| |`radio_station_menu_position` | ` 5` | |
+| |`radio_station_manage_options_capability` | ` 'manage_options'` | |
+| |`radio_station_export_playlists` | ` false` | |
+|*radio_station_role_editor*|`radio_station_role_editor_message` | ` true` | |
+|**includes/data-feeds.php**||||
+|*radio_station_api_discovery_link*|`radio_station_api_discovery_header` | ` $header` | |
+| |`radio_station_api_discovery_link` | ` $link` | |
+| |`radio_station_api_discovery_rsd` | ` $link` | |
+|*radio_station_add_station_data*|`radio_station_station_data` | ` $station_data` | |
+|*radio_station_get_broadcast_data*|`radio_station_broadcast_data` | ` $broadcast` | |
+|*radio_station_get_shows_data*|`radio_station_shows_data` | ` $shows` | `$show`|
+|*radio_station_get_languages_data*|`radio_station_genres_data` | ` $genres` | `$genre`|
+|*radio_station_station_endpoint*|`radio_station_languages_data` | ` $languages_data` | `$language`|
+|*radio_station_register_rest_routes*|`radio_station_route_slug_base` | ` 'radio'` | |
+| |`radio_station_route_slug_station` | ` 'station'` | |
+| |`radio_station_route_slug_broadcast` | ` 'broadcast'` | |
+| |`radio_station_route_slug_schedule` | ` 'schedule'` | |
+| |`radio_station_route_slug_shows` | ` 'shows'` | |
+| |`radio_station_route_slug_genres` | ` 'genres'` | |
+| |`radio_station_route_slug_languages` | ` 'languages'` | |
+|*radio_station_route_radio*|`radio_station_route_urls` | ` $routes` | |
+| |`radio_station_route_slug_base` | ` 'radio'` | |
+|*radio_station_route_station*|`radio_station_route_station` | ` $station` | `$request`|
+| |`radio_station_route_broadcast` | ` $broadcast` | `$request`|
+|*radio_station_route_schedule*|`radio_station_route_schedule` | ` $schedule` | `$request`|
+|*radio_station_route_genres*|`radio_station_route_shows` | ` $show_list` | `$request`|
+|*radio_station_route_languages*|`radio_station_route_genres` | ` $genre_list` | `$request`|
+| |`radio_station_route_languages` | ` $language_list` | `$request`|
+|**includes/master-schedule.php**||||
+|*radio_station_master_schedule*|`radio_station_master_schedule_default_atts` | ` $defaults` | `$view`, `$views`|
+| |`radio_station_schedule_clock_atts` | ` array()` | `$atts`|
+| |`radio_station_schedule_timezone_atts` | ` array()` | `$atts`|
+| |`radio_station_schedule_control_order` | ` $control_order` | `$atts`|
+| |`radio_station_schedule_controls` | ` $controls` | `$atts`|
+| |`radio_station_schedule_controls_output` | ` $output` | `$atts`|
+| |`radio_station_schedule_override` | ` $output` | `$atts`|
+| |`master_schedule_table_view` | ` $html` | `$atts`|
+| |`master_schedule_tabs_view` | ` $html` | `$atts`|
+| |`master_schedule_list_view` | ` $html` | `$atts`|
+|*radio_station_ajax_schedule_loader*|`radio_station_master_schedule_loader_js` | ` $js` | |
+|*radio_station_master_schedule_genre_selector*|`radio_station_master_schedule_load_script` | ` $js` | `$atts`|
+| |`radio_station_master_schedule_table_js` | ` $js` | |
+|**includes/post-types.php**||||
+|*radio_station_create_post_types*|`radio_station_post_type_show` | ` $post_type` | |
+| |`radio_station_post_type_playlist` | ` $post_type` | |
+| |`radio_station_post_type_override` | ` $post_type` | |
+| |`radio_station_host_interface` | ` false` | |
+| |`radio_station_post_type_host` | ` $post_type` | |
+| |`radio_station_producer_interface` | ` false` | |
+|*radio_station_post_type_editor*|`radio_station_post_type_producer` | ` $post_type` | |
+|*radio_station_add_featured_image_support*|`radio_station_admin_bar_post_types` | ` $post_types` | `'new'`|
+| |`radio_station_admin_bar_post_types` | ` $post_types` | `'edit'`|
+| |`radio_station_admin_bar_post_types` | ` $post_types` | `'view'`|
+| |`radio_station_genre_taxonomy_args` | ` $args` | |
+| |`radio_station_language_taxonomy_args` | ` $args` | |
+|**includes/post-types-admin.php**||||
+|*radio_remove_language*|`radio_station_language_edit_styles` | ` $css` | |
+| |`radio_station_language_edit_script` | ` $js` | |
+| |`radio_station_metabox_position` | ` 'rstop'` | `'shows'`|
+|*radio_station_add_show_hosts_metabox*|`radio_station_show_edit_styles` | ` $css` | |
+|*radio_station_add_show_producers_metabox*|`radio_station_metabox_position` | ` 'rstop'` | `'shifts'`|
+|*radio_station_shift_edit_script*|`radio_station_shift_list_edit_styles` | ` $css` | |
+| |`radio_station_shift_edit_script` | ` $js` | |
+|*radio_station_add_show_helper_box*|`radio_station_metabox_position` | ` 'rstop'` | `'helper'`|
+|*radio_station_add_override_show_metabox*|`radio_station_metabox_position` | ` 'rstop'` | `'overrides'`|
+| |`radio_station_override_edit_styles` | ` $css` | |
+|*radio_station_override_show_script*|`radio_station_override_show_script` | ` $js` | |
+| |`radio_station_metabox_position` | ` 'rstop'` | `'overrides'`|
+| |`radio_station_override_list_edit_styles` | ` $css` | |
+|*radio_station_override_save_data*|`radio_station_override_edit_script` | ` $js` | |
+|*radio_station_override_sortable_columns*|`radio_station_show_avatar` | ` $thumbnail_url` | `$post_id`|
+|*radio_station_override_past_future_filter*|`radio_station_overrides_past_future_default` | ` $pastfuture` | |
+| |`radio_station_metabox_position` | ` 'rstop'` | `'playlist'`|
+|*radio_track_add*|`radio_station_tracks_list_styles` | ` $css` | |
+|**includes/shortcodes.php**||||
+|*radio_station_clock_shortcode*|`radio_station_timezone_shortcode` | ` $output` | `$atts`|
+|*radio_station_archive_list_shortcode*|`radio_station_clock` | ` $clock` | `$atts`|
+| |`radio_station_'.$type.'_archive_post_args` | ` $args` | |
+| |`radio_station_'.$type.'_archive_posts` | ` $archive_posts` | |
+| |`radio_station_time_separator` | ` $time_separator` | `$post_type.'-archive'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `$post_type.'-archive'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `$post_type.'-archive'`, `$atts`|
+| |`radio_station_archive_shortcode_no_records` | ` $message` | `$post_type`, `$atts`|
+| |`radio_station_archive_'.$type.'_list_excerpt_length` | ` false` | |
+| |`radio_station_archive_'.$type.'_list_excerpt_more` | ` '[…]'` | |
+| |`radio_station_archive_shortcode_info_order` | ` $infokeys` | `$post_type`, `$atts`|
+|*radio_station_show_archive_list*|`radio_station_show_times_separator` | ` $separator` | `'override'`|
+| |`radio_station_'.$type.'_archive_content` | ` $post_content` | `$post_id`|
+| |`radio_station_'.$type.'_archive_excerpt` | ` $excerpt` | `$post_id`|
+| |`radio_station_archive_shortcode_info_custom` | ` ''` | `$post_id`, `$post_type`, `$atts`|
+| |`radio_station_archive_shortcode_info` | ` $info` | `$post_id`, `$post_type`, `$atts`|
+| |`radio_station_'.$type.'_archive_list` | ` $list` | `$atts`, `$post_type`|
+| |`radio_station_genre_archive_post_args` | ` $args` | |
+| |`radio_station_genre_archive_posts` | ` $posts` | |
+| |`radio_station_genre_image` | ` false` | `$genre`|
+| |`radio_station_genre_archive_excerpt_length` | ` false` | |
+| |`radio_station_genre_archive_excerpt_more` | ` '[…]'` | |
+| |`radio_station_genre_archive_excerpt` | ` $excerpt` | `$post->ID`|
+|*radio_station_language_archive_list*|`radio_station_genre_archive_list` | ` $list` | `$atts`|
+|*radio_station_archive_pagination*|`radio_station_language_archive_post_args` | ` $args` | |
+| |`radio_station_language_archive_posts` | ` $posts` | |
+| |`radio_station_language_archive_excerpt_length` | ` false` | |
+| |`radio_station_language_archive_excerpt_more` | ` '[…]'` | |
+| |`radio_station_genre_archive_excerpt` | ` $excerpt` | `$post->ID`|
+| |`radio_station_language_archive_list` | ` $list` | `$atts`|
+|*radio_station_show_posts_archive*|`radio_station_get_show_hosts` | ` false` | `$show_id`, `$args`|
+| |`radio_station_get_show_producers` | ` false` | `$show_id`, `$args`|
+| |`radio_station_get_show_episodes` | ` false` | `$show_id`, `$args`|
+| |`radio_station_show_'.$type.'_list_excerpt_length` | ` false` | |
+| |`radio_station_show_'.$type.'_list_excerpt_more` | ` '[…]'` | |
+| |`radio_station_show_'.$type.'_content` | ` $bio_content` | `$user_id`|
+| |`radio_station_show_'.$type.'_excerpt` | ` $excerpt` | `$user_id`|
+| |`radio_station_show_list_archive_avatar` | ` $thumbnail` | `$post['ID']`, `$type`|
+| |`radio_station_show_'.$type.'_content` | ` $post_content` | `$post_id`|
+| |`radio_station_show_'.$type.'_excerpt` | ` $excerpt` | `$post_id`|
+| |`radio_station_show_'.$type.'_list` | ` $list` | `$atts`|
+| |`radio_station_current_show_dynamic` | ` false` | `$atts`|
+| |`radio_station_widgets_ajax_override` | ` $ajax` | `'current-show'`, `$widget`|
+| |`radio_station_current_show_widget_excerpt_length` | ` false` | |
+| |`radio_station_current_show_widget_excerpt_more` | ` '[…]'` | |
+| |`radio_station_current_show_shortcode_excerpt_length` | ` false` | |
+| |`radio_station_current_show_shortcode_excerpt_more` | ` '[…]'` | |
+| |`radio_station_time_separator` | ` $time_separator` | `'current-show'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'current-show'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'current-show'`, `$atts`|
+| |`radio_station_current_show_link` | ` $show_link` | `$show_id`, `$atts`|
+| |`radio_station_show_times_separator` | ` $separator` | `'current-show'`|
+| |`radio_station_current_show_title_display` | ` $title` | `$show_id`, `$atts`|
+| |`radio_station_current_show_avatar_size` | ` $atts['avatar_size']` | `$show_id`|
+| |`radio_station_current_show_avatar` | ` $show_avatar` | `$show_id`, `$atts`|
+| |`radio_station_current_show_avatar_display` | ` $avatar` | `$show_id`, `$atts`|
+| |`radio_station_dj_link` | ` $host_link` | `$host`|
+| |`radio_station_current_show_hosts_display` | ` $hosts` | `$show_id`, `$atts`|
+| |`radio_station_current_show_encore_display` | ` $encore` | `$show_id`, `$atts`|
+| |`radio_station_current_show_playlist_display` | ` $playlist` | `$show_id`, `$atts`|
+| |`radio_station_current_show_widget_excerpt` | ` $excerpt` | `$show_id`, `$atts`|
+| |`radio_station_current_show_shortcode_excerpt` | ` $excerpt` | `$show_id`, `$atts`|
+| |`radio_station_current_show_description_display` | ` $description` | `$show_id`, `$atts`|
+| |`radio_station_current_show_shifts_display` | ` $shift_display` | `$show_id`, `$atts`|
+| |`radio_station_current_show_custom_display` | ` ''` | `$show_id`, `$atts`|
+| |`radio_station_current_show_section_order` | ` $order` | `$atts`|
+| |`radio_station_no_current_show_text` | ` $no_current_show` | `$atts`|
+| |`radio_station_countdown_dynamic` | ` false` | `'current-show'`, `$atts`, `$current_shift_end`|
+|*radio_station_upcoming_shows_shortcode*|`radio_station_current_show_load_script` | ` $js` | `$atts`|
+| |`radio_station_upcomins_shows_dynamic` | ` false` | `$atts`|
+| |`radio_station_widgets_ajax_override` | ` $ajax` | `'upcoming-shows'`, `$widget`|
+| |`radio_station_upcoming_shows_section_order` | ` $order` | `$atts`|
+| |`radio_station_time_separator` | ` $time_separator` | `'upcoming-shows'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'upcoming-shows'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'upcoming-shows'`, `$atts`|
+| |`radio_station_upcoming_show_link` | ` $show_link` | `$show_id`, `$atts`|
+| |`radio_station_show_times_separator` | ` $separator` | `'upcoming-shows'`|
+| |`radio_station_upcoming_show_title_display` | ` $title` | `$show_id`, `$atts`|
+| |`radio_station_upcoming_show_avatar_size` | ` $atts['avatar_size']` | `$show_id`|
+| |`radio_station_upcoming_show_avatar` | ` $show_avatar` | `$show_id`, `$atts`|
+| |`radio_station_upcoming_show_avatar_display` | ` $avatar` | `$show_id`, `$atts`|
+| |`radio_station_dj_link` | ` $host_link` | `$host`|
+| |`radio_station_upcoming_show_hosts_display` | ` $hosts` | `$show_id`, `$atts`|
+| |`radio_station_upcoming_show_encore_display` | ` $encore` | `$show_id`, `$atts`|
+| |`radio_station_upcoming_show_shifts_display` | ` $shift_display` | `$show_id`, `$atts`|
+| |`radio_station_upcoming_shows_custom_display` | ` ''` | `$show_id`, `$atts`|
+|**includes/support-functions.php**||||
+|*radio_station_get_shows*|`radio_station_get_shows` | ` $shows` | `$defaults`|
+|*radio_station_get_overrides*|`radio_station_show_day_shifts` | ` $day_shifts` | |
+|*radio_station_get_show_data*|`radio_station_get_overrides` | ` $override_list` | `$start_date`, `$end_date`|
+| |`radio_station_cached_data` | ` false` | `$datatype`, `$show_id`|
+|*radio_station_get_show_data_meta*|`radio_station_show_data_excerpt_length` | ` 55` | |
+| |`radio_station_show_data_excerpt_more` | ` ''` | |
+| |`radio_station_show_'.$datatype` | ` $results` | `$show_id`, `$args`|
+|*radio_station_get_show_description*|`radio_station_show_data_meta` | ` $show_data` | `$show_id`|
+| |`radio_station_show_data_excerpt_length` | ` 55` | |
+| |`radio_station_show_data_excerpt_more` | ` ''` | |
+| |`radio_station_show_data_description` | ` $description` | `$show_id`|
+| |`radio_station_show_data_excerpt` | ` $excerpt` | `$show_id`|
+| |`radio_station_override_data` | ` $override_data` | `$override_id`|
+| |`radio_station_linked_overrides` | ` $override_ids` | `$post_id`|
+| |`radio_station_linked_override_times` | ` $overrides` | `$post_id`|
+| |`radio_station_previous_show` | ` $prev_shift` | `$time`|
+| |`radio_station_previous_show` | ` $prev_shift` | `$time`|
+|*radio_station_get_current_show*|`radio_station_current_schedule` | ` $show_shifts` | `$time`|
+|*radio_station_get_previous_show*|`radio_station_previous_show` | ` $prev_shift` | `$time`|
+| |`radio_station_current_show` | ` $current_show` | `$time`|
+|*radio_station_get_current_playlist*|`radio_station_next_show` | ` $next_show` | `$time`|
+| |`radio_station_next_shows` | ` $next_shows` | `$limit`, `$show_shifts`|
+| |`radio_station_next_shows` | ` $next_shows` | `$limit`, `$show_shifts`|
+| |`radio_station_get_genres` | ` $genres` | `$args`|
+|*radio_station_get_language_shows*|`radio_station_show_genres_query_args` | ` $args` | `$genre`|
+| |`radio_station_show_languages_query_args` | ` $args` | `$language`|
+|*radio_station_update_show_avatar*|`radio_station_show_avatar_post_types` | ` $post_types` | |
+|*radio_station_get_show_avatar_url*|`radio_station_show_avatar_id` | ` $avatar_id` | `$show_id`|
+| |`radio_station_show_avatar_size` | ` $size` | |
+| |`radio_station_show_avatar_url` | ` $avatar_url` | `$show_id`, `$size`|
+| |`radio_station_show_avatar_size` | ` $size` | |
+| |`radio_station_show_avatar_output` | ` $avatar` | `$show_id`, `$size`|
+|*radio_station_get_stream_url*|`radio_station_stream_url` | ` $streaming_url` | |
+|*radio_station_get_stream_formats*|`radio_station_fallback_url` | ` $fallback_url` | |
+| |`radio_station_stream_formats` | ` $formats` | |
+|*radio_station_get_station_url*|`radio_station_station_url` | ` $station_url` | |
+|*radio_station_get_schedule_url*|`radio_station_station_image_url` | ` $station_image` | |
+| |`radio_station_schedule_url` | ` $schedule_url` | |
+| |`radio_station_api_url` | ` $api_url` | |
+|*radio_station_get_route_url*|`radio_station_route_slug_base` | ` 'radio'` | |
+| |`radio_station_route_slug_'.$route` | ` $route` | |
+|*radio_station_get_feed_url*|`radio_station_feed_slug_'.$feedname` | ` $feedname` | |
+| |`radio_station_host_url` | ` $host_url` | `$host_id`|
+|*radio_station_get_upgrade_url*|`radio_station_producer_url` | ` $producer_url` | `$producer_id`|
+|*radio_station_patreon_button_styles*|`radio_station_patreon_button` | ` $button` | `$page`|
+|*radio_station_get_weekday*|`radio_station_get_timezone_options` | ` $options` | `$include_wp_timezone`|
+|*radio_station_get_schedule_weekdays*|`radio_station_schedule_weekday_start` | ` $weekstart` | |
+|**includes/class-current-show-widget.php**||||
+|*update*|`radio_station_current_show_widget_fields` | ` $fields` | `$this`, `$instance`|
+|*widget*|`radio_station_current_show_widget_update` | ` $instance` | `$new_instance`, `$old_instance`|
+| |`radio_station_current_show_widget_atts` | ` $atts` | `$instance`|
+|**includes/class-upcoming-shows-widget.php**||||
+|*update*|`radio_station_upcoming_shows_widget_fields` | ` $fields` | `$this`, `$instance`|
+|*widget*|`radio_station_upcoming_shows_widget_update` | ` $instance` | `$new_instance`, `$old_instance`|
+| |`radio_station_upcoming_shows_widget_atts` | ` $atts` | `$instance`|
+|**includes/class-current-playlist-widget.php**||||
+|*update*|`radio_station_playlist_widget_fields` | ` $fields` | `$this`, `$instance`|
+|*widget*|`radio_station_playlist_widget_update` | ` $instance` | `$new_instance`, `$old_instance`|
+| |`radio_station_current_playlist_widget_atts` | ` $atts` | `$instance`|
+| |`radio_station_current_playlist_widget_override` | ` $output` | `$args`, `$atts`|
+|**includes/class-radio-clock-widget.php**||||
+| |`radio_station_clock_widget_atts` | ` $atts` | `$instance`|
+|**includes/class-radio-player-widget.php**||||
+|*form*|`radio_station_player_theme_options` | ` $options` | |
+| |`radio_station_player_button_options` | ` $options` | |
+|*update*|`radio_station_player_widget_fields` | ` $fields` | `$this`, `$instance`|
+|*widget*|`radio_station_player_widget_update` | ` $instance` | `$new_instance`, `$old_instance`|
+|**templates/master-schedule-table.php**||||
+| |`radio_station_schedule_start_time` | ` $start_time` | `'table'`, `$atts`|
+| |`radio_station_show_time_separator` | ` $shifts_separator` | `'schedule-table'`|
+| |`radio_station_time_separator` | ` $time_separator` | `'schedule-table'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'schedule-table'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'schedule-table'`, `$atts`|
+| |`radio_station_schedule_start_day` | ` false` | `'table'`|
+| |`radio_station_schedule_show_avatar_size` | ` 'thumbnail'` | `'table'`|
+| |`radio_station_schedule_table_excerpt_length` | ` false` | |
+| |`radio_station_schedule_table_excerpt_more` | ` '[…]'` | |
+| |`radio_station_schedule_arrows` | ` $arrows` | `'table'`|
+| |`radio_station_schedule_table_info_order` | ` $infokeys` | |
+| |`radio_station_schedule_loader_control` | ` ''` | `'table'`, `'left'`|
+| |`radio_station_schedule_loader_control` | ` ''` | `'table'`, `'right'`|
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_avatar_display` | ` $avatar` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_title_display` | ` $title` | `$show_id`, `'table'`|
+| |`radio_station_show_edit_link` | ` ''` | `$show_id`, `$shift['id']`, `'table'`|
+| |`radio_station_schedule_show_hosts` | ` $show_hosts` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_hosts_display` | ` $hosts` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_time` | ` $show_time` | `$show_id`, `'table'`, `$shift`, `$tcount`|
+| |`radio_station_schedule_show_time_display` | ` true` | `$show_id`, `'table'`, `$shift`|
+| |`radio_station_schedule_show_encore` | ` $show_encore` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_encore_display` | ` $encore` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_file_anchor` | ` $anchor` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_file_display` | ` $file` | `$show_file`, `$show_id`, `'table'`|
+| |`radio_station_schedule_show_excerpt` | ` $show_excerpt` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_excerpt_display` | ` $excerpy` | `$show_id`, `'table'`|
+| |`radio_station_schedule_show_custom_display` | ` ''` | `$show_id`, `'table'`|
+| |`radio_station_schedule_add_link` | ` ''` | `$times`, `'table'`|
+|**templates/master-schedule-tabs.php**||||
+| |`radio_station_schedule_start_time` | ` $start_time` | `'tabs'`|
+| |`radio_station_show_times_separator` | ` $shifts_separator` | `'schedule-tabs'`|
+| |`radio_station_time_separator` | ` $time_separator` | `'schedule-tabs'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'schedule-tabs'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'schedule-tabs'`, `$atts`|
+| |`radio_station_schedule_start_day` | ` false` | `'tabs'`|
+| |`radio_station_schedule_show_avatar_size` | ` 'thumbnail'` | `'tabs'`|
+| |`radio_station_schedule_tabs_excerpt_length` | ` false` | |
+| |`radio_station_schedule_tabs_excerpt_more` | ` '[…]'` | |
+| |`radio_station_schedule_arrows` | ` $arrows` | `'tabs'`|
+| |`radio_station_schedule_tabs_info_order` | ` $infokeys` | |
+| |`radio_station_schedule_loader_control` | ` ''` | `'tabs'`, `'left'`|
+| |`radio_station_schedule_tabs_avatar_position_start` | ` $avatar_position` | |
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_avatar_display` | ` $avatar` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_title_display` | ` $title` | `$show_id`, `'tabs'`|
+| |`radio_station_show_edit_link` | ` ''` | `$show_id`, `$shift['id']`, `'tabs'`|
+| |`radio_station_schedule_show_hosts` | ` $show_hosts` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_hosts_display` | ` $hosts` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_time` | ` $show_time` | `$show_id`, `'tabs'`, `$shift`, `$tcount`|
+| |`radio_station_schedule_show_times_display` | ` true` | `$show_id`, `'tabs'`, `$shift`|
+| |`radio_station_schedule_show_encore` | ` $show_encore` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_encore_display` | ` $encore` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_file_anchor` | ` $anchor` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_file_display` | ` $file` | `$show_file`, `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_genres` | ` $genres` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_custom_display` | ` ''` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_excerpt` | ` $show_excerpt` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_show_excerpt_display` | ` $excerpt` | `$show_id`, `'tabs'`|
+| |`radio_station_schedule_loader_control` | ` ''` | `'tabs'`, `'right'`|
+|**templates/master-schedule-legacy.php**||||
+| |`radio_station_schedule_show_avatar_size` | ` 'thumbnail'` | `'legacy'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show['id']`, `'legacy'`|
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show['id']`, `'legacy'`|
+| |`radio_station_schedule_show_time` | ` $times` | `$show['id']`, `'legacy'`, `false`, `false`|
+| |`radio_station_schedule_show_encore` | ` $encore` | `$show['id']`, `'legacy'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show['id']`, `'legacy'`|
+|**templates/master-schedule-list.php**||||
+| |`radio_station_show_times_separator` | ` $shifts_separator` | `'schedule-list'`|
+| |`radio_station_time_separator` | ` $time_separator` | `'schedule-list'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'schedule-list'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'schedule-list'`, `$atts`|
+| |`radio_station_schedule_start_day` | ` false` | `'list'`|
+| |`radio_station_schedule_show_avatar_size` | ` 'thumbnail'` | `'list'`|
+| |`radio_station_schedule_list_excerpt_length` | ` false` | |
+| |`radio_station_schedule_list_excerpt_more` | ` '[…]'` | |
+| |`radio_station_schedule_list_info_order` | ` $infokeys` | |
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_avatar_display` | ` $avatar` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_title` | ` $title` | `$show_id`, `'list'`|
+| |`radio_station_show_edit_link` | ` ''` | `$show_id`, `$shift['id']`, `'list'`|
+| |`radio_station_schedule_show_hosts` | ` $show_hosts` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_hosts_display` | ` $hosts` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_time` | ` $show_time` | `$show_id`, `'list'`, `$shift`, `$tcount`|
+| |`radio_station_schedule_show_time_display` | ` true` | `$show_id`, `'list'`, `$shift`|
+| |`radio_station_schedule_show_encore` | ` $show_encore` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_encore_display` | ` $encore` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_file_anchor` | ` $anchor` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_file_display` | ` $file` | `$show_file`, `$show_id`, `'list'`|
+| |`radio_station_schedule_show_genres_display` | ` $genres` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_excerpt` | ` $show_excerpt` | `$show_id`, `'list'`|
+| |`radio_station_schedule_show_custom_display` | ` ''` | `$show_id`, `'list'`|
+|**templates/single-playlist-content.php**||||
+| |`radio_station_link_playlist_to_show_before` | ` $before` | `$post`, `$show`|
+| |`radio_station_link_playlist_to_show_after` | ` $after` | `$post`, `$show`|
+|**templates/single-show-content.php**||||
+| |`radio_station_show_title` | ` $show_title` | `$post_id`|
+| |`radio_station_show_header` | ` $header_id` | `$post_id`|
+| |`radio_station_show_avatar` | ` $avatar_id` | `$post_id`|
+| |`radio_station_show_thumbnail` | ` $thumbnail_id` | `$post_id`|
+| |`radio_station_show_genres` | ` $genres` | `$post_id`|
+| |`radio_station_show_languages` | ` $languages` | `$post_id`|
+| |`radio_station_show_hosts` | ` $hosts` | `$post_id`|
+| |`radio_station_show_producers` | ` $producers` | `$post_id`|
+| |`radio_station_show_active` | ` $active` | `$post_id`|
+| |`radio_station_show_shifts` | ` $shifts` | `$post_id`|
+| |`radio_station_show_file` | ` $show_file` | `$post_id`|
+| |`radio_station_show_download` | ` $show_download` | `$post_id`|
+| |`radio_station_show_link` | ` $show_link` | `$post_id`|
+| |`radio_station_show_email` | ` $show_email` | `$post_id`|
+| |`radio_station_show_phone` | ` $show_phone` | `$post_id`|
+| |`radio_station_show_patreon` | ` $show_patreon` | `$post_id`|
+| |`radio_station_show_rss` | ` $show_rss` | `$post_id`|
+| |`radio_station_show_social_icons` | ` false` | `$post_id`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'show-template'`, `$post_id`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'show-template'`, `$post_id`|
+| |`radio_station_show_website_title` | ` $title` | `$post_id`|
+| |`radio_station_show_home_icon` | ` $icon` | `$post_id`|
+| |`radio_station_show_phone_title` | ` $title` | `$post_id`|
+| |`radio_station_show_phone_icon` | ` $icon` | `$post_id`|
+| |`radio_station_show_email_title` | ` $title` | `$post_id`|
+| |`radio_station_show_email_icon` | ` $icon` | `$post_id`|
+| |`radio_station_show_rss_title` | ` $title` | `$post_id`|
+| |`radio_station_show_rss_icon` | ` $icon` | `$post_id`|
+| |`radio_station_show_page_icons` | ` $show_icons` | `$post_id`|
+| |`radio_station_show_page_posts_limit` | ` false` | `$post_id`|
+| |`radio_station_show_page_playlist_limit` | ` false` | `$post_id`|
+| |`radio_station_show_jump_links` | ` 'yes'` | `$post_id`|
+| |`radio_station_show_avatar_size` | ` 'medium'` | `$post_id`, `'show-page'`|
+| |`radio_station_show_social_icons_display` | ` ''` | |
+| |`radio_station_show_patreon_title` | ` $title` | `$post_id`|
+| |`radio_station_show_patreon_button` | ` $patreon_button` | `$post_id`|
+| |`radio_station_show_player_label` | ` ''` | `$post_id`|
+| |`radio_station_show_download_title` | ` $title` | `$post_id`|
+| |`radio_station_show_images_blocks` | ` $image_blocks` | `$post_id`|
+| |`radio_station_show_image_block_order` | ` $image_block_order` | `$post_id`|
+| |`radio_station_show_info_label` | ` $label` | `$post_id`|
+| |`radio_station_show_hosts_label` | ` $label` | `$post_id`|
+| |`radio_station_show_producers_label` | ` $label` | `$post_id`|
+| |`radio_station_show_genres_label` | ` $label` | `$post_id`|
+| |`radio_station_show_languages_label` | ` $label` | `$post_id`|
+| |`radio_station_show_phone_label` | ` $label` | `$post_id`|
+| |`radio_station_show_meta_blocks` | ` $meta_blocks` | `$post_id`|
+| |`radio_station_show_meta_block_order` | ` $meta_block_order` | `$post_id`|
+| |`radio_station_show_times_label` | ` $label` | `$post_id`|
+| |`radio_station_show_no_shifts_label` | ` $label` | `$post_id`|
+| |`radio_station_show_timezone_label` | ` $label` | `$post_id`|
+| |`radio_station_show_times_separator` | ` $separator` | `'show-content'`|
+| |`radio_station_show_encore_label` | ` $label` | `$post_id`|
+| |`radio_station_override_date_format` | ` 'j F'` | |
+| |`radio_station_override_show_past_dates` | ` false` | |
+| |`radio_station_show_times_separator` | ` $separator` | `'override-content'`|
+| |`radio_station_show_schedule_link_title` | ` $title` | `$post_id`|
+| |`radio_station_show_schedule_link_anchor` | ` $label` | `$post_id`|
+| |`radio_station_show_page_blocks` | ` $blocks` | `$post_id`|
+| |`radio_station_show_more_label` | ` $label` | `$post_id`|
+| |`radio_station_show_less_label` | ` $label` | `$post_id`|
+| |`radio_station_show_description_label` | ` $label` | `$post_id`|
+| |`radio_station_show_description_anchor` | ` $anchor` | `$post_id`|
+| |`radio_station_show_posts_label` | ` $label` | `$post_id`|
+| |`radio_station_show_posts_anchor` | ` $posts_label` | `$post_id`|
+| |`radio_station_show_page_posts_shortcode` | ` $shortcode` | `$post_id`|
+| |`radio_station_show_playlists_label` | ` $label` | `$post_id`|
+| |`radio_station_show_playlists_anchor` | ` $playlist_label` | `$post_id`|
+| |`radio_station_show_page_playlists_shortcode` | ` $shortcode` | `$post_id`|
+| |`radio_station_show_page_sections` | ` $sections` | `$post_id`|
+| |`radio_station_show_header_size` | ` 'full'` | `$post_id`|
+| |`radio_station_show_page_header_image` | ` $header_image` | `$post_id`|
+| |`radio_station_show_page_block_order` | ` $block_order` | `$post_id`|
+| |`radio_station_show_latest_posts_label` | ` $label` | `$post_id`|
+| |`radio_station_show_page_latest_shortcode` | ` $shortcode` | `$post_id`|
+| |`radio_station_show_page_section_order` | ` $section_order` | `$post_id`|
+|**player/radio-player.php**||||
+|*radio_station_player_output*|`radio_station_player_output_args` | ` $args` | `$instance`|
+| |`radio_station_player_station_image_tag` | ` $image` | `$args['image']`, `$args`, `$instance`|
+|*radio_station_player_shortcode*|`radio_station_player_section_order` | ` $section_order` | `$args`|
+| |`radio_station_player_control_order` | ` $control_order` | `$args`, `$instance`|
+| |`radio_station_player_station_text_alt` | ` $station_text_alt` | `$args`, `$instance`|
+| |`radio_station_player_show_text_alt` | ` $show_text_alt` | `$args`, `$instance`|
+| |`radio_station_player_html` | ` $player` | `$args`, `$instance`|
+| |`radio_station_player_default_title_display` | ` $title` | |
+| |`radio_station_player_default_image_display` | ` $image` | |
+| |`radio_station_player_default_script` | ` $script` | |
+| |`radio_station_player_default_layout` | ` $layout` | |
+| |`radio_station_player_default_volume` | ` $volume` | |
+| |`radio_station_player_default_theme` | ` $theme` | |
+| |`radio_station_player_default_buttons` | ` $buttons` | |
+| |`radio_station_player_shortcode_attributes` | ` $atts` | |
+| |`radio_station_player_default_title` | ` ''` | |
+| |`radio_station_player_default_image` | ` ''` | |
+|*radio_station_player_ajax*|`radio_station_player_output` | ` $override` | `$atts`|
+| |`radio_station_player_atts` | ` $atts` | |
+| |`radio_station_player_mediaelements_interface` | ` $html` | `$atts`, `$post_id`|
+|*radio_station_player_enqueue_script*|`radio_station_player_pageload_script` | ` ''` | |
+| |`radio_station_player_scripts` | ` $js` | |
+| |`radio_station_player_fallbacks` | ` $fallbacks` | |
+|*radio_station_player_enqueue_mediaelements*|`radio_station_player_mediaelement_settings` | ` $player_settings` | |
+|*radio_station_player_script*|`radio_station_player_save_interval` | ` $save_interval` | |
+| |`radio_station_player_jplayer_swf_path` | ` ''` | |
+| |`radio_station_player_title` | ` $player_title` | |
+| |`radio_station_player_image` | ` $player_image` | |
+| |`radio_station_player_volume` | ` $player_volume ) )` | |
+| |`radio_station_player_single` | ` $player_single` | |
+| |`radio_station_player_fallbacks` | ` $fallbacks` | |
+| |`radio_station_player_debug` | ` $debug` | |
+|*radio_station_player_iframe*|`radio_station_player_data` | ` false` | `$station`|
+|*radio_station_player_script_howler*|`radio_station_player_script_amplitude` | ` $js` | |
+| |`radio_station_player_script_howler` | ` $js` | |
+| |`radio_station_player_script_jplayer` | ` $js` | |
+
+
+## [Pro] Pro Filter List
+
+Below is a list of filters that are available within [Radio Station Pro](https://radiostation.pro).
+
+| File / *Function* | Filter | Value | Extra Args |
+| - | - |
+|**radio-station-pro.php**||||
+| |`radio_station_editor_relogin_script` | ` $js` | `$type`|
+|*radio_station_pro_thickbox_loading_image*|`radio_station_thickbox_loading_icon_url` | ` $thickbox_loading_url` | |
+| |`radio_station_thickbox_styles` | ` $css` | |
+|*radio_station_pro_set_roles*|`radio_station_user_shows` | ` $shows` | `$type`, `$user_id`|
+|**includes/rsp-data-feeds.php**||||
+|*radio_station_pro_register_rest_routes*|`radio_station_route_slug_base` | ` 'radio'` | |
+| |`radio_station_route_slug_episodes` | ` false` | |
+| |`radio_station_route_slug_hosts` | ` 'hosts'` | |
+| |`radio_station_route_slug_producers` | ` 'producers'` | |
+|*radio_station_pro_route_episodes*|`radio_station_route_episodes` | ` $episode_list` | |
+| |`radio_station_feed_hosts` | ` $host_list` | |
+|*radio_station_pro_route_producers*|`radio_station_route_producers` | ` $producer_list` | |
+|**includes/rsp-episodes.php**||||
+|*radio_station_pro_register_taxonomies*|`radio_station_topic_taxonomy_args` | ` $args` | |
+|*radio_station_pro_set_data_slug*|`radio_station_guest_taxonomy_args` | ` $args` | |
+| |`radio_station_episode_url` | ` $episode_url` | `$episode_id`|
+|*radio_station_pro_get_show_episodes*|`radio_station_episode_avatar_output` | ` $avatar` | `$episode_id`|
+| |`radio_station_episode_avatar_id` | ` $avatar_id` | `$episode_id`|
+|*radio_station_pro_get_show_page_episodes*|`radio_station_show_page_episodes_limit` | ` false` | `$post_id`|
+|**includes/rsp-episodes-admin.php**||||
+|*radio_station_pro_add_episodes_submenu*|`radio_station_metabox_position` | ` 'rstop'` | `'profiles'`|
+|*radio_episode_type*|`radio_station_episode_edit_styles` | ` $css` | |
+| |`radio_station_update_segments` | ` false` | `$post_id`|
+|**includes/rsp-import-export.php**||||
+|*radio_station_create_show_image_archive*|`radio_station_valid_with_paragraph_tags` | ` true` | |
+|**includes/rsp-metadata.php**||||
+|*radio_station_pro_get_stream_metadata*|`radio_station_stream_metadata_types` | ` $data_types` | |
+|*radio_station_pro_broadcast_data*|`radio_station_stream_metadata` | ` $np` | `$stream`|
+| |`radio_station_stream_metadata_url` | ` $metadata` | `$broadcast`|
+| |`radio_station_metadata_cache_interval` | ` 5` | |
+| |`radio_station_current_song` | ` $currentsong` | |
+|*radio_station_pro_icy_stream_title*|`radio_station_icy_metadata_method` | ` $method` | |
+|*radio_station_pro_icy_song_info*|`radio_station_pro_metadata` | ` $metadata` | `$stream`, `'shoutcast1'`|
+|*radio_station_pro_shoutcast2_current_song*|`radio_station_pro_metadata` | ` $metadata` | `$stream`, `'shoutcast2'`|
+| |`radio_station_stream_mount_index` | ` $mount` | `$stream`|
+|**includes/rsp-player.php**||||
+|*radio_station_pro_player_scripts*|`radio_station_player_bar_metadata_cycle` | ` $metadata_cycle` | |
+| |`radio_station_pro_scripts` | ` $js` | |
+| |`radio_station_player_bar_atts` | ` $atts` | |
+|**includes/rsp-post-types.php**||||
+| |`radio_station_pro_post_type_episodes` | ` $episodes` | |
+|**includes/rsp-profiles.php**||||
+|*radio_station_pro_get_profile_posts*|`radio_station_'.$profile_type.'_'.$data_type` | ` $results` | `$author_id`, `$args`|
+| |`radio_station_show_avatar_output` | ` $avatar` | `$profile_id`, `$type`|
+| |`radio_station_profile_avatar_id` | ` $avatar_id` | `$profile_id`, `$type`|
+| |`radio_station_'.$type.'_hosts_label` | ` $label` | `$post_id`|
+| |`radio_station_'.$type.'_hosts_anchor` | ` $anchor` | `$post_id`|
+| |`radio_station_'.$type.'_page_hosts_shortcode` | ` $shortcode` | `$post_id`|
+| |`radio_station_'.$type.'_producers_label` | ` $label` | `$post_id`|
+| |`radio_station_'.$type.'_producers_anchor` | ` $anchor` | `$post_id`|
+| |`radio_station_'.$type.'_page_producers_shortcode` | ` $shortcode` | `$post_id`|
+| |`radio_station_'.$type.'_team_label` | ` $label` | `$post_id`|
+| |`radio_station_'.$type.'_team_anchor` | ` $anchor` | `$post_id`|
+|**includes/rsp-profiles-admin.php**||||
+|*radio_station_pro_add_profile_metabox*|`radio_station_metabox_position` | ` 'rstop'` | `'profiles'`|
+|*radio_station_pro_add_image_metaboxes*|`radio_station_profile_edit_styles` | ` $css` | |
+|**includes/rsp-schedule-editor.php**||||
+|*radio_station_pro_schedule_editor_menu*|`radio_station_pro_view_images` | ` false` | |
+| |`radio_station_pro_schedule_editor_atts` | ` $atts` | |
+|**includes/rsp-schedule-views.php**||||
+|*radio_station_pro_schedule_loader_control*|`radio_station_schedule_arrows` | ` $arrows` | `$view`|
+| |`master_schedule_grid_view` | ` $html` | `$atts`|
+| |`master_schedule_calendar_view` | ` $html` | `$atts`|
+|*radio_shift_grid*|`radio_station_pro_master_schedule_grid_js` | ` $js` | |
+|*radio_calendar_show_highlight*|`radio_station_pro_master_schedule_calendar_js` | ` $js` | |
+|*radio_slide_check*|`radio_station_show_slider_script` | ` $js` | |
+| |`radio_station_pro_view_order` | ` $view_order` | `$atts`|
+|*radio_switch_view*|`radio_station_pro_view_images` | ` false` | `$atts`|
+|**includes/rsp-shortcodes.php**||||
+|*radio_station_pro_archive_list_shortcode*|`radio_station_'.$type.'_archive_post_args` | ` $args` | |
+| |`radio_station_'.$type.'_archive_posts` | ` $archive_posts` | |
+| |`radio_station_time_separator` | ` $time_separator` | `$post_type.'-archive'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `$post_type.'-archive'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `$post_type.'-archive'`, `$atts`|
+| |`radio_station_archive_shortcode_no_records` | ` $message` | `$post_type`, `$atts`|
+| |`radio_station_archive_'.$type.'_list_excerpt_length` | ` false` | |
+| |`radio_station_archive_'.$type.'_list_excerpt_more` | ` '[…]'` | |
+| |`radio_station_archive_shortcode_info_order` | ` $infokeys` | `$post_type`, `$atts`|
+| |`radio_station_'.$type.'_archive_avatar_size` | ` 'thumbnail'` | `$post_id`, `$type.'-archive'`|
+|*radio_station_pro_profile_list_shortcode*|`radio_station_archive_shortcode_meta` | ` ''` | `$post_id`, `$post_type`, `$atts`|
+| |`radio_station_'.$type.'_archive_content` | ` $post_content` | `$post_id`|
+| |`radio_station_'.$type.'_archive_excerpt` | ` $excerpt` | `$post_id`|
+| |`radio_station_archive_shortcode_info_custom` | ` ''` | `$post_id`, `$post_type`, `$atts`|
+| |`radio_station_archive_shortcode_info` | ` $info` | `$post_id`, `$post_type`, `$atts`|
+| |`radio_station_'.$type.'_archive_list` | ` $list` | `$atts`|
+| |`radio_station_'.$profile_type.'_'.$type.'_list_excerpt_length` | ` false` | |
+| |`radio_station_'.$profile_type.'_'.$type.'_list_excerpt_more` | ` '[…]'` | |
+| |`radio_station_'.$profile_type.'_'.$type.'_content` | ` $post_content` | `$post_id`|
+| |`radio_station_'.$profile_type.'_'.$type.'_excerpt` | ` $excerpt` | `$post_id`|
+|**includes/rsp-social.php**||||
+|*radio_station_pro_get_social_icon*|`radio_station_social_icons_services` | ` $services` | |
+| |`radio_station_social_icon_url` | ` $icon_url` | `$service`|
+|*radio_station_pro_social_icons_inputs*|`radio_station_social_icon_dir` | ` get_stylesheet_directory() . '/images/'` | |
+| |`radio_station_social_icon_path` | ` get_stylesheet_directory_uri() . '/images/'` | |
+| |`radio_station_social_icon_output` | ` $html` | `$service`|
+|*radio_social_first_last*|`radio_station_pro_social_icon_script` | ` $js` | |
+|*radio_station_pro_social_icons_save*|`radio_station_social_icon_edit_styles` | ` $css` | |
+|**includes/rsp-timezones.php**||||
+|*radio_station_pro_timezone_resources*|`radio_station_timezone_switcher_styles` | ` $css` | |
+|**templates/master-schedule-grid.php**||||
+| |`radio_station_schedule_start_time` | ` $start_time` | `'grid'`|
+| |`radio_station_schedule_show_time_separator` | ` $shifts_separator` | `'schedule-grid'`|
+| |`radio_station_time_separator` | ` $time_separator` | `'schedule-grid'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'schedule-grid'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'schedule-grid'`, `$atts`|
+| |`radio_station_schedule_start_day` | ` false` | `'grid'`|
+| |`radio_station_schedule_show_avatar_size` | ` $avatar_size` | `'grid'`|
+| |`radio_station_schedule_arrows` | ` $arrows` | `'grid'`|
+| |`radio_station_schedule_grid_info_order` | ` $infokeys` | |
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_avatar_display` | ` $avatar` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_title_display` | ` $title` | `$show_id`, `'grid'`|
+| |`radio_station_show_edit_link` | ` ''` | `$show_id`, `$shift['id']`, `'grid'`|
+| |`radio_station_schedule_show_hosts` | ` $show_hosts` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_hosts_display` | ` $hosts` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_time` | ` $show_time` | `$show_id`, `'grid'`, `$shift`, `$tcount`|
+| |`radio_station_schedule_show_times_display` | ` true` | `$show_id`, `'grid'`, `$shift`|
+| |`radio_station_schedule_show_encore` | ` $show_encore` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_encore_display` | ` $encore` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_file_anchor` | ` $anchor` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_file_display` | ` $file` | `$show_file`, `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_genres` | ` $genres` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_show_custom_display` | ` ''` | `$show_id`, `'grid'`|
+| |`radio_station_schedule_loader_control` | ` ''` | `'grid'`, `'left'`|
+| |`radio_station_schedule_loader_control` | ` ''` | `'grid'`, `'right'`|
+| |`radio_station_master_schedule_styles_grid` | ` $css` | |
+|**templates/master-schedule-calendar.php**||||
+| |`radio_station_schedule_start_time` | ` $start_time` | `'calendar'`|
+| |`radio_station_schedule_show_time_separator` | ` $shifts_separator` | `'schedule-calendar'`|
+| |`radio_station_time_separator` | ` $time_separator` | `'schedule-calendar'`|
+| |`radio_station_time_format_start` | ` $start_data_format` | `'schedule-calendar'`, `$atts`|
+| |`radio_station_time_format_end` | ` $end_data_format` | `'schedule-calendar'`, `$atts`|
+| |`radio_station_schedule_start_day` | ` false` | `'calendar'`|
+| |`radio_station_schedule_show_avatar_size` | ` 'thumbnail'` | `'calendar'`|
+| |`radio_station_schedule_tabs_excerpt_length` | ` false` | |
+| |`radio_station_schedule_tabs_excerpt_more` | ` '[…]'` | |
+| |`radio_station_schedule_arrows` | ` $arrows` | `'calendar'`|
+| |`radio_station_schedule_calendar_info_order` | ` $infokeys` | |
+| |`radio_station_schedule_add_link` | ` ''` | `$times`, `'calendar'`|
+| |`radio_station_schedule_show_link` | ` $show_link` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_avatar` | ` $show_avatar` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_avatar_display` | ` $avatar` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_title_display` | ` $title` | `$show_id`, `'calendar'`|
+| |`radio_station_show_edit_link` | ` ''` | `$show_id`, `$shift['id']`, `'calendar'`|
+| |`radio_station_schedule_show_hosts` | ` $show_hosts` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_hosts_display` | ` $hosts` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_time` | ` $show_time` | `$show_id`, `'calendar'`, `$shift`, `$tcount`|
+| |`radio_station_schedule_show_times_display` | ` true` | `$show_id`, `'calendar'`, `$shift`|
+| |`radio_station_schedule_show_encore` | ` $show_encore` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_encore_display` | ` $encore` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_file` | ` $show_file` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_file_anchor` | ` $anchor` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_file_display` | ` $file` | `$show_file`, `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_genres` | ` $genres` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_custom_display` | ` ''` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_excerpt` | ` $show_excerpt` | `$show_id`, `'calendar'`|
+| |`radio_station_schedule_show_excerpt_display` | ` $excerpt` | `$show_id`, `'calendar'`|
+
+
+
+
diff --git a/docs/Manage.md b/docs/Manage.md
new file mode 100644
index 0000000..436296a
--- /dev/null
+++ b/docs/Manage.md
@@ -0,0 +1,36 @@
+Radio Station Plugin Management
+
+***
+
+## Admin Lists
+
+Shows, Overrides and Playlists each have their own standard list page via the WordPress admin area, which you can find via the Radio Station admin menu. Each list has relevant columns added so you can see more information about each record at a glance without having to Edit an individual page to see them, including Show Shifts, Show Override Dates, and Playlist Tracks. Also displayed are Show Hosts, Genres and Language terms, and the Show Avatar.
+
+## Shift Editing
+
+You can add Show Shifts from the Show's Edit page. You can click Add Shift as many times as you like to create new Shift entries. For a Shift to be valid it must have all time fields filled in. The Encore field (repeat airing) is optional. You can disable a Shift and it will be unused but still saved. Each Shift entry also has a Duplicate and Remove icon to copy that entry to a new Shift or remove it. Shifts are not altered until you save them via clicking Update for a Show.
+
+### [Pro] Visual Schedule Editor
+
+[Radio Station Pro](https://radiostation.pro) includes a Visual Schedule Editor to allow you to assign Shifts directly to the Schedule - whether on the Frontend or Backend! On the backend the Visual Schedule Editor is loaded at the top of the Shows and Overrides list pages. And on the Frontend it is loaded wherever the Schedule is displayed (via shortcode or automatic page setting.)
+
+Show Editors and Administrators can edit existing Shows and Shifts by clicking the edit pen icon next to any Show title on the Schedule, or on any shift time. This will popup a thickbox editing screen for that Show's shifts (or single shift time) where the shift time(s) can be modified and updated.
+
+Clicking the plus icon will load a popup thickbox screen allowing the adding of either a Show or an Override, with the times filled in based on which timeslot is clicked. Simply modify the time as desired and choose an existing Show or Override to add the timeslot to and Save. The new shift will be added and the window will revert to an edit screen for that Show/Override.
+
+
+## Shift Conflict Checking
+
+### Shift Save Checking
+
+Since 2.3.0, when Shifts are saved on the Show Edit page, they are checked against all other Show's (active) Shifts - and then against other Shifts for the same show - to detect for Shift Conflicts. If a conflict is found, that Shift is disabled and the specific conflict is displayed underneath the Shift after saving.
+
+Once the time is adjusted (or adjusted on the conflicting Show Shift and saved) so that there is no conflict, you can then uncheck the Disabled box for that Shift. If there are no further conflicts found, the Shift will then be active.
+
+### Existing Schedule Conflicts
+
+Since shift save checking was introduced in 2.3.0, you might already have existing schedule conflicts without realizing it. Radio Station attempts to handle existing conflicts in three ways:
+
+* An Admin Notice is generated displaying the Shift Conflicts, linked to their Shows.
+* Show Shift Conflicts are highlighted in red in the Shift column on the Admin Shows list page.
+* The Master Schedule display does it's best to check for conflicts and handle the overlaps.
\ No newline at end of file
diff --git a/docs/Options.md b/docs/Options.md
new file mode 100644
index 0000000..9abb413
--- /dev/null
+++ b/docs/Options.md
@@ -0,0 +1,384 @@
+# Radio Station Plugin Options
+
+***
+
+Plugin Settings are stored in an array under the `radio_station` key in the WordPress options table.
+
+Below is a list of plugin options available via the Plugin Settings Screen.
+
+
+### Plugin Setting Value Filters
+
+Note for custom flexibility, all Plugin Settings can also be filtered programmatically via their respective option key. Use `add_filter` to add a filter to `radio_station_{settings_key}`, then check your desired conditions to modify the value before returning it. eg:
+
+```
+add_filter( 'radio_station_station_phone', 'my_custom_station_phone' );
+function my_custom_station_phone( $number ) {
+ $current_hour = (int) date( 'G', time() );
+ if ( $current_hour > 20 ) {$number = '(123) 456 7890';}
+ return $number;
+}
+```
+
+The above example will change the display of the Station Phone number after 8pm (server time).
+
+
+## General
+
+### Broadcast
+
+#### Streaming URL
+Default: None. Key: streaming_format
+Enter the Streaming URL for your Radio Station. This will be discoverable via Data Feeds and used by default by the Radio Player.
+
+#### Stream Format
+Default: AAC/M4A. Key: streaming_format
+Select the format for your stream. This will be discoverable via Data Feeds and used by default by the Radio Player.
+
+#### Fallback URL
+Default: None. Key: fallback_url
+Enter the fallback Streaming URL for your Radio Station. This will be discoverable via Data Feeds and used by default by the Radio Player.
+
+#### Streaming URL
+Default: OGG. Key: fallback_format
+Select the format for your fallback stream. This will be discoverable via Data Feeds and used by default by the Radio Player.
+
+#### Main Broadcast Language
+Default: WordPress Language. Key: radio_language
+Select the main language used on your Radio Station.
+
+
+### Station
+
+#### Station Title
+Default: none. Key: station_title
+
+#### Station Image
+Default: none. Key: station_image
+
+#### Location Timezone
+Default: WordPress Timezone. Key: timezone_location
+Select your Broadcast Location for Radio Timezone display.
+
+#### Clock Time Format
+Default: 12 Hour Format. Key: clock_time_format
+Default Time Format for display output. Can be overridden in each shortcode or widget.
+
+#### Station Phone
+Default: none. Key: station_phone
+Default Phone Number to use for requests etc.
+
+#### Shows Phone
+Default: On. Key: shows_phone
+Use the Station Phone Number on Shows which do not have a phone number specified.
+
+#### Station Email
+Default: none. Key: station_email
+Default Email Address to use for requests etc.
+
+#### Shows Email
+Default: On. Key: shows_email
+Use the Station Email Address on Shows which do not have an email address specified.
+
+
+### Feeds
+
+#### Enable Data Routes
+Default: On. Key: enable_data_routes
+Enables Station Data Routes via WordPress REST API.
+
+#### Enable Data Feeds
+Default: On. Key: enable_data_feeds
+Enable Station Data Feeds via WordPress Feed links.
+
+#### Ping Netmix Directory
+Default: On. Key: ping_netmix_directory
+
+#### Clear Transients
+Default: Off. Key: clear_transients
+
+#### [Pro] Transient Caching
+Default: On. transient_caching
+Use Transient Caching to improve Schedule calculation performance.
+
+
+## Player
+
+### Basic Defaults
+
+#### Player Title
+Default: on. Key: player_title
+Display your Radio Station Title in Player by default.
+
+#### Player Image
+Default: on. Key: player_image
+Display your Radio Station Image in Player by default.
+
+#### Player Script
+Default: amplitude. Key: player_script
+Default audio script to use for Radio Streaming Player. Ampliture, Howler and Jplayer.
+
+#### Player Theme
+Default: light. Key: player_theme
+Default Player Controls theme style. Light or dark to match your theme.
+
+#### Player Buttons
+Default: rounded. Key: player_buttons
+Default Player Buttons shape style. Circular, rounded or square.
+
+#### Player Volume Controls
+Default: all. Key: player_volumes
+Which volume controls to display in the Player by default.
+
+#### Player Debug Mode
+Default: off. Key: player_debug
+Output player debug information in browser javascript console.
+
+### Player Colors
+
+#### [Pro] Playing Highlight Color
+Default: #70E070. Key: player_playing_color
+Default highlight color to use for Play button icon when playing.
+
+#### [Pro] Controls Highlight Color
+Default: #00A0E0. Key: player_buttons_color
+Default highlight color to use for player Control button icons when active.
+
+#### [Pro] Volume Knob Color
+Default: #80C080. Key: player_thumb_color
+Default Knob Color for Player Volume Slider.
+
+#### [Pro] Volume Track Color
+Default: #80C080. Key: player_range_color
+Default Track Color for Player Volume Slider.
+
+### Advanced Defaults
+
+#### Player Start Volume
+Default: 77. Key: player_volume
+Initial volume for when the Player starts playback. 0-100
+
+#### Single Player
+Default: on. Key: player_single
+Stop any existing Player instances on the page or in other windows or tabs when a Player is started.
+
+#### [Pro] Player Autoresume
+Default: on. Key: player_autoresume
+Attempt to resume playback if visitor was playing. Only triggered when the user first interacts with the page.
+
+#### [Pro] Player Popup
+Default: off. Key: player_popup
+Add button to open Popup Player in separate window.
+
+### Player Bar
+
+#### [Pro] Sitewide Player Bar
+Default: off. Key: player_bar
+Add a fixed position Player Bar which displays Sitewide. Fixed top or bottom position.
+
+#### [Pro] Fade In Player Bar
+Default: 2500. Key: player_bar_fadein
+Number of milliseconds after Page load over which to fade in Player Bar. Use 0 for instant display
+
+#### [Pro] Continuous Playback
+Default: on. Key: player_bar_continuous
+Uninterrupted Sitewide Bar playback while user is navigating between pages! Pages are loaded in background and faded in while Player Bar persists.
+
+#### [Pro] Player Page Fade
+Default: 2000. Key: player_bar_pagefade
+Number of milliseconds over which to fade in new Pages when continuous playback is enabled. Use 0 for instant display.
+
+#### [Pro] Bar Player Text Color
+Default: #FFFFFF. Key: player_bar_text
+Text color for the fixed position Sitewide Bar Player.
+
+#### [Pro] Bar Player Background Color
+Default: black. Key: player_bar_background
+Background color for the fixed position Sitewide Bar Player.
+
+#### [Pro] Bar Player Current Show
+Default: on. Key: player_bar_currentshow
+Display the Current Show in the Player Bar.
+
+#### [Pro] Bar Player Now Playing
+Default: on. Key: player_bar_nowplaying
+Display the Now Playing Track metadata in the Player Bar.
+
+#### [Pro] Bar Player Track Animation
+Default: backandforth. Key: player_bar_track_animation
+How to animate the currently playing track display.
+
+#### [Pro] Bar Player Metadata Source
+Default: none (use Stream URL.) Key: player_bar_metadata
+Alternative metadata source URL for Now Playing Track metadata.
+
+
+## Pages
+
+### Master Schedule
+
+#### Master Schedule Page
+Default: None. Key: schedule_page
+Select the Page you are displaying the Master Schedule on.
+
+#### Automatic Schedule Display
+Default: On. Key: schedule_auto
+Replaces selected page content with Master Schedule.
+Alternatively customize with the shortcode: `[master-schedule]`
+See [Master Schedule Shortcode](./Shortcodes.md#master-schedule) for more info.
+
+#### Schedule View Default
+Default: Table. Key: schedule_view
+View type to use for automatic display on Master Schedule Page.
+
+#### Radio Clock
+Default: On. Key: schedule_clock
+Whether to enable the display of the Radio/User Times clock on the automatic Master Schedule Page.
+
+#### [Pro] Schedule View Switcher
+Default: On. Key: schedule_switcher
+Enable View Switching on the automatic Master Schedule Page.
+
+#### [Pro] Available Views
+Default: table, tabs. Key: schedule_views
+Which Views to enable for switching on the automatic Master Schedule Page.
+
+
+### Show Pages
+
+#### Show Info Blocks Position
+Default: Left. Key: show_block_position
+Where to position Show info blocks relative to Show Page content.
+
+#### Show Content Layout
+Default: Tabbed. Key: show_section_layout
+How to display extra sections below Show description. In content tabs or standard layout down the page.
+
+#### Show Content Header Image
+Default: Off. Key: show_header_image
+If your chosen template does not display the Featured Image, enable this and use the Content Header Image box on the Show edit screen instead.
+
+#### Show Posts Per Page
+Default: 10. Key: show_posts_per_page
+Linked Show Posts per page on the Show Page tab/display.
+
+#### Show Playlists per Page
+Default: 10. Key: show_playlists_per_page
+Playlists per page on the Show Page tab/display.
+
+#### [Pro] Show Episodes per Page
+Default: 10. Key: show_episodes_per_page
+Number of Show Episodes per page on the Show page tab/display.
+
+
+### Archives
+
+#### Show Archives Page
+Default: None. Key: show_archive_page
+Select the Page for displaying the Show archive list.
+
+#### Show Archives Automatic Display
+Default: On. Key: show_archive_auto
+Replaces selected page content with default Show Archive.
+Alternatively customize display using the shortcode: `[shows-archive]`
+See [Show Archives Shortcode](./Shortcodes.md#show-archives-shortcode) for more info.
+
+#### Override Archives Page
+Default: None. Key: override_archive_page
+Select the Page for displaying the Override archive list.
+
+#### Override Archives Automatic Display
+Default: On. Key: override_archive_auto
+Replaces selected page content with default Override Archive.
+Alternatively customize display using the shortcode: `[overrides-archive]`
+See [Show Archives Shortcode](./Shortcodes.md#overrides-archives-shortcode) for more info.
+
+#### Playlist Archives Page
+Default: None. Key: playlist_archive_page
+Select the Page for displaying the Playlist archive list.
+
+#### Playlist Archives Automatic Display
+Default: On. Key: playlist_archive_auto
+Replaces selected page content with default Playlist Archive.
+Alternatively customize display using the shortcode: `[playlists-archive]`
+See [Playlist Archives Shortcode](./Shortcodes.md#playlist-archives-shortcode) for more info.
+
+#### [Pro] Team Archives Page
+Default: None. Key: team_archive_page
+Replaces selected page content with default Team Archive (Hosts and Producers.)
+Alternatively customize display using the shortcode: `[team-archive]`
+
+#### Genre Archives Page
+Default: None. Key: genre_archive_page
+Select the Page for displaying the Genre archive list.
+
+#### Genre Archives Automatic Display
+Default: On. Key: genre_archive_auto
+Replaces selected page content with default Genre Archive.
+Alternatively customize display using the shortcode: `[genres-archive]`
+See [Genre Archives Shortcode](./Shortcodes.md#genre-archives-shortcode) for more info.
+
+
+## Templates
+
+Since 2.3.0, the way that Templates are implemented has changed.
+See [Templates](./Display.md#page-templates) for more info.
+
+### Single Templates
+
+#### Show Template
+Default: page.php. Key: show_template
+Which template to use for displaying Show and Override content.
+
+#### Combines Show Template Method
+Default: Off. Key: show_template_combined
+For advanced usage. Use both a custom template AND content filtering for a Show. (Not compatible with Legacy templates.)
+
+#### Playlist Template
+Default: page.php. Key: playlist_template
+Which template to use for displaying Playlist content.
+
+#### Combined Playlist Template Method
+Default: Off. Key: playlist_template_combined
+For advanced usage. Use both a custom template AND content filtering for a Playlist. (Not compatible with Legacy templates.)
+
+## Widgets
+
+#### AJAX Loading
+Default: On. Key: ajax_widgets
+Whether to load Widget contents via AJAX by default. This prevents stale cached displays of Current/Upcoming Shows etc. Note this can be changed on a per widget basis.
+
+#### [Pro] Dynamic Reloading
+Default: On. Key: dynamic_reload
+Whether to reload Widgets automatically on change of Current Show. Can also be set on a per widget basis.
+
+#### [Pro] Convert Show Times
+Default: On. Key: convert_show_times
+Automatically display Show times converted into the visitor timezone, based on their browser setting.
+
+#### [Pro] User Timezone Switching
+Default: On. Key: timezone_switching
+Allow visitors to select their Timezone manually for Show time conversions.
+
+
+## Roles
+
+Since 2.3.0, a new Show Editor role has been added with Publish and Edit capabilities for all Radio Station Post Types. You can assign this Role to any user to give them complete Station Schedule and Radio Station Post Type updating permissions without giving them a full WordPress administrator role.
+See [Roles](./Roles.md#show-editor-role] for more info.
+
+### Permissions
+
+#### Add to Author Role Capabilities
+Default: On. Key: add_author_capabilities
+Allow users with the WordPress Author role to publish and edit their own Shows and Playlists.
+
+#### Add to Editor Role Capabilities
+Default: On. Key: add_editor_capabilities
+Allow users with the WordPress Editor role to edit all Radio Station post types.
+
+
+### Role Editing
+
+#### [Pro] Role Editor Interface
+Allows you to assign any of the Radio Station plugin Roles directly to any user. For more information see [Roles Documentation](./Roles.md#role-editing)
+
diff --git a/docs/Player.md b/docs/Player.md
new file mode 100644
index 0000000..77a6f12
--- /dev/null
+++ b/docs/Player.md
@@ -0,0 +1,158 @@
+# Radio Station Stream Player
+
+***
+
+
+## Stream Player
+
+The Stream Player is available as a Shortcode, Widget or Block. In Pro it is also available as a Sitewide Bar Player, as well as an Elementor Widget and Beaver Builder Module.
+
+#### Default Player Settings
+
+Default settings for the Player can be set on the Plugin Settings page on the Player tab. These will be used in widgets wherever the widget options are set to "Default". This saves you from setting them twice, but also means that you can override these defaults in individual widgets as needed. (see [Options](./Options.md#player) for a list of these options.)
+
+* *Player Title*: Display your Radio Station Title in Player by default.
+* *Player Image*: Display your Radio Station Image in Player by default.
+* *Script*: Default audio script to use for Radio Streaming Player. Ampliture, Howler or Jplayer.
+* *Fallback Scripts*: Fallback audio scripts to try if/when the default Player script fails.
+* *Theme*: Default Player Controls theme style. Light or dark to match your theme.
+* *Buttons*: Default Player Buttons shape style. Circular, rounded or square.
+* *Volume Controls*: Which volume controls to display in the Player by default.
+* *Player Debug Mode*: Output player debug information in browser javascript console.
+* *Start Volume*: Initial volume for when the Player starts playback.
+* *Single Player*: Stop other Player instances when a Player is started.
+
+Please note you will not see volume controls when using an iOS device as they are not supported.
+
+### Stream Player Shortcode
+
+#### Shortcode Attribute Options
+
+`[radio-player]` or `[stream-player]`
+
+The following attributes are available for this shortcode:
+
+* *url* : Stream or file URL. Default: Plugin setting.
+* *script* : Default audio script. 'amplitude', 'jplayer', or 'howler'. Default: amplitude
+* *layout* : Player section layout. 'horizontal', 'vertical'. Default: 'vertical'
+* *theme* : Player Buttom theme. 'light' or 'dark'. Default: 'light'.
+* *buttons* : Control buttons shape. 'circular', 'rounded' or 'square'. Default 'rounded'.
+* *title* : Player/Station Title. String. 0 for none
+* *image* : Player/Station Image. URL (recommended size 256x256). Default none.
+* *volume* : Initial Player Volume. 0-100. Default: 77
+* *volumes* : Volume Control Buttons, comma separated. (slider,updown,mute,max) Default: all.
+* *default* : Use this as the default Player on the page. 0 or 1. Default 0.
+
+Example Shortcode: `[radio-player url="http://example.com/my-stream-url/"]`
+
+[Demo Site Example Output](https://demo.radiostation.pro/player-shortcode/)
+
+#### Using the Shortcode in Templates
+
+Remember, if you want to display a Shortcode within a custom Template (or elsewhere in custom code), you can use the WordPress `do_shortcode` function. eg. `do_shortcode('[stream-player]');`
+
+#### [Pro] Extra Widget and Shortcode Options
+
+All additional options available in Pro are also available for individual Player widgets and shortcodes (including Gutenberg Block, Elementor Widget and Beaver Builder Modules.)
+
+* **Color Options**
+* *text_color* : Player Text Color. Defaults to none (inherit.)
+* *background_color* : Player Background Color. Defaults to none (inherit.)
+* *playing_color* : Playing Highlight Color. Defaults to Plugin Setting.
+* *buttons_color* : Buttons Highlight Color. Defaults to Plugin Setting.
+* *track_color* : Volume Track Color. Defaults to Plugin Setting.
+* *thumb_color* : Volume Thumb Color. Defaults to Plugin Setting.
+* **Extra Options**
+* *popup* : Add button to open Popup Player in separate window. 0 or 1. Defaults to Plugin Setting.
+* *currentshow* : Display current Show information in player section. 0 or 1. Defaults to Plugin Setting.
+* *nowplaying* : Display currently playing track via stream metadata/playlist. 0 or 1. Defaults to Plugin Setting.
+* *animation* : Track animation to use. none, lefttoright, righttoleft, backandforth. Default: backandforth
+
+### Stream Player Widget
+
+A Player widget instance can be added to any widget area via the WordPress Admin Appearance -> Widgets page. The widget options correspond to the shortcode attributes above, allowing you control over the widget display output.
+
+### Stream Player Block
+
+A Player block can be added via the Block Editor by clicking the blue + icon. The `[Radio Station] Stream Player` Block can be found in the Radio Station block category (above the Embed category.)
+
+#### [Pro] Extra Block Options
+
+The Radio Player Block supports extra theming and color options matching to the extra shortcode/widget options above.
+
+
+### [Pro] Sitewide Bar Player
+
+[Radio Station Pro](https://radiostation.pro) includes a Sitewide Bar Streaming Player, including continuous uninterruptable playback via the integrated Teleporter page transition plugin. The Player Bar isn't added via the Widgets page, but is instead configured via the Plugin Settings page under the Player tab. It has the following main options:
+
+* *Bar Position*: Fixed in the header or footer area (top or bottom of page, unaffected by scrolling.)
+* *Bar Height*: Set the absolute height of the Player Bar in pixels.
+* *Fade In Player Bar*: How long to take to fade in Player Bar after page load.
+* *Continous Playback*: Enables uninterrupt playback while navigating between pages.
+* *Player Page Fade*: How long to take to fade between pages when continous playback is enabled.
+* *Text Color and Background Color*: Bar Player Default text and background colors.
+* *Autoresume Playback*: attempts to autoresume playback for returning visitors.
+* *Popup Player Button*: adds a button to open the player in a separate window.
+* *Current Show Display*: whether to display the Current Show in the Player Bar.
+* *Now Playing Display*: whether to display current track information via stream metadata.
+* *Metadata URL*: alternative URL for metadata retrieval (normally via stream URL.)
+
+#### [Pro] Responsive Bar Display
+
+
+
+
+#### [Pro] Continous Player Integration
+
+Using our purpose-built Teleporter plugin, page transitions enable the audio player instance to remain open in the current window while navigating your website. Please note this means this feature is NOT compatible with other page transition scripts. One notable example, you will need to turn off Elementor page transition animations as these cause a conflict (by setting both before and after animations to None in your Elementor page transition settings.)
+
+Developers should note that the continous player page transitions are implemented by adding click event listeners to `a` link tags. This means that dynamic links - those that do not exist on page load but are added in content later - will need special treatment to preserve continuous playback. Some examples of these include some mobile menus or breadcrumbs that are created by javascript dynamically on hover, and AJAX loaded "more" or filtered content.
+
+The solution to integrated these dynamic links is to use a filter to allow Teleporter (the page transition plugin) target the classes of the dynamic links. Knowing these claseses to target, Teleporter will then handle them via click event bubbling. Here's an example of how that can be done. (Note the format is just a comma separated list of classes without a `.` prefix:)
+
+```
+add_filter( 'teleporter_dynamic_link_classes', 'my_custom_dynamic_link_classes' );
+function my_custom_dynamic_link_classes( $classes ) {
+ $classes = 'mobile-menu,elementor-item';
+ return $classes;
+}
+```
+
+Similarly, if there are links that you wish to force to not transition for some reason, you can use the `teleporter_ignore_link_classes` filter in the same way. If you need to use selectors other than classes, you can use the filters `teleporter_dynamic_selectors` and `teleporter_ignore_selectors` to add those respectively also.
+
+#### [Pro] Smooth Page Transitions
+
+Page transitions are implemented by including the Teleporter plugin internally. If you want to access further controls or settings for page transitions you can install the Teleporter plugin separately from the WordPress repository. You can set things like page transition time, page loading bar color as well as dynamic and ignore classes via the Teleporter settings screen. These can also be changed via CSS and filters without needing the plugin, it simply provides an easy interface for changing these settings.
+
+#### [Pro] Extra Bar Color Options
+
+The Player Bar also includes some extra color options to match the look and feel of your site (these are also used as defaults for other Player instances.)
+
+* *Playing Icon Highlight Color*: Play button highlight color when playing stream.
+* *Control Icons Highlight Color*: Volume Button Hover highlight and player loading highlight.
+* *Volume Track and Knob Colors*: To style the volume slider track and it's thumb knob.
+
+And in addition to the existing Light and Dark button themes, you can also choose from colored button themes of: Red, Orange, Yellow, Light Green, Green, Cyan, Light Blue, Blue, Purple or Magenta. Matching Play/Pause and Volume/Track control images will be used when you activate these theme options.
+
+#### [Pro] Playback Autoresume
+
+
+#### [Pro] Popup Player Window
+
+
+#### [Pro] Track Metadata
+
+
+
+
+
+
+
+
+### [Pro] Beaver Builder Module
+
+The Radio Player Beaver Builder Module can be found in the Radio Station category section of the Modules tab (ensure "Standard Modules" is selected in the dropdown.) Hover any module on the page and click the Settings spanner icon to customize the module's settings as desired. Each module has color and typography options in the Style tab. Changing any of the settings will reload the module preview dynamically.
+
+### [Pro] Elementor Widget
+
+The Radio Player Elementor Widget can be found in the Radio Station category section of the Elements Tab in Elementor. Drag a Radio Station widget element to your content area as normal. Click on any element to customize the Settings in the left pane as desired. Each element widget has color and typography options in the Style tab. Changing any of the settings will reload the element preview dynamically.
diff --git a/docs/Pro.md b/docs/Pro.md
new file mode 100644
index 0000000..660c13a
--- /dev/null
+++ b/docs/Pro.md
@@ -0,0 +1,115 @@
+# Radio Station Pro Documentation
+
+***
+
+Pro Feature Documentation is included where relevant within the existing Free version documentation, marked with `[Pro]`.
+
+But in order to find the Pro feature documentation more easily, an index is provided here linking to each of those sections.
+
+Note that additional Pro Documentation will be added in the relevant section as new Features are released into the Pro version.
+
+If you are interested in testing cutting edge features in development for this next release, read about [Installing the Development Version](./FAQ.md#how-do-i-install-the-latest-development-version-for-testing) and [Pro Beta Version Testing](./#pro-beta-version-testing).
+
+
+### Radio Station Plus
+
+Please note that the following features are *not* available in Radio Station Plus:
+
+- Persistent Stream Player Bar (and related features)
+- Visual Schedule Editor Interface
+- Episodes and Segments (and related data/features)
+- Page Builder Modules (for Elementor and Beaver Builder)
+
+
+### PRO Plugin Features
+
+#### API
+
+[Episodes Data Endpoint](./API.md#pro-episodes-endpoint)
+
+[Hosts Data Endpoint](./API.md#pro-hosts-endpoint)
+
+[Producers Data Endpoint](./API.md#pro-producers-endpoint)
+
+#### Data
+
+[Show Episodes](./Data.md#pro-show-episodes)
+
+[Host and Producer Profiles](./Data.md#pro-host-and-producer-profiles)
+
+#### Display
+
+[Profile Images](./Display.md#pro-profile-images)
+
+[Genre Images and Colors](./Display.md#genre-images-and-colors)
+
+#### Filters
+
+[Pro Filters List](./Filters.md#pro-pro-filter-list)
+
+#### Manage
+
+[Visual Shift Editor](./Manage.md#pro-visual-shift-editor)
+
+#### Options
+
+[Pro Options](./Options.md) (marked throughout options list)
+
+#### Roles
+
+[Role Editor Interface](./Roles.md#pro-role-editor-interface)
+
+#### Shortcodes
+
+[Schedule Grid View](./Shortcodes.md#master-schedule-shortcode)
+
+[Schedule Calendar View](./Shortcodes.md#master-schedule-shortcode)
+
+[Multiple View Switching](./Shortcodes.md#pro-multiple-view-switching)
+
+[Timezone Switching](./Shortcodes.md#pro-user-timezone-switching)
+
+[Hosts Archive Shortcode](./Shortcodes.md#pro-hosts-archive-shortcode)
+
+[Producers Archive Shortcode](./Shortcodes.md#pro-producers-archive-shortcode)
+
+[Team Archive Shortcode](./Shortcodes.md#pro-team-archive-shortcode)
+
+[Show Episodes Archive Shortcode](./Shortcodes.md#pro-show-episodes-archive-shortcode)
+
+#### Widgets
+
+[Timezone Switcher](./Widgets.md#pro-timezone-switcher)
+
+[Extra Block Options](./Widgets.md#pro-extra-block-options)
+
+[Elementor Widgets](./Widgets.md#pro-elementor-widgets)
+
+[Beaver Builder Modules]('./Widgets.md#pro-beaver-builder-modules)
+
+
+### Pro Player Features
+
+[Extra Widget and Shortcode Options](./Player.md#pro-extra-widget-and-shortcode-options)
+
+[Extra Block Options](./Player.md#pro-extra-block-options)
+
+[Sitewide Bar Player](./Player.md#pro-sitewide-bar-player)
+
+[Responsive Bar Display](./Player.md#pro-responsive-bar-display)
+
+[Continous Player Integration](./Player.md#pro-continuous-player-integration)
+
+[Smooth Page Transitions](./Player.md#pro-smooth-page-transitions)
+
+[Extra Bar Color Options](./Player.md#pro-extra-bar-color-options)
+
+[Playback AutoResume](./Player.md#pro-playback-autoresume)
+
+[Popup Player](./Player.md#pro-popup-player)
+
+[Track Metadata](./Player.md#pro-track-metadata)
+
+[Elementor Widget](./Player.md#pro-elementor-widget)
+
+[Beaver Builder Module]('./Player.md#pro-beaver-builder-module)
diff --git a/docs/Roadmap.md b/docs/Roadmap.md
new file mode 100644
index 0000000..b18b10d
--- /dev/null
+++ b/docs/Roadmap.md
@@ -0,0 +1,51 @@
+# Radio Station Plugin Roadmap
+
+***
+
+Since taking over the development of the free Radio Station plugin in June 2019, we've made massive improvements to every aspect of the project. Two years later, we released the Pro version and have continued to update and improve both versions, adding innovative features as we go.
+
+The below table is a simplified overview to help keep track of all the major different features in Free and Pro, both those that we've added already and planned upcoming features. (If you want to contribute ideas or suggestions, please do so by opening a [Github Issue](https://github.com/netmix/radio-station/).)
+
+If you are interested in testing cutting edge features in development for this next release, read about [Installing the Development Version](./FAQ.md#how-do-i-install-the-latest-development-version-for-testing) and [Pro Beta Version Testing](./#pro-beta-version-testing).
+
+For minor release notes, see the [Radio Station Changelog](./CHANGELOG.md)
+(Note: The Radio Station Pro changelog can be found in the plugin's `readme.txt`)
+
+Normal = Feature Completed
+
+*italics* = Upcoming Feature
+
+**bold** = Next Major Feature(s)
+
+
+| Free | Version | Pro | Version |
+| --- | --- | --- | --- |
+| SCHEDULE |
+| Scheduler with Conflict Checker | 2.3.0 | with Visual Schedule Editor | 2.3.5 |
+| Table, Tab and List Schedule Views | 2.2.8 | with Grid, Calendar and View Switching | 2.3.4 |
+| Weekly Show Shifts and Date Overrides | 2.2.8 | with Recurring Periodic Overrides | 2.6.0 |
+| CONTENT |
+| Show Posts and Linked Overrides | 2.2.8 | with Show Episodes Post Type | 2.4.1 |
+| Playlist with Track Editor | 2.2.8 | **Show Episode Segment Editor** | |
+| Archive Shortcodes with Automatic Pages | 2.3.0 | with Episode and Team Archives | 2.4.1.8 |
+| Show Page Display Template | 2.3.0 | with Social Icons | 2.3.5 |
+| Show Hosts and Producers | 2.2.8 | with Profile Page Templates | 2.4.1 |
+| PLAYER |
+| Streaming Player Widget/Shortcode | 2.3.4 | with Sitewide Persistant Player Bar | 2.4.1 |
+| Player Theme and Button Options | 2.3.4 | with Extra Skin and Color Options | 2.6.0 |
+| *File Playing Support and Buttons* | | *with Playlist Track Support* | |
+| WIDGETS/SHORTCODES |
+| Radio Blocks (Gutenberg Block Editor) | 2.5.0 | Elementor and Beaver Builder Modules | 2.6.0 |
+| Show Widget/Shortcodes with Countdowns | 2.2.8 | with Dynamic Reloading | 2.4.1 |
+| Current Playlist Widget/Shortcode | 2.2.8 | Player Track Metadata Display | 2.4.1.3 |
+| Radio Clock Widget/Shortcode | 2.3.2 | with User Timezone Switcher | 2.4.1 |
+| EXTRAS |
+| Host / Producer / Show Editor Roles | 2.3.0 | with Role Assignment Interface | 2.3.0 |
+| REST/Feed API Endpoints | 2.3.0 | with Episodes, Hosts and Producers | 2.4.1 |
+| Standard HTML Markup Output | 2.3.0 | *with SEO Schema Markup* | |
+| Show Genre Assignments | 2.2.8 | Episode Guests and Topics | 2.4.1 |
+
+
+[comment]: # (Show and Override Feeds)
+
+Interested in going Pro? For a full feature list comparison, [click here to find out more about Radio Station Pro](https://radiostation.pro/pricing/).
diff --git a/docs/Roles.md b/docs/Roles.md
new file mode 100644
index 0000000..72c4ec0
--- /dev/null
+++ b/docs/Roles.md
@@ -0,0 +1,43 @@
+# Radio Station Plugin Roles
+
+***
+
+Note that while the default interface in WordPress allows you to assign a single role to a user, it also supports multiple roles, but you need to add a plugin to get an interface for this. Radio Station Pro will include a Role Editing Interface for assigning plugin roles to users directly, and this will be available on the Plugin Settings page.
+
+For the free version, you can also use the [Multiple Roles Plugin](https://wordpress.org/plugins/multiple-roles/) to assign roles from a User page. If you want even more fine-grained control over specific user capabilities and roles, then you can use the [User Role Editor Plugin](https://wordpress.org/plugins/user-role-editor/) or Justin Tadlock's excellent [Members](http://wordpress.org/extend/plugins/members/) plugin.
+
+### Plugin Roles
+
+#### Show Host Role
+
+Previously labelled DJ role, users with this Role can be assigned to a Show and are displayed as Show Hosts on the Show page, as well as in other widgets and shortcodes if the option to display them is set. Hosts assigned to a Show are then able to Edit that Show as well.
+
+#### Show Producer Role
+
+Similar to the Show Host role, users with this Role can be assigned to a Show and are displayed on the Show page, as well as in other widgets and shortcodes if the option to display them is set. Producers assigned to a Show are then able to Edit that Show as well.
+
+#### Show Editor Role
+
+Since 2.3.0, a new Show Editor role has been added with Publish and Edit capabilities for all Radio Station Post Types. You can assign this Role to any user to give them full Station Schedule updating permissions, without them needing to be a site Administrator.
+
+
+### WordPress Role Capabilities
+
+#### Author Role Capabilities
+
+There is a Plugin Setting which if enabled, allows users with the in-built WordPress Author role to publish and edit their own Shows and Playlists.
+
+#### Editor Role Capabilities
+
+There is a Plugin Setting which if enabled, allows users with the in-built WordPress Editor role to edit all Radio Station post types.
+
+#### Administrator Role Capabilities
+
+Users with Administrator role are automatically given all permissions to edit all Radio Station post types.
+
+
+### Role Editing
+
+#### [Pro] Role Editor Interface
+
+[Radio Station Pro](https://radiostation.pro) includes a Role Editor Interface that is accessible directly from the Plugin Settings page under the Roles tab. This allows you to assign any of the Radio Station plugin Roles easily to any existing user.
diff --git a/docs/Shortcodes.md b/docs/Shortcodes.md
new file mode 100644
index 0000000..4287ffa
--- /dev/null
+++ b/docs/Shortcodes.md
@@ -0,0 +1,368 @@
+# Radio Station Plugin Shortcodes
+
+***
+
+Note if you want to display a Shortcode within a custom Template, you can use the WordPress `do_shortcode` function. eg. `do_shortcode('[master-schedule]');`
+
+Shortcode Output Examples can be seen on the [Radio Station Demo Site](http://demo.radiostation.pro)
+
+
+## Master Schedule
+
+### Master Schedule Shortcode
+
+Use the shortcode `[radio-schedule]` or `[master-schedule]` on any page. This will generate a full-page schedule in one of five Views:
+
+* [Table](https://demo.radiostation.pro/master-schedule/table-view/) (default) - responsive program in table form
+* [Tabbed](https://demo.radiostation.pro/master-schedule/tabbed-view/) - responsive styled list view with day selection tabs
+* [List](https://demo.radiostation.pro/master-schedule/list-view/) - unstyled plain list view for custom development use
+* [Divs](https://demo.radiostation.pro/master-schedule/divs-view/) - (deprecated - display issues) legacy unstyled div-based view
+* [Legacy](https://demo.radiostation.pro/master-schedule/legacy-view/) - (deprecated) legacy table view
+* [Grid](https://demo.radiostation.pro/master-schedule/grid-view/) - [Pro] extra grid style view available in Pro version
+* [Calendar](https://demo.radiostation.pro/master-schedule/calendar-view/) - [Pro] extra calendar style view available in Pro version
+
+The above View names are linked to examples on the Demo Site.
+
+Note that Divs and Legacy Views are considered deprecated as they do not honour Schedule Overrides, but have been kept for backwards compatibility only. The legacy Divs view also has display issues.
+
+The following attributes are available for the shortcode:
+
+* **Layout Display Options**
+* *view* : Which View to use for display output. 'table', 'tabs', 'list', 'divs', 'legacy', 'grid', 'calendar'. Default 'table'.
+* *time* : Display time format you with to use. 12 and 24. Default is Plugin Setting.
+* *clock* : Display Radio Clock above schedule. (See clock shortcode.) 0 or 1. Default is Plugin Setting.
+* **Show Display Attributes**
+* *show_times* : Whether to display the show shift's start and end times. 0 or 1. Default 1.
+* *show_link* : Display the title of the show as a link to its profile page. 0 or 1. Default 1.
+* *show_image* : Whether the display the show's avatar. 0 or 1. Default 0 (1 for Tabbed View.)
+* *show_desc* : Whether to display Show Description excerpt. 0 or 1.
+* *show_hosts* : Whether to display a list of show hosts. 0 or 1. Default 0.
+* *link_hosts* : Whether to link each show host to their profile page. 0 or 1. Default 0.
+* *show_genres* : Whether to display a list of show genres. 0 or 1. Default 0 (1 for Tabbed View.)
+* *show_encore* : Whether to display 'encore airing' for a show shift. 0 or 1. Default 1.
+* *show_file* : Whether to add a link to the latest audio file. 0 or 1. Default 0.
+* **Time Display Attributes**
+* *days* : Display for single day or multiple days (string or 0-6, comma separated.) Default all.
+* *start_day* : day of the week to start schedule (string or 0-6, or 'today') Defaults to WordPress setting.
+* *display_day* : Full or short day heading ('full' or 'short') Default short for Table, full for Tabs/List.
+* *display_date* : Date format for date subheading. 0 for none. Default 'jS' for Table/List, 0 for Tabs.
+* *display_month* : Full or short month subheading ('full', 'short') Default 'short'.
+* ** View-specific Attributes **
+* *image_position* : Tabs View: Show image position placement. 'right', 'left' or 'alternate'. Default 'left'.
+* *hide_past_shows* : Tabs View: Hide shows that are finished in the Schedule. 0 or 1. Default 0.
+* *gridwidth* : [Pro] Grid View: Set the width, in pixels of the grid columns. Default 150.
+* *divheight* : [Legacy] Divs View: Set the height, in pixels, of the individual divs. Default 45.
+* *time_spaced* : [Pro] Grid View: Enabled time spacing with background images. 0 or 1. Default 0.
+* *weeks* : Number of weeks to display in calendar. For Pro 'calendar' view only. Default 4.
+* *previous_weeks* : Number of past weeks to display in calendar. For Pro 'calendar' view only. Default 1.
+
+Example: Display the schedule in 24-hour time format, use `[master-schedule time="24"]`.
+
+##### [Pro] Multiple View Switching
+
+In [Radio Station Pro](https://radiostation.pro), you can display multiple views which the user can switch between. This gives your visitors better access to your schedule in the way that is better suited to them. You can set the available views in the plugin settings for the automatic schedule page as well as the default view. Alternatively you can use the Master Schedule Shortcode as normal on a page and provide a comma-separated list of views (the first value provided will be used as the default view.) eg. `[master-schedule view="table,tabs,grid"]`
+
+The default view can also be set via the plugin settings page, or by adding a `default_view` shortcode attribute if you wish to override this setting in the shortcode.
+
+Further, if you wish to customize the attributes for each particular view, you can use the `radio_station_pro_multiview_attributes` filter. By adding a view prefix to the attribute you wish to change, this will override the attribute for that view. eg. To display the full day heading in Tabs view, but short day headings in Table view:
+
+```
+add_filter( 'radio_station_pro_multiview_atts', 'my_custom_multiview_attributes );
+function my_custom_multiview_attributes( $atts ) {
+ $atts['table_display_day'] = 'short';
+ $atts['tabs_display_day'] = 'long';
+ return $atts;
+}
+```
+
+[Demo Site Example Output](https://demo.radiostation.pro/master-schedule/multiple-view-switching/)
+
+
+#### Radio Timezone Shortcode
+
+`[radio-timezone]`
+
+Displays the Radio Station Timezone selected via Plugin Settings. There are no attributes for this shortcode. This is the default display above the Master Schedule when the Radio Clock is turned off in the Plugin Settings.
+
+
+#### Radio Clock Shortcode
+
+`[radio-clock]`
+
+Displays the current server time and user time. Also available as a Widget.
+
+The following attributes are available for this shortcode:
+
+* *time_format* : Display time format you with to use. 12 and 24. Default is Plugin Setting.
+* *day* : Display day after current times. 'full', 'short' or 'none'. Default 'full'.
+* *date* : Display date after current times. 0 or 1. Default 1.
+* *month* : Display months after current times. 'full', 'short' or 'none'. Default 'full'.
+* *zone* : Display timezone after current times. 0 or 1. Default 1.
+* *seconds* : Display seconds with current times. 0 or 1. Default 0.
+
+The Radio Clock can also be displayed by default above the Master Schedule by enabling this in the Plugin Settings, or by adding `clock="1"` to the Master Schedule shortcode. (It's display attributes as part of the schedule can changed using the `radio_station_schedule_clock` filter.)
+
+
+##### [Pro] User Timezone Switching
+
+In [Radio Station Pro](https://radiostation.pro), where the Radio Clock is displayed (whether as a widget or part of the schedule) the user is given an extra link "Change Timezone" through which they can select their local timezone. The Radio Clock (and/or Schedule) will then provide an additional converted display for displayed times into the user's selected timezone.
+
+
+## Archive Shortcodes
+
+Note for ease of use either the singular or plural version of each archive shortcode will work.
+
+### Shows Archive Shortcode
+
+`[shows-archive]` (or `[show-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *description* : Show description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *hide_empty* : Only display if Shows are found. 0 or 1. Default 0.
+* *time* : Display time format you with to use. Valid values are 12 and 24. Default is the Plugin Setting.
+* *genre* : Genres to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *language* : Languages to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *status* : Query for Show status. Default 'publish'.
+* *perpage* : Query for number of Shows. Default -1 (all)
+* *offset* : Query for Show offset. Default '' (no offset)
+* *orderby* : Query to order Show display by. Default 'title'.
+* *order* : Query order for Shows. Default 'ASC'.
+* *show_avatars* : Display the Show Avatar. 0 or 1. Default 1.
+* *thumbnails* : Display Show Featured image if no Show Avatar. 0 or 1. Default 0.
+* *with_shifts* : Only display Shows with active Shifts. 0 or 1. Default 0.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/shows-archive/)
+
+### Overrides Archive Shortcode
+
+`[overrides-archive]` (or `[override-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *description* : Override description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *hide_empty* : Only display if Overides are found. 0 or 1. Default 0.
+* *show_dates* : Display the Schedule Override dates and start/end times. 0 or 1. Default 1.
+* *time* : Display time format you with to use. Valid values are 12 and 24. Default is the Plugin Setting.
+* *genre* : Genres to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *language* : Languages to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *status* : Query for Override status. Default 'publish'.
+* *perpage* : Query for number of Overrides. Default -1 (all)
+* *offset* : Query for Override offset. Default '' (no offset)
+* *orderby* : Query to order Override display by. Default 'title'.
+* *order* : Query order for Overrides. Default 'ASC'.
+* *show_avatars* : Display the Override Avatar. 0 or 1. Default 1.
+* *thumbnails* : Display Override Featured image if no Overide Avatar. 0 or 1. Default 0.
+* *with_dates* : Only display Shows with Date set. 0 or 1. Default 0.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/overrides-archive/)
+
+### Playlists Archive Shortcode
+
+`[playlists-archive]` (or `[playlist-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *description* : Playlist description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *hide_empty* : Only display if Playlists are found. 0 or 1. Default 0.
+* *genre* : Genres to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *language* : Languages to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *status* : Query for Playlist status. Default 'publish'.
+* *perpage* : Query for number of Playlists. Default -1 (all)
+* *offset* : Query for Playlists offset. Default '' (no offset)
+* *orderby* : Query to order Playlists display by. Default 'title'.
+* *order* : Query order for Playlists. Default 'ASC'.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/playlists-archive/)
+
+### Genres Archive Shortcode
+
+`[genres-archive]` (or `[genre-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *genres* : Genres to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *link_genres* : Link Genre titles to term pages. 0 or 1. Default 1.
+* *genre_desc* : Display Genre term description. 0 or 1. Default 1.
+* *genre_images' : [Pro] Display Genre images. 0 or 1. Default 1.
+* *view* : Layout display view. 'list' or 'grid'. Default 'grid'.
+* *image_width* : [Pro] Set a width style in pixels for Genre images. Default is 100.
+* *hide_empty* : No output if no records to display for Genre. 0 or 1. Default 1.
+* *status* : Query for Show status. Default 'publish'.
+* *perpage* : Query for number of Shows. Default -1 (all)
+* *offset* : Query for Show offset. Default '' (no offset)
+* *orderby* : Query to order Show display by. Default 'title'.
+* *order* : Query order for Shows. Default 'ASC'.
+* *with_shifts* : Only display Shows with active Shifts. 0 or 1. Default 0.
+* *show_avatars* : Display the Show Avatar. 0 or 1. Default 1.
+* *thumbnails* : Display Show Featured image if no Show Avatar. 0 or 1. Default 0.
+* *avatar_width* : * *avatar_width* : Set a width style in pixels for Show Avatars. Default is 75.
+* *show_desc* : Display Show Descriptions. 'none', 'full' or 'excerpt'. Default 'none'.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/genres-archive/)
+
+### Language Archive Shortcode
+
+`[languages-archive]` (or `[language-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *languages* : Genres to display (ID or slug). Separate multiple values with commas. Default empty (all)
+* *link_languages* : Link Genre titles to term pages. 0 or 1. Default 1.
+* *language_desc* : Display Genre term description. 0 or 1. Default 1.
+* *view* : Layout display view. 'list' or 'grid'. Default 'grid'.
+* *hide_empty' : No output if no records to display for Genre. 0 or 1. Default 1.
+* *status* : Query for Show status. Default 'publish'.
+* *perpage* : Query for number of Shows. Default -1 (all)
+* *offset* : Query for Show offset. Default '' (no offset)
+* *orderby* : Query to order Show display by. Default 'title'.
+* *order* : Query order for Shows. Default 'ASC'.
+* *with_shifts* : Only display Shows with active Shifts. 0 or 1. Default 0.
+* *show_avatars* : Display the Show Avatar. 0 or 1. Default 1.
+* *thumbnails* : Display Show Featured image if no Show Avatar. 0 or 1. Default 0.
+* *avatar_width* : * *avatar_width* : Set a width style in pixels for Show Avatars. Default is 75.
+* *show_desc* : Display Show Descriptions. 'none', 'full' or 'excerpt'. Default 'none'.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/languages-archive/)
+
+### [Pro] Hosts Archive Shortcode
+
+`[host-archive]` or `[hosts-archive]`
+
+* *description* : Profile description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *status* : Query for Host status. Default 'publish'.
+* *perpage* : Query for number of Hosts. Default -1 (all)
+* *offset* : Query for Host offset. Default '' (no offset)
+* *orderby* : Query to order Host display by. Default 'title'.
+* *order* : Query order for Hosts. Default 'ASC'.
+* *thumbnails* : Display profile image. 0 or 1. Default 0.
+* *social* : Display social profile icons. 0 or 1. Default 1.
+* *shows* : Display a list of Shows assigned to Host. 0 or 1. Default 1.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/hosts-archive/)
+
+### [Pro] Producers Archive Shortcode
+
+`[producer-archive]` or `[producers-archive]`
+
+* *description* : Profile description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *status* : Query for Producer status. Default 'publish'.
+* *perpage* : Query for number of Producers. Default -1 (all)
+* *offset* : Query for producer offset. Default '' (no offset)
+* *orderby* : Query to order Producer display by. Default 'title'.
+* *order* : Query order for Producers. Default 'ASC'.
+* *thumbnails* : Display profile image. 0 or 1. Default 0.
+* *social* : Display social profile icons. 0 or 1. Default 1.
+* *shows* : Display a list of Shows assigned to Producer. 0 or 1. Default 1.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/producers-archive/)
+
+### [Pro] Episodes Archive Shortcode
+
+`[episode-archive]` or `[episodes-archive]`
+
+* *description* : Episode description display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *view* : Display view option. 'list' or 'grid' option. Default 'list'.
+* *status* : Query for Episode status. Default 'publish'.
+* *perpage* : Query for number of Episodes. Default -1 (all)
+* *offset* : Query for episode offset. Default '' (no offset)
+* *orderby* : Query to order Episode display by. Default 'title'.
+* *order* : Query order for Episode. Default 'ASC'.
+* *thumbnails* : Display Episode image. 0 or 1. Default 0.
+* *show* : Display the Show the Episode is assigned to. 0 or 1. Default 1.
+* *number* : Display the Episode Number. 0 or 1. Default 1.
+* *air_date* : Display the Episode Air Date. 0 or 1. Default 1.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/episodes-archive/)
+
+### [Pro] Team Archive Shortcode
+
+`[team-archive]`
+
+The following attributes are available for this shortcode:
+
+* *view* : Layout display view. 'list' or 'grid'. Default list.
+
+...
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/team-archive/)
+
+### Show Posts Archive Shortcode
+
+`[show-posts-archive]` (or `[show-post-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *per_page* : Number of Show Posts to display per page. Default 15.
+* *limit* : Limit of Show Posts to display. Default 0 (no limit)
+* *content* : Post Content display. 'none', 'full' or 'excerpt'. Default 'excerpt'.
+* *thumbnails* : Display Show Post Thumbnails. 0 or 1. Default 1.
+* *pagination* : Paginate Show Post Display. 0 or 1. Default 1.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/show-posts-archive/)
+
+### Show Playlists Archive Shortcode
+
+`[show-playlists-archive]` (or `[show-playlist-archive]`)
+
+The following attributes are available for this shortcode:
+
+* *per_page* : Number of Show Playlists to display per page. Default 15.
+* *limit* : Limit of Show Playlists to display. Default 0 (no limit)
+* *content* : Playlist Content display. 'none', 'full' or 'excerpt. Default 'excerpt'.
+* *pagination* : Paginate Show Post Display. 0 or 1. Default 1.
+
+[Demo Site Example Output](https://demo.radiostation.pro/archive-shortcodes/show-playlists-archive/)
+
+### [Pro] Show Episodes Archive Shortcode
+
+`[show-episodes-archive]` (or `[show-episode-archive]`)
+
+This shortcode is available in [Radio Station Pro](https://radiostation.pro)
+
+
+## Widget Shortcodes
+
+### Current Show Widget Shortcode
+
+`[current-show]` - see [Current Show Widget](./Widgets.md#current-show-widget)
+
+### Upcoming Shows Widget Shortcode
+
+`[upcoming-shows]` - see [Upcoming Shows Widget](./Widgets.md#upcoming-shows-widget)
+
+### Current Playlist Widget Shortcode
+
+`[current-playlist]` - see [Current Playlist Widget](./Widgets.md#current-playlist-widget)
+
+
+### Legacy Shortcodes
+
+#### Show List
+
+`[show-list]`
+
+This shortcode is considered *Deprecated*. Use the [Shows Archive Shortcode](#shows-archive-shortcode) instead: `[shows-archive]`
+
+The following attributes are available for this shortcode:
+
+* *genre* : Displays shows only from the specified genre(s). Separate multiple genres with a comma, e.g. genre="pop,rock".
+
+Examples: `[list-shows genre="pop"]`, `[list-shows genre="pop,rock,metal"]`
+
+#### Show Playlists
+
+`[show-playlists]` (or `[get-playlists]`
+
+This shortcode is considered *Deprecated*. Use the [Show Playlists Archive Shortcode](#show-playlists-archive-shortcode) instead: `[show-playlists-archive]`
+
+The following attributes are available for this shortcode:
+
+* *show* : The ID of the Show to display Playlists for.
+* *limit* : Maximum number of Playlists to display.
+
diff --git a/docs/Widgets.md b/docs/Widgets.md
new file mode 100644
index 0000000..4bfd130
--- /dev/null
+++ b/docs/Widgets.md
@@ -0,0 +1,172 @@
+# Radio Station Plugin Widgets
+
+***
+
+You can add any of the Radio Station Plugin Widgets to your site's sidebar widget areas via the WordPress Appearance -> Widgets screen.
+
+Note Widgets are displayed via their corresponding Shortcodes to prevent code duplication and maintain display consistency. (The selected Widget options are converted into Shortcode attributes.) This also means if you want to display a Widget within a custom Template, you can use the corresponding shortcode in your template file. eg. `do_shortcode('[current-show]');`
+
+[Radio Station Demo Site](http://demo.radiostation.pro)
+
+
+## Radio Player Widget
+
+Since 2.4.0, Radio Station includes a Streaming Player Widget! (see [Radio Player Widget](./Player.md#radio-player-widget))
+
+### Radio Player Shortcode
+
+`[radio-player]` (see [Radio Player Shortcode](./Player.md#radio-player-shortcode))
+
+#### [Pro] Sitewide Bar Player
+
+[Radio Station Pro](https://radiostation.pro) includes a Sitewide Bar Streaming Player. It isn't added via the Widgets Page, but is instead configured via the Plugin Settings Page under the Player tab. (see [Sitewide Bar Player](./Player.md#pro-sitewide-bar-player) )
+
+## Current Show Widget
+
+Displays the currently playing Show - if there is one scheduled to play right now.
+
+### Current Show Shortcode
+
+`[current-show]` (legacy supported name: `[dj-widget]`)
+
+The following attribute options are available for this shortcode:
+
+* **Load Display Attributes**
+* *ajax* : Whether to AJAX load this widget/shortcode. 0 or 1. Default is Plugin Setting.
+* *dynamic* : [Pro] Whether to dynamically reload on show changeover time. 0 or 1. Default 1.
+* *title* : The title you would like to appear over the Current Show. Default is 'Current Show'.
+* *no_shows* : The text displayed when no show is scheduled for the current time. Default empty.
+* *hide_empty*: Hide the content of the widget/shortcode if there is no current show. 0 or 1. Default 0.
+* **Show Display Attributes**
+* *title_position* : Relative to Avatar. 'above', 'below', 'left' or 'right'. Default is 'right'.
+* *show_link* : Display a link to a show's page. 0 or 1 Default is 1.
+* *show_avatar* : Display a show's thumbnail. 0 or 1. Default is 1.
+* *avatar_width* : Set a width style in pixels for Show Avatars. Default is not to set.
+* **Time Display Attributes**
+* *show_sched* : Display the Show's schedule. 0 or 1. Default is 1.
+* *show_all_sched* : Displays all schedules for a show if it airs on multiple days. 0 or 1. Default is 0.
+* *countdown* : Display a Countdown until the current Show ends. 0 or 1. Default is 0.
+* *time_format* : The time format used for displaying schedules. 12 or 24. Default is Plugin Setting.
+* **Extra Display Options**
+* *display_hosts* : Display the names of the Hosts/DJs on the show. 0 or 1. Default is 0.
+* *link_hosts* : Link Show hosts to their profile pages. 0 or 1. Default is 0.
+* *show_desc* : Display an excerpt of the show's description. 0 or 1. Default is 0.
+* *show_playlist* : Display a link to the show's current playlist, if any. 0 or 1. Default is 1.
+* *show_encore* : Display encore presentation text (when set for Show). 0 or 1. Default is 1.
+
+Example: `[current-show title="Now On-Air" show_avatar="1" show_link="1" show_sched="1"]`
+
+[Demo Site Example Output](https://demo.radiostation.pro/extra-shortcodes/current-show-widget/)
+
+
+## Upcoming Shows Widget
+
+Displays a limited list of Upcoming Shows - if there are Shows scheduled.
+
+### Upcoming Shows Shortcode
+
+`[upcoming-shows]` (legacy supported name: `[dj-coming-up-widget]`)
+
+The following attribute options are available for this shortcode:
+
+* **Load Display Attributes**
+* *limit* : The number of upcoming shows to display. 0 or 1. Default is 1.
+* *ajax* : Whether to AJAX load this widget/shortcode. 0 or 1. Default is Plugin Setting.
+* *dynamic* : [Pro] Whether to dynamically reload on show changeover time. 0 or 1. Default 1.
+* *title* : The title you would like to appear over the Upcoming Shows.
+* *no_shows* : The text displayed when no upcoming show is scheduled. Default empty.
+* *hide_empty* : Hide the content of the widget/shortcode if there is no current show. 0 or 1. Default 0.
+* **Show Display Attributes**
+* *show_link* : Link the Show title to the Show's page. 0 or 1. Default is 0.
+* *title_position* : Relative to Avatar. 'above', 'below', 'left' or 'right'. Default is 'right'.
+* *show_avatar* : Display upcoming Show Avatars. 0 or 1. Default is 0.
+* *avatar_size* : The name of the (WordPress) size to use for the Show Avatar. Default
+* *avatar_width* : Set a width style in pixels for Show Avatars. Default is not to set.
+* **Time Display Attributes**
+* *show_sched* : Display the show's schedules. 0 or 1. Default is 1.
+* *countdown* : Display the Countdown until the next Show. 0 or 1. Default is 0.
+* *time_format* : The time format used for displaying schedules. 12 or 24. Default is Plugin Setting.
+* **Extra Display Attributes**
+* *display_hosts* : Display the names of the DJs on the show. 0 or 1. Default is 0.
+* *link_hosts* : Link Show hosts to their profile pages. 0 or 1. Default is 0.
+* *show_encore* : Display encore presentation text (when set for Show). 0 or 1. Default is 1.
+
+Example: `[upcoming-shows title="Coming Up On-Air" show_avatar="1" show_link="1" limit="3" time_format="12" schow_sched="1"]`
+
+[Demo Site Example Output](https://demo.radiostation.pro/extra-shortcodes/upcoming-shows-widget/)
+
+
+## Current Playlist Widget
+
+Displays the Playlist assigned to the currently playing Show - if there is one assigned to it.
+
+### Current Playlist Shortcode
+
+`[current-playlist]` (legacy supported name `[now-playing]`)
+
+The following attribute options are available for this shortcode:
+
+* **Load Display Attributes**
+* *ajax* : Whether to AJAX load this widget/shortcode. 0 or 1. Default is Plugin Setting.
+* *dynamic* : [Pro] Whether to dynamically reload on show changeover time. 0 or 1. Default 1.
+* *hide_empty*: Hide the content of the widget/shortcode if there is no current playlist. 0 or 1. Default 0.
+* **Playlist Display Attributes**
+* *title* : The title you would like to appear over the Playlist display. Default empty.
+* *playlist_title* : Whether to display the name of the current playlist. 0 or 1. Default 0.
+* *link* : Whether to link the playlist title to the playlist's page. 0 or 1. Default 1.
+* *countdown* : Display the Playlist remaining time Countdown. 0 or 1. Default is 0.
+* *no_shows* : The text displayed when there is no current playlist. Default empty.
+* **Track Display Attributes**
+* *artist* : Display artist name. 0 or 1 Default is 1.
+* *song* : Display song name. 0 or 1. Default is 1.
+* *album* : Display album name. 0 or 1. Default is 0.
+* *label* : Display label name. 0 or 1. Default is 0.
+* *comments* : Display DJ comments. 0 or 1. Default is 0.
+
+Example: `[current-playlist title="Current Song" artist="1" song="1" album="1" label="1" comments="0"]`
+
+[Demo Site Example Output](https://demo.radiostation.pro/extra-shortcodes/current-playlist-widget/)
+
+
+## Radio Clock Widget
+
+`[radio-clock]` (see [Radio Clock Shortcode](./Shortcodes.md#radio-clock-shortcode))
+
+Radio Station includes a Radio Clock Widget which will display the current Radio Station time in your selected Radio Station Timezone (via the Plugin Settings page) alongside the site visitor's current time (via browser timezone detection.)
+
+#### [Pro] Timezone Switcher
+
+[Radio Station Pro](https://radiostation.pro) includes a user Timezone Switcher in the which will allow your listener's to select a timezone and display adjusted Schedule and Show times.
+
+
+## Page Builder Widgets
+
+Page builder widgets have been introduced in Free 2.5.0 (Gutenberg Blocks) and Pro 2.6.0 (Elementor and Beaver Builder Modules.) All have the same settings as the standard widgets and support dynamic live previewing. (All output is rendered via the original shortcode functions, whether the settings are passed from shortcode attributes, widget options, block settings, or module settings.)
+
+### Radio Station Blocks
+
+Radio Blocks have been added to allow direct adding of any of the Radio Station widgets as Blocks in the WordPress Block Editor (aka Gutenberg.) You can find these by adding a new Block (pressing the blue + button.) All the Radio Blocks can be found grouped in the "Radio Station" category section (scroll down and the different categories will load.)
+
+Once you have added a Radio Station Block, click on the Block and then click the Gear Settings icon in the top right of the Block Editor. The settings for the block will appear in the panel on the right. Changing any of the settings will reload the block preview dynamically.
+
+#### Conditional Field Options
+
+It is worth noting that there are a few conditional fields in some of the page builder widgets (this is true regardless of the builder.) Most significantly, some options specific to a Schedule view will appear only if that view is selected. Additionally, some Archive view options will only appear for specific post types.
+
+### [Pro] Extra Block Options
+
+Some of the extra features available in Pro are also available in their relevent matching page builder widgets (these options will only display when the Pro plugin is activated):
+
+* The Radio Schedule Block has the extra Grid and Calendar Views, and multiview switching.
+* The Radio Archive Block can display Episode, Host and Producer post type archives.
+* The Radio Player Block supports extra theming and color options like the Sitewide Player Bar.
+* The Current Show/Playlist and Upcoming Show Blocks support dynamic reloading at Show changeovers.
+
+### [Pro] Beaver Builder Modules
+
+Readio Station Beaver Builder Modules can be found in the Radio Station category section of the Modules tab (ensure "Standard Modules" is selected in the dropdown.) Hover any module on the page and click the Settings spanner icon to customize the module's settings as desired. Each module has color and typography options in the Style tab. Changing any of the settings will reload the module preview dynamically.
+
+### [Pro] Elementor Widgets
+
+Radio Station Elementor Widgets can be found in the Radio Station category section of the Elements Tab in Elementor. Drag a Radio Station widget element to your content area as normal. Click on any element to customize the Settings in the left pane as desired. Each element widget has color and typography options in the Style tab. Changing any of the settings will reload the element preview dynamically.
+
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..e989d70
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,51 @@
+# Radio Station Documentation Index
+
+***
+
+
+| Topic | Description |
+| --- | --- |
+| [FAQ](./FAQ.md) | Frequently Asked Questions |
+| [Options](./Options.md) | Plugin Option Details and Value Filters |
+| [Display](./Display.md) | Automatic Pages, Templates, Images and Translations |
+| [Player](./Player.md) | Radio Stream Player |
+| [Manage](./Manage.md) | Admin Lists, Shift Editing, Conflict Checker |
+| [Roles](./Roles.md) | Plugin Roles and related Capabilities |
+| [Widgets](./Widgets.md) | Current Show, Upcoming Shows, Playlist, Clock, Player |
+| [Shortcodes](./Shortcodes.md) | Data List Archives and Master Schedule Views |
+| [Data](./Data.md) | Post Types, Taxonomies and Translations |
+| [Filters](./Filters.md) | Custom Development Value Filters |
+| [API](./API.md) | Data API via REST and Feed Endpoints
+| [Roadmap](./Roadmap.md) | Feature Roadmap for Free and Pro Versions |
+| [Changelog](../CHANGELOG.md) | Log of Changes for each Release |
+| [Pro](./Pro.md) | Index of Pro Feature Documentation |
+
+
+### Quickstart Guide
+
+Once you have installed and activated the **Radio Station** plugin on your WordPress site, your WordPress Admin area will now have a new menu item titled Radio Station with submenu page items. Note if you have a specific question, you can check out the [Frequently Asked Questions](./FAQ.md) as you may find the answer there.
+
+Firstly, you can visit the Plugin Settings screen to adjust the default [Plugin Options](./Options.md) to your liking. Here you can set your Radio Timezone and Language, along with your Streaming URL and Station Logo, as well as other global plugin settings. Also from this Settings page you can assign [Automatic Pages](./Display.md#automatic-pages) and Views for your Program Schedule display and for other plugin post type archive displays. But first you will want to add some Shows to display in your Schedule!
+
+To do this, click on Shows in the admin submenu, then on "Add a New Show" at the top. Give it a Shift timeslot and a description and then click Publish. Then view the Show page by clicking the Show Permalink under the show title. (Depending on your Theme, you may wish to adjust the [Templates](./Display.md#page-templates) used.) You can also assign different [Images](./Display.md#images) to Shows.
+
+Next, have a look at your Program Schedule page to see the Show displayed there also. Keep adding Shows until you have your Schedule filled in! You can also add schedule Overrides for specific date and time blocks only. For ease of use they can be fully or partially linked to an existing Show. You can further [Manage](./Manage.md) your Shows and other Station data via the WordPress Admin area.
+
+Now you may want to give some users on your site some plugin [Roles](./Roles.md). (Note that while the default interface in WordPress allows you to assign a single role to a user, it also supports multiple roles, but you need to add a plugin to get an interface for this.) Giving a user role of Host/DJ or Producer to a user will allow them to be assigned to a Show on the Show Edit Page and allow them to edit that Show. You can also assign the Show Editor role if you have someone who needs to edit all plugin post types without being a site Administator.
+
+There are a number of [Widgets](./Widgets.md) you can add to your site via your *Appearance -> Widgets* admin submenu. These are also available as [Shortcodes](./Shortcodes.md) and [Blocks](./Widgets.md#radio-station-blocks). There are widgets for the Current Show or Playlist, and another to display Upcoming Shows. In this way you can also add a Stream Player, Radio Clock, Schedule View or Show List anywhere you like.
+
+Radio Station has several in-built [Data](./Data.md) types. These include [Custom Post Types](./Data.md#custom-post-types) for Shows, Schedule Overrides and Playlists. There are [Taxonomies](./Data.md#taxonomies) for Genres and Languages. You can override most data values and display output via custom [Data Filters](./Filters.md#data-filters) throughout the plugin. We have also incorporated a [Data API](./API.md) in the plugin available via REST and/or WordPress Feeds, and this data is accessible in JSON format.
+
+This plugin is under active development and we are continuously working to enhance the free version available on [WordPress.Org](https://wordpress.org/plugins/radio-station/), as well as creating new feature additions for [Radio Station Pro](https://radiostation.pro/). Check out the [Roadmap](./Roadmap.md) if you are interested in seeing what is coming up next!
+
+#### [Pro] Professional Version Documentation
+
+For ease of reference, documentation of features that are included in [Radio Station Pro](https://radiostation.pro) are included within the Free Documentation here, simply marked with `[Pro]` like the heading above. For a list of all these features linked to their revelant sections see the [Pro Feature Index](./Pro.md)
+
+#### Plugin Support and Contributing
+
+If you are wanting to Submit a Bug or Feature Request, you can do so via the [WordPress.Org Plugin Support Forum](https://wordpress.org/support/plugin/radio-station/), but we would prefer you submit a more detailed issue via [GitHub Issues](https://github.com/netmix/radio-station/issues) where we track and prioritize these using GitHub Projects.
+
+Similarly, you can Contribute directly to the plugin via submitting an Issue or Pull Request on the [Github Plugin Repository](https://github.com/netmix/radio-station/). Or if you would prefer to get involved in the plugin's development even more substantially, please [Contact Us via Email](mailto:info@netmix.com) and let us know what you would like to do.
+
diff --git a/freemius-pricing/freemius-pricing.css b/freemius-pricing/freemius-pricing.css
new file mode 100644
index 0000000..b7f250f
--- /dev/null
+++ b/freemius-pricing/freemius-pricing.css
@@ -0,0 +1,9 @@
+/* Radio Station - Freemius Pricing Page v2 Styles */
+
+#fs_pricing_app .fs-packages .fs-plan-features li:first-child {display: none;}
+#fs_pricing_app .fs-packages li:first-child .fs-plan-features li:first-child {display: block; font-weight: bold;}
+#fs_pricing_app .fs-packages li:nth-child(3) .fs-plan-features li:nth-child(3) {font-weight: bold;}
+#fs_pricing_app .fs-packages .fs-plan-features li:nth-child(2) {font-weight: bold;}
+#fs_pricing_app .fs-packages li:first-child .fs-plan-features li:nth-child(2) {font-weight: normal;}
+
+#fs_pricing_app .fs-packages .fs-button {color: #FFF; background: #2da1d0;}
diff --git a/freemius/LICENSE.txt b/freemius/LICENSE.txt
new file mode 100644
index 0000000..30ace6a
--- /dev/null
+++ b/freemius/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
\ No newline at end of file
diff --git a/freemius/README.md b/freemius/README.md
new file mode 100644
index 0000000..1b42646
--- /dev/null
+++ b/freemius/README.md
@@ -0,0 +1,289 @@
+Freemius WordPress SDK
+======================
+
+Welcome to the official repository for the Freemius SDK! Adding the SDK to your WordPress plugin, theme, or add-ons, enables all the benefits that come with using the [Freemius platform](https://freemius.com) such as:
+
+* [Software Licensing](https://freemius.com/wordpress/software-licensing/)
+* [Secure Checkout](https://freemius.com/wordpress/checkout/)
+* [Subscriptions](https://freemius.com/wordpress/recurring-payments-subscriptions/)
+* [Automatic Updates](https://freemius.com/wordpress/automatic-software-updates/)
+* [Seamless EU VAT](https://freemius.com/wordpress/collecting-eu-vat-europe/)
+* [Cart Abandonment Recovery](https://freemius.com/wordpress/cart-abandonment-recovery/)
+* [Affiliate Platform](https://freemius.com/wordpress/affiliate-platform/)
+* [Analytics & Usage Tracking](https://freemius.com/wordpress/insights/)
+* [User Dashboard](https://freemius.com/wordpress/user-dashboard/)
+
+* [Monetization](https://freemius.com/wordpress/)
+* [Analytics](https://freemius.com/wordpress/insights/)
+* [More...](https://freemius.com/wordpress/features-comparison/)
+
+Freemius truly empowers developers to create prosperous subscription-based businesses.
+
+If you're new to Freemius then we recommend taking a look at our [Getting Started](https://freemius.com/help/documentation/getting-started/) guide first.
+
+If you're a WordPress plugin or theme developer and are interested in monetizing with Freemius then you can [sign-up for a FREE account](https://dashboard.freemius.com/register/):
+
+https://dashboard.freemius.com/register/
+
+Once you have your account setup and are familiar with how it all works you're ready to begin [integrating Freemius](https://freemius.com/help/documentation/wordpress-sdk/integrating-freemius-sdk/) into your WordPress product
+
+You can see some of the existing WordPress.org plugins & themes that are already utilizing the power of Freemius here:
+
+* https://profiles.wordpress.org/freemius/#content-plugins
+* https://includewp.com/freemius/#focus
+
+## Code Documentation
+
+You can find the SDK's documentation here:
+https://freemius.com/help/documentation/wordpress-sdk/
+
+## Integrating & Initializing the SDK
+
+As part of the integration process, you'll need to [add the latest version](https://freemius.com/help/documentation/getting-started/#add_the_latest_wordpress_sdk_into_your_product) of the Freemius SDK into your WordPress project.
+
+Then, when you've completed the [SDK integration form](https://freemius.com/help/documentation/getting-started/#fill_out_the_sdk_integration_form) a snippet of code is generated which you'll need to copy and paste into the top of your main plugin's PHP file, right after the plugin's header comment.
+
+Note: For themes, this will be in the root `functions.php` file instead.
+
+A typical SDK snippet will look similar to the following (your particular snippet may differ slightly depending on your integration):
+
+```php
+if ( ! function_exists( 'my_prefix_fs' ) ) {
+ // Create a helper function for easy SDK access.
+ function my_prefix_fs() {
+ global $my_prefix_fs;
+
+ if ( ! isset( $my_prefix_fs ) ) {
+ // Include Freemius SDK.
+ require_once dirname(__FILE__) . '/freemius/start.php';
+
+ $my_prefix_fs = fs_dynamic_init( array(
+ 'id' => '1234',
+ 'slug' => 'my-new-plugin',
+ 'premium_slug' => 'my-new-plugin-premium',
+ 'type' => 'plugin',
+ 'public_key' => 'pk_bAEfta69seKymZzmf2xtqq8QXHz9y',
+ 'is_premium' => true,
+ // If your plugin is a serviceware, set this option to false.
+ 'has_premium_version' => true,
+ 'has_paid_plans' => true,
+ 'is_org_compliant' => true,
+ 'menu' => array(
+ 'slug' => 'my-new-plugin',
+ 'parent' => array(
+ 'slug' => 'options-general.php',
+ ),
+ ),
+ // Set the SDK to work in a sandbox mode (for development & testing).
+ // IMPORTANT: MAKE SURE TO REMOVE SECRET KEY BEFORE DEPLOYMENT.
+ 'secret_key' => 'sk_ubb4yN3mzqGR2x8#P7r5&@*xC$utE',
+ ) );
+ }
+
+ return $my_prefix_fs;
+ }
+
+ // Init Freemius.
+ my_prefix_fs();
+ // Signal that SDK was initiated.
+ do_action( 'my_prefix_fs_loaded' );
+}
+
+```
+
+## Usage example
+
+You can call any SDK methods by prefixing them with the shortcode function for your particular plugin/theme (specified when completing the SDK integration form in the Developer Dashboard):
+
+```php
+get_upgrade_url(); ?>
+```
+
+Or when calling Freemius multiple times in a scope, it's recommended to use it with the global variable:
+
+```php
+get_account_url();
+?>
+```
+
+There are many other SDK methods available that you can use to enhance the functionality of your WordPress product. Some of the more common use-cases are covered in the [Freemius SDK Gists](https://freemius.com/help/documentation/wordpress-sdk/gists/) documentation.
+
+## Adding license-based logic examples
+
+Add marketing content that encourages your users to upgrade to a paid version:
+
+```php
+is_not_paying() ) {
+ echo '