__( 'Save as a template', 'elementor' );
__( 'New', 'elementor' );
__( 'Create component', 'elementor' );
__( 'To create new components, update Elementor Pro to the latest version.', 'elementor' );
__( 'Update Now', 'elementor' );
__( 'Add %s', 'elementor' );
__( 'Edit %s', 'elementor' );
__( 'Duplicate %s', 'elementor' );
__( 'Delete %s', 'elementor' );# Members - EDD Integration
The Members - EDD Integration add-on more nicely integrates the [Easy Digital Downloads](https://easydigitaldownloads.com/) plugin capabilities into the Members plugin's role manager.
The [Members plugin](https://themehybrid.com/plugins/members) and the Easy Digital Downloads plugin are required to be installed and activated for this plugin to work.
## Shop Role Group
The plugin creates a new "Shop" role group for Easy Digital Downloads, which is located under the Users > Roles screen in the admin. This role group lists the following roles:
- Shop Accountant
- Shop Manager
- Shop Vendor
- Shop Worker
- Any other roles that have EDD-related capabilities.
This allows you to see any roles that have EDD permissions at a glance.
## Downloads Capability Group
When editing or creating a new role in Members, this add-on creates a new "Downloads" tab (cap group). This tab combines all of the Easy Digital Downloads capabilities in one place. That way, you can see all available caps without jumping between different tabs.
## Copyright and License
This project is licensed under the [GNU GPL](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html), version 2 or later.
2019 © [Justin Tadlock](http://justintadlock.com).
/******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./node_modules/@monaco-editor/loader/lib/es/_virtual/_rollupPluginBabelHelpers.js":
/*!*****************************************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/_virtual/_rollupPluginBabelHelpers.js ***!
\*****************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ arrayLikeToArray: function() { return /* binding */ _arrayLikeToArray; },
/* harmony export */ arrayWithHoles: function() { return /* binding */ _arrayWithHoles; },
/* harmony export */ defineProperty: function() { return /* binding */ _defineProperty; },
/* harmony export */ iterableToArrayLimit: function() { return /* binding */ _iterableToArrayLimit; },
/* harmony export */ nonIterableRest: function() { return /* binding */ _nonIterableRest; },
/* harmony export */ objectSpread2: function() { return /* binding */ _objectSpread2; },
/* harmony export */ objectWithoutProperties: function() { return /* binding */ _objectWithoutProperties; },
/* harmony export */ objectWithoutPropertiesLoose: function() { return /* binding */ _objectWithoutPropertiesLoose; },
/* harmony export */ slicedToArray: function() { return /* binding */ _slicedToArray; },
/* harmony export */ toPrimitive: function() { return /* binding */ _toPrimitive; },
/* harmony export */ toPropertyKey: function() { return /* binding */ _toPropertyKey; },
/* harmony export */ unsupportedIterableToArray: function() { return /* binding */ _unsupportedIterableToArray; }
/* harmony export */ });
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: true,
configurable: true,
writable: true
}) : e[r] = t, e;
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = true,
o = false;
try {
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = true, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _objectWithoutProperties(e, t) {
if (null == e) return {};
var o,
r,
i = _objectWithoutPropertiesLoose(e, t);
if (Object.getOwnPropertySymbols) {
var n = Object.getOwnPropertySymbols(e);
for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
}
return i;
}
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
if (-1 !== e.indexOf(n)) continue;
t[n] = r[n];
}
return t;
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/config/index.js":
/*!*******************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/config/index.js ***!
\*******************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ config; }
/* harmony export */ });
var config = {
paths: {
vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs'
}
};
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/index.js":
/*!************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/index.js ***!
\************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* reexport safe */ _loader_index_js__WEBPACK_IMPORTED_MODULE_0__["default"]; }
/* harmony export */ });
/* harmony import */ var _loader_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loader/index.js */ "./node_modules/@monaco-editor/loader/lib/es/loader/index.js");
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/loader/index.js":
/*!*******************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/loader/index.js ***!
\*******************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ loader; }
/* harmony export */ });
/* harmony import */ var _virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../_virtual/_rollupPluginBabelHelpers.js */ "./node_modules/@monaco-editor/loader/lib/es/_virtual/_rollupPluginBabelHelpers.js");
/* harmony import */ var state_local__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! state-local */ "./node_modules/state-local/lib/es/state-local.js");
/* harmony import */ var _config_index_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../config/index.js */ "./node_modules/@monaco-editor/loader/lib/es/config/index.js");
/* harmony import */ var _validators_index_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../validators/index.js */ "./node_modules/@monaco-editor/loader/lib/es/validators/index.js");
/* harmony import */ var _utils_compose_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/compose.js */ "./node_modules/@monaco-editor/loader/lib/es/utils/compose.js");
/* harmony import */ var _utils_deepMerge_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utils/deepMerge.js */ "./node_modules/@monaco-editor/loader/lib/es/utils/deepMerge.js");
/* harmony import */ var _utils_makeCancelable_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/makeCancelable.js */ "./node_modules/@monaco-editor/loader/lib/es/utils/makeCancelable.js");
var _excluded = ["monaco"];
/** the local state of the module */
var _state$create = state_local__WEBPACK_IMPORTED_MODULE_1__["default"].create({
config: _config_index_js__WEBPACK_IMPORTED_MODULE_2__["default"],
isInitialized: false,
resolve: null,
reject: null,
monaco: null
}),
_state$create2 = (0,_virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__.slicedToArray)(_state$create, 2),
getState = _state$create2[0],
setState = _state$create2[1];
/**
* set the loader configuration
* @param {Object} config - the configuration object
*/
function config(globalConfig) {
var _validators$config = _validators_index_js__WEBPACK_IMPORTED_MODULE_3__["default"].config(globalConfig),
monaco = _validators$config.monaco,
config = (0,_virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__.objectWithoutProperties)(_validators$config, _excluded);
setState(function (state) {
return {
config: (0,_utils_deepMerge_js__WEBPACK_IMPORTED_MODULE_5__["default"])(state.config, config),
monaco: monaco
};
});
}
/**
* handles the initialization of the monaco-editor
* @return {Promise} - returns an instance of monaco (with a cancelable promise)
*/
function init() {
var state = getState(function (_ref) {
var monaco = _ref.monaco,
isInitialized = _ref.isInitialized,
resolve = _ref.resolve;
return {
monaco: monaco,
isInitialized: isInitialized,
resolve: resolve
};
});
if (!state.isInitialized) {
setState({
isInitialized: true
});
if (state.monaco) {
state.resolve(state.monaco);
return (0,_utils_makeCancelable_js__WEBPACK_IMPORTED_MODULE_6__["default"])(wrapperPromise);
}
if (window.monaco && window.monaco.editor) {
storeMonacoInstance(window.monaco);
state.resolve(window.monaco);
return (0,_utils_makeCancelable_js__WEBPACK_IMPORTED_MODULE_6__["default"])(wrapperPromise);
}
(0,_utils_compose_js__WEBPACK_IMPORTED_MODULE_4__["default"])(injectScripts, getMonacoLoaderScript)(configureLoader);
}
return (0,_utils_makeCancelable_js__WEBPACK_IMPORTED_MODULE_6__["default"])(wrapperPromise);
}
/**
* injects provided scripts into the document.body
* @param {Object} script - an HTML script element
* @return {Object} - the injected HTML script element
*/
function injectScripts(script) {
return document.body.appendChild(script);
}
/**
* creates an HTML script element with/without provided src
* @param {string} [src] - the source path of the script
* @return {Object} - the created HTML script element
*/
function createScript(src) {
var script = document.createElement('script');
return src && (script.src = src), script;
}
/**
* creates an HTML script element with the monaco loader src
* @return {Object} - the created HTML script element
*/
function getMonacoLoaderScript(configureLoader) {
var state = getState(function (_ref2) {
var config = _ref2.config,
reject = _ref2.reject;
return {
config: config,
reject: reject
};
});
var loaderScript = createScript("".concat(state.config.paths.vs, "/loader.js"));
loaderScript.onload = function () {
return configureLoader();
};
loaderScript.onerror = state.reject;
return loaderScript;
}
/**
* configures the monaco loader
*/
function configureLoader() {
var state = getState(function (_ref3) {
var config = _ref3.config,
resolve = _ref3.resolve,
reject = _ref3.reject;
return {
config: config,
resolve: resolve,
reject: reject
};
});
var require = window.require;
require.config(state.config);
require(['vs/editor/editor.main'], function (loaded) {
var monaco = loaded.m /* for 0.53 & 0.54 */ || loaded /* for other versions */;
storeMonacoInstance(monaco);
state.resolve(monaco);
}, function (error) {
state.reject(error);
});
}
/**
* store monaco instance in local state
*/
function storeMonacoInstance(monaco) {
if (!getState().monaco) {
setState({
monaco: monaco
});
}
}
/**
* internal helper function
* extracts stored monaco instance
* @return {Object|null} - the monaco instance
*/
function __getMonacoInstance() {
return getState(function (_ref4) {
var monaco = _ref4.monaco;
return monaco;
});
}
var wrapperPromise = new Promise(function (resolve, reject) {
return setState({
resolve: resolve,
reject: reject
});
});
var loader = {
config: config,
init: init,
__getMonacoInstance: __getMonacoInstance
};
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/utils/compose.js":
/*!********************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/utils/compose.js ***!
\********************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ compose; }
/* harmony export */ });
var compose = function compose() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return function (x) {
return fns.reduceRight(function (y, f) {
return f(y);
}, x);
};
};
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/utils/curry.js":
/*!******************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/utils/curry.js ***!
\******************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ curry; }
/* harmony export */ });
function curry(fn) {
return function curried() {
var _this = this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return args.length >= fn.length ? fn.apply(this, args) : function () {
for (var _len2 = arguments.length, nextArgs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
nextArgs[_key2] = arguments[_key2];
}
return curried.apply(_this, [].concat(args, nextArgs));
};
};
}
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/utils/deepMerge.js":
/*!**********************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/utils/deepMerge.js ***!
\**********************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ merge; }
/* harmony export */ });
/* harmony import */ var _virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../_virtual/_rollupPluginBabelHelpers.js */ "./node_modules/@monaco-editor/loader/lib/es/_virtual/_rollupPluginBabelHelpers.js");
function merge(target, source) {
Object.keys(source).forEach(function (key) {
if (source[key] instanceof Object) {
if (target[key]) {
Object.assign(source[key], merge(target[key], source[key]));
}
}
});
return (0,_virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__.objectSpread2)((0,_virtual_rollupPluginBabelHelpers_js__WEBPACK_IMPORTED_MODULE_0__.objectSpread2)({}, target), source);
}
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/utils/isObject.js":
/*!*********************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/utils/isObject.js ***!
\*********************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ isObject; }
/* harmony export */ });
function isObject(value) {
return {}.toString.call(value).includes('Object');
}
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/utils/makeCancelable.js":
/*!***************************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/utils/makeCancelable.js ***!
\***************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ CANCELATION_MESSAGE: function() { return /* binding */ CANCELATION_MESSAGE; },
/* harmony export */ "default": function() { return /* binding */ makeCancelable; }
/* harmony export */ });
// The source (has been changed) is https://github.com/facebook/react/issues/5465#issuecomment-157888325
var CANCELATION_MESSAGE = {
type: 'cancelation',
msg: 'operation is manually canceled'
};
function makeCancelable(promise) {
var hasCanceled_ = false;
var wrappedPromise = new Promise(function (resolve, reject) {
promise.then(function (val) {
return hasCanceled_ ? reject(CANCELATION_MESSAGE) : resolve(val);
});
promise["catch"](reject);
});
return wrappedPromise.cancel = function () {
return hasCanceled_ = true;
}, wrappedPromise;
}
/***/ }),
/***/ "./node_modules/@monaco-editor/loader/lib/es/validators/index.js":
/*!***********************************************************************!*\
!*** ./node_modules/@monaco-editor/loader/lib/es/validators/index.js ***!
\***********************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ validators; },
/* harmony export */ errorHandler: function() { return /* binding */ errorHandler; },
/* harmony export */ errorMessages: function() { return /* binding */ errorMessages; }
/* harmony export */ });
/* harmony import */ var _utils_curry_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/curry.js */ "./node_modules/@monaco-editor/loader/lib/es/utils/curry.js");
/* harmony import */ var _utils_isObject_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/isObject.js */ "./node_modules/@monaco-editor/loader/lib/es/utils/isObject.js");
/**
* validates the configuration object and informs about deprecation
* @param {Object} config - the configuration object
* @return {Object} config - the validated configuration object
*/
function validateConfig(config) {
if (!config) errorHandler('configIsRequired');
if (!(0,_utils_isObject_js__WEBPACK_IMPORTED_MODULE_1__["default"])(config)) errorHandler('configType');
if (config.urls) {
informAboutDeprecation();
return {
paths: {
vs: config.urls.monacoBase
}
};
}
return config;
}
/**
* logs deprecation message
*/
function informAboutDeprecation() {
console.warn(errorMessages.deprecation);
}
function throwError(errorMessages, type) {
throw new Error(errorMessages[type] || errorMessages["default"]);
}
var errorMessages = {
configIsRequired: 'the configuration object is required',
configType: 'the configuration object should be an object',
"default": 'an unknown error accured in `@monaco-editor/loader` package',
deprecation: "Deprecation warning!\n You are using deprecated way of configuration.\n\n Instead of using\n monaco.config({ urls: { monacoBase: '...' } })\n use\n monaco.config({ paths: { vs: '...' } })\n\n For more please check the link https://github.com/suren-atoyan/monaco-loader#config\n "
};
var errorHandler = (0,_utils_curry_js__WEBPACK_IMPORTED_MODULE_0__["default"])(throwError)(errorMessages);
var validators = {
config: validateConfig
};
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.styles.ts":
/*!************************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.styles.ts ***!
\************************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ EditorWrapper: function() { return /* binding */ EditorWrapper; },
/* harmony export */ ResizeHandle: function() { return /* binding */ ResizeHandle; }
/* harmony export */ });
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/ui */ "@elementor/ui");
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_ui__WEBPACK_IMPORTED_MODULE_0__);
const EditorWrapper = (0,_elementor_ui__WEBPACK_IMPORTED_MODULE_0__.styled)((0,_elementor_ui__WEBPACK_IMPORTED_MODULE_0__.Box))`
/* @noflip */
direction: ltr;
border: 1px solid var( --e-a-border-color );
border-radius: 8px;
padding: 4px;
position: relative;
height: 200px;
.monaco-editor .suggest-widget {
width: 220px !important;
max-width: 220px !important;
z-index: 1001;
}
.visual-content-dimmed {
opacity: 0.6;
color: #aaa !important;
pointer-events: none;
}
.monaco-editor {
.margin-view-overlays > div:nth-of-type( 1 ) .cldr.codicon.codicon-folding-expanded {
visibility: hidden;
}
.monaco-scrollable-element {
> .scrollbar {
width: 6px !important;
> .slider {
width: 6px !important;
}
}
}
}
`;
const ResizeHandle = (0,_elementor_ui__WEBPACK_IMPORTED_MODULE_0__.styled)((0,_elementor_ui__WEBPACK_IMPORTED_MODULE_0__.Button))`
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 6px;
cursor: ns-resize;
background: transparent;
border: none;
padding: 0;
&:hover {
background: rgba( 0, 0, 0, 0.05 );
}
&:active {
background: rgba( 0, 0, 0, 0.1 );
}
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate( -50%, -50% );
width: 30px;
height: 2px;
background: var( --e-a-border-color );
border-radius: 1px;
}
`;
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.tsx":
/*!******************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.tsx ***!
\******************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ CssEditor: function() { return /* binding */ CssEditor; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-controls */ "@elementor/editor-controls");
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _elementor_editor_ui__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @elementor/editor-ui */ "@elementor/editor-ui");
/* harmony import */ var _elementor_editor_ui__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_ui__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @elementor/ui */ "@elementor/ui");
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _monaco_editor_react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @monaco-editor/react */ "./node_modules/@monaco-editor/react/dist/index.mjs");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var _css_editor_styles__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./css-editor.styles */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.styles.ts");
/* harmony import */ var _css_validation__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./css-validation */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-validation.ts");
/* harmony import */ var _resize_handle__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./resize-handle */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/resize-handle.tsx");
/* harmony import */ var _visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./visual-content-change-protection */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/visual-content-change-protection.ts");
const createEditorDidMountHandler = (editorRef, monacoRef, onUserContentChange, setIsValid, syntaxRuleOptions) => {
return (editor, monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
(0,_visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__.preventChangeOnVisualContent)(editor);
(0,_css_validation__WEBPACK_IMPORTED_MODULE_7__.setCustomSyntaxRules)(editor, monaco, syntaxRuleOptions);
const initialCode = editor.getModel()?.getValue() ?? '';
const initialUserContent = (0,_visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__.getActual)(initialCode);
onUserContentChange(initialUserContent);
monaco.editor.onDidChangeMarkers(() => {
(0,_css_validation__WEBPACK_IMPORTED_MODULE_7__.clearMarkersFromVisualContent)(editor, monaco);
setIsValid((0,_css_validation__WEBPACK_IMPORTED_MODULE_7__.validate)(editor, monaco));
});
editor.setPosition({
lineNumber: 2,
column: (editor.getModel()?.getLineContent(2).length ?? 0) + 1
});
disableFoldingFirstRow(editor);
// eslint-disable-next-line no-bitwise
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyA, () => {
const editorModel = editor.getModel();
if (!editorModel) {
return;
}
const fullRange = editorModel.getFullModelRange();
const contentEndLine = fullRange.endLineNumber - 1;
let endColumn = editorModel.getLineLastNonWhitespaceColumn(contentEndLine);
if (endColumn === 0) {
endColumn = editorModel.getLineMaxColumn(contentEndLine);
}
editor.setSelection(new monaco.Selection(fullRange.startLineNumber + 1, fullRange.startColumn, contentEndLine, endColumn));
});
};
};
function disableFoldingFirstRow(editor) {
if (!(typeof editor.getDomNode === 'function')) {
return;
}
const marginViewOverlays = editor.getDomNode()?.querySelector('.margin-view-overlays');
const handler = event => {
const ev = event;
if (ev.button !== 0) {
return;
}
const target = ev.target;
const firstElem = marginViewOverlays?.children[0];
const isFirstRow = target === firstElem || target.parentElement === firstElem;
if (isFirstRow) {
event.preventDefault();
event.stopPropagation();
}
};
marginViewOverlays?.addEventListener('mousedown', handler);
editor.onDidDispose(() => {
marginViewOverlays?.removeEventListener('mousedown', handler);
});
}
const CssEditor = ({
value,
onChange,
syntaxRuleOptions,
readOnly = false
}) => {
const theme = (0,_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.useTheme)();
const containerRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
const editorRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
const monacoRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
const debounceTimer = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
const [hasContent, setHasContent] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(value.trim() !== '');
const [isValid, setIsValid] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
const [contentVersion, setContentVersion] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);
useOnUpdate(() => {
const code = editorRef.current?.getModel()?.getValue() ?? '';
const userContent = (0,_visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__.getActual)(code);
setHasContent(!userContent.trim());
onChange(userContent, isValid);
}, [contentVersion, isValid]);
const handleUserContentChange = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(newValue => {
setHasContent(newValue.trim() !== '');
}, []);
const handleResize = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
editorRef.current?.layout();
}, []);
const handleHeightChange = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(height => {
if (containerRef.current) {
containerRef.current.style.height = `${height}px`;
}
}, []);
const handleEditorChange = () => {
if (!editorRef.current || !monacoRef.current) {
return;
}
(0,_css_validation__WEBPACK_IMPORTED_MODULE_7__.setCustomSyntaxRules)(editorRef?.current, monacoRef.current, syntaxRuleOptions);
if (debounceTimer.current) {
clearTimeout(debounceTimer.current);
}
debounceTimer.current = setTimeout(() => {
setContentVersion(prev => prev + 1);
}, 500);
};
const handleEditorDidMount = createEditorDidMountHandler(editorRef, monacoRef, handleUserContentChange, setIsValid, syntaxRuleOptions);
const handleReset = () => editorRef.current?.getModel()?.setValue((0,_visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__.setVisualContent)(''));
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
const timerRef = debounceTimer;
return () => {
const timer = timerRef.current;
if (timer) {
clearTimeout(timer);
}
};
}, []);
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_editor_ui__WEBPACK_IMPORTED_MODULE_2__.FloatingActionsBar, {
actions: hasContent ? [/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.ClearIconButton, {
key: "clear",
tooltipText: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Clear', 'elementor-pro'),
onClick: handleReset
})] : []
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.Box, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_css_editor_styles__WEBPACK_IMPORTED_MODULE_6__.EditorWrapper, {
ref: containerRef,
dir: "ltr"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_monaco_editor_react__WEBPACK_IMPORTED_MODULE_4__.Editor, {
height: "100%",
language: "css",
theme: theme.palette.mode === 'dark' ? 'vs-dark' : 'vs',
value: (0,_visual_content_change_protection__WEBPACK_IMPORTED_MODULE_9__.setVisualContent)(value),
onMount: handleEditorDidMount,
onChange: handleEditorChange,
options: {
lineNumbers: 'on',
lineNumbersMinChars: 3,
folding: true,
minimap: {
enabled: false
},
fontFamily: 'Roboto, Arial, Helvetica, Verdana, sans-serif',
fontSize: 12,
renderLineHighlight: 'none',
hideCursorInOverviewRuler: true,
overviewRulerBorder: false,
fixedOverflowWidgets: true,
suggestFontSize: 10,
suggestLineHeight: 14,
stickyScroll: {
enabled: false
},
lineDecorationsWidth: 2,
wordWrap: 'on',
scrollBeyondLastLine: false,
readOnly,
editContext: false
}
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_resize_handle__WEBPACK_IMPORTED_MODULE_8__.ResizeHandleComponent, {
onResize: handleResize,
containerRef: containerRef,
onHeightChange: handleHeightChange
}))));
};
function useOnUpdate(callback, dependencies) {
const hasMounted = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
if (hasMounted.current) {
callback();
} else {
hasMounted.current = true;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies);
}
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-validation.ts":
/*!*********************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-validation.ts ***!
\*********************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ clearMarkersFromVisualContent: function() { return /* binding */ clearMarkersFromVisualContent; },
/* harmony export */ setCustomSyntaxRules: function() { return /* binding */ setCustomSyntaxRules; },
/* harmony export */ validate: function() { return /* binding */ validate; }
/* harmony export */ });
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_0__);
const syntaxRules = {
pseudoState: {
pattern: '^\\s*[&]{0,1}\\s*(?::hover|:active|:focus)',
regex: true,
message: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_0__.__)('The use of pseudo-states is not permitted. Instead, switch to the desired pseudo state and add your custom code there.', 'elementor-pro')
},
mediaQuery: {
pattern: '@media\\s+[^{]*\\b(?:min-width|max-width|width)\\b',
regex: true,
message: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_0__.__)('The use of @media width queries is not permitted. Instead, switch to the desired breakpoint and add your custom code there.', 'elementor-pro')
}
};
function setCustomSyntaxRules(editor, monaco, options) {
const model = editor.getModel();
if (!model) {
return true;
}
const customMarkers = [];
Object.entries(syntaxRules).forEach(([id, rule]) => {
if (options?.rules?.[id] === false) {
return;
}
const matches = model.findMatches(rule.pattern, true, rule.regex ?? false, true, null, true);
matches.forEach(match => {
customMarkers.push({
severity: monaco.MarkerSeverity.Error,
message: rule.message,
startLineNumber: match.range.startLineNumber,
startColumn: match.range.startColumn,
endLineNumber: match.range.endLineNumber,
endColumn: match.range.endColumn,
source: 'custom-css-rules'
});
});
});
monaco.editor.setModelMarkers(model, 'custom-css-rules', customMarkers);
return customMarkers.length === 0;
}
function validate(editor, monaco) {
const model = editor.getModel();
if (!model) {
return true;
}
const allMarkers = monaco.editor.getModelMarkers({
resource: model.uri
});
return allMarkers.filter(marker => marker.severity === monaco.MarkerSeverity.Error).length === 0;
}
function clearMarkersFromVisualContent(editor, monaco) {
const model = editor.getModel();
if (!model) {
return;
}
const allMarkers = monaco.editor.getModelMarkers({
resource: model.uri
});
const filteredMarkers = allMarkers.filter(marker => marker.startLineNumber !== 1);
const nonCustomMarkers = filteredMarkers.filter(m => m.source !== 'custom-css-rules');
if (nonCustomMarkers.length === allMarkers.length) {
return;
}
monaco.editor.setModelMarkers(model, 'css', nonCustomMarkers);
}
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/resize-handle.tsx":
/*!*********************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/resize-handle.tsx ***!
\*********************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ ResizeHandleComponent: function() { return /* binding */ ResizeHandleComponent; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_editor_styles__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./css-editor.styles */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.styles.ts");
const ResizeHandleComponent = ({
onResize,
containerRef,
onHeightChange
}) => {
const handleResizeMove = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(e => {
const container = containerRef.current;
if (!container) {
return;
}
const containerRect = container.getBoundingClientRect();
const newHeight = Math.max(100, e.clientY - containerRect.top);
onHeightChange?.(newHeight);
onResize(newHeight);
}, [containerRef, onResize, onHeightChange]);
const handleResizeEnd = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(() => {
document.removeEventListener('mousemove', handleResizeMove);
document.removeEventListener('mouseup', handleResizeEnd);
}, [handleResizeMove]);
const handleResizeStart = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(e => {
e.preventDefault();
e.stopPropagation();
document.addEventListener('mousemove', handleResizeMove);
document.addEventListener('mouseup', handleResizeEnd);
}, [handleResizeMove, handleResizeEnd]);
react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {
return () => {
document.removeEventListener('mousemove', handleResizeMove);
document.removeEventListener('mouseup', handleResizeEnd);
};
}, [handleResizeMove, handleResizeEnd]);
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_css_editor_styles__WEBPACK_IMPORTED_MODULE_1__.ResizeHandle, {
onMouseDown: handleResizeStart,
"aria-label": "Resize editor height",
title: "Drag to resize editor height"
});
};
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/visual-content-change-protection.ts":
/*!***************************************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/visual-content-change-protection.ts ***!
\***************************************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ getActual: function() { return /* binding */ getActual; },
/* harmony export */ preventChangeOnVisualContent: function() { return /* binding */ preventChangeOnVisualContent; },
/* harmony export */ setVisualContent: function() { return /* binding */ setVisualContent; }
/* harmony export */ });
const preventChangeOnVisualContent = editor => {
const model = editor.getModel();
if (!model) {
return;
}
// On first load
applyVisualContentStyling(editor, model);
// On every change
model.onDidChangeContent(() => {
applyVisualContentStyling(editor, model);
});
disableCursorOnVisualContent(editor);
overridePushEditOperations(model);
};
const applyVisualContentStyling = (editor, model) => {
const decorationsCollection = editor.createDecorationsCollection();
const lineCount = model.getLineCount();
const decorations = [];
decorations.push({
range: {
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: model.getLineContent(1).length + 1
},
options: {
inlineClassName: 'visual-content-dimmed',
isWholeLine: false
}
});
if (lineCount > 1) {
decorations.push({
range: {
startLineNumber: lineCount,
startColumn: 1,
endLineNumber: lineCount,
endColumn: model.getLineContent(lineCount).length + 1
},
options: {
inlineClassName: 'visual-content-dimmed',
isWholeLine: false
}
});
}
decorationsCollection.set(decorations);
};
const disableCursorOnVisualContent = editor => {
const model = editor.getModel();
if (!model) {
return;
}
editor.onDidChangeCursorPosition(e => {
const totalLines = model.getLineCount();
const position = e.position;
if (position.lineNumber === 1) {
editor.setPosition({
lineNumber: 2,
column: 1
});
} else if (position.lineNumber === totalLines) {
editor.setPosition({
lineNumber: totalLines - 1,
column: model.getLineContent(totalLines - 1).length + 1
});
}
});
};
const overridePushEditOperations = model => {
const originalPushEditOperations = model.pushEditOperations;
model.pushEditOperations = (beforeCursorState, editOperations, cursorStateComputer) => {
const totalLines = model.getLineCount();
const modelRange = model.getFullModelRange();
const filteredOperations = editOperations.filter(operation => {
const range = operation.range;
const affectsProtectedLine = range.startLineNumber === 1 || range.endLineNumber === 1 || range.startLineNumber === totalLines || range.endLineNumber === totalLines;
if (affectsProtectedLine && isFullContentReplacement(range, modelRange) && hasVisualContent(operation.text)) {
return true;
}
return !affectsProtectedLine;
});
return originalPushEditOperations.call(model, beforeCursorState, filteredOperations, cursorStateComputer);
};
};
const isFullContentReplacement = (range, modelRange) => {
return range.startLineNumber === modelRange.startLineNumber && range.endLineNumber === modelRange.endLineNumber && range.startColumn === modelRange.startColumn && range.endColumn === modelRange.endColumn;
};
const hasVisualContent = text => {
if (!text) {
return false;
}
return text.startsWith('element.style {') && text.endsWith('}');
};
const setVisualContent = value => {
const trimmed = value.trim();
return `element.style {\n${trimmed ? ' ' + trimmed.replace(/\n/g, '\n ') + '\n' : ' \n'}}`;
};
const getActual = value => {
const lines = value.split('\n');
if (lines.length < 2) {
return '';
}
return lines.slice(1, -1).map(line => line.replace(/^ {2}/, '')).join('\n');
};
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/controls/attributes-control.tsx":
/*!********************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/controls/attributes-control.tsx ***!
\********************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AttributesControl: function() { return /* binding */ AttributesControl; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-controls */ "@elementor/editor-controls");
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__);
const AttributesControl = (0,_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.createControl)(() => {
const getHelperText = (key, value) => {
if (value && !key) {
return {
keyHelper: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)("Empty attribute names aren't valid and won't render on the page.", 'elementor-pro')
};
}
return {};
};
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.KeyValueControl, {
keyName: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Name', 'elementor-pro'),
valueName: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Value', 'elementor-pro'),
regexKey: "^[a-zA-Z0-9_-]*$",
validationErrorMessage: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Names can only use letters, numbers, dashes (-) and underscores (_).', 'elementor-pro'),
getHelperText: getHelperText
});
});
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/controls/display-conditions-control.tsx":
/*!****************************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/controls/display-conditions-control.tsx ***!
\****************************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ DisplayConditionsControl: function() { return /* binding */ DisplayConditionsControl; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-controls */ "@elementor/editor-controls");
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _elementor_icons__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @elementor/icons */ "@elementor/icons");
/* harmony import */ var _elementor_icons__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_elementor_icons__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @elementor/ui */ "@elementor/ui");
/* harmony import */ var _elementor_ui__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _prop_types_display_conditions__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../prop-types/display-conditions */ "./packages/packages/pro/editor-controls-extended/src/prop-types/display-conditions.ts");
/* harmony import */ var _utils_display_conditions_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/display-conditions-utils */ "./packages/packages/pro/editor-controls-extended/src/utils/display-conditions-utils.ts");
const OPEN_MODAL_EVENT = 'elementor/display-conditions/open';
const CLOSE_MODAL_EVENT = 'elementor/display-conditions/close';
const SET_CACHE_NOTICE_STATUS_EVENT = 'elementor/display-conditions/set-cache-notice-status';
const ariaLabel = (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Display Conditions', 'elementor-pro');
async function setCacheNoticeStatus() {
return new Promise((resolve, reject) => {
window.dispatchEvent(new CustomEvent(SET_CACHE_NOTICE_STATUS_EVENT, {
detail: {
resolve,
reject
}
}));
});
}
const DisplayConditionsControl = (0,_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.createControl)(({
disabled = false
}) => {
const {
setValue,
value: displayConditionsValue
} = (0,_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.useBoundProp)(_prop_types_display_conditions__WEBPACK_IMPORTED_MODULE_5__.displayConditionsPropTypeUtil);
const [isModalOpen, setIsModalOpen] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
const hasValue = !!displayConditionsValue?.length;
const setControlValue = value => setValue((0,_utils_display_conditions_utils__WEBPACK_IMPORTED_MODULE_6__.transformV3ToV4)(JSON.parse(value ?? '[]')));
const getControlValue = () => (0,_utils_display_conditions_utils__WEBPACK_IMPORTED_MODULE_6__.transformV4ToV3)(displayConditionsValue) ?? [];
const onClose = () => {
setIsModalOpen(false);
window.dispatchEvent(new CustomEvent(CLOSE_MODAL_EVENT));
};
const conditions = getControlValue();
const openConditionsModal = () => {
setIsModalOpen(true);
window.dispatchEvent(new CustomEvent(OPEN_MODAL_EVENT, {
detail: {
props: {
getControlValue,
setControlValue,
onClose,
setCacheNoticeStatus
}
}
}));
};
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.Stack, {
direction: "row",
spacing: 2,
sx: {
justifyContent: 'flex-end',
alignItems: 'center'
}
}, disabled && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.Chip, {
icon: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_icons__WEBPACK_IMPORTED_MODULE_2__.CrownFilledIcon, {
fontSize: "tiny"
}),
size: "tiny",
color: "promotion",
variant: "standard",
sx: {
width: '20px',
'& .MuiChip-label': {
display: 'none'
}
}
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.Tooltip, {
title: ariaLabel,
placement: "top"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.Box, {
sx: {
cursor: disabled ? 'not-allowed' : 'pointer',
display: 'inline-flex'
}
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_ui__WEBPACK_IMPORTED_MODULE_3__.ToggleButton, {
value: JSON.stringify(conditions),
size: "tiny",
variant: "outline",
"aria-pressed": hasValue,
"aria-expanded": isModalOpen,
selected: hasValue,
"aria-haspopup": "dialog",
"aria-label": ariaLabel,
onClick: openConditionsModal,
disabled: disabled,
sx: {
pointerEvents: disabled ? 'none' : 'auto'
},
"data-behavior": "display-conditions"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_icons__WEBPACK_IMPORTED_MODULE_2__.SitemapIcon, {
fontSize: "tiny"
}))))));
});
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/controls/options-control.tsx":
/*!*****************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/controls/options-control.tsx ***!
\*****************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ OptionsControl: function() { return /* binding */ OptionsControl; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/editor-controls */ "@elementor/editor-controls");
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n");
/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__);
const OptionsControl = (0,_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.createControl)(() => {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createElement(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_1__.KeyValueControl, {
keyName: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Name', 'elementor-pro'),
valueName: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Value', 'elementor-pro')
});
});
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/extend-transition-properties.ts":
/*!********************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/extend-transition-properties.ts ***!
\********************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ extendTransitionProperties: function() { return /* binding */ extendTransitionProperties; },
/* harmony export */ transitionProperties: function() { return /* reexport safe */ _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionProperties; },
/* harmony export */ transitionsItemsList: function() { return /* reexport safe */ _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionsItemsList; }
/* harmony export */ });
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-controls */ "@elementor/editor-controls");
/* harmony import */ var _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__);
function extendTransitionProperties(isDisabled = false) {
if (!_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionProperties || _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionProperties.length === 0) {
return;
}
_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionProperties.forEach(category => {
category.properties.forEach(property => {
if (property.value !== 'all') {
property.isDisabled = isDisabled;
}
});
});
_elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionsItemsList.splice(0, _elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionsItemsList.length, ..._elementor_editor_controls__WEBPACK_IMPORTED_MODULE_0__.transitionProperties.map(category => ({
label: category.label,
items: category.properties.map(property => property.label)
})));
}
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/init.ts":
/*!********************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/init.ts ***!
\********************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ init: function() { return /* binding */ init; }
/* harmony export */ });
/* harmony import */ var _elementor_license_api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/license-api */ "@elementor/license-api");
/* harmony import */ var _elementor_license_api__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_license_api__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _extend_transition_properties__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./extend-transition-properties */ "./packages/packages/pro/editor-controls-extended/src/extend-transition-properties.ts");
async function init() {
const [isExpired, features] = await Promise.all([(0,_elementor_license_api__WEBPACK_IMPORTED_MODULE_0__.fetchLicenseStatus)().catch(() => false), (0,_elementor_license_api__WEBPACK_IMPORTED_MODULE_0__.fetchTierFeatures)().catch(() => [])]);
if (features.includes('transitions')) {
(0,_extend_transition_properties__WEBPACK_IMPORTED_MODULE_1__.extendTransitionProperties)(isExpired);
}
}
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/prop-types/condition-group.ts":
/*!******************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/prop-types/condition-group.ts ***!
\******************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ conditionGroupPropTypeUtil: function() { return /* binding */ conditionGroupPropTypeUtil; }
/* harmony export */ });
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-props */ "@elementor/editor-props");
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_schema__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/schema */ "@elementor/schema");
/* harmony import */ var _elementor_schema__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_schema__WEBPACK_IMPORTED_MODULE_1__);
const unknownChildrenSchema = _elementor_schema__WEBPACK_IMPORTED_MODULE_1__.z.any().nullable();
const conditionGroupPropTypeUtil = (0,_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__.createPropUtils)('condition-group', _elementor_schema__WEBPACK_IMPORTED_MODULE_1__.z.array(unknownChildrenSchema));
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/prop-types/display-conditions.ts":
/*!*********************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/prop-types/display-conditions.ts ***!
\*********************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ displayConditionsPropTypeUtil: function() { return /* binding */ displayConditionsPropTypeUtil; }
/* harmony export */ });
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-props */ "@elementor/editor-props");
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _elementor_schema__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @elementor/schema */ "@elementor/schema");
/* harmony import */ var _elementor_schema__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_elementor_schema__WEBPACK_IMPORTED_MODULE_1__);
const unknownChildrenSchema = _elementor_schema__WEBPACK_IMPORTED_MODULE_1__.z.any().nullable();
const displayConditionsPropTypeUtil = (0,_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__.createPropUtils)('display-conditions', _elementor_schema__WEBPACK_IMPORTED_MODULE_1__.z.array(unknownChildrenSchema));
/***/ }),
/***/ "./packages/packages/pro/editor-controls-extended/src/utils/display-conditions-utils.ts":
/*!**********************************************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/utils/display-conditions-utils.ts ***!
\**********************************************************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ transformV3ToV4: function() { return /* binding */ transformV3ToV4; },
/* harmony export */ transformV4ToV3: function() { return /* binding */ transformV4ToV3; }
/* harmony export */ });
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @elementor/editor-props */ "@elementor/editor-props");
/* harmony import */ var _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__);
// Value must be falsy for navigator indication to work properly
function transformV3ToV4(displayConditions) {
if (!Array.isArray(displayConditions) || !displayConditions?.length) {
return null;
}
const transformed = displayConditions.filter(conditionGroup => !!conditionGroup?.length).map(conditionGroup => ({
$$type: 'condition-group',
value: conditionGroup.map(condition => _elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__.stringPropTypeUtil.create(JSON.stringify(condition)))
}));
return transformed.length ? transformed : null;
}
function transformV4ToV3(value) {
const conditionGroups = value;
const transformed = conditionGroups?.map(group => group.value.map(conditions => {
return JSON.parse(_elementor_editor_props__WEBPACK_IMPORTED_MODULE_0__.stringPropTypeUtil.extract(conditions) ?? '[]');
})) ?? null;
return getStructuredConditions(transformed);
}
function shouldConvertConditionsStructure(conditions) {
return !!conditions?.length && !Array.isArray(conditions?.[0]);
}
function getStructuredConditions(conditions) {
return shouldConvertConditionsStructure(conditions) ? [conditions] : conditions;
}
/***/ }),
/***/ "./node_modules/state-local/lib/es/state-local.js":
/*!********************************************************!*\
!*** ./node_modules/state-local/lib/es/state-local.js ***!
\********************************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function compose() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return function (x) {
return fns.reduceRight(function (y, f) {
return f(y);
}, x);
};
}
function curry(fn) {
return function curried() {
var _this = this;
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return args.length >= fn.length ? fn.apply(this, args) : function () {
for (var _len3 = arguments.length, nextArgs = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
nextArgs[_key3] = arguments[_key3];
}
return curried.apply(_this, [].concat(args, nextArgs));
};
};
}
function isObject(value) {
return {}.toString.call(value).includes('Object');
}
function isEmpty(obj) {
return !Object.keys(obj).length;
}
function isFunction(value) {
return typeof value === 'function';
}
function hasOwnProperty(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
}
function validateChanges(initial, changes) {
if (!isObject(changes)) errorHandler('changeType');
if (Object.keys(changes).some(function (field) {
return !hasOwnProperty(initial, field);
})) errorHandler('changeField');
return changes;
}
function validateSelector(selector) {
if (!isFunction(selector)) errorHandler('selectorType');
}
function validateHandler(handler) {
if (!(isFunction(handler) || isObject(handler))) errorHandler('handlerType');
if (isObject(handler) && Object.values(handler).some(function (_handler) {
return !isFunction(_handler);
})) errorHandler('handlersType');
}
function validateInitial(initial) {
if (!initial) errorHandler('initialIsRequired');
if (!isObject(initial)) errorHandler('initialType');
if (isEmpty(initial)) errorHandler('initialContent');
}
function throwError(errorMessages, type) {
throw new Error(errorMessages[type] || errorMessages["default"]);
}
var errorMessages = {
initialIsRequired: 'initial state is required',
initialType: 'initial state should be an object',
initialContent: 'initial state shouldn\'t be an empty object',
handlerType: 'handler should be an object or a function',
handlersType: 'all handlers should be a functions',
selectorType: 'selector should be a function',
changeType: 'provided value of changes should be an object',
changeField: 'it seams you want to change a field in the state which is not specified in the "initial" state',
"default": 'an unknown error accured in `state-local` package'
};
var errorHandler = curry(throwError)(errorMessages);
var validators = {
changes: validateChanges,
selector: validateSelector,
handler: validateHandler,
initial: validateInitial
};
function create(initial) {
var handler = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
validators.initial(initial);
validators.handler(handler);
var state = {
current: initial
};
var didUpdate = curry(didStateUpdate)(state, handler);
var update = curry(updateState)(state);
var validate = curry(validators.changes)(initial);
var getChanges = curry(extractChanges)(state);
function getState() {
var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function (state) {
return state;
};
validators.selector(selector);
return selector(state.current);
}
function setState(causedChanges) {
compose(didUpdate, update, validate, getChanges)(causedChanges);
}
return [getState, setState];
}
function extractChanges(state, causedChanges) {
return isFunction(causedChanges) ? causedChanges(state.current) : causedChanges;
}
function updateState(state, changes) {
state.current = _objectSpread2(_objectSpread2({}, state.current), changes);
return changes;
}
function didStateUpdate(state, handler, changes) {
isFunction(handler) ? handler(state.current) : Object.keys(changes).forEach(function (field) {
var _handler$field;
return (_handler$field = handler[field]) === null || _handler$field === void 0 ? void 0 : _handler$field.call(handler, state.current[field]);
});
return changes;
}
var index = {
create: create
};
/* harmony default export */ __webpack_exports__["default"] = (index);
/***/ }),
/***/ "react":
/*!**************************!*\
!*** external ["React"] ***!
\**************************/
/***/ (function(module) {
module.exports = window["React"];
/***/ }),
/***/ "@elementor/editor-controls":
/*!*************************************************!*\
!*** external ["elementorV2","editorControls"] ***!
\*************************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["editorControls"];
/***/ }),
/***/ "@elementor/editor-props":
/*!**********************************************!*\
!*** external ["elementorV2","editorProps"] ***!
\**********************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["editorProps"];
/***/ }),
/***/ "@elementor/editor-ui":
/*!*******************************************!*\
!*** external ["elementorV2","editorUi"] ***!
\*******************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["editorUi"];
/***/ }),
/***/ "@elementor/icons":
/*!****************************************!*\
!*** external ["elementorV2","icons"] ***!
\****************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["icons"];
/***/ }),
/***/ "@elementor/license-api":
/*!*********************************************!*\
!*** external ["elementorV2","licenseApi"] ***!
\*********************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["licenseApi"];
/***/ }),
/***/ "@elementor/schema":
/*!*****************************************!*\
!*** external ["elementorV2","schema"] ***!
\*****************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["schema"];
/***/ }),
/***/ "@elementor/ui":
/*!*************************************!*\
!*** external ["elementorV2","ui"] ***!
\*************************************/
/***/ (function(module) {
module.exports = window["elementorV2"]["ui"];
/***/ }),
/***/ "@wordpress/i18n":
/*!******************************!*\
!*** external ["wp","i18n"] ***!
\******************************/
/***/ (function(module) {
module.exports = window["wp"]["i18n"];
/***/ }),
/***/ "./node_modules/@monaco-editor/react/dist/index.mjs":
/*!**********************************************************!*\
!*** ./node_modules/@monaco-editor/react/dist/index.mjs ***!
\**********************************************************/
/***/ (function(__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ DiffEditor: function() { return /* binding */ we; },
/* harmony export */ Editor: function() { return /* binding */ de; },
/* harmony export */ "default": function() { return /* binding */ Ft; },
/* harmony export */ loader: function() { return /* reexport safe */ _monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__["default"]; },
/* harmony export */ useMonaco: function() { return /* binding */ Le; }
/* harmony export */ });
/* harmony import */ var _monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @monaco-editor/loader */ "./node_modules/@monaco-editor/loader/lib/es/index.js");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ "react");
var le={wrapper:{display:"flex",position:"relative",textAlign:"initial"},fullWidth:{width:"100%"},hide:{display:"none"}},v=le;var ae={container:{display:"flex",height:"100%",width:"100%",justifyContent:"center",alignItems:"center"}},Y=ae;function Me({children:e}){return react__WEBPACK_IMPORTED_MODULE_1__.createElement("div",{style:Y.container},e)}var Z=Me;var $=Z;function Ee({width:e,height:r,isEditorReady:n,loading:t,_ref:a,className:m,wrapperProps:E}){return react__WEBPACK_IMPORTED_MODULE_1__.createElement("section",{style:{...v.wrapper,width:e,height:r},...E},!n&&react__WEBPACK_IMPORTED_MODULE_1__.createElement($,null,t),react__WEBPACK_IMPORTED_MODULE_1__.createElement("div",{ref:a,style:{...v.fullWidth,...!n&&v.hide},className:m}))}var ee=Ee;var H=(0,react__WEBPACK_IMPORTED_MODULE_1__.memo)(ee);function Ce(e){(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(e,[])}var k=Ce;function he(e,r,n=!0){let t=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(!0);(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(t.current||!n?()=>{t.current=!1}:e,r)}var l=he;function D(){}function h(e,r,n,t){return De(e,t)||be(e,r,n,t)}function De(e,r){return e.editor.getModel(te(e,r))}function be(e,r,n,t){return e.editor.createModel(r,n,t?te(e,t):void 0)}function te(e,r){return e.Uri.parse(r)}function Oe({original:e,modified:r,language:n,originalLanguage:t,modifiedLanguage:a,originalModelPath:m,modifiedModelPath:E,keepCurrentOriginalModel:g=!1,keepCurrentModifiedModel:N=!1,theme:x="light",loading:P="Loading...",options:y={},height:V="100%",width:z="100%",className:F,wrapperProps:j={},beforeMount:A=D,onMount:q=D}){let[M,O]=(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(!1),[T,s]=(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(!0),u=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),c=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),w=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),d=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(q),o=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(A),b=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(!1);k(()=>{let i=_monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__["default"].init();return i.then(f=>(c.current=f)&&s(!1)).catch(f=>f?.type!=="cancelation"&&console.error("Monaco initialization: error:",f)),()=>u.current?I():i.cancel()}),l(()=>{if(u.current&&c.current){let i=u.current.getOriginalEditor(),f=h(c.current,e||"",t||n||"text",m||"");f!==i.getModel()&&i.setModel(f)}},[m],M),l(()=>{if(u.current&&c.current){let i=u.current.getModifiedEditor(),f=h(c.current,r||"",a||n||"text",E||"");f!==i.getModel()&&i.setModel(f)}},[E],M),l(()=>{let i=u.current.getModifiedEditor();i.getOption(c.current.editor.EditorOption.readOnly)?i.setValue(r||""):r!==i.getValue()&&(i.executeEdits("",[{range:i.getModel().getFullModelRange(),text:r||"",forceMoveMarkers:!0}]),i.pushUndoStop())},[r],M),l(()=>{u.current?.getModel()?.original.setValue(e||"")},[e],M),l(()=>{let{original:i,modified:f}=u.current.getModel();c.current.editor.setModelLanguage(i,t||n||"text"),c.current.editor.setModelLanguage(f,a||n||"text")},[n,t,a],M),l(()=>{c.current?.editor.setTheme(x)},[x],M),l(()=>{u.current?.updateOptions(y)},[y],M);let L=(0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{if(!c.current)return;o.current(c.current);let i=h(c.current,e||"",t||n||"text",m||""),f=h(c.current,r||"",a||n||"text",E||"");u.current?.setModel({original:i,modified:f})},[n,r,a,e,t,m,E]),U=(0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{!b.current&&w.current&&(u.current=c.current.editor.createDiffEditor(w.current,{automaticLayout:!0,...y}),L(),c.current?.editor.setTheme(x),O(!0),b.current=!0)},[y,x,L]);(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{M&&d.current(u.current,c.current)},[M]),(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{!T&&!M&&U()},[T,M,U]);function I(){let i=u.current?.getModel();g||i?.original?.dispose(),N||i?.modified?.dispose(),u.current?.dispose()}return react__WEBPACK_IMPORTED_MODULE_1__.createElement(H,{width:z,height:V,isEditorReady:M,loading:P,_ref:w,className:F,wrapperProps:j})}var ie=Oe;var we=(0,react__WEBPACK_IMPORTED_MODULE_1__.memo)(ie);function Pe(){let[e,r]=(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(_monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__["default"].__getMonacoInstance());return k(()=>{let n;return e||(n=_monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__["default"].init(),n.then(t=>{r(t)})),()=>n?.cancel()}),e}var Le=Pe;function He(e){let r=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)();return (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{r.current=e},[e]),r.current}var se=He;var _=new Map;function Ve({defaultValue:e,defaultLanguage:r,defaultPath:n,value:t,language:a,path:m,theme:E="light",line:g,loading:N="Loading...",options:x={},overrideServices:P={},saveViewState:y=!0,keepCurrentModel:V=!1,width:z="100%",height:F="100%",className:j,wrapperProps:A={},beforeMount:q=D,onMount:M=D,onChange:O,onValidate:T=D}){let[s,u]=(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(!1),[c,w]=(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(!0),d=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),o=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),b=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null),L=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(M),U=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(q),I=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(),i=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(t),f=se(m),Q=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(!1),B=(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(!1);k(()=>{let p=_monaco_editor_loader__WEBPACK_IMPORTED_MODULE_0__["default"].init();return p.then(R=>(d.current=R)&&w(!1)).catch(R=>R?.type!=="cancelation"&&console.error("Monaco initialization: error:",R)),()=>o.current?pe():p.cancel()}),l(()=>{let p=h(d.current,e||t||"",r||a||"",m||n||"");p!==o.current?.getModel()&&(y&&_.set(f,o.current?.saveViewState()),o.current?.setModel(p),y&&o.current?.restoreViewState(_.get(m)))},[m],s),l(()=>{o.current?.updateOptions(x)},[x],s),l(()=>{!o.current||t===void 0||(o.current.getOption(d.current.editor.EditorOption.readOnly)?o.current.setValue(t):t!==o.current.getValue()&&(B.current=!0,o.current.executeEdits("",[{range:o.current.getModel().getFullModelRange(),text:t,forceMoveMarkers:!0}]),o.current.pushUndoStop(),B.current=!1))},[t],s),l(()=>{let p=o.current?.getModel();p&&a&&d.current?.editor.setModelLanguage(p,a)},[a],s),l(()=>{g!==void 0&&o.current?.revealLine(g)},[g],s),l(()=>{d.current?.editor.setTheme(E)},[E],s);let X=(0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{if(!(!b.current||!d.current)&&!Q.current){U.current(d.current);let p=m||n,R=h(d.current,t||e||"",r||a||"",p||"");o.current=d.current?.editor.create(b.current,{model:R,automaticLayout:!0,...x},P),y&&o.current.restoreViewState(_.get(p)),d.current.editor.setTheme(E),g!==void 0&&o.current.revealLine(g),u(!0),Q.current=!0}},[e,r,n,t,a,m,x,P,y,E,g]);(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{s&&L.current(o.current,d.current)},[s]),(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{!c&&!s&&X()},[c,s,X]),i.current=t,(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{s&&O&&(I.current?.dispose(),I.current=o.current?.onDidChangeModelContent(p=>{B.current||O(o.current.getValue(),p)}))},[s,O]),(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{if(s){let p=d.current.editor.onDidChangeMarkers(R=>{let G=o.current.getModel()?.uri;if(G&&R.find(J=>J.path===G.path)){let J=d.current.editor.getModelMarkers({resource:G});T?.(J)}});return()=>{p?.dispose()}}return()=>{}},[s,T]);function pe(){I.current?.dispose(),V?y&&_.set(m,o.current.saveViewState()):o.current.getModel()?.dispose(),o.current.dispose()}return react__WEBPACK_IMPORTED_MODULE_1__.createElement(H,{width:z,height:F,isEditorReady:s,loading:N,_ref:b,className:j,wrapperProps:A})}var fe=Ve;var de=(0,react__WEBPACK_IMPORTED_MODULE_1__.memo)(fe);var Ft=de;
//# sourceMappingURL=index.mjs.map
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ if (!(moduleId in __webpack_modules__)) {
/******/ delete __webpack_module_cache__[moduleId];
/******/ var e = new Error("Cannot find module '" + moduleId + "'");
/******/ e.code = 'MODULE_NOT_FOUND';
/******/ throw e;
/******/ }
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ !function() {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
!function() {
/*!*********************************************************************!*\
!*** ./packages/packages/pro/editor-controls-extended/src/index.ts ***!
\*********************************************************************/
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AttributesControl: function() { return /* reexport safe */ _controls_attributes_control__WEBPACK_IMPORTED_MODULE_1__.AttributesControl; },
/* harmony export */ CssEditor: function() { return /* reexport safe */ _components_css_code_editor_css_editor__WEBPACK_IMPORTED_MODULE_6__.CssEditor; },
/* harmony export */ DisplayConditionsControl: function() { return /* reexport safe */ _controls_display_conditions_control__WEBPACK_IMPORTED_MODULE_3__.DisplayConditionsControl; },
/* harmony export */ OptionsControl: function() { return /* reexport safe */ _controls_options_control__WEBPACK_IMPORTED_MODULE_2__.OptionsControl; },
/* harmony export */ conditionGroupPropTypeUtil: function() { return /* reexport safe */ _prop_types_condition_group__WEBPACK_IMPORTED_MODULE_5__.conditionGroupPropTypeUtil; },
/* harmony export */ displayConditionsPropTypeUtil: function() { return /* reexport safe */ _prop_types_display_conditions__WEBPACK_IMPORTED_MODULE_4__.displayConditionsPropTypeUtil; }
/* harmony export */ });
/* harmony import */ var _init__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./init */ "./packages/packages/pro/editor-controls-extended/src/init.ts");
/* harmony import */ var _controls_attributes_control__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/attributes-control */ "./packages/packages/pro/editor-controls-extended/src/controls/attributes-control.tsx");
/* harmony import */ var _controls_options_control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/options-control */ "./packages/packages/pro/editor-controls-extended/src/controls/options-control.tsx");
/* harmony import */ var _controls_display_conditions_control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./controls/display-conditions-control */ "./packages/packages/pro/editor-controls-extended/src/controls/display-conditions-control.tsx");
/* harmony import */ var _prop_types_display_conditions__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./prop-types/display-conditions */ "./packages/packages/pro/editor-controls-extended/src/prop-types/display-conditions.ts");
/* harmony import */ var _prop_types_condition_group__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./prop-types/condition-group */ "./packages/packages/pro/editor-controls-extended/src/prop-types/condition-group.ts");
/* harmony import */ var _components_css_code_editor_css_editor__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./components/css-code-editor/css-editor */ "./packages/packages/pro/editor-controls-extended/src/components/css-code-editor/css-editor.tsx");
(0,_init__WEBPACK_IMPORTED_MODULE_0__.init)();
}();
(window.elementorV2 = window.elementorV2 || {}).editorControlsExtended = __webpack_exports__;
/******/ })()
;
window.elementorV2.editorControlsExtended?.init?.();
The post Things to Do in Mexico City appeared first on Char's Footsteps.
]]>Mexico City offers a range of attractions that cater to different interests. Some of the most notable things to do include:

