Repository URL to install this package:
|
Version:
3.7.1 ▾
|
<?php
/**
* Main Elements API Class to create Oxygen elements
*
* @since 2.4
* @author Ilya K.
*/
Class OxygenElement extends OxygenElementControls {
public $defaults;
function __construct($title, $id, $file='', $icon='', $button_place='', $other_options=array(), $has_js = false) {
$this->defaults = array();
$this->white_list_options = array();
$this->not_css_options = array();
$this->rebuild_options = array();
$this->fallbackToDefaults = true;
$this->valuesCSS = array();
$this->assets_path = "";
$this->base64_options = array();
$this->scripts = array();
$this->styles = array();
$this->oxygen_id_styles_output = array();
// set params
$this->presets = array();
$this->params['controls'] = array();
$this->params['title'] = $title;
$this->params['icon'] = $icon;
$this->params['button_place'] = $button_place;
$this->params['button_priority']= isset($other_options['button_priority']) ? $other_options['button_priority'] : 10;
$this->params['other_options'] = $other_options;
$this->params['has_js'] = $has_js;
if ($file!=='') {
$this->params['dir_path'] = plugin_dir_path( $file );
$this->params['dir_url'] = plugin_dir_url( $file );
}
else {
$this->params['dir_path'] = "";
$this->params['dir_url'] = "";
}
// default HTML tag is div
$this->params['html_tag'] = "div";
// set shortcode tag ourselves and don't let developers do this
$this->params['tag'] = $this->generate_tag($id);
// add shortcodes
add_shortcode($this->params['tag'], array($this, 'shortcode'));
for ( $i = 2; $i <= 16; $i++ ) {
add_shortcode($this->params['tag'] . "_" . $i, array($this, 'shortcode'));
}
// +Add section button
$this->addButton();
// create old CT_Component instance to use some methods
$this->component = new CT_Component(
array(
'name' => $this->params['title'],
'tag' => $this->params['tag'],
)
);
global $oxygen_vsb_components;
$oxygen_vsb_components[$this->params['tag']] = $this->component;
// remove the default button location hook, we will re-add this later
remove_action("ct_toolbar_fundamentals_list", array( $this->component, "component_button" ) );
add_filter("ct_components_nice_names", array( $this, "niceName") );
add_filter("oxy_components_html_templates", array( $this, "html_template") );
// generate CSS output
//add_action("oxygen_default_classes_output", array( $this, "generate_defaults_css" ) ); DISABLED
add_filter("oxygen_user_classes_output", array( $this, "generate_classes_css"), 10, 7);
add_filter("oxy_component_css_styles", array( $this, "generate_id_css"), 10, 5);
// add some data to Oxygen builder
add_action("ct_builder_ng_init", array( $this, "init_api_data") );
// options that allowed to be used in media, states and classes
add_filter("oxy_options_white_list", array( $this, "white_list_options_callback") );
add_filter("oxy_base64_encode_options", array( $this, "base64_options_callback") );
// options that is not plain CSS properties or have a registered CSS selector that will render it
// by default all Elements API options are "not_css_options"
add_filter("ct_not_css_options", array( $this, "not_css_options_callback") );
add_action("wp_enqueue_scripts", array( $this, "enqueue_scripts") );
add_action("wp_enqueue_scripts", array( $this, "enqueue_styles") );
add_action('wp_ajax_oxy_load_controls_'.$this->params['tag'], array( $this, 'register_ajax_controls'), 100 );
}
/**
* Element [shortcode] HTML output
*
* @author Ilya K.
* @since 2.4
*/
public function shortcode($atts, $content=null, $name=null) {
if ( ! $this->component->validate_shortcode( $atts, $content, $name ) ) {
return '';
}
// set OxygenElement options to old CT_Component options
$this->component->options['params'] = $this->get_controls();
$options = $this->component->set_options( $atts );
$options['tag'] = isset($options['html_tag']) ? $options['html_tag'] : $this->params['html_tag'];
// save $options to instance
$this->params["shortcode_options"] = $options;
// prepare HTML output
if (isset($this->params['php_callback'])) {
if ( is_callable($this->params['php_callback']) || function_exists($this->params['php_callback'])) {
ob_start();
// base 64 decode $options
$processed_options = $this->unprefix_options($options);
$processed_options = $this->base64_decode_options($processed_options);
call_user_func_array($this->params['php_callback'], array($processed_options, $this->unprefix_options($this->defaults), $content));
$html = ob_get_clean();
}
else {
$this->add_error($this->params['php_callback'] . " PHP Callback does not exist");
}
}
else {
$html = $this->params['html'];
$html = str_replace("%%CONTENT%%", do_shortcode($content), $html);
$html = $this->filterOptions($html);
}
ob_start();
?>
<<?php echo esc_attr($options['tag'])?> id="<?php echo esc_attr($options['selector']); ?>" class="<?php if(isset($options['classes'])) echo esc_attr($options['classes']); echo " " . $this->params['wrapper_class']; ?>"><?php echo $html; ?></<?php echo esc_attr($options['tag'])?>>
<?php
if ( isset($this->params['inlineJS']) ) {
$this->JSOutput($this->params['inlineJS']);
}
$this->outputErrors();
$html = ob_get_clean();
return $html;
}
/**
* Set Elements HTML tag
*
* @author Ilya K.
* @since 2.4
*/
public function setTag($tag) {
if (is_array($tag) && isset($tag['default'])) {
$tag = $tag['default'];
}
$this->params['html_tag'] = $tag;
}
/**
* Element HTML setter
*
* @author Ilya K.
* @since 2.4
*/
public function HTML($html, $wrapper_class='') {
// save HTML template to pass to Angular
$this->params['html_template'] = $this->prefix_options($html);
$this->params['wrapper_class'] = is_array($wrapper_class) ? implode(" ", $wrapper_class) : $wrapper_class;
// this one to render inside shortocde, remove keywords from it
$html = preg_replace_callback(
"/OXY_EDITABLE\([^)]+\)/",
array( $this, "oxy_editable"),
$html);
$this->params['html'] = $this->prefix_options($html);
}
/**
* Set Element PHPCallback and add proper hooks to show AJAX button and handle AJAX requests
*
* @author Ilya K.
* @since 2.4
*/
public function PHPCallback($callback, $wrapper_class='') {
$this->params['php_callback'] = $callback;
$this->params['wrapper_class'] = is_array($wrapper_class) ? implode(" ", $wrapper_class) : $wrapper_class;
add_action("ct_toolbar_component_settings", array( $this, "apply_params_button"), 100 );
add_action('wp_ajax_oxy_render_' . $this->get_tag(), array( $this, "ajax_render_callback") );
add_filter("template_include", array( $this, "single_template"), 100 );
}
/**
* @author Ilya K.
* @since 2.4
*/
public function addApplyParamsButton() {
add_action("ct_toolbar_component_settings", array( $this, "apply_params_button"), 100 );
}
/**
* @author Ilya K.
* @since 3.2
*/
public function removeApplyParamsButton() {
remove_action("ct_toolbar_component_settings", array( $this, "apply_params_button"), 100 );
}
/**
* Callback to remove OXY_EDITABLE attr and save defaults value
*
* @author Ilya K.
* @since 2.4
*/
private function oxy_editable($matches) {
// get the value inside the parentheses
// '/\(([^)]+)\)/'
preg_match('/\(([^)]+)\)/', $matches[0], $option);
if (!$option) {
return $matches;
}
$option = explode("|", $option[1]);
// default value is mandatory for OXY_EDITABLE
if (!$option[1]) {
return $matches;
}
$prefixed_name = $this->prefix_option($option[0]);
$this->defaults[$prefixed_name] = $option[1];
// this is obviously not a CSS options
$this->add_to_not_css_options($prefixed_name);
//var_dump($this->defaults);
return "";
}
/**
* Generate proper template to use in Angular. Not for public use
* Action hook: 'oxy_components_html_templates'
*
* @author Ilya K.
* @since 2.4
*/
public function html_template($templates) {
$template = array();
// prepare HTML
$html = $this->params['html_template'];
// nestable elements
if (strpos($html, "%%CONTENT%%") !== false || (isset($this->params['nestable']) && $this->params['nestable'])){
$template["nestable"] = "true";
}
$html = str_replace("%%CONTENT%%", "", $html);
$template["html"] = $html;
// this will tell JS to make AJAX request
if ($this->params['php_callback']){
$template["phpCallback"] = "true";
}
// add wrapper class if defined
if ($this->params['wrapper_class']!==""){
$template["class"] = $this->params['wrapper_class'];
}
// add inline JS if defined
if ($this->params['inlineJS']!==""){
$template["js"] = $this->params['inlineJS'];
}
// if has some arbitrary js script that runs after the item is loaded
if ($this->params['has_js']!==""){
$template["has_js"] = $this->params['has_js'];
}
/**
* Other options
*/
// rebuild on DOM change
if (isset($this->params["other_options"]['rebuild_on_dom_change']) && $this->params["other_options"]['rebuild_on_dom_change']==true){
$template["rebuildOnDOMChange"] = "true";
}
// only child
if (isset($this->params["other_options"]['only_child'])){
$template["onlyChild"] = $this->params["other_options"]['only_child'];
}
// only parent
if (isset($this->params["other_options"]['only_parent'])){
$template["onlyParent"] = $this->params["other_options"]['only_parent'];
}
if (isset($this->css_selectors)){
$template["CSSSelectors"] = $this->css_selectors;
}
if (is_array($this->registeredSelectors)) {
foreach ($this->registeredSelectors as $selector) {
$template["registeredSelectors"][$selector->getSelector()] = $selector->propertiesArray();
}
}
if (isset($this->complexProperties) && is_array($this->complexProperties)) {
$template["complexProperties"] = $this->complexProperties;
}
if (isset($this->valuesCSS) && is_array($this->valuesCSS)) {
$template["valuesCSS"] = $this->valuesCSS;
}
if (isset($this->params['pageCSS']) && !empty($this->params['pageCSS'])) {
$template["pageCSS"] = $this->params['pageCSS'];
}
if (isset($this->params['prefilledComponentStructure']) && is_array($this->params['prefilledComponentStructure'])) {
$template["prefilledComponentStructure"] = $this->params['prefilledComponentStructure'];
}
if (isset($this->rebuild_options) && is_array($this->rebuild_options)) {
$template["rebuildTriggerOptions"] = $this->rebuild_options;
}
$template['HTMLTag'] = $this->params['html_tag'];
// push to other templates
$templates[$this->params['tag']] = $template;
return $templates;
}
/**
* Enqueue JS provided by user
*
* @author Ilya K.
* @since 2.4
*/
public function JS($scripts, $dep=array()) {
// save scripts to include later with wp_enqueue_scripts
$this->scripts['scripts'] = $scripts;
$this->scripts['dep'] = $dep;
}
/**
* wp_enqueue_scripts callback to include elements scripts
*
* @author Ilya K.
* @since 2.4
*/
public function enqueue_scripts() {
$js = isset($this->scripts['scripts']) ? $this->scripts['scripts'] : false;
$dep = isset($this->scripts['dep']) ? $this->scripts['dep'] : array();
if (is_array($js)) {
foreach ($js as $key => $path) {
wp_enqueue_script( $this->params['tag'].'-'.$key, $this->params['dir_url'] . $path, $dep);
}
}
else if( !empty($js) ) {
wp_enqueue_script( $this->params['tag'], $this->params['dir_url'] . $js, $dep);
}
}
/**
* Add custom inline JS code that goes just after the component
*
* @author Ilya K.
* @since 2.4
*/
public function inlineJS($js) {
$this->params['inlineJS'] = $this->prefix_options($js);
}
/**
* Add custom inline JS code that goes just after the component
*
* @author Ilya K.
* @since 3.4
*/
public function builderInlineJS($js) {
$this->params['builderInlineJS'] = $this->prefix_options($js);
}
/**
* Add custom JS code to footer
*
* @author Ilya K.
* @since 2.4
*/
public function footerJS($js) {
$this->params['footerJS'] = $js;
add_action("wp_footer", array( $this, "footer_js_output") );
}
/**
* Echo custom JS code added by user to footer
* Action hook: 'wp_footer'
*
* @author Ilya K.
* @since 2.4
*/
public function footer_js_output() {
if ( isset($this->params['footerJS']) ) {
$this->JSOutput($this->params['footerJS']);
}
}
/**
* Enqueue CSS files
*
* @author Ilya K.
* @since 2.4
*/
public function CSS($styles) {
$this->styles = $styles;
}
/**
* wp_enqueue_scripts callback to include Elemnet styles
*
* @author Ilya K.
* @since 2.4
*/
public function enqueue_styles() {
$css = $this->styles;
if (is_array($css)) {
foreach ($css as $key => $path) {
wp_enqueue_style( $this->params['tag'].'-'.$key, $this->params['dir_url'] . $path);
}
}
else {
wp_enqueue_style( $this->params['tag'], $this->params['dir_url'] . $css);
}
}
/**
* Static component CSS to add to universal.css
*
* @author Ilya K.
* @since 2.4
*/
public function staticCSS($css) {
$this->params['staticCSS'] = $css;
add_action("oxygen_default_classes_output", array( $this, "static_css_output") );
}
/**
* Echo element's default static CSS
* Action hook: 'oxygen_default_classes_output'
*
* @author Ilya K.
* @since 2.4
*/
public function static_css_output() {
if ( isset($this->params['staticCSS']) ) {
$css = str_replace("%%ASSETS_PATH%%", $this->assets_path, $this->params['staticCSS']);
// filter for additional features
// since 3.0
$css = apply_filters("oxy_elements_api_static_css_output", $css);
echo $css;
}
}
/**
* Page CSS to add to page files cache .css
*
* @author Ilya K.
* @since 2.4
*/
public function pageCSS($css) {
$this->params['pageCSS'] = $css;
add_action("oxygen_id_styles_output-". $this->get_tag(), array( $this, "page_css_output") );
}
/**
* Echo element's default static CSS
* Action hook: 'oxygen_id_styles_output-{element slug}'
*
* @author Ilya K.
* @since 2.4
*/
public function page_css_output() {
if ( isset($this->params['pageCSS']) ) {
$css = str_replace("%%ASSETS_PATH%%", $this->assets_path, $this->params['pageCSS']);
// filter for additional features
// since 3.0
$css = apply_filters("oxy_elements_api_page_css_output", $css);
echo $css;
}
}
/**
* Static component CSS to add to universal.css
*
* @author Ilya K.
* @since 2.4
*/
public function builderCSS($css) {
$this->params['builderCSS'] = $css;
add_filter("oxygen_iframe_styles", array( $this, "builder_css_output") );
}
/**
* Echo element's default static CSS
* Action hook: 'oxygen_default_classes_output'
*
* @author Ilya K.
* @since 2.4
*/
public function builder_css_output($styles) {
if ( isset($this->params['builderCSS']) ) {
return $styles . "<style id=\"{$this->get_tag()}-styles\" type=\"text/css\">" . $this->params['builderCSS'] . "</style>";
}
return $styles;
}
/**
* Add custom CSS selectors
*
* @author Ilya K.
* @since 2.4
*/
public function addCSSSelector($selector) {
$this->css_selectors[] = $selector;
}
/**
* Add custom CSS selectors
*
* @author Ilya K.
* @since 2.4
*/
public function registerCSSSelector($selector="") {
if (!isset($this->registeredSelectors[$selector])) {
$this->registeredSelectors[$selector] = new OxygenSelector($selector, $this);
}
return $this->registeredSelectors[$selector];
}
/**
* Generate default '.element-name' CSS styles to be added to universal.css
* callback to 'oxygen_default_classes_output' action hook
*
* @author Ilya
* @since 2.4
*/
public function generate_defaults_css() {
$options = $this->initial_defaults;
$styles = "";
// generate '.tag-name' defaults class
if ( is_array( $options ) ) {
foreach ( $options as $name => $value ) {
// skip uints
if ( strpos( $name, "-unit") ) {
continue;
}
// skip empty values
if ( $value === "" ) {
continue;
}
// handle global fonts
if ( $name == "font-family" && is_array( $value ) ) {
$value = $global_settings['fonts'][$value[1]];
if ( strpos($value, ",") === false && strtolower($value) != "inherit" ) {
$value = "'$value'";
}
}
// handle unit options
if ( isset($options[$name.'-unit']) && $options[$name.'-unit'] ) {
// set to auto
if ( $options[$name.'-unit'] == 'auto' ) {
$value = 'auto';
}
// or add unit
else {
$value .= $options[$name.'-unit'];
}
}
if (in_array(str_replace("-", "_", $name), $this->not_css_options)) {
continue;
}
if ( $value !== "" ) {
$styles .= "$name:$value;\r\n";
}
}
}
if (trim($styles)!==""){
echo ".{$this->params['tag']} {\r\n";
echo $styles;
echo "}\r\n";
}
// run through all custom CSS generation process
echo $this->generate_css($options, true, ".".$this->params['tag']);
}
/**
* Generate CSS styles output for .classes
*
* @author Ilya K.
* @since 2.4
*/
public function generate_classes_css($css, $class, $state, $options, $is_media = false, $is_selector = false, $defaults) {
if ($is_selector) {
return $css;
}
$is_element = false;
foreach ($options as $key => $value) {
if (strpos($key,$this->get_tag())!==false) {
$is_element = true;
break;
}
}
if ($is_element) {
$selector = ".".$class;
$css .= $this->generate_css($options, false, $selector, $state, $defaults);
$is_element = false;
}
return $css;
}
/**
* Generate CSS styles output for #IDs
*
* @author Ilya K.
* @since 2.4
*/
public function generate_id_css($styles, $states, $selector, $class_obj, $defaults) {
if ($class_obj->options['tag'] != $this->params['tag']){
return $styles;
}
// #id styles should not contain any defaults
$this->fallbackToDefaults = false;
if (is_array($states)) {
$selector = "#".$selector;
foreach ($states as $state_name => $state_options) {
// skip classes
if ($state_name=='classes')
continue;
// skip string options and empty arrays
if (!is_array($state_options)||empty($state_options))
continue;
$styles .= $this->generate_css($state_options, false, $selector, $state_name, $defaults);
}
}
$this->fallbackToDefaults = true;
// run only once per element, not for each element appearence on the page
if (!isset($this->oxygen_id_styles_output[$this->get_tag()])) {
ob_start();
do_action("oxygen_id_styles_output-".$this->get_tag());
$page_css = ob_get_clean();
$this->oxygen_id_styles_output[$this->get_tag()] = true;
$styles = $page_css . $styles;
}
$styles = apply_filters( "oxygen_id_styles_filter-".$this->get_tag(), $styles, $states, $selector );
return $styles;
}
/**
* Generate CSS output
*
* @author Ilya K.
* @since 2.4
*/
private function generate_css($params=false, $class=false, $selector="", $state_name="", $defaults=array()) {
if ($state_name=='original'||!$state_name=='original'){
$state_name = "";
}
else {
$state_name = ":".$state_name;
}
$css = "";
$this->params["shortcode_options"] = $params;
// process custom registered CSS selectors
if (isset($this->registeredSelectors) && is_array($this->registeredSelectors)) {
foreach ($this->registeredSelectors as $registeredSelector) {
$styles = $registeredSelector->generateCSS($params, $state_name);
if ($styles) {
$css .= $this->prefixCSSSelectors($styles,$selector);
}
}
}
// process CSS values
// merge controls from Primary Section and all Custom Sections
$controls = array_merge($this->params['controls'], $this->get_section_controls_objects());
foreach ($controls as $control) {
if (is_object($control)) {
$value_css = $control->getValueCSS($params, $selector);
$css .= $this->prefixCSSSelectors($value_css, $selector);
}
}
return $css;
}
/**
* Output various API data to builder ng-init
*
* @author Ilya K.
* @since 2.4
*/
public function init_api_data() {
// presets
$output = json_encode( $this->presets );
$output = htmlspecialchars( $output, ENT_QUOTES );
echo "APIPresets=$output;";
}
/**
* AJAX callback to render Element in Oxygen. Used when AJAX call made to admin-ajax.php
*
* @author Ilya K.
* @since 2.4
*/
public function ajax_render_callback() {
define("OXY_ELEMENTS_API_AJAX", true);
oxygen_vsb_ajax_request_header_check();
$component_json = file_get_contents('php://input');
$component = json_decode( $component_json, true );
$children = $component['component']['children'];
$options = $component['options']['original'];
$options['selector'] = $component['options']['selector'];
$options = $this->component->keys_dash_to_underscore($options);
// save $options to instance
$this->params["shortcode_options"] = $options;
// WooCo Product Builder fix to make child elements show defined post data
if (isset($component['options']['product_builder_id']) && $component['options']['product_builder_id']) {
global $post;
$product = wc_get_product($component['options']['product_builder_id']);
if ($product) {
$post = get_post($component['options']['product_builder_id']);
setup_postdata($post);
}
}
$content = null;
if( isset($component['component']['children']) && !empty(isset($component['component']['children'])) ) {
$content = "[oxy-empty-shortcode]";//= $component['component']['children'];
}
if ( is_callable( $this->params['php_callback'] ) || function_exists( $this->params['php_callback'] ) ) {
wp_enqueue_scripts();
call_user_func_array( $this->getParam('php_callback'), array( $this->unprefix_options($options), $this->unprefix_options($this->defaults), $content) );
$wp_scripts = wp_scripts();
$wp_styles = wp_styles();
wp_print_scripts( $wp_scripts->queue );
//wp_print_styles( $wp_styles->queue );
if ( isset($this->params['inlineJS']) ) {
$this->JSOutput($this->params['inlineJS']);
}
if ( isset($this->params['builderInlineJS']) ) {
$this->JSOutput($this->params['builderInlineJS']);
}
}
else {
echo $this->params['php_callback'] . " PHP Callback does not exist";
}
// add page-width media (taken from CT_Component::build_css())
// TODO: Wrap this in a function?
global $media_queries_list;
global $media_queries_list_above;
$media_queries_list["page-width"]["maxSize"] = oxygen_vsb_get_page_width($ct_template_id).'px';
$media_queries_list_above["page-width"]["minSize"] = (oxygen_vsb_get_page_width($ct_template_id)+1).'px';
// Custom CSS added with customCSS() for "oxygen_id_styles_filter-..." hook
?>
<style type="text/css">
<?php echo apply_filters( "oxygen_id_styles_filter-".$this->get_tag(), "", $component['options'], '#'.$component['options']['selector'] ); ?>
</style>
<?php
die();
}
/**
* 'template_include' filter used when AJAX call made to page/post permalink
*
* @author Ilya K.
* @since 2.4
*/
public function single_template( $template ) {
$new_template = '';
if( isset($_REQUEST['action']) && stripslashes($_REQUEST['action']) == 'oxy_render_' . $this->get_tag()) {
if ( file_exists(CT_FW_PATH . '/api/single.template.php') ) {
global $oxy_api_element;
$oxy_api_element = $this;
$new_template = CT_FW_PATH . '/api/single.template.php';
}
}
if ( '' != $new_template ) {
return $new_template ;
}
return $template;
}
/**
* 'template_include' filter used when AJAX call made to page/post permalink
*
* @author Ilya K.
* @since 2.4
*/
public function nestable() {
$this->params['nestable'] = true;
}
/**
* Register a complex property like "box-shadow: {a} {b} {c} {d}"
* Use mapProperty("box-shadow>a", ...) to map each part.
*
* @author Ilya K.
* @since 2.4
*/
public function defineProperty($property, $value) {
$this->complexProperties[$property] = $value;
}
/**
* Prefix selectors with element #id, commas and media queries supported
*
* @author Ilya K.
* @since 3.0
*/
public function prefixCSSSelectors($css="", $selector) {
$parts = explode('}', $css);
foreach ($parts as &$part) {
if (empty($part)) {
continue;
}
$partDetails = explode('{',$part);
// check for @media queries
if (substr_count($part,"{")==2) {
$mediaQuery = $partDetails[0]."{";
$partDetails[0] = $partDetails[1];
$mediaQueryStarted = true;
}
// prefix comma separated selectors
$subParts = explode(',', $partDetails[0]);
foreach ($subParts as &$subPart) {
if (trim($subPart)=="@font-face") continue;
$subPart = $selector . ' ' . trim($subPart);
}
if (substr_count($part,"{")==2){
// media query start
$part = $mediaQuery."\n".implode(', ', $subParts)."{".$partDetails[2];
}
elseif (empty($part[0]) && $mediaQueryStarted){
// media query end
$mediaQueryStarted = false;
$part = implode(', ', $subParts)."{".$partDetails[2]."}\n"; //finish media query
}
elseif (isset($partDetails[1])){
$part = implode(', ', $subParts)."{".$partDetails[1];
}
}
$css = implode("}\n", $parts);
return $css;
}
/**
* Prefix selectors with element #id, commas and media queries supported
*
* @author Ilya K.
* @since 3.0
*/
public function base64_options_callback($options) {
if (!is_array($options)) {
return $options;
}
$options = array_merge($options, $this->base64_options);
return $options;
}
/**
* Empty AJAX callback to prevent 404 error for Elements that doesn't use the AJAX Controls
*
* @author Ilya K.
* @since 3.4
*/
public function register_ajax_controls() {
die();
}
}