<# if ( 'undefined' !== typeof FusionApp ) { #>
<# } else { #>
<# } #>
/*global fusionredux_change, wp, fusionredux, libFilter */ /** * Media Uploader * Dependencies : jquery, wp media uploader * Feature added by : Smartik - http://smartik.ws/ * Date : 05.28.2013 */ (function($){ "use strict"; fusionredux.field_objects = fusionredux.field_objects || {}; fusionredux.field_objects.media = fusionredux.field_objects.media || {}; var isFiltered; $( document ).ready( function() { } ); fusionredux.field_objects.media.init = function( selector ) { if ( !selector ) { selector = $( document ).find( ".fusionredux-group-tab:visible" ).find( '.fusionredux-container-media:visible' ); } $( selector ).each( function() { var el = $( this ); var parent = el; if ( !el.hasClass( 'fusionredux-field-container' ) ) { parent = el.parents( '.fusionredux-field-container:first' ); } if ( parent.is( ":hidden" ) ) { // Skip hidden fields return; } if ( parent.hasClass( 'fusionredux-field-init' ) ) { parent.removeClass( 'fusionredux-field-init' ); } else { return; } isFiltered = false; // Remove the image button el.find( '.remove-image, .remove-file' ).unbind( 'click' ).on( 'click', function() { fusionredux.field_objects.media.removeFile( $( this ).parents( 'fieldset.fusionredux-field:first' ) ); } ); // Upload media button el.find( '.media_upload_button' ).unbind().on( 'click', function( event ) { fusionredux.field_objects.media.addFile( event, $( this ).parents( 'fieldset.fusionredux-field:first' ) ); } ); } ); }; // Add a file via the wp.media function fusionredux.field_objects.media.addFile = function( event, selector ) { event.preventDefault(); var frame; var jQueryel = $( this ); var libFilter; // If the media frame already exists, reopen it. if ( frame ) { frame.open(); return; } // Get library filter data var filter = $( selector ).find('.library-filter').data('lib-filter'); // Must exist to do decoding if (filter !== undefined) { if (filter !== ''){ libFilter = []; isFiltered = true; filter = decodeURIComponent(filter); filter = JSON.parse(filter); $.each(filter, function(index, value) { libFilter.push(value); }); } } // Create the media frame. frame = wp.media( { multiple: false, library: { type: libFilter //Only allow images }, // Set the title of the modal. title: jQueryel.data( 'choose' ), // Customize the submit button. button: { // Set the text of the button. text: jQueryel.data( 'update' ) // Tell the button not to close the modal, since we're // going to refresh the page when the image is selected. } } ); // When an image is selected, run a callback. frame.on( 'select', function() { // Grab the selected attachment. var attachment = frame.state().get( 'selection' ).first(); frame.close(); var data = $( selector ).find('.data').data(); if ( typeof fusionredux.field_objects.media === 'undefined' || typeof fusionredux.field_objects.media === undefined ) { fusionredux.field_objects.media = {}; } if ( data === undefined || data.mode === 'undefined' ) { data = {}; data.mode = "image"; } if (isFiltered === true) { data.mode = 0; } if (data.mode === 0) { } else { if ( data.mode !== false) { if (attachment.attributes.type !== data.mode) { if (attachment.attributes.subtype !== data.mode ) { return; } } } } selector.find( '.upload' ).val( attachment.attributes.url ); selector.find( '.upload-id' ).val( attachment.attributes.id ); selector.find( '.upload-height' ).val( attachment.attributes.height ); selector.find( '.upload-width' ).val( attachment.attributes.width ); fusionredux_change( $( selector ).find( '.upload-id' ) ); var thumbSrc = attachment.attributes.url; if ( typeof attachment.attributes.sizes !== 'undefined' && typeof attachment.attributes.sizes.thumbnail !== 'undefined' ) { thumbSrc = attachment.attributes.sizes.thumbnail.url; } else if ( typeof attachment.attributes.sizes !== 'undefined' ) { var height = attachment.attributes.height; for ( var key in attachment.attributes.sizes ) { var object = attachment.attributes.sizes[key]; if ( object.height < height ) { height = object.height; thumbSrc = object.url; } } } else { thumbSrc = attachment.attributes.icon; } selector.find( '.upload-thumbnail' ).val( thumbSrc ); if ( !selector.find( '.upload' ).hasClass( 'noPreview' ) ) { selector.find( '.screenshot' ).empty().hide().append( '' ).slideDown( 'fast' ); } //selector.find('.media_upload_button').unbind(); selector.find( '.remove-image' ).removeClass( 'hide' );//show "Remove" button selector.find( '.fusionredux-background-properties' ).slideDown(); } ); // Finally, open the modal. frame.open(); }; // Function to remove the image on click. Still requires a save fusionredux.field_objects.media.removeFile = function( selector ) { // This shouldn't have been run... if ( !selector.find( '.remove-image' ).addClass( 'hide' ) ) { return; } selector.find( '.remove-image' ).addClass( 'hide' );//hide "Remove" button selector.find( '.upload' ).val( '' ); selector.find( '.upload-id' ).val( '' ); selector.find( '.upload-height' ).val( '' ); selector.find( '.upload-width' ).val( '' ); selector.find( '.upload-thumbnail' ).val( '' ); fusionredux_change( $( selector ).find( '.upload-id' ) ); selector.find( '.fusionredux-background-properties' ).hide(); var screenshot = selector.find( '.screenshot' ); // Hide the screenshot screenshot.slideUp(); selector.find( '.remove-file' ).unbind(); // We don't display the upload button if .upload-notice is present // This means the user doesn't have the WordPress 3.5 Media Library Support if ( selector.find( '.section-upload .upload-notice' ).length > 0 ) { selector.find( '.media_upload_button' ).remove(); } }; })( jQuery ); /*! elementor-pro - v3.18.0 - 06-12-2023 */ "use strict"; (self["webpackChunkelementor_pro"] = self["webpackChunkelementor_pro"] || []).push([["carousel"],{ /***/ "../modules/carousel/assets/js/frontend/handlers/base.js": /*!***************************************************************!*\ !*** ../modules/carousel/assets/js/frontend/handlers/base.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = void 0; class CarouselBase extends elementorModules.frontend.handlers.SwiperBase { getDefaultSettings() { return { selectors: { swiperContainer: '.elementor-main-swiper', swiperSlide: '.swiper-slide' }, slidesPerView: { widescreen: 3, desktop: 3, laptop: 3, tablet_extra: 3, tablet: 2, mobile_extra: 2, mobile: 1 } }; } getDefaultElements() { const selectors = this.getSettings('selectors'), elements = { $swiperContainer: this.$element.find(selectors.swiperContainer) }; elements.$slides = elements.$swiperContainer.find(selectors.swiperSlide); return elements; } getEffect() { return this.getElementSettings('effect'); } getDeviceSlidesPerView(device) { const slidesPerViewKey = 'slides_per_view' + ('desktop' === device ? '' : '_' + device); return Math.min(this.getSlidesCount(), +this.getElementSettings(slidesPerViewKey) || this.getSettings('slidesPerView')[device]); } getSlidesPerView(device) { if ('slide' === this.getEffect()) { return this.getDeviceSlidesPerView(device); } return 1; } getDeviceSlidesToScroll(device) { const slidesToScrollKey = 'slides_to_scroll' + ('desktop' === device ? '' : '_' + device); return Math.min(this.getSlidesCount(), +this.getElementSettings(slidesToScrollKey) || 1); } getSlidesToScroll(device) { if ('slide' === this.getEffect()) { return this.getDeviceSlidesToScroll(device); } return 1; } getSpaceBetween(device) { let propertyName = 'space_between'; if (device && 'desktop' !== device) { propertyName += '_' + device; } return this.getElementSettings(propertyName).size || 0; } getSwiperOptions() { const elementSettings = this.getElementSettings(); const swiperOptions = { grabCursor: true, initialSlide: this.getInitialSlide(), slidesPerView: this.getSlidesPerView('desktop'), slidesPerGroup: this.getSlidesToScroll('desktop'), spaceBetween: this.getSpaceBetween(), loop: 'yes' === elementSettings.loop, speed: elementSettings.speed, effect: this.getEffect(), preventClicksPropagation: false, slideToClickedSlide: true, handleElementorBreakpoints: true }; if ('yes' === elementSettings.lazyload) { swiperOptions.lazy = { loadPrevNext: true, loadPrevNextAmount: 1 }; } if (elementSettings.show_arrows) { swiperOptions.navigation = { prevEl: '.elementor-swiper-button-prev', nextEl: '.elementor-swiper-button-next' }; } if (elementSettings.pagination) { swiperOptions.pagination = { el: '.swiper-pagination', type: elementSettings.pagination, clickable: true }; } if ('cube' !== this.getEffect()) { const breakpointsSettings = {}, breakpoints = elementorFrontend.config.responsive.activeBreakpoints; Object.keys(breakpoints).forEach(breakpointName => { breakpointsSettings[breakpoints[breakpointName].value] = { slidesPerView: this.getSlidesPerView(breakpointName), slidesPerGroup: this.getSlidesToScroll(breakpointName), spaceBetween: this.getSpaceBetween(breakpointName) }; }); swiperOptions.breakpoints = breakpointsSettings; } if (!this.isEdit && elementSettings.autoplay) { swiperOptions.autoplay = { delay: elementSettings.autoplay_speed, disableOnInteraction: !!elementSettings.pause_on_interaction }; } return swiperOptions; } getDeviceBreakpointValue(device) { if (!this.breakpointsDictionary) { const breakpoints = elementorFrontend.config.responsive.activeBreakpoints; this.breakpointsDictionary = {}; Object.keys(breakpoints).forEach(breakpointName => { this.breakpointsDictionary[breakpointName] = breakpoints[breakpointName].value; }); } return this.breakpointsDictionary[device]; } updateSpaceBetween(propertyName) { const deviceMatch = propertyName.match('space_between_(.*)'), device = deviceMatch ? deviceMatch[1] : 'desktop', newSpaceBetween = this.getSpaceBetween(device); if ('desktop' !== device) { this.swiper.params.breakpoints[this.getDeviceBreakpointValue(device)].spaceBetween = newSpaceBetween; } else { this.swiper.params.spaceBetween = newSpaceBetween; } this.swiper.params.spaceBetween = newSpaceBetween; this.swiper.update(); } async onInit() { elementorModules.frontend.handlers.Base.prototype.onInit.apply(this, arguments); if (1 >= this.getSlidesCount()) { return; } const Swiper = elementorFrontend.utils.swiper; this.swiper = await new Swiper(this.elements.$swiperContainer, this.getSwiperOptions()); const elementSettings = this.getElementSettings(); if ('yes' === elementSettings.pause_on_hover) { this.togglePauseOnHover(true); } // Expose the swiper instance in the frontend this.elements.$swiperContainer.data('swiper', this.swiper); } getChangeableProperties() { return { autoplay: 'autoplay', pause_on_hover: 'pauseOnHover', pause_on_interaction: 'disableOnInteraction', autoplay_speed: 'delay', speed: 'speed', width: 'width' }; } updateSwiperOption(propertyName) { if (0 === propertyName.indexOf('width')) { this.swiper.update(); return; } const elementSettings = this.getElementSettings(), newSettingValue = elementSettings[propertyName], changeableProperties = this.getChangeableProperties(); let propertyToUpdate = changeableProperties[propertyName], valueToUpdate = newSettingValue; // Handle special cases where the value to update is not the value that the Swiper library accepts switch (propertyName) { case 'autoplay': if (newSettingValue) { valueToUpdate = { delay: elementSettings.autoplay_speed, disableOnInteraction: 'yes' === elementSettings.pause_on_interaction }; } else { valueToUpdate = false; } break; case 'autoplay_speed': propertyToUpdate = 'autoplay'; valueToUpdate = { delay: newSettingValue, disableOnInteraction: 'yes' === elementSettings.pause_on_interaction }; break; case 'pause_on_hover': this.togglePauseOnHover('yes' === newSettingValue); break; case 'pause_on_interaction': valueToUpdate = 'yes' === newSettingValue; break; } // 'pause_on_hover' is implemented by the handler with event listeners, not the Swiper library if ('pause_on_hover' !== propertyName) { this.swiper.params[propertyToUpdate] = valueToUpdate; } this.swiper.update(); } onElementChange(propertyName) { if (1 >= this.getSlidesCount()) { return; } if (0 === propertyName.indexOf('width')) { this.swiper.update(); // If there is another thumbs slider, like in the Media Carousel widget. if (this.thumbsSwiper) { this.thumbsSwiper.update(); } return; } // This is for handling the responsive control 'space_between'. // Responsive controls require a separate way of handling, and some currently don't work // (Swiper bug, currently exists in v5.3.6) TODO: update Swiper when bug is fixed and handle responsive controls if (0 === propertyName.indexOf('space_between')) { this.updateSpaceBetween(propertyName); return; } const changeableProperties = this.getChangeableProperties(); if (Object.prototype.hasOwnProperty.call(changeableProperties, propertyName)) { this.updateSwiperOption(propertyName); } } onEditSettingsChange(propertyName) { if (1 >= this.getSlidesCount()) { return; } if ('activeItemIndex' === propertyName) { this.swiper.slideToLoop(this.getEditSettings('activeItemIndex') - 1); } } } exports["default"] = CarouselBase; /***/ }), /***/ "../modules/carousel/assets/js/frontend/handlers/testimonial-carousel.js": /*!*******************************************************************************!*\ !*** ../modules/carousel/assets/js/frontend/handlers/testimonial-carousel.js ***! \*******************************************************************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js"); Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = void 0; var _base = _interopRequireDefault(__webpack_require__(/*! ./base */ "../modules/carousel/assets/js/frontend/handlers/base.js")); class TestimonialCarousel extends _base.default { getDefaultSettings() { const defaultSettings = super.getDefaultSettings(); defaultSettings.slidesPerView = { desktop: 1 }; Object.keys(elementorFrontend.config.responsive.activeBreakpoints).forEach(breakpointName => { defaultSettings.slidesPerView[breakpointName] = 1; }); if (defaultSettings.loop) { defaultSettings.loopedSlides = this.getSlidesCount(); } return defaultSettings; } getEffect() { return 'slide'; } } exports["default"] = TestimonialCarousel; /***/ }) }]); //# sourceMappingURL=carousel.998a291abf70435fd698.bundle.js.map The inspiration -

The inspiration

Why I created this platform to protect homeowners like you

We had a vision. A beautiful home transformation that would change how we lived and loved our space. We spent months planning — carefully choosing materials, finalizing a budget, and agreeing a clear timeline with a builder who seemed trustworthy.

The plan was simple: move out for four months while the work was completed. We found a nearby rental and relocated with our family, excited for what was to come. We thought we had done everything right.

The first two months were fine, timelines and milestones met. Thereafter the cracks appeared.

At first, it was minor: missed days, vague updates, delays in sourcing materials. Then came a string of excuses — poor weather, personal issues, staff shortages. The pace of work slowed. Our calls and emails became more frequent, more desperate. But progress didn’t follow.

By the time our rental lease was nearing its end — six months later — our home was far from complete. Walls, ceilings were unpainted, electrics and plumbing were unfinished, the tiling wasn’t done, and the flat roof was still exposed, with no decking or balustrades installed. We were running out of money, patience, and options.

We had no choice but to move back in.

We returned not to a dream home, but a building site. Dusty floors. Exposed wires. Rooms we couldn’t use. A kitchen we couldn’t cook in. And young children trying to make sense of the chaos around them.

It was devastating.

Worse still, I had to sack the builders. Their contract meant nothing when the work they promised — and were paid for — simply wasn’t done.

From there, we had to start again.

We began a rigorous recruitment process to find a builder who could take over and actually deliver. It wasn’t easy. After being burnt, trust doesn’t come easy. Eventually, we hired a new team to complete the brickwork, correct the sagging flat roof, and apply the decking and balustrades. But that was only part of the job.

We also had to bring in new electricians, plumbers, tilers, decorators — all while living in the house. Managing schedules. Managing mess. Managing our own stress.

In the end, the project cost us over £40,000 more than planned and dragged on for another year. What should have been a moment of joy became a long, exhausting lesson in what can go wrong when you choose the wrong builder

And that’s why I created LuxuryBuilder.co.uk.

Not just as a blog, but as a mission — to protect homeowners from the mistakes I made. To help others avoid rogue builders, poor planning, and costly surprises. To provide clear advice, trusted recommendations, and a place to get educated before committing thousands of pounds to a project.

Here, you’ll find insights based on real experiences — not marketing fluff. Whether you’re planning a loft conversion, a luxury extension, or a full-home renovation, this site is here to give you clarity, confidence, and control.

Because building your dream home shouldn’t feel like a nightmare.

You deserve peace of mind.
You deserve transparency.
You deserve builders who show up — and finish the job.

Let LuxuryBuilder.co.uk be your guide to getting it right the first time.

Thinking about transforming your home?

Get your free personalised AI renovation plan in 60 seconds.

Get My Free Plan →

Free — no obligation — plan ready in 60 seconds

Scroll to Top