Visit the Zócalo: The city’s main square, Plaza de la Constitución, is one of the largest public squares in the world. It’s surrounded by important historical buildings, including the Metropolitan Cathedral and the National Palace.
Explore the Museums: Mexico City is home to over 150 museums!! Key highlights include:
Chapultepec Park: Chapultepec Park (or Bosque de Chapultepec) is one of the largest urban parks in the world and a must-do if you’re in Mexico City. It’s home to a number of attractions from gardens, museums, fountains, art galleries, the Chapultepec Castle and a zoo.

Teotihuacán Pyramids: Located about an hour from the city centre, this archaeological site features the Pyramid of the Sun and the Pyramid of the Moon. There are lots of guided tours available from the city.
Palacio de Bellas Artes: A stunning cultural centre known for its art exhibitions and performances. The building’s architecture and the murals inside are also major attractions.
Xochimilco: Take a day trip from Mexico City to Xochimilco. It’s known for it’s canals and famous colourful boat rides (trajineras) along the waterways.
Local Markets: Mexico City has a variety of markets, such as Mercado de la Merced and La Ciudadela craft market, where you can shop for local food, crafts and souvenirs.
Hop on Hop Off Bus: A great option for seeing the main sites of Mexico City is the famous hop on hop off tour bus! I joined this and enjoyed site across the 4 main routes. These take you round the Historic Centre, Polanco, the Basilica route (for the best religious spots!) and the South Route (best bohemian spots and archaeology). The city has so much to see and the hop-on-hop-off bus is a great way to see it, particularly given how big it is!

