Changeset 3429507
- Timestamp:
- 12/30/2025 09:13:50 AM (2 months ago)
- Location:
- unlimited-elements-for-elementor/trunk
- Files:
-
- 40 edited
-
assets_libraries/filters/ue_filters.js (modified) (5 diffs)
-
changelog.txt (modified) (1 diff)
-
inc_php/framework/google/client.class.php (modified) (3 diffs)
-
inc_php/framework/google/places/place.class.php (modified) (1 diff)
-
inc_php/framework/google/places/place_review.class.php (modified) (7 diffs)
-
inc_php/framework/google/places/places_services.class.php (modified) (4 diffs)
-
inc_php/framework/settings_output.class.php (modified) (3 diffs)
-
inc_php/unitecreator_actions.class.php (modified) (1 diff)
-
inc_php/unitecreator_addon.class.php (modified) (8 diffs)
-
inc_php/unitecreator_addons.class.php (modified) (3 diffs)
-
inc_php/unitecreator_api_integrations.class.php (modified) (16 diffs)
-
inc_php/unitecreator_entrance_animations.class.php (modified) (3 diffs)
-
inc_php/unitecreator_helperhtml.class.php (modified) (3 diffs)
-
inc_php/unitecreator_import_changelog.class.php (modified) (2 diffs)
-
inc_php/unitecreator_output.class.php (modified) (19 diffs)
-
inc_php/unitecreator_params_processor.class.php (modified) (6 diffs)
-
inc_php/unitecreator_rss.class.php (modified) (9 diffs)
-
inc_php/unitecreator_schema.class.php (modified) (1 diff)
-
inc_php/unitecreator_settings.class.php (modified) (10 diffs)
-
includes.php (modified) (1 diff)
-
js/manager/unitecreator_manager_actions_addons.js (modified) (1 diff)
-
js/manager/unitecreator_manager_actions_inline.js (modified) (3 diffs)
-
js/manager/unitecreator_manager_items.js (modified) (8 diffs)
-
js/settings.js (modified) (50 diffs)
-
js/unitecreator_addon_config.js (modified) (2 diffs)
-
js/unitecreator_addondefaults_admin.js (modified) (1 diff)
-
provider/assets/provider_admin.js (modified) (4 diffs)
-
provider/core/unlimited_elements/dialog_param_elementor.class.php (modified) (1 diff)
-
provider/core/unlimited_elements/gutenberg/assets/gutenberg_integrate.js (modified) (34 diffs)
-
provider/core/unlimited_elements/settings/general_settings_el.xml (modified) (1 diff)
-
provider/functions_wordpress.class.php (modified) (1 diff)
-
provider/provider_browser.class.php (modified) (1 diff)
-
provider/provider_helper.class.php (modified) (1 diff)
-
provider/provider_params_processor_multisource.class.php (modified) (1 diff)
-
provider/provider_settings.class.php (modified) (16 diffs)
-
provider/woocommerce_integrate.class.php (modified) (5 diffs)
-
readme.txt (modified) (3 diffs)
-
release_log.txt (modified) (1 diff)
-
unlimited_elements.php (modified) (2 diffs)
-
views/addondefaults.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
unlimited-elements-for-elementor/trunk/assets_libraries/filters/ue_filters.js
r3379552 r3429507 9 9 var t = this; 10 10 11 var g_showDebug = false;11 var g_showDebug = true; 12 12 var g_debugInitMode = false; 13 13 … … 462 462 if(!isInitAfter) 463 463 isInitAfter = isSpecialFilterInitAfter(objFilter, objGrid); 464 464 465 465 if(g_showDebug){ 466 466 trace("init after: "+isInitAfter); … … 2275 2275 operateAjax_setHtmlSyngGrids(response, objGrid, isLoadMore); 2276 2276 2277 objGrid.trigger(g_vars.EVENT_AJAX_REFRESHED );2277 objGrid.trigger(g_vars.EVENT_AJAX_REFRESHED,[{isLoadMore:isLoadMore}]); 2278 2278 g_objBody.trigger(g_vars.EVENT_AJAX_REFRESHED_BODY, [objGrid]); 2279 2279 2280 2280 //trigger body as well 2281 2281 … … 3763 3763 */ 3764 3764 function isSpecialFilterInitAfter(objFilter, objGrid){ 3765 3765 3766 3766 var type = getFilterType(objFilter); 3767 3768 if(type != g_types.PAGINATION )3767 3768 if(type != g_types.PAGINATION && type != g_types.LOADMORE) 3769 3769 return(false); 3770 3770 … … 3775 3775 3776 3776 if(g_showDebug == true) 3777 trace("Set paginationto ajax init");3777 trace("Set "+type+" to ajax init"); 3778 3778 3779 3779 return(true); -
unlimited-elements-for-elementor/trunk/changelog.txt
r3403331 r3429507 1 1 2 2 == Changelog == 3 4 5 version 2.0.2 2025-12-30 = 6 7 Plugin Changes: 8 9 * Feature: add google reviews from serp api feature 10 * Fix - fixed double tax pricing in woocommerce variation 11 * Fix - fixed the entrance animation together with load more issue 12 * Fix - some blocks related bug fixes 13 * Fix - fixed default items not loading js issues 14 * Fix - fixed some small security issue 15 16 Widgets Changes: 17 18 * Feature: Unlimited Grid (Pro) - Added Title HTML Tag option for better SEO and accessibility. 19 * Feature: Content Carousel (Free) - Enhanced custom SVG icon support for navigation arrows, and added title and description text shadow options. 20 * Feature: Content Slider (Free) - Added title HTML tag option, improved links by adding link attributes, and introduced Slide Border, Overlay Hover, and Overlay Hover Transition Duration options. 21 * Feature: Video on Hover (Free) - Added a "Schema" option to enable schema markup support for better SEO and AI understand your content for increased visibility in AI powered search features. 22 * Feature: Text Field (Free) - Introduced a dynamic URL Autopopulate feature, allowing fields to automatically retrieve and fill values from custom query string parameters upon page load 23 * Feature: Off Canvas (Pro) - Added Background Color Hover and Text Color Hover options, allowing finer visual control over hover states and enabling consistent styling of the Trigger Button element. 24 * Feature: Side Menu (Free) - Added support for the Conditions widget, enabling seamless control over the Side Menu by allowing it to be opened, closed, or toggled dynamically based on defined conditions 25 * Feature: Conditions (Free) - Added three new actions UE Open Side Menu, UE Close Side Menu, and UE Toggle Side Menu allowing full programmatic control over the Side Menu widget behavior 26 * Feature: Snow Background (Free) - Added Speed Mobile option, allowing precise control over animation and interaction speed specifically for mobile devices, ensuring smoother performance, better usability on smaller screens 27 * Feature: Swipe Carousel (Pro) - Added Title Tag option, allowing users to define the HTML tag for the title element to better match the page structure, improve semantic markup, and enhance SEO and accessibility control. 28 * Feature: Grid Gallery (Pro) - Added Enable Link On Main Image option, allowing the main image within each item to function as a clickable link. 29 * Feature: Post Grid (Free) - Made the Title Spacing option fully responsive, allowing different spacing values to be applied across desktop, tablet, and mobile for more precise control of layout and typography. 30 * Feature: Expanding Content Cards (Free) - Added Sub Title option, enabling users to include an additional descriptive text line beneath the main title. 31 * Feature: Vertical Curved Timeline (Free) - Added "Content Vertical Position" option when the image option is enabled, also introduced "Alternate Image Alignment" option for improved design and customization. 32 * Feature: Icon Accordion (Free) - Added multi-source support along with pagination and filtering features, and improved the item color options for easier styling. 33 * Feature: Dynamic Post Popup (Pro) - Added Arrows Type option, introducing support for choosing between icon-based arrows and text-based arrows. 34 * Feature: Portfolio Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 35 * Feature: Overlay Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 36 * Feature: Material Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 37 * Feature: Icon Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 38 * Feature: Unlimited Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 39 * Feature: Logo Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 40 * Feature: Woo Product Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 41 * Feature: Post Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 42 * Feature: Repeater Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 43 * Feature: Flip Box Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 44 * Feature: Loop Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 45 * Feature: Notification (Free) - The widget's accessibility was improved by replacing the close div with a semantic <button> element, adding the essential aria-label="Close Notification" for screen reader clarity, applying role="status" to the main container for conveying passive informative content. 46 * Feature: Contact Form 7 (Free) - Improved accessibility of the widget by enabling proper keyboard navigation within form elements. 47 * Feature: Fullscreen Menu (Free) - Added Expand Collapse Icon Spacing option, allowing users to precisely control the distance between the expand/collapse icon and the text. 48 * Feature: Gradient Underline Text Effect (Free) - Made the Text Align option responsive, allowing different alignment settings for desktop, tablet, and mobile. 49 * Feature: Post Accordion (Free) - Added Icon Spacing option introduced a new control that allows adjusting the distance between the icon and its adjacent elements. 50 * Feature: Team Member Carousel (Free) - Added Icon One, Icon Two, Icon Three, Icon Four and Link One, Link Two, Link Three, Link Four options to the Source options 51 * Feature: Unlimited Google Maps (Free) - Added Scroll To Active Navigation Item option, ensuring that navigation automatically scrolls to keep the active item visible. 52 * Feature: 360 Panorama Virtual Tour (Pro) - Enhanced the widget's accessibility by applying ARIA attributes (role, aria-label) and tabindex="0" to interactive controls, allowing keyboard-only users to activate them by simulating a click using the Enter or Space key. 53 * Fix: Repeater Table (Pro) - Accessibility for the widget was improved by implementing the scope="col" attribute on all header cells to explicitly link data to headings 54 * Fix: Restaurant Menu (Free) - Accessibility for the widget was improved by adding role="list" and role="listitem" for better navigation structure, converting the "See More" toggle into a keyboard-accessible button with aria-expanded states. 55 * Fix: Loop Grid (Pro) - Fixed an issue where the Empty Message Type: Template was displayed even when the Loop Widget had successfully rendered items, causing the empty state template to appear incorrectly alongside actual content 56 * Fix: Border Hero (Free) - The button field was incorrectly configured as a text field and has now been changed to a proper link field. A security vulnerability was also fixed to improve overall safety and stability. 57 * Fix: Content Grid (Free) - Removed empty links to prevent rendering unnecessary anchor elements and to avoid potential accessibility and SEO issues when link fields are left blank 58 * Fix: Comparison List (Pro) - Fixed an issue where column widths were not working correctly on mobile devices, and resolved RTL layout issues. 59 * Fix: Horizontal Timeline (Free) - Removed empty links to prevent rendering unnecessary anchor elements and to avoid potential accessibility and SEO issues when link fields are left blank. 60 * Fix: Price Range Filter (Pro) - Fixed an issue where slider thumbnail elements were displayed incorrectly on RTL (right-to-left) websites, ensuring proper alignment, order, and interaction consistency across all supported directions 61 * Fix: Random Content (Free) - Accessibility for the Random Content widget was improved by converting the shuffle trigger into a semantic <button>, marking the shuffle icon as aria-hidden="true" to avoid redundant noise, and applying aria-live="polite" to the content container 62 * Fix: Stacking Cards (Free) - Fixed an issue where the Alternate Page Position option was not functioning correctly on the live site and was only applied inside the Elementor editor 63 * Fix: Protected Content (Free) - Accessibility for the Protected Content widget was improved by correcting the form structure to properly link the <label> (title) to the password <input> using the "for" and "id" attributes, adding aria-required="true" to the input 64 * Fix: Cookie Consent (Free) - Accessibility for the Cookie Consent widget was improved by switching interactive <div> elements to semantic <button> tags, and ensuring the main container dynamically uses role="dialog" and aria-modal="true" only when the banner is visible, thereby guiding keyboard and screen reader focus correctly. 65 * Fix: Age Verification (Free) - The Age Verification widget's accessibility was improved by adding role="dialog", aria-modal="true", and descriptive aria-label attributes to all input fields and buttons for better screen reader and keyboard navigation 66 * Fix: Content Tabs (Free) - Fixed an issue where theme-defined padding styles for ul elements were being unintentionally overridden by the widgets CSS, ensuring better compatibility with various themes and preserving the original list spacing and layout as defined by the active theme 67 * Fix: Image Tooltip (Free) - Added aria-describedby attribute to the main widget wrapper to improve accessibility, and included additional HTML attributessuch as alt for the imageto ensure better semantic structure, screen-reader support, and overall accessibility compliance 68 * Fix: Event Box (Free) - Added ARIA attributes to interactive elements and link labels for improved screen reader compatibility. 69 * Fix: Calendar (Pro) - Increased the CSS priority of the Event Title element, ensuring its styling reliably overrides conflicting theme rules and displays consistently across different layouts and environments. 70 * Fix: Masonry & Justified Gallery (Free) - Fixed an issue where the Tile Text Panel styling options were not appearing in the widget settings panel, ensuring all relevant controls are now properly displayed and accessible for customization. 71 * Fix: Submit Button (Free) - Fixed issue where attempting to proceed to the next step of Multi Source widget from the first page without filling mandatory fields caused unwanted spacing to be repeatedly added to the form 72 * Fix: Vertical Curved Timeline (Free) - Fixed an issue where "Button Spacing" option was not working. 73 * Fix: Vertical Curved Timeline (Free) - Fixed issue where, when the image element was disabled and the content inside an item had a width below 100%, items with an even item count were not aligning correctly to the side, ensuring proper layout consistency across all configurations. 74 * Fix: Justified Image Carousel (Pro) - Fixed an issue on touch devices where opening the lightbox required two taps instead of one, ensuring the lightbox now opens correctly on a single tap for a smoother and more responsive user experience. 75 * Fix: QR Code (Pro) - Accessibility of the QR Code widget was improved by applying role="img" to the SVG container to define it as a graphic and using aria-label to provide the visible title as the accessible name, ensuring screen readers can announce the image's content. 76 * Fix: Animated Mouse Scroll Icon (Free) - The accessibility of the widget was improved by adding aria-label="Scroll Down" and role="button" to the link wrapper and using aria-hidden="true" on the decorative animation elements 77 * Fix: Vertical Curved Timeline (Free) - Fixed issue when button wasn't clickable for all items except last one. 78 * Fix: Repeater Tabs (Free) - Fixed issue when Repeater Tabs couldn't display all content with JSON / CSV Repeater Source. 79 * Fix: Social Share Buttons (Free) - Fixed an issue where the Telegram share option was redirecting to Xing instead of Telegram. 80 * Release: Person Schema (Pro) - Person Schema adds structured data that defines an individual for search engines. Improve SEO by clearly connecting a person to content, roles, and social profiles. 81 3 82 4 83 version 2.0.1 2025-11-26 = -
unlimited-elements-for-elementor/trunk/inc_php/framework/google/client.class.php
r3379552 r3429507 62 62 */ 63 63 protected function get($endpoint, $params = array()){ 64 64 65 65 return $this->request(UEHttpRequest::METHOD_GET, $endpoint, $params); 66 66 } … … 108 108 $url = $this->getBaseUrl() . $endpoint; 109 109 $query = ($method === UEHttpRequest::METHOD_GET) ? $params : array(); 110 $body = ($method !== UEHttpRequest::METHOD_GET) ? $params : array();110 $body = ($method !== UEHttpRequest::METHOD_GET) ? $params : array(); 111 111 112 112 if(empty($params[self::PARAM_QUERY]) === false){ … … 151 151 }); 152 152 153 153 154 $response = $request->request($method, $url); 154 155 $data = $response->json(); -
unlimited-elements-for-elementor/trunk/inc_php/framework/google/places/place.class.php
r2969618 r3429507 9 9 */ 10 10 public function getReviews(){ 11 11 12 12 $reviews = $this->getAttribute("reviews", array()); 13 13 $reviews = UEGoogleAPIPlaceReview::transformAll($reviews); 14 14 15 15 return $reviews; 16 16 } 17 17 18 /** 19 * get place info if available 20 */ 21 public function getPlaceInfo(){ 22 23 $arrInfo = $this->getAttribute("place_info"); 24 25 return($arrInfo); 26 } 27 28 /** 29 * get place info if available 30 */ 31 public function getSearchParams(){ 32 33 $arrParams = $this->getAttribute("search_parameters"); 34 35 return($arrParams); 36 } 37 38 /** 39 * get search meta data 40 */ 41 public function getMetaData(){ 42 43 $arrParams = $this->getAttribute("search_metadata"); 44 45 return($arrParams); 46 } 47 48 49 18 50 } -
unlimited-elements-for-elementor/trunk/inc_php/framework/google/places/place_review.class.php
r3329756 r3429507 3 3 class UEGoogleAPIPlaceReview extends UEGoogleAPIModel{ 4 4 5 private $isSerp = false; 6 7 /** 8 * set that the review is from serp source 9 */ 10 public function setSerpSource(){ 11 12 $this->isSerp = true; 13 } 14 15 5 16 /** 6 17 * Get the identifier. … … 23 34 */ 24 35 public function getText($asHtml = false){ 25 26 $text = $this->getAttribute("text"); 27 36 37 $name = "text"; 38 if($this->isSerp == true) 39 $name = "snippet"; 40 41 $text = $this->getAttribute($name); 42 28 43 if($asHtml === true) 29 44 $text = nl2br($text); … … 38 53 */ 39 54 public function getRating(){ 40 55 41 56 $rating = $this->getAttribute("rating"); 42 57 … … 54 69 55 70 $time = $this->getTime(); 71 56 72 $date = uelm_date($format, $time); 57 73 … … 66 82 public function getAuthorName(){ 67 83 84 85 if($this->isSerp == true){ 86 87 $user = $this->getAttribute("user"); 88 $name = UniteFunctionsUC::getVal($user, "name"); 89 90 return($name); 91 } 92 93 68 94 $name = $this->getAttribute("author_name"); 69 95 … … 77 103 */ 78 104 public function getAuthorPhotoUrl(){ 79 105 106 if($this->isSerp == true){ 107 $user = $this->getAttribute("user"); 108 $url = UniteFunctionsUC::getVal($user, "thumbnail"); 109 110 return($url); 111 } 112 80 113 $url = $this->getAttribute("profile_photo_url"); 81 114 … … 89 122 */ 90 123 private function getTime(){ 91 124 125 if($this->isSerp == true){ 126 127 $dateString = $this->getAttribute("iso_date"); 128 129 $timestamp = strtotime($dateString); 130 131 return($timestamp); 132 } 133 92 134 $time = $this->getAttribute("time"); 93 135 94 136 return $time; 137 } 138 139 /** 140 * get time ago text 141 */ 142 public function getTimeAgoText(){ 143 144 $name = "relative_time_description"; 145 146 if($this->isSerp == true) 147 $name = "date"; 148 149 $timeAgo = $this->getAttribute($name); 150 151 return($timeAgo); 95 152 } 96 153 -
unlimited-elements-for-elementor/trunk/inc_php/framework/google/places/places_services.class.php
r3379552 r3429507 5 5 */ 6 6 class UEGoogleAPIPlacesService extends UEGoogleAPIClient{ 7 7 8 private $isSerp = false; 9 8 10 /** 9 11 * Get the place details. … … 14 16 * @return UEGoogleAPIPlace 15 17 */ 16 public function getDetails($placeId, $params = array()){ 18 public function getDetails($placeId, $params = array(),$showDebug = false){ 19 20 $this->isSerp = false; 17 21 18 22 $params["place_id"] = $placeId; … … 24 28 else 25 29 $params["reviews_no_translations"] = true; 26 30 27 31 $response = $this->get("/details/json", $params); 28 32 33 //debug 34 if($showDebug == true){ 35 36 HelperHtmlUC::putHtmlDataDebugBox_start(); 37 38 dmp("Official API Request Debug"); 39 40 $paramsForDebug = $params; 41 42 dmp("Send Params"); 43 dmp($paramsForDebug); 44 45 $dataShow = UniteFunctionsUC::modifyDataArrayForShow($response); 46 47 dmp("Response Data"); 48 dmp($dataShow); 49 50 HelperHtmlUC::putHtmlDataDebugBox_end(); 51 } 29 52 30 53 $response = UEGoogleAPIPlace::transform($response["result"]); 31 54 55 32 56 return $response; 33 57 } 58 59 /** 60 * get details using serp function 61 */ 62 public function getDetailsSerp($placeID, $apiKey, $params = array(),$showDebug = false){ 34 63 64 if(empty($apiKey)) 65 UniteFunctionsUC::throwError("No serp api key"); 66 67 $this->isSerp = true; 68 69 $cacheTime = 1440; //day in minutes 70 71 $params["place_id"] = $placeID; 72 $params["api_key"] = $apiKey; 73 74 $headers = array(); 75 76 $request = UEHttp::make(); 77 78 if(!empty($headers)) 79 $request->withHeaders($headers); 80 81 $request->asJson(); 82 $request->acceptJson(); 83 84 $request->cacheTime($cacheTime); 85 $request->withQuery($params); 86 87 $url = "https://serpapi.com/search?engine=google_maps_reviews"; 88 89 //first call 90 91 $response = $request->request(UEHttpRequest::METHOD_GET, $url); 92 93 $data = $response->json(); 94 95 if($showDebug == true){ 96 97 HelperHtmlUC::putHtmlDataDebugBox_start(); 98 99 dmp("Serp API Request Debug"); 100 101 $paramsForDebug = $params; 102 103 $apiKey = UniteFunctionsUC::getVal($paramsForDebug, "api_key"); 104 105 $paramsForDebug["api_key"] = substr($apiKey, 0, 10) . '********'; 106 107 dmp("Send Params"); 108 dmp($paramsForDebug); 109 110 $dataShow = UniteFunctionsUC::modifyDataArrayForShow($data); 111 112 dmp("Response Data"); 113 dmp($dataShow); 114 115 } 116 117 $error = UniteFunctionsUC::getVal($data, "error"); 118 if(!empty($error)){ 119 dmp($data); 120 UniteFunctionsUC::throwError($error); 121 } 122 123 $pagination = UniteFunctionsUC::getVal($data, "serpapi_pagination"); 124 $nextPageToken = UniteFunctionsUC::getVal($pagination, "next_page_token"); 125 126 //second call: 127 128 if(!empty($nextPageToken)){ 129 130 $params["next_page_token"] = $nextPageToken; 131 $params["num"] = 20; 132 133 $request->withQuery($params); 134 135 $response = $request->request(UEHttpRequest::METHOD_GET, $url); 136 $data2 = $response->json(); 137 138 if($showDebug == true){ 139 140 dmp("Second Request - Send Params2"); 141 dmp($params); 142 143 $dataShow2 = UniteFunctionsUC::modifyDataArrayForShow($data); 144 145 dmp("Second Request - Response Data"); 146 dmp($dataShow2); 147 148 } 149 150 $arrReviews2 = UniteFunctionsUC::getVal($data2, "reviews"); 151 152 if(!empty($arrReviews2)) 153 $data["reviews"] += $arrReviews2; 154 155 } 156 157 if($showDebug == true) 158 HelperHtmlUC::putHtmlDataDebugBox_end(); 159 160 161 $place = UEGoogleAPIPlace::transform($data); 162 163 return($place); 164 } 165 166 35 167 /** 36 168 * Get the base URL for the API. … … 39 171 */ 40 172 protected function getBaseUrl(){ 41 42 return "https://maps.googleapis.com/maps/api/place"; 173 174 if($this->isSerp == true) 175 return("https://serpapi.com/search?engine=google_maps_reviews"); 176 else 177 return "https://maps.googleapis.com/maps/api/place"; 178 43 179 } 44 180 -
unlimited-elements-for-elementor/trunk/inc_php/framework/settings_output.class.php
r3397365 r3429507 1067 1067 data-value="<?php echo esc_attr($itemValue); ?>" 1068 1068 class="unite-setting-tabs-item-label" 1069 for="<?php echo esc_attr($itemId); ?>" onclick="console.log('Set Tab: <?= $itemValue ?>')"1069 for="<?php echo esc_attr($itemId); ?>")" 1070 1070 > 1071 1071 <input … … 2134 2134 $strTemplateOptions = ''; 2135 2135 if (!empty($options)) { 2136 $strTemplateOptions = '<template id="' . esc_attr($this->wrapperID) . '-options" >'2136 $strTemplateOptions = '<template id="' . esc_attr($this->wrapperID) . '-options" class="tpl-options">' 2137 2137 . wp_json_encode($options, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) 2138 2138 . '</template>'; … … 2143 2143 $controlsTemplateHTML = ''; 2144 2144 if (!empty($controls)) { 2145 $controlsTemplateHTML = '<template id="' . esc_attr($this->wrapperID) . '-controls" >'2145 $controlsTemplateHTML = '<template id="' . esc_attr($this->wrapperID) . '-controls" class="tpl-controls">' 2146 2146 . wp_json_encode($controls, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) 2147 2147 . '</template>'; -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_actions.class.php
r3329756 r3429507 373 373 374 374 $addons->saveAddonDefaultsFromData($data); 375 375 376 376 HelperUC::ajaxResponseSuccess(esc_html__("Saved", "unlimited-elements-for-elementor")); 377 377 break; -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_addon.class.php
r3397365 r3429507 2200 2200 $objSettings->addGutenbergEditorBackgroundSection(); 2201 2201 2202 2202 2203 2203 //choose if add items chooser 2204 2204 … … 2223 2223 $objSettings->initByCreatorParams($arrParams, $this->paramsCats); 2224 2224 } 2225 2225 2226 2226 2227 2227 //add items repeater … … 3503 3503 */ 3504 3504 public function getTestData($num){ 3505 3505 3506 3506 $arrData = array(); 3507 3507 … … 3510 3510 $fieldName = "test_slot" . $num; 3511 3511 $jsonData = UniteFunctionsUC::getVal($this->data, $fieldName); 3512 3513 3512 3514 3513 if(empty($jsonData)) … … 3520 3519 $arrData = array(); 3521 3520 } 3522 3521 3523 3522 return ($arrData); 3524 3523 } … … 3530 3529 3531 3530 $arrData = $this->getTestData(2); 3532 3531 3533 3532 return ($arrData); 3534 3533 } … … 3538 3537 */ 3539 3538 public function getAllTestData($isJson = false){ 3540 3539 3541 3540 $arrData = array(); 3542 3541 … … 3648 3647 3649 3648 $data = $this->modifyAddonDataConvertToUrlAssets($data); 3650 3649 3651 3650 $dataJson = json_encode($data); 3652 3651 -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_addons.class.php
r3397365 r3429507 1457 1457 */ 1458 1458 public function getTestAddonData($data){ 1459 1459 1460 1460 $objAddon = $this->initAddonByData($data); 1461 1461 … … 1464 1464 $isCombine = UniteFunctionsUC::strToBool($isCombine); 1465 1465 1466 1467 1466 $data = $objAddon->getTestData($slotNum); 1468 1469 1470 1467 1471 1468 if($isCombine === true){ … … 1480 1477 return $output; 1481 1478 } 1482 1479 1480 1483 1481 return $data; 1484 1482 } -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_api_integrations.class.php
r3379552 r3429507 55 55 const GOOGLE_REVIEWS_FIELD_EMPTY_API_KEY = "google_reviews_empty_api_key"; 56 56 const GOOGLE_REVIEWS_FIELD_PLACE_ID = "google_reviews_place_id"; 57 const GOOGLE_REVIEWS_FIELD_API_TEXT = "google_reviews_api_text"; 57 58 const GOOGLE_REVIEWS_FIELD_CACHE_TIME = "google_reviews_cache_time"; 58 59 const GOOGLE_REVIEWS_FIELD_LANG = "google_reviews_lang"; 60 const GOOGLE_REVIEWS_FIELD_SHOW_DEBUG = "google_reviews_show_debug"; 61 const GOOGLE_REVIEWS_SORT_BY = "google_reviews_sort_by"; 59 62 const GOOGLE_REVIEWS_DEFAULT_CACHE_TIME = 10; 63 60 64 61 65 const GOOGLE_SHEETS_FIELD_EMPTY_CREDENTIALS = "google_sheets_empty_credentials"; … … 96 100 97 101 private static $instance = null; 98 102 103 private $googleReviewsShowDebug = false; 104 99 105 private $params = array(); 100 106 … … 185 191 break; 186 192 default: 193 194 if(UniteFunctionsUC::isMaxDebug()){ 195 UniteFunctionsUC::showTrace(); 196 } 197 187 198 UniteFunctionsUC::throwError(__FUNCTION__ . " Error: API type \"$type\" is not implemented"); 199 break; 188 200 } 189 201 … … 195 207 */ 196 208 public function getDataForMultisource($type, $params){ 197 209 198 210 $data = $this->getData($type, $params); 199 211 200 212 switch($type){ 201 213 case self::TYPE_CURRENCY_EXCHANGE: … … 205 217 $data = UniteFunctionsUC::getVal($data, "daily"); 206 218 break; 219 case self::TYPE_GOOGLE_REVIEWS: 220 $data = UniteFunctionsUC::getVal($data, "reviews"); 221 break; 207 222 } 208 223 … … 215 230 * @return array 216 231 */ 217 public function addDataToParams($data, $name ){232 public function addDataToParams($data, $name, $paramType = null){ 218 233 219 234 $params = UniteFunctionsUC::getVal($data, $name, array()); 220 235 $params = UniteFunctionsUC::clearKeysFirstUnderscore($params); 221 236 222 237 try{ 238 223 239 $apiType = UniteFunctionsUC::getVal($params, "api_type"); 240 241 242 //some small fix - get api type by special param type 243 244 if(empty($apiType)){ 245 switch($paramType){ 246 case "reviews": 247 $apiType = "google_reviews"; 248 break; 249 } 250 } 251 224 252 $apiData = $this->getData($apiType, $params); 225 253 226 254 $params["success"] = true; 227 255 $params = array_merge($params, $apiData); … … 241 269 * @return array 242 270 */ 243 public function getSettingsFields( ){244 271 public function getSettingsFields($isSingle = false){ 272 245 273 $fields = array(); 246 274 247 275 if(GlobalsUnlimitedElements::$enableGoogleAPI === true){ 248 276 $fields[self::TYPE_GOOGLE_EVENTS] = $this->getGoogleEventsSettingsFields(); 249 $fields[self::TYPE_GOOGLE_REVIEWS] = $this->getGoogleReviewsSettingsFields( );277 $fields[self::TYPE_GOOGLE_REVIEWS] = $this->getGoogleReviewsSettingsFields($isSingle); 250 278 $fields[self::TYPE_GOOGLE_SHEETS] = $this->getGoogleSheetsSettingsFields(); 251 279 $fields[self::TYPE_YOUTUBE_PLAYLIST] = $this->getYoutubePlaylistSettingsFields(); 252 280 } 253 281 254 282 if(GlobalsUnlimitedElements::$enableCurrencyAPI === true) 255 283 $fields[self::TYPE_CURRENCY_EXCHANGE] = $this->getCurrencyExchangeSettingsFields(); … … 269 297 public function addServiceSettingsFields($settingsManager, $type, $name, $condition = null){ 270 298 271 $fields = $this->getSettingsFields( );299 $fields = $this->getSettingsFields(true); 272 300 $fields = UniteFunctionsUC::getVal($fields, $type); 273 301 274 302 if(empty($fields)) 275 303 return; 276 304 277 305 // add api type 278 306 $params = array(); … … 280 308 281 309 $settingsManager->addHiddenInput($name . "_api_type", $type, "API Type", $params); 282 310 283 311 // add the fields 284 HelperProviderUC::addSettingsFields($settingsManager, $fields, $name, $condition); 285 } 286 312 HelperProviderUC::addSettingsFields($settingsManager, $fields, $name, $condition, true); 313 } 314 315 287 316 /** 288 317 * init the api integrations … … 365 394 $key = $name."_".$key; 366 395 367 $value = empty($this->params[$key]) ? $fallback : $this->params[$key]; 368 369 return $value; 396 $paramValue = UniteFunctionsUC::getVal($this->params, $key); 397 398 if(empty($paramValue)) 399 $paramValue = $fallback; 400 401 return $paramValue; 370 402 } 371 403 … … 464 496 */ 465 497 private function getExchangeRateApiKey(){ 466 498 467 499 $key = $this->getRequiredParam(self::SETTINGS_EXCHANGE_RATE_API_KEY, "Exchange Rate API key"); 468 500 … … 480 512 481 513 $fields = $this->addEmptyApiKeyField($fields, $key, self::CURRENCY_EXCHANGE_FIELD_EMPTY_API_KEY, "Exchange Rate API"); 482 514 483 515 $fields = array_merge($fields, array( 484 516 array( … … 584 616 } 585 617 586 /** 587 * get google reviews settings fields 588 */ 589 private function getGoogleReviewsSettingsFields(){ 590 591 $fields = array(); 592 593 $fields = $this->addGoogleEmptyApiKeyField($fields, self::GOOGLE_REVIEWS_FIELD_EMPTY_API_KEY); 594 595 $fields = array_merge($fields, array( 596 array( 597 "id" => self::GOOGLE_REVIEWS_FIELD_PLACE_ID, 598 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 599 "text" => __("Place ID", "unlimited-elements-for-elementor"), 600 // translators: %s is page url 601 "desc" => sprintf(__("You can find the place ID by using <a href='%s' target='_blank'>Place ID Finder</a>.", "unlimited-elements-for-elementor"), "https://developers.google.com/maps/documentation/javascript/examples/places-placeid-finder"), 602 ), 603 array( 604 "id" => self::GOOGLE_REVIEWS_FIELD_CACHE_TIME, 605 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 606 "text" => __("Cache Time", "unlimited-elements-for-elementor"), 607 // translators: %d is a number 608 "desc" => sprintf(__("Optional. You can specify the cache time of results in minutes. The default value is %d minutes.", "unlimited-elements-for-elementor"), self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME), 609 "default" => self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME, 610 ), 611 array( 612 "id" => self::GOOGLE_REVIEWS_FIELD_LANG, 613 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 614 "text" => __("Language Code", "unlimited-elements-for-elementor"), 615 "desc" => sprintf(__("Optional. Specify a language code. Example: de and google will translate the review.", "unlimited-elements-for-elementor"), self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME), 616 "default" => "", 617 ) 618 )); 619 620 return $fields; 621 } 618 622 619 623 620 … … 868 865 } 869 866 870 /** 871 * get google reviews data 872 */ 873 private function getGoogleReviewsData(){ 874 875 $data = array(); 876 867 868 private function _________GOOGLE_REVIEWS_________(){} 869 870 /** 871 * get serp api key 872 */ 873 private function getSerpAPIKey(){ 874 875 if(GlobalsUnlimitedElements::$enableSerpAPI == false) 876 return(null); 877 878 $serpKey = HelperProviderCoreUC_EL::getGeneralSetting("serpapi_key"); 879 $serpKey = trim($serpKey); 880 881 return($serpKey); 882 } 883 884 /** 885 * is serp api enabled 886 */ 887 private function isGoogleReviewsSerpEnabled(){ 888 889 $apiKey = $this->getSerpAPIKey(); 890 891 if(!empty($apiKey)) 892 return(true); 893 894 return(false); 895 } 896 897 /** 898 * get official google reviews 899 */ 900 private function getGoogleReviewsData_official($placeId){ 901 877 902 $this->validateGoogleApiKey(); 878 879 $placeId = $this->getRequiredParam(self::GOOGLE_REVIEWS_FIELD_PLACE_ID, "Place ID"); 903 880 904 $cacheTime = $this->getCacheTimeParam(self::GOOGLE_REVIEWS_FIELD_CACHE_TIME, self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME); 881 905 882 906 $placesService = new UEGoogleAPIPlacesService(); 883 907 $placesService->setCacheTime($cacheTime); … … 893 917 ); 894 918 895 $place = $placesService->getDetails($placeId, $placeParams); 896 897 foreach($place->getReviews() as $review){ 898 899 $data[] = array( 900 "id" => $review->getId(), 901 "date" => $review->getDate(self::FORMAT_DATETIME), 902 "text" => $review->getText(true), 903 "rating" => $review->getRating(), 904 "author_name" => $review->getAuthorName(), 905 "author_photo" => $review->getAuthorPhotoUrl(), 919 $place = $placesService->getDetails($placeId, $placeParams, $this->googleReviewsShowDebug); 920 921 return($place); 922 } 923 924 /** 925 * get google reviews from serp service 926 */ 927 private function getGoogleReviewsData_serp($placeId){ 928 929 $placesService = new UEGoogleAPIPlacesService(); 930 931 $placeParams = array(); 932 933 $apiKey = $this->getSerpAPIKey(); 934 935 $reviewsLanguage = $this->getParam(self::GOOGLE_REVIEWS_FIELD_LANG); 936 $placeParams["hl"] = $reviewsLanguage; 937 938 $reviewsSortBy = $this->getParam(self::GOOGLE_REVIEWS_SORT_BY); 939 $placeParams["sort_by"] = $reviewsSortBy; 940 941 $place = $placesService->getDetailsSerp($placeId, $apiKey, $placeParams, $this->googleReviewsShowDebug); 942 943 return($place); 944 } 945 946 947 /** 948 * get google reviews data 949 */ 950 private function getGoogleReviewsData(){ 951 952 $data = array(); 953 954 $showDebug = $this->getParam(self::GOOGLE_REVIEWS_FIELD_SHOW_DEBUG); 955 956 if($showDebug == true) 957 $this->googleReviewsShowDebug = true; 958 959 try{ 960 961 $serpAPIKey = $this->getSerpAPIKey(); 962 963 $isSerp = !empty($serpAPIKey); 964 965 if($this->googleReviewsShowDebug == true){ 966 967 $message = "Reviews API Debug"; 968 969 if($isSerp == true) 970 $message .= "<br> Output google reviews data using serp.com API"; 971 else 972 $message .= "<br> Output google reviews data using Official Google API"; 973 974 echo HelperHtmlUC::getDebugWarningMessageHtml($message); 975 } 976 977 $placeId = $this->getRequiredParam(self::GOOGLE_REVIEWS_FIELD_PLACE_ID, "Place ID"); 978 979 980 if($isSerp == false) 981 $place = $this->getGoogleReviewsData_official($placeId); 982 else 983 $place = $this->getGoogleReviewsData_serp($placeId); 984 985 986 //add api source 987 988 $apiSourceText = ($isSerp == true)?"serp":"official"; 989 990 $data["reviews_api_type"] = $apiSourceText; 991 992 //add place info 993 994 $arrInfo = $place->getPlaceInfo(); 995 996 if(!empty($arrInfo)) 997 $data["place_info"] = $arrInfo; 998 999 //add search link 1000 1001 if($isSerp == true){ 1002 1003 $arrMetaData = $place->getMetaData(); 1004 1005 $data["api_search_link_for_debug"] = UniteFunctionsUC::getVal($arrMetaData, "json_endpoint"); 1006 $data["api_search_html_link_for_debug"] = UniteFunctionsUC::getVal($arrMetaData, "prettify_html_file"); 1007 1008 } 1009 1010 1011 //add reviews 1012 1013 $arrReviews = $place->getReviews(); 1014 1015 $arrReviewsOutput = array(); 1016 1017 foreach($arrReviews as $review){ 1018 1019 if($isSerp == true) 1020 $review->setSerpSource(); 1021 1022 $arrReviewsOutput[] = array( 1023 "id" => $review->getId(), 1024 "date" => $review->getDate(self::FORMAT_DATETIME), 1025 "time_ago" => $review->getTimeAgoText(), 1026 "text" => $review->getText(true), 1027 "rating" => $review->getRating(), 1028 "author_name" => $review->getAuthorName(), 1029 "author_photo" => $review->getAuthorPhotoUrl(), 1030 ); 1031 1032 } 1033 1034 $data["reviews"] = $arrReviewsOutput; 1035 1036 }catch(Exception $e){ 1037 1038 if($this->googleReviewsShowDebug == true){ 1039 1040 $message = $e->getMessage(); 1041 1042 echo HelperHtmlUC::getErrorMessageHtml($message,"",true); 1043 } 1044 1045 throw $e; 1046 } 1047 1048 return $data; 1049 } 1050 1051 1052 /** 1053 * get google reviews settings fields 1054 */ 1055 private function getGoogleReviewsSettingsFields($isSingle = false){ 1056 1057 $fields = array(); 1058 1059 $fields = $this->addGoogleEmptyApiKeyField($fields, self::GOOGLE_REVIEWS_FIELD_EMPTY_API_KEY); 1060 1061 $fields[] = array( 1062 "id" => self::GOOGLE_REVIEWS_FIELD_PLACE_ID, 1063 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 1064 "text" => __("Place ID", "unlimited-elements-for-elementor"), 1065 // translators: %s is page url 1066 "desc" => sprintf(__("You can find the place ID by using <a href='%s' target='_blank'>Place ID Finder</a>.", "unlimited-elements-for-elementor"), "https://developers.google.com/maps/documentation/javascript/examples/places-placeid-finder"), 1067 "default"=>"ChIJmeeg4mbcQUcRIOHu3gDTJDs" 1068 ); 1069 1070 $isSerpEnabled = $this->isGoogleReviewsSerpEnabled(); 1071 1072 if($isSerpEnabled == false) 1073 $text = sprintf(__("To get more then 5 reviews, enter %s key in general settings", "unlimited-elements-for-elementor"), "<a href='https://serpapi.com' target='_blank'>serpapi.com</a>"); 1074 else 1075 $text = sprintf(__("Fetching google reviews using %s service. The cache time is 1 day.", "unlimited-elements-for-elementor"), "<a href='https://serpapi.com' target='_blank'>serpapi.com</a>"); 1076 1077 //if there is no option - no need for text 1078 if(GlobalsUnlimitedElements::$enableSerpAPI == true){ 1079 1080 $fields[] = array( 1081 "id" => self::GOOGLE_REVIEWS_FIELD_API_TEXT, 1082 "type" => UniteCreatorDialogParam::PARAM_STATIC_TEXT, 1083 "text" => $text, 906 1084 ); 907 1085 } 908 909 return $data; 910 } 911 1086 1087 //for serp api the cache is 1 day 1088 1089 if($isSerpEnabled == false){ 1090 1091 $fields[] = array( 1092 "id" => self::GOOGLE_REVIEWS_FIELD_CACHE_TIME, 1093 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 1094 "text" => __("Cache Time", "unlimited-elements-for-elementor"), 1095 // translators: %d is a number 1096 "desc" => sprintf(__("Optional. You can specify the cache time of results in minutes. The default value is %d minutes.", "unlimited-elements-for-elementor"), self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME), 1097 "default" => self::GOOGLE_REVIEWS_DEFAULT_CACHE_TIME, 1098 ); 1099 } 1100 1101 1102 $fields[] = array( 1103 "id" => self::GOOGLE_REVIEWS_FIELD_LANG, 1104 "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, 1105 "text" => __("Language Code", "unlimited-elements-for-elementor"), 1106 "desc" => __("Optional. Specify a language code. Example: de and google will translate the review.", "unlimited-elements-for-elementor"), 1107 "default" => "", 1108 ); 1109 1110 if($isSerpEnabled == true){ 1111 1112 $fields[] = array( 1113 "id" => self::GOOGLE_REVIEWS_SORT_BY, 1114 "type" => UniteCreatorDialogParam::PARAM_DROPDOWN, 1115 "text" => __("Sort By", "unlimited-elements-for-elementor"), 1116 "options" => array( 1117 "qualityScore"=>"Most Relevant", 1118 "newestFirst"=>"Newest", 1119 "ratingHigh"=>"Highest Rating", 1120 "ratingLow"=>"Lowest Rating", 1121 ), 1122 "default" => "qualityScore" 1123 ); 1124 1125 } 1126 1127 1128 //add fields for single mode 1129 1130 if($isSingle == true){ 1131 1132 $fields[] = array( 1133 "id" => "google_reviews_hr_beore_debug", 1134 "type" => UniteCreatorDialogParam::PARAM_HR, 1135 ); 1136 1137 $fields[] = array( 1138 "id" => self::GOOGLE_REVIEWS_FIELD_SHOW_DEBUG, 1139 "type" => UniteCreatorDialogParam::PARAM_RADIOBOOLEAN, 1140 "text" => __("Show Debug", "unlimited-elements-for-elementor"), 1141 ); 1142 1143 } 1144 1145 1146 return $fields; 1147 } 1148 1149 912 1150 private function _________GOOGLE_SHEETS_________(){} 1151 913 1152 914 1153 /** … … 1072 1311 return $key; 1073 1312 } 1074 1313 1314 1075 1315 /** 1076 1316 * get weather forecast settings fields -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_entrance_animations.class.php
r3251080 r3429507 454 454 var step = 100; 455 455 456 var objItems = objElement.find("."+classItem) ;456 var objItems = objElement.find("."+classItem).not(".uc-entrance-animate"); 457 457 458 458 var numItems = objItems.length; … … 464 464 465 465 objItems.each(function(index, item){ 466 466 467 467 var timeoutTime = time; 468 468 if(order == "up") … … 576 576 ueCheckEntranceAnimation(objElement, <?php echo esc_attr($animationStep)?>, "<?php echo esc_attr($classItem)?>", "<?php echo esc_attr($order)?>") 577 577 }); 578 579 objElement.on("uc_ajax_refreshed", function(){ 580 581 objElement.removeData("ue_entrance_animation_started"); 582 578 579 objElement.on("uc_ajax_refreshed", function(event, params){ 580 581 objElement.removeData("ue_entrance_animation_started"); 582 583 if(params.isLoadMore && params.isLoadMore === true){ 584 585 ueStartEntranceAnimation(objElement, <?php echo esc_attr($animationStep)?>, "<?php echo esc_attr($classItem)?>", "<?php echo esc_attr($order)?>"); 586 587 return(true); 588 } 589 583 590 ueCheckEntranceAnimation(objElement, <?php echo esc_attr($animationStep)?>, "<?php echo esc_attr($classItem)?>", "<?php echo esc_attr($order)?>") 584 591 }); -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_helperhtml.class.php
r3401883 r3429507 488 488 return($html); 489 489 } 490 491 protected function z_________PUTTERS_______(){}492 490 491 protected function z_________DEBUG_______(){} 492 493 493 /** 494 494 * put debug box 495 495 */ 496 496 public static function putHtmlDataDebugBox($data){ 497 497 498 498 self::putHtmlDataDebugBox_start(); 499 499 … … 503 503 if(is_string($data)) 504 504 $data = htmlspecialchars($data); 505 505 506 506 dmp($data); 507 507 … … 556 556 return($html); 557 557 } 558 559 560 protected function z_________PUTTERS_______(){} 561 558 562 559 563 -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_import_changelog.class.php
r3329756 r3429507 146 146 147 147 $wpdb->query("TRUNCATE TABLE {$changelogTable}"); 148 149 $adminUserID = $this->getAdminID();150 148 149 $adminUserID = get_current_user_id(); 150 151 151 foreach ($data as $item) { 152 152 … … 172 172 } 173 173 174 /**175 * get admin user id176 */177 private function getAdminID() {178 179 $arrUsers = UniteFunctionsWPUC::getAdminUsers();180 181 if(empty($arrUsers))182 return(null);183 184 $firstUser = $arrUsers[0];185 186 $userID = $firstUser->ID;187 188 return($userID);189 }190 174 191 175 /** -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_output.class.php
r3401883 r3429507 989 989 * prepare css selector dimentions css 990 990 */ 991 private function prepareCSSSelectorDimentionsCSS($selectorValue, $value){ 992 993 $top = UniteFunctionsUC::getVal($value, "top"); 994 $right = UniteFunctionsUC::getVal($value, "right"); 995 $bottom = UniteFunctionsUC::getVal($value, "bottom"); 996 $left = UniteFunctionsUC::getVal($value, "left"); 997 $unit = UniteFunctionsUC::getVal($value, "unit", "px"); 998 999 $css = $this->processCSSSelectorReplaces($selectorValue, array( 1000 "{{top}}" => $top . $unit, 1001 "{{right}}" => $right . $unit, 1002 "{{bottom}}" => $bottom . $unit, 1003 "{{left}}" => $left . $unit, 1004 )); 1005 1006 return $css; 1007 } 991 function prepareCSSSelectorDimentionsCSS($selectorValue, $value){ 992 993 $top = UniteFunctionsUC::getVal($value, "top"); 994 $right = UniteFunctionsUC::getVal($value, "right"); 995 $bottom = UniteFunctionsUC::getVal($value, "bottom"); 996 $left = UniteFunctionsUC::getVal($value, "left"); 997 $unit = UniteFunctionsUC::getVal($value, "unit", "px"); 998 999 $rawSides = array($top, $right, $bottom, $left); 1000 $hasValue = false; 1001 1002 foreach ($rawSides as $sideValue) { 1003 if ($sideValue !== '' && $sideValue !== null && $sideValue !== false) { 1004 $sideValue = trim((string)$sideValue); 1005 if ($sideValue !== '') { 1006 $hasValue = true; 1007 break; 1008 } 1009 } 1010 } 1011 1012 if ($hasValue === false) return ""; 1013 1014 $prepareSide = function($v) use ($unit) { 1015 if ($v === '' || $v === null || $v === false) 1016 return ''; 1017 1018 $v = trim((string)$v); 1019 if ($v === '') 1020 return ''; 1021 1022 return $v . $unit; 1023 }; 1024 1025 $css = $this->processCSSSelectorReplaces($selectorValue, array( 1026 "{{top}}" => $prepareSide($top), 1027 "{{right}}" => $prepareSide($right), 1028 "{{bottom}}" => $prepareSide($bottom), 1029 "{{left}}" => $prepareSide($left), 1030 )); 1031 1032 return $css; 1033 } 1008 1034 1009 1035 /** … … 1164 1190 1165 1191 foreach($selectors as $index => $selector){ 1166 $selectors[$index] = " #" . $wrapperId . " " . trim($selector);1192 $selectors[$index] = "." . $wrapperId . " " . trim($selector); 1167 1193 } 1168 1194 … … 1267 1293 $padding = UniteFunctionsUC::getVal($paramsTmp, 'advanced_padding'); 1268 1294 if (is_array($padding)) { 1295 1296 $unit = !empty($padding['unit']) ? $padding['unit'] : 'px'; 1297 1269 1298 if(!empty($padding['is_linked']) && $padding['is_linked'] && $padding['top'] !== '' && $padding['top'] !== null) { 1270 1299 $val = (string)$padding['top']; 1271 if($val !== '' && $val !== null) { 1272 $advanced_styles .= 'padding: ' . $val . $padding['unit'] . '; '; 1300 if ($val !== '' && $val !== null && $val !== false) { 1301 $val = trim((string)$val); 1302 if ($val !== '') { 1303 $advanced_styles .= 'padding: ' . $val . $unit . '; '; 1304 } 1273 1305 } 1274 1306 } else { 1275 1307 foreach($sides as $side) { 1276 1308 $val = UniteFunctionsUC::getVal($padding, $side); 1277 if($val !== '' && $val !== null && $val !== false) { 1278 $advanced_styles .= 'padding-' . $side . ': ' . (string)$val . $padding['unit'] . '; '; 1309 1310 if ($val === '' || $val === null || $val === false) { 1311 continue; 1279 1312 } 1313 1314 $val = trim((string)$val); 1315 if ($val === '') { 1316 continue; 1317 } 1318 1319 $advanced_styles .= 'padding-' . $side . ': ' . $val . $unit . '; '; 1280 1320 } 1281 1321 } … … 1483 1523 1484 1524 if($advanced_styles != '') { 1485 $styles .= "\n #" . $wrapperId . " {" . $advanced_styles . "}";1525 $styles .= "\n." . $wrapperId . " {" . $advanced_styles . "}"; 1486 1526 } 1487 1527 … … 1514 1554 } 1515 1555 } 1556 1557 $styles .= ' /* debug 2 */'; 1516 1558 1517 1559 return $styles; … … 1587 1629 $styles .= $this->processItemsSelectors(); 1588 1630 1631 $styles .= ' /* debug 3 */'; 1632 1589 1633 if(empty($styles) === true) 1590 1634 return null; 1591 1635 1592 UniteProviderFunctionsUC::printCustomStyle($styles);1636 // UniteProviderFunctionsUC::printCustomStyle($styles); 1593 1637 1594 1638 return $styles; … … 1623 1667 1624 1668 $styles .= $itemStyles; 1669 1670 $styles .= ' /* debug 4 */'; 1625 1671 } 1626 1672 … … 1632 1678 */ 1633 1679 public function getSelectorsCss(){ 1634 1680 1635 1681 $style = $this->processPreviewParamsSelectors(); 1636 1682 … … 1662 1708 1663 1709 //process selectors only for preview (elementor output uses its own processing) 1710 1664 1711 $this->processPreviewParamsSelectors(); 1665 1712 … … 1921 1968 return($html); 1922 1969 } 1923 1970 1924 1971 /** 1925 1972 * modify debug array - output for debug, fordebug … … 2000 2047 */ 2001 2048 private function putDebugDataHtml_posts($arrItemData){ 2002 2049 2003 2050 $numPosts = count($arrItemData); 2004 2051 … … 2009 2056 if(empty($arrItemData)) 2010 2057 return($html); 2011 2058 2012 2059 $isShowMeta = ($this->debugDataType == "post_meta"); 2013 2060 2014 2061 foreach($arrItemData as $index => $item){ 2015 2062 2016 2063 $isPost = false; 2017 2064 if($item instanceof WP_Post) … … 2036 2083 2037 2084 $item = UniteFunctionsUC::getVal($item, "item"); 2038 2085 2039 2086 $postData = UniteFunctionsUC::getArrFirstValue($item); 2040 2087 2041 2088 $title = UniteFunctionsUC::getVal($postData, "title"); 2042 2089 $alias = UniteFunctionsUC::getVal($postData, "alias"); … … 2132 2179 */ 2133 2180 private function putDebugDataHtml($arrData, $arrItemData){ 2134 2181 2135 2182 $html = "<div class='uc-debug-output' style='font-size:16px;color:black;text-decoration:none;background-color:white;padding:3px;'>"; 2136 2183 2137 2184 $html .= dmpGet("<b>Widget Debug Data</b> (turned on by setting in widget advanced section)<br>",true); 2138 2185 … … 2141 2188 2142 2189 if(!empty($paramListing) && $this->itemsType == "template"){ 2143 2190 2144 2191 $arrItemData = $this->putDebugDataHtml_getItemsFromListing($paramListing, $arrData); 2145 2192 } 2146 2147 2193 2148 2194 switch($this->debugDataType){ 2149 2195 case "post_titles": … … 2204 2250 if($isInsideEditor == true){ 2205 2251 2206 $css = " #{$ucID}-root .uc-background-editor-placeholder{2252 $css = ".{$ucID}-root .uc-background-editor-placeholder{ 2207 2253 font-size:12px; 2208 2254 padding:20px; … … 2210 2256 } 2211 2257 2212 #{$ucID}-root{2258 .{$ucID}-root{ 2213 2259 position:relative; 2214 2260 border:1px solid gray; … … 2232 2278 $css = " 2233 2279 /* background wrapper */ 2234 #{$ucID}-root.uc-background-active{2280 .{$ucID}-root.uc-background-active{ 2235 2281 position: absolute; 2236 2282 top:0px; … … 2543 2589 $advancedAddClasses = UniteFunctionsUC::getVal($params, "advanced_css_classes"); 2544 2590 2545 $output .= "\n<div id=\"" . esc_attr($id) . "\" class=\"ue-widget-root " . esc_attr($advancedAddClasses) . " \" data-id=\"" . esc_attr($rootId) . "\">";2591 $output .= "\n<div id=\"" . esc_attr($id) . "\" class=\"ue-widget-root " . esc_attr($advancedAddClasses) . " " . esc_attr($id) . "\" data-id=\"" . esc_attr($rootId) . "\">"; 2546 2592 } 2547 2593 -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_params_processor.class.php
r3397365 r3429507 1014 1014 if(!empty($imageUrl)) 1015 1015 $imageUrl = UniteFunctionsUC::sanitize($imageUrl, UniteFunctionsUC::SANITIZE_URL); 1016 1016 1017 1017 $data[$name] = $imageUrl; 1018 1018 … … 1826 1826 case "currency_api": 1827 1827 case "weather_api": 1828 $data = UniteCreatorAPIIntegrations::getInstance()->addDataToParams($data, $name); 1828 case "reviews": 1829 $data = UniteCreatorAPIIntegrations::getInstance()->addDataToParams($data, $name, $type); 1829 1830 break; 1830 1831 case "rss_feed": … … 1858 1859 1859 1860 break; 1861 1860 1862 } 1861 1863 return($data); … … 1863 1865 1864 1866 private function z__________VALUES_OUTPUT__________(){} 1865 1866 1867 1868 1867 1869 /** 1868 1870 * get processe param data, function with override … … 1870 1872 protected function getProcessedParamData($data, $value, $param, $processType){ 1871 1873 1872 1873 1874 $type = UniteFunctionsUC::getVal($param, "type"); 1874 1875 $name = UniteFunctionsUC::getVal($param, "name"); 1875 1876 1876 1877 $isOutputProcessType = $this->isOutputProcessType($processType); 1877 1878 1878 1879 //special params - all types 1879 1880 switch($type){ 1881 case UniteCreatorDialogParam::PARAM_TEXTFIELD: 1882 $data = $this->maybeSanitizeLink($data, $name, $value); 1883 break; 1880 1884 case UniteCreatorDialogParam::PARAM_DROPDOWN: 1881 1885 case UniteCreatorDialogParam::PARAM_NUMBER: … … 1936 1940 } 1937 1941 1942 /** 1943 * maybe sanitize link in text field 1944 */ 1945 protected function maybeSanitizeLink($data, $name, $value){ 1946 1947 if(strpos($name,"link_") === false && strpos($name,"_link") === false) 1948 return($data); 1949 $valueLow = strtolower($value); 1950 if(strpos($valueLow,"javascript") === false) 1951 return($data); 1952 1953 //if it's a link 1954 $value = UniteFunctionsUC::sanitize($value, UniteFunctionsUC::SANITIZE_URL); 1955 1956 $data[$name] = $value; 1957 1958 return($data); 1959 } 1960 1938 1961 1939 1962 /** -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_rss.class.php
r3329756 r3429507 320 320 321 321 $hasImageKey = $this->hasImageKey($firstRssElement); 322 323 if($hasImageKey == false)322 323 if($hasImageKey == true) 324 324 return($arrRss); 325 326 $isAddedImage = true; 325 327 326 328 foreach ($arrRss as $rssKey => $rssItem) { 327 329 328 330 $imageLink = $this->getFirstImageLinkFromContent($rssItem); 329 330 if(!empty($imageLink)) 331 332 if(!empty($imageLink)){ 333 334 $isAddedImage = true; 335 331 336 $arrRss[$rssKey]['image_url'] = $imageLink; 332 333 } 334 337 } 338 339 } 340 341 //add image key to others 342 if($isAddedImage == true){ 343 foreach ($arrRss as $rssKey => $rssItem) { 344 if(array_key_exists("image_url", $arrRss[$rssKey]) == false) 345 $arrRss[$rssKey]["image_url"] = ""; 346 } 347 } 348 349 335 350 return($arrRss); 336 351 } … … 475 490 476 491 $rssKeyArr = $this->filterByTypeAndCatId($params, UniteCreatorDialogParam::PARAM_TEXTFIELD, $catValue[GlobalsUC::ATTR_CATID]); 477 478 492 479 493 $isAutoDetect = UniteFunctionsUC::getVal($data, "auto_detect_keys"); … … 487 501 } 488 502 503 489 504 foreach ($rssKeyArr as $keyValue) { 490 505 … … 503 518 504 519 } else { 505 520 506 521 $autoKey = $this->autoDetectKeys($keyName, $firstRssElement); 507 522 508 523 if (!empty($autoKey)) { 509 524 … … 515 530 516 531 $searchImageKey = $this->searchForImageKey($firstRssElement); 517 518 if (!empty($searchImageKey)) 532 533 if (!empty($searchImageKey)){ 519 534 $keys[$keyName] = $searchImageKey; 520 535 536 } 521 537 } 522 538 } … … 529 545 $autoText = $isAutoKey?" <span style='color:grey;'>[auto detect]</span>":""; 530 546 531 if(!array_key_exists($keyName, $keys)) 547 if(!array_key_exists($keyName, $keys)){ 532 548 dmp("{$title}: <span style='color:darkred;'>not detected, please select a custom one</span>"); 549 } 533 550 else if (array_key_exists($keys[$keyName], $firstRssElement)) 534 551 dmp("{$title}: <b>$key</b> {$autoText}"); … … 556 573 else 557 574 $keys["date_key_original"] = "publish_date"; 558 575 559 576 560 577 return $keys; … … 570 587 571 588 $autoDetectKey = $this->rssAutoDetectKeys[$keyName]; 572 589 573 590 $arrKeys = explode('|', $autoDetectKey); 574 575 foreach ($arrKeys as $arrKey) { 576 577 $foundValue = UniteFunctionsUC::getVal($rssElement, $arrKey); 578 579 if (!empty($foundValue)) { 580 return $arrKey; 581 } 591 592 foreach ($arrKeys as $key) { 593 594 if(array_key_exists($key, $rssElement)) 595 return($key); 582 596 } 583 597 … … 648 662 $descKeys = explode('|', $possibleDescKeys); 649 663 $rssKeys = array_merge($contentKeys, $descKeys); 650 651 $pattern = '/<img[^>]+src=["\']([^"\']+)["\']/i'; 652 664 665 //$pattern = '/<img[^>]+src=["\']([^"\']+)["\']/i'; 666 $pattern = '/<img\b[^>]*\bsrc\s*=\s*["\']([^"\']+)["\'][^>]*>/i'; 667 653 668 foreach ($rssKeys as $rssKey) { 669 654 670 if(array_key_exists($rssKey, $rssItem)) { 671 655 672 if (!is_array($rssItem[$rssKey])) { 656 if (preg_match($pattern, $rssItem[$rssKey], $matches)) { 673 674 $content = $rssItem[$rssKey]; 675 676 if (preg_match($pattern, $content, $matches)) { 677 657 678 if (!empty($matches[1])) { 658 679 return $matches[1]; 659 680 } 660 681 } 682 661 683 } else { 662 684 $firstImage = $this->getFirstImageLinkFromContent($rssItem[$rssKey]); 663 685 664 686 if (!empty($firstImage)) { 665 687 return $firstImage; -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_schema.class.php
r3401896 r3429507 369 369 break; 370 370 case self::ROLE_IMAGE: 371 if($value == GlobalsUC::$url_no_image_placeholder) 372 $value = ""; 371 373 case self::ROLE_LINK: 372 374 $value = UniteFunctionsUC::sanitize($value, UniteFunctionsUC::SANITIZE_URL); 373 375 break; 374 376 } 375 377 376 378 return($value); 377 379 } -
unlimited-elements-for-elementor/trunk/inc_php/unitecreator_settings.class.php
r3397365 r3429507 31 31 private $currentTabs; 32 32 private static $addEditWidgetHTML = null; 33 private $currentCategoryID; 33 34 34 35 … … 395 396 396 397 $params["label_block"] = true; 397 398 398 399 $this->add($name, $defaultValue, $text, self::TYPE_GALLERY, $params); 399 400 } … … 435 436 $objManager = new UniteCreatorManagerInline(); 436 437 $objManager->setStartAddon($addon); 437 438 438 439 $arrParams["items_manager"] = $objManager; 439 440 $this->add("uc_items_editor", "", self::PARAM_NOTEXT, self::TYPE_ITEMS, $arrParams); … … 521 522 if(empty($name) === true){ 522 523 $this->currentTabs = null; 523 524 524 return null; 525 525 } … … 537 537 // add/update the tab 538 538 $value = UniteFunctionsUC::getVal($tabs["items"], $name); 539 540 539 if(empty($value) === true){ 540 541 541 $value = "tab_" . UniteFunctionsUC::getRandomString(5); 542 542 … … 544 544 545 545 try{ 546 $this->updateSettingItems($tabs["name"] , $tabs["items"]);546 $this->updateSettingItems($tabs["name"] . '-' . $this->currentCategoryID, $tabs["items"]); 547 547 }catch(Exception $exception){ 548 $this->addTabs($tabs["name"] , $tabs["items"], $value);548 $this->addTabs($tabs["name"] . '-' . $this->currentCategoryID, $tabs["items"], $value); 549 549 } 550 550 } … … 553 553 554 554 return array( 555 "name" => $tabs["name"] ,555 "name" => $tabs["name"] . '-' . $this->currentCategoryID, 556 556 "value" => $value, 557 557 ); … … 820 820 break; 821 821 case "base_widget": //operate base widget addon object 822 break; 823 case "reviews": 824 825 UniteCreatorAPIIntegrations::getInstance()->addServiceSettingsFields($this, UniteCreatorAPIIntegrations::TYPE_GOOGLE_REVIEWS, $name, $condition); 826 822 827 break; 823 828 default: … … 1804 1809 1805 1810 foreach($arrParamsWithCats as $catID => $arrCat){ 1806 1811 1812 $this->currentCategoryID = $catID; 1813 1814 // reset tabs to create for new cat 1815 $this->currentTabs = null; 1816 1807 1817 $title = UniteFunctionsUC::getVal($arrCat, "title"); 1808 1818 $tab = UniteFunctionsUC::getVal($arrCat, "tab"); … … 1859 1869 $this->addByCreatorParam($param); 1860 1870 1861 } //foreach params1871 } 1862 1872 1863 1873 if($isGeneralCategory == true && GlobalsUC::$isProVersion == false){ 1864 1865 1874 $this->addFreeVersionInsideNotification(); 1866 1875 } -
unlimited-elements-for-elementor/trunk/includes.php
r3403331 r3429507 13 13 14 14 if(!defined("UNLIMITED_ELEMENTS_VERSION")) 15 define("UNLIMITED_ELEMENTS_VERSION", "2.0. 1");15 define("UNLIMITED_ELEMENTS_VERSION", "2.0.2"); 16 16 17 17 //disable elementor support for debugging purposes. keep it commented 18 18 //define("UE_DISABLE_ELEMENTOR_SUPPORT", true); 19 19 20 20 21 21 $currentFile = __FILE__; 22 22 $currentFolder = dirname($currentFile); -
unlimited-elements-for-elementor/trunk/js/manager/unitecreator_manager_actions_addons.js
r2938098 r3429507 688 688 var numItems = jQuery("#uc_list_items li").length; 689 689 if(numItems == 0){ 690 jQuery("#no_items_text").hide(); 690 691 jQuery("#no_items_text").show(); 692 691 693 g_emptyAddonsWrapper.show(); 692 694 g_manager.updateGlobalHeight(null, 390); -
unlimited-elements-for-elementor/trunk/js/manager/unitecreator_manager_actions_inline.js
r2762592 r3429507 135 135 if(typeof arrItems != "object") 136 136 return(false); 137 137 138 138 jQuery.each(arrItems, function(index, itemData){ 139 139 appendItem(itemData, true); … … 378 378 */ 379 379 function appendItem(objValues, noUpdate){ 380 380 381 var htmlItem = generateItemHtml(objValues); 382 381 383 var objItem = g_objItems.appendItem(htmlItem, noUpdate); 382 384 objItem.data("params", objValues); 385 383 386 } 384 387 … … 526 529 //init from data 527 530 var arrInitItems = g_objWrapper.data("init-items"); 528 529 if(arrInitItems && typeof arrInitItems == "object") 531 532 if(arrInitItems && typeof arrInitItems == "object"){ 530 533 t.setItemsFromData(arrInitItems); 534 } 531 535 532 536 }; -
unlimited-elements-for-elementor/trunk/js/manager/unitecreator_manager_items.js
r3023498 r3429507 1511 1511 this.appendItem = function(htmlItem, noUpdate){ 1512 1512 1513 jQuery("#uc_list_items").show(); 1513 var objListItems = jQuery("#uc_list_items"); 1514 objListItems.show(); 1515 1514 1516 t.hideNoAddonsText(); 1515 1517 1516 1518 var objItem = jQuery(htmlItem); 1517 1519 1518 jQuery("#uc_list_items").append(objItem);1520 objListItems.append(objItem); 1519 1521 1520 1522 if(noUpdate !== true) … … 1752 1754 if(jQuery("#uc_list_items li").length == 0){ 1753 1755 jQuery("#uc_list_items").hide(); 1754 jQuery("#no_items_text").show(); 1756 1757 showNoItemsText(true); 1758 1755 1759 }else{ 1756 1760 t.hideNoAddonsText(); … … 1767 1771 1768 1772 jQuery("#uc_list_items").html("").hide(); 1769 jQuery("#no_items_text").show(); 1770 1773 1774 showNoItemsText(true); 1775 1771 1776 if(noUpdate !== true) 1772 1777 t.checkSelectRelatedItems(); … … 1879 1884 1880 1885 if(numItems == 0){ 1881 jQuery("#no_items_text").show(); 1886 1887 //show 1888 showNoItemsText(true); 1889 1882 1890 jQuery("#uc_list_items").hide(); 1883 1891 }else{ … … 1886 1894 jQuery("#uc_list_items").show(); 1887 1895 t.hideNoAddonsText(); 1896 1888 1897 } 1889 1898 … … 1891 1900 } 1892 1901 1902 /** 1903 * hide or show actually no items text 1904 */ 1905 function showNoItemsText(isShow){ 1906 1907 var objNoItems = jQuery("#no_items_text"); 1908 1909 if(isShow == true) 1910 objNoItems.show(); 1911 else 1912 objNoItems.hide(); 1913 1914 } 1893 1915 1894 1916 /** … … 1896 1918 */ 1897 1919 this.hideNoAddonsText = function(){ 1898 jQuery("#no_items_text").hide(); 1899 1920 1921 //hide 1922 showNoItemsText(false); 1923 1900 1924 g_manager.triggerEvent(g_manager.events.ITEM_HIDE_EMPTY_TEXT); 1901 1925 } … … 1986 2010 */ 1987 2011 this.initItems = function(objManager){ 2012 1988 2013 initItems(objManager); 1989 2014 }; -
unlimited-elements-for-elementor/trunk/js/settings.js
r3397365 r3429507 15 15 16 16 var g_debug = false; 17 18 var g_debug_selectors = null; 19 //var g_debug_selectors = "item_border_width"; //enter input name 17 20 18 21 var g_vars = { … … 320 323 321 324 function isInputFullyInited($input){ 322 return _initedInputsOnce && _initedInputsOnce.has($input[0]);325 return _initedInputsOnce.has($input[0]); 323 326 } 324 327 … … 488 491 */ 489 492 this.getSettingsValues = function (controlsOnly, isChangedOnly) { 493 490 494 validateInited(); 491 495 … … 497 501 jQuery.each(objInputs, function () { 498 502 const objInput = jQuery(this); 499 500 if (objInput.closest(".unite-setting-row").hasClass("unite-setting-hidden") === true) 501 return; 502 503 503 504 const name = getInputName(objInput); 505 504 506 if (!name) return; 505 507 … … 509 511 510 512 if (isInputFullyInited(objInput)) { 513 514 if (objInput.closest(".unite-setting-row").hasClass("unite-setting-hidden") === true) { 515 //console.log('hidden: ' + name); 516 return; 517 } 518 511 519 value = getSettingInputValue(objInput); 512 520 if (value === g_vars.NOT_UPDATE_OPTION) return; … … 578 586 */ 579 587 function clearInput(objInput, dataname, checkboxDataName, skipControl){ 580 588 581 589 var name = getInputName(objInput); 582 590 var type = getInputType(objInput); … … 681 689 case "checkbox": 682 690 case "radio": 683 defaultValue = objInput.data(checkboxDataName);684 defaultValue = g_ucAdmin.strToBool(defaultValue); 685 686 objInput.prop("checked", defaultValue);691 defaultValue = objInput.data(checkboxDataName); 692 693 defaultValue = g_ucAdmin.strToBool(defaultValue); 694 objInput.prop("checked", defaultValue === true); 687 695 break; 688 696 case "editor_tinymce": … … 1066 1074 } 1067 1075 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 // =[2]1084 1085 1076 function _______EVENTS_____(){} 1086 1077 … … 1144 1135 return; 1145 1136 1146 var value = getSettingInputValue(objInput); 1137 var value = getSettingInputValue(objInput); 1138 var name = getInputName(objInput); 1147 1139 1148 1140 switch (type) { … … 1163 1155 break; 1164 1156 } 1157 1158 // update cache 1159 if (!g_temp.cacheValues || typeof g_temp.cacheValues !== "object") { 1160 g_temp.cacheValues = {}; 1161 } 1162 if (name && value !== g_vars.NOT_UPDATE_OPTION) { 1163 g_temp.cacheValues[name] = value; 1164 } 1165 1165 1166 1166 //process control change … … 1179 1179 eventName = t.events.CHANGE; 1180 1180 } 1181 1182 var name = getInputName(objInput);1183 1181 1184 1182 checkUpdateResponsivePlaceholders(objInput); … … 1218 1216 */ 1219 1217 function initControls() { 1218 1220 1219 if (!g_objWrapper) 1221 1220 return; 1222 1221 1223 var template = jQuery("#" + g_objWrapper.attr("id") + "-controls"); 1222 // var template = jQuery("#" + g_objWrapper.attr("id") + "-controls"); 1223 var template = g_objWrapper.find('.tpl-controls'); 1224 1224 var objControls = null; 1225 1225 … … 1236 1236 1237 1237 g_arrControls = objControls.parents; 1238 1238 1239 g_arrChildrenControls = objControls.children; 1239 1240 } … … 1294 1295 } 1295 1296 1297 /* 1298 * init input once 1299 */ 1296 1300 function initInputOnce(objInput, funcChange){ 1301 1297 1302 if (_initedInputsOnce.has(objInput[0])) return; 1298 1303 1299 1304 if (!funcChange) 1300 1305 funcChange = t.onSettingChange; 1301 1306 1302 1307 var type = getInputType(objInput); 1308 1303 1309 var basicType = getInputBasicType(objInput); 1304 1310 … … 1308 1314 trace(objInput); 1309 1315 } 1316 1317 //no need to init items panel from here 1318 if(type == "items"){ 1319 _initedInputsOnce.add(objInput[0]); 1320 return(false); 1321 } 1322 1310 1323 1311 1324 // init by type … … 1402 1415 break; 1403 1416 } 1417 1404 1418 1405 1419 t.disableTriggerChange(); 1420 1406 1421 try { 1407 clearInput(objInput, "initval", "initchecked", true); 1408 1409 if (g_temp && g_temp.cacheValues && typeof g_temp.cacheValues === "object") { 1410 const name = getInputName(objInput); 1411 1412 if (name && Object.prototype.hasOwnProperty.call(g_temp.cacheValues, name)) { 1413 1414 setInputValue(objInput, g_temp.cacheValues[name], g_temp.cacheValues); 1422 const name = getInputName(objInput); 1423 1424 if (!g_temp.cacheValues || typeof g_temp.cacheValues !== 'object') { 1425 g_temp.cacheValues = {}; 1426 } 1427 1428 if (name && !Object.prototype.hasOwnProperty.call(g_temp.cacheValues, name)) { 1429 const plannedValue = getPlannedInitValue(objInput); 1430 1431 if (plannedValue !== g_vars.NOT_UPDATE_OPTION) { 1432 g_temp.cacheValues[name] = plannedValue; 1415 1433 } 1416 1434 } 1417 1435 1418 } catch(e){ 1436 clearInput(objInput, "initval", "initchecked", true); 1437 1438 if (name && Object.prototype.hasOwnProperty.call(g_temp.cacheValues, name)) { 1439 setInputValue(objInput, g_temp.cacheValues[name], g_temp.cacheValues); 1440 } 1441 1442 } catch (e) { 1419 1443 console.error("Init-once error:", objInput, e); 1420 1444 } finally { … … 1429 1453 */ 1430 1454 function initSettings() { 1455 1431 1456 const $inputs = getObjInputs(); 1432 1457 … … 1446 1471 1447 1472 let type = getInputType($input); 1473 1448 1474 if(type == 'repeater') { 1449 1475 initInputOnce($input, t.onSettingChange); … … 1459 1485 else if (g_debug === true) console.log("All inputs scheduled for init-once"); 1460 1486 })(); 1487 1461 1488 } 1462 1489 … … 1476 1503 if (!g_options) g_options = {}; 1477 1504 1478 var template = jQuery("#" + g_objWrapper.attr("id") + "-options");1479 1505 var objOptions = {}; 1480 1506 1507 var template = g_objWrapper.find(".tpl-options").first(); 1508 1509 if (!template.length) { 1510 var wrapperId = g_objWrapper.attr("id"); 1511 if (wrapperId) { 1512 template = jQuery("#" + wrapperId + "-options"); 1513 } 1514 } 1515 1481 1516 if (template.length) { 1482 var raw = (template[0].content?.textContent ?? template.html() ?? "").trim(); 1517 var raw = ""; 1518 1519 if ( 1520 template[0].tagName && 1521 template[0].tagName.toLowerCase() === "template" && 1522 template[0].content && 1523 typeof template[0].content.textContent === "string" 1524 ) { 1525 raw = template[0].content.textContent; 1526 } else { 1527 raw = template.html() || ""; 1528 } 1529 1530 raw = jQuery.trim(raw); 1531 1483 1532 if (raw) { 1484 1533 try { 1485 1534 var parsed = JSON.parse(raw); 1486 if (parsed && typeof parsed === "object") objOptions = parsed; // игнорируем null 1535 if (parsed && typeof parsed === "object") { 1536 objOptions = parsed; 1537 } 1487 1538 } catch (e) { 1488 1539 console.warn("Options JSON parse error:", e); … … 1495 1546 jQuery.each(arrOptions, function (index, optionKey) { 1496 1547 g_options[optionKey] = g_ucAdmin.getVal(objOptions, optionKey, g_options[optionKey]); 1548 1497 1549 if (Object.keys(objOptions).length) { 1498 1550 objOptions[optionKey] = true; … … 1505 1557 } 1506 1558 1507 if (g_options["id_prefix"]) g_IDPrefix = "#" + g_options["id_prefix"]; 1559 if (g_options["id_prefix"]) { 1560 g_IDPrefix = "#" + g_options["id_prefix"]; 1561 } 1508 1562 } 1509 1563 … … 1528 1582 1529 1583 }; 1530 1531 1584 1532 1585 … … 1772 1825 }; 1773 1826 1774 // =[2]1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 // =[1]1786 1827 1787 1828 function _________CUSTOM_SETTING_TYPES__________(){} … … 1791 1832 */ 1792 1833 const customSettingTypeCache = {}; 1834 1793 1835 function getCustomSettingType(settingName) { 1794 1836 if (!settingName) { … … 3436 3478 */ 3437 3479 function initTabs(objWrapper) { 3480 3438 3481 objWrapper.find(".unite-setting-tabs-item-label").on("click", function () { 3439 3482 var objInput = jQuery(this); … … 3450 3493 }); 3451 3494 3452 /*3453 objWrapper.find(".unite-setting-tabs-item-input").on("change", function () {3454 var objInput = jQuery(this);3455 var id = objInput.attr("name");3456 var value = objInput.val();3457 3458 objInput.closest(".unite-list-settings")3459 .find(".unite-setting-row[data-tabs-id=\"" + id + "\"]")3460 .addClass("unite-tabs-hidden")3461 .filter("[data-tabs-value=\"" + value + "\"]")3462 .removeClass("unite-tabs-hidden");3463 });3464 */3465 3495 objWrapper.find(".unite-setting-tabs-item-input").first().trigger('click'); 3466 3496 } … … 4033 4063 */ 4034 4064 function initItemsPanel(){ 4035 4065 4036 4066 var objItemsWrapper = g_objParent.find(".uc-setting-items-panel"); 4037 4067 if(objItemsWrapper.length == 0) … … 4127 4157 */ 4128 4158 function initSubSettingsDialog(objWrapper) { 4159 4129 4160 var objDialog = getSubSettingsDialog(objWrapper); 4130 4161 … … 4259 4290 4260 4291 /** 4292 * init repeater 4293 */ 4294 /** 4261 4295 * init repeater 4262 4296 */ … … 4310 4344 addRepeaterItem(null, objWrapper, itemValues, $item); 4311 4345 }); 4346 4347 var name = getInputName(objWrapper); 4348 var cachedValue = null; 4349 4350 if (g_temp.cacheValues && g_temp.cacheValues.hasOwnProperty(name)) { 4351 cachedValue = g_temp.cacheValues[name]; 4352 } 4353 4354 if (cachedValue && Array.isArray(cachedValue) && cachedValue.length > 0) { 4355 setRepeaterValues(objWrapper, cachedValue); 4356 } else { 4357 var defaultItems = objWrapper.data("itemvalues"); 4358 if (defaultItems && objWrapper.find(".unite-repeater-item").length === 0) { 4359 setRepeaterValues(objWrapper, null, true); 4360 } 4361 } 4312 4362 } 4313 4363 … … 4315 4365 * add repeater item 4316 4366 */ 4317 function addRepeaterItem(event, objWrapper, itemValues, objItemInsertAfter) {4318 if (!objWrapper)4319 objWrapper = jQuery(this).closest(".unite-setting-repeater");4320 4321 var objSettingsTemplate = objWrapper.find(".unite-repeater-template");4322 var objItemsWrapper= objWrapper.find(".unite-repeater-items");4323 var objEmpty= objWrapper.find(".unite-repeater-empty");4324 4325 g_ucAdmin.validateDomElement(objItemsWrapper, "items wrapper");4326 g_ucAdmin.validateDomElement(objSettingsTemplate, "settings template");4327 4328 objEmpty.hide();4329 4330 // prepare item values4331 if (!itemValues) {4332 var itemNumber = objWrapper.find(".unite-repeater-item").length + 1;4333 var itemTitle= objWrapper.data("item-title") + " " + itemNumber;4334 4335 itemValues = {4336 title: itemTitle, 4337 };4338 }4339 4340 if (!itemValues._generated_id)4341 itemValues._generated_id = g_ucAdmin.getRandomString(5);4342 4343 // get item html4344 var textDelete= objWrapper.data("text-delete");4345 var textDuplicate = objWrapper.data("text-duplicate");4346 4347 var html= "<div class='unite-repeater-item'>";4348 html+= " <div class='unite-repeater-item-header'>";4349 4350 if (itemValues.title == '') {4351 html += " <div class='unite-repeater-item-title unite-repeater-item-title-placeholder'>Item Title</div>";4367 function addRepeaterItem(event, objWrapper, itemValues, objItemInsertAfter) { 4368 if (!objWrapper) 4369 objWrapper = jQuery(this).closest(".unite-setting-repeater"); 4370 4371 var objSettingsTemplate = objWrapper.find(".unite-repeater-template"); 4372 var objItemsWrapper = objWrapper.find(".unite-repeater-items"); 4373 var objEmpty = objWrapper.find(".unite-repeater-empty"); 4374 4375 g_ucAdmin.validateDomElement(objItemsWrapper, "items wrapper"); 4376 g_ucAdmin.validateDomElement(objSettingsTemplate, "settings template"); 4377 4378 objEmpty.hide(); 4379 4380 // prepare item values 4381 if (!itemValues) { 4382 var itemNumber = objWrapper.find(".unite-repeater-item").length + 1; 4383 var itemTitle = objWrapper.data("item-title") + " " + itemNumber; 4384 4385 itemValues = { 4386 title: itemTitle 4387 }; 4388 } 4389 4390 if (!itemValues._generated_id) 4391 itemValues._generated_id = g_ucAdmin.getRandomString(5); 4392 4393 // get item html 4394 var textDelete = objWrapper.data("text-delete"); 4395 var textDuplicate = objWrapper.data("text-duplicate"); 4396 4397 var html = "<div class='unite-repeater-item'>"; 4398 html += " <div class='unite-repeater-item-header'>"; 4399 4400 if (itemValues.title == "") { 4401 html += " <div class='unite-repeater-item-title unite-repeater-item-title-placeholder'>Item Title</div>"; 4352 4402 } else { 4353 html += " <div class='unite-repeater-item-title'>" + itemValues.title + "</div>";4403 html += " <div class='unite-repeater-item-title'>" + itemValues.title + "</div>"; 4354 4404 } 4355 4405 4356 html += " <div class='unite-repeater-item-actions'>"; 4357 html += " <button class='unite-repeater-item-action unite-repeater-item-duplicate uc-tip' title='" + textDuplicate + "'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M8.625.375H.375v8.25h8.25V.375Z' /><path d='M10.125 3.375h1.5v8.25h-8.25v-1.5' /></svg></button>"; 4358 html += " <button class='unite-repeater-item-action unite-repeater-item-delete uc-tip' title='" + textDelete + "'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='m1.5 1.5 9 9M10.5 1.5l-9 9' /></svg></button>"; 4359 html += " </div>"; 4360 html += " </div>"; 4361 html += " <div class='unite-repeater-item-content'>"; 4362 html += objSettingsTemplate.html(); 4363 html += " </div>"; 4364 html += "</div>"; 4365 4366 // change item settings IDs 4367 var objItem = jQuery(html); 4368 var objItemSettingsWrapper = objItem.find(".unite_settings_wrapper"); 4369 4370 g_ucAdmin.validateDomElement(objItemSettingsWrapper, "item settings wrapper"); 4371 4372 var template = jQuery("#" + g_objWrapper.attr("id") + "-options"); 4373 var options = null; 4406 html += " <div class='unite-repeater-item-actions'>"; 4407 html += " <button class='unite-repeater-item-action unite-repeater-item-duplicate uc-tip' title='" + textDuplicate + "'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M8.625.375H.375v8.25h8.25V.375Z' /><path d='M10.125 3.375h1.5v8.25h-8.25v-1.5' /></svg></button>"; 4408 html += " <button class='unite-repeater-item-action unite-repeater-item-delete uc-tip' title='" + textDelete + "'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='m1.5 1.5 9 9M10.5 1.5l-9 9' /></svg></button>"; 4409 html += " </div>"; 4410 html += " </div>"; 4411 html += " <div class='unite-repeater-item-content'>"; 4412 html += objSettingsTemplate.html(); 4413 html += " </div>"; 4414 html += "</div>"; 4415 4416 // change item settings IDs 4417 var objItem = jQuery(html); 4418 var objItemSettingsWrapper = objItem.find(".unite_settings_wrapper"); 4419 4420 g_ucAdmin.validateDomElement(objItemSettingsWrapper, "item settings wrapper"); 4421 4422 var template = objSettingsTemplate.find(".tpl-options").first(); 4423 var options = null; 4424 4374 4425 if (template.length) { 4375 4426 try { 4376 options = JSON.parse(template.html().trim()); 4427 var raw; 4428 4429 if (template[0].content && template[0].content.textContent) { 4430 raw = template[0].content.textContent.trim(); 4431 } else { 4432 raw = (template.html() || "").trim(); 4433 } 4434 4435 if (raw) { 4436 options = JSON.parse(raw); 4437 } 4377 4438 } catch (e) { 4378 4439 console.warn("Options JSON parse error:", e); … … 4380 4441 } 4381 4442 4382 var idPrefix = options?.id_prefix; 4383 var newID = idPrefix + "item_" + itemValues._generated_id + "_"; 4384 4385 html = g_ucAdmin.replaceAll(html, idPrefix, newID); 4386 4387 // change item settings wrapper ID 4388 objItem = jQuery(html); 4389 objItemSettingsWrapper = objItem.find(".unite_settings_wrapper"); 4390 objItemSettingsWrapper.attr("id", "unite_settings_repeater_" + newID); 4391 4392 if (objItemInsertAfter) 4393 objItemInsertAfter.after(objItem); 4394 else 4395 objItemsWrapper.append(objItem); 4396 4397 // init item settings 4398 var objSettings = new UniteSettingsUC(); 4399 4443 var idPrefix = options && options.id_prefix ? options.id_prefix : null; 4444 4445 if (!idPrefix) { 4446 idPrefix = (g_IDPrefix || "#unite_setting_").replace(/^#/, ""); 4447 } 4448 4449 var newID = idPrefix + "item_" + itemValues._generated_id + "_"; 4450 4451 html = g_ucAdmin.replaceAll(html, idPrefix, newID); 4452 4453 objItem = jQuery(html); 4454 objItemSettingsWrapper = objItem.find(".unite_settings_wrapper"); 4455 4456 objItemSettingsWrapper.attr("id", "unite_settings_repeater_" + newID); 4457 4458 if (objItemInsertAfter) 4459 objItemInsertAfter.after(objItem); 4460 else 4461 objItemsWrapper.append(objItem); 4462 4463 // init item settings 4464 var objSettings = new UniteSettingsUC(); 4400 4465 objSettings.init(objItemSettingsWrapper, { cacheValues: itemValues }); 4401 objSettings?.setValues(itemValues); 4402 4403 objItem.data("objsettings", objSettings); 4404 4405 // init item title change 4406 var objTitleInput = objSettings.getInputByName("title"); 4407 var objItemTitle = objItem.find(".unite-repeater-item-title"); 4408 4409 objTitleInput?.on("input", function () { 4410 var value = objTitleInput.val(); 4411 4412 objItemTitle.text(value); 4413 }); 4414 4415 t.onSettingChange(null, objWrapper); 4416 } 4466 objSettings.setValues && objSettings.setValues(itemValues); 4467 4468 objItem.data("objsettings", objSettings); 4469 4470 // init item title change 4471 var objTitleInput = objSettings.getInputByName("title"); 4472 var objItemTitle = objItem.find(".unite-repeater-item-title"); 4473 4474 if (objTitleInput && objTitleInput.length) { 4475 objTitleInput.on("input", function () { 4476 var value = objTitleInput.val(); 4477 4478 if (value === "") { 4479 objItemTitle 4480 .addClass("unite-repeater-item-title-placeholder") 4481 .text("Item Title"); 4482 } else { 4483 objItemTitle 4484 .removeClass("unite-repeater-item-title-placeholder") 4485 .text(value); 4486 } 4487 }); 4488 } 4489 4490 t.onSettingChange(null, objWrapper); 4491 } 4417 4492 4418 4493 /** … … 4449 4524 */ 4450 4525 function getRepeaterValues(objWrapper) { 4526 4451 4527 var values = []; 4452 4528 … … 4470 4546 4471 4547 if (obj && typeof obj.getSettingsValues === "function") { 4472 values.push(obj.getSettingsValues());4548 values.push(obj.getSettingsValues()); 4473 4549 } 4474 4550 }); … … 4646 4722 return; 4647 4723 4648 if (!g_arrControls[controlID]) 4649 return;4724 if (!g_arrControls[controlID]) 4725 return; 4650 4726 4651 4727 var controlValue = getSettingInputValue(objInput); … … 4658 4734 4659 4735 jQuery.each(arrChildControls, function (childName, objControl) { 4736 4660 4737 var isSap = g_ucAdmin.getVal(objControl, "forsap"); 4661 4738 var rowID; … … 4665 4742 rowID = g_IDPrefix + "ucsap_" + childName; 4666 4743 } else { //setting 4667 rowID = g_IDPrefix + childName + "_row"; 4668 objChildInput = jQuery(g_IDPrefix + childName); 4744 // rowID = g_IDPrefix + childName + "_row"; 4745 // objChildInput = jQuery(g_IDPrefix + childName); 4746 4747 rowID = '#' + g_objWrapper.attr('id') + ' [data-name=' + childName + ']' ; 4669 4748 } 4670 4749 … … 4916 4995 var objPicker = jQuery(this); 4917 4996 4918 objPicker.html('<option value="px" data-content="<div class=\'unite-units-picker-item\'>px</div>">px</option><option value="vh" data-content="<div class=\'unite-units-picker-item\'>vh</div>">vh</option><option value="%" data-content="<div class=\'unite-units-picker-item\'>%</div>">%</option><option value="em" data-content="<div class=\'unite-units-picker-item\'>em</div>">em</option><option value="rem" data-content="<div class=\'unite-units-picker-item\'>rem</div>">rem</option> ').val(objPicker.data('value'));4997 objPicker.html('<option value="px" data-content="<div class=\'unite-units-picker-item\'>px</div>">px</option><option value="vh" data-content="<div class=\'unite-units-picker-item\'>vh</div>">vh</option><option value="%" data-content="<div class=\'unite-units-picker-item\'>%</div>">%</option><option value="em" data-content="<div class=\'unite-units-picker-item\'>em</div>">em</option><option value="rem" data-content="<div class=\'unite-units-picker-item\'>rem</div>">rem</option><option value="deg" data-content="<div class=\'unite-units-picker-item\'>deg</div>">deg</option>').val(objPicker.data('value')); 4919 4998 4920 4999 initSelect2(objPicker, { … … 4992 5071 */ 4993 5072 this.getSelectorsCss = function () { 5073 4994 5074 var objInputs = getObjInputs(); 5075 4995 5076 var css = ""; 4996 5077 4997 5078 jQuery.each(objInputs, function () { 5079 4998 5080 var objInput = jQuery(this); 4999 5081 5000 5082 // skip hidden/disabled setting 5001 5083 if (objInput.closest(".unite-setting-row").hasClass("unite-setting-hidden") === true) … … 5004 5086 var type = getInputType(objInput); 5005 5087 var style; 5006 5088 5007 5089 switch (type) { 5008 5090 case "repeater": … … 5013 5095 break; 5014 5096 } 5015 5097 5098 5099 //-- debug selector css 5100 5101 if(isDebugSelector(objInput) == true){ 5102 trace("process input selectors: "+name); 5103 var name = getInputName(objInput); 5104 trace(objInput); 5105 trace(style); 5106 } 5107 5108 5016 5109 if (style) 5017 5110 css += style; … … 5030 5123 selector = selector.trim(); 5031 5124 5032 if (g_selectorWrapperID) 5033 selector = "#" + g_selectorWrapperID + " " + selector; 5125 if (g_selectorWrapperID) { 5126 let selectorId = jQuery('#' + g_selectorWrapperID + ' .ue-widget-root').attr('id'); 5127 selector = "." + selectorId + " " + selector; 5128 } 5129 5034 5130 5035 5131 return selector; … … 5078 5174 * get dimentions selector replaces 5079 5175 */ 5080 function getDimentionsSelectorReplaces(value) { 5081 return { 5082 "{{top}}": value.top + value.unit, 5083 "{{right}}": value.right + value.unit, 5084 "{{bottom}}": value.bottom + value.unit, 5085 "{{left}}": value.left + value.unit 5086 }; 5087 } 5176 function getDimentionsSelectorReplaces(value, isDebug){ 5177 5178 if(isDebug === true){ 5179 trace("get dimentions replaces"); 5180 trace(value); 5181 } 5182 5183 if (!value || typeof value !== "object") 5184 return; 5185 5186 var unit = value.unit || "px"; 5187 5188 function normSide(side) { 5189 if (side === "" || side == null) 5190 return ""; 5191 5192 return side + unit; 5193 } 5194 5195 var top = normSide(value.top); 5196 var right = normSide(value.right); 5197 var bottom = normSide(value.bottom); 5198 var left = normSide(value.left); 5199 5200 if (!top && !right && !bottom && !left) 5201 return; 5202 5203 if (!top) top = "0" + unit; 5204 if (!right) right = "0" + unit; 5205 if (!bottom) bottom = "0" + unit; 5206 if (!left) left = "0" + unit; 5207 5208 return { 5209 "{{top}}": top, 5210 "{{right}}": right, 5211 "{{bottom}}": bottom, 5212 "{{left}}": left 5213 }; 5214 } 5088 5215 5089 5216 /** … … 5116 5243 * get input selector replaces 5117 5244 */ 5118 function getInputSelectorReplaces(objInput) { 5119 5120 if (!objInput || objInput.length === 0) return; 5245 function getInputSelectorReplaces(objInput, isDebug) { 5246 5247 if (!objInput || objInput.length === 0) 5248 return; 5249 5121 5250 var value = getSettingInputValue(objInput); 5122 5251 5252 if(isDebug == true){ 5253 trace("run function: getInputSelectorReplaces"); 5254 trace(value); 5255 } 5256 5123 5257 if (!value) 5124 5258 return; 5125 5259 5126 5260 var type = getInputType(objInput); 5127 5261 5262 if (value === g_vars.NOT_UPDATE_OPTION) 5263 return; 5264 5265 if (value === "" || value == null) 5266 return; 5267 5268 // if (typeof value != "object") return; 5269 5128 5270 switch (type) { 5129 5271 case "dimentions": 5130 return getDimentionsSelectorReplaces(value); 5272 var replaces = getDimentionsSelectorReplaces(value); 5273 return replaces; 5274 break; 5131 5275 case "image": 5132 return getImageSelectorReplaces(value); 5276 var replaces = getImageSelectorReplaces(value); 5277 return replaces; 5278 break; 5133 5279 case "range": 5134 return getRangeSliderSelectorReplaces(value); 5135 } 5136 5280 var replaces = getRangeSliderSelectorReplaces(value); 5281 return replaces; 5282 break; 5283 } 5284 5137 5285 return { "{{value}}": value }; 5138 5286 } 5287 5288 5139 5289 5140 5290 /** … … 5187 5337 */ 5188 5338 function processInputSelectors(objInput) { 5339 5189 5340 var groupSelector = objInput.data("group-selector"); 5190 5341 … … 5199 5350 var type = getInputType(objInput); 5200 5351 var style; 5201 5352 5202 5353 switch (type) { 5203 5354 case "group_selector": … … 5215 5366 break; 5216 5367 } 5217 5368 5218 5369 if (!style) 5219 5370 return; 5220 5371 5221 5372 var device = getResponsivePickerValue(objInput); 5222 5373 5223 5374 switch (device) { 5224 5375 case "tablet": … … 5236 5387 * process repeater selectors 5237 5388 */ 5238 function processRepeaterSelectors(objWrapper) {5389 function processRepeaterSelectors(objWrapper) { 5239 5390 var style = ""; 5240 5391 5241 5392 objWrapper.find(".unite-repeater-item").each(function () { 5242 var objSettings = jQuery(this).data("objsettings"); 5243 5244 objSettings?.setSelectorWrapperID(g_selectorWrapperID); 5245 5246 var value = objSettings?.getSettingsValues(); 5247 var css = objSettings?.getSelectorsCss(); 5248 5249 css = processSelectorReplaces(css, { "{{current_item}}": ".elementor-repeater-item-" + value?._generated_id }); 5250 5251 style += css; 5393 var objItem = jQuery(this); 5394 var objSettings = objItem.data("objsettings"); 5395 5396 if (!objSettings) { 5397 var $inner = objItem.find(".unite_settings_wrapper"); 5398 if ($inner.length) { 5399 try { 5400 objSettings = new UniteSettingsUC(); 5401 objSettings.init($inner, {}); 5402 objItem.data("objsettings", objSettings); 5403 } catch (e) { 5404 objSettings = null; 5405 } 5406 } 5407 } 5408 5409 if (objSettings) { 5410 5411 objSettings.setSelectorWrapperID(g_selectorWrapperID); 5412 5413 var value = objSettings.getSettingsValues(); 5414 var css = objSettings.getSelectorsCss(); 5415 5416 css = processSelectorReplaces(css, { "{{current_item}}": ".elementor-repeater-item-" + value?._generated_id }); 5417 5418 style += css; 5419 } 5252 5420 }); 5253 5421 … … 5296 5464 */ 5297 5465 function getInputSelectorsStyle(objInput, selectors) { 5298 var replaces = getInputSelectorReplaces(objInput); 5466 5467 //debug 5468 var isDebug = isDebugSelector(objInput); 5469 5470 if(isDebug){ 5471 trace("Run function: getInputSelectorsStyle"); 5472 trace("get selector style: "+g_debug_selectors); 5473 } 5474 5475 var replaces = getInputSelectorReplaces(objInput, isDebug); 5476 5477 if(isDebug){ 5478 trace("replacers"); 5479 trace(replaces); 5480 } 5299 5481 5300 5482 if (!replaces) 5301 5483 return; 5302 5303 return getSelectorsStyle(selectors, replaces); 5484 5485 var style = getSelectorsStyle(selectors, replaces); 5486 5487 if(isDebug){ 5488 trace("style"); 5489 trace(style); 5490 } 5491 5492 return style; 5493 } 5494 5495 /** 5496 * check if debug selector 5497 */ 5498 function isDebugSelector(objInput){ 5499 5500 if(!g_debug_selectors) 5501 return(g_debug_selectors); 5502 5503 var name = getInputName(objInput); 5504 if(name == g_debug_selectors) 5505 return(true); 5506 5507 return(false); 5304 5508 } 5305 5509 -
unlimited-elements-for-elementor/trunk/js/unitecreator_addon_config.js
r2535588 r3429507 661 661 this.init = function(objWrapper, isPreviewMode){ 662 662 663 663 664 if(g_objWrapper) 664 665 throw new Error("the config is alrady inited, can't init it twice"); … … 687 688 parseInputOptions(objOptions); 688 689 689 690 690 //set settings events 691 691 692 g_objSettings.init(g_objSettingsContainer); 692 693 693 694 initEvens(); 694 695 -
unlimited-elements-for-elementor/trunk/js/unitecreator_addondefaults_admin.js
r2386165 r3429507 170 170 */ 171 171 this.init = function(){ 172 172 173 173 g_objWrapper = jQuery("#uc_addondefaults_wrapper"); 174 174 g_options = g_objWrapper.data("options"); -
unlimited-elements-for-elementor/trunk/provider/assets/provider_admin.js
r3329756 r3429507 642 642 * get first editor options 643 643 */ 644 function initEditor_tinyMCE_GetOptions(objEditorsOptions) { 645 if (jQuery.isEmptyObject(objEditorsOptions)) 646 return {}; 647 648 for (var key in objEditorsOptions) { 649 var options = objEditorsOptions[key]; 650 651 if (options !== null) 652 return options; 653 } 654 655 return {}; 656 } 644 645 function initEditor_tinyMCE_GetOptions(objEditorsOptions) { 646 if (!objEditorsOptions || jQuery.isEmptyObject(objEditorsOptions)) 647 return {}; 648 649 for (var key in objEditorsOptions) { 650 if (!objEditorsOptions.hasOwnProperty(key)) continue; 651 var options = objEditorsOptions[key]; 652 653 if (options != null) 654 return options; 655 } 656 657 return {}; 658 } 657 659 658 660 /** 659 661 * trigger change on editor, pass back 660 662 */ 661 function onEditorChange(editor, objSettings){ 662 663 var objInput = editor.getElement(); 664 objInput = jQuery(objInput); 665 objSettings.triggerKeyupEvent(objInput); 666 } 663 var uelm_ucEditorChangeTimers = uelm_ucEditorChangeTimers || {}; 664 665 function onEditorChange(editor, objSettings) { 666 667 var DEBOUNCE_DELAY = 600; 668 669 var editorId = editor && (editor.id || (editor.getElement && editor.getElement().id)); 670 if (!editorId) 671 return; 672 673 if (uelm_ucEditorChangeTimers[editorId]) { 674 clearTimeout(uelm_ucEditorChangeTimers[editorId]); 675 } 676 677 uelm_ucEditorChangeTimers[editorId] = setTimeout(function () { 678 679 if (!editor || !editor.getElement) { 680 return; 681 } 682 683 var el = editor.getElement(); 684 if (!el) 685 return; 686 687 editor.save(); 688 689 var $el = jQuery(el); 690 691 $el.trigger("keyup"); 692 $el.trigger("change"); 693 694 delete uelm_ucEditorChangeTimers[editorId]; 695 696 }, DEBOUNCE_DELAY); 697 } 667 698 668 699 /** … … 714 745 */ 715 746 function initEditors_afterTimeout(objSettings, arrEditorNames, isForce) { 747 716 748 if (typeof window.tinyMCEPreInit === "undefined" && arrEditorNames.length) 717 749 throw new Error("Init " + arrEditorNames[0] + " editor error. no other editors found on page"); … … 725 757 if (isForce === true 726 758 || window.tinyMCEPreInit.mceInit.hasOwnProperty(inputID) === false 727 || window.tinyMCEPreInit.mceInit[inputID] === null) 728 initEditor_tinyMCE(inputID, objSettings); 759 || window.tinyMCEPreInit.mceInit[inputID] === null) { 760 initEditor_tinyMCE(inputID, objSettings); 761 } 762 729 763 }); 730 764 } … … 929 963 } 930 964 931 window.tinyMCEPreInit.mceInit[editorID] = null; 932 window.tinyMCEPreInit.qtInit[editorID] = null; 965 // window.tinyMCEPreInit.mceInit[editorID] = null; 966 // window.tinyMCEPreInit.qtInit[editorID] = null; 967 968 if (window.tinyMCEPreInit && window.tinyMCEPreInit.mceInit) { 969 delete window.tinyMCEPreInit.mceInit[editorID]; 970 } 971 972 if (window.tinyMCEPreInit && window.tinyMCEPreInit.qtInit) { 973 delete window.tinyMCEPreInit.qtInit[editorID]; 974 } 933 975 } 934 976 }); -
unlimited-elements-for-elementor/trunk/provider/core/unlimited_elements/dialog_param_elementor.class.php
r3329756 r3429507 944 944 $arrTypes["rss_feed"] = __("Rss Feed Fields","unlimited-elements-for-elementor"); 945 945 $arrTypes["repeater"] = __("Repeater","unlimited-elements-for-elementor"); 946 $arrTypes["reviews"] = __("Google Reviews","unlimited-elements-for-elementor"); 946 947 947 948 $optionsClass = "uc-special-attribute-options"; -
unlimited-elements-for-elementor/trunk/provider/core/unlimited_elements/gutenberg/assets/gutenberg_integrate.js
r3397365 r3429507 3 3 4 4 (function (wp) { 5 var g_debug = true; 5 6 var g_debug = true; 7 6 8 function trace(str){ console.log(str); } 7 9 function debug(){ if(!g_debug) return; console.log.apply(console, arguments); } 8 9 // ping editor to activate Save btn 10 function pingSaveButton(){ 11 try{ 12 const d = wp?.data?.dispatch('core/editor'); 13 if (d?.editPost) { d.editPost({}); return; } 14 }catch(e){} 15 try{ 16 const d = wp?.data?.dispatch('core/edit-site'); 17 if (d?.setIsDirty) { d.setIsDirty(true); return; } 18 }catch(e){} 19 try{ 20 const d = wp?.data?.dispatch('core/edit-widgets'); 21 if (d?.setHasEdits) { d.setHasEdits(true); return; } 22 }catch(e){} 23 } 24 10 25 11 var wbe = wp.blockEditor; 26 12 var wc = wp.components; … … 45 31 return el("img", { src: previewUrl, style: { width: "100%", height: "auto" } }); 46 32 47 var blockProps = wbe.useBlockProps(); 48 var widgetContentState = we.useState(null); 49 var settingsVisibleState = we.useState(false); 50 var settingsContentState = we.useState(null); 51 var isLoadingSettingsState = we.useState(false); 52 53 var widgetRef = we.useRef(null); 54 var widgetLoaderRef = we.useRef(null); 55 var widgetRequestRef = we.useRef(null); 56 57 var ucSettingsRef = we.useRef(new UniteSettingsUC()); 58 var ucHelperRef = we.useRef(new UniteCreatorHelper()); 59 60 var settingsInitedRef = we.useRef(false); 61 var initedSettingsElementRef = we.useRef(null); 62 var lastSentDataRef = we.useRef(null); 63 var settingsObserverRef = we.useRef(null); 64 var settingsWatchdogTimerRef = we.useRef(null); 65 66 var ucSettings = ucSettingsRef.current; 67 var ucHelper = ucHelperRef.current; 33 const blockProps = wbe.useBlockProps(); 34 35 // block options cache key 36 const cacheKeyBase = props.clientId || props.attributes._id; 37 38 // state 39 const [widgetContent, setWidgetContent] = we.useState(null); 40 const [settingsVisible, setSettingsVisible] = we.useState(false); 41 const [settingsContent, setSettingsContent] = we.useState(null); 42 const [isLoadingSettings, setIsLoadingSettings] = we.useState(false); 43 44 // refs 45 const widgetRef = we.useRef(null); 46 const widgetLoaderRef = we.useRef(null); 47 const widgetRequestRef = we.useRef(null); 48 49 const ucSettingsRef = we.useRef(new UniteSettingsUC()); 50 const ucHelperRef = we.useRef(new UniteCreatorHelper()); 51 52 const settingsInitedRef = we.useRef(false); 53 const initedSettingsElementRef = we.useRef(null); 54 const lastSentDataRef = we.useRef(null); 55 const settingsObserverRef = we.useRef(null); 56 57 const ucSettings = ucSettingsRef.current; 58 const ucHelper = ucHelperRef.current; 68 59 69 60 // orchestrator refs 70 const didFirstPreviewRef = we.useRef(false); 71 const firstPreviewReadyRef = we.useRef(false); 72 const lastPreviewPayloadRef = we.useRef(null); 73 const firstPreviewTimerRef = we.useRef(null); 61 const didFirstPreviewRef = we.useRef(false); 62 const firstPreviewReadyRef = we.useRef(false); 63 const lastPreviewPayloadRef = we.useRef(null); 64 const firstPreviewTimerRef = we.useRef(null); 74 65 75 66 function isSettingsReady() { … … 80 71 } 81 72 82 // root block's element83 73 function getWidgetRootEl(){ 84 74 const $root = jQuery(widgetRef.current); 85 // пробуем найти «внутренний» корень, если он помечен 75 86 76 const $inner = $root.find('[data-uc-root]').first(); 87 77 return ($inner.length ? $inner : $root); 88 78 } 89 79 90 function ensureStyleTag($root){91 if ($root.find("[name=uc_selectors_css]").length === 0) {92 $root.prepend('<style name="uc_selectors_css"></style>');93 }94 return $root.find("[name=uc_selectors_css]");95 }96 97 80 function applyStylePreviewOnce(maxTries = 30, delay = 50){ 81 82 if (!props.isSelected) { 83 return; 84 } 85 98 86 let tries = 0; 99 87 (function tick(){ … … 102 90 103 91 if ($root.length && ready) { 104 ensureStyleTag($root); 105 const css = (ucSettings.getSelectorsCss && ucSettings.getSelectorsCss()) || ''; 106 if (css && css !== lastSelectorsCssRef.current) { 107 $root.find("[name=uc_selectors_css]").text(css); 108 lastSelectorsCssRef.current = css; 109 } 110 111 const selIncludes = ucSettings.getSelectorsIncludes && ucSettings.getSelectorsIncludes(); 112 const selHash = stableStringify(selIncludes || {}); 113 if (selIncludes && selHash !== lastIncludesHashRef.current) { 114 ucHelper.putIncludes(getPreviewWindowElement(), selIncludes); 115 lastIncludesHashRef.current = selHash; 116 } 117 // одновременно прокинем box-model, если есть 118 applyLiveBoxModelInline(); 92 updateSelectorsPreview(); 119 93 return; 120 94 } … … 124 98 125 99 // --- delay for color/range/dimensions --- 126 const DELAY_MS = 800;127 100 const saveDelayTimerRef = we.useRef(null); 128 const pendingSaveRef = we.useRef(false);129 101 const lastChangeTypeRef = we.useRef(null); 130 102 const suppressNextReloadRef= we.useRef(false); 131 const pendingSuppressByDomRef = we.useRef(false);132 133 const styleOnlyDirtyRef = we.useRef(false);134 103 135 104 const lastSelectorsCssRef = we.useRef(''); … … 137 106 138 107 function applyLiveBoxModelInline(){ 108 139 109 try{ 140 110 if (!isSettingsReady()) return; … … 169 139 } 170 140 171 // общая функция обновления css + includes + live box-model 141 // update oldCss with props from newCss 142 function mergeCss(oldCss, newCss) { 143 function createEmptyMap() { 144 return { 145 scopesOrder: [], 146 selectorsOrder: {}, 147 rules: {} 148 }; 149 } 150 151 function ensureScope(map, scope) { 152 if (!map.rules[scope]) { 153 map.rules[scope] = {}; 154 map.selectorsOrder[scope] = []; 155 map.scopesOrder.push(scope); 156 } 157 } 158 159 function addRuleToMap(map, scope, selector, body) { 160 selector = selector && selector.trim(); 161 if (!selector) return; 162 163 var decls = {}; 164 var parts = body.split(';'); 165 for (var i = 0; i < parts.length; i++) { 166 var part = parts[i].trim(); 167 if (!part) continue; 168 var colonPos = part.indexOf(':'); 169 if (colonPos === -1) continue; 170 var prop = part.slice(0, colonPos).trim(); 171 var val = part.slice(colonPos + 1).trim(); 172 if (!prop || !val) continue; 173 decls[prop] = val; 174 } 175 176 if (!Object.keys(decls).length) return; 177 178 ensureScope(map, scope); 179 if (!map.rules[scope][selector]) { 180 map.rules[scope][selector] = {}; 181 map.selectorsOrder[scope].push(selector); 182 } 183 184 var target = map.rules[scope][selector]; 185 for (var p in decls) { 186 target[p] = decls[p]; 187 } 188 } 189 190 function parseSimpleRulesInto(map, scope, css) { 191 var len = css.length; 192 var i = 0; 193 while (i < len) { 194 195 while (i < len && /\s|;/.test(css[i])) i++; 196 if (i >= len) break; 197 198 if (css[i] === '}') { 199 i++; 200 continue; 201 } 202 203 var selStart = i; 204 while (i < len && css[i] !== '{' && css[i] !== '}') i++; 205 if (i >= len || css[i] === '}') { 206 i++; 207 continue; 208 } 209 var selector = css.slice(selStart, i).trim(); 210 i++; 211 212 var bodyStart = i; 213 var depth = 1; 214 while (i < len && depth > 0) { 215 if (css[i] === '{') depth++; 216 else if (css[i] === '}') depth--; 217 i++; 218 } 219 var body = css.slice(bodyStart, i - 1); 220 221 addRuleToMap(map, scope, selector, body); 222 } 223 } 224 225 function parseCssToMap(css) { 226 var map = createEmptyMap(); 227 if (!css) return map; 228 229 var len = css.length; 230 var i = 0; 231 232 while (i < len) { 233 234 while (i < len && /\s|;/.test(css[i])) i++; 235 if (i >= len) break; 236 237 if (css[i] === '@') { 238 var atStart = i; 239 while (i < len && css[i] !== '{') i++; 240 if (i >= len) break; 241 var prelude = css.slice(atStart, i).trim(); 242 i++; // '{' 243 244 var bodyStart = i; 245 var depth = 1; 246 while (i < len && depth > 0) { 247 if (css[i] === '{') depth++; 248 else if (css[i] === '}') depth--; 249 i++; 250 } 251 var body = css.slice(bodyStart, i - 1); 252 253 parseSimpleRulesInto(map, prelude, body); 254 } else if (css[i] === '}') { 255 i++; 256 continue; 257 } else { 258 259 var selStart = i; 260 while (i < len && css[i] !== '{' && css[i] !== '}') i++; 261 if (i >= len || css[i] === '}') { 262 i++; 263 continue; 264 } 265 var selector = css.slice(selStart, i).trim(); 266 i++; 267 268 var bodyStart2 = i; 269 var depth2 = 1; 270 while (i < len && depth2 > 0) { 271 if (css[i] === '{') depth2++; 272 else if (css[i] === '}') depth2--; 273 i++; 274 } 275 var body2 = css.slice(bodyStart2, i - 1); 276 277 addRuleToMap(map, '', selector, body2); 278 } 279 } 280 281 return map; 282 } 283 284 function mapToCss(map) { 285 var out = ''; 286 287 for (var s = 0; s < map.scopesOrder.length; s++) { 288 var scope = map.scopesOrder[s]; 289 var selectors = map.selectorsOrder[scope]; 290 if (!selectors || !selectors.length) continue; 291 292 if (scope === '') { 293 294 for (var i = 0; i < selectors.length; i++) { 295 var sel = selectors[i]; 296 var props = map.rules[scope][sel]; 297 var keys = Object.keys(props); 298 if (!keys.length) continue; 299 out += sel + '{'; 300 for (var k = 0; k < keys.length; k++) { 301 var prop = keys[k]; 302 out += prop + ':' + props[prop] + ';'; 303 } 304 out += '}'; 305 } 306 } else { 307 308 var inner = ''; 309 for (var j = 0; j < selectors.length; j++) { 310 var sel2 = selectors[j]; 311 var props2 = map.rules[scope][sel2]; 312 var keys2 = Object.keys(props2); 313 if (!keys2.length) continue; 314 inner += sel2 + '{'; 315 for (var k2 = 0; k2 < keys2.length; k2++) { 316 var prop2 = keys2[k2]; 317 inner += prop2 + ':' + props2[prop2] + ';'; 318 } 319 inner += '}'; 320 } 321 if (inner) { 322 out += scope + '{' + inner + '}'; 323 } 324 } 325 } 326 327 return out; 328 } 329 330 var oldMap = parseCssToMap(oldCss || ''); 331 var newMap = parseCssToMap(newCss || ''); 332 333 for (var s = 0; s < newMap.scopesOrder.length; s++) { 334 var scope = newMap.scopesOrder[s]; 335 var newSelectors = newMap.selectorsOrder[scope]; 336 if (!newSelectors || !newSelectors.length) continue; 337 338 ensureScope(oldMap, scope); 339 340 for (var i = 0; i < newSelectors.length; i++) { 341 var sel = newSelectors[i]; 342 var newProps = newMap.rules[scope][sel]; 343 344 if (!oldMap.rules[scope][sel]) { 345 oldMap.rules[scope][sel] = {}; 346 oldMap.selectorsOrder[scope].push(sel); 347 } 348 349 var target = oldMap.rules[scope][sel]; 350 for (var p in newProps) { 351 target[p] = newProps[p]; 352 } 353 } 354 } 355 356 return mapToCss(oldMap); 357 } 358 359 /** 360 * update selectors preview 361 */ 172 362 function updateSelectorsPreview() { 173 if (!isSettingsReady()) return; 363 364 var isDetailedDebug = false; 365 366 debug("run func: updateSelectorsPreview"); 367 368 if (!props.isSelected) { 369 370 if(isDetailedDebug == true) 371 trace("not selected, exit"); 372 373 return; 374 } 375 376 if (!isSettingsReady()) { 377 378 if(isDetailedDebug == true) 379 trace("not ready, exit"); 380 381 return; 382 } 174 383 const $root = jQuery(widgetRef.current); 175 if ($root.length === 0) return; 176 177 ensureStyleTag($root); 178 384 if ($root.length === 0) { 385 386 if(isDetailedDebug == true) 387 trace("not root, exit"); 388 389 return; 390 } 391 179 392 const css = (ucSettings.getSelectorsCss && ucSettings.getSelectorsCss()) || ''; 393 180 394 if (css && css !== lastSelectorsCssRef.current) { 181 $root.find('[name=uc_selectors_css]').text(css); 182 lastSelectorsCssRef.current = css; 395 396 if ($root.find("[name=uc_selectors_css]").length === 0) { 397 $root.prepend('<style name="uc_selectors_css"></style>'); 398 } 399 const $style = $root.find('[name=uc_selectors_css]'); 400 const prevCss = $style.text() || ''; 401 402 const mergedCss = mergeCss(prevCss, css); 403 404 $style.text(mergedCss); 405 lastSelectorsCssRef.current = mergedCss; 406 407 if(isDetailedDebug == true) 408 trace("merge css!"); 409 410 }else{ 411 412 if(isDetailedDebug == true) 413 trace("not root, exit"); 414 183 415 } 184 416 … … 190 422 } 191 423 192 // и сразу инлайн-бокс-модель193 424 applyLiveBoxModelInline(); 194 } 195 425 426 } 427 428 /** 429 * flush save new 430 * @param typeOverride 431 */ 196 432 function flushSaveNow(typeOverride) { 197 433 if (!isSettingsReady()) return; … … 212 448 213 449 try { 214 delete uelm_WidgetSettingsCache[props.attributes._id]; 215 uelm_WidgetSettingsCacheFlags[props.attributes._id] = false; 450 delete uelm_WidgetSettingsCache[cacheKeyBase]; 451 delete uelm_WidgetSettingsCache[cacheKeyBase + '_settings']; 452 453 uelm_WidgetSettingsCacheFlags[cacheKeyBase] = false; 454 uelm_WidgetSettingsCacheFlags[cacheKeyBase + '_settings'] = false; 216 455 } catch(e){} 217 456 … … 223 462 224 463 props.setAttributes({ data: mergedStr }); 225 styleOnlyDirtyRef.current = false; 226 } finally { 227 pendingSaveRef.current = false; 228 } 464 465 } finally { } 229 466 } 230 467 … … 243 480 var settingsId = "ue-gutenberg-settings-" + props.clientId; 244 481 var settingsErrorId = settingsId + "-error"; 245 var settingsVisible = settingsVisibleState[0];246 var setSettingsVisible = settingsVisibleState[1];247 248 var settingsContent = settingsContentState[0];249 var setSettingsContent = settingsContentState[1];250 251 var widgetContent = widgetContentState[0];252 var setWidgetContent = widgetContentState[1];253 254 var isLoadingSettings = isLoadingSettingsState[0];255 var setIsLoadingSettings = isLoadingSettingsState[1];256 482 257 483 function stableStringify(obj){ 258 484 try { return JSON.stringify(obj, Object.keys(obj).sort()); } 259 485 catch(e){ return ""; } 260 }261 function deepEqual(a, b){262 if (a === b) return true;263 return stableStringify(a) === stableStringify(b);264 486 } 265 487 … … 275 497 }; 276 498 277 var saveSettingsIfChanged = function (explicitPatch, suppressReload) {278 const currentObj = (function(){279 try { return props.attributes.data ? JSON.parse(props.attributes.data) : {}; }280 catch(e){ return {}; }281 })();282 283 const patch = explicitPatch || ucSettings.getSettingsValues();284 const mergedObj = { ...currentObj, ...patch };285 286 if (deepEqual(currentObj, mergedObj)) {287 updateSelectorsPreview();288 return;289 }290 291 const mergedStr = JSON.stringify(mergedObj);292 293 const isStyleOnly = !!suppressReload || !!pendingSuppressByDomRef.current;294 if (isStyleOnly) {295 pendingSuppressByDomRef.current = false;296 styleOnlyDirtyRef.current = true;297 298 updateSelectorsPreview();299 pingSaveButton();300 return;301 }302 303 if (lastSentDataRef.current === mergedStr) {304 updateSelectorsPreview();305 return;306 }307 308 try {309 delete uelm_WidgetSettingsCache[props.attributes._id];310 uelm_WidgetSettingsCacheFlags[props.attributes._id] = false;311 } catch(e){}312 313 lastSentDataRef.current = mergedStr;314 315 props.setAttributes({316 _rootId: ucHelper.getRandomString(5),317 data: mergedStr,318 });319 };320 321 499 var initSettings = function () { 500 501 debug("init settings!"); 502 322 503 var $settingsElement = getSettingsElement(); 323 504 if (!$settingsElement || $settingsElement.length === 0) return; … … 340 521 341 522 function handleSettingsEvent(evt, payload) { 523 524 const name = (payload?.name ?? '').toString(); 525 342 526 const type = (payload?.type ?? '').toString().toLowerCase().trim(); 343 527 const STYLE_ONLY_TYPES = ['color','range','dimensions']; … … 347 531 // style only fields 348 532 lastChangeTypeRef.current = 'styles'; 349 350 styleOnlyDirtyRef.current = true;351 533 352 534 if (saveDelayTimerRef.current) { … … 355 537 } 356 538 saveDelayTimerRef.current = setTimeout(() => { 357 flushSaveNow( type);539 flushSaveNow('styles'); 358 540 }, 180); 359 541 } else { … … 367 549 flushSaveNow(type); 368 550 } 369 370 } 371 551 } 552 372 553 ucSettings.setEventOnChange(handleSettingsEvent); 373 554 if (typeof ucSettings.onEvent === 'function') { … … 376 557 377 558 ucSettings.setEventOnSelectorsChange(function () { 378 if (!isSettingsReady()) return; 559 560 if (!isSettingsReady()) 561 return; 562 379 563 updateSelectorsPreview(); 564 565 lastChangeTypeRef.current = 'styles'; 566 567 flushSaveNow('styles'); 380 568 }); 381 569 382 570 ucSettings.setEventOnResponsiveTypeChange(function (event, type) { 383 uelm_WidgetSettingsCacheFlags[ props.attributes._id] = true;384 uelm_WidgetSettingsCacheFlags[ props.attributes._id+ '_settings'] = true;571 uelm_WidgetSettingsCacheFlags[cacheKeyBase] = true; 572 uelm_WidgetSettingsCacheFlags[cacheKeyBase + '_settings'] = true; 385 573 386 574 var deviceType = type.charAt(0).toUpperCase() + type.substring(1); … … 403 591 }; 404 592 593 // init settings when panel visible + settings HTML ready + real DOM exists 405 594 function maybeInitSettings(){ 406 if (!settingsVisible) return; 407 if (!settingsContent) return; 408 595 596 if (!settingsVisible) 597 return; 598 599 if (!settingsContent) 600 return; 601 409 602 var $settingsElement = getSettingsElement(); 410 603 if (!$settingsElement || $settingsElement.length === 0) return; … … 432 625 } 433 626 434 function startSettingsWatchdog(){435 stopSettingsWatchdog();436 settingsWatchdogTimerRef.current = setInterval(function(){437 if (!settingsVisible || !settingsContent) return;438 439 var $settingsElement = getSettingsElement();440 var elem = $settingsElement && $settingsElement[0];441 442 if (!elem) return;443 444 if (!ucSettings.isInited() || initedSettingsElementRef.current !== elem) {445 initSettings();446 }447 }, 2000);448 }449 function stopSettingsWatchdog(){450 if (settingsWatchdogTimerRef.current) {451 clearInterval(settingsWatchdogTimerRef.current);452 settingsWatchdogTimerRef.current = null;453 }454 }455 456 627 // AJAX: load settings HTML 457 628 var loadSettingsContent = function () { 458 var widgetCacheKey = props.attributes._id+ '_settings';629 var widgetCacheKey = cacheKeyBase + '_settings'; 459 630 460 631 setIsLoadingSettings(true); … … 480 651 if (isTestFreeVersion) requestData.testfreeversion = true; 481 652 482 debug('Load get_addon_settings_html');483 653 g_ucAdmin.ajaxRequest("get_addon_settings_html", requestData, function (response) { 484 654 … … 497 667 // AJAX: load widget HTML 498 668 var loadWidgetContent = function (overrideSettings) { 499 var widgetCacheKey = props.attributes._id;669 var widgetCacheKey = cacheKeyBase; 500 670 501 671 if ( uelm_WidgetSettingsCache[widgetCacheKey] && uelm_WidgetSettingsCacheFlags[widgetCacheKey] ) { … … 507 677 508 678 if (!widgetContent) { 509 // load existing widgets from the page 510 for (var index in g_gutenbergParsedBlocks) { 511 var block = g_gutenbergParsedBlocks[index]; 512 if (block.name === props.name) { 679 680 if (typeof window.uelm_setBlocks === 'undefined') { 681 window.uelm_setBlocks = new Set(); 682 } 683 684 var blockKey = props.name + '_' + props.clientId; 685 686 if (window.uelm_setBlocks.has(blockKey)) { 687 // Block already processed, skipping 688 return; 689 } 690 691 for (var i = 0; i < g_gutenbergParsedBlocks.length; i++) { 692 var block = g_gutenbergParsedBlocks[i]; 693 694 if (block && block.name === props.name && block.html) { 695 513 696 setWidgetContent(block.html); 514 delete g_gutenbergParsedBlocks[index]; 515 debug('loadWidgetContent loaded from page content'); 697 698 try { 699 if (isSettingsReady()) { 700 var values = getSettings(); 701 if (values !== null) { 702 ucSettings.setCacheValues(values); 703 } 704 } 705 } catch (e) { 706 console.warn('Failed to sync ucSettings from data after load-from-page', e); 707 } 708 709 uelm_WidgetSettingsCache[widgetCacheKey] = { 710 html: block.html, 711 includes: {} 712 }; 713 uelm_WidgetSettingsCacheFlags[widgetCacheKey] = true; 714 715 delete g_gutenbergParsedBlocks[i]; 716 717 window.uelm_setBlocks.add(blockKey); 718 719 debug('loadWidgetContent loaded from page'); 516 720 return; 517 721 } 518 722 } 723 724 console.warn('No content found for block:', blockKey); 519 725 } 520 726 … … 527 733 loaderElement.show(); 528 734 529 debug('loadWidgetContent load from server , uc_items length: ' + (settings?.uc_items?.length ?? 0));735 debug('loadWidgetContent load from server'); 530 736 531 737 widgetRequestRef.current = g_ucAdmin.ajaxRequest("get_addon_output_data", { … … 543 749 loaderElement.hide(); 544 750 }); 751 545 752 }; 546 753 … … 569 776 570 777 attachSettingsObserver(); 571 startSettingsWatchdog();778 // startSettingsWatchdog(); 572 779 573 780 loadWidgetContent(); … … 610 817 // mark color/range/dimensions to suppress reload 611 818 we.useEffect(function () { 819 612 820 debug('[effect 3]'); 613 function markIfColorOrRange(e){ 821 822 function checkEventType(e){ 823 614 824 const t = e.target; 615 825 if (!t) return; 826 616 827 const settingsRoot = document.getElementById(settingsId); 617 828 if (!settingsRoot || !settingsRoot.contains(t)) return; 618 829 619 const type = (t.type || '').toLowerCase();620 const hasColor Class = t.classList && t.classList.contains('unite-color-picker');621 const isRange = type === 'range' || (t.classList && t.classList.contains('unite-range-slider'));622 const isDimensions = !! (t.closest && t.closest('.unite-dimensions'));623 624 if (hasColor Class) {830 const type = (t.type || '').toLowerCase(); 831 const hasColor = t.classList?.contains('unite-color-picker'); 832 const isRange = type === 'range' || t.classList?.contains('unite-range-slider'); 833 const isDimensions = !!t.closest?.('.unite-dimensions'); 834 835 if (hasColor) { 625 836 lastChangeTypeRef.current = 'color'; 626 pendingSuppressByDomRef.current = true;627 pingSaveButton();628 837 } else if (isRange || isDimensions) { 629 838 lastChangeTypeRef.current = isDimensions ? 'dimensions' : 'range'; 630 pendingSuppressByDomRef.current = true;631 pingSaveButton();632 } 633 }634 document.addEventListener(' input', markIfColorOrRange, true);635 document.addEventListener('change', markIfColorOrRange, true); 839 } 840 } 841 842 document.addEventListener('input', checkEventType, true); 843 document.addEventListener('change', checkEventType, true); 844 636 845 return () => { 637 document.removeEventListener('input', markIfColorOrRange, true);638 document.removeEventListener('change', markIfColorOrRange, true);846 document.removeEventListener('input', checkEventType, true); 847 document.removeEventListener('change', checkEventType, true); 639 848 }; 640 849 }, [settingsId]); … … 643 852 we.useEffect(function () { 644 853 debug('[effect 4]'); 854 645 855 if (!widgetContent) return; 646 856 jQuery(widgetRef.current).html(widgetContent); 857 647 858 }, [widgetContent]); 648 859 … … 670 881 671 882 we.useEffect(function () { 672 debug('[effect 7]'); 883 884 debug('[effect 7]'); 885 673 886 maybeInitSettings(); 674 }, [settingsVisible, settingsContent]); 887 888 }, [settingsVisible, settingsContent, previewDeviceType, props.attributes.data]); 675 889 676 890 we.useEffect(function () { 891 677 892 debug('[effect 8]'); 678 maybeInitSettings(); 679 }, [previewDeviceType, props.attributes.data]); 893 894 if (!settingsContent) return; 895 runFirstPreviewOnce(); 896 897 }, [settingsContent]); 680 898 681 899 we.useEffect(function () { 682 debug('[effect 9]'); 683 if (!settingsContent) return; 684 runFirstPreviewOnce(); 685 }, [settingsContent]); 686 687 we.useEffect(function () { 688 debug('[effect 10]'); 689 900 901 debug('[effect 9]'); 902 690 903 if (!firstPreviewReadyRef.current) { 691 904 if (widgetContent) { … … 695 908 } 696 909 } 910 911 debug("check the settings"); 912 697 913 if (suppressNextReloadRef.current) { 698 914 699 915 suppressNextReloadRef.current = false; 700 916 917 debug("effect 9 - supress next reload"); 918 701 919 updateSelectorsPreview(); 920 702 921 return; 703 922 } … … 710 929 } 711 930 })(); 931 932 trace("the settings"); 933 trace(settings); 934 712 935 const payloadStr = JSON.stringify(settings || {}); 713 936 if (payloadStr === lastPreviewPayloadRef.current) { 714 debug('[effect 10] [5]'); 937 938 debug("Update Selectors Preview"); 939 715 940 updateSelectorsPreview(); 716 941 return; … … 719 944 720 945 loadWidgetContent(settings); 946 721 947 }, [props.attributes.data]); 722 948 723 // fix changes on lost focus949 // set focus and mouse events 724 950 we.useEffect(function () { 725 debug('[effect 11]'); 951 952 debug('[effect 10]'); 726 953 727 954 function onBlur(e){ … … 733 960 } 734 961 } 735 document.addEventListener('blur', onBlur, true); 736 window.addEventListener('beforeunload', function(){ if (isSettingsReady()) flushSaveNow(); }); 737 return () => { 738 document.removeEventListener('blur', onBlur, true); 739 }; 740 }, []); 741 742 we.useEffect(function () { 743 debug('[effect 12]'); 962 963 function onBeforeUnload() { 964 if (isSettingsReady()) flushSaveNow(); 965 } 966 744 967 function onPointerUpOrTouchEnd() { 745 968 const t = (lastChangeTypeRef.current || '').toLowerCase(); … … 748 971 } 749 972 } 973 974 document.addEventListener('blur', onBlur, true); 975 window.addEventListener('beforeunload', onBeforeUnload); 750 976 window.addEventListener('pointerup', onPointerUpOrTouchEnd, { passive: true }); 751 977 window.addEventListener('touchend', onPointerUpOrTouchEnd, { passive: true }); 978 752 979 return () => { 753 980 window.removeEventListener('pointerup', onPointerUpOrTouchEnd); 754 981 window.removeEventListener('touchend', onPointerUpOrTouchEnd); 982 window.removeEventListener('beforeunload', onBeforeUnload); 983 document.removeEventListener('blur', onBlur, true); 755 984 }; 756 985 }, []); 757 758 // init settings when panel visible + settings HTML ready + real DOM exists759 function maybeInitSettings(){760 if (!settingsVisible) return;761 if (!settingsContent) return;762 763 var $settingsElement = getSettingsElement();764 if (!$settingsElement || $settingsElement.length === 0) return;765 766 var elem = $settingsElement[0];767 if (!ucSettings.isInited() || initedSettingsElementRef.current !== elem) {768 initSettings();769 }770 }771 986 772 987 function runFirstPreviewOnce(maxTries = 30, delay = 50) { … … 814 1029 815 1030 lastPreviewPayloadRef.current = mergedStr; 1031 816 1032 loadWidgetContent(merged); 817 1033 -
unlimited-elements-for-elementor/trunk/provider/core/unlimited_elements/settings/general_settings_el.xml
r3317018 r3429507 239 239 </field> 240 240 241 <field name="serpapi_heading" 242 type="statictext" 243 label="<b>Serp API</b>"> 244 </field> 245 246 <field name="serpapi_key" 247 type="text" 248 label="Serp API Key" 249 description="Used for google reviews. Get your API key here: <a href='https://serpapi.com/' target='_blank'>serpapi.com</a>." 250 default=""> 251 </field> 252 241 253 <field name="wpml_heading" 242 254 type="statictext" -
unlimited-elements-for-elementor/trunk/provider/functions_wordpress.class.php
r3403331 r3429507 3897 3897 } 3898 3898 3899 /**3900 * get all admin users3901 */3902 public static function getAdminUsers(){3903 3904 $arrAdminUsers = get_users( array( 'role' => 'Administrator' ) );3905 3906 return($arrAdminUsers);3907 }3908 3899 3909 3900 /** -
unlimited-elements-for-elementor/trunk/provider/provider_browser.class.php
r3251080 r3429507 26 26 27 27 } 28 -
unlimited-elements-for-elementor/trunk/provider/provider_helper.class.php
r3397365 r3429507 1261 1261 1262 1262 switch($field["type"]){ 1263 case UniteCreatorDialogParam::PARAM_HR: 1264 1265 $settingsManager->addHr($paramName, $params); 1266 1267 break; 1263 1268 case UniteCreatorDialogParam::PARAM_STATIC_TEXT: 1264 1269 $settingsManager->addStaticText($field["text"], $paramName, $params); -
unlimited-elements-for-elementor/trunk/provider/provider_params_processor_multisource.class.php
r3379552 r3429507 294 294 $type = UniteFunctionsUC::getVal($params, "type"); 295 295 $data = UniteCreatorAPIIntegrations::getInstance()->getDataForMultisource($type, $params); 296 296 297 297 298 return $data; 298 299 } -
unlimited-elements-for-elementor/trunk/provider/provider_settings.class.php
r3397365 r3429507 2300 2300 $params["is_multiple"] = true; 2301 2301 $params["elementor_condition"] = $arrConditionIncludeAuthor; 2302 2302 2303 2303 $this->addMultiSelect($name . "_excludeby_authors", $arrAuthors, __("Exclude By Author", "unlimited-elements-for-elementor"), "", $params); 2304 2304 … … 3630 3630 'responsive_id' => 'advanced_margin', 3631 3631 'units' => ['px','vh','%','em','rem'], 3632 'selector' => ' .ue-widget-root',3632 'selector' => '', 3633 3633 'selector_value' => 'margin-top:{{top}}!important;margin-right:{{right}}!important;margin-bottom:{{bottom}}!important;margin-left:{{left}}!important;', 3634 3634 ] … … 3653 3653 'responsive_id' => 'advanced_padding', 3654 3654 'units' => ['px','vh','%','em','rem'], 3655 'selector' => ' .ue-widget-root',3655 'selector' => '', 3656 3656 'selector_value' => 'padding-top:{{top}}!important;padding-right:{{right}}!important;padding-bottom:{{bottom}}!important;padding-left:{{left}}!important;', 3657 3657 ] … … 3698 3698 'show_slider' => false, 3699 3699 3700 'selector' => ' .ue-widget-root',3700 'selector' => '', 3701 3701 'selector_value' => 'z-index:{{value}};', 3702 3702 … … 3723 3723 'tab' => self::TAB_ADVANCED, 3724 3724 'items' => [0, 1], 3725 'selector' => ' .ue-widget-root',3725 'selector' => '', 3726 3726 ] 3727 3727 ); … … 3747 3747 [ 3748 3748 'tab' => self::TAB_ADVANCED, 3749 'selector' => ' .ue-widget-root',3749 'selector' => '', 3750 3750 'selector_value' => 'data-bg-type:{{value}};', 3751 3751 ] … … 3761 3761 'tab' => self::TAB_ADVANCED, 3762 3762 'elementor_condition' => ['advanced_background_type' => 'solid'], 3763 'selector' => ' .ue-widget-root',3763 'selector' => '', 3764 3764 'selector_value' => 'background-color:{{value}};', 3765 3765 ] … … 3775 3775 'tab' => self::TAB_ADVANCED, 3776 3776 'elementor_condition' => ['advanced_background_type' => 'solid'], 3777 'selector' => ' .ue-widget-root',3777 'selector' => '', 3778 3778 'selector_value' => 'background-image:url({{value}}); background-size:cover; background-repeat:no-repeat;', 3779 3779 ] … … 3789 3789 'tab' => self::TAB_ADVANCED, 3790 3790 'elementor_condition' => ['advanced_background_type' => 'gradient'], 3791 'selector' => ' .ue-widget-root',3791 'selector' => '', 3792 3792 'selector_value' => 'background-image:linear-gradient({{advanced_background_gradient_angle}}deg, {{value}}, {{advanced_background_gradient_color2}});', 3793 3793 ] … … 3803 3803 'tab' => self::TAB_ADVANCED, 3804 3804 'elementor_condition' => ['advanced_background_type' => 'gradient'], 3805 'selector' => ' .ue-widget-root',3805 'selector' => '', 3806 3806 'selector_value' => 'background-image:linear-gradient({{advanced_background_gradient_angle}}deg, {{advanced_background_gradient_color1}}, {{value}});', 3807 3807 ] … … 3823 3823 'units' => '', 3824 3824 'show_slider' => true, 3825 'selector' => ' .ue-widget-root',3825 'selector' => '', 3826 3826 'selector_value' => 'background-image:linear-gradient({{value}}deg, {{advanced_background_gradient_color1}}, {{advanced_background_gradient_color2}});', 3827 3827 ] … … 3852 3852 'tab' => self::TAB_ADVANCED, 3853 3853 3854 'selector' => ' .ue-widget-root',3854 'selector' => '', 3855 3855 'selector_value' => 'border-style:{{value}};', 3856 3856 … … 3876 3876 'elementor_condition' => $borderTypeCondition, 3877 3877 3878 'selector' => ' .ue-widget-root',3878 'selector' => '', 3879 3879 'selector_value' => 3880 3880 'border-top-width:{{top}};' . … … 3902 3902 'elementor_condition' => $borderTypeCondition, 3903 3903 3904 'selector' => ' .ue-widget-root',3904 'selector' => '', 3905 3905 'selector_value' => 3906 3906 'border-top-width:{{top}}{{unit}};' . … … 3928 3928 'elementor_condition' => $borderTypeCondition, 3929 3929 3930 'selector' => ' .ue-widget-root',3930 'selector' => '', 3931 3931 'selector_value' => 3932 3932 'border-top-width:{{top}}{{unit}};' . … … 3948 3948 'elementor_condition' => $borderTypeCondition, 3949 3949 3950 'selector' => ' .ue-widget-root',3950 'selector' => '', 3951 3951 'selector_value' => 'border-color:{{value}};', 3952 3952 -
unlimited-elements-for-elementor/trunk/provider/woocommerce_integrate.class.php
r3379552 r3429507 251 251 $arrProduct["woo_sale_price_to"] = null; 252 252 }else{ 253 254 $regularPriceFrom = $this->modifyPrice($regularPriceFrom, $objProduct); 255 $regularPriceTo = $this->modifyPrice($regularPriceTo, $objProduct); 256 253 257 254 $arrProduct["woo_regular_price_from"] = $regularPriceFrom; 258 255 $arrProduct["woo_regular_price_to"] = $regularPriceTo; … … 726 723 $price = UniteFunctionsUC::getVal($arrData, "price"); 727 724 725 $priceNoTax = wc_get_price_excluding_tax($objInfo); 726 $priceWithTax = wc_get_price_including_tax($objInfo); 727 728 728 $price = apply_filters("woocommerce_product_get_price", $price, $objInfo); 729 729 $salePrice = apply_filters("woocommerce_product_get_sale_price", $salePrice, $objInfo); 730 730 $regularPrice = apply_filters("woocommerce_product_get_regular_price", $regularPrice, $objInfo); 731 731 732 733 732 $salePrice = $this->modifyPrice($salePrice, $objInfo); 734 733 $regularPrice = $this->modifyPrice($regularPrice, $objInfo); 735 734 $price = $this->modifyPrice($price, $objInfo); 736 735 737 $priceNoTax = wc_get_price_excluding_tax($objInfo);738 $priceWithTax = wc_get_price_including_tax($objInfo);739 740 736 if(empty($regularPrice) && !empty($price)) 741 737 $regularPrice = $price; … … 744 740 $arrData["price"] = $price; 745 741 $arrData["sale_price"] = $salePrice; 746 747 742 748 743 $arrProduct = array(); … … 780 775 781 776 $arrProduct = $this->addPricesFromTo($arrProduct, $arrPrices, $objInfo); 782 777 783 778 $arrProduct["woo_price"] = $arrProduct["woo_price_from"]; 784 779 $arrProduct["woo_price_id"] = $arrProduct["woo_price_from_id"]; … … 816 811 //put add to cart link 817 812 $arrProduct = $this->addAddToCartData($arrProduct, $productID, $productSku); 813 818 814 819 815 -
unlimited-elements-for-elementor/trunk/readme.txt
r3421055 r3429507 940 940 941 941 == Changelog == 942 943 944 version 2.0.2 2025-12-30 = 945 946 Plugin Changes: 947 948 * Feature: add google reviews from serp api feature 949 * Fix - fixed double tax pricing in woocommerce variation 950 * Fix - fixed the entrance animation together with load more issue 951 * Fix - some blocks related bug fixes 952 * Fix - fixed default items not loading js issues 953 * Fix - fixed some small security issue 954 955 Widgets Changes: 956 957 * Feature: Unlimited Grid (Pro) - Added Title HTML Tag option for better SEO and accessibility. 958 * Feature: Content Carousel (Free) - Enhanced custom SVG icon support for navigation arrows, and added title and description text shadow options. 959 * Feature: Content Slider (Free) - Added title HTML tag option, improved links by adding link attributes, and introduced Slide Border, Overlay Hover, and Overlay Hover Transition Duration options. 960 * Feature: Video on Hover (Free) - Added a "Schema" option to enable schema markup support for better SEO and AI understand your content for increased visibility in AI powered search features. 961 * Feature: Text Field (Free) - Introduced a dynamic URL Autopopulate feature, allowing fields to automatically retrieve and fill values from custom query string parameters upon page load 962 * Feature: Off Canvas (Pro) - Added Background Color Hover and Text Color Hover options, allowing finer visual control over hover states and enabling consistent styling of the Trigger Button element. 963 * Feature: Side Menu (Free) - Added support for the Conditions widget, enabling seamless control over the Side Menu by allowing it to be opened, closed, or toggled dynamically based on defined conditions 964 * Feature: Conditions (Free) - Added three new actions UE Open Side Menu, UE Close Side Menu, and UE Toggle Side Menu allowing full programmatic control over the Side Menu widget behavior 965 * Feature: Snow Background (Free) - Added Speed Mobile option, allowing precise control over animation and interaction speed specifically for mobile devices, ensuring smoother performance, better usability on smaller screens 966 * Feature: Swipe Carousel (Pro) - Added Title Tag option, allowing users to define the HTML tag for the title element to better match the page structure, improve semantic markup, and enhance SEO and accessibility control. 967 * Feature: Grid Gallery (Pro) - Added Enable Link On Main Image option, allowing the main image within each item to function as a clickable link. 968 * Feature: Post Grid (Free) - Made the Title Spacing option fully responsive, allowing different spacing values to be applied across desktop, tablet, and mobile for more precise control of layout and typography. 969 * Feature: Expanding Content Cards (Free) - Added Sub Title option, enabling users to include an additional descriptive text line beneath the main title. 970 * Feature: Vertical Curved Timeline (Free) - Added "Content Vertical Position" option when the image option is enabled, also introduced "Alternate Image Alignment" option for improved design and customization. 971 * Feature: Icon Accordion (Free) - Added multi-source support along with pagination and filtering features, and improved the item color options for easier styling. 972 * Feature: Dynamic Post Popup (Pro) - Added Arrows Type option, introducing support for choosing between icon-based arrows and text-based arrows. 973 * Feature: Portfolio Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 974 * Feature: Overlay Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 975 * Feature: Material Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 976 * Feature: Icon Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 977 * Feature: Unlimited Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 978 * Feature: Logo Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 979 * Feature: Woo Product Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked. 980 * Feature: Post Carousel (Free) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 981 * Feature: Repeater Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 982 * Feature: Flip Box Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 983 * Feature: Loop Carousel (Pro) - Added the Slides To Change option, which determines the exact number of carousel items moved when navigation arrows are clicked 984 * Feature: Notification (Free) - The widget's accessibility was improved by replacing the close div with a semantic <button> element, adding the essential aria-label="Close Notification" for screen reader clarity, applying role="status" to the main container for conveying passive informative content. 985 * Feature: Contact Form 7 (Free) - Improved accessibility of the widget by enabling proper keyboard navigation within form elements. 986 * Feature: Fullscreen Menu (Free) - Added Expand Collapse Icon Spacing option, allowing users to precisely control the distance between the expand/collapse icon and the text. 987 * Feature: Gradient Underline Text Effect (Free) - Made the Text Align option responsive, allowing different alignment settings for desktop, tablet, and mobile. 988 * Feature: Post Accordion (Free) - Added Icon Spacing option introduced a new control that allows adjusting the distance between the icon and its adjacent elements. 989 * Feature: Team Member Carousel (Free) - Added Icon One, Icon Two, Icon Three, Icon Four and Link One, Link Two, Link Three, Link Four options to the Source options 990 * Feature: Unlimited Google Maps (Free) - Added Scroll To Active Navigation Item option, ensuring that navigation automatically scrolls to keep the active item visible. 991 * Feature: 360 Panorama Virtual Tour (Pro) - Enhanced the widget's accessibility by applying ARIA attributes (role, aria-label) and tabindex="0" to interactive controls, allowing keyboard-only users to activate them by simulating a click using the Enter or Space key. 992 * Fix: Repeater Table (Pro) - Accessibility for the widget was improved by implementing the scope="col" attribute on all header cells to explicitly link data to headings 993 * Fix: Restaurant Menu (Free) - Accessibility for the widget was improved by adding role="list" and role="listitem" for better navigation structure, converting the "See More" toggle into a keyboard-accessible button with aria-expanded states. 994 * Fix: Loop Grid (Pro) - Fixed an issue where the Empty Message Type: Template was displayed even when the Loop Widget had successfully rendered items, causing the empty state template to appear incorrectly alongside actual content 995 * Fix: Border Hero (Free) - The button field was incorrectly configured as a text field and has now been changed to a proper link field. A security vulnerability was also fixed to improve overall safety and stability. 996 * Fix: Content Grid (Free) - Removed empty links to prevent rendering unnecessary anchor elements and to avoid potential accessibility and SEO issues when link fields are left blank 997 * Fix: Comparison List (Pro) - Fixed an issue where column widths were not working correctly on mobile devices, and resolved RTL layout issues. 998 * Fix: Horizontal Timeline (Free) - Removed empty links to prevent rendering unnecessary anchor elements and to avoid potential accessibility and SEO issues when link fields are left blank. 999 * Fix: Price Range Filter (Pro) - Fixed an issue where slider thumbnail elements were displayed incorrectly on RTL (right-to-left) websites, ensuring proper alignment, order, and interaction consistency across all supported directions 1000 * Fix: Random Content (Free) - Accessibility for the Random Content widget was improved by converting the shuffle trigger into a semantic <button>, marking the shuffle icon as aria-hidden="true" to avoid redundant noise, and applying aria-live="polite" to the content container 1001 * Fix: Stacking Cards (Free) - Fixed an issue where the Alternate Page Position option was not functioning correctly on the live site and was only applied inside the Elementor editor 1002 * Fix: Protected Content (Free) - Accessibility for the Protected Content widget was improved by correcting the form structure to properly link the <label> (title) to the password <input> using the "for" and "id" attributes, adding aria-required="true" to the input 1003 * Fix: Cookie Consent (Free) - Accessibility for the Cookie Consent widget was improved by switching interactive <div> elements to semantic <button> tags, and ensuring the main container dynamically uses role="dialog" and aria-modal="true" only when the banner is visible, thereby guiding keyboard and screen reader focus correctly. 1004 * Fix: Age Verification (Free) - The Age Verification widget's accessibility was improved by adding role="dialog", aria-modal="true", and descriptive aria-label attributes to all input fields and buttons for better screen reader and keyboard navigation 1005 * Fix: Content Tabs (Free) - Fixed an issue where theme-defined padding styles for ul elements were being unintentionally overridden by the widgets CSS, ensuring better compatibility with various themes and preserving the original list spacing and layout as defined by the active theme 1006 * Fix: Image Tooltip (Free) - Added aria-describedby attribute to the main widget wrapper to improve accessibility, and included additional HTML attributessuch as alt for the imageto ensure better semantic structure, screen-reader support, and overall accessibility compliance 1007 * Fix: Event Box (Free) - Added ARIA attributes to interactive elements and link labels for improved screen reader compatibility. 1008 * Fix: Calendar (Pro) - Increased the CSS priority of the Event Title element, ensuring its styling reliably overrides conflicting theme rules and displays consistently across different layouts and environments. 1009 * Fix: Masonry & Justified Gallery (Free) - Fixed an issue where the Tile Text Panel styling options were not appearing in the widget settings panel, ensuring all relevant controls are now properly displayed and accessible for customization. 1010 * Fix: Submit Button (Free) - Fixed issue where attempting to proceed to the next step of Multi Source widget from the first page without filling mandatory fields caused unwanted spacing to be repeatedly added to the form 1011 * Fix: Vertical Curved Timeline (Free) - Fixed an issue where "Button Spacing" option was not working. 1012 * Fix: Vertical Curved Timeline (Free) - Fixed issue where, when the image element was disabled and the content inside an item had a width below 100%, items with an even item count were not aligning correctly to the side, ensuring proper layout consistency across all configurations. 1013 * Fix: Justified Image Carousel (Pro) - Fixed an issue on touch devices where opening the lightbox required two taps instead of one, ensuring the lightbox now opens correctly on a single tap for a smoother and more responsive user experience. 1014 * Fix: QR Code (Pro) - Accessibility of the QR Code widget was improved by applying role="img" to the SVG container to define it as a graphic and using aria-label to provide the visible title as the accessible name, ensuring screen readers can announce the image's content. 1015 * Fix: Animated Mouse Scroll Icon (Free) - The accessibility of the widget was improved by adding aria-label="Scroll Down" and role="button" to the link wrapper and using aria-hidden="true" on the decorative animation elements 1016 * Fix: Vertical Curved Timeline (Free) - Fixed issue when button wasn't clickable for all items except last one. 1017 * Fix: Repeater Tabs (Free) - Fixed issue when Repeater Tabs couldn't display all content with JSON / CSV Repeater Source. 1018 * Fix: Social Share Buttons (Free) - Fixed an issue where the Telegram share option was redirecting to Xing instead of Telegram. 1019 * Release: Person Schema (Pro) - Person Schema adds structured data that defines an individual for search engines. Improve SEO by clearly connecting a person to content, roles, and social profiles. 942 1020 943 1021 … … 1057 1135 * Fix: Image Zoom Magnifier (Pro) - Added aria-describedby attribute to Image Zoom Magnifier widget for improved accessibility. 1058 1136 * Fix: Icon Accordion (Free) - Fixed an issue where the accordion was not opening correctly after the latest update, ensuring proper functionality and access. 1059 * Fix: Content Slider (Free) - Added a trigger for the resize event after carousel load to prevent layout issues and ensure proper display in all cases.1060 1137 * Fix: Comparison List (Pro) - Fixed an issue with column borders not displaying correctly in RTL mode. 1061 1138 * Fix: Icon Tabs (Free) - Added role attributes and aria-selected attributes to the Icon Tabs widget to improve accessibility. … … 1071 1148 1072 1149 1073 version 1.5.150 - 2025-09-10 =1074 1075 Plugin Changes:1076 1077 * Update: updated freemius sdk to newest version1078 * Fix: fixed bug in background widgets second condition1079 * Fix: fixed some double js init with elementor popups1080 * Fix: fixed avoid duplicates in post related widgets with filters1081 * Fix: fixed svg output instead of url in some cases1082 * Fix: fixed some bug with wpml inside loop and elementor grid1083 * Fix: prepared the plugin for elementor class optimization1084 1085 1086 Widgets Changes:1087 1088 * Feature: Content Toggle (Free) - Added an Open Tab Based on URL option, allowing specific tabs to be activated automatically when accessing a page via a direct link.1089 * Feature: Age Verification (Free) - Added a Date Inputs Order option, allowing customization of the display order for date input fields to better match regional formats and preferences.1090 * Feature: Content Toggle (Free) - Added an Enable Cookie option that allows the selected Content Toggle Item to be remembered, ensuring user choice is preserved across sessions.1091 * Feature: Flip Box Carousel (Pro) - Added a Front Image Source option within the Source settings, allowing more flexibility in selecting and managing front image content.1092 * Feature: Flip Box (Free) - Added a Front Image Border Radius option, allowing customization of corner rounding for the front image to enhance design flexibility.1093 * Feature: Before After (Free) - Before/After slider is now fully keyboard accessible: users can Tab to the handle and move it with arrow keys, while aria-valuenow attribute updates dynamically for screen readers.1094 * Feature: Off Canvas Filters (Pro) - Added the possibility to manually add filters into the Off Canvas Filters popup using Items by simply specifying the CSS ID of the desired filter.1095 * Feature: Mega Menu (Pro) - Allowed the use of HTML in the Item Title options, enabling richer formatting and enhanced customization of item titles.1096 * Feature: Hotspots (Free) - Added an Activate On Page Load option, allowing a specific item to be activated automatically when the page loads for improved user experience and control.1097 * Feature: Audio Player (Pro) - Added a Controls Wrap Mode option, allowing better management and layout of control elements within the widget for improved flexibility and appearance.1098 * Feature: Breadcrumbs (Free) - Allowed the use of HTML tags in Breadcrumbs text elements, enabling richer formatting and improved customization options.1099 * Feature: Hexagon Grid (Pro) - Added a Show Second Background Image option, allowing a secondary image to appear on hover for enhanced visual interactivity and design flexibility.1100 * Change: Multi Step Form (Free) - Changed the target Elementor container from `elementor-widget-container` to `.elementor-widget` to ensure the widget functions correctly following the Elementor 3.32 version release.1101 * Change: Submit Button (Free) - Changed the target Elementor container from elementor-widget-container to .elementor-widget to ensure the widget functions correctly following the Elementor 3.32 version release.1102 * Change: Content Toggle (Free) - Renamed Cookie options into Open Tab Rules1103 * Change: Audio Player (Pro) - Changed the source type of the audio library for the widget from CDN to self-hosted, improving performance, reliability, and control over asset loading.1104 * Fix: Age Verification (Free) - Fixed an issue where the Load After Delay option was not functioning correctly, ensuring proper delayed loading behavior.1105 * Fix: Off Canvas Filters (Pro) - Fixed an issue where the Off Canvas Filter widget was not appending the Accordion Filter widget under its container, ensuring proper structure and functionality.1106 * Fix: Flip Box (Free) - Improved accessibility across the widget, enhancing usability for all users, including those relying on assistive technologies.1107 * Fix: Masonry & Justified Gallery (Pro) - Fixed an issue related to video playback in the Lightbox when the pagination option was enabled, ensuring smooth and uninterrupted video functionality.1108 * Fix: Icon Box Hover Effect (Free) - Added protection to prevent SVG icons from disappearing on hover, ensuring consistent visibility and proper styling.1109 * Fix: Background Switcher (Pro) - Fixed an issue where the Item Height option was not working in Vertical Mode on mobile devices, ensuring proper display and responsiveness.1110 1111 1112 version 1.5.149 - 2025-08-21 =1113 1114 Plugin Changes:1115 1116 * Feature - acf fields now get taxonomies array1117 * Feature - changed users list to all users1118 * Feature - added object_id to multisource item fields1119 * Fix - added alphabet filter to active filtered items1120 * Fix - fixed search by sku in woo ajax search1121 * Fix - fixed email likert form field saving and sending1122 * Fix - fixed form file field upload1123 * Fix - fixed post list stuck when insert on editor1124 * Fix - done some protection for the galleries against mallicious code1125 1126 Widgets Changes:1127 1128 * Release: Content Toggle (Free) - The Content Toggle Widget lets you switch between two content views with a smooth toggle.1129 Ideal for comparisons, light/dark modes, or showing alternative info in the same space.1130 * Release: How To (Free) - The How To Widget lets you create step-by-step instructional content with clear titles, descriptions, images, and estimated times. It's perfect for tutorials, guides, or walkthroughs.1131 * Release: Animated Mesh Gradient Background (Pro) - Add a dynamic, fluid feel to your sections with the Animated Mesh Gradient Background widget.1132 Smooth, colorful gradients flow and shift to create an eye-catching, modern backdrop.1133 * Release: Bracket Maker (Pro) - Create interactive tournament brackets with the Bracket Maker widget. Perfect for sports, games, or competitions, customize teams, rounds, and styles easily.1134 1135 * Feature: List (Free) - Improved widget behavior: if Enable Link option is disabled, it is now possible to add links inside the Item Text option.1136 * Feature: Mega Menu (Pro) - Allowed the use of HTML tags within the Text Graphic element, enabling richer formatting and greater customization of text content.1137 * Feature: Flag Icons (Free) - Extended the "Link" option to work with all layout types, not just the dropdown layout, providing greater flexibility and consistency across different designs.1138 * Feature: Portfolio Carousel (Pro) - Added a new Image Grow On Hover option, allowing images to smoothly scale up when hovered over.1139 * Feature: Justified Image Carousel (Pro) - Added new elements Subtitle and Button to expand available content options in the widget.1140 * Feature: Repeater Carousel (Pro) - Added support for WYSIWYG type of ACF field, enabling rich text content to be displayed properly within the widget.1141 * Feature: Repeater Grid (Pro) - Added support for WYSIWYG type of ACF field, enabling rich text content to be displayed properly within the widget.1142 * Feature: Vertical Curved Timeline (Free) - Added Layout Type option allowing elements to be positioned in a column or a row.1143 * Feature: Logo Marquee (Free) - Added Item Image Object Fit and Item Image Object Position options, allowing precise control over how images are scaled and positioned within items.1144 * Feature: Payment Method Icons (Free) - Added two new cards as payment method icons: Union Tank Eckstein and DKV, providing broader payment option representation1145 * Feature: Compact Gallery (Free) - Added Lightbox option, enabling users to display content in a pop-up overlay for an improved viewing experience1146 * Feature: Grid Gallery (Pro) - Added Lightbox option, enabling users to display content in a pop-up overlay for an improved viewing experience1147 * Feature: Slider Gallery (Free) - Added Lightbox option, enabling users to display content in a pop-up overlay for an improved viewing experience.1148 * Feature: Thumbnail Gallery (Free) - Added Lightbox option, enabling users to display content in a pop-up overlay for an improved viewing experience.1149 * Feature: Dropdown Button (Pro) - Added Dropdown Horizontal Align option, allowing users to control the horizontal alignment of dropdown menus.1150 * Feature: Post Tabs (Free) - Added Button Typography option allowing customization of font settings (e.g., size, weight, style) for the button element1151 * Feature: Post Grid (Free) - Added Categories Position option, allowing users to set the desired position of category labels within the widget1152 * Feature: Repeater Accordion (Pro) - Added support for WYSIWYG field type of the ACF plugin in Repeater fields, enabling proper handling of rich text content.1153 * Feature: Content Grid (Free) - Added Title Link Type option, allowing the title to open the lightbox on click for improved interactivity.1154 * Feature: Repeater Table (Pro) - Added support for WYSIWYG type of ACF field, enabling rich text content to be displayed properly within the widget.1155 * Feature: Horizontal Timeline (Pro) - Added Button Text option into Multisource options, allowing users to customize the button label for improved clarity and flexibility.1156 * Change: Testimonial Carousel (Free) - Increased the Nav Arrow Spacing option range by extending its minimum and maximum values to -2000 and 2000, providing greater flexibility for customization.1157 * Change: Justified Image Carousel (Pro) - Changed the name of the Fix Broken Layout option to Load After Images for clearer understanding.1158 * Change: Content Grid (Free) - Changed the option name "Dynamic Popup Select" to "Button Link Type" in order to make its purpose clearer and more intuitive for users.1159 * Fix: Mega Menu (Pro) - Fixed an issue where clicking on the Graphic Element would not follow the assigned link when the Clickable Link Item option was enabled, ensuring proper link functionality.1160 * Fix: Justified Image Carousel (Pro) - Removed hardcoded data-elementor-lightbox-title and title attributes from widget links, allowing Elementor Lightbox to respect global Show Title settings.1161 * Fix: Overlay Carousel (Pro) - Added protection to prevent padding from being applied by client website theme CSS, ensuring consistent layout and styling of the widget.1162 * Fix: Flag Icons (Free) - Fixed an issue with the Link option where additional link attributes were not being applied, ensuring proper functionality and attribute handling.1163 * Fix: Flag Icons (Free) - Fixed the Remote Connection behavior when the layout type is set to Carousel, ensuring proper synchronization and smooth functionality.1164 * Fix: Justified Image Carousel (Pro) - Fixed an issue where images in the Lightbox were not displayed at full size, ensuring correct scaling and optimal visual presentation.1165 * Fix: Flip Box (Free) - Fixed an issue where the Flip Box element was not flipping back to the front side after the mouse leave event, restoring expected interactive behavior.1166 * Fix: Repeater Table (Pro) - Fixed an issue where **Columns** added in the Editor could result in an empty title, ensuring that all column titles display correctly and maintain proper content structure.1167 * Fix: 360 Product Viewer (Pro) - Added a tiny delay before initialization to ensure proper functionality when used inside Elementor popups, improving stability and preventing potential loading issues1168 * Fix: Expanding Content Cards (Pro) - Fixed an issue where Remote and Sync functionalities were not functioning properly in responsive mode, ensuring smooth operation and consistent behavior across all device sizes.1169 * Fix: Justified Image Carousel (Pro) - Added Content Vertical Offset option, available when Content Position is set to Bottom of the Image and Content Placement is set to Bottom.1170 * Fix: Layers (Pro) - Fixed an issue in the WordPress Gutenberg editor where images were displaying with an abnormal height property at certain breakpoints.1171 * Fix: Scroll Sequence (Pro) - Changed widget default values to display the default example of the Scroll Sequence widget: File Name Prefix Padding: 4, Image Extension: webp, Number Of Images: 59.1172 * Fix: Timeline Bullets (Free) - Added missing alt attributes for img elements to improve accessibility and SEO1173 * Fix: Video on Hover (Free) - Replaced touchstart with pointerup to allow scrolling on touch devices while still supporting tap to play video.1174 * Fix: Thumbnail Gallery (Free) - Fixed bug where the Text Panel (Title and Description) was not displaying.1175 * Fix: Masonry & Justified Gallery (Pro) - Allowed using SVG icons for custom icons that include CSS IDs and CSS classes, enabling more flexible styling and customization.1176 * Fix: Woo AJAX Search (Free) - Fixed issue where the Price continued to display even when the Show Price in Results option was disabled.1177 * Fix: Portfolio Carousel (Pro) - Fixed issue where link element was still rendered even when the Link option was empty.1178 * Fix: Smooth Scroll (Pro) - Fixed issue where Smooth Scroll widget was preventing anchor link click from triggering scroll behavior.1179 * Fix: Content Carousel (Free) - Fixed issue where Carousel widget was not initializing properly after page load.1180 * Fix: Carousel Gallery (Pro) - Fixed issue where the Pause icon was incorrectly displayed at the same time as the Play icon, causing visual overlap and confusion in the video controls.1181 * Fix: Logo Marquee (Free) - Fixed issue where the widget did not work on Safari mobile devices, ensuring full compatibility.1182 * Fix: Masonry & Justified Gallery (Pro) - Fixed issue where HTML5 videos were not playing correctly when using the Native Pagination functionality1183 1184 1185 version 1.5.148 - 2025-07-21 =1186 1187 * Fix - elementor rtl issues fix1188 1189 1190 version 1.5.147 - 2025-07-17 =1191 1192 Plugin Changes:1193 1194 * Fix - added entity decode for subjects in the forms emails1195 * Fix - fixed bug with remove orderby in post selection1196 * Fix - some protection of elementor widgets register in case of cache1197 * Fix - ajax search with titles only issues1198 * Fix - fixed background widgets js code issues1199 1200 Widgets Changes:1201 1202 * Feature: Animated Floating Elements Background (Pro) - Added Starting Point option, allowing users to define the initial position or item displayed when the widget loads1203 * Feature: Job Listing (Pro) - Added Remote Connect option, also added Fallback Image option to display a default image when the item image is missing.1204 * Feature: Post Carousel (Free) - Added Show Full Post Content option, allowing users to display the complete content of a post instead of just an excerpt.1205 * Feature: Job Listing (Pro) - Added new styling options for Job Info Items, including Border Radius, Padding, Background Color, Background Color on Hover, Border, and Border on Hover for enhanced visual customization.1206 * Feature: Content Box Overlay (Free) - Enabled Image size/resolution selection. Introduced Transition Duration, Title HTML Tag, Icon, and Show Title condition and debug option: "Preview Hidden Content" to assist during styling. Also added Icon, and badge option.1207 * Feature: Animated Border Icon Box (Free) - Added Title Condition and Title HTML Tag option. Enabled SVG Icon Upload and introduced "Enable Full Box Link" option. Also introduced a new Layout tab and reorganized related options for clarity. Added Badge Option and moved Title Border settings to the new Separator tab, along with new Border Width and Separator Gap controls.1208 * Change: Post Grid (Free) - Added 9 new options in the "Image Object Position" setting to allow precise image alignment: top left, top center, top right, center left, center center, center right, bottom left, bottom center, and bottom right.1209 * Change: Content Box Overlay (Free) - Added new styling options, Height, Image Fit, Image Position, Image CSS Filter, Title Spacing, and Separator line styles. Improved overall widget functionality and optimized CSS loading based on conditions, and removed unused CSS for better optimization.1210 * Change: Animated Border Icon Box (Free) - Added a dedicated Icon tab in styles, including controls for Icon Size, Color, Shadow, and Gap. Also added a separate Icon Gap setting. Removed old typography option, and added styling options for the Title. Restructured HTML and CSS, removing unused elements for improved performance.1211 * Change: Tabs Filter (Pro) - Added CSS rule to remove text decoration from the tab item link element, enhancing the overall visual clarity.1212 * Fix: Repeater Table (Pro) - Fixed issue where CSV and JSON sources for the table were not working with the widget, ensuring proper data loading and display.1213 * Fix: Alphabet Filter (Pro) - Fixed issue where enabling the Post Count option triggered an alert displaying an error, ensuring smooth functionality.1214 * Fix: Morph Background Slider (Pro) - Fixed Issue when Transition Direction option were not working as expected.1215 * Fix: Liquid Full Screen Menu (Pro) - Fixed issue where the Menu was causing horizontal scrolling when resizing the window, ensuring proper responsive behavior.1216 * Fix: Testimonial Carousel (Free) - Added aditional check of Masonry Layout Items position and corection in case of misalignment.1217 * Fix: Post Grid (Free) - Fixed an issue where, when the List View option was enabled and the layout was switched to list view, post items were not full width as expected. Instead, they were displaying at inconsistent. This update ensures that all post items properly stretch to full width in list view mode for a consistent and cleaner layout.1218 * Fix: Underline Link Hover Effect (Pro) - Fixed issue where the transition duration was not applied to the text element, ensuring smooth and consistent animations1219 * Other: Select Dropdown Filter (Free) - Added additional instruction for the Select First option, providing clearer guidance on its usage and behavior.1220 * Other: Tabs Filter (Pro) - Added additional instruction for the Select First option, providing clearer guidance on its usage and behavior.1221 * Release: Flag Icons (Free) - New Widget: Flag Icons. Easily add country flag icons to your Elementor designs perfect for highlighting international destinations, events, or global content!1222 1223 1150 1224 1151 -
unlimited-elements-for-elementor/trunk/release_log.txt
r3403331 r3429507 1 2 3 4 version 2.0.2: 5 6 * Feature: add google reviews from serp api feature 7 * Fix - fixed double tax pricing in woocommerce variation 8 * Fix - fixed the entrance animation together with load more issue 9 * Fix - some blocks related bug fixes 10 * Fix - fixed default items not loading js issues 11 * Fix - fixed some small security issue 1 12 2 13 -
unlimited-elements-for-elementor/trunk/unlimited_elements.php
r3403331 r3429507 5 5 * Description: Elementor all-in-one addons pack with the best widgets for Elementor, offering 100+ free widgets, templates, and tools to create stunning websites! 6 6 * Author: Unlimited Elements 7 * Version: 2.0. 17 * Version: 2.0.2 8 8 * Author URI: http://unlimited-elements.com 9 9 * Text Domain: unlimited-elements-for-elementor … … 11 11 * Requires PHP: 7.4 12 12 * 13 * Tested up to: 6. 8.314 * Elementor tested up to: 3.3 3.215 * Elementor Pro tested up to: 3.3 3.113 * Tested up to: 6.9 14 * Elementor tested up to: 3.34.0 15 * Elementor Pro tested up to: 3.34.0 16 16 * 17 17 * License: GPLv2 or later -
unlimited-elements-for-elementor/trunk/views/addondefaults.php
r3251080 r3429507 95 95 96 96 $arrOptions = $this->getOptions($addon); 97 97 98 98 //init addon config 99 99 $addonConfig = new UniteCreatorAddonConfig(); 100 100 $addonConfig->setStartAddon($addon); 101 101 102 102 $this->isDataExists = $addon->isDefaultDataExists(); 103 103
Note: See TracChangeset
for help on using the changeset viewer.