/* eslint-disable no-global-assign */
/*global fvwpflowplayer_helper_tag, fv_wp_flowplayer_re_edit, fv_wp_flowplayer_re_insert, fv_flowplayer_set_post_thumbnail_id, fv_flowplayer_set_post_thumbnail_nonce, */
/*global FCKeditorAPI, setPostThumbnailL10n, send_to_editor, tinymce*/
jQuery(function() {
// The actual editor
window.fv_player_editor = (function($) {
var
$doc = $(document),
$el_editor = $('#fv-player-shortcode-editor'),
$el_preview = $('#fv-player-shortcode-editor-preview'),
el_spinner = $('#fv-player-shortcode-editor-preview-spinner'),
el_preview_target = $('#fv-player-shortcode-editor-preview-target'),
$el_notices = $('#fv-player-editor-notices'),
// data to save in Ajax
ajax_save_this_please = false,
// last saved data to detect changes for auto-saving
ajax_save_previous = false,
current_player_db_id = 0,
current_player_object = false,
current_video_db_id = 0,
current_video_to_edit = -1,
current_editor_tab = false,
// Used for Video Custom Fields
current_post_id = 0,
current_post_meta_key = 0,
deleted_videos = [],
deleted_video_meta = [],
deleted_player_meta = [],
prevent_reload_for_current_save = false,
// stores the button which was clicked to open the editor
editor_button_clicked = 0,
// the post editor content being edited
editor_content,
// used in WP Heartbeat
edit_lock_removal = {},
// CodeMirror instance, if any
instance_code_mirror,
// keep track of the last cursor position in CodeMirror
instance_code_mirror_cursor_last,
// TinyMCE instance, if any
instance_tinymce,
// Foliopress WYSIWYG instance, if any
instance_fp_wysiwyg,
is_loading = false,
// are we editing player which is not yet in DB?
is_unsaved = true,
is_playlist_hero_editing = false,
// is the player already saved in the DB but actually
// still in a "draft" status? i.e. not published yet
has_draft_status = true,
// will be > 0 when any meta data are loading that need saving along with the form (example: S3 video duration, PPV product creation)
// ... this prevents overlay closing until all meta required data are loaded and stored
is_loading_meta_data = 0,
// whether we're editing a single video (true) or showing a playlist view (false)
editing_video_details = false,
// are we currently saving data?
is_saving = false,
fv_player_shortcode_editor_ajax,
// used when editing shortcode in TinyMCE
helper_tag = window.fvwpflowplayer_helper_tag,
// the part of shortcode outside of [fvplayer id="XYZ"]
// also accessed from outside
shortcode_remains,
// Some shortcode args should be kept. For example if you edit
// [fvplayer id="1" sort="newest"] that sort should not be removed
store_shortcode_args = {},
// Some shortcode args do not have a DB counterpars, so they should always
// be kept on the shortcode. For example if you edit
// [fvplayer src="some_video_url" sort="newest"] that sort should not be removed,
// as it's not been transferred into the DB
always_keep_shortcode_args = {},
// Flowplayer only lets us specify the RTMP server for the first video in plalist, so we store it here when the playlist item order is changing etc.
store_rtmp_server = '',
// stores parts of editor HTML which are later re-used when adding new items
template_playlist_item,
template_video,
template_subtitles_tab,
// used to remember which widget we are editing, if any
widget_id,
is_editing_playlist_item_title = false,
// list of errors that currently prevent auto-saving in the form of: { error_identifier_with_(plugin_)prefix : "the actual error text to show" }
// ... this will be shown in place of the "Saved!" message bottom overlay and it will always show only the first error in this object,
// as to not overload the user and UI with errors. Once that error is corrected, it gets removed from this object and next one (if any) is shown.
errors = {},
store_debug_log = [];
debug_log('Loading...');
/**
* Adds a notice at the bottom of player. Used for successful save and save errors. Notices are removed on successful save.
*
* @param {string} id
* @param {string} msg
* @param {int} [timeout] Duration for which it should appear or omit for persistent message
*/
function add_notice( id, msg, timeout ) {
var notice = $('
'+msg+'
' );
$el_notices.append( notice );
if( typeof(timeout) == 'number' ) {
setTimeout( function() {
notice.fadeOut();
}, timeout );
}
}
function add_shortcode_args( args ) {
let
params = jQuery.map( args, function (value, index) {
return index + '="' + value + '"';
});
return params.length ? ' ' + params.join(' ') : '';
}
function copy_player_button_toggle( show ) {
$('#fv-player-shortcode-editor .copy_player').toggle( show );
}
function check_for_player_meta_field(fieldName) {
return get_field_conf( fieldName ).store == 'player_meta';
}
function check_for_video_meta_field(fieldName) {
if ( get_field_conf( fieldName ).store == 'video_meta' ) {
return true;
}
return [
'fv_wp_flowplayer_field_duration',
'fv_wp_flowplayer_field_dvr',
'fv_wp_flowplayer_field_auto_splash',
'fv_wp_flowplayer_field_auto_caption',
'fv_wp_flowplayer_field_audio'
].indexOf(fieldName) > -1;
}
function get_current_player_db_id() {
return current_player_db_id;
}
function get_current_player_object() {
return current_player_object;
}
function get_current_video_meta( key ) {
var video_object = get_current_video_object(),
video_meta = false;
if( video_object ) {
$( video_object.meta ).each( function(k,v) {
if( v.meta_key == key ) {
video_meta = v;
return false;
}
} );
}
return video_meta;
}
function get_current_video_meta_value( key ) {
var video_object = get_current_video_object(),
video_id = video_object && video_object.id ? video_object.id : -1,
video_meta = get_current_video_meta( key );
if( video_meta ) {
debug_log( "Video meta '"+key+"' for #"+video_id, video_meta.meta_value );
return video_meta.meta_value;
}
debug_log( "Video meta '"+key+"' not found for #"+video_id );
return false;
}
function get_current_video_object() {
if( current_video_to_edit == -1 ) return false;
if( current_player_object.videos && current_player_object.videos[current_video_to_edit] ) {
return current_player_object.videos[current_video_to_edit];
}
return false;
}
/*
* Used when entering link to the "hero" field.
* If it's used in the playlist mode, we need to insert the hidden new playlist row and use the added field.
*/
function get_hero_src_field( input ) {
let editor_src_field = get_field("src")
if( $( '#playlist-hero:visible' ).length && input.data('playlist-hero') ) {
// Remember the new item index to not add new video on each keyup event!
if( !input.data( 'new-item-index' ) ) {
input.data( 'new-item-index', get_playlist_items_count() );
}
editor_src_field = editor_src_field.eq( input.data( 'new-item-index' ) );
// Add new playlist item if it's not there
if( !editor_src_field.length ) {
let new_item = playlist_item_add();
// If we are going to type in the URL, do not show the row
if( input.attr( 'type') == 'text' ) {
$( '#fv-player-editor-playlist .fv-player-editor-playlist-item:last').hide();
}
editor_src_field = get_field( "src", new_item.video_tab );
}
}
return editor_src_field;
}
/*
* Returns the number of videos in a playlist for the current player.
*/
function get_playlist_items_count() {
// TODO: Could we just use current_player_object.videos.length ? Would that work with editing?
return jQuery('#fv-player-editor-playlist .fv-player-editor-playlist-item').length;
}
function get_playlist_video_meta( meta_key, index ) {
var video_object = current_player_object.videos && current_player_object.videos[index],
video_meta = false;
if( video_object ) {
$( video_object.meta ).each( function(k,v) {
if( v.meta_key == meta_key ) {
video_meta = v;
return false;
}
} );
}
return video_meta;
}
function get_playlist_video_object( index ) {
if( current_player_object.videos && current_player_object.videos[index] ) {
return current_player_object.videos[index]
}
return false;
}
function get_playlist_video_meta_value( meta_key, index ) {
var video_meta = get_playlist_video_meta( meta_key, index );
if( video_meta ) {
return video_meta.meta_value;
}
return false;
}
/**
* A shorthand to save you from all the "fv_wp_flowplayer_field_"
* when selecting fields
*
* @param {string} key The field key. For example "src" gives
* you "fv_wp_flowplayer_field_src"
* @param {bool|jQuery|string} where Lets you narrow down the element wher you
* want to locate he field. You can use a jQuery
* element or a string selector for jQuery.
* Or TRUE to get the currently open playlist item.
*
* @return {jQuery} The field element/elements (in case of multiple language subtitles)
*/
function get_field( key, where ) {
var element = false,
selector = '.' + map_names_to_editor_fields(key) + ', [name=' + map_names_to_editor_fields(key) + ']';
if( typeof(where) == 'boolean' && where ) {
where = jQuery('.fv-player-tab [data-index='+current_video_to_edit+']');
element = where.find(selector);
} else if( where && typeof(where) == "object" ) {
element = where.find(selector);
} else if( where && typeof(where) == "string" ) {
element = $el_editor.find(where).find(selector);
} else {
element = $el_editor.find(selector);
}
if( !element.length ) {
console.log('FV Player Editor Warning: field '+key+' not found');
}
return element;
}
function get_field_conf( name ) {
name = get_field_name( name );
if ( window.fv_player_editor_fields && window.fv_player_editor_fields[ name ] ) {
return window.fv_player_editor_fields[ name ];
}
return {};
}
/**
* Gives you "src" out of "fv_wp_flowplayer_field_src"
*
* @param {string} name The field name
*
* @return {string} The field.... real name?
*/
function get_field_name( name ) {
if (name.indexOf('fv_wp_flowplayer_field_') > -1) {
return name.replace('fv_wp_flowplayer_field_', '');
}
return name;
}
/**
* Gives you the desired tab with video information
*
* @param {int|string} index Number, or first, or last
* @param {string} tab Tab name to obtain, options:
* * video-files
* * subtitles
*
* @return {object} The tab element
*/
function get_tab( index, tab ) {
var selector = '.fv-player-tab-'+tab+' [data-playlist-item]';
if( index == 'first' ) {
selector += ':first';
} else if( index == 'last' ) {
selector += ':last';
} else {
selector += '[data-index='+index+']';
}
return $el_editor.find(selector);
}
/**
* Gives you all desired tabs of a certain kind
*
* @return {object} The tab elements
*/
function get_tabs( tab ) {
var selector = '.fv-player-tab-'+tab+' [data-playlist-item]';
return $el_editor.find(selector);
}
function has_errors() {
for ( var i in errors ) {
return errors[ i ];
}
return false;
}
function hide_inputs() {
if( fv_player_editor_conf.hide ) {
$.each( fv_player_editor_conf.hide, function( k, v ) {
try {
if( 'configure-video' === v ) {
$( 'a.configure-video' ).hide();
$( "" ).appendTo( "head" )
} else {
$( '.fv-player-editor-field-wrap-'+v ).hide();
$( '[name=fv_wp_flowplayer_field_'+v+']' ).hide();
}
} catch(e) {
debug_log( 'Hide Field: Failure', e );
}
} );
}
}
/**
* Automatically handles new, updated or removed player meta data
* via JS.
*
* @param options Object with the following elements:
* element -> the actual element on page (input, select...) to check and get meta value from
* data -> existing player data, including player meta data
* meta_section -> section for the meta data, for example "player" for common metas, "ppv" for pay per view plugin etc.
* meta_key -> rhe actual key to check for and potentially add/update/remove
* handle_delete -> if true, value-less ('') elements will be considered indication that the meta key should be deleted
* delete_callback -> if set, this function is called when a meta key is deleted
* edit_callback -> if set, this function is called when a meta key is updated
* insert_callback -> if set, this function is called when a meta key is added
*/
function insertUpdateOrDeletePlayerMeta(options) {
var
$element = jQuery(options.element),
value = map_input_value( $element );
if ( ! options.meta_section ) {
options.meta_section = 'player';
}
// don't do anything if we've not found the actual element
if (!$element.length) {
return;
}
// check whether to update or delete this meta
if ($element.data('id')) {
// only delete this meta if delete was not prevented via options
// and if there was no value specified, otherwise update
if ((!options.handle_delete || options.handle_delete !== false) && !value) {
deleted_player_meta.push( $element.data('id') );
$element
.removeData('id')
.removeAttr('data-id');
// execute delete callback, if present
if (options.delete_callback && typeof(options.delete_callback) == 'function') {
options.delete_callback();
}
} else {
if (typeof(options.data) != 'undefined' && typeof(options.data['player_meta'][options.meta_section]) == 'undefined') {
options.data['player_meta'][options.meta_section] = {};
}
// update if we have an ID
if (typeof(options.data) != 'undefined') {
options.data['player_meta'][options.meta_section][options.meta_key] = {
'id': $element.data('id'),
'value': value
}
}
// execute update callback, if present
if (options.edit_callback && typeof(options.edit_callback) == 'function') {
options.edit_callback();
}
}
} else if (value || options.handle_delete !== false ) {
if (typeof(options.data) != 'undefined' && typeof(options.data['player_meta'][options.meta_section]) == 'undefined') {
options.data['player_meta'][options.meta_section] = {};
}
// insert new data if no meta ID
if (typeof(options.data) != 'undefined') {
options.data['player_meta'][options.meta_section][options.meta_key] = {
'value': value
}
}
if ( value ) {
// execute insert callback, if present
if (options.insert_callback && typeof(options.insert_callback) == 'function') {
options.insert_callback();
}
} else {
if (options.delete_callback && typeof(options.delete_callback) == 'function') {
options.delete_callback();
}
}
}
}
/**
* Automatically handles new, updated or removed video meta data
* via JS.
*
* @param options Object with the following elements:
* element -> the actual element on page (input, select...) to check and get meta value from
* data -> existing player data, including video meta data
* meta_section -> section for the meta data, for example "player" for common metas, "ppv" for pay per view plugin etc.
* meta_key -> rhe actual key to check for and potentially add/update/remove
* handle_delete -> if true, value-less ('') elements will be considered indication that the meta key should be deleted
* delete_callback -> if set, this function is called when a meta key is deleted
* edit_callback -> if set, this function is called when a meta key is updated
* insert_callback -> if set, this function is called when a meta key is added
*/
function insertUpdateOrDeleteVideoMeta(options) {
var
$element = jQuery(options.element),
value = map_input_value( $element );
// don't do anything if we've not found the actual element
if (!$element.length) {
return;
}
// check whether to update or delete this meta
if ($element.data('id')) {
// only delete this meta if delete was not prevented via options
// and if there was no value specified, otherwise update
if ((!options.handle_delete || options.handle_delete !== false) && !$element.val()) {
deleted_video_meta.push( $element.data('id') );
$element
.removeData('id')
.removeAttr('data-id');
// execute delete callback, if present
if (options.delete_callback && typeof(options.delete_callback) == 'function') {
options.delete_callback();
}
} else {
if (typeof(options.data) != 'undefined' && typeof(options.data['video_meta'][options.meta_section]) == 'undefined') {
options.data['video_meta'][options.meta_section] = {};
}
if (typeof(options.data) != 'undefined') {
// update if we have an ID
options.data['video_meta'][options.meta_section][options.meta_index][options.meta_key] = {
'id': $element.data('id'),
'value': value
}
// execute update callback, if present
if (options.edit_callback && typeof(options.edit_callback) == 'function') {
options.edit_callback();
}
}
}
} else if (value) {
if (typeof(options.data) != 'undefined' && typeof(options.data['video_meta'][options.meta_section]) == 'undefined') {
options.data['video_meta'][options.meta_section] = {};
}
// insert new data if no meta ID
if (typeof(options.data) != 'undefined') {
options.data['video_meta'][options.meta_section][options.meta_index][options.meta_key] = {
'value': value
}
// execute insert callback, if present
if (options.insert_callback && typeof(options.insert_callback) == 'function') {
options.insert_callback();
}
}
}
}
$doc.ready( function(){
var
next = false, // track if the player data has changed while saving
overlay_close_waiting_for_save = false,
ajax_saving = true,
int_keyup = false;
/*$(window).on('beforeunload', function(e) {
if (is_draft && is_draft_changed) {
return e.originalEvent.returnValue = 'You have unsaved changes. Are you sure you want to close this dialog and loose them?';
}
});*/
if( jQuery().fv_player_box ) {
debug_log('Attaching click actions...');
$doc.on( 'click', '.fv-wordpress-flowplayer-button, .fv-player-editor-button, .fv-player-edit', function(e) {
// make the TinyMCE editor below this button active,
// as otherwise we would be inserting into the last TinyMCE instance
// on the page if no TinyMCE instance was clicked into - which is not what we want to do
if (typeof(tinyMCE) != 'undefined' && typeof(tinyMCE.activeEditor) != 'undefined' && this.className.indexOf('fv-wordpress-flowplayer-button') > -1) {
var
$btn = jQuery(this),
$wraper_div = $btn.parents('.wp-editor-wrap:first');
for (var i in tinyMCE.editors) {
if (tinyMCE.editors[i].editorContainer && tinyMCE.editors[i].editorContainer.id && $wraper_div.find('#' + tinyMCE.editors[i].editorContainer.id).length) {
tinyMCE.activeEditor = tinyMCE.editors[i];
break;
}
}
}
editor_button_clicked = this;
e.preventDefault();
$.fv_player_box( {
onComplete : editor_open,
onClosed : editor_close,
onOpen: lightbox_open
} );
widget_id = $(this).data().number;
});
/**
* Elementor Widget support
*/
if ( window.elementor && window.elementor.channels && window.elementor.channels.editor ) {
// "Configure Player" button
elementor.channels.editor.on( 'fv-player-elementor-editor-open', function(e) {
editor_button_clicked = e.el;
$.fv_player_box( {
onComplete : editor_open,
onClosed : editor_close,
onOpen: lightbox_open
} );
});
// "Select Media" button for "Source URL"
elementor.channels.editor.on( 'fv-player-elementor-pick-source_url ', function(e) {
fv_flowplayer_uploader_button = jQuery( e.el );
$( '.elementor-control-source_url [data-setting="url"]' ).addClass( 'fv_flowplayer_target' );
fv_flowplayer_uploader_open();
} );
// "Select Media" button for "Splash URL"
elementor.channels.editor.on( 'fv-player-elementor-pick-splash_url ', function(e) {
fv_flowplayer_uploader_button = jQuery( e.el );
$( '.elementor-control-splash_url [data-setting="url"]' ).addClass( 'fv_flowplayer_target' );
fv_flowplayer_uploader_open();
} );
}
/**
* Look for buttons in the block editor canvas iframe (post editor and Site Editor).
*/
function getEditorCanvasIframe() {
return jQuery('iframe[name="editor-canvas"], iframe.editor-canvas__iframe, iframe.edit-site-visual-editor__editor-canvas');
}
function setupSiteEditorHandlers() {
var site_editor_iframe = getEditorCanvasIframe().contents();
if( site_editor_iframe.length ) {
debug_log( 'Editor canvas iframe found!' );
// FV Player Editor
site_editor_iframe.off( 'click', '.fv-wordpress-flowplayer-button, .fv-player-editor-button, .fv-player-edit' );
site_editor_iframe.on( 'click', '.fv-wordpress-flowplayer-button, .fv-player-editor-button, .fv-player-edit', function(e) {
editor_button_clicked = this;
e.preventDefault();
$.fv_player_box( {
top: "100px",
initialWidth: 1100,
initialHeight: 50,
width:"1100px",
height:"100px",
href: "#fv-player-shortcode-editor",
inline: true,
title: 'Add FV Player',
onComplete : editor_open,
onClosed : editor_close,
onOpen: lightbox_open
} );
widget_id = $(this).data().number;
});
// FV Player Block "Select Media" button
site_editor_iframe.off( 'click', '.fv-player-gutenberg-media' );
site_editor_iframe.on( 'click', '.fv-player-gutenberg-media', fv_flowplayer_uploader_init );
}
}
// Initial setup with interval
var site_editor_load = setInterval( function() {
var canvas = getEditorCanvasIframe();
if( canvas.length ) {
clearInterval(site_editor_load);
setupSiteEditorHandlers();
// Watch for iframe load events (fires when iframe reloads)
canvas.on('load', function() {
debug_log( 'Editor canvas iframe loaded, reattaching handlers' );
setTimeout( setupSiteEditorHandlers, 100 );
});
}
}, 1000 );
$doc.on( 'click', '.fv-player-export', function(e) {
let $element = jQuery(this),
player_id = $element.data('player_id');
e.preventDefault();
$.fv_player_box( {
onComplete : function() {
overlay_show('loading');
debug_log('Running fv_player_db_export Ajax for #' + player_id );
$.post(ajaxurl, {
action: 'fv_player_db_export',
playerID : player_id,
nonce : $element.data('nonce'),
cookie: encodeURIComponent(document.cookie),
}, function(json_export_data) {
var overlay = overlay_show('export');
overlay.find('textarea').val( $('').text(json_export_data).html() );
}).fail( function( jqXHR, textStatus, errorThrown ) {
overlay_show('message', 'An unexpected error has occurred while exporting player #' + player_id + ': ' + errorThrown + '
');
$element
.hide()
.after($spinner);
debug_log('Running fv_player_db_clone Ajax.');
$.post(ajaxurl, {
action: "fv_player_db_clone",
nonce: $element.data('nonce'),
playerID: $element.data('player_id')
}, function(playerID){
if (playerID != '0' && !isNaN(parseFloat(playerID)) && isFinite(playerID)) {
// add the inserted player's row
$.get(
fv_player_editor_conf.admin_url + '&id=' + playerID,
function (response) {
$('#the-list tr:first').before(jQuery(response).find('#the-list tr:first'));
$spinner.remove();
$element.show();
}).fail(function() {
$spinner.remove();
$element.show();
});
} else {
$spinner.remove();
$element.show();
alert(playerID); // show respone message
}
}).fail(function() {
$spinner.remove();
$element.show();
alert('Error');
});
return false;
});
}
// Gutenberg style checkboxes
$el_editor.on( 'change', '.components-form-toggle input[type=checkbox]', function() {
var wrap = $(this).closest('.components-form-toggle'),
checked = $(this).prop('checked'),
name = $(this).attr('name').replace( /fv_wp_flowplayer_field_/, '' );
checkbox_toggle_worker(wrap, name, checked);
});
$el_editor.on('change input', '.components-text-control__input, .components-select-control__input', function() {
var input = jQuery(this),
parent = input.closest('.fv-player-editor-children-wrap'),
name = input.attr('name').replace( /fv_wp_flowplayer_field_/, '' ),
wrap = input.parents( '.fv-player-editor-field-wrap-' + name );
text_and_select_worker( input, parent, name, wrap );
});
/*
* Intro view text input and button
*/
// Copy hero input value to the first video src and trigger event for save & preview
$('[name=hero-src]').on('change keyup', function() {
$('#playlist-hero .fv-player-editor-notice.notice-use-ui').hide();
let input = $( this ),
value = input.val().trim(),
is_valid_url = false;
if( value ) {
if( value.match( /https?:\/\// ) ) {
for (var vtype in window.fv_player_editor_matcher) {
if (window.fv_player_editor_matcher[vtype].matcher.exec(value) !== null) {
is_valid_url = true;
break;
}
}
}
if( $( '#playlist-hero:visible' ).length ) {
$('#playlist-hero .fv-player-editor-notice.notice-url-format').toggle( !is_valid_url );
} else {
$('#fv-player-shortcode-editor-preview-no .fv-player-editor-notice.notice-url-format').toggle( !is_valid_url );
}
}
if( !is_valid_url ) {
return false;
}
get_hero_src_field( $(this) ).val( value ).trigger( 'keyup' );
});
$('.button-hero').on('click', function() {
// click on the media library button next to that input
get_hero_src_field( $(this) ).closest('.components-base-control__field').find('.add_media').trigger('click');
});
/*
* NAV TABS
*/
$('.fv-player-tabs-header a').on( 'click', function(e) {
e.preventDefault();
$('.fv-player-tabs-header a').removeClass('nav-tab-active');
$(this).addClass('nav-tab-active')
$('.fv-player-tabs > .fv-player-tab').hide();
$('.' + $(this).data('tab')).show();
current_editor_tab = $(this).data('tab');
});
/*
* Select playlist item
* keywords: select item
*/
$doc.on('click','.fv-player-editor-playlist-item .configure-video, .fv-player-editor-playlist-item .fvp_item_video-thumbnail', function() {
if( fv_player_editor_conf.hide && fv_player_editor_conf.hide.indexOf( 'configure-video' ) > 0 ) {
return false;
}
playlist_item_show( $(this).parents('.fv-player-editor-playlist-item').attr('data-index') );
return false;
});
/*
* Show edit input
* keywords: edit playlist items edit playlist items
*/
$doc.on('click','.fv-player-tab-playlist .fv-player-editor-playlist-item .fvp_item_video-filename', function(e) {
e.stopPropagation();
var
$parent = $(e.target).closest('[data-index]'),
filename = $parent.find('.fvp_item_video-filename'),
wrap = $parent.find('.fvp_item_video-title-wrap'),
input = $parent.find('.fvp_item_video-edit-input');
wrap.hide();
input.val(filename.text()).show().focus();
is_editing_playlist_item_title = true;
});
// Clicking anywhere it the editor should close the playlist item title editing
$el_editor.on( 'click', function(e) {
if( is_editing_playlist_item_title ) {
// Except the title input box
if( jQuery(e.target).hasClass('fvp_item_video-edit-input') ) {
return;
}
is_editing_playlist_item_title = false;
title_editor_close();
}
});
/*
* Edit title
* keywords: edit title playlist items edit title playlist items
*/
$doc.on('keyup','.fv-player-tab-playlist .fv-player-editor-playlist-item .fvp_item_video-edit-input', function(e) {
e.stopPropagation();
if( e.key == "Enter" ) {
title_editor_close();
return;
}
var
$parent = $(e.target).parents('[data-index]'),
index = $parent.attr('data-index'),
video_tab = get_tab(index, 'video-files'),
new_title = jQuery(this).val();
$parent.find('.fvp_item_video-filename').text( new_title );
get_field('title', video_tab).val( new_title ).trigger('keyup');
});
/*
* Remove playlist item
* keywords: delete playlist items remove playlist items
*/
$doc.on('click','.fv-player-tab-playlist .fv-player-editor-playlist-item .fvp_item_remove', function(e) {
e.stopPropagation();
if( !confirm('Would you like to remove this video?') ) {
return false;
}
var
playlist_row = $(e.target).parents('[data-index]'),
index = playlist_row.attr('data-index'),
id = get_tab(index,'video-files').attr('data-id_video');
deleted_videos.push(id);
playlist_row.remove();
get_tab(index,'video-files').remove();
get_tab(index,'subtitles').remove();
get_tab(index,'cues').remove();
// Keep data-index in order
playlist_index();
// if no playlist item is left, add a new one
// TODO: Some better way? Like do it after the data is saved
if( !jQuery('.fv-player-tab-subtitles [data-playlist-item][data-index]').length ){
// Required, with this it actually saves the change!
playlist_item_add();
if( fv_player_editor_conf.frontend ) {
reset_preview();
playlist_show();
} else {
playlist_item_show(0);
}
}
$doc.trigger('fv_player_editor_item_delete');
return false;
});
/*
* Sort playlist
*/
$('.fv-player-tab-playlist #fv-player-editor-playlist').sortable({
start: function() {
store_rtmp_server = get_field( 'rtmp', get_tab('first','video-files') ).val();
},
update: playlist_sortable_update,
axis: 'y',
handle: '.fv-player-editor-playlist-move-handle',
containment: ".fv-player-tab-playlist"
});
$doc.on('click','.fv-player-editor-playlist-item .fv-player-editor-playlist-move-up, .fv-player-editor-playlist-item .fv-player-editor-playlist-move-down', function(e) {
var button = $(e.target),
item = button.closest('[data-index]');
if( button.hasClass('fv-player-editor-playlist-move-up') ) {
item.fadeOut( 250, function() {
item.insertBefore( item.prev() );
item.fadeIn( 250, playlist_sortable_update );
});
} else {
item.fadeOut( 250, function() {
item.insertAfter( item.next() );
item.fadeIn( 250, playlist_sortable_update );
});
}
});
function playlist_sortable_update() {
$doc.trigger('fv-player-editor-sortable-update');
var new_sort = [];
$('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').each(function(){
var
index = $(this).attr('data-index'),
video_tab_item = get_tab(index,'video-files'),
subtitle_tab_item = get_tab(index,'subtitles');
new_sort.push({
video_tab_item : video_tab_item.clone(),
subtitle_tab_item : subtitle_tab_item.clone()
});
video_tab_item.remove();
subtitle_tab_item.remove();
});
$.each(new_sort, function(k,v) {
$('.fv-player-tab-video-files').append(v.video_tab_item);
$('.fv-player-tab-subtitles').append(v.subtitle_tab_item);
});
get_field( 'rtmp', get_tab('first','video-files') ).val( store_rtmp_server );
playlist_index();
$doc.trigger('fv_player_editor_playlist_sort');
}
/*
* Uploader
*/
var fv_flowplayer_uploader;
var fv_flowplayer_uploader_button;
$doc.on( 'click', '#fv-player-shortcode-editor .components-button.add_media, .fv-player-gutenberg-media', fv_flowplayer_uploader_init );
/**
* In order for Media Library to work we need:
* - set fv_flowplayer_uploader_button
* - add .fv_flowplayer_target to the input field that will be updated
*/
function fv_flowplayer_uploader_init(e) {
e.preventDefault();
fv_flowplayer_uploader_button = jQuery(this);
var el_input = fv_flowplayer_uploader_button.closest('.components-base-control__field').find('[name=' + fv_flowplayer_uploader_button.data('target') + ']');
// Fallback to previous input, used in FV Player Block for the "Settings" sidebar, also called "Inspector"
if ( ! el_input.length ) {
el_input = fv_flowplayer_uploader_button.prev('.components-panel__row').find('input');
}
/**
* Mark the input as target.
*
* If using Gutenberg block .fv_flowplayer_target won't be added anywhere, but it does not matter.
* We update the block properites directly.
*/
if( el_input.length ) {
// Remove target from the previous field
jQuery('.fv_flowplayer_target').removeClass('fv_flowplayer_target' );
el_input.addClass('fv_flowplayer_target');
}
fv_flowplayer_uploader_open();
}
function fv_flowplayer_uploader_open() {
debug_log( 'Opening Media Library...' );
//If the uploader object has already been created, reopen the dialog
if (fv_flowplayer_uploader) {
fv_flowplayer_uploader.open();
return;
}
//Extend the wp.media object
fv_flowplayer_uploader = wp.media.frames.file_frame = wp.media({
title: 'Add Video',
button: {
text: 'Choose'
},
multiple: false
});
fv_flowplayer_uploader.on('open', function() {
$( document ).trigger( "mediaBrowserOpen" );
jQuery('.media-router .media-menu-item').eq(0).trigger('click');
jQuery('.media-frame-title h1').text(fv_flowplayer_uploader_button.text());
// Hide Media Library tabs which are not allowed
if( fv_player_editor_conf.library ) {
let libraries = fv_player_editor_conf.library.split(/,/),
library_found = false;
$( '.media-router .media-menu-item' ).each( function( i, el ) {
let found = false;
$( libraries ).each( function( v, library ) {
if( $(el).attr('id').match(library) ) {
found = library_found = true;
$(el).trigger( 'click' );
}
});
if( !found ) {
$(el).hide();
}
});
// At least close the Media Library if the desired that was not found
// TODO: It should not open at all if the required library is not found
if ( ! library_found ) {
debug_log('Libraries '+fv_player_editor_conf.library+' not found!');
fv_flowplayer_uploader.close();
}
}
});
//When a file is selected, grab the URL and set it as the text field's value
fv_flowplayer_uploader.on('select', function() {
var attachment = fv_flowplayer_uploader.state().get('selection').first().toJSON();
var target_element = $('.fv_flowplayer_target');
// Update the HTML input field
if ( target_element.length ) {
target_element.val(attachment.url).trigger('change').trigger('keyup');
target_element.removeClass('fv_flowplayer_target' );
}
// Did we open the media library from within the block?
if ( fv_flowplayer_uploader_button.hasClass( 'fv-player-gutenberg-media' ) ) {
var clientId = null;
if ( typeof wp !== 'undefined' && wp.data && wp.data.select( 'core/block-editor' ) ) {
clientId = wp.data.select( 'core/block-editor' ).getSelectedBlockClientId();
}
if ( ! clientId ) {
clientId = jQuery('.is-selected[data-type="fv-player-gutenberg/basic"]').data('block');
}
if ( ! clientId ) {
clientId = jQuery('iframe[name="editor-canvas"], iframe.editor-canvas__iframe, iframe.edit-site-visual-editor__editor-canvas').contents().find('.is-selected[data-type="fv-player-gutenberg/basic"]').data('block');
}
if ( clientId ) {
debug_log( 'Media library updating block attributes.' );
wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes(clientId, { src: attachment.url });
}
}
if( attachment.type == 'video' ) {
// TODO: Fill video title
} else if( attachment.type == 'image' ) {
if( attachment.id ) {
// update splash attachent id
get_field( 'splash_attachment_id', true ).val(attachment.id);
}
if( typeof(fv_flowplayer_set_post_thumbnail_id) != "undefined" ) {
if( jQuery('#remove-post-thumbnail').length > 0 ) {
return;
}
debug_log('Running set-post-thumbnail Ajax.');
jQuery.post(ajaxurl, {
action:"set-post-thumbnail",
post_id: fv_flowplayer_set_post_thumbnail_id,
thumbnail_id: attachment.id,
_ajax_nonce: fv_flowplayer_set_post_thumbnail_nonce,
cookie: encodeURIComponent(document.cookie)
}, function(str){
if ( str == '0' ) {
alert( setPostThumbnailL10n.error );
} else {
jQuery('#postimagediv .inside').html(str);
jQuery('#postimagediv .inside #plupload-upload-ui').hide();
}
} );
}
}
});
//Open the uploader dialog
fv_flowplayer_uploader.open();
}
template_playlist_item = jQuery('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').parent().html();
template_video = get_tab('first','video-files').parent().html();
template_subtitles_tab = jQuery('.fv-player-tab-subtitles').html();
/*
* End of playlist Actions
*/
jQuery('#fv_wp_flowplayer_field_end_actions').on( 'change', show_end_actions );
/*
* Preview iframe dialog resize
*/
$doc.on('fvp-preview-complete',function() {
$el_preview.attr('class','preview-show');
// If editor was in the intro mode, we show the playlist and enable the full-editor
if( $el_editor.hasClass('is-intro') ) {
playlist_show();
$el_editor.removeClass('is-intro');
$('[name=hero-src]').val('');
}
// If editor was in the frontend mode and adding playlist item we hide the playlist hero item now
// and instead show the playlist item row
if( $( '#playlist-hero:visible' ).length ) {
playlist_hero_hide();
}
});
/*
* Video share option
*/
// TODO: Check
jQuery('#fv_wp_flowplayer_field_share').on( 'change', function(){
var value = jQuery(this).val();
switch(value){
case 'Custom':
jQuery("#fv_wp_flowplayer_field_share_custom").show();
break;
default:
jQuery("#fv_wp_flowplayer_field_share_custom").hide();
break;
}
});
$doc.on("change", "#fv-player-shortcode-editor-right input, #fv-player-shortcode-editor-right select", save );
$doc.on("keyup", "#fv-player-shortcode-editor-right input[type=number], #fv-player-shortcode-editor-right input[type=text], #fv-player-shortcode-editor-right textarea", function(e) {
clearTimeout(int_keyup);
int_keyup = setTimeout( function() {
save(e);
}, fv_player_editor_conf.keyup_throttle );
});
$doc.on('fv_flowplayer_shortcode_new fv-player-editor-non-db-shortcode', function() {
insert_button_toggle(true);
copy_player_button_toggle(true);
ajax_saving = false;
});
$doc.on('fv_player_editor_player_loaded', function() {
ajax_saving = false;
is_unsaved = false;
});
$doc.on('fv_flowplayer_player_editor_reset', function() {
ajax_saving = true;
is_unsaved = true;
has_draft_status = true;
//is_draft_changed = false;
});
$doc.on('fv_player_editor_playlist_sort', save );
$doc.on('fv_player_editor_item_delete', save );
$doc.on('fv_player_editor_language_delete', save );
/*
* @param {object} [e] The event handle is invoked by input change event
*/
function save(e) {
// "ajax_saving" is implicitly set to true to make sure we wait with any saving until
// all existing player's data are loaded and filled into inputs
if ( ajax_saving ) {
return;
}
// Skips the interface toggles (Advanced Settings, RTMP)
if( e && $(e.target).hasClass('no-data') ) {
return;
}
if( e && $(e.target).data('no-reload') ) {
prevent_reload_for_current_save = true;
} else {
prevent_reload_for_current_save = false;
}
var
ajax_data = build_ajax_data(true),
db_data_loading = true;
for ( var item in ajax_data.videos ) {
// we have video data already loaded from DB,
// ... this is the only way to see if we actually have
// at least a single object key while preserving
// browsers compatibility for browsers without support
// for Object.keys()
db_data_loading = false;
break;
}
if ( db_data_loading || !ajax_data.videos ) {
// editing a player and still waiting for the videos to load from DB
return;
}
if( ajax_save_previous && JSON.stringify(ajax_data) == JSON.stringify(ajax_save_previous) ) {
debug_log('No changes to save.');
return;
}
if( is_saving ) {
debug_log('Still saving!');
next = true;
return;
}
ajax_save_previous = ajax_data;
ajax(ajax_data);
}
function ajax( data ) {
ajax_save_this_please = data;
}
function error(msg) {
is_saving = false;
$el_editor.find('.button-primary').prop('disabled', false);
overlay_show('message', 'An unexpected error has occurred. Please try again. '+msg, true );
}
setInterval( function() {
if ( !ajax_save_this_please || is_loading_meta_data ) return;
// show error overlay if we have errors
var err = has_errors();
if ( err ) {
add_notice( 'error', err );
return;
}
var what_is_saving = ajax_save_this_please;
is_saving = true;
insert_button_toggle_disabled(true);
el_spinner.show();
remove_notices();
ajax_save_this_please = set_control_fields( ajax_save_this_please );
debug_log('Running fv_player_db_save Ajax.', ajax_save_this_please.update );
let time_start_save = performance.now();
$.ajax({
type: 'POST',
url: ajaxurl + '?action=fv_player_db_save',
data: JSON.stringify( {
data: ajax_save_this_please,
nonce: fv_player_editor_conf.edit_nonce,
} ),
contentType: 'application/json',
dataType: 'json',
success: function(response) {
debug_log( 'Finished fv_player_db_save Ajax in ' + debug_time( time_start_save ) + '.' );
if( response.error ) {
if( response.fatal_error ) {
debug_log( 'Fatal error in fv_player_db_save: ' + response.fatal_error );
var json_export_data = jQuery('').text(JSON.stringify(what_is_saving)).html();
var overlay = overlay_show('error_saving');
overlay.find('textarea').val( response.error + '\n\nJSON data:\n\n' + $('').text(json_export_data).html() );
jQuery('#fv_player_copy_to_clipboard').select();
} else {
add_notice( 'error', response.error );
debug_log( 'Error in fv_player_db_save: ' + response.error );
}
el_spinner.hide();
is_saving = false;
return;
}
let time_start_display = performance.now();
debug_log('player ID after save: '+response.id);
current_player_object = response;
current_player_db_id = response.id;
try {
$(response.videos).each( function(k,v) {
var item = $('.fv-player-playlist-item').eq(k);
if( !item.data('id_video') ) {
item.attr('data-id_video',v.id);
}
if( k == current_video_to_edit ) {
debug_log('current_video_db_id after save: '+v.id);
current_video_db_id = v.id;
}
/*
* The video saving might fetch the video duration, splash screen and title...
* So we fill that in
*/
// TODO: Populate chapters, error, is_live, is_audio, encryption key
var video_tab = get_tab( k, 'video-files' ),
subtitles_tab = get_tab( k, 'subtitles' ),
splash_field = get_field( 'splash', video_tab ),
splash_attachment_id_field = get_field( 'splash_attachment_id', video_tab ),
title_field = get_field( 'title', video_tab ),
auto_splash = get_playlist_video_meta_value( 'auto_splash', k ),
auto_caption = get_playlist_video_meta_value( 'auto_caption', k );
if( get_field('auto_splash', video_tab ).val() == '0' ) {
auto_splash = false;
}
if( get_field('auto_caption', video_tab ).val() == '0' ) {
auto_caption = false;
}
if( v.splash && ( !splash_field.val() || auto_splash ) ) {
splash_field.val( v.splash );
if( v.splash_attachment_id && !splash_attachment_id_field.val() ) {
splash_attachment_id_field.val( v.splash_attachment_id )
}
}
// Populate video meta fields for which the video checking on video save has added value
Object.keys( window.fv_player_editor_fields ).forEach( function( field_name ) {
if ( 'video_meta' == window.fv_player_editor_fields[ field_name].store ) {
let field_value = get_playlist_video_meta_value( field_name, k );
if( field_value ) {
if ( !get_field( field_name, video_tab ).val() ) {
get_field( field_name, video_tab ).val( field_value );
}
if ( !get_field( field_name, subtitles_tab ).val() ) {
get_field( field_name, subtitles_tab ).val( field_value );
}
}
}
} );
if( auto_splash ) {
get_field('auto_splash', video_tab ).val( auto_splash );
}
if( v.title && ( !title_field.val() || auto_caption ) ) {
title_field.val( v.title );
text_and_select_worker( title_field, title_field.closest('.fv-player-editor-children-wrap'), 'title', title_field.parents( '.fv-player-editor-field-wrap-title' ) );
}
if( auto_caption ) {
get_field('auto_caption', video_tab ).val( auto_caption );
}
// The Ajax save can give us some video details and it detects stream type so we refresh that information here if we are editing that video
if( current_video_to_edit == k ) {
show_video_details(k);
show_stream_fields_worker(k);
show_playlist_not_supported(k);
}
});
// If we are in playlist view, we refresh the list too
if( current_video_to_edit == -1 ) {
playlist_refresh();
}
/**
* Allow plugins to fill in the hidden fields with data coming back from save response.
* These are the fields which hold ID of any additional items created, like PPV product IDs.
* Their IDs need to be taken into consideration right away as otherwise the PPV products might multiple
* if you make more changes to the price and save again before the initial save is done.
*/
$doc.trigger( 'fv_flowplayer_player_meta_load_high_priority', [ response ] );
debug_log( 'Finished populating editor fields for ' + response.videos.length + ' videos in ' + debug_time( time_start_display ) + '.' );
// Did the data change while saving?
if( next ) {
debug_log('There is more to save...');
if ( is_unsaved ) {
init_saved_player_fields();
}
ajax( build_ajax_data(true) );
next = false;
} else {
is_saving = false;
insert_button_toggle_disabled(false);
el_spinner.hide();
add_notice( 'success', 'Saved!', 2500 );
// close the overlay, if we're waiting for the save
if (overlay_close_waiting_for_save) {
// add this player's ID into players that no longer need an edit lock
if (current_player_db_id > 0) {
edit_lock_removal[current_player_db_id] = 1;
}
overlay_close_waiting_for_save = false;
$.fn.fv_player_box.close();
} else if ( typeof( response.html ) != "undefined" && !prevent_reload_for_current_save ) {
if( response.html ) {
// auto-refresh preview
el_preview_target.html( response.html );
$doc.trigger('fvp-preview-complete');
} else {
reset_preview();
}
}
prevent_reload_for_current_save = false;
// if we're creating a new player, hide the Save / Insert button and
// add all the data and inputs to page that we need for an existing player
if ( is_unsaved ) {
copy_player_button_toggle(false);
init_saved_player_fields();
is_unsaved = false;
//is_draft_changed = false;
ajax_saving = false;
ajax_save_this_please = false;
}
// Output the shortcode into the pre-configured output field
if( fv_player_editor_conf.field_selector ){
insert_shortcode( '[fvplayer id="'+current_player_db_id+'"]');
}
// Allow plugins to fill in the field with data coming back from save response
$doc.trigger( 'fv_flowplayer_player_meta_load', [ response ] );
// Set the current data as previous to let auto-saving detect changes
// For new player this will have video and player IDs
ajax_save_previous = build_ajax_data(true);
}
} catch(e) {
error(e);
}
},
error: function( jqXHR, textStatus, errorThrown) {
add_notice( 'error', '
' + v.post_title + '' + type + status + taxonomies_and_terms + '
')
} );
attachment_details.find('.posts-list').html('').append( ul );
attachment_details.show();
$( '#fv-player-editor-copy_player-overlay .button-primary' ).prop( 'disabled', false );
});
$doc.on('click', '#fv-player-editor-copy_player-overlay .button-primary', function() {
$el_editor.find('.button-primary').text('Insert');
let selected = $( '#fv-player-editor-copy_player-overlay .attachment.selected' );
if( selected.length > 0 ) {
editor_open( selected.data('details').id );
}
});
$doc.on('click', '.fv_player_field_insert-button', function() {
if (is_saving || ajax_save_this_please) {
// for some reason, clicking on already-disabled primary button re-enables it,
// so we'll just need to disable it again here
insert_button_toggle_disabled(true);
} else {
// make sure we mark this player as published in the DB
has_draft_status = false;
editor_submit();
}
});
$doc.on('click', '.playlist_add', function() {
// If it's front-end mode do not add new item instantly, but show input and a button
if( fv_player_editor_conf.frontend ) {
console.log( 'is_playlist_hero_editing', is_playlist_hero_editing );
if( is_playlist_hero_editing ) {
$('#playlist-hero .fv-player-editor-notice.notice-use-ui').show();
} else {
playlist_hero_show();
}
} else {
playlist_item_add();
}
return false;
});
$doc.on('click', '.playlist_edit', function() {
if ( !jQuery(this).hasClass('disabled') ) {
playlist_show();
reload_preview( current_video_to_edit );
}
return false;
});
// prevent closing of the overlay if we have unsaved data
// unfortunately there is no event for this which we could use
// TODO: Now we can do this propery since we dropped Colorbox!
$.fn.fv_player_box.oldClose = $.fn.fv_player_box.close;
$.fn.fv_player_box.close = function() {
// don't close editor if we have errors showing, otherwise we'd just overlay them by an infinite loader
if ( has_errors() ) {
return;
}
/*if (is_draft && is_draft_changed && !window.confirm('You have unsaved changes. Are you sure you want to close this dialog and loose them?')) {
return false;
}*/
// TODO: Why !is_unsaved?
if ( ( !is_unsaved || is_saving ) && has_draft_status && !is_fv_player_screen( editor_button_clicked ) ) {
// TODO: change into notification bubble
alert('Your new player was saved as a draft. To reopen it, open the editor again and use the "Pick existing player" button.');
}
// prevent closing if we're still saving the data
if ( ajax_save_this_please || is_saving || is_loading_meta_data ) {
// if we already have the overlay changed, bail out
if (overlay_close_waiting_for_save) {
return;
}
overlay_close_waiting_for_save = true;
$('.fv-wp-flowplayer-notice-small, .fv-player-shortcode-editor-small-spinner').hide();
overlay_show('loading');
// call fv_wp_flowplayer_submit() which will create a repeating task
// that will check for all meta data being loaded,
// so we can auto-close this overlay once that's done
editor_submit();
return;
}
// close the dialog if confirmed
// $.fn.fv_player_box.oldClose();
// reset variables
is_unsaved = true;
has_draft_status = true;
//is_draft_changed = false;
// manually invoke a heartbeat to remove an edit lock immediatelly
if (current_player_db_id > -1 && window.wp && wp.heartbeat && wp.heartbeat.connectNow ) {
// do this asynchronously to allow our cleanup procedures set lock removal data for the next hearbeat
setTimeout(wp.heartbeat.connectNow, 500);
}
}
/*
Loads and displays a list of all players in a dropdown.
*/
$doc.on('click', '.copy_player', function() {
// show loader
overlay_show('loading');
debug_log('Running fv_player_db_retrieve_all_players_for_dropdown Ajax.');
$.post(ajaxurl, {
action: 'fv_player_db_retrieve_all_players_for_dropdown',
nonce: fv_player_editor_conf.search_nonce,
}, show_players ).fail( function ( jqXHR, textStatus, errorThrown ) {
overlay_show('message', 'An unexpected error has occurred while loading players: ' + errorThrown + '
Please try again.');
});
return false;
});
let do_search = false,
is_searching = false,
search_val = false;
$doc.on('keyup', '#fv-player-editor-copy_player-overlay [name=players_selector]', function() {
search_val = $(this).val();
do_search = true;
});
setInterval( function() {
if( do_search ) {
if( is_searching ) {
// Try again later
return;
}
$.post(ajaxurl, {
action: 'fv_player_db_retrieve_all_players_for_dropdown',
nonce: fv_player_editor_conf.search_nonce,
search: search_val
}, show_players ).fail( function ( jqXHR, textStatus, errorThrown ) {
overlay_show('message', 'An unexpected error has occurred while searching in player data: ' + errorThrown + '
Please try again.');
});
is_searching = true;
do_search = false;
}
}, 1000 );
function show_players(json_data) {
if( !json_data.success ) {
overlay_show('message', json_data.data );
return;
}
is_searching = false;
let overlay = overlay_show('copy_player'),
list = overlay.find('.attachments'),
button = overlay.find('.button-primary');
button.prop( 'disabled', true );
list.html('');
for (var i in json_data.players) {
let data = json_data.players[i],
image = $( data.thumbs[0] ).find('img'),
player_name = data.player_name || data.video_titles.join( ', ' );
image.removeAttr('width');
let item = $('
');
item.find('.thumbnail').append( image );
item.find('.thumbnail .filename > div').html( player_name );
if( image.length == 0 ) {
item.find('.thumbnail .filename').removeClass('hidden');
}
item.attr( 'data-details', JSON.stringify( data ) );
list.append( item );
}
show_players_resize();
}
$( window ).on( 'resize', show_players_resize );
function show_players_resize() {
let player_browser = $( '#fv-player-editor-copy_player-overlay .attachments-browser'),
player_browser_list = player_browser.find( '.attachments' ),
idealColumnWidth = $( window ).width() < 640 ? 135 : 150;
player_browser.attr( 'data-columns', Math.min( Math.round( player_browser_list.width() / idealColumnWidth ), 12 ) || 1 );
}
});
/**
* Initializes shortcode, removes playlist items, hides elements, figures out
* which actual field is edited - post editor, widget, etc.
*/
function editor_init() {
// if error / message overlay is visible, hide it
overlay_hide();
remove_notices();
jQuery('#fv_wp_flowplayer_field_player_name').show();
jQuery('#player_id_top_text').html('');
/**
* Get the active post editor instance
*/
// is there a Custom Video field or Gutenberg field next to the button?
var field = $(editor_button_clicked).parents('.fv-player-editor-wrapper, .fv-player-gutenberg').find('.fv-player-editor-field'),
elementor_field = $(editor_button_clicked).closest( '#elementor-controls' ).find( '[data-setting="shortcode"]' ),
widget = jQuery('#widget-widget_fvplayer-'+widget_id+'-text');
if( fv_player_editor_conf.field_selector ){
var custom_field_selector = jQuery(fv_player_editor_conf.field_selector)
// If the pre-configured field was not failed it's a big deal!
if( !custom_field_selector.length ){
alert( 'FV Player Editor: Field '+fv_player_editor_conf.field_selector+' not found!' );
}
editor_content = custom_field_selector.val();
// No need for insert button
insert_button_toggle(false);
} else if( field.length ) {
if (field[0].tagName != 'TEXTAREA' && !field.hasClass('attachement-shortcode')) {
field = field.find('textarea').first();
}
editor_content = jQuery(field).val();
} else if( elementor_field.length ) {
editor_content = elementor_field.val();
} else if( widget.length ){
editor_content = widget.val();
} else if( document.querySelector('.CodeMirror') && typeof(CodeMirror) !== 'undefined' ) {
instance_code_mirror = document.querySelector('.CodeMirror').CodeMirror;
} else if( typeof(FCKeditorAPI) == 'undefined' && jQuery('#content:not([aria-hidden=true])').length){
editor_content = jQuery('#content:not([aria-hidden=true])').val();
} else if( typeof tinymce !== 'undefined' && typeof tinymce.majorVersion !== 'undefined' && typeof tinymce.activeEditor !== 'undefined' && tinymce.majorVersion >= 4 ){
instance_tinymce = tinymce.activeEditor;
} else if( typeof tinyMCE !== 'undefined' ) {
instance_tinymce = tinyMCE.getInstanceById('content');
} else if(typeof(FCKeditorAPI) !== 'undefined' ){
instance_fp_wysiwyg = FCKeditorAPI.GetInstance('content');
}
reset_preview();
jQuery('.fv-player-tab-video-files [data-playlist-item]').each( function(i,e) {
if( i == 0 ) return;
jQuery(e).remove();
} );
jQuery('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').each( function(i,e) {
if( i == 0 ) return;
jQuery(e).remove();
} );
jQuery('.fv-player-tab-subtitles').html(template_subtitles_tab);
jQuery('.fv_wp_flowplayer_field_subtitles_lang').val(0);
/**
* TABS
*/
jQuery('#fv-player-shortcode-editor a[data-tab=fv-player-tab-playlist]').hide();
jQuery('#fv-player-shortcode-editor a[data-tab=fv-player-tab-video-files]').trigger('click');
jQuery('.nav-tab').show;
reset_editor_ids();
editing_video_details = true;
$el_editor
.addClass( 'is-singular' )
.addClass( 'is-singular-active' )
.removeClass( 'is-playlist-active' )
.removeClass( 'is-playlist' );
//hide empy tabs hide tabs
jQuery('.fv-player-tab-playlist').hide();
jQuery('.fv-player-playlist-item-title').html('');
jQuery('.fv-player-tab-video-files [data-playlist-item]').show();
jQuery('.playlist_edit').html(jQuery('.playlist_edit').data('create')).removeClass('button-primary').addClass('button');
tabs_refresh();
playlist_buttons_disable(false);
playlist_buttons_toggle(true);
set_embeds('');
el_preview_target.html('');
if( typeof(fv_player_shortcode_editor_ajax) != "undefined" ) {
fv_player_shortcode_editor_ajax.abort();
}
$doc.trigger('fv-player-editor-init');
hide_inputs();
$el_editor.show();
}
/**
* Checks all the input fields and created the JavaScript object.
* Works when saving and also previewing.
*/
function build_ajax_data( give_it_all ) {
var
$tabs = $el_editor.find('.fv-player-tab'),
regex = /((fv_wp_flowplayer_field_|fv_wp_flowplayer_hlskey|fv_player_field_ppv_)[^ ]*)/g,
data = {'video_meta' : {}, 'player_meta' : {}},
end_of_playlist_action = jQuery('#fv_wp_flowplayer_field_end_actions').val(),
single_video_showing = !give_it_all && editing_video_details;
// special processing for end video actions
if (end_of_playlist_action && end_of_playlist_action != 'Nothing') {
switch (end_of_playlist_action) {
case 'redirect':
data['fv_wp_flowplayer_field_end_action_value'] = jQuery('#fv_wp_flowplayer_field_redirect').val();
break;
case 'popup':
data['fv_wp_flowplayer_field_end_action_value'] = jQuery('#fv_wp_flowplayer_field_popup_id').val();
break;
case 'email_list':
data['fv_wp_flowplayer_field_end_action_value'] = jQuery('#fv_wp_flowplayer_field_email_list').val();
break;
}
}
// add playlist name
data['fv_wp_flowplayer_field_player_name'] = jQuery('#fv_wp_flowplayer_field_player_name').val();
// add post ID manually here, as it's a special meta key
insertUpdateOrDeletePlayerMeta({
data: data,
meta_section: 'player',
meta_key: 'post_id',
element: jQuery('#fv_wp_flowplayer_field_post_id')[0],
handle_delete: false
});
// trigger meta data save events, so we get meta data from different
// plugins included as we post
jQuery(document).trigger('fv_flowplayer_player_meta_save', [data, $tabs]);
$tabs.each(function() {
var
$tab = jQuery(this),
is_videos_tab = $tab.hasClass('fv-player-tab-video-files'),
is_subtitles_tab = $tab.hasClass('fv-player-tab-subtitles'),
$input_groups = ((is_videos_tab || is_subtitles_tab) ? $tab.find('[data-playlist-item]') : $tab.find('input, select, textarea')),
save_index = -1;
// prepare video and subtitles data, which are duplicated through their input names
if (is_videos_tab) {
data['videos'] = {};
} else if (is_subtitles_tab) {
data['video_meta']['subtitles'] = {};
data['video_meta']['transcript_src'] = {};
}
// iterate over all tables in tabs
$input_groups.each(function() {
// only videos, subtitles tabs have tables, so we only need to search for their inputs when working with those
var
$inputs = ((is_videos_tab || is_subtitles_tab) ? jQuery(this).find('input, select, textarea').not('.no-data') : jQuery(this).not('.no-data') ),
table_index = jQuery(this).data('index');
save_index++;
$inputs.each(function() {
var
$this = jQuery(this),
m;
while ((m = regex.exec(this.name)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// let plugins update video meta, if applicable
jQuery(document).trigger('fv_flowplayer_video_meta_save', [data, save_index, this]);
// videos tab
if (is_videos_tab) {
if (!data['videos'][save_index]) {
data['videos'][save_index] = {
id: jQuery('.fv-player-playlist-item[data-index=' + table_index + ']').data('id_video')
};
}
// check for a meta field
if (check_for_video_meta_field(m[1])) {
// prepare HLS data, if not prepared yet
if (!data['video_meta']['video']) {
data['video_meta']['video'] = {};
}
if (!data['video_meta']['video'][save_index]) {
data['video_meta']['video'][save_index] = {};
}
insertUpdateOrDeleteVideoMeta({
data: data,
meta_section: 'video',
meta_key: get_field_name(m[1]),
meta_index: save_index,
element: this
});
} else {
data['videos'][save_index][m[1]] = map_input_value($this);
}
}
// subtitles tab, subtitles inputs
else if (is_subtitles_tab) {
// TODO: Adding field in PHP with languages => true should add to this array:
let video_meta_languages = {
subtitles: [],
transcript_src: [],
}
$.each( video_meta_languages, function( k, v ) {
if( $this.attr('name') == 'fv_wp_flowplayer_field_' + k ) {
if (!data['video_meta'][ k ][save_index]) {
data['video_meta'][ k ][save_index] = [];
}
let parent = $this.closest( '.components-base-control__field' ),
lang = parent.find( '[name=fv_wp_flowplayer_field_' + k + '_lang]' );
// jQuery-select the SELECT element when we get an INPUT, since we need to pair them
if ( $this[0].nodeName == 'INPUT') {
data['video_meta'][ k ][save_index].push({
code : lang.val(),
file : $this.val(),
id: parent.data('id_videometa')
});
}
}
});
}
// all other tabs
else {
let player_attribute_value = map_input_value($this);
if (check_for_player_meta_field(m[1])) {
// meta data input
insertUpdateOrDeletePlayerMeta({
data: data,
meta_section: 'player',
meta_key: get_field_name(m[1]),
element: this,
handle_delete: false
});
} else {
// Is there a default value for the setting?
let field = m[1].replace(/fv_wp_flowplayer_field_/, '');
if( typeof(window.fv_player_editor_defaults[field]) != "undefined" ) {
// Is it the same? Do not save.
if( window.fv_player_editor_defaults[field] == this.checked ) {
continue;
// Should it be off while default is on? Save literal false
} else if( window.fv_player_editor_defaults[field] && !this.checked ) {
player_attribute_value = 'false';
}
}
// ordinary player attribute
data[m[1]] = player_attribute_value;
}
}
}
});
});
});
// remove any empty videos, i.e. without a source
// this is used when loading data from DB to avoid previewing an empty video that's in editor by default
if (data['videos']) {
var
data_videos_new = {},
x = 0;
for (var i in data['videos']) {
if (data['videos'][i]['src'] || data['videos'][i]['src1'] || !data['videos'][i]['src2']) {
// if we should show preview of a single video only, add that video here,
// otherwise add all videos here
if (!single_video_showing || x == current_video_to_edit) {
data_videos_new[x++] = data['videos'][i];
} else {
x++;
}
}
}
data['videos'] = data_videos_new;
}
// add player ID and deleted elements for a DB update
data['update'] = current_player_db_id;
data['deleted_videos'] = deleted_videos.join(',');
data['deleted_video_meta'] = deleted_video_meta.join(',');
data['deleted_player_meta'] = deleted_player_meta.join(',');
return data;
}
function debug_log( message, details ) {
store_debug_log.push( message );
console.log( 'FV Player Editor: '+message);
if( details ) {
store_debug_log.push( details );
console.log(details);
}
}
function debug_time( time ) {
return Math.round( 100 * ( performance.now() - time ) / 1000 ) / 100 + ' seconds';
}
/**
* Closing the editor
* * remove hidden tags from post editor
* * updates the wp-admin -> FV Player screen
* * sets data for WordPress Heartbeat to unlock the player
* * calls editor_init() for editor clean-up
*/
function editor_close() {
// don't close editor if we have errors showing, otherwise we'd just overlay them by an infinite loader
if ( has_errors() ) {
return;
}
// remove TinyMCE hidden tags and other similar tags which aids shortcode editing
// to prevent opening the same player over and over
editor_content = editor_content.replace(fv_wp_flowplayer_re_insert,'');
editor_content = editor_content.replace(fv_wp_flowplayer_re_edit,'');
editor_content = editor_content.replace(/#fvp_placeholder#/, '');
editor_content = editor_content.replace(/#fvp_codemirror_placeholder#/, '');
set_post_editor_content(editor_content);
// this variable needs to be reset here and not in editor_init
set_current_video_to_edit( -1 );
// check if code mirror is active and if so, focus on it & restore cursor position
if( instance_code_mirror && instance_code_mirror_cursor_last ) {
// run after everything else is done
setTimeout(function() {
instance_code_mirror.focus();
instance_code_mirror.setCursor( instance_code_mirror_cursor_last, 0 );
instance_code_mirror_cursor_last = false;
},0);
}
if ( !is_fv_player_screen(editor_button_clicked) ) {
// todo: what it the point of this call being made?
// TODO: Perhaps to ensure the temporary strings in editor are removed?
//set_post_editor_content( editor_content.replace( fv_wp_flowplayer_re_insert, '' ) );
// trigger update for the FV Player Custom Videos/Meta Box and Gutenberg field for preview refresh purposes
var
$editor_button_clicked = $(editor_button_clicked),
$fv_player_custom_meta_box = $editor_button_clicked.parents('.fv-player-editor-wrapper').find('.fv-player-editor-field'),
$fv_player_gutenberg = $editor_button_clicked.parents('.fv-player-gutenberg');
if ( $fv_player_custom_meta_box.length ) {
$fv_player_custom_meta_box.trigger('fv_flowplayer_shortcode_insert');
}
// Update the Gutenberg fields
if( get_current_player_object().videos && get_current_player_object().videos[0] && fv_player_editor.clientId ) {
var src = get_current_player_object().videos[0].src,
splash = get_current_player_object().videos[0].splash,
title = get_current_player_object().videos[0].title,
timeline_previews = get_playlist_video_meta_value( 'timeline_previews', 0 ),
hlskey = get_playlist_video_meta_value( 'hls_hlskey', 0 );
wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes(fv_player_editor.clientId, { src: src, splash: splash, title: title, timeline_previews: timeline_previews, hls_hlskey: hlskey, player_id: current_player_db_id } );
}
// Refresh the Elementor Widget preview
let elementor_field = $(editor_button_clicked).closest( '#elementor-controls' ).find( '[data-setting="shortcode"]' );
if ( elementor_field.length ) {
elementor_field.trigger( 'input' );
}
} else if( current_player_db_id > 0 ) {
// Append or update player row in wp-admin -> FV Player
if( fv_player_editor_conf.is_fv_player_screen ) {
var playerRow = $('#the-list span[data-player_id="' + current_player_db_id + '"]')
if( playerRow.length == 0 ) {
var firstRow = $('#the-list tr:first'),
newRow = firstRow.clone();
newRow.find('td').html('');
playerRow = newRow.find('td').eq(0);
firstRow.before( newRow )
}
jQuery.post( ajaxurl, {
action : 'fv_player_table_new_row',
nonce : fv_player_editor_conf.table_new_row_nonce,
playerID : current_player_db_id
}, function(response) {
playerRow.closest('tr').replaceWith( $(response).find('#the-list tr') );
});
playerRow.append('
');
// Update the player on the wp-admin -> Posts, Pages or CPT screen
} else if( fv_player_editor_conf.is_edit_posts_screen ) {
// Existing player
let target_el = jQuery('.fv-player-edit[data-player_id='+current_player_db_id+']'),
args = {
action : 'fv_player_edit_posts_cell',
nonce : fv_player_editor_conf.edit_posts_cell_nonce,
playerID : current_player_db_id
};
// New player, load from the new post meta
if ( target_el.length === 0 && current_post_id ) {
target_el = jQuery('.fv-player-edit[data-post-id='+current_post_id+']');
args.post_id = current_post_id;
args.meta_key = current_post_meta_key;
}
target_el.find('.fv_player_splash_list_preview').append('
');
jQuery.post( ajaxurl, args, function(response) {
if ( response ) {
target_el.replaceWith( response );
}
});
}
}
// we need to do this now to make sure Heartbeat gets the correct data
if (current_player_db_id > 0 ){
edit_lock_removal[current_player_db_id] = 1;
current_player_db_id = 0;
debug_log( 'editor_close current_player_db_id = 0' );
}
if ( fv_player_editor_conf.on_close ) {
fv_player_editor_conf.on_close();
}
editor_init();
}
/**
* removes previous values from editor
* fills new values from shortcode
*
* @param {int} selected_player_id Optional, force load of specified player ID using "Pick existing player"
*/
function editor_open( selected_player_id ) {
if( !selected_player_id ) {
editor_init();
}
instance_code_mirror_cursor_last = false;
// remove any DB data IDs that may be left in the form
$el_editor.find('[data-id]').removeData('id').removeAttr('data-id');
$el_editor.find('[data-id_video]').removeData('id_video').removeAttr('data-id_video');
$el_editor.find('[data-id_videometa]').removeData('id_videometa').removeAttr('data-subtitles');
// fire up editor reset event, so plugins can clear up their data IDs as well
$doc.trigger('fv_flowplayer_player_editor_reset');
// reset content of any input fields, except what has .extra-field
$el_editor.find("input:not(.extra-field), textarea").each( function() {
$(this)
.val( '' )
.prop( 'checked', false )
.trigger( 'change' );
} );
$el_editor.find('select:not([multiple])').prop('selectedIndex',0); // select first index, ignore multiselect - let it be handled separately
$el_editor.find(".fv_player_field_insert-button").text( 'Insert' );
if( window.fv_player_editor_defaults ) {
jQuery.each( window.fv_player_editor_defaults, function(k,v) {
var checkbox = $el_editor.find('[name=fv_wp_flowplayer_field_'+k+'][type=checkbox]');
if( checkbox.length ) {
checkbox.prop( 'checked', !!v ).trigger( 'change' );
var wrap = checkbox.closest('.components-form-toggle');
wrap.toggleClass( 'is-checked is-default', !!v );
}
} );
}
var
field = $(editor_button_clicked).parents('.fv-player-editor-wrapper, .fv-player-gutenberg').find('.fv-player-editor-field'),
clientId = $(editor_button_clicked).parents('.fv-player-editor-wrapper, .fv-player-gutenberg').find('.fv-player-gutenberg-client-id').val(),
elementor_field = $(editor_button_clicked).closest( '#elementor-controls' ).find( '[data-setting="shortcode"]' ),
is_elementor = elementor_field.length,
is_gutenberg = $(editor_button_clicked).parents('.fv-player-gutenberg').length,
shortcode = false,
shortcode_parse_fix = false;
if( clientId ) {
fv_player_editor.clientId = clientId;
} else {
fv_player_editor.clientId = null;
}
// if we've got a numeric DB ID passed to this function, use it directly
// but don't replace editor_content, since we'll need that to be actually updated
// rather then set to a player ID
if (selected_player_id) {
debug_log('Loading for player id: '+selected_player_id );
shortcode = 'fvplayer id="' + selected_player_id + '"';
current_post_id = $(editor_button_clicked).data('post-id');
current_post_meta_key = $(editor_button_clicked).data('meta_key');
if( current_post_id || current_post_meta_key ) {
debug_log( 'New player for post #' + current_post_id + ' for ' + current_post_meta_key + ' meta_key.' );
}
} else {
/**
* Load shortcode for the active post editor
*/
if( fv_player_editor_conf.field_selector ){
let custom_field_selector = jQuery(fv_player_editor_conf.field_selector)
// If the pre-configured field was not failed it's a big deal!
if( !custom_field_selector.length ){
alert( 'FV Player Editor: Field '+fv_player_editor_conf.field_selector+' not found!' );
}
editor_content = custom_field_selector.val();
shortcode = editor_content;
// Edit button on wp-admin -> FV Player screen
} else if (is_fv_player_screen_edit(editor_button_clicked)) {
current_player_db_id = $(editor_button_clicked).data('player_id');
current_post_id = $(editor_button_clicked).data('post-id');
current_post_meta_key = $(editor_button_clicked).data('meta_key');
if( current_post_id || current_post_meta_key ) {
debug_log( 'New player for post #' + current_post_id + ' for ' + current_post_meta_key + ' meta_key.' );
editor_content = '';
} else {
debug_log('Loading for FV Player screen, player id: '+current_player_db_id );
// create an artificial shortcode from which we can extract the actual player ID later below
editor_content = '[fvplayer id="' + current_player_db_id + '"]';
shortcode = editor_content;
}
}
// Add new button on wp-admin -> FV Player screen
else if (is_fv_player_screen_add_new(editor_button_clicked)) {
debug_log('Loading for FV Player screen, new player' );
// create empty shortcode for Add New button on the list page
editor_content = '';
shortcode = '';
}
// custom Field or Widget
else if (field.length || jQuery('#widget-widget_fvplayer-' + widget_id + '-text').length) {
debug_log('Loading for custom field or a widget...');
// this is a horrible hack as it adds the hidden marker to the otherwise clean text field value
// just to make sure the shortcode varible below is parsed properly.
// But it allows some extra text to be entered into the text widget, so for now - ok
if (editor_content.match(/\[/)) {
editor_content = '[<' + helper_tag +' rel="FCKFVWPFlowplayerPlaceholder">' + helper_tag + '>' + editor_content.replace('[', '') + '';
} else {
editor_content = '<' + helper_tag + ' rel="FCKFVWPFlowplayerPlaceholder">' + helper_tag + '>' + editor_content + '';
}
} else if ( is_elementor ) {
debug_log( 'Loading for Elementor Widget...', editor_content );
// CodeMirror
} else if( instance_code_mirror ) {
debug_log('Loading for CodeMirror...' );
// get content
editor_content = instance_code_mirror.getDoc().getValue();
// get cursor position
var position = instance_code_mirror.getDoc().getCursor(),
line = position.line,
ch = position.ch,
line_value = instance_code_mirror.getDoc().getLine(line);
// save cursor position
instance_code_mirror_cursor_last = {line: line, ch: ch};
// look for start of shortcode
var shotcode_pattern = new RegExp(/\[fvplayer[^\[\]]*]?/g);
if( shotcode_pattern.test(line_value) ) {
for( var start = ch; start--; start >= 0 ) {
if( line_value[start] == '[' ) {
var sliced_content = line_value.slice(start);
var matched = sliced_content.match(shotcode_pattern);
if( matched ) {
shortcode = matched[0];
}
break;
} else if( line_value[start] == ']' ) {
break
}
}
} else {
// add placeholder for new editor
instance_code_mirror.getDoc().replaceRange('#fvp_codemirror_placeholder#', {line: line, ch: ch}, {line: line, ch: ch});
editor_content = instance_code_mirror.getDoc().getValue();
}
}
// TinyMCE in Text Mode
else if (typeof (FCKeditorAPI) == 'undefined' && jQuery('#content:not([aria-hidden=true])').length) {
debug_log('Loading for TinyMCE in Text Mode...');
var position = jQuery('#content:not([aria-hidden=true])').prop('selectionStart');
// look for start of shortcode
for (var start = position; start--; start >= 0) {
if (editor_content[start] == '[') {
var sliced_content = editor_content.slice(start);
var matched = sliced_content.match(/^\[fvplayer[^\[\]]*]?/);
// found the shortcode!
if (matched) {
shortcode = matched[0];
}
break;
} else if (editor_content[start] == ']') {
break
}
}
// TODO: It would be better to use #fv_player_editor_{random number}# and remember it for the editing session
editor_content = editor_content.slice(0, position) + '#fvp_placeholder#' + editor_content.slice(position);
}
// Foliopress WYSIWYG
else if (
instance_fp_wysiwyg && (
instance_tinymce == undefined || (typeof tinyMCE !== 'undefined' && tinyMCE.activeEditor.isHidden())
)
) {
debug_log('Loading for Foliopress WYSIWYG...' );
editor_content = instance_fp_wysiwyg.GetHTML();
if (editor_content.match(fv_wp_flowplayer_re_insert) == null) {
instance_fp_wysiwyg.InsertHtml('<' + fvwpflowplayer_helper_tag + ' rel="FCKFVWPFlowplayerPlaceholder">' + fvwpflowplayer_helper_tag + '>');
editor_content = instance_fp_wysiwyg.GetHTML();
}
} else if( instance_tinymce ) {
debug_log('Loading for TinyMCE in Visual Mode...' );
// TinyMCE in Visual Mode
editor_content = instance_tinymce.getContent();
instance_tinymce.settings.validate = false;
if (editor_content.match(fv_wp_flowplayer_re_insert) == null) {
var tags = ['b', 'span', 'div'];
for ( let i in tags) {
instance_tinymce.execCommand('mceInsertContent', false, '<' + tags[i] + ' data-mce-bogus="1" rel="FCKFVWPFlowplayerPlaceholder">' + tags[i] + '>');
editor_content = instance_tinymce.getContent();
fv_wp_flowplayer_re_edit = new RegExp('\\[f[^\\]]*?<' + tags[i] + '[^>]*?rel="FCKFVWPFlowplayerPlaceholder"[^>]*?>.*?' + tags[i] + '>.*?[^\]\\]', "mi");
fv_wp_flowplayer_re_insert = new RegExp('<' + tags[i] + '[^>]*?rel="FCKFVWPFlowplayerPlaceholder"[^>]*?>.*?' + tags[i] + '>', "gi");
if (editor_content.match(fv_wp_flowplayer_re_insert)) {
break;
}
}
}
instance_tinymce.settings.validate = true;
} else {
editor_content = '';
}
if( !shortcode ){
let content = editor_content.replace(/\n/g, '\uffff');
if ( is_elementor || is_gutenberg ) {
shortcode = content;
} else {
let match = content.match( fv_wp_flowplayer_re_edit );
if( match ) {
shortcode = match[0];
}
}
}
}
// remove visual editor placeholders etc.
if (shortcode) {
shortcode = shortcode
.replace(/^\[|]+$/gm, '')
.replace(fv_wp_flowplayer_re_insert, '')
.replace(/\\'/g, ''');
}
if( shortcode ) {
debug_log('Loading shortcode: '+shortcode );
// check for new, DB-based player shortcode
var result = /fvplayer.* id="([\d,]+)"/g.exec(shortcode);
if (result !== null) {
shortcode_parse_fix = shortcode
.replace(/(popup|ad)='[^']*?'/g, '')
.replace(/(popup|ad)="(.*?[^\\\\/])"/g, '');
shortcode_remains = shortcode_parse_fix.replace( /^\S+\s*?/, '' );
// DB-based player, create a "wait" overlay
overlay_show('loading');
// store player ID into fv_player_conf, so we can keep sending it
// in WP heartbeat
current_player_db_id = result[1];
debug_log('Loading shortcode player id: '+current_player_db_id );
if (edit_lock_removal[result[1]]) {
delete edit_lock_removal[result[1]];
}
// check if we don't have multiple-playlists shortcode,
// in which case we need to stop and show an error message
if (shortcode.indexOf(',') > -1) {
overlay_show('message', 'Shortcode editor is not available for multiple players shortcode tag.');
return;
}
// stop Ajax saving that might occur from thinking it's a draft taking place
is_unsaved = false;
is_loading = true;
// now load playlist data
// load video data via an AJAX call
debug_log('Running fv_player_db_load Ajax.');
let time_start = performance.now();
fv_player_shortcode_editor_ajax = jQuery.post( ajaxurl + '?fv_player_db_load=' + result[1], {
action : 'fv_player_db_load',
nonce : fv_player_editor_conf.db_load_nonce,
playerID : result[1],
current_video_to_edit: get_playlist_items_count() > 1 ? current_video_to_edit : -1,
}, function(response) {
var vids = response['videos'];
debug_log('Finished fv_player_db_load Ajax in ' + debug_time( time_start ) + '.',response);
if (response) {
if ( response.debug_duplicate_players && response.debug_duplicate_players.length ) {
alert( "FV Player Editor warning:\n\n\
It seems the player #" + result[1] + " which you are about to edit is a duplicate of the player #" + response.debug_duplicate_players.join(', ') + ".\n\n\
Any changes made to the videos will update the other player as well. \
You can also carefully remove the duplicate player by checking which one is actually used in your post.\n\n\
Please also contact FV Player support with the following debug information:\n\n\
" + store_debug_log.join("\n")
);
}
if( response.error ) {
reset_editor_ids();
overlay_show('message', response.error );
// The editor failed to load, it's not locked
edit_lock_removal[current_player_db_id] = 1;
// Prevent autosave notice from appearing
is_unsaved = true;
is_loading = false;
return;
}
let time_start = performance.now();
init_saved_player_fields( result[1] );
// remove everything with index 0 and the initial video placeholder,
// otherwise our indexing & previews wouldn't work correctly
jQuery('[data-index="0"]').remove();
jQuery('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').remove();
jQuery('.fv-player-tab-video-files table').remove();
set_embeds(response['embeds']);
current_player_object = response;
// fire the player load event to cater for any plugins listening
$doc.trigger('fv_flowplayer_player_meta_load', [response]);
for (var key in response) {
// put the field value where it belongs
if (key !== 'videos') {
// in case of meta data, proceed with each player meta one by one
if (key == 'meta') {
for ( let i in response[key]) {
set_editor_field(response[key][i]['meta_key'], response[key][i]['meta_value'], response[key][i]['id']);
}
} else {
set_editor_field(key, response[key]);
}
}
}
let new_video_tabs = [],
new_subtitles_tabs = [];
// add videos from the DB
for (var x in vids) {
let
video_meta_languages = {
subtitles: [],
transcript_src: [],
},
video_meta_to_set = [];
// add all subtitles
if (vids[x].meta && vids[x].meta.length) {
for (var m in vids[x].meta) {
let is_languages_meta = false;
$.each( video_meta_languages, function( k, v ) {
if (vids[x].meta[m].meta_key.indexOf( k ) > -1) {
is_languages_meta = true;
// Map subtitles_en to en and subtitles to just empty string
let lang = vids[x].meta[m].meta_key.replace( k + '_', '');
if ( 'subtitles' === lang ) {
lang = '';
}
video_meta_languages[k].push({
lang: lang,
file: vids[x].meta[m].meta_value,
id: vids[x].meta[m].id
});
}
} );
if ( ! is_languages_meta ) {
video_meta_to_set.push(vids[x].meta[m]);
}
}
}
let new_item = playlist_item_add( vids[x], true );
new_video_tabs.push( new_item.video_tab );
new_subtitles_tabs.push( new_item.subtitles_tab );
$.each( video_meta_languages, function( k, v ) {
for (var i in v) {
language_add( k, v[i].file, v[i].lang, new_item.subtitles_tab, v[i].id);
}
} );
// This sets some additional video meta fields, like remove_black_bars
if (video_meta_to_set.length) {
for ( let i in video_meta_to_set) {
set_editor_field( video_meta_to_set[i].meta_key, video_meta_to_set[i].meta_value, video_meta_to_set[i].id, new_item.video_tab );
}
}
// fire up meta load event for this video, so plugins can process it and react
$doc.trigger('fv_flowplayer_video_meta_load', [ x, vids[x].meta, new_item.video_tab, new_item.subtitles_tab ] );
}
// Add all the new video and subtitle tabs to the DOM all at once
jQuery('.fv-player-tab-video-files').append( new_video_tabs );
jQuery('.fv-player-tab-subtitles').append( new_subtitles_tabs );
debug_log( 'Finished populating editor fields for ' + vids.length + ' videos in ' + debug_time( time_start ) + '.' );
// show playlist instead of the "add new video" form
// if we have more than 1 video
if( current_video_to_edit > -1 ) {
playlist_item_show(current_video_to_edit);
} else if ( vids.length > 1 || fv_player_editor_conf.frontend ) {
playlist_show();
} else {
playlist_item_show(0);
}
// if this player is published, mark it as such
has_draft_status = ( response.status == 'draft' );
// Set the current data as previous to let auto-saving detect changes
ajax_save_previous = build_ajax_data(true);
is_loading = false;
}
overlay_hide();
if ( response.html ) {
// auto-refresh preview
el_preview_target.html( response.html )
$doc.trigger('fvp-preview-complete');
}
// show the Insert button, as this is only used when adding a new player into a post
// and using the Pick existing player button, where we need to be able to actually
// insert the player code into the editor
// ... also, keep the Pick existing player button showing, if we decided to choose
// a different player
if ( selected_player_id ) {
insert_button_toggle(true);
copy_player_button_toggle(true);
} else if ( response.status == 'draft' ) {
// show Save / Insert button, as we're still
// in draft mode for this player
insert_button_toggle(true);
fix_save_btn_text();
} else {
insert_button_toggle(false);
copy_player_button_toggle(false);
}
$doc.trigger('fv_player_editor_player_loaded');
$doc.trigger('fv_player_editor_finished');
}).fail( function( jqXHR, textStatus, errorThrown ) {
if ( jqXHR.status == 404 ) {
overlay_show('message', 'The requested player #' + result[1] + ' could not be found.');
} else {
overlay_show('message', 'An unexpected error has occurred while loading player #' + result[1] + ': ' + errorThrown + '
Please try again.');
}
// show the Insert button, as this is only used when adding a new player into a post
// and using the Pick existing player button, where we need to be able to actually
// insert the player code into the editor
// ... also, keep the Pick existing player button showing, if we decided to choose
// a different player
if ( selected_player_id ) {
insert_button_toggle(true);
copy_player_button_toggle(true);
}
});
} else {
debug_log('Loading shortcode without player id...');
// TODO: Check if all the values are set properly, for example controlbar checkbox is not set properly anymore
$doc.trigger('fv-player-editor-non-db-shortcode');
// ordinary text shortcode in the editor
shortcode_parse_fix = shortcode.replace(/(popup|ad)='[^']*?'/g, '');
shortcode_parse_fix = shortcode_parse_fix.replace(/(popup|ad)="(.*?[^\\\\/])"/g, '');
shortcode_remains = shortcode_parse_fix.replace( /^\S+\s*?/, '' );
var srcurl = shortcode_parse_arg( shortcode_parse_fix, 'src' );
var srcurl1 = shortcode_parse_arg( shortcode, 'src1' );
var srcurl2 = shortcode_parse_arg( shortcode, 'src2' );
var srcrtmp = shortcode_parse_arg( shortcode, 'rtmp' );
var srcrtmp_path = shortcode_parse_arg( shortcode, 'rtmp_path' );
var iwidth = shortcode_parse_arg( shortcode_parse_fix, 'width' );
var iheight = shortcode_parse_arg( shortcode_parse_fix, 'height' );
var sad_skip = shortcode_parse_arg( shortcode, 'ad_skip' );
var scontrolbar = shortcode_parse_arg( shortcode, 'controlbar' );
var sautoplay = shortcode_parse_arg( shortcode, 'autoplay' );
var sliststyle = shortcode_parse_arg( shortcode, 'liststyle' );
var sembed = shortcode_parse_arg( shortcode_parse_fix, 'embed' );
var sloop = shortcode_parse_arg( shortcode, 'loop' );
var slive = shortcode_parse_arg( shortcode, 'live' );
var sshare = shortcode_parse_arg( shortcode, 'share', false, shortcode_share_parse_arg );
var sspeed = shortcode_parse_arg( shortcode, 'speed' );
var ssplash = shortcode_parse_arg( shortcode, 'splash' );
var ssplashend = shortcode_parse_arg( shortcode, 'splashend' );
var ssticky = shortcode_parse_arg( shortcode, 'sticky' );
var splaylist_advance = shortcode_parse_arg( shortcode, 'playlist_advance' );
var ssubtitles = shortcode_parse_arg( shortcode, 'subtitles' );
var aSubtitlesLangs = shortcode.match(/subtitles_[a-z][a-z]+/g);
for( let i in aSubtitlesLangs ){ // move
shortcode_parse_arg( shortcode, aSubtitlesLangs[i], false, shortcode_subtitle_parse_arg );
}
if(!aSubtitlesLangs){ // move
subtitle_language_add(false, false );
}
var smobile = shortcode_parse_arg( shortcode, 'mobile' );
var sredirect = shortcode_parse_arg( shortcode, 'redirect' );
var sCaptions = shortcode_parse_arg( shortcode, 'caption' );
var sSplashText = shortcode_parse_arg( shortcode, 'splash_text' );
var sPlaylist = shortcode_parse_arg( shortcode, 'playlist' );
var sad = shortcode_parse_arg( shortcode, 'ad', true );
var iadwidth = shortcode_parse_arg( shortcode, 'ad_width' );
var iadheight = shortcode_parse_arg( shortcode, 'ad_height' );
// TODO: Test if RTMP is shown
if( srcrtmp != null && srcrtmp[1] != null ) {
jQuery(".fv_wp_flowplayer_field_rtmp").val( srcrtmp[1] );
jQuery(".fv_wp_flowplayer_field_rtmp_wrapper").show();
}
if( srcrtmp_path != null && srcrtmp_path[1] != null ) {
jQuery(".fv_wp_flowplayer_field_rtmp_path").val( srcrtmp_path[1] );
jQuery(".fv_wp_flowplayer_field_rtmp_wrapper").show();
}
var playlist_row = jQuery('.fv-player-tab-playlist tbody tr:first')
if( srcurl != null && srcurl[1] != null ) {
get_field( "src" ).val( srcurl[1] ).trigger('change');
}
if( srcurl1 != null && srcurl1[1] != null ) {
get_field( "src1" ).val( srcurl1[1] ).trigger('change');
if( srcurl2 != null && srcurl2[1] != null ) {
get_field( "src2" ).val( srcurl2[1] ).trigger('change');
}
get_field( 'toggle_advanced_settings' ).prop( 'checked', true ).trigger('change');
}
if( srcurl != null && srcurl[1] != null ) {
get_field( "src" ).val( srcurl[1] );
playlist_row.find('.fvp_item_video-filename').text( srcurl[1] );
}
get_field('width').val(iwidth[1] || '');
get_field('height').val(iheight[1] || '');
if( sautoplay != null && sautoplay[1] != null ) {
if (sautoplay[1] == 'true')
get_field("autoplay")[0].selectedIndex = 1;
if (sautoplay[1] == 'false')
get_field("autoplay")[0].selectedIndex = 2;
if (sautoplay[1] == 'muted')
get_field("autoplay")[0].selectedIndex = 3;
}
if( sliststyle != null && sliststyle[1] != null ) {
var objPlaylistStyle = get_field("playlist")[0];
if (sliststyle[1] == 'horizontal') objPlaylistStyle.selectedIndex = 1;
if (sliststyle[1] == 'tabs') objPlaylistStyle.selectedIndex = 2;
if (sliststyle[1] == 'prevnext') objPlaylistStyle.selectedIndex = 3;
if (sliststyle[1] == 'vertical') objPlaylistStyle.selectedIndex = 4;
if (sliststyle[1] == 'slider') objPlaylistStyle.selectedIndex = 5;
if (sliststyle[1] == 'season') objPlaylistStyle.selectedIndex = 6;
if (sliststyle[1] == 'polaroid') objPlaylistStyle.selectedIndex = 7;
if (sliststyle[1] == 'text') objPlaylistStyle.selectedIndex = 8;
}
if( sembed != null && sembed[1] != null ) {
if (sembed[1] == 'true')
get_field("embed")[0].selectedIndex = 1;
if (sembed[1] == 'false')
get_field("embed")[0].selectedIndex = 2;
}
if( smobile != null && smobile[1] != null )
get_field("mobile").val(smobile[1]);
if( ssplash != null && ssplash[1] != null ) {
get_field("splash").val( ssplash[1] ).trigger('change');
var playlist_img = jQuery('')
.attr('width', 120 )
.attr('src', ssplash[1] );
playlist_row.find('.fvp_item_splash').html( playlist_img );
}
var aSubtitles = false;
if( ssubtitles != null && ssubtitles[1] != null ) {
aSubtitles = ssubtitles[1].split(';');
get_field("subtitles").eq(0).val( aSubtitles[0] );
aSubtitles.shift(); // the first item is no longer needed for playlist parsing which will follow
}
if( ssticky != null && ssticky[1] != null ) {
if (ssticky[1] == 'true')
get_field("sticky")[0].selectedIndex = 1;
if (ssticky[1] == 'false')
get_field("sticky")[0].selectedIndex = 2;
}
if( sad != null && sad[1] != null ) {
sad = sad[1].replace(/'/g,'\'').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
sad = sad.replace(/&/g,'&');
get_field("overlay").val(sad);
get_field( 'toggle_overlay' ).prop('checked', true).trigger('change');
}
if( iadheight != null && iadheight[1] != null )
get_field("overlay_height").val(iadheight[1]);
if( iadwidth != null && iadwidth[1] != null )
get_field("overlay_width").val(iadwidth[1]);
if( sad_skip != null && sad_skip[1] != null && sad_skip[1] == 'yes' )
get_field("overlay_skip").prop('checked',true).trigger('change');
if( sspeed != null && sspeed[1] != null ) {
if (sspeed[1] == 'buttons')
get_field("speed")[0].selectedIndex = 1;
if (sspeed[1] == 'no')
get_field("speed")[0].selectedIndex = 2;
}
/*
if( ssplashend != null && ssplashend[1] != null && ssplashend[1] == 'show' )
document.getElementById("fv_wp_flowplayer_field_splashend").checked = 1;
if( sloop != null && sloop[1] != null && sloop[1] == 'true' )
document.getElementById("fv_wp_flowplayer_field_loop").checked = 1;
if( sredirect != null && sredirect[1] != null )
document.getElementById("fv_wp_flowplayer_field_redirect").value = sredirect[1];
*/
if( sSplashText != null && sSplashText[1] != null ) {
get_field("splash_text").val(sSplashText[1]);
}
/*
* Video end dropdown
*/
//TODO: shortcode popup
// get_field("popup")[0].parentNode.style.display = 'none'
var spopup = shortcode_parse_arg( shortcode, 'popup', true );
let end_actions_val = false;
if( sredirect != null && sredirect[1] != null ){
end_actions_val = 2;
get_field("redirect").val( sredirect[1] );
} else if( sloop != null && sloop[1] != null && sloop[1] == 'true' ){
end_actions_val = 3;
} else if( spopup != null && spopup[1] != null ) {
end_actions_val = 4;
spopup = spopup[1].replace(/'/g,'\'').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
spopup = spopup.replace(/&/g,'&');
if( spopup.match(/email-[0-9]*/)){
end_actions_val = 6;
get_field("email_list").val(spopup.match(/email-([0-9]*)/)[1]);
} else {
get_field("popup_id").val(spopup);
}
} else if( ssplashend != null && ssplashend[1] != null && ssplashend[1] == 'show' ){
end_actions_val = 5;
}
if( end_actions_val ) {
get_field("end_actions").prop( 'selectedIndex', end_actions_val ).trigger('change');
get_field( 'toggle_end_action' ).prop('checked', true).trigger('change');
}
if( splaylist_advance != null && splaylist_advance[1] != null ) {
let field = get_field("playlist_advance")[0];
if (splaylist_advance[1] == 'true') field.selectedIndex = 1;
if (splaylist_advance[1] == 'false') field.selectedIndex = 2;
}
if( scontrolbar != null && scontrolbar[1] != null ) {
let field = get_field("controlbar")[0];
if (scontrolbar[1] == 'yes' || scontrolbar[1] == 'show' ) field.selectedIndex = 1;
if (scontrolbar[1] == 'no' || scontrolbar[1] == 'hide' ) field.selectedIndex = 2;
}
var aCaptions = false;
if( sCaptions ) {
aCaptions = shortcode_arg_split(sCaptions);
var caption = aCaptions.shift();
get_field("title").val( caption );
if( caption ) {
playlist_row.find('.fvp_item_video-filename').text( caption );
}
}
var aSplashText = false;
if( sSplashText ) {
aSplashText = shortcode_arg_split(sSplashText);
var splash_text = aSplashText.shift();
get_field("splash_text").val( splash_text );
}
if( sPlaylist ) {
// check for all-numeric playlist items separated by commas
// which outlines video IDs from a database
var aPlaylist = sPlaylist[1].split(';');
for ( let i in aPlaylist) {
// TODO: Will this work?
playlist_item_add( {
src: aPlaylist[i],
title: aCaptions[i],
subtitles: aSubtitles[i],
splash_text: aSplashText[i]
} );
}
}
if( jQuery('.fv-fp-subtitles .fv-fp-subtitle:first input.fv_wp_flowplayer_field_subtitles').val() == '' ) {
jQuery('.fv-fp-subtitles .fv-fp-subtitle:first').remove();
}
try {
jQuery(document).trigger('fv_flowplayer_shortcode_parse', [ shortcode_parse_fix, shortcode_remains ] );
} catch(e) {
debug_log('Error: fv_flowplayer_shortcode_parse', e);
}
if (slive != null && slive[1] != null && slive[1] == 'true') {
jQuery("input[name=fv_wp_flowplayer_field_live]").each(function () {
this.checked = 1;
jQuery(this).closest('tr').show();
});
}
if(sPlaylist){
playlist_show();
} else {
playlist_item_show(0);
}
tabs_refresh();
}
store_shortcode_args = {};
always_keep_shortcode_args = {};
for( let i in fv_player_editor_conf.shortcode_args_to_preserve ) {
let value = shortcode_parse_arg( shortcode_parse_fix, fv_player_editor_conf.shortcode_args_to_preserve[i] );
if (value && value[1]) {
store_shortcode_args[fv_player_editor_conf.shortcode_args_to_preserve[i]] = value[1];
}
}
if( store_shortcode_args.length ) {
debug_log('Preserving shortcode args', store_shortcode_args );
}
for( let i in fv_player_editor_conf.shortcode_args_not_db_compatible ) {
let value = shortcode_parse_arg( shortcode_parse_fix, fv_player_editor_conf.shortcode_args_not_db_compatible[i] );
if (value && value[1]) {
always_keep_shortcode_args[fv_player_editor_conf.shortcode_args_not_db_compatible[i]] = value[1];
}
}
if( always_keep_shortcode_args.length ) {
debug_log('Always preserve shortcode args', always_keep_shortcode_args );
}
} else {
debug_log('New player...' );
playlist_item_show(0);
jQuery(document).trigger('fv_flowplayer_shortcode_new');
shortcode_remains = '';
fix_save_btn_text();
}
$doc.trigger('fv_player_editor_finished');
}
/**
* Makes sure that the Save / Insert button is showing the correct text
* based on current context.
*/
function fix_save_btn_text() {
// rename insert to save for new playlists if we come from list view
if ( is_fv_player_screen_add_new( editor_button_clicked ) || is_fv_player_screen_edit( editor_button_clicked ) ) {
jQuery('.fv_player_field_insert-button').text('Save');
} else {
// we're in a post / widget, rename save button to Insert
jQuery('.fv_player_field_insert-button').text('Insert');
}
}
/*
* Saving the data
*/
function editor_submit() {
// bail out if we're already saving, we're loading meta data or we have errors
if ( ajax_save_this_please || is_saving || is_loading_meta_data || has_errors() ) {
// if we're saving a new player, let's disable the Save button and wait until meta data are loaded
if ( current_player_db_id < 0 ) {
if (is_loading_meta_data) {
insert_button_toggle_disabled(true);
$el_editor.find('.button-primary').text('Saving...');
var checker = setInterval(function() {
if (is_loading_meta_data <= 0) {
clearInterval(checker);
editor_submit(); // call this function again, so we can really save this time
}
}, 500);
return;
}
} else {
// if we're updating a player, just return here if we're loading meta data,
// as that would result in duplicate save - once with and once without meta data
if (is_loading_meta_data) {
return;
}
}
// we have errors, disable the Save button
if ( has_errors() ) {
insert_button_toggle_disabled(true);
return;
}
}
// TODO: Tell people they only have RTMP
var field_rtmp = get_field("rtmp"),
field_rtmp_path = get_field("rtmp_path")
if(
field_rtmp.attr('placeholder') == '' &&
get_field("rtmp_wrapper").is(":visible") &&
(
( field_rtmp.val() != '' && field_rtmp_path.val() == '' ) ||
( field_rtmp.val() == '' && field_rtmp_path.val() != '' )
)
) {
alert('Please enter both server and path for your RTMP video.');
return false;
} else if(
get_field("src").val() == ''
&& get_field("rtmp").val() == ''
&& get_field("rtmp_path").val() == '') {
alert('Please enter the file name of your video file.');
return false;
}
var ajax_data = build_ajax_data(true);
overlay_show('loading');
var player_was_non_db = (current_player_db_id > -1 ? false : true);
// unmark DB player ID as being currently edited
if ( current_player_db_id > 0 ) {
current_player_db_id = 0;
}
// if player should be in published state, add it into the AJAX data
if ( !has_draft_status ) {
ajax_data['status'] = 'published';
}
debug_log('Running fv_player_db_save Ajax on submit.');
ajax_data = set_control_fields( ajax_data );
let time_start = performance.now();
// save data
// We use ?fv_player_db_save=1 as some people use that to exclude firewall rules
$.ajax({
type: 'POST',
url: ajaxurl + '?action=fv_player_db_save',
data: JSON.stringify( {
data: ajax_data,
nonce: fv_player_editor_conf.edit_nonce,
} ),
contentType: 'application/json',
dataType: 'json',
success: function(response) {
debug_log( 'Finished fv_player_db_save Ajax in ' + debug_time( time_start ) + '.' );
if( response.error ) {
if( response.error && response.fatal_error ) {
debug_log( 'Fatal error in fv_player_db_save: ' + response.fatal_error );
let json_export_data = jQuery('').text(JSON.stringify(ajax_data)).html();
let overlay = overlay_show('error_saving');
overlay.find('textarea').val( response.error + '\n\nJSON data:\n\n' + $('').text(json_export_data).html() );
jQuery('#fv_player_copy_to_clipboard').select();
} else {
add_notice( 'error', response.error );
debug_log( 'Error in fv_player_db_save: ' + response.error );
}
el_spinner.hide();
is_saving = false;
return;
}
// player saved, reset draft status
is_unsaved = false;
//is_draft_changed = false;
current_player_db_id = parseInt(response.id);
if( current_player_db_id > 0 ) {
let shortcode_insert = '[fvplayer id="' + current_player_db_id + '"';
// we have extra presentation parameters to keep
if (store_shortcode_args) {
// if this was a non-DB player and is being converted into a DB-player,
// remove all parameters to keep that will go into the DB
var args_to_keep = {};
for (var i in store_shortcode_args) {
if (always_keep_shortcode_args[ i ]) {
args_to_keep[ i ] = store_shortcode_args[ i ];
}
}
store_shortcode_args = args_to_keep;
shortcode_insert += add_shortcode_args( store_shortcode_args );
} else if (always_keep_shortcode_args && player_was_non_db) {
// we have extra parameters to keep that are DB-incompatible
shortcode_insert += add_shortcode_args( always_keep_shortcode_args );
} else {
// simple DB shortcode, no extra presentation parameters
insert_shortcode('[fvplayer id="' + current_player_db_id + '"]');
}
shortcode_insert += ']';
insert_shortcode( shortcode_insert );
if( fv_player_editor.clientId ) {
wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes(fv_player_editor.clientId, { shortcodeContent: shortcode_insert, player_id: current_player_db_id });
}
jQuery(".fv-wordpress-flowplayer-button").fv_player_box.close();
} else {
let json_export_data = jQuery('').text(JSON.stringify(ajax_data)).html();
let overlay = overlay_show('error_saving');
overlay.find('textarea').val( $('').text(json_export_data).html() );
jQuery('#fv_player_copy_to_clipboard').select();
}
},
error: function( jqXHR, textStatus, errorThrown ) {
overlay_show('message', 'An unexpected error has occurred while saving player: ' + errorThrown + '
Please try again');
debug_log( 'HTTP Error in fv_player_db_save: ' + errorThrown + ': ' + jqXHR.responseText );
}
});
return;
}
function fv_wp_flowplayer_dialog_resize() {
debug_log('Deprecated fv_wp_flowplayer_dialog_resize() call.');
}
function get_pretty_aspect_ratio( video ) {
let w, h, dividend, divisor, remainder;
// Sanitize input
video.width = parseInt( video.width );
video.height = parseInt( video.height );
video.aspect_ratio = parseFloat( video.aspect_ratio );
if( video.width && video.height ) {
w = video.width;
h = video.height;
} else if( video.aspect_ratio ) {
w = 1;
h = video.aspect_ratio;
} else {
return false;
}
if( h == w ) {
return '1:1';
} else {
if( h > w ) {
dividend = h;
divisor = w;
}
if( w > h ) {
dividend = w;
divisor = h;
}
let gcd = -1,
loop_runs = 0;
while( gcd == -1 ) {
loop_runs++;
// Avoid endless loop, what if...
if( loop_runs > 100 ) {
gcd = divisor;
break;
}
remainder = dividend % divisor;
if( remainder == 0 ){
gcd = divisor;
} else {
dividend = divisor;
divisor = remainder;
}
}
return ( w / gcd ) + ':' + ( h / gcd );
}
}
function insert_button_toggle( show ) {
// Do not show Insert button if player is configured for a set field
if( show && fv_player_editor_conf.field_selector ) {
return;
}
$('.fv_player_field_insert-button').toggle( show );
}
function insert_button_toggle_disabled( disable ) {
var button = $('.fv_player_field_insert-button');
if( disable ) {
button.prop('disabled', true);
} else {
button.prop('disabled', false);
}
}
/**
* Sends new shortcode to post editor
*/
function insert_shortcode( shortcode ) {
// do not insert new shortcode if using button on wp-admin -> FV Player
if( is_fv_player_screen(editor_button_clicked) ) {
return;
}
var field = $(editor_button_clicked).parents('.fv-player-editor-wrapper').find('.fv-player-editor-field'),
elementor_field = $(editor_button_clicked).closest( '#elementor-controls' ).find( '[data-setting="shortcode"]' ),
gutenberg = $(editor_button_clicked).parents('.fv-player-gutenberg').find('.fv-player-editor-field'),
widget = jQuery('#widget-widget_fvplayer-'+widget_id+'-text'),
custom_field_selector = jQuery(fv_player_editor_conf.field_selector);
if ( fv_player_editor_conf.on_insert ) {
fv_player_editor_conf.on_insert( shortcode );
return;
}
// Field set by the [fvplayer_editor field="{selector}"]
if( custom_field_selector.length ){
custom_field_selector.val( shortcode );
// is there a Gutenberg field together in wrapper with the button?
} else if( gutenberg.length ) {
// is there a plain text field together in wrapper with the button?
} else if (field.length) {
field.val(shortcode);
// Prevents double event triggering in FV Player Custom Video box
//field.trigger('fv_flowplayer_shortcode_insert', [shortcode]);
} else if ( elementor_field.length ) {
elementor_field.val( shortcode ).trigger( 'input' );
// FV Player in a Widget
} else if( widget.length ){
widget.val( shortcode );
widget.trigger('keyup'); // trigger keyup to make sure Elementor updates the content
widget.trigger('fv_flowplayer_shortcode_insert', [ shortcode ] );
// CodeMirror tab
} else if(instance_code_mirror) {
editor_content = editor_content.replace(/#fvp_codemirror_placeholder#/, shortcode);
set_post_editor_content(editor_content);
} else if (typeof(FCKeditorAPI) == 'undefined' && jQuery('#content:not([aria-hidden=true])').length) {
// editing
if( editor_content.match(/\[.*?#fvp_placeholder#.*?\]/) ) {
editor_content = editor_content.replace(/\[.*?#fvp_placeholder#.*?\]/, shortcode);
//inserting
} else {
editor_content = editor_content.replace(/#fvp_placeholder#/, shortcode);
}
set_post_editor_content(editor_content);
// or are we editing a shortcode in post content?
} else if (editor_content.match(fv_wp_flowplayer_re_edit)) {
editor_content = editor_content.replace(fv_wp_flowplayer_re_edit, shortcode)
set_post_editor_content(editor_content);
// is it a new player instance
} else {
// in existing post content?
if (editor_content != '') {
editor_content = editor_content.replace(fv_wp_flowplayer_re_insert, shortcode)
set_post_editor_content(editor_content);
// in blank post?
} else {
editor_content = shortcode;
send_to_editor(shortcode);
}
}
}
/*
Determines if the button clicked is on wp-admin -> FV Player or wp-admin -> Posts (if using FV Player Video Custom Fields)
*/
function is_fv_player_screen(button) {
// TODO: Should use fv_player_editor_conf.is_fv_player_screen || fv_player_editor_conf.is_edit_posts_screen
return is_fv_player_screen_add_new(button) || is_fv_player_screen_edit(button);
}
/*
Determines if the button clicked is Add New on wp-admin -> FV Player
*/
function is_fv_player_screen_add_new(button) {
return typeof( $(button).data('add_new') ) != 'undefined';
}
/*
Determines if the button clicked is Edit on wp-admin -> FV Player
*/
function is_fv_player_screen_edit(button) {
return (
typeof( $(button).data('player_id') ) != 'undefined' ||
typeof( $(button).data('post-id') ) != 'undefined'
);
}
/*
Determines if the button clicked is Insert/Edit on wp-admin -> Appearance -> Widgets
*/
function is_fv_player_widgets(button) {
return button.id.indexOf('widget-widget_fvplayer') > -1;
}
function is_live_stream( video ) {
return !! ( video.src && video.src.match( /\.m3u8|live/ ) );
}
/*
Sets lightbox class once it opens
*/
function lightbox_open() {
$("#fv_player_box").addClass("fv-flowplayer-shortcode-editor");
}
function map_checkbox_value( $element ) {
var input_type = $element.get(0).type.toLowerCase(),
is_checked = $element.get(0).checked;
if ( 'checkbox' == input_type ) {
return is_checked ? 'true' : '';
}
}
function map_dropdown_value( dropdown_element ) {
var $valueLessOptions = dropdown_element.find('option:not([value])'),
value = dropdown_element[0].value.toLowerCase(),
index = dropdown_element[0].selectedIndex;
// multiselect element
if(dropdown_element[0].multiple) {
var selected = [],
options = dropdown_element[0].options,
opt;
for (var i=0, iLen=options.length; i 1){
$('.fv-player-playlist-item-title').html('Playlist item no. ' + ++new_index);
$('.playlist_edit').html($('.playlist_edit').data('edit'));
$el_editor.addClass( 'is-playlist' );
}else{
$('.playlist_edit').html($('.playlist_edit').data('create'));
$el_editor.addClass( 'is-singular' );
}
// As Flowplayer only lets us set RTMP server for the first video in playlist, prefill it for this new item as well
if(new_index > 1){
get_field('rtmp',video_tab).val( get_field('rtmp',$('.fv-player-tab-video-files table').eq(0) ).val()).attr('readonly',true);
}
$('.fv_wp_flowplayer_field_subtitles_lang, .subtitle_language_add_link').attr('style',false);
tabs_refresh();
if( previous_index != current_video_to_edit && get_playlist_items_count() > 1 ) {
reload_preview( current_video_to_edit );
}
hide_inputs();
$doc.trigger('fv-player-editor-video-opened', [ new_index ] );
/**
* Upgrade text inputs to select boxes for matching fields
*/
window.fv_player_editor_fields_with_language.forEach( function( field_name ) {
let fields_with_languages = $( '.fv_wp_flowplayer_field_' + field_name + '_lang', subtitles_tab );
fields_with_languages.each( function() {
let $element = $( this ),
value = $element.val();
$element.replaceWith( ''
);
} );
} );
}
/**
* Recalculate the data-index values for playlist items
*/
function playlist_index() {
$doc.trigger('fv-player-editor-initial-indexing');
$('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').each( index );
$('.fv-player-tab-video-files [data-playlist-item]').each( index );
$('.fv-player-tab.fv-player-tab-subtitles [data-playlist-item]').each( index );
function index() {
/**
* The element already had the data-index defined before, so it seems
* we need to also set it with data(), since jQuery caches the first
* .data() retrieval in the internal data object.
*
* We cannot just use .data() as we use CSS selectors for elements in JS.
*/
$(this).attr('data-index', $(this).index() ).data( 'index', $(this).index() );
}
}
// fills playlist editor table from individual video items
function playlist_refresh() {
// fills playlist editor table from individual video items
let video_files = jQuery('.fv-player-tab-video-files [data-playlist-item]');
video_files.each( function( k, v ) {
let current = jQuery(v),
video = get_playlist_video_object( k ),
currentUrl = get_field("src",current).val();
if (!currentUrl.length) {
currentUrl = 'Video ' + (k + 1);
}
let playlist_row = jQuery('.fv-player-tab-playlist #fv-player-editor-playlist .fv-player-editor-playlist-item').eq( current.data('index') );
let video_preview = video.splash_display ? video.splash_display : get_field("splash", current).val(),
playlist_img = jQuery('')
.attr('width', 120 )
.attr('src', video_preview );
playlist_row.find('.fvp_item_video-thumbnail')
.html( video_preview.length ? playlist_img : '' )
.toggleClass( 'no-img', !video_preview.length );
let video_name = decodeURIComponent(currentUrl).split("/").pop();
video_name = video_name.replace(/\+/g,' ');
video_name = video_name.replace(/watch\?v=/,'YouTube: ');
let playlist_title = playlist_row.find('.fvp_item_video-filename');
playlist_title.text( video_name );
// do not put in title if it's loading
if (!playlist_title.hasClass('fv-player-shortcode-editor-small-spinner')) {
let title = get_field("title",current).val();
if( title ) {
playlist_title.text( title );
}
}
playlist_row.find('.fvp_item_video-duration').text( seconds_to_hms( video.duration ) );
});
}
/**
* Displays playlist editor
* keywords: show playlist
*/
function playlist_show() {
current_video_db_id = 0;
editing_video_details = false;
$el_editor
.addClass( 'is-playlist-active' )
.removeClass( 'is-playlist' )
.removeClass( 'is-singular' )
.removeClass( 'is-singular-active' );
// show all the tabs previously hidden
jQuery('.fv-player-tabs-header .nav-tab').attr('style',false);
jQuery('a[data-tab=fv-player-tab-playlist]').trigger('click');
set_current_video_to_edit( -1 );
playlist_index();
playlist_refresh();
playlist_index();
jQuery('.fv-player-tab-playlist').show();
tabs_refresh();
show_end_of_video_actions();
$doc.trigger('fv-player-editor-video-opened', [ current_video_to_edit ] );
return false;
}
function reload_preview( video_index ) {
if(
video_index > -1 && ( !current_player_object.videos || !current_player_object.videos[video_index] ) ||
video_index== -1 && !current_player_object.videos
) {
reset_preview();
return;
}
el_spinner.show();
debug_log('Running fv_player_db_load Ajax for preview.');
let time_start = performance.now();
// load player data and reload preview of the full player
// when we go back from editing a single video in a playlist
fv_player_shortcode_editor_ajax = jQuery.post( ajaxurl + '?fv_player_db_load=' + current_player_db_id, {
action : 'fv_player_db_load',
nonce : fv_player_editor_conf.db_load_nonce,
playerID : current_player_db_id,
current_video_to_edit: get_playlist_items_count() > 1 ? video_index : -1,
}, function(response) {
debug_log('Finished fv_player_db_load Ajax in ' + debug_time( time_start ) + '.',response);
if ( response.html ) {
// auto-refresh preview
el_preview_target.html( response.html )
$doc.trigger('fvp-preview-complete');
}
}).fail(function( jqXHR, textStatus, errorThrown ) {
if ( jqXHR.status == 404) {
overlay_show('message', 'The requested player #' + current_player_db_id + ' could not be found.');
} else {
overlay_show('message', 'An unexpected error has occurred while loading player #' + current_player_db_id + ': ' + errorThrown + '
Please try again.');
}
}).always(function() {
el_spinner.hide();
});
}
function remove_notices() {
$el_notices.html('');
}
function reset_editor_ids() {
debug_log( 'reset_editor_ids' );
current_player_db_id = 0;
current_player_object = false;
current_video_db_id = 0;
current_post_id = 0;
current_post_meta_key = 0;
}
function reset_preview() {
$el_preview.attr('class','preview-no');
if( fv_player_editor_conf.frontend ) {
$el_editor.addClass('is-intro');
}
}
function seconds_to_hms( seconds ) {
try {
var duration_hms = new Date(seconds * 1000).toISOString().substr(11, 8);
return duration_hms.replace( /^00:/, '' );
} catch(e) {
return '';
}
}
function set_current_video_to_edit( index ) {
current_video_to_edit = parseInt( index );
}
function set_control_fields( ajax_data ) {
// add current video that we're editing into the save data
ajax_data['current_video_to_edit'] = get_playlist_items_count() > 1 ? current_video_to_edit : -1;
// set current editor tab for ajax_data
ajax_data['current_editor_tab'] = current_editor_tab;
// Used for Video Custom Fields
if( current_post_id ) {
ajax_data['current_post_id'] = current_post_id;
}
if( current_post_meta_key ) {
ajax_data['current_post_meta_key'] = current_post_meta_key;
}
// Used in FV Player Pay Per View to store the post ID for the product
ajax_data['editor_post_id'] = fv_flowplayer_set_post_thumbnail_id;
return ajax_data;
}
// used several times below, so it's in a function
function set_editor_field( key, real_val, id, video_tab ) {
var real_key = map_names_to_editor_fields(key);
if ( video_tab ) {
$element = jQuery( '[name="' + real_key + '"]', video_tab );
} else {
$element = jQuery( '[name="' + real_key + '"]' );
}
// special processing for end video actions
if (real_key == 'fv_wp_flowplayer_field_end_action_value') {
show_end_actions( false, real_val );
return;
} else if (['fv_wp_flowplayer_field_email_list', 'fv_wp_flowplayer_field_popup_id', 'fv_wp_flowplayer_field_redirect'].indexOf(real_key) > -1) {
// ignore the original fields, if we still use old DB values
return;
}
// player and video IDs wouldn't have corresponding fields
if ( $element && $element.length ) {
// dropdowns could have capitalized values
if ($element.get(0).nodeName == 'SELECT') {
if ($element.find('option[value="' + real_val + '"]').length) {
$element.val(real_val);
} else {
// try capitalized
var caps = real_val.charAt(0).toUpperCase() + real_val.slice(1);
$element.find('option').each(function() {
if (this.text == caps) {
$element.val(caps);
}
});
}
} else if ($element.get(0).nodeName == 'INPUT' && $element.get(0).type.toLowerCase() == 'checkbox') {
let checked = real_val === '1' || real_val === 'on' || real_val === 'true' || real_val === 'yes';
// Is the value empty and is there default?
if( real_val == '' && typeof(window.fv_player_editor_defaults[key]) != "undefined" ) {
return;
}
$element.prop( 'checked', checked ).trigger('change');
} else {
$element.val(real_val);
}
// if an ID exists, this is a meta field
// and the data id needs to be added to it as well
if (typeof(id) != 'undefined') {
$element.attr('data-id', id);
}
}
}
function set_post_editor_content( html ) {
if ( editor_button_clicked.className.indexOf('fv-player-editor-button') > -1 || is_fv_player_screen(editor_button_clicked) || is_fv_player_widgets(editor_button_clicked) || $(editor_button_clicked).parents('.fv-player-gutenberg').find('.fv-player-editor-field').length) {
return;
}
if ( instance_code_mirror ) {
// set code mirror content
instance_code_mirror.getDoc().setValue( html );
} else if( typeof(FCKeditorAPI) == 'undefined' && jQuery('#content:not([aria-hidden=true])').length ){
jQuery('#content:not([aria-hidden=true])').val(html);
} else if ( typeof(instance_fp_wysiwyg) != 'undefined' && ( instance_tinymce == undefined || typeof tinyMCE !== 'undefined' && tinyMCE.activeEditor.isHidden() ) ) {
instance_fp_wysiwyg.SetHTML( html );
}
else if ( instance_tinymce ) { // instance_tinymce will be null if we're updating custom meta box
instance_tinymce.setContent( html );
}
}
/**
* Hide any overlays
*/
function overlay_hide() {
$('.fv-player-editor-overlay').hide();
return false;
}
/**
* Show a certain kind of overlay
*/
function overlay_show( type, message ) {
overlay_hide();
var overlayDiv = $('#fv-player-editor-'+type+'-overlay');
overlayDiv.show();
if( typeof(message) != 'undefined' ) {
overlayDiv.find('p').html( message );
}
return overlayDiv;
}
/**
* Populate content of the Embeds tab and show it if there is any content to be set
*
* @param string html The OL > LI list of posts which contain the same player.
*/
function set_embeds( html ) {
// ugly way of making sure that tab stays hidden as otherwise playlist_show() would reveal it
$('[data-tab=fv-player-tab-embeds]').toggleClass('always-hide',!html);
get_tabs('embeds').find('td').html(html);
}
function shortcode_arg_split(sInput) {
sInput[1] = sInput[1].replace(/\\;/gi, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
var aInput = sInput[1].split(';');
for( var i in aInput ){
aInput[i] = aInput[i].replace(/\\"/gi, '"');
aInput[i] = aInput[i].replace(/\\/gi, ';');
aInput[i] = aInput[i].replace(//gi, ';');
}
return aInput;
}
/*
Also used by FV Player Pro and FV Player Pay Per View
*/
function shortcode_parse_arg( sShortcode, sArg, bHTML, sCallback ) {
var rDoubleQ = new RegExp(sArg+"=\"","g");
var rSingleQ = new RegExp(sArg+"='","g");
var rNoQ = new RegExp(sArg+"=[^\"']","g");
var rMatch = false;
if( sShortcode.match(rDoubleQ) ) {
//rMatch = new RegExp(sArg+'="(.*?[^\\\\/])"',"g");
rMatch = new RegExp('[ "\']' + sArg + '="(.*?[^\\\\])"', "g");
} else if (sShortcode.match(rSingleQ)) {
rMatch = new RegExp('[ "\']' + sArg + "='([^']*?)'", "g");
} else if (sShortcode.match(rNoQ)) {
rMatch = new RegExp('[ "\']' + sArg + "=([^\\]\\s,]+)", "g");
}
if( !rMatch ){
return false;
}
var aOutput = rMatch.exec(sShortcode);
fv_player_editor.set_shortcode_remains( fv_player_editor.get_shortcode_remains().replace( rMatch, '' ) );
if( aOutput && bHTML ) {
aOutput[1] = aOutput[1].replace(/\\"/g, '"').replace(/\\(\[|])/g, '$1');
}
if( aOutput && sCallback ) {
sCallback(aOutput);
} else {
return aOutput;
}
}
function shortcode_share_parse_arg( args ) {
var field = fv_player_editor.get_field("share")[0];
if (args[1] == 'yes' ) {
field.selectedIndex = 1;
} else if (args[1] == 'no' ) {
field.selectedIndex = 2;
} else {
field.selectedIndex = 3;
args = args[1].split(';');
if( typeof(args[0]) == "string" ) fv_player_editor.get_field('share_url').val(args[0]);
if( typeof(args[1]) == "string" ) fv_player_editor.get_field('share_title').val(args[1]);
fv_player_editor.get_field("share_custom").show();
}
}
function shortcode_subtitle_parse_arg( args ) {
var input = ('shortcode_subtitle_parse_arg',args);
var aLang = input[0].match(/subtitles_([a-z][a-z])/);
subtitle_language_add( input[1], aLang[1] );
}
function subtitle_language_add( sInput, sLang ) {
language_add( 'subtitles', sInput, sLang );
}
/*
* Adds another language for a field
*
* @param {string} field Field name
* @param {string} sInput Value to input
* @param {string} sLang Language to use
* @param {jQuery} subtitles_tab Video Subtitles tab element
* @param {int} video_meta_id Field video meta ID
*/
function language_add( field, sInput, sLang, subtitles_tab, video_meta_id ) {
// Used when parsing a legacy shortcode like [fvplayer src="video-1.mp4" subtitles="video-1.vtt"]
if( typeof( subtitles_tab ) == "undefined" ){
// Reguired when parsing a legacy shortcode
if( current_video_to_edit == -1 ) {
current_video_to_edit = 0;
}
subtitles_tab = get_tab( current_video_to_edit, 'subtitles' );;
}
var subElement = false;
// If we are loading data, do we have an empty subtitle field?
if( sInput ) {
// TODO: Function to get last of the language inputs which is not a child
subElement = $('.fv-player-editor-field-wrap-' + field + ' > .components-base-control > .components-base-control__field:last', subtitles_tab);
if( subElement.length ) {
if( get_field( field,subElement).val() ) {
subElement = false;
}
}
}
// If we do not have an empty subtitle field, add new
if( !subElement ) {
// Get the last inputs and clear the values
// TODO: Function to get last of the language inputs which is not a child
subElement = $( $('.fv-player-editor-field-wrap-' + field + ' > .components-base-control > .components-base-control__field:last', subtitles_tab ).prop('outerHTML') );
subElement.find('[name]').val('');
subElement.removeAttr('data-id_videometa');
// Insert the new input after the last exiting input
// TODO: Function to get last of the language inputs which is not a child
subElement.insertAfter( $('.fv-player-editor-field-wrap-' + field + ' > .components-base-control > .components-base-control__field:last', subtitles_tab) );
if( !sInput ) {
// force user to pick the language by removing the blank value and selecting what's first
subElement.find('select option[value=""]').remove();
setTimeout( function() {
subElement.find('select option').eq(0).prop('selected',true);
}, 0 );
}
}
if (typeof(video_meta_id) !== 'undefined') {
subElement.attr('data-id_videometa', video_meta_id);
}
let new_field = get_field( field, subElement);
if( sInput ) {
new_field.val(sInput).trigger('change');
}
show_short_link( new_field );
if ( sLang ) {
if( sLang == 'iw' ) sLang = 'he';
if( sLang == 'in' ) sLang = 'id';
if( sLang == 'jw' ) sLang = 'jv';
if( sLang == 'mo' ) sLang = 'ro';
if( sLang == 'sh' ) sLang = 'sr';
get_field( field + '_lang',subElement).val(sLang).trigger('change');
}
}
function tabs_refresh() {
var visibleTabs = 0;
$el_editor.find('a[data-tab]').removeClass('fv_player_interface_hide');
$el_editor.find('.fv-player-tabs > .fv-player-tab').each(function(){
var bHideTab = true
$(this).find('.fv_player_editor-panel__body, .fv-player-editor-playlist-item').each(function(){
if( $(this).css('display') === 'block' || $(this).css('display') === 'flex' || $(this).css('display') === 'table' ){
bHideTab = false;
return false;
}
});
var tab;
var data = jQuery(this).attr('class').match(/fv-player-tab-[^ ]*/);
if(data[0]){
tab = $el_editor.find('a[data-tab=' + data[0] + ']');
}
if( fv_player_editor_conf.tabs && fv_player_editor_conf.tabs == 'none' ) {
bHideTab = true;
}
if(bHideTab){
tab.addClass('fv_player_interface_hide')
} else {
tab.removeClass('fv_player_interface_hide');
if(tab.css('display')!=='none')
visibleTabs++
}
});
if(visibleTabs<=1){
$el_editor.find('.nav-tab').addClass('fv_player_interface_hide');
$el_editor.find('.nav-tab-wrapper').addClass('fv_player_interface_hide');
}
var end_actions_label = $('label[for=fv_wp_flowplayer_field_end_actions]');
if( $el_editor.hasClass('is-playlist-active')){
end_actions_label.html( end_actions_label.data('playlist-label') )
} else {
end_actions_label.html( end_actions_label.data('single-label') )
}
}
/*
Click handlers
*/
/*
Click on Add Another Language (of Subtitles)
*/
$doc.on('click', '.add_language', function() {
language_add( $( this ).data( 'field_name' ), false );
return false;
});
/*
Click on "Remove" to remove a language for a field
*/
$doc.on('click', '.remove_language', function() {
let button = $( this ),
field_name = button.data( 'field_name' ),
field_label = button.data( 'field_label' ),
field = jQuery(this).closest( '.components-base-control__field' ),
id = field.attr('data-id_videometa')
if( !confirm('Would you like to remove this ' + field_label + '?') ) {
return false;
}
if (id) {
deleted_video_meta.push(id);
}
// TODO: How to get the field wrap and how to get its langauge versions?
let language_fields = get_field( field_name, true )
.closest('.components-base-control')
.find( '.components-base-control__field' );
// if it's not the last subtitle, remove it completely
if( language_fields.length > 1){
field.remove();
// otherwise just empty the inputs to let user add new subtitles
} else {
field.find('[name]').val('');
field.removeAttr('data-id_videometa');
}
$doc.trigger('fv_player_editor_language_delete');
return false;
});
/*
Click on Import player
*/
$doc.on('click', '#fv-player-editor-import-overlay-import', function() {
var button = this,
data = jQuery('#fv_player_import_data').val();
if (!data) {
fv_player_editor.overlay_notice( button, 'No data to import!', 'warning', 5000 );
return false;
}
try {
JSON.parse(data);
} catch(e) {
fv_player_editor.overlay_notice( button, 'Bad JSON format!', 'error', 5000 );
return false;
}
fv_player_editor.overlay_notice_close_all();
overlay_show('loading');
debug_log('Running fv_player_db_import Ajax.');
jQuery.post(ajaxurl, {
action: 'fv_player_db_import',
nonce: fv_player_editor_conf.db_import_nonce,
data: data,
cookie: encodeURIComponent(document.cookie),
}, function(response) {
if (response != '0' && !isNaN(parseFloat(response)) && isFinite(response)) {
var playerID = response;
// add the inserted player's row
jQuery.get(
fv_player_editor_conf.admin_url + '&id=' + playerID,
function (response) {
jQuery('#the-list tr:first').before(jQuery(response).find('#the-list tr:first'));
jQuery('.fv-wordpress-flowplayer-button').fv_player_box.close();
}).fail(function() {
jQuery('.fv-wordpress-flowplayer-button').fv_player_box.close();
});
} else {
fv_player_editor.overlay_notice( button, response, 'error' );
}
}).fail(function() {
fv_player_editor.overlay_notice( button, 'Unknown error!', 'error' );
});
return false;
});
/*
Editor short links
*/
var shortened_field_edited = false, // store field that if being edited
shortened_field_button = false; // store Media Library button for the field
// click on shortened preveew, show original input and hide preview
$doc.on('click', '.fv_player_editor_url_shortened', function() {
var preview = jQuery(this),
wrap = preview.closest('.components-base-control__field');
shortened_field_edited = preview.next('.fv_player_editor_url_field')
shortened_field_button = wrap.find('.add_media')
preview.addClass('fv_player_interface_hide');
preview.attr('title', '');
shortened_field_button.show();
shortened_field_edited.removeClass('fv_player_interface_hide');
shortened_field_edited.focus();
});
// Clicking outside of the edited field should close the field editing
$el_editor.on( 'click', function(e) {
if( shortened_field_edited ) {
if(
!$(e.target).hasClass('fv_player_editor_url_shortened') &&
!$(e.target).parent().hasClass('fv_player_editor_url_shortened') &&
!shortened_field_edited.is(e.target) &&
!shortened_field_button.is(e.target)
) {
show_short_link( shortened_field_edited );
}
}
});
// focus lost from input
$doc.on('change', '.fv_player_editor_url_field', function() {
show_short_link(jQuery(this));
});
$doc.on('fv_player_editor_finished fv_flowplayer_shortcode_item_switch', function() {
jQuery('.fv_player_editor_url_field').each(function(index, item) {
show_short_link(jQuery(item))
});
});
function show_short_link(field) {
var value = field.val().trim(),
preview = field.prev('.fv_player_editor_url_shortened'),
button = field.closest( '.components-base-control__field').find('.add_media'); // Media Library Button
shortened_field_edited = false;
shortened_field_button = false;
if( !value ) { // no value, hide preview
field.removeClass('fv_player_interface_hide');
preview.addClass('fv_player_interface_hide');
button.show();
} else {
field.addClass('fv_player_interface_hide');
preview.removeClass('fv_player_interface_hide');
preview.attr('title', value );
preview.find('.link-preview').html( shorten_original_link( value ) ); // shorten preview link
button.hide();
}
}
function shorten_original_link(original_link) {
if(!original_link) return original_link;
let parts = original_link.trim().split('/'),
is_hls = parts[parts.length - 1].indexOf('.m3u8') != -1,
new_parts = [];
$(parts).each( function(k,v) {
let c = '';
// filename
if( k == parts.length - 1 ) {
// if it's HLS hilight the extension only
if( is_hls ) {
let filename_parts = v.split('.');
if( filename_parts.length == 2 ) {
new_parts.push( ''+filename_parts[0]+'.'+filename_parts[1]+'' );
return true;
}
}
// HLS filename - use the directory name
} else if( is_hls && k == parts.length - 2 ) {
// domain
} else if ( k == 2 ) {
// hilight bucket name for AWS
let domain_parts = v.split('.');
if( domain_parts.length > 1 && domain_parts[1].indexOf('s3') == 0 ) {
let bucket_name = domain_parts[0];
delete(domain_parts[0])
new_parts.push( ''+bucket_name+''+domain_parts.join('.')+'' );
return true;
}
} else {
c = ' class="path"';
}
new_parts.push( ''+v+'' );
});
return new_parts.join('/');
}
/*
End of video action
*/
$doc.on('change', '#fv_wp_flowplayer_field_end_actions', function() {
var selected = this.value;
jQuery('.fv-player-editor-field-wrap-redirect').addClass('fv_player_interface_hide');
jQuery('.fv-player-editor-field-wrap-popup_id').addClass('fv_player_interface_hide');
jQuery('.fv-player-editor-field-wrap-email_list').addClass('fv_player_interface_hide');
switch (selected) {
case 'redirect':
jQuery('.fv-player-editor-field-wrap-redirect').removeClass('fv_player_interface_hide');
break;
case 'popup':
jQuery('.fv-player-editor-field-wrap-popup_id').removeClass('fv_player_interface_hide');
break;
case 'email_list':
jQuery('.fv-player-editor-field-wrap-email_list').removeClass('fv_player_interface_hide');
break;
default:
break;
}
});
/*
Extra fields to reveal when using a HLS or MPD stream
*/
$doc.on('fv_flowplayer_shortcode_item_switch', function(e, index) {
show_video_details(index);
show_stream_fields_worker(index);
show_rtmp_fields();
show_end_of_video_actions();
show_playlist_not_supported(index);
});
$doc.on('fv_flowplayer_shortcode_new', function() {
show_video_details(0);
show_stream_fields_worker(0);
show_rtmp_fields();
show_end_of_video_actions();
show_playlist_not_supported();
});
function show_end_of_video_actions() {
var player_object = get_current_player_object(),
picked_action = player_object.end_actions,
action_value = player_object.end_action_value;
select_end_of_video_action(picked_action, action_value);
}
function select_end_of_video_action(action, value) {
if( action == 'redirect' ) {
jQuery('.fv-player-editor-field-wrap-redirect').removeClass('fv_player_interface_hide');
get_field('redirect', false).val(value);
} else if ( action == 'popup' ) {
jQuery('.fv-player-editor-field-wrap-popup_id').removeClass('fv_player_interface_hide');
get_field('popup', false).find('[value="'+value+'"]').prop('selected', true);
} else if ( action == 'email_list' ) {
jQuery('.fv-player-editor-field-wrap-email_list').removeClass('fv_player_interface_hide');
get_field('email_list', false).find('[value="'+value+'"]').prop('selected', true);
}
}
function show_playlist_not_supported( index = 0 ) {
let video = get_playlist_video_object( index ),
result = {
'supported': true
}
if ( ! video.src ) {
return;
}
if (
video.src.indexOf('//vimeo.com') > -1 ||
video.src.indexOf('//vimeopro.com') > -1
) {
result.supported = false;
}
// fire up a JS event for the FV Player Pro to catch,
// so it can check the URL and make sure we don't show
// a warning message for PRO-supported video types
$doc.trigger('fv-player-editor-src-change', [ video.src, result, this ]);
if( result.supported ) {
playlist_buttons_disable(false);
} else {
// Disable the playlist editing buttons if the video type is not supported in playlists
playlist_buttons_disable('FV Player Pro required for playlists with this video type');
}
}
function show_rtmp_fields() {
var video_object = get_current_video_object(),
is_enabled = video_object.rtmp || video_object.rtmp_path
get_field( 'rtmp_show', true )
.prop('checked', is_enabled)
.trigger('change');
if( is_enabled ) {
get_field( 'toggle_advanced_settings', true )
.prop('checked', true)
.trigger('change');
}
}
function show_stream_fields_worker( index = 0 ) {
var encrypted = get_current_player_object() ? get_playlist_video_meta_value( 'encrypted', index ) : false;
// hlskey
var hlskey = get_field('hls_hlskey', true);
if( encrypted || hlskey.val() ) {
hlskey.closest('.fv_player_interface_hide').show();
} else {
hlskey.closest('.fv_player_interface_hide').hide();
}
// get live from video object
var live_field = get_field('live', true),
live_value = get_current_video_object() ? get_current_video_object().live : false,
error = get_playlist_video_meta_value( 'error', index ),
// Show the stream settings if it's not found and it's HLS stream
// as it often returns 404 error if it's not live at the moment
stream_fields_visible = !! ( error && is_live_stream( get_current_video_object() ) );
live_field.prop('checked', !!live_value);
live_field.closest('.fv_player_interface_hide').toggle( !!live_value || stream_fields_visible );
checkbox_toggle_worker( jQuery(live_field).parent('.components-form-toggle'), 'live', !!live_value );
jQuery.each( [ 'audio', 'dvr' ], function(k,v) {
var field = get_field(v, true),
meta = get_current_player_object() ? get_playlist_video_meta_value( v, index ) : false,
force_visible = stream_fields_visible,
is_hls = get_current_video_object().src && get_current_video_object().src.match( /\.m3u8/ );
// Show Audio track checkbox if we have HLS with unknown dimensions
if ( 'audio' === v && is_hls && get_current_video_object().width == 0 && get_current_video_object().height == 0 ) {
force_visible = true;
}
field.prop('checked', !!meta);
field.closest('.fv_player_interface_hide').toggle( !!meta || force_visible );
checkbox_toggle_worker( jQuery(field).parent('.components-form-toggle'), v, !!meta );
});
}
function show_video_details( index = 0 ) {
var video_tab = get_tab(index,'video-files'),
video = get_playlist_video_object( index ),
error = get_playlist_video_meta_value( 'error', index )
var video_info = get_field('video_info', video_tab).find('ul'),
show = false;
video_info.html('');
if( video.duration > 0 ) {
video_info.append( '
' );
show = true;
if ( is_live_stream( video ) ) {
video_info.append( '
FV Player was not able to determine the stream type, please check what applies below.
' );
video_info.append( '
This error is normal for some of the HLS live streams if they are not currently streaming.
' );
}
}
if( show ) {
video_info.closest('.fv_player_interface_hide').removeClass('fv_player_interface_hide');
}
}
function checkbox_toggle_worker( wrap, name, checked ) {
var compare = checked ? 1 : 0;
wrap.toggleClass( 'is-checked', checked );
// If the checkox is checked its parents must be visible
if( checked ) {
wrap
.closest('.fv-player-editor-children-wrap')
.removeClass('fv_player_interface_hide');
}
wrap.toggleClass( 'is-default', !!window.fv_player_editor_defaults[name] );
if( window.fv_player_editor_dependencies[name] ) {
jQuery.each( window.fv_player_editor_dependencies[name], function(value,inputs) {
jQuery.each( inputs, function(k,input_name) {
// We get the last matching field as if we are loading a playlist, we might have multiple fields
// TODO: Do this more sensibly, like when switching playlist items
let field_wrap = $('.fv-player-editor-field-wrap-' + input_name + ':last' );
// TODO: What should be saved when it's enabled?
field_wrap.toggleClass( 'is-visible-dependency', value == compare );
field_wrap.toggleClass( 'is-hidden-dependency', value != compare );
});
});
}
wrap.closest( '.fv-player-editor-children-wrap' ).find( '.fv-player-editor-field-children-'+name ).toggle( checked );
}
function text_and_select_worker( input, parent, name, wrap ) {
// Reveal the input if it has value even if it's not enabled in Post Interface options
if ( input.val() ) {
// Only if it's not field with shortening
if ( input.prev( '.fv_player_editor_url_shortened' ).length === 0 ) {
input.closest( '.fv_player_interface_hide' ).show();
}
wrap.show();
}
// Show children inputs if input has value
parent.find('.fv-player-editor-field-children-' + name ).toggle( !! input.val() );
}
function show_end_actions( e, value ) {
// redirect, popup and email_list
var type = jQuery('#fv_wp_flowplayer_field_end_actions').val();
jQuery('.fv_player_actions_end-toggle').hide().find('[name]').val('');
// The field id is different for popup
if( type == 'popup' ) {
type = 'popup_id';
}
var field = jQuery('#fv_wp_flowplayer_field_' + type);
field.parents('tr').show();
if( value ) {
field.val( value ).trigger( 'change' );
}
}
function init_saved_player_fields() {
deleted_videos = [];
deleted_video_meta = [];
deleted_player_meta = [];
}
function title_editor_close() {
$('.fv-player-editor-playlist-item .fvp_item_video-title-wrap').show();
$('.fv-player-editor-playlist-item .fvp_item_video-edit-input').hide();
}
/*
Mark each manually updated title or splash field as such
*/
$doc.on('input change', '#fv_wp_flowplayer_field_splash, #fv_wp_flowplayer_field_title', function() {
if ( is_loading ) {
return;
}
let $input;
// if this element already has data set, don't do any of the selections below
if (typeof( jQuery(this).data('fv_player_user_updated') ) != 'undefined') {
return;
}
if( this.id == 'fv_wp_flowplayer_field_splash' ) {
$input = get_field('auto_splash', true);
} else {
$input = get_field('auto_caption', true);
}
if( $input.length > 0 ) {
$input.val(0);
debug_log(this.id+' has been updated manually!');
}
});
// Public stuff
return {
add_notice,
playlist_index,
reload_preview( index ) {
return reload_preview(index);
},
fv_wp_flowplayer_dialog_resize,
get_current_player_db_id,
get_current_player_object,
get_current_video_meta,
get_current_video_meta_value,
get_current_video_object,
get_current_video_index() {
return parseInt( current_video_to_edit );
},
get_current_video_db_id() {
return current_video_db_id;
},
get_edit_lock_removal() {
return edit_lock_removal;
},
get_field( key, where ) {
return get_field( key, where );
},
get_playlist_video_meta,
get_playlist_video_meta_value,
get_playlist_video_object,
get_shortcode_remains: function() {
return shortcode_remains;
},
get_tab( index, tab ) {
return get_tab( index, tab );
},
insertUpdateOrDeletePlayerMeta,
insertUpdateOrDeleteVideoMeta,
is_busy_saving() {
return ajax_save_this_please || is_saving;
},
// Allows external entities to set which video will be edited once the editor opens
set_current_video_to_edit,
set_edit_lock_removal( val ) {
edit_lock_removal = val;
},
set_shortcode_remains: function(value) {
shortcode_remains = value;
},
/**
* Show a notice in the overlay above the editor
*
* @param {Object} button The button in the overlay that was clicked
* Used to find the overlay and the notice in it
* @param {string} html Content of the notice
* @param {string} type success|error
* @param {int} close_after Optional number of miliseconds to close the
* notice after
*
*/
overlay_notice: function(button, html, type, close_after ) {
var overlay = jQuery(button).closest('.fv-player-editor-overlay'),
notice = overlay.find('.fv-player-editor-overlay-notice');
notice
.html(html)
.removeClass('notice-error')
.removeClass('notice-success')
.addClass('notice-'+type)
.css('visibility', 'visible');
overlay.show();
if( close_after ) {
setTimeout(function() {
notice.css('visibility', 'hidden');
}, close_after);
}
},
overlay_notice_close_all: function() {
$('.fv-player-editor-overlay-notice').css('visibility', 'hidden');
},
playlist_item_add: playlist_item_add,
playlist_show: playlist_show,
shortcode_parse_arg,
// We keep it for backwards compatibility
editor_resize: function() {},
get_playlist_items_count,
insert_button_toggle,
copy_player_button_toggle,
// TODO: Deprecated
meta_data_load_started() {
is_loading_meta_data++;
},
meta_data_load_finished() {
is_loading_meta_data--;
},
editor_open: editor_open,
error_add( identifier, txt ) {
errors[ identifier ] = txt;
// no save button while we have errors
insert_button_toggle_disabled( true );
},
error_remove( identifier ) {
delete errors[ identifier ];
if ( !has_errors() ) {
// enable save button
insert_button_toggle_disabled( false );
}
},
errors_remove( prefix ) {
var errors_found = [];
for ( var key in errors ) {
if ( key.startsWith( prefix ) ) {
errors_found.push( key );
}
}
if ( errors_found.length ) {
for ( var val of errors_found ) {
delete errors[ val ];
}
}
if ( has_errors() ) {
// enable save button
insert_button_toggle_disabled( false );
}
},
has_errors,
b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode('0x' + p1);
}));
},
};
})(jQuery);
if ( window.wp && wp.data ) {
wp.data.subscribe( function() {
if ( wp.data.select('core/editor') && wp.data.select('core/editor').getCurrentPost().fv_player_reload ) {
setTimeout( function() {
location.href = location.href;
}, 0 );
}
} );
}
});