Visit the neighbourhoods of Mexico City! More info on my favourite spots below.
Explore one of the many incredible neighbourhoods on offer. One of my favourtie things to do in Mexico City was just walk around the city exploring the different vibes of each area. Coming from London, I always appreciate cities that has neighbourhoods with very different vibes/atmospheres. From very old, historic regions to trendy areas, there’s so much on offer here.
My favourite to roam around in were Condesa and Roma. These popular areas are known for their vibrant cafe culture, Art Deco architecture, colourful streets and cool restaurants/bars.
Polanco is another great neighbourhood to spend time in, particularly for its shopping and for how close it is to Chapultepec.
Centro Historico, or the historic centre, is another must for its history and for getting to experience a different vibe to the city from the trendy Condesa or Roma regions.
Coyoacán and San Ángel are two other historic neighbourhoods to visit. These areas are known for their colonial architecture, cobblestone streets, markets and cultural significance.

Mexico City has a well-developed public transport network, making it easy to navigate. Some common ways to get around include:
Metro: The Mexico City Metro is the most efficient way to travel. It’s extensive, covering the main areas of the city and tickets are cheap! I only used it a few times during my stay but found it to be quite clean and comfortable.
Metrobús: A bus rapid transit system that operates along major corridors. It’s ideal for travelling longer distances within the city.
Taxis: Taxis are widely available but only use authorised taxis (such as Sitio Taxis) or rideshare apps like Uber for safety and convenience. This was my main way of travelling around Mexico City – it was great for price and convenience.
Bicycles: The Ecobici public bicycle-sharing system is a good option for those wanting to cycle around the city, especially in areas like Reforma and Condesa.
Walking: In many areas such as the Historic Centre, Condesa, and Roma, walking is the best way to explore local shops, cafes, and cultural sites.

I stayed in Mexico City for 6 days, which felt like enough time to see key highlights and get a feel for a few different neighbourhoods. As the city is so big, you could easily stay longer. I recommend at least 3 days to visit the central attractions such as the Zócalo, museums, and Teotihuacán. Having a few extra days will allow for day trips or additional activities in the surrounding areas, like Xochimilco.
If you have a particular interest in Mexican history, art, or cuisine, consider extending your stay to a week to delve deeper into the cultural offerings.
Mexico City is well-connected and easy to reach by air, land, or bus.

While Mexico City is generally safe for tourists, certain areas can have higher crime rates. I was quite apprehensive before I arrived about this but actually felt very safe during my stay. As with any big city, stick to well-known tourist areas, avoid displaying valuables and be cautious in crowded places.
I went to Mexico City in January and found it to be fantastic weather – very comfortable temperatures of 25°C and no rain. I’ve heard January to May is great for avoiding the rain and having nice warm temperatures. The rainy season is typically from June to October, so avoid if possible.
Although Spanish is the primary language, many people in tourist areas speak basic English. However, learning a few common Spanish phrases can be helpful! I used a lonely planet phrase book, Google translate and Coffee Break Espanol podcast lessons (I highly recommend!) which all helped me during my trip.
I was told tap water quality can vary in Mexico so I stuck to bottled water for drinking and brushing my teeth during my stay.
Credit cards are accepted in most hotels, restaurants and shops in tourist areas. However, it’s a good idea to carry some cash, especially in local markets or for smaller purchases.
I absolutely loved my time in Mexico City! I found it offered a great mix of cultural, historical, and natural attractions with amazing food! With an efficient public transport system and a mix of modern and traditional experiences, it’s a city that caters to all types of travellers. After my time in Mexico City, I flew to the East Coast to explore Tulum, Valladolid and Holbox.
The post Things to Do in Mexico City appeared first on Char's Footsteps.
]]>The post Regents Canal Walk appeared first on Char's Footsteps.
]]>
The full walk starts at Little Venice, a picturesque basin where the canal meets the Grand Union Canal. From here, the towpath passes through Maida Hill Tunnel and skirts Regent’s Park, offering views of London Zoo. Continuing east, the route runs through Camden Town, known for its market and music venues, before heading towards King’s Cross and the redeveloped Coal Drops Yard. Further along, you pass through Islington Tunnel (where you’ll need to detour onto streets) and follow the canal past Victoria Park in Hackney. The final stretch runs to Limehouse Basin, where it meets the River Thames.
I love doing this walk at different times throughout the year, to enjoy the changing seasons and different scenery that brings. From spring blossom to autumnal leaves, every season brings a different vibe! I recommend going at different points throughout the year if you’re able to.

There are so many great places to stop along the Regent’s Canal;
If you want a shorter walk, Little Venice to Camden Town is one of the most popular sections along the route. At around 2.5 miles (4km), it take approximately one hour to complete at a leisurely pace. This stretch offers peaceful water views, passes the edge of Regent’s Park and you’ll be treated to sneak peaks of London Zoo! You’ll end in Camden, where you can explore the market, get food/drinks in the sun and just enjoy the buzzing vibe of the area. This is one of my favourite walks in London without doubt!

At roughly 9 miles in length, the entire route takes around 4–5 hours, depending on pace and stops.
For the most page, the walk is safe. However as a woman, I wouldn’t recommend walking down the canal late at night in the dark!
Yes, you can cycle along the towpath. But it does get narrow and very busy at times – particularly on weekends. If you are going to cycle, be prepared to go slow and have to stop at points. This isn’t the place for a training ride, as you’d have with Richmond Park or Regents Park Laps.
Yes, you can break up the whole walk into smaller, more manageable sections. As the canal goes through a number of transport links, it’s easy to join or leave the Regent’s Canal Walk at different points.
There are signs at major points, but you might want to use a GPS/Google Maps/AllTrails too. Once you’re on the walk, it’s mostly obvious where to go. There are a couple of points where you need to go back up to street level and join the path from round the corner, but there are signs posts here to help with that.
Yes, there are lots of places to eat – especially around Camden, King’s Cross and Little Venice. I also love that there are a few restaurants and cafes hidden along the path too!
The post Regents Canal Walk appeared first on Char's Footsteps.
]]>The post A Day Trip to Xochimilco, Mexico City appeared first on Char's Footsteps.
]]>Xochimilco is an area in Mexico City that’s best known for its canal and colourful flat-bottomed boats called Trajineras. They’re used by locals and tourists alike to enjoy the waterways with music, food and drinks on board. The name “Xochimilco” actually means “flower field” in Nahuatl – a tribute to the area’s history of chinampas. These are man-made agricultural islands that were built by the Aztecs – with some still working today!
Xochimilco is located about 23km south of Mexico City and is easily accessible by public transport, ride shares or taxis. If you’re short on time, a taxi or ride share (like Uber), would be your best option. This should take around 45-60 minutes from the city centre, depending on traffic.
To get to Xochimilco by public transport, take the Mtro Line 2 (blue line) to Tasqueña (southern terminal station). From Tasqueña, swap to the Tren Ligero (light rail) and ride to Xochimilco station. This journey will take about an hour in total. From Xochimilco station, it’s about 10 minutes walk to the main embarcaderos (the waterfront where the boat docks are).
There are several embarcaderos in Xochimilco to choose from depending on what vibe you want. Some are known for being more relaxed and nature orientated, whilst others are busier and more party-focused.
Embarcadero Nativitas is the most tourist-friendly dock with lots of boats, guides and vendors to choose from. This is the one we went to and I’d advise this one if it’s your first time. It was the most straightforward one to get too and there were some English speakers here too. If you’re planning on turning up and booking on the day, this is likely going to be your best option too.
Embarcadero Nuevo Nativitas is similar to the one above but it’s typically less crowded. So, if you’ve had no luck at Embarcadero Nativitas, then head to this one!
Embarcadero Cuemanco is good if you’re looking for a quieter option that has more of a nature-focus.

I did my trip to Xochimilco as part of a tour, which included the cost of a Trajinera, the transport, some food/drinks and a few other stops along the way. But if you’re looking for just the price of the boat, the current government-set rate is around $600 MXN per hour per boat. Prices are set by boat, not by person! So regardless of how many people are on board, you’ll be paying that price. Maximum capacity is usually around 15-20. This was a big part of why my sister and I decided to join a tour, as we couldn’t guarantee finding other people to pair up with to help split the costs of the boat.
Be sure to confirm the price before you board, and if someone tries to significantly overcharge you, just walk away. There were so many operators at the dock, that you’ll be able to find someone else offering at the more standard rate. Booking ahead isn’t a must, however, if you’re going with a large group or you’re looking for a themed experience (like mariachi packages or booxy boats), then I’d recommend booking in advance.

Our group tour had about 15 people and everyone was lovely! My sister and I definitely lucked out. Upon arriving at Embarcadero Nativitas, we had a toilet stop and then met our gondolier to board our traditional trajinera boat. Going on a Thursday meant we could float down the various waterways in a more peaceful setting, compared to the hectic Weekend parties. The gondolier let us hold the long, wooden pole, which was surprisingly heavy! We bought some food and drinks from various vendors along the way, having a lovely group lunch together. Our guide for the day was able to point out specific bits of wildlife and we passed some scenic areas like old bridges. There were floating mariachi bands that played us a few songs. I believe these are usually charged at a fee, but our tour guide took care of it. A typical ride will last 1-2 hours, so by the time we’d finished eating, we were heading back towards the boat dock.

If you’re looking for something different, and you have the time, you can ask your gondolier about visiting the Isla de las Muñecas. Isla de las Muñecas is a remote chinampa filled with hundreds of old, decaying dolls that hang from trees. The journey takes about 2-3 hours each way, so we didn’t have time for this 6-hour round trip (minimum), but I heard interesting things about this trip. From what our guide said, it’s more for fans of the macabre and off-the-beaten track activities. This is typically something you need to arrange ahead of time, or go with a tour, given how far away it is.

The best time to visit will largely depend on what vibe you want. The weekdays are quieter, with fewer crowds and less traffic on the canals. This would be a good time to visit with young children. The weekends are much busier and festive, particularly in the afternoon. If you’re looking for a lively atmosphere, with music and drinks, go on a weekend afternoon. Public holdiays and Sundays are especially busy, so avoid this time if you want a quiet, peaceful trip.
I would say 2–3 hours, including boat time and walking around the local area, if enough time. If you’re planning to go to Isla de las Muñecas, consider spending the day here. Our time in Xochimilco was dictated by our tour guide, however, I didn’t feel like we were being rushed to see everything or that we spent too long in each place.
You don’t need to book in advance, but i’d recommend doing so if you’re going during a busy time (e.g. weekend or holidays). In addition, if you’re going in a big group, you’re more likely to get your preferred time slot and better deals if you book ahead. However, if you’re unsure on dates, you can just turn up and negotiate. Go directly to the boats, don’t accept offers from people trying to sell boat tours elsewhere. If you’re looking for themed experiences, or more boozy parties experiences, i’d book ahead with a tour. As my sister and I don’t speak Spanish, we went directly with a tour group who were able to organise the whole thing for us.
Yes, we definitely felt safe in the main waterfront areas. However, like with any city, it’s best to keep your valuables secure and be mindful of your surroundings.
Some boatmen might allow you to share with others, especially at busy docks. You can also ask around to split the cost. If you’re concerned about price, it might be worth looking into group tours there, as that’ll cover the cost of transport too, and might make it cheaper overall.
Yes, although it can get loud and boozy on weekends. Weekdays or mornings are better for families with children.
No. However, there are toilets along the waterfront that you can use before boarding. There were also a few stops along the canals also have basic facilities – however, our boat didn’t stop off anywhere, so I’d recommend using the toilets before departing.
I had such a great time visiting Xochimilco. It was the perfect escape from the busy city centre we were staying in and gave us a small glimpse into local life. If I could go back, i’d probably join one of the party boats to have a different experience. I would recommend going with a tour to help make the logistics of the day go smoothly – particularly if you don’t speak Spanish. It also kept the cost down for my sister and I, instead of paying for a full boat for just the two of us.
For other Mexico related posts, check our the best things to do in Holbox, things to do in Mexico City and some advice on how to travel between Tulum/Chetumal and Flores!
The post A Day Trip to Xochimilco, Mexico City appeared first on Char's Footsteps.
]]>The post The Pinnacles: The Perfect Perth to Pinnacles Day Trip appeared first on Char's Footsteps.
]]>I chose to do a day trip with a tour company, so they could deal with the logistics and driving. This is the specific one I did, but there will be lots to choose from if you do a Google search.
Given the star-gazing aspect of the trip, the tour started at midday. I was the first person to be picked up, then we spent about 30 minutes collecting others from their different accommodations around Perth CBD. My understanding is the tour will pick anyone up from the Freemantle or Perth CBD area. We had about 15 people in the group – all ranging from different ages and backgrounds. Our driver was hilarious and did a great job at connecting to the group together.
We then embarked on the 90 minute drive to our first stop – the Lancelin Sand Dunes! Our driver told us the tour had been shut down for the week prior, due to devastating bush fires that had broken out. I recommend if you’re doing this trip solo to be checking the status of the area.
The first proper stop on the tour was in Lancelin, for the white desert sands and sand-boarding. The Lancelin Sand Dunes are located about 1.5hrs north of Perth and are a fantastic stopover on the way to the Pinnacles. Spanning over 2km, these white sand dunes are breathtaking and offer great views of the surrounding coastline and area. They also make for a great spot for sand-boarding!!

For the sand-boarding, you can either bring your own board or rent your own once their. They were included in our tour, so we got many turns going down the powdery sand. I went down on my bum but a few brave people in our group stood up and boarded down. I had experienced sand-boarding in Wadi Rum, Jordan before but this felt very different due to the incredibly white sand! The dunes also too high, which meant we could walk up many times for multiple sand-boarding experiences…in Wadi Rum, I think I had the fitness to walk up the dunes twice! They also had different steepness, so if you’re an adrenaline junkie, you can find the harder ones compared to the flatter ones I went down.
It’s best to visit early morning or late afternoon, as the sand is cooler then and the sunlight will offer really pretty photo ops! My biggest tip for the Lancelin Sand Dunes is to bring eye protection. People in our group bought swim googles from the nearby shop or, like me, wore sunglasses. Even with sunglasses, I still got some sand in my eyes. You’ll need protection from sand going in your eyes, along with the glare from the sand…it’s completely white!!

The drive from Lancelin Sand Dunes to the Pinnacles took approximately an hour. We had another toilet break along the way at a public toilet. For anyone anxious about having enough toilet breaks, our guide was brilliant at checking in on anyone needing to stop and there were plenty of opportunities during the trip.
Once we arrived at the national park, we had an hour to walk round and explore the Pinnacles. It honestly felt otherworldly and was such a unique experience. Being able to walk around these giant rock formations as we neared sunset also provided us with the perfect lighting. After we had explored a bit, a few of us sat on one of the bigger rocks and watch the star of sunset. It felt extremely surreal.

Whilst our group enjoyed the Pinnacles, our brilliant guide set up a table of food, wine and soft drinks. At around 7:15, we slowly started coming back to our base to serve up a plate of food and get a drink, before sitting round in a circle underneath the stars. It was buffet style so you could pick and choose what you wanted from the amazing selection of food. There was ample seating provided, so it was a case of finding a spot and eating dinner under the stars!

One of the main reasons I signed up to this day trip to the Pinnacles was for the star gazing. Being from London, and only having lived in big cities, I’ve rarely seen a sky filled with stars! And this trip did not disappoint. Once we’d finished eating dinner, everyone passed round binoculars that our guide had provided. He also had one powerful telescope, that we all took turns queueing up for to see different constellations and planets that were pointed out to us. My highlights; seeing the Moon, Jupiter and Saturn!! According to our guide, on certain clear nights in the year, you can also see parts of the Milky Way and shooting stars.
I would recommend bringing warm clothing for this, because once the sun started to set, it was quite chilly. During the day, we wore shorts and t-shirts, but changed into leggings and jumpers for the evening. The guide did have some blankets to pass around, but everyone was saying how surprisingly cold it was. In addition to warm clothes/blankets, I would recommend brining a proper camera if possible. My iPhone really couldn’t capture what we were seeing well, so I just sat and enjoyed the skies. But it would have been nice to have a few good photos too.

Once we finished enjoying the night’s sky, we began the 2-2.5 hour journey back to Perth. Our driver was on high alert for wild animals who tend to jump into the road. I would warn people planning to do this trip without a guide to be extra alert during dusk/evening, given the number of animals that jump on and across the road. Our tour guide was telling us horror stories of people having bad crashes after colliding with Kangaroos and Wombats. We were dropped back at our individual accommodations around Perth CBD in the order we were picked up in…meaning I got back first! We got back fairly late, so I would plan to do this trip the day before something like an early morning flight.
All-in-all, the Perth to Pinnacles day trip far exceeded my expectations and I’m so glad I did it. It was definitely a highlight of my time in WA. I also loved visiting Fremantle, Rottnest Island amd exploring Perth during my visit.
The Pinnacles were formed over thousands of years through a combination of geological and environmental processes. These striking limestone formations, located in Nambung National Park, are thought to have originated from ancient seashells, rich in calcium carbonate, that were broken down into sand by natural forces.
Over time, these sands were compacted and cemented together to form limestone beneath layers of sand dunes. Wind and water erosion then played a critical role in exposing and sculpting the limestone into the dramatic pillars seen today. The surrounding vegetation, along with microbial activity, contributed to the development of hardened layers that protected the underlying structures, allowing the Pinnacles to stand resilient against erosion.
Cervantes is the nearest town to the Pinnacles and is home to a few restaurants, different accommodations, lookout points, beaches and the main Pinnacles visitor centre. We briefly stopped here during our tour to use the public restrooms.
It costs $15 AUD per person to enter the Pinnacles – this was included in our price for the day trip.
There’s a small gift shop nearby the entrance, however this closes at 4:30pm. So be sure to bring plenty of water and snacks with you for the duration of your stay. If you’re going in the day, bring sunscreen and a hat to protect you from the sun. If you’re going in the evening, bring warm clothes.
We spent a few hours there at that felt like enough! We were able walk the 1.5km circular route to see the incredible rock formations and have time to enjoy dinner under the stars.
The post The Pinnacles: The Perfect Perth to Pinnacles Day Trip appeared first on Char's Footsteps.
]]>The post Visiting the Lancelin Sand Dunes in Perth WA appeared first on Char's Footsteps.
]]>
For anyone looking to do this trip on a tour, this is the specific one I did and I had the best time!! A day of sand-boarding on the dunes, stargazing, the Pinnacles and a BBQ dinner in the desert.
The main things to do at the Lancelin Sand Dunes is sand-boarding. Similar to snowboarding but on sand, this activity involves sliding down the dunes on a specially designed board. Boards can be rented from nearby vendors in the town of Lancelin or directly at the dunes. These were provided for us during our day trip tour so I can’t comment personally on renting the boards. I believe boards can be rented for $10 an hour, between 10am-3pm. Check out the Lancelin site for more detailed information on renting boards if you’re doing this without a tour.
I was a bit nervous about doing this but the powdery sand, mixed with the varying heights of the dunes, made it suitable for anyone! I chose the small dunes, whilst others in the group chose the larger, steeper areas. For the sand-boarding, it’s best to bring sunglasses or goggles to protect your eyes from the glare and flying sand. Be sure to also bring plenty of water and sunscreen, as the dunes can get very hot, especially during the summer months.

For those with a four-wheel drive (4WD) vehicle, the Lancelin Sand Dunes offer an exciting opportunity to test your off-roading skills. The dunes’ varied terrain provides a challenge for both novice and experienced drivers. Make sure your vehicle is equipped for sand driving, and always deflate your tyres to the recommended pressure for better traction.
This area is a fantastic place for photographers! The pristine white dunes against the bright blue sky create stunning backdrops. Near to the dunes, is the Lancelin Lookout and a beach. These are also great to check out if you want to capture the landscape of the area.

For those seeking a quieter experience, the dunes also offer space to relax and take in the scenery. Bring a picnic and enjoy the peaceful surroundings! The little town nearby have shops like IGA and cafes, where you can buy food to take with you. Be sure to bring all your litter with you!!
The Lancelin Sand Dunes are located near the town of Lancelin, approximately 130km / 90 minutes north of Perth, Western Australia.
The easiest way to get there from Perth is to drive up the Mitchell Freeway North until you get to the Indian Ocean Drive. You’ll find the town of Lancelin about 6km off this road. Here you’ll be able to find the boards to rent for dune boarding, shops and facilities.
I strongly recommend brining sunglasses, and even goggles, to protect your eyes from the sand going into your eyes and from the white sand’s glare. Outside of this, bring the standard sunscreen, water and comfortable clothes.
No, access to the Lancelin Sand Dunes is free. However, fees will apply for board rentals or guided tours.
You can park at the base of the dunes and walk to the sand-boarding areas in any vehicle. However, if you plan to dive on the dunes themselves then you’ll need a 4WD vehicle.
Yes, the dunes are family-friendly, and children can enjoy sand-boarding on the smaller slopes. However, adult supervision is recommended, especially on steeper dunes.
The dunes can be visited year-round, but mornings and late afternoons are the best times to avoid the midday heat. Spring and autumn offer milder temperatures, making these seasons particularly pleasant. Be sure to check the weather closely prior to going! We had bush fires around the area in the days prior to going, which led to road closures to this area.
There are no facilities at the dunes themselves, but the nearby town of Lancelin has shops, cafes and rental services for equipment. e.g. for boards for the sand dunes or goggles to protect your eyes.
The post Visiting the Lancelin Sand Dunes in Perth WA appeared first on Char's Footsteps.
]]>The post A Complete Guide to Hiking Mount Ainslie in Canberra appeared first on Char's Footsteps.
]]>
Mount Ainslie is located just on the edge of Canberra’s city centre, making it very easy to get to. The easiest way would be to drive take Limestone Avenue towards the Australian War Memorial and follow signs for Mount Ainslie Drive. There’s a car park at the base near the Australian War Memorial or at the summit if you prefer to drive up and enjoy the views without the hike. I found it very easy to get taxis/ubers.
There isn’t direct public transport to the trailhead but you can get buses to the Australian War Memorial. From there, it’s a short walk to the start of the Mount Ainslie Walking Track. During my visit to Canberra, all buses were free of charge, so this made it an economical way to travel around the city!
Depending on where you’re staying within Canberra, you can walk there. The city has great paths and trails, making it easier to navigate most places by foot. As I had just done the Mount Ainslie Parkrun, I started the hike from there.

The Mount Ainslie Walking Track is a well-maintained trail, suitable for beginners and seasoned hikers alike. The most popular route starts at the rear of the Australian War Memorial and follows a clear path to the summit. It’s open year-round.
The trail is mostly uphill, with steady inclines and some steeper sections. It’s paved with gravel and has steps in parts, making it accessible and safe. I found the trail completely fine, but there was a set of steps towards the summit that were a little steep (pic for reference below).

The trail winds through bushland typical of the Canberra region, with eucalyptus trees and native shrubs. It’s supposed to be great for birdwatchers, with a variety including galahs, rosellas and cockatoos. I’m not really clued up on bird species, but saw pretty, colourful birds en route. I was told kangaroos can occasionally be seen in the mornings or evenings, but didn’t spot any on my hike.
There are interpretive signs along the trail, which provide interesting insights into Canberra’s history and design. It was nice to stop along the route to read more about the city, particularly its links to Walter Burley Griffin’s vision for the city.
Once you’ve reached the summit, you’ll be treated to an iconic, unobstructed view over Canberra. The central alignment of Anzac Parade, leading to Parliament House, is a highlight, showcasing the city’s unique layout. On clear days, you can see as far as Black Mountain and Lake Burley Griffin. It was definitely worth the sweaty walk up! There are a few facilities at the lookout point too. I heard great things about going the lookout at sunset for stunning, colourful views over the city. Unfortunately, I didn’t have time to go back in the evening but I’ve seen incredible sunset shots from this lookout and would try squeeze it into your itinerary if possible.

Lookout Platform: The summit has a large viewing platform with railings, perfect for photographs.
Picnic Area: There are benches and shaded areas for resting or enjoying a snack.
Information Boards: These provide details about Canberra’s landmarks and history.
Toilets: Available near the car park at the summit.
Water Fountain: There is a water fountain at the top (but not along the trail). I stupidly didn’t bring water so finding this was a big relief.

Here are a few extra tips to make your trip to Mount Ainslie more enjoyable:
Bring Water: My biggest tip is to bring water. Although there is a water fountain at the top, there are no refill stations on the trail itself. I went in November, when it wasn’t particularly hot, and regretted not having water with me within 10 minutes of the hike. There is a great fountain at the top, but best to be prepared and bring your own in case.
Wear Sturdy Shoes: The trail can be uneven in parts so it’s to wear proper walking, closed shoes. I saw some people struggling in flip flops (or thongs as the Aussies would say) along the trail. It’s also quite dusty which I imagine would make your feet quite gross.
Check Weather Conditions: Definitely check the weather ahead of the walk and avoid hiking in extreme heat or storms.
Start Early: Beat the crowds and enjoy the serene morning atmosphere.
Respect Nature: Stay on the trail and avoid disturbing wildlife.
It takes on average 1-2 hours to walk to the top. This will depend on your fitness levels and how many times you stop – there’s several great photo ops on the way up! I walked up in about 1hr 10 minutes, which included a few stops to take photos.
Yes, the trail is manageable for beginners with a moderate fitness level. It’s a steady uphill climb but definitely not overly challenging. There are also a number of spots along the trail where you can stop for a break if needed.
Dogs are allowed up Mount Ainslie as long as they’r’e on a leash. Ensure they’re comfortable with other hikers and wildlife.
The actual walking track isn’t suitable for prams or wheelchairs, due to the steps, uneven terrain and narrow paths at points. However the summit is accessible by car, so you can drive up to the top and enjoy the view from there.
You can technically go throughout the year, but spring (Sept-Nov) and autumn (Mar-May) offer comfortable weather. I haven’t visited Mount Ainslie during the winter, but I imagine this would be the least optimal time to go given the colder and darker weather. For colourful foliage, be sure to visit in the spring or autumn.
Yes, if you’re not feeling up to the hike, you can definitely drive to Mount Ainslie lookout. It’s a quick and easy drive from Canberra’s city centre with free parking at the top. You can also order Ubers from here, so you could hike up and get a taxi back down if you’re short on time.
Mount Ainslie is really pretty hike that’s definitely worth doing during a trip to Canberra. It provides a great opportunity to see some of the city’s natural beauty, learn about its history and get great views at the top. If you’re in the area, be sure to check out the Australian War Memorial – I would say out of all the war museums I’ve been to, this is my favourite one! If you’re short on time, and just want views over Canberra, you can get great views at the rooftop of Parliament House. For more information on the Mount Ainslie hike, and others in Canberra, check out this ACT government site.
Other great hikes I did during my stay in Canberra, included the trails at the National Arboretum Canberra. Outside of Canberra, some of my favourite hiking areas have included Blue Mountains in NSW and the 1000 steps hike in Melbourne.
The post A Complete Guide to Hiking Mount Ainslie in Canberra appeared first on Char's Footsteps.
]]>The post My Paris Marathon Experience appeared first on Char's Footsteps.
]]>After months of training, and a few setbacks (flu/knee problems), I set off for Paris on Friday, 1st April! One of my younger sisters kindly came with me to support. As I live in London, we decided it would be easiest to catch the Eurostar to Paris, instead of flying. We stayed at the Hotel Mercure Gare du Nord, right opposite the Eurostar. This turned out to be an ideal location as the Metro to the start/finish line was on our doorstep and getting back to London after the race was easy.
In order to collect your bib at the Paris Marathon Expo, you need to have a few extra bits! In order to participate in the Paris Marathon, a medical certificate was required. This is to prove you’re medically fit to run the race. I had to pay for a doctors appoitment to receive a signed certificate, stating “no contraindication to competitive running”. This is a mandatory step, to comply with French law, and has to be presented at the Paris Marathon Expo. You can’t collect your race bib without doing this.
Update: there has been an update in this process. The required medical certificate has now been replaced with a digital health declaration. Runners now have to complete an online form during registration, attesting to their fitness for long-distance running. Hopefully this makes the race easier to enter. Read more about this process on the official Paris Marathon site.
I knew if the Paris Marathon Expo was anything like the London Marathon Expo, it would get busy quickly! So we got up early on Saturday to head to the Salon du Running (Expo). The expo was located at the Porte du Versailles. It was quite an effort to travel to from the city centre and we somehow ended up on a tram at one point, but once there, picking everything up was extremely quick.
As stated earlier, you need to have your medical certificate, personal identification and your convocation form sorted ahead of time! They won’t issue your bib without these three things!! Once you’ve shown these documents, they’ll give you the race bib and chip timer. In addition, they give you a green backpack to store any belongings on race day.

The expo was a similar size to London, so after collecting the essentials, we walked around a bit. I’m terrible at race expos, as I just want to buy everything! I was fairly restrained during the expo and only bought a new pair of Skin compression sleeves. After, I headed to the KT stand to get my knee taped up as a precautionary. Seeing the finishers T-shirt and medal on display really got my nerves going. Although I had run the distance before, my training cycle hadn’t gone as smoothly this time. My knee issues had forced me to miss my 20-miler and a few other training runs, so I was feeling less prepared than when I ran the London Marathon the previous year.
We spent the rest of the day exploring Paris and eating. That evening I picked up some brown bread and jam from a local supermarket (my usual pre-race breakfast) and laid my race kit out. I didn’t have to worry too much about what to wear, as the weather forecast was for high temperatures and clear skies.

On marathon morning, I woke up at 6:30am, got dressed and had my breakfast. I barely slept from nerves and was now feeling unprepared for the race. It seemed a bit cold, but I stuck with my decision of wearing shorts based on the weather forecast. I always pack a bin bag to wear at the start line. This turned out to be a great decision, as it ended up being 24 degrees!
The marathon started at the Champs-Elysee, which was easy to reach via the metro. The atmosphere on the metro, and in the streets, was already building and my nerves started to melt away. Upon arriving at the Arc de Triomphe, we were greeted by utter chaos!!! All the roads were still open, so there were cars and buses everywhere, with people running through the traffic to get pre-race selfies with the Arc de Triomphe. The start corals, which go down the entire Champs-Elysees), were fenced off. However, there was no separate runners’ area (as with the London Marathon). Meaning there were people – friends, families, runners, officials, tourists, etc. – everywhere!!

Amongst this chaos, it wasn’t clear where the bag drop was, and the small bit of GCSE French I knew didn’t help. We scrambled our way through the crowds down the entire Champs-Elysees trying to find the bag drop, only to realise it was back at the top, about half a mile away from the metro. By this point, we were incredibly hot and stressed…not what you want just before a marathon. I managed to run back up to where the official bag drop was and check my bag in. The atmosphere was buzzing here and I couldn’t wait to get started.
The 4hr coral didn’t open for a while, so I waited with my sister amongst the other runners. All anyone could talk about was the heat and lack of clouds, which was beginning to concern me a bit. Once the pen opened, I went for a last minute toilet stop, which surprisingly only took five minutes to queue for, and made my way to the front of the pen. The atmosphere was great, with lots of music and the commentator speaking in an array of languages to get the crowds going.

I managed to get to the front of our coral, so after another short wait in the pen…we were off!
Within a few minutes of the race starting, I was already feeling hot. I knew this was going to be a lot harder than London the previous year. The crowds were out in large from the beginning, with flags from all countries being waved. Having crowds during a race always makes a difference and I’m always so grateful whenever someone shouts my name. The race started off downhill, heading towards the Place de la Concorde and then carried on towards Bastille. I had arranged to meet my sister at the 5km-point (Bastille), but delayed metros meant we just missed each other. As the race goes back past Bastille at 14 miles, I said I’d see her on the way back. Bastille was the first water stop: with bananas, buckets of raisins, orange slices and water.

A huge positive of the Paris Marathon is the incredible sites you pass along the way. I find it makes a real difference running a race with incredible sites and ‘attractions’ during the race. It almost felt like a walking (or running) tour of Paris.
The race then carried on East of Paris towards the ‘Bois de Vincennes’ park. This was a beautiful part of the race and a nice change of scene from the city streets. At this point, the heat was really affecting me and I was annoyed for not bringing more protection. Sun cream, a cap and sunglasses would have likely helped. Aside from the heat, I was running at a steady pace and my knee wasn’t causing any issues. My strategy at this point was to run a slower pace than in training to compensate the heat.

At mile 14, I saw my amazing sister. This gave me a desperately needed energy and motivation boost. All I could muster was that it was unbearably hot – which apparently all the runners had been saying to their families! It made such a difference knowing she was waiting on the course and helped to break down the miles mentally. At this point, I started noticing a familiar twinge in my knee…

After saying goodbye to my sister, the race took us towards some of Paris’s incredible sites. Some of my favourite were the Notre Dame, the Eiffel Tower and Musee d’Orsay. It was such an awesome way to see a city! It wasn’t long before we were coming up to the infamous tunnels. This was a part of the race I had been particularly dreading. I had heard negative things about the tunnels from other runners. Additionally, the only part of the London Marathon without crowds were the London tunnels. However, the tunnels turned out to be a great relief from the relentless heat. They were filled with relaxing music and ‘aromatherapy’ pictures. I was just glad to have a break from the direct sunlight and cool down a bit. The exit from the tunnels is uphill, but honestly you don’t need to worry about these.

For the few nasty hills coming out of the tunnels, the spirit of the marathon shone through! Everyone was helping each other out, despite language barriers! This French girl could see I was struggling and kept pointing at my water and ushering me to drink. We plodded alongside each other trying to communicate for about a mile. It never ceases to amaze me how total strangers are willing to help each other out during races. The heat was really starting to take a toll on people now, with many walking alongside the course. At several points, fire engines were lining the course spraying everyone with water!
After passing the main sites of Paris, we headed towards the Bois de Boulogne park. My knee was starting to really bother me. Apparently I saw and spoke to my sister at this point, but worryingly, I have no recollection of this. I think I was too exhausted from the heat. This part of the race was not great, and unfortunately, where everything went downhill.

The atmosphere in the park was dead – with no spectators or music. By now I’d say half the runners were walking and struggling their way through the heat. Two ambulances drove down the course to help those collapsed on the roadside. The roads were open to the public, so on multiple occasions, people would run or cycle directly across the course. The water stations, still chaotic as ever, were becoming dangerous with thrown water bottles and orange peels making the road extremely slippery. It was a very different experience to the last miles of the London Marathon! By now, I just wanted the race to be over

My strategy by now was to run-walk to the end. Listening to music and messaging my sister helped me through, but I felt disappointed with how the race had gone. It felt like my training for the sub-4 hadn’t paid off. I put that time goal out my mind and tried to enjoy what was left of the race.
At the 26-mile sign, we turned a corner and were suddenly opened to loads of spectators. And the end in sight!! I ran the last several hundred metres and finished in 4:57. Not the sub-4 hour I had hoped, or trained for, but I was relieved the marathon was over!

After crossing the finish line, I hobbled my way through the finish funnel. Collecting my goody bag, medal, my bag and then onto finding my sister amongst the crowds. The race gave out free Paris Marathon themed ponchos too – great for post-race warmth and as a memento. Bodies were sprawled across the floor as people refuelled on the free fruit and water that was handed out at the end.
After finding my sister, we went in search of some much-needed pizza! My legs were killing me but they improved significantly after putting on leg compression sleeves. I’m a huge advocate for using compression socks and leg sleeves on after any long distance. I found they definitely make a difference to recovery! Later that afternoon, we caught the Eurostar back to London. Although it made the day long, I was glad to be sleeping in my own bed that night.

Although I’m happy to have experienced the Paris marathon, I don’t think I would do it again. I found the admin before the marathon (e.g. the medical certificate) was a lot of effort. Particularly in comparison to other large marathon races. I also felt that parts of the day were disorganised and I disliked aspects of the course. The park area towards the end was especially boring to get through. Having said that, I would recommend this race to people wanting to experience a large marathon, with fairly easy entry. Races like the London or Berlin Marathon, are so hard to get into, so this is a great European alternative. The crowds were also pretty good!
Well done to everyone else who finished this 2016 race – it was a hot one!
The post My Paris Marathon Experience appeared first on Char's Footsteps.
]]>The post Bank Holidays 2026 and How to Maximise Your Annual Leave appeared first on Char's Footsteps.
]]>I’ve put this guide together to help break down exactly which days to book off in 2026 to maximise your time away. I’ve also included a few global desintation recommendations to visit for each period based on reliable weather. From Easter getaways and May sunshine to late-summer breaks and winter sun holidays over Christmas, here’s how to make your 2026 annual leave work harder…so you don’t have to!

The UK bank holidays in 2026 are as follows:

There are lots of great places to visit at this time of year. Countries in Asia with good weather at this time include Singapore, Malaysia and Bali. Morocco (Marrakech, Agadir), Cyprus, Canary Islands, Florida (other places in USA). A bucket list place for me would be the Japanese Cherry Blossoms!
A few favourites to visit In May include places in USA (West Coast National Parks) Turkey, Malta, Northern Australia and Queensland, Greek Islands (Crete, Rhodes, Santorini) and Italy (Sicily, Amalfi, Puglia))
I love using this time to visit places in Europe before it gets too hot or crowded! Porto, Lisbon (or anywhere in Portugal!), Rome, Croatia, South of France, and Spain are some of my favourites. China is also meant to be good at this time of year (along with Autumn).
The August Bank Holiday is a great time to travel as it’s usually towards the end of the school holidays and avoids the peak European heat. One of the best places I’ve been to during this time was Jordan! I was there for about 10 days, visiting places like Petra, Wadi Rum, Amman and Dana Biosphere Reserve. Other great places are the Greek Islands, Spain, Portugal, Canada (Vancouver, the Rockies) and Indonesia.
Travelling during the Christmas and New Year period is usually one of the most expensive times of year! Book ahead if you plan to go away during this time – particularly if you’re looking for a winter sun holiday. Great places to visit during this time include South Africa (Cape Town & the Garden Route), the Caribbean, the Maldives, Thailand (Phuket, Krabi, Bangkok), Mexico (Cancun, Tulum, Holbox), Hawaii, Sri Lanka, the Middle East (UAE, Qatar etc), and if you have more time, Australia and New Zealand!
Using this plan can stretch your annual leave in 2026 from 19 days to 46 days off! I recommend planning and booking early! Using bank holidays helps to maximise your annual leave but it’s the more expensive option.
The post Bank Holidays 2026 and How to Maximise Your Annual Leave appeared first on Char's Footsteps.
]]>The post The Best Things to Do in Auckland: A Visitor’s Guide appeared first on Char's Footsteps.
]]>The Auckland waterfront is a lively area with restaurants, bars, and scenic views. The Viaduct Harbour, in particular, has a vibrant atmosphere with plenty of dining options and a view of the marina. Visitors can enjoy a leisurely walk along the water, try the local seafood, or take a boat tour to see Auckland from the harbour. Regular ferries depart from here to nearby islands, which makes it a convenient spot for island hopping.
Located in the Auckland Domain park, the Auckland War Memorial Museum is one of the country’s most important cultural institutions. It offers a look at New Zealand’s history, including Māori and Pacific Islander cultures, early colonial history and natural history exhibits. The museum also has a moving memorial to New Zealanders who served in the world wars. I found it to be a fascinating visit, showcasing New Zealand’s heritage. There are often live cultural performances too – particularly during weekends! I highly recommend visiting if you’re in Auckland.
The Auckland Domain is a large public park set on the grounds of an extinct volcano. It’s Auckland’s oldest park and it’s ideal for walking, picnicking, or enjoying some nature! I would say the best thing to do is see the Wintergardens – a series of glasshouses filled with exotic plants and seasonal displays. I’m a big fan of Botanic Gardens, so I enjoyed this. The Auckland Domain is also home to sculptures and walking trails that offer views over the city and Waitematā Harbour.
Sky Tower is the tallest freestanding structure in the Southern Hemisphere and offers panoramic views over Auckland. You can take a lift to the observation deck, where there’s 360-degree views of the city, the harbour and the nearby islands. I am terrified of heights, so decided to sit this one out. But hostel friends I made raved about this for days after. They even have SkyJump for the adventurous people, where you can free-fall from the tower’s height!! The SkyWalk allows you to walk around the exterior platform of the tower at 192 metres high. Both of these sounded petrifying, but again, friends I made there said it was brilliant!
Wynyard Quarter is a waterfront neighbourhood in Auckland, with a mix of green spaces, waterfront views and a variety of eateries. Highlights include Silo Park, where you’ll find outdoor movies and events, or North Wharf, where you’ll get meals with a view. There are also a number of great seafood and local cuisine in this area too! I liked coming here for a chilled spot. enjoying the Auckland Harbour and watching the local boats.
Auckland is rich in Māori culture and there are several places where you can learn more about this incredible culture, including the Tāmaki Makaurau. There are guided tours offering insights into the city’s Māori history, including visits to significant cultural sites. You’ll find Māori cultural performances at the Auckland Museum, as well as carvings, art and traditional stories that give context to New Zealand’s indigenous history. Marae visits are also available, where you can gain a deeper understanding of Māori customs and traditions.
Waiheke Island is known for its beaches, wineries and relaxed island atmosphere. It’s a a short ferry ride from downtown Auckland, so it’s very easy to do as a day trip. The island has a variety of vineyards that offer wine tasting and cellar door experiences, alongside cafes and restaurants with views over the Hauraki Gulf. There are also a number of walking trails through native bush and coastal areas, as well as water sports like kayaking and paddleboarding. Unfortunately, I was too short on time to properly enjoy Waiheke Island, but if time permits, it’s a great place for a few days of enjoying slower pace of life and sample local wines.
The Auckland Art Gallery Toi o Tāmaki has the most extensive collection of national and international art in New Zealand. It includes works from well-known Māori and Pacific Island artists, as well as pieces from European and Asian art traditions. The gallery itself is an architectural blend of modern and historic elements, with temporary exhibitions and events throughout the year. It is free entry to the gallery, although some special exhibitions may have a fee. With over 15,000 works, Auckland Art Gallery is definitely worth checking out during your time in Auckland.
Mount Eden is a volcanic cone that offers one of the best vantage points in Auckland. A hike to the summit will reward you with panoramic views of the city, harbours and surrounding volcanic landscape. Honestly stunning!! The crater itself, known as Maungawhau, is a significant Māori site and holds cultural importance. It is a heritage site, so be sure to be respectful of the environment here. The walk to the top only takes about 10–15 minutes and is suitable for most fitness levels.
Ponsonby is an inner-city suburb, known for its vibrant atmosphere, boutique shops, cafes and restaurants. This neighbourhood is popular for its mix of modern and historic buildings and is definitely worth checking out during your visit to Auckland. The main Ponsonby Road is lined with bars, eateries and specialty stores. I was only there for 3 nights, but it seemed to come alive with a fun bar and live music scene.
Waitematā Harbour is a defining feature of Auckland’s landscape and a cruise is a great way to see the city from the water. Various cruises are available, from short tours to dinner cruises and sailboat rentals. The harbour cruise provides views of the city skyline, the Harbour Bridge, and the islands in the Hauraki Gulf. There seemed to be hands-on sailing experiences or even participate in races.

There are also a number of activities for families visiting Auckland! Auckland Zoo has a wide range of animals from around the world, with a big focus on conservation. The zoo is spacious and well-maintained, offering interactive exhibits and special animal experiences. With over 130 species here, it’s a great place to spend the day in Auckland. Another popular attraction for kids is SEA LIFE Kelly Tarlton’s Aquarium. This features underwater tunnels, penguin colonies and marine exhibits.
What is the best way to get around Auckland? Auckland has a comprehensive public transport system, including buses, trains, and ferries. The AT HOP card is a convenient option for multiple trips. Taxis and rideshare services like Uber are also widely available.
When is the best time to visit Auckland? Auckland is a year-round destination, but the warmer months (November to March) are ideal for enjoying outdoor activities. This is also peak tourist season, so booking in advance is recommended.
Are there beaches near Auckland? Yes, Auckland has several accessible beaches. Mission Bay and Takapuna Beach are popular for swimming and picnicking, while Piha and Muriwai on the west coast offer black-sand beaches and surfing opportunities.
Can you visit active volcanoes in Auckland? Auckland’s volcanoes are dormant, but they do provide excellent hiking and viewpoints. Rangitoto Island and Mount Eden are among the most popular spots for exploring volcanic landscapes!
While Auckland has a lot to offer, there are several day trip options for you to explore more of the surrounding region.
Waitakere Ranges: A short drive from Auckland, this regional park offers hiking trails, waterfalls and stunning lookouts. I would say highlights include the Karekare Falls and the black-sand beaches of Piha and Muriwai.
Hobbiton Movie Set: Fans of The Lord of the Rings films can visit the Hobbiton Movie Set in Matamata, roughly 2-hours away from Auckland. The set includes reconstructed hobbit homes and guided tours.
Coromandel Peninsula: Known for Hot Water Beach and Cathedral Cove, the Coromandel Peninsula is a great choice for a coastal day trip. The drive takes around two hours, but the beaches and scenic views definitely make it worthwhile.
Rangitoto Island: Accessible by ferry, Rangitoto is Auckland’s largest volcano. A hike to the summit offers panoramic views over the city and you can explore the island’s lava caves and coastal forests.
Rotorua: Roughly 3hrs from Auckland, Rotorua is known for geothermal attractions (e.g. geysers and hot springs), redwood forests and Māori cultural experiences. This was my ultimate favourite place during my trip to New Zealand! I recommend staying here for several days though (and not a day trip) as there’s a lot of nature to explore. Other great places to see Redwood Forests, outside of Rotorua, are at the Redwood Forests in Melbourne!
From Auckland, I made my way to Lake Taupo, to visit where my Great Grandpa and Grandma are from!!! Then onto my favourite place in the North Island of New Zealand…Rotorua! I found Auckland to be a great city, from a cultural and historic perspective and with the nice restaurants I got to try. I would say it’s not a place you need to spend huge amounts of time in during your visit to New Zealand. Base yourself there for a few days to explore the city and nearby places and then move onto other areas. Be sure to check out the Discover Auckland site for the latest things on in the city!
The post The Best Things to Do in Auckland: A Visitor’s Guide appeared first on Char's Footsteps.
]]>The post Best London Half Marathons appeared first on Char's Footsteps.
]]>The Big Half is an inclusive, fun, festival-like race that will take you through the streets of East London. Organised by London Marathon Events, the Big Half is a very well organised run with great atmosphere! The race starts near Tower Bridge and follows part of the London Marathon route, finishing in Greenwich. Running over Tower Bridge is an absolute highlight! You’ll be treated to great crowds along the route, cheering you along!
Be sure to spend some time exploring all the incredible things Greenwich has to offer!
Official Site: Big Half. Race Date: September
The London Landmarks Half Marathon is arguably one of the capital’s most iconic running races. As the name suggests, this 13.1 mile race will take you through the city’s famous landmarks. From the Tower of London to St. Paul’s Cathedral to Nelson’s Column, you’ll soak up the city’s rich history and culture throughout. It’s known for its lively themes and costumes and the event actually encourages participants to dress up! In the past there have been some iconic outfits, with one guy even dressing up as Big Ben. With its unique mix of running and sightseeing, its the perfect race for locals and visitors alike.

I found the crowd support during the race fantastic too, with spectators and race organisers cheering along the whole course!
Entry can be done via the ballot or through a charity place. It’s an extremely popular race, so consider other half marathons in London if you can’t get into this one.
Official Site: London Landmarks Half Marathon. Race Date: April
The Hackney Half is probably one of my favourite London Half Marathons. As the race is part of the Hackney Moves Festival, there is incredible pre and post running events to check out. I loved the race village and found the atmosphere to be electric! The race takes you through East London’s vibrant streets, with a lively crowd, music zones and local DJs. It feels more like a street party than a race and this is probably my favourite half marathon in London!

It was a bit hilly at points but still very enjoyable! The ballot entry goes very quickly, so be sure to enter early or sign up for a charity fundraising place.
Official Site: Hackney Half. Race Date: May.
Set in the scenic Richmond area, this half marathon starts in Kew Gardens and continues along the Thames, offering some of the city’s most picturesque views. The Richmond RunFest hosts a few events on the same day, including the 10km, Half Marathon and Full Marathon. This makes for great atmosphere amongst the runners and the supporters. You’ll pass through stunning scenery at the Royal Botanic Gardens and historical settings like Ham House, Richmond Palace and Teddington Lock. The race ends in Old Deer Park with live music and fitness expo vibes, similar to that of Hackney Half.
Places tend to go quickly so be sure to sign up well in advance.
Official Site: Richmond RunFest. Race Date: September
The Kingston Half Marathon offers a fast, scenic course along the River Thames. This race has become popular for those attempting a personal best time, given it’s relatively flat profile. The route covers sections of Kingston, Hampton Court and scenic riverside paths.
Official Site: Kingston Half Marathon. Race Date: October.
The Ealing Half Marathon is known for its friendly, community-driven atmosphere and has won awards for being one of the best half marathons in the UK. The course takes runners through suburban Ealing and is supported by enthusiastic locals. Although it’s not the most exciting route, the atmosphere is brilliant – particularly as it’s the largest closed-road race in West London. It’s also very well organised and supported.
Official Site: Ealing Half Marathon. Race Date: September.
The Royal Parks Half Marathon is famous for its stunning route through four of London’s Royal Parks – Hyde Park, Green Park, St. James’s Park, and Kensington Gardens. The race also goes by a few key London landmarks, like Buckingham Palace, Houses of Parliament, Trafalgar Square and the Admiralty Arch!
Taking place in October, it’s one of the most picturesque urban races. I have always loved running, or supporting, at this race with all the autumnal colours of the park. There’s a great mix of on-road running, with lots of fantastic crowd support, and parts on quieter paths through the parks. This is an extremely popular race, so the ballot typically fills up, but charity places are available for those who miss out.
Official Site: Royal Parks Half. Race Date: October.

The Greenwich Park Half Marathon is held in one of London’s stunning royal parks, with lush greenery and panoramic views of the city skyline. It’s a low-key race in comparison to the Big Half or LLHM, which makes it easier to enter. However, this is considered to be a more challenging race due to its hilly course and looped route. If you decide to do the half, you’ll be doing 6.5 laps of the course!
Having said that, it does offer a beautiful running setting along with multiple race distances. Along with the half, you’ll find 5km and 10km options too. I loved this as it meant I could sign up with friends who opted for shorter distances, and the lapped course meant we could have a group stationed in one place who could easily spot us all.
This race is also held a few times each year, with registration typically opening well in advance.
Official Site: Greenwich Park Half Race Date: Throughout Year
The Wimbledon Half is a smaller race with a friendly atmosphere, looping through the scenic grounds and parklands of Wimbledon. Known for its mix of road and trail sections, it’s a great choice for runners looking for a quieter experience. Held multiple times throughout the year, registration is open well in advance and much easier to get a place compared to the bigger half marathons in London.
Official Site: Wimbledon Half Race Date: October.
Starting and finishing at Hampton Court Palace, this unique race offers a historical backdrop combined with a scenic riverside route. There’s a great atmosphere at this race, with lots of people dressed up as historic figures, like Henry VIII! It’s a lovely half marathon race and has a perfect finish. Be sure to spend some time in Hampton area afterwards. Whether that be exploring Hampton Court Palace, or popping into Hampton itself and enjoy the cute pubs and cafes there.
Official Site: Hampton Court Palace Half. Race Date: March.
The Battersea Park Half Marathon is ideal for those seeking a smaller, looped course. Held within the flat grounds of Battersea Park, it offers runners a chance for a fast time on a straightforward route. I would say if you don’t like repetitive courses, this one probably isn’t for you…it’s 4.5 laps of the same loop. However, this can work best if you have a small cheer squad who can pick a point and guarantee to see you multiple times during the race.
Official Site: Battersea Half Race Date: Main event November, with others throughout Year.
Some of my favourite websites to find running races in London (and beyond) are Let’s Do This, Find a Race and Running Calendar! These will help you find races of all distances and regions.
Related: Singapore Half Marathon
The post Best London Half Marathons appeared first on Char's Footsteps.
]]>