CINXE.COM

HubSpot Community - About kennedyp - HubSpot Community

<!DOCTYPE html><html prefix="og: http://ogp.me/ns#" dir="ltr" lang="en" class="no-js"> <head> <title> HubSpot Community - About kennedyp - HubSpot Community </title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="icon" href="/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/16x16?v=v2"/> <meta content="Welcome to the HubSpot Community! Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot." name="description"/><meta content="width=device-width, initial-scale=1.0" name="viewport"/><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><link href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781" rel="canonical"/> <meta content="128" property="og:image:width"/><meta content="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2" property="og:image"/><meta content="community.hubspot.com" property="og:site_name"/><meta content="profile" property="og:type"/><meta content="72" property="og:image:height"/><meta content="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781" property="og:url"/><meta content="kennedyp" property="og:title"/><meta content="kennedyp" property="profile:username"/> <link href="/skins/6566941/cf3d09a7a299b32f8a1bbd2f844fb61f/hubspot.css" rel="stylesheet" type="text/css"/> <!-- Twitter Card metadata: For Japanese Blog Articles only --> <meta name="description" property="og:description" content="Welcome to my HubSpot Community profile!" /> <meta name="google-site-verification" content="JhdbLb5-5cPIvkeSYPUWX4n-wvBOsUlTzu7NjgCxLjQ" /> <script> window.$start = performance.now(); window.$stats = {}; // We need to define this BEFORE the lib is loaded so it initializes properly with this config window.__unocss = { theme: { breakpoints: { '@s': '(max-width: 479px)', '@m': '(max-width: 767px)', '@l': '(max-width: 1023px)', '@xl': '(min-width: 1200px)', }, px2rem: false, }, /** /* Within the RegEx rules below are named groups: /* - <d> stands for direction, think top, right, bottom, left, x, y /* - <g> stands for global, think values like auto|inherit|initial|revert|revert-layer|unset etc. /* - <m> stands for modifier, used for sub-properties like "-size" in "background-size" /* - <p> stands for property /* - <s> stands for selector where applicable /* - <u> stands for unit, default is px, no need to add that, for all other units, write them behind the value /* - <v> stands for value /*/ rules: [ // if you need a class to be !important simply prefix the class in HTMl with ! (it's automatic) // animation/transition utilities [/^a:(?<p>[a-z\-]+)?\/?(?<dur>[\d.ms]+)?\/?(?<e>[a-z\-]+)?\/?(?<del>[\d.ms]+)?$/, ([, p, dur, e, del]) => ( Object.entries({ 'transition-property': p || false, 'transition-duration': dur || '236ms', 'transition-timing-function': e || 'ease-in-out', 'transition-delay': del || false, }).reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = /^[\d\.]+$/.test(value) ? `${value}ms` : value.replace(/(?<!var\()(?<v>--\S+)/, `var(${value})`); } return rules; }, {}) )], // border utilities // This one is a bit special, it's basically able to set a variety of properties if specified, the pattern is like // `b:<direction><color>/<width>/<style>/<radius>` if you want to omit certain values, you need the slash (/), // but with no value in between, so let's say you only want to set the border-style, you would do `b:///dashed`, // only the border-width: `t:/3`, only border-top-color: `b:tred`, all properties support CSS custom properties // and properties that have numeric values support also `calc()`, e.g. `b:t--color-red` and `t:blue/calc(100px/5)` // The order of properties is different than standard CSS which is `<width><style><color>` as in my experience // 90% of border styling are color, but we mostly write `1px solid <color>`, which is annoying, so this utility // defaults to `border-width: 1px` and `border-style: solid` which can of course easily be changed, but doing simply // `b:green` will result in a 1px solid green border in all directions, which cuts down on how much we have to type // if you don't like these defaults, it's quite simple to change it to whatever behaviour you like! // @limitations: This utility does NOT deal with any kind of border-image stuff, I can't even remember when I have // used this feature last, so not going to include it as a utility, if you really want to use it, write CSS... // @note: The strange looking negative lookaheads in the regex below are there to deal with 'reserved' word like // `transparent` or all kinds of named colors that would otherwise get chopped up by the direction indicators // workaround is simply using a hex color with 0 alpha, e.g. #0000... // TODO: Add in support for `outline`, don't want to make a separate util for it as it is relatively similar to // a border and mostly just annoying default focus behavior for lazy people who don't care about :focus... // TODO: Add support for border radius, probably best to take everything at the end (e.g. after <style>) and // split it by slashes, discard anything more than 4 and then apply them as border-radius would be applied /* TODO: think about how to implement fake directional borders (use box-shadow outlines for full ones) &r-fake { position: relative; &:after { color: red; content: ''; position: absolute; top: 0; bottom: 0; width: 1px; } } */ [/^b:(?<d>[bi][se]|r(?![ego])|t(?![r])|l(?![aei])|b(?![eilru])|y(?![e])|x|o(?![lr]))?\(?(?<v>(?:[a-z\-]+?\([\S]+?\)|[^\s\(\)]+?)+?)\)?$/, ([, d, v]) => ( ({ t: ['-top'], r: ['-right'], b: ['-bottom'], l: ['-left'], x: ['-right', '-left'], y: ['-top', '-bottom'], is: ['-inline-start'], ie: ['-inline-end'], bs: ['-block-start'], be: ['-block-end'], }[d] || ['']).reduce((rules, dir) => ( (v || '') .split(/\/?(?<v>[a-z\-]+\([\S]+?\)+(?![,\)_])|[^\s\/]+)\/?/g) .filter((x) => x.trim()) // handles shortcut values for inherit (`i`) and skip (`x`) // you can optionally specify border radius separated by _: `.b:red/1/solid/1px_2px_3px_4px` // but it has to be specified with the unit! slash-notation doesn't need the unit, will default to px // there's a special class `.b:none` to remove a border, but `.b:x/0` is shorter .map((x, i) => /^[\d\.]+$/.test(x) ? `${x}px` : x.replace(/(?<!var\()(--[^\s,/]+)/g, 'var($1)').replace(/_/g, ' ').replace(/^i$/g, 'inherit').replace(/^c$/g, 'currentColor').replace(/^s$/g, 'solid').replace(/^x$/g, '')) // we want always minimum 3 values, as we want to set defaults for width/style if not defined .reduce((arr, val, i, values) => (values.length < 3 ? [...values, ...Array(3-Math.min(values.length, 3)).fill(null)] : values), []) .reduce((ret, val, i, arr) => ({ ...ret, ...({ 0: !['', 'none'].includes(val) ? { [({o: 'outline'}[d] || `border${dir}`) + '-color']: val } : {}, 1: ![''].includes(val) ? { [({o: 'outline'}[d] || `border${dir}`) + '-width']: arr[0] === 'none' ? '0' : val || '1px' } : {}, 2: ![''].includes(val) ? { [({o: 'outline'}[d] || `border${dir}`) + '-style']: val || 'solid' } : {}, 3: { 'border-radius': val }, 4: { 'border-radius': `${ret['border-radius']} ${val}` }, 5: { 'border-radius': `${ret['border-radius']} ${val}` }, 6: { 'border-radius': `${ret['border-radius']} ${val}` }, }[i] || {}) }), {}) ), {}) )], /* OLD border version [/^(?<p>b):(?<d>[bi][se]|r(?![ego])|t(?![r])|l(?![aei])|b(?![eilru])|y(?![e])|x)?(?<g>inherit|initial|revert|revert-layer|unset)?(?<outline>o(?![lr]))?(?<color>[^\/\s:@]*)?\/?(?<width>[\d.]+|--[^\/\s:@]+|calc\(.*?\))?(?<u>[a-zA-Z%]+)?\/?(?<style>[^\/]*)?\/?(?<radius>[\S]+)?$/, ([, p, d, g, outline, color, width, u, style, radius]) => ( { undefined: [''], t: ['-top'], r: ['-right'], b: ['-bottom'], l: ['-left'], x: ['-right', '-left'], y: ['-top', '-bottom'], is: ['-inline-start'], ie: ['-inline-end'], bs: ['-block-start'], be: ['-block-end'], }[d].reduce((rules, dir) => { // global values like auto, initial, revert will be captured by the <u> group Object.entries({ [(outline ? 'outline' : `border${dir}`) + '-color']: color || g ? `${color?.replace(/(?<!var\()(?<v>--\S+)/, `var(${color})`) || ( g || '')}` : false, [(outline ? 'outline' : `border${dir}`) + '-width']: `${width?.replace(/(?<!var\()(?<v>--\S+)/, `var(${width})`) || '1'}${u || (!width?.startsWith('--') && !width?.includes('calc') ? 'px' : '')}`, [(outline ? 'outline' : `border${dir}`) + '-style']: `${style?.replace(/(?<!var\()(?<v>--\S+)/, `var(${style})`) || 'solid'}`, 'border-radius': radius ? radius.split(/\//g).map((v) => (/^[\d\.]+$/.test(v) ? `${v}px` : v.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`))).join(' ') : false, }).forEach(([prop, value]) => { if ( value ) { rules[prop] = g ? g : value; } }); return rules; }, {}) )], */ [/^b:(?<c>circle)|(?:rad\(?(?<v>(?:[a-z\-]+?\([\S]+?\)|[^\s\(\)]+?)+?)\)?)$/, ([, c, v]) => ( c ? { 'border-radius': '50%' } : (v || '') .split(/\/?(?<v>[a-z\-]+\([\S]+?\)+(?![,\)_])|[^\s\/]+)\/?/g) .filter((x) => x.trim()) .map((x, i) => /^[\d\.]+$/.test(x) ? `${x}px` : x.replace(/(?<!var\()(--[^\s,/]+)/g, 'var($1)').replace(/_/g, ' ').replace(/^i$/g, 'inherit').replace(/^c$/g, 'currentColor').replace(/^x$/g, '')) .reduce((ret, val, i) => ({ ...ret, ...({ 'border-radius': `${ret['border-radius'] || ''} ${val}` }) }), {}) )], // background: shorthand and advanced multi-value uncovered sub-property accessor // @note: if you encounter problems consider disabling this rule and swap it out // for the commented out one below that just takes in custom properties [/^bg:(?<m>-[a-z]+)?_?(?<v>['"0-9A-Za-z .,\/()\-_!%#]+)$/, ([, m, v]) => ({ [`background${m || ''}`]: v?.replace(/_/g, ' ')?.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`) })], // background[-prop]: variable/custom property interpolation, use like `bg:-image--var-name` // @note: swap with rule above if that one creates problems, it does this as well // [/^bg:(?<m>-[a-z]+)?_?(?<v>--.*|none)$/, ([, m, v]) => ({ // [`background${m || ''}`]: /^none$/.test(v) ? 'none' : `var(${v})` // })], // background-color: Use custom property syntax for actual colors like `bg:--c-green` [/^bg:(?<v>current|transparent)$/, ([, v]) => ({ 'background-color': v?.replace('current', 'currentColor') })], // background-position: we only support the 1 value version with utils /* TODO: think about implementing these modifiers from the SCSS version @include modifiers(( 'b': 'bottom', 'c': 'center', 'l': 'left', 'lb': 'left bottom', 'lt': 'left top', 'r': 'right', 'rb': 'right bottom', 'rt': 'right top', 't': 'top', ), $properties: 'background-position', $prefix: 'pos-', $separator: '-'); */ [/^bg:(?<v>center|top|right|bottom|left)$/, ([, v]) => ({ 'background-position': v })], // background-repeat: we only support the 1 value version with utils [/^bg:(?<v>repeat-x|repeat-y|repeat|space|no-repeat)$/, ([, v]) => ({ 'background-repeat': v })], // background-size: only word values suported with utils [/^bg:(?<v>auto|cover|contain)$/, ([, v]) => ({ 'background-size': v })], // box-shadow [/^bs:(?<p>oi|o)?\/?(?<v>[^\s]+)?$/, ([, p, v]) => ({ // `/([^\s\/]+\([\S]+?\))|\//g` => splits by slash except when they are between parenthesis like `calc(100px/2)` // `b`/`o` and `oi` are shortcuts for box-shadow outlines/borders (they don't affect the box-model), `o` creates // and outline outside of the box, `oi` one that does not go beyond the box-boundaries. This can be useful // for scenarios where you want a border in a certain state but not others and then have to set // `border-color: transparent` on those elements. But box-shadow outlines can't be direction controlled, // so they are only useful if the element should have a border on all sides 'box-shadow': ({ 'o': '0 0 0 ', 'oi': 'inset 0 0 0 ', }[p] || '') + v.split(/([^\s\/]+\([\S]+?\))|\//g).filter((x) => x).map((x) => (/^[\d\.]+$/.test(x) ? `${x}px` : x.replace(/(?<!var\()(--\S+)/, `var(${x})`))).join(' ') })], // cursor: just make sure you got the value right, unrestricted for brevity sake [/^c:(?<v>\S+)$/, ([, v]) => ({ 'cursor': v })], // display: There's a lot of different utilities summarized under the d: prefix, see preflights // still working on figuring out the best way to implement them as some might still require // browser prefixes, or target multiple properties etc. [/^(?<p>bg|d):(?<filter>blur|brightness|contrast|drop-shadow|grayscale|hue-rotate|invert|opacity|saturate|sepia)\(?(?<v>calc\([\S]+?\)|[^\s)]+)?\)?$/, ([, p, filter, v]) => ({ [`${p === 'bg' ? 'backdrop-' : ''}filter`]: `${filter}(${v?.startsWith('calc') ? v : v?.replace(/\//g, ' ') || ''})` })], ['d:b', { 'display': 'block' }], // `d:c(<name>/<type>)` || `d:c(<name>)` || `d:c` // https://developer.mozilla.org/en-US/docs/Web/CSS/container [/^d:c(?:\((?<n>[^\s\/]+)\/?(?<t>[^\s\/]+)?\))?$/, ([, n, t]) => ({ 'container': `${n || 'x'} / ${t || 'inline-size'}` })], ['d:i', { 'display': 'inline' }], ['d:ib', { 'display': 'inline-block' }], ['d:none', { 'display': 'none' }], ['d:hide', { 'visibility': 'hidden' }], ['d:show', { 'visibility': 'visible' }], ['d:invisible', { 'border': '0', 'clip': 'rect(1px, 1px, 1px, 1px)', 'height': '1px', 'outline': 'none', 'overflow': 'hidden', 'padding': '0', 'position': 'absolute', 'width': '1px', }], /* 1 */ ['d:f', { 'display': 'flex' }], ['d:fi', { 'display': 'flex inline' }], ['d:f-col', { 'flex-direction': 'column' }], ['d:f-row', { 'flex-direction': 'row' }], ['d:f-wrap', { 'flex-wrap': 'wrap' }], ['d:fc-items-center', { 'align-items': 'center' }], ['d:fc-justify-between', { 'justify-content': 'space-between' }], ['d:fc-justify-center', { 'justify-content': 'center' }], // If functions are used the value wrapping with () is mandatory! `d:f(col/1/0/calc(3px*100))` [/^d:f\(?(?<dir>col|row)?\/?(?<grow>[\d.]+)?\/?(?<shrink>[\d.]+)?\/?(?<basis>[\S]+)?\)+?$/, ([, dir, grow, shrink, basis]) => ( Object.entries({ 'flex-direction': { col: 'column', row: 'row' }[dir] || false, 'flex-grow': grow || false, 'flex-shrink': shrink || false, 'flex-basis': /^[\d\.]+$/.test(basis) ? `${basis}px` : (basis || '').replace(/(?<!var\()(?<v>--\S+)/, `var(${basis})`) || false, }).reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = value; } return rules; }, {}) )], // order matters here, needs to be below the general `d:f` rule [/^d:f-(?<v>none|auto|initial)$/, ([, v]) => ({ 'flex': v })], // grid/flexbox gap, be careful though when using `d:g` it needs the gap defined within it's values // to properly calculate column width! so specify gap there if you use `d:g(<values>)`, it's fine to // use `d:gap` if you use `d:g` (without values) and define your template-columns yourself via CSS [/^d:gap(?<row>calc\([\S]+?\)|[^\s\/]+)?\/?(?<col>[^\s]+)?$/, ([, row, col]) => ( Object.entries({ 'row-gap': col ? row : false, 'column-gap': row ? col : false, 'gap': (row && !col) ? row : false, }).reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = /^[\d\.]+$/.test(value) ? `${value}px` : value.replace(/(?<!var\()(?<v>--\S+)/, `var(${value})`); } return rules; }, {}) )], // flexbox/grid align and justify utils to be used like: // `.d:jself(center) or .d:j-self(center) or .d:js(center) or .d:aitems(start) or .d:a-items(start) or .d:ai(start)` [/^d:(?<d>a|j)-?(?<scope>c|i|s|t|content|items|self|tracks)[\(-](?<v>\S*?)\)?$/, ([, d, scope, v]) => ( { [`${{ a: 'align', j: 'justify' }[d]}-${{ c: 'content', i: 'items', s: 'self', t: 'tracks' }[scope] || scope}`]: v } )], // grid: format `d:g` to just set the display property, `d:g(<template-cols>/<gap/row-gap>?/<col-gap>?/<template-rows>?)` // TODO: Keep an eye on `grid-template-rows: masonry` support, it would be awesome, but is not supported as of 2023 // it's technically implemented as the 4th via `grid-template-rows`: That one is a bit different, can't do it with // optional gap values, so even if no gaps should be defined, it needs to be written like `d:g(12/0/0/masonry)` // there is a special value `equal` that will make all rows equal height to the tallest one: `d:g(12/0/0/equal)` // the 4th value is basically a free-for-all, you can go crazy with stuff like `repeat(auto,minmax(calc(100vh/3),1fr)))` // to define the grid-template-rows, I use it rarely, but it's there if needed. [/^d:(?<p>g|gi)(?:\((?<v>[^\s]+)?\))?$/, ([, p, v], ctx) => ( ({ 'g': [ ['display', 'grid'] ], 'gi': [ ['display', 'inline-grid'] ], }[p] || []).reduce((rules, [prop, value]) => { if ( value ) { //console.log('rule matcher args', p, v, ctx); // set the display property rules[prop] = value; // parse the grid config values and inject additional properties into the outer reduced array // depending on how many config values were provided, do not give unit to first value automatically // as it is the number of grid-columns and needs to be a unitless value for the internal calc(), it // can be a custom CSS property/variable or calc() resolving in a unitless number itself though, // meaning: `d:g(--var-cols/12px/calc(100px/4))` or `d:g(calc(48/4)/12px/calc(100px/4))` are fine. // Does it make sense to calc() within calc()? not sure... but it's possible ;). // If the first value has a unit, no automatic cols calculation will happen, but the value will // be used as a min-width of a grid-column letting the browser do the heavy lifting, this can be // very helpful not having to define any kind of @media queries for responsiveness! // `d:g(264px)` => `grid-template-columns: repeat(auto-fit, minmax(264px, 1fr));` // auto-fill vs auto-fit: https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/ if ( v ) { v = v.split(/\/?(?<v>[a-z\-]+\([\S]+?\)(?![,\)])+|[^\s\/]+)\/?/g).filter(x => x).map((x, i) => /^[\d\.]+$/.test(x) && i !== 0 ? `${x}px` : x.replace(/(?<!var\()(?<v>--\S+)/, `var(${x})`)); // The last value can be 'fit' or 'fill' regardless of position to select auto-filling algo const f = /(fit|fill)/.test(v.at(-1)) ? v.pop() : 'fit'; const props = ({ 1: [['grid-template-columns', `repeat(auto-${f}, minmax(${/[\d\.]+[a-z%]+$/.test(v[0]) ? v[0] : `calc(100%/${v[0]})`}, 1fr))`]], 2: [['grid-template-columns', `repeat(auto-${f}, minmax(${/[\d\.]+[a-z%]+$/.test(v[0]) ? v[0] : `calc(100%/${v[0]} - ${v[1]})`}, 1fr))`], ['gap', v[1]]], 3: [['grid-template-columns', `repeat(auto-${f}, minmax(${/[\d\.]+[a-z%]+$/.test(v[0]) ? v[0] : `calc(100%/${v[0]} - ${v[2]})`}, 1fr))`], ['row-gap', v[1]], ['column-gap', v[2]]], 4: [['grid-template-columns', `repeat(auto-${f}, minmax(${/[\d\.]+[a-z%]+$/.test(v[0]) ? v[0] : `calc(100%/${v[0]} - ${v[2] || 0})`}, 1fr))`], ['row-gap', v[1]], ['column-gap', v[2]], ( v[3] === 'equal' ? ['grid-auto-rows', '1fr'] : ['grid-template-rows', ( v[3] ? v[3].replace(/_/g, ' ') : null )] )], }[Math.min(v.length, 4)] || []).forEach(([_p, _v]) => (rules[_p] = _v)); } } return rules; }, {}) )], [/^d:(?:g-?)?(?<p>col|row)\(?(?<v>\S+?)\)?$/, ([, p, v]) => ({ [`grid-${p === 'col' ? 'column' : 'row'}`]: v.replace(/_/g, ' ') })], // grid col/row span // regex captures everything after `d:span<v>` (or within parenthesis `d:span(<v>)`), syntax is: // variant a): `d:span<v[<col[<start>-<end?>]>/<row?[<start>-<end?>]>]>` // variant b): `d:span(<v[<col[<start>-<end?>]>/<row?[<start>-<end?>]>]>)`, // we deal with the value inside the matcher function and split it into it's components // TODO: Re-evaluate if the separate util is worth it as we can simply use `.d:g-col(<v>` and `.d:g-row(<v>)` [/^d:(?:g-?)?span\(?(?<v>(?:[a-z\-]+?\([\S]+?\)|[^\s\(\)]+?)+?)\)?$/, ([, v]) => ( (v || '') .split(/\/?(?<v>[a-z\-]+\([\S]+?\)+(?![,\)_])|[^\s\/]+)\/?/g) .filter(x => x.trim()) // don't need the auto-pixelator here, span values are unitless, but we support CSS vars //.map((x, i) => /^[\d\.]+$/.test(x) ? `${x}px` : x.replace(/(?<!var\()(--[^\s,/]+)/g, 'var($1)').replace(/_/g, ' ')) .map((x, i) => x.replace(/(?<!var\()(--[^\s,/]+)/g, 'var($1)')) .reduce((rules, value, i) => ({ ...rules, ...({ // if useing CSS vars, specify the full value, like this it's most flexible 0: { 'grid-column': value.includes('var(') ? value : value.split('-').map((x, i, a) => a.length === 1 ? `span ${x}` : x ).join(' / ') }, 1: { 'grid-row': value.includes('var(') ? value : value.split('-').map((x, i, a) => a.length === 1 ? `span ${x}` : x ).join(' / ') }, }[i] || {}) }), {}) )], ['d:noverflow', { 'overflow': 'hidden' }], // order (flexbox/grid), syntax: `d:order(?-?<v>)?` [/^d:order\(?(?<v>[-\d]+)\)?$/, ([, v]) => ({ 'order': v })], // display => overflow(-[x|y]) auto [/^d:scroll(?<m>-[xy])?$/, ([, m]) => ({ [`overflow${m || ''}`]: 'auto' })], ['d:scroll', { 'overflow': 'auto' }], ['d:scroll(x)', { 'overflow-x': 'auto' }], ['d:scroll(y)', { 'overflow-y': 'auto' }], // it's a bit tricky to yield those ::selectors, but this is how it can be done... // question is: should we just preflight those as raw CSS instead of doing constructs like this? // the strings ship with the bundle, one way or the other, but as preflights at least the utils // are readable... and after all, there's nothing dynamic about them, simply creating overhead here // futhermore it's questionable to do it like that as it will most likely result in more // characters/bytes shipped than if it was just pure CSS... [/^(?<sel>d:scroll-nobar)$/, ([, sel], context) => { return `${context.constructCSS({ 'scrollbar-width': 'none', /* Firefox */ '-ms-overflow-style': 'none', /* IE 10+ */ })}\n.${CSS.escape(sel)}::-webkit-scrollbar { width: 0; height: 0; }` /* WebKit */ }], // pointer-events [/^e:(?<v>\S+)$/, ([, v]) => ({ 'pointer-events': v })], // list-style: order of values is type | image | position, escape quotes with ^ [/^l:(?<v>.{3,}?)?(?:_(?<url>.+?))?(?:_(?<pos>outside|inside))?$/, ([, v, url, pos]) => ({ 'list-style': `${v?.replaceAll('^', '"')} ${url || ''} ${pos || ''}` })], // margin and padding: supports basics plus custom properties (variables), optional directions and global word values [/^(?<p>m|p):(?<d>r(?!e)|[ltbxy]|[bi][se])?(?<v>(?:(?:-(?!-))?[\d._]+)|--\S+|calc\(.*?\))?(?<u>[a-zA-Z%]+)?$/, ([, p, d, v, u]) => ( { undefined: [''], t: ['-top'], r: ['-right'], b: ['-bottom'], l: ['-left'], x: ['-right', '-left'], y: ['-top', '-bottom'], is: ['-inline-start'], ie: ['-inline-end'], bs: ['-block-start'], be: ['-block-end'], }[d].reduce((rules, dir) => { // global values like auto, initial, revert will be captured by the <u> group // TODO: not sure what the `.replaceAll('_', ' ')` is for, maybe a copy paste leftover from another rul? rules[`${{m: 'margin', p: 'padding'}[p]}${dir}`] = `${v?.replaceAll('_', ' ')?.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`) || ''}${u || (!v?.startsWith('--') && !v?.includes('calc') ? 'px' : '')}`; return rules; }, {}) )], // opacity // technically the spec allows for percantage values, but we convert anything to float, makes things easier [/^o:(?<v>[\d.%]+|--\S+|calc\(.*?\))?$/, ([, v]) => ({ 'opacity': isNaN(parseFloat(v)) ? (v?.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`) || '') : ( parseFloat(v) <= 1 ? parseFloat(v) : parseFloat(v)/100 ) })], // position utils [/^(?<p>pos):(?<d>r(?![e]|$)|[ltb])?(?<v>[\S]+)?$/, ([, p, d, v]) => ( { 'a': [ ['position', 'absolute'] ], 'f': [ ['position', 'fixed'], ['backface-visibility', 'hidden'] ], 'r': [ d ? ['right', null] : ['position', 'relative'] ], 's': [ ['position', 'sticky'] ], 'center': [ ['position', 'absolute'], ['top', '50%'], ['left', '50%'], ['transform', 'translate(-50%, -50%)'] ], 'center-x': [ ['position', 'relative'], ['left', '50%'], ['transform', 'translateX(-50%) perspective(1px)'] ], 'center-y': [ ['position', 'relative'], ['top', '50%'], ['transform', 'translateY(-50%) perspective(1px)'] ], 'reset': [ ['position', 'static'] ], // direction utils, set the value falsy so we can set it within reduce 't': [ ['top', null] ], 'b': [ ['bottom', null] ], 'l': [ ['left', null] ], undefined: [], }[d || v].reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = value; } else if ( d && v ) { rules[prop] = /^[\d\.]+$/.test(v) ? `${v}px` : v.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`); } return rules; }, {}) )], /** /* FLUID MEDIA CONTENT UTILS (IMG, VIDEOS, IFRAMES etc.) /* 1. Element will be stretched to the full extend of the nearest realtively-positioned /* ancestor. /* 2. Element will be stretched to the entire viewport and follow the user's scrolling, /* good for modal windows and overlays /* 3. Add this class to the element that contains the fluid media content and do not forget /* to define the aspect ratio like `.scale:frame(16/9)`. /* 4. Add this class to the element that should scale in a specific ratio, useful for /* fluid videos, iframes (maps anybody?), embeds but also images. /* 5. Allows an image to be responsvie up to its container width but not exceeding /* it's native size. /*/ [/^scale:(?<v>[^\s(]+)\(?(?<ratio>[\d\/]+)?\)?$/, ([, v, ratio]) => ( { 'fit': [ ['bottom', '0'], ['left', '0'], ['margin', 'auto'], ['position', 'absolute'], ['right', '0'], ['top', '0'] ], /* 1 */ 'fullscreen': [ ['backface-visibility', 'hidden'], ['bottom', '0'], ['left', '0'], ['margin', 'auto'], ['position', 'fixed'], ['right', '0'], ['top', '0'] ], /* 2 */ 'frame': [ ['display', 'block'], ['position', 'relative'], ['aspect-ratio', (ratio ? `${ratio}` : null) ] ], /* 3 */ // The padding hack is old-school now, we now have native aspect ratio, but for reference // I'll leave those here anyways, the calculation goes as follows: `9 / 16 * 100% = 56.25%` // So if a custom aspect ratio is needed one could simply apply `p:tcalc(2/12*100%)` //'frame(16/9)': [ ['padding-top', '56.25%'] ], //'frame(3/2)': [ ['padding-top', '66.66666%'] ], //'frame(4/3)': [ ['padding-top', '75%'] ], 'content': [ ['bottom', '0'], ['left', '0'], ['margin', 'auto'], ['position', 'absolute'], ['right', '0'], ['top', '0'] ], /* 4 */ 'img': [ ['display', 'block'], ['height', 'auto'], ['max-width', '100%'] ], /* 5 */ undefined: [], }[v].reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = value; } return rules; }, {}) )], /** /* TEXT UTILS /* /* 1. Enables font kerning in all browsers. /* see also: http://blog.typekit.com/2014/02/05/kerning-on-the-web/ /* 2. Ensure that the node has a maximum width after which truncation can occur. /* 3. Fix for IE 8/9 if 'word-wrap: break-word' is in effect on ancestor nodes. /* 4. A little helper to increase the font-weight slightly without having to rely /* on font-style. Especially useful to increase readability of very small type! /*/ // This one is a bit special, it's basically able to set a variety of properties if specified, the pattern is like // `t:<color>/<font-size>/<line-height>/<font-weight>/<font-style>/<font-family>` if you want to omit certain values, // you need the slash (/), but with no value in between, so let's say you only want to set the font family, // you would do `t://///Arial`, only the font-weight: `t:///700`, only the color: `t:red`, all properties // support CSS custom properties and properties that have numeric values support also `calc()`, e.g. `t:--color-red` // and `t:blue/calc(100px/5)/calc(100px/4)` // TODO: support letter-spacing here or in separate utility? // TODO: support other functions than calc(), clamp() in particular, but basically all CSS math functions [/^(?<p>t):(?<color>[^\/\s:@]*)?\/?(?<size>[\d.]+|--[^\/\s:@]+|calc\(.*?\))?(?<u>[a-zA-Z%]+)?\/?(?<lh>[\d.]+|--[^\/\s:@]+|calc\(.*?\))?(?<lhu>[a-zA-Z%]+)?\/?(?<weight>[\d.]+|--[^\/\s:@]+|calc\(.*?\))?\/?(?<style>[^\/\s:@]*)?\/?(?<family>[^\/\s:@]*)?$/, ([, p, color, size, u, lh, lhu, weight, style, family]) => ( Object.entries({ 'color': color ? `${color?.replace(/(?<!var\()(?<v>--\S+)/, `var(${color})`) || ''}` : false, 'font-size': size || size === '0' ? `${size?.replace(/(?<!var\()(?<v>--\S+)/, `var(${size})`) || ''}${u || (!size?.startsWith('--') && !size?.includes('calc') ? 'px' : '')}` : false, 'line-height': lh || lh === '0' ? `${lh?.replace(/(?<!var\()(?<v>--\S+)/, `var(${lh})`) || ''}${lhu || ''}` : false, 'font-weight': weight ? `${weight?.replace(/(?<!var\()(?<v>--\S+)/, `var(${weight})`) || ''}` : false, 'font-style': style ? `${style?.replace(/(?<!var\()(?<v>--\S+)/, `var(${style})`) || ''}` : false, 'font-family': family ? `${family?.replace(/(?<!var\()(?<v>--\S+)/, `var(${family})`) || ''}` : false, }).reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = value; } return rules; }, {}) )], // font-family // This utility isn't really meant to define full font-stacks, you may try to use the general utility // like `t://///Arial` even though it's questionable to use like that. By default it just applies the global CSS // keywords for font-family, including such as ui-serif etc. which are not supported by browsers today // the power of this one is that it will also look for pre-defined custom CSS properties/variables at // :root level with the specific pattern of `---t-family-<keyword>`, if found it will use the variable // value. In this way font stacks can easily be defined via such variables. Besides the standrad spec // keywords, it also supports some custom short ones: sans => sans-serif, mono => monospace, ui => system-ui // TODO: Make variable names configurable via theme or at least the prefix and allow passing in the specific // `--t-family-<name>` variable via CSS class as well, like this we could reference full font stacks without // making things too ugly? How can we access theme within rules? [/^t:(?<v>serif|sans-serif|sans|monospace|mono|cursive|fantasy|system-ui|ui|ui-serif|ui-sans-serif|ui-monospace|ui-rounded|emoji|math|fangsong)$/, ([, v]) => ({ 'font-family': !!getComputedStyle(document.documentElement).getPropertyValue(`---t-family-${v}`) ? `var(---t-family-${v})` : v.replace(/^(sans|mono|ui)$/gi, x => ({'sans': 'sans-serif', 'mono': 'monospace', 'ui': 'system-ui'}[x])) })], // font-style // Shortcuts for what is doable with the 'general' utility above as well, the key word `normal` // is already used by the font-weight utility below, so `t:regular` is used to set `font-style: normal` // specifying the angle for `font-style: oblique 20deg` is supported as well with `t:oblique/20` // valid angle values are +-90deg, but most fonts don't support it anyways... [/^t:(?<v>italic|oblique|regular)\/?(?<deg>-?\d{1,2})?$/, ([, v, deg]) => ({ 'font-style': `${v.replace('regular', 'normal')}${deg ? ` ${deg}deg`: ''}` })], // font-weight // Those are shortcuts which can all be achieved by using the above 'general' utility as well, but they // are still included for convenience, lighter/bolder are relative-to-parent font-weights (see MDN) // this util has a feature where it looks for custom CSS properties (variables) defined at `:root` level // that match the specific pattern of `---t-weight-<word|weight>`, if found, it will use those instead of // the actual word or numeric weight, this allows to re-define what those words/weights mean in terms of // actual font-weight, e.g. if default for `font-weight: normal` is 400, if you set `---t-weight-normal: 300` // at `:root` level and use the utility `t:normal` on some element, it's font-weight will now be 300 // these custom CSS properties/variables are intentionally prefixed with 3! dashes (---) to hopefully avoid // any conflicts with 'regularly' defined custom CSS variables which accidentially have the same name! [/^t:(?<v>lighter|bolder|thin|normal|bold|heavy|\d00)$/, ([, v]) => ({ 'font-weight': !!getComputedStyle(document.documentElement).getPropertyValue(`---t-weight-${v}`) ? `var(---t-weight-${v})` : v.replace('thin', '100').replace('heavy', '900') })], // `t:boldest` applies a text-shadow which adds to the visual boldness of text regardless of font-weight applied ['t:boldest', { 'text-shadow': '0 0 0.3px currentColor' }], // text-align: we only support the 1 value version with utils, the string thing seems fringe anyways // additionally we convert left/right 'absolute' values to start/end which is text-direction aware // `justify-all` and `match-parent` are in the spec but seem very poorly supported! [/^t:(?<v>start|end|left|right|center|justify|justify-all|match-parent)$/, ([, v]) => ({ 'text-align': v.replace('left', 'start').replace('right', 'end') })], // text-decoration // We don't support multiple text-decoration-line values, if you really need that, specify it via multiple classes // text-decoration-skip is also not supported as browser support is 0, but text-decoration-skip-ink is supported // There is a shortcut `t:del` which translates to `t:line-through` automatically. // `blink` is deprecated and only Opera and Safari still support it...We don't, it's bullshit UX anyways... // A text-decoration-line value has to be defined, otherwise this util won't catch, the other additional properties // can be omitted, the format is `t:<decoration-line>/<style>/<color>/<thickness>/<skip-ink> [/^t:(?<line>none|underline|overline|line-through|del)\/?(?<style>solid|double|dotted|dashed|wavy|--[^\/\s:@]+)?\/?(?<color>[^\/\s:@]*)?\/?(?<width>[\d.]+|--[^\/\s:@]+|calc\(.*?\))?(?<u>[a-zA-Z%]+)?\/?(?<skip>none|auto|all|--[^\/\s:@]+)?$/, ([, line, style, color, width, u, skip]) => ( Object.entries({ 'text-decoration-line': line ? `${line?.replace(/(?<!var\()(?<v>--\S+)/, `var(${line})`).replace('del', 'line-through') || ''}` : false, 'text-decoration-style': style ? `${style?.replace(/(?<!var\()(?<v>--\S+)/, `var(${style})`) || ''}` : false, 'text-decoration-color': color ? `${color?.replace(/(?<!var\()(?<v>--\S+)/, `var(${color})`) || ''}` : false, 'text-decoration-thickness': width || width === '0' ? `${width?.replace(/(?<!var\()(?<v>--\S+)/, `var(${width})`) || ''}${u || (!width?.startsWith('--') && !width?.includes('calc') ? 'px' : '')}` : false, 'text-decoration-skip-ink': skip ? `${skip?.replace(/(?<!var\()(?<v>--\S+)/, `var(${skip})`) || ''}` : false, }).reduce((rules, [prop, value]) => { if ( value ) { rules[prop] = value; } return rules; }, {}) )], // text-tranform [/^t:(?<v>capitalize|caps|lowercase|lcase|uppercase|ucase)$/, ([, v]) => ({ 'text-transform': v.replace('caps', 'capitalize').replace('lcase', 'lowercase').replace('ucase', 'uppercase') })], ['t:break', { 'word-wrap': 'break-word' /* 4 */ }], ['t:nowrap', { 'white-space': 'nowrap' }], // TODO: not sure if this even does something visually, what is it's purpose? remove it? ['t:kern', { '-webkit-font-feature-settings': '"kern" 1', 'font-feature-settings': '"kern" 1', '-webkit-font-kerning': 'normal', 'font-kerning': 'normal', 'text-rendering': 'optimizeLegibility' }], // 1 ['t:truncate', { 'max-width': '100%' /* 2 */, 'overflow': 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap', 'word-wrap': 'normal' /* 3 */ }], // transform // transform function values are very diverse, just write them as usual separated with commas just NO spaces! // You can use `calc()` or custom css properties `--some-var` if they are allowed to be used for function values // we can't cast non-pixel values to pixels either, because different functions have different inputs, like `scale()` // for example expects unitless values, so for the values, you do need to specify the unit if appropriate // Does NOT support specifying multiple transform functions in one call, write proper CSS for a use-case like that! // Does support transform-origin/box/style with the appropriate prefix `b|o|s` e.g. `.tr:o(center/50px)` // note that the classname consistently has to encapsulate the value in parenthesis, e.g. `.tr:rotate(120deg)`, but // also `.tr:o(--some-var/calc(100px/2))` => `transform-origin: var(--some-var) calc(100px/2);` // The order of the transform functions in the RegEx matters, so don't touch! [/^tr:(?<p>[bo]|s(?![ck]))?(?<fn>matrix3d|matrix|none|perspective|rotate3d|rotateX|rotateY|rotateZ|rotate|scale3d|scaleX|scaleY|scaleZ|scale|skewX|skewY|skew|translate3d|translateX|translateY|translateZ|translate)?\((?<v>[^\s]+)?\)$/, ([, p, fn, v]) => ({ [{ b: 'transform-box', o: 'transform-origin', s: 'transform-style', undefined: 'transform', }[p]]: p // `/\/(?![\S]+\))/g` => splits by slash except when they followed by `)` like `calc(100px/2)` ? v.split(/\/(?![\S]+\))/g).map((x) => (/^[\d\.]+$/.test(x) ? `${x}px` : x.replace(/(?<!var\()(--\S+)/, `var(${x})`))).join(' ') : fn.match(/none/) ? fn : `${fn}(${v?.replace(/(?<!var\()(?<v>--\S+)/g, `var(${v})`)})` })], // width & height [/^(?<p>w|h):(?<m>min|max|screen)?(?<v>[\d.]+|--\S+|calc\(.*?\))?(?<u>[a-zA-Z%]+)?$/, ([, p, m, v, u]) => ( { undefined: [''], min: ['min-'], max: ['max-'], screen: [(rules) => (rules[`${{w: 'width', h: 'height'}[p]}`] = `100v${p}`)] }[m].reduce((rules, mod) => { if ( mod instanceof Function ) { mod(rules); return rules; } // global values like auto, initial, revert will be captured by the <u> group // replacing _ with ' ' allows for escaping required spaces in calc +/- operations like calc(100% - 32px) rules[`${mod}${{w: 'width', h: 'height'}[p]}`] = `${v?.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`).replaceAll('_', ' ') || ''}${u || (!v?.startsWith('--') && !v?.includes('calc') ? 'px' : '')}`; return rules; }, {}) )], [/^z:(?<v>.+)$/, ([, v]) => ({ 'z-index': v.replace(/(?<!var\()(?<v>--\S+)/, `var(${v})`) })], // debug/dev tools, simply add the class `?` to an element and it will make itself very visible ;) // It might very often be the case that you need to overrule existing styles that conflict with the // dev tool class, so just prefix with `!` to make everything !important. [/^(\?)$/, (_, { constructCSS, generator }) => ( `@keyframes __imhere{0%{box-shadow:inset 0 0 0 2px red}100%{box-shadow:inset 0 0 0 6px yellow}}\n${constructCSS({ animation: '__imhere 0.5s ease-in-out alternate infinite' })}` )], // create a box around an element, good for highlighting stuff // arrows can be useful for screenshots, try it out with `?[]>`, `?[]^`, `?[]<`, `?[]>t` etc. [/^(?<sel>\?\[(?<inset>[0-9-]+)?\/?(?<width>\d+)?\](?:(?<arrow>\>|\<|\^)(?<top>t)?)?\/?(?<hue>\d+)?\/?(?<opacity>[\d.%]+)?)$/, ([, sel, inset, width, arrow, top, hue, opacity], ctx) => ( `${ctx.constructCSS({ overflow: 'visible !important', position: 'relative', })}${ arrow ? `\n.${CSS.escape(sel)}:before { color: hsl(${hue || 0} 100% 50%) !important; content: '↗' !important; font-size: 36px !important; font-weight: 300 !important; line-height: 1 !important; position: absolute; ${top ? `top: -${Math.abs(inset || 0)}px` : `bottom: -${Math.abs(inset || 0)}px`}; ${arrow === '<' ? `right: -${Math.abs(inset || 0)}px` : `left: ${ arrow === '^' ? '50%' : `-${Math.abs(inset || 0)}px` }`}; transform: translate(${arrow === '<' ? '150%' : `${arrow === '^' ? '-50%' : '-150%'}`}, ${ top ? '-125%' : '125%'}) ${arrow === '<' ? 'rotate(-90deg)' : arrow === '^' ? 'rotate(-45deg)' : ''} ${ top ? `scale(${arrow === '<' ? '-1,1' : arrow === '^' ? '-1' : '1,-1'})` : ''}; z-index: 9999 !important; }` : ''}\n.${CSS.escape(sel)}:after { box-shadow: inset 0 0 0 ${Math.abs(inset || 0)}px hsl(${hue || 0} 100% 50% / ${opacity || .1}) !important; content: '' !important; inset: ${inset || 0}px !important; margin: 0 !important; padding: 0 !important; outline: ${width || 1}px solid hsl(${hue || 0} 100% 50%) !important; position: absolute !important; transform: none !important; z-index: 9999 !important; }` )], // and the dev grid overlay [/^(?<sel>\?#\(?(?<s>\d+)?\/?(?<o>[\d.]+)?\/?(?<h>[\d]+)?(?<r>r)?\)?)$/, ([, sel, s, o, h, r], ctx) => ( `${ctx.constructCSS({ 'position': 'relative', })}\n.${CSS.escape(sel)}:before { background-image: linear-gradient(hsl(${ h ? h : '0'} 100% ${ h ? '50%' : '0%'} / ${ o ? o : '.12'}) 1px, transparent 1px), linear-gradient(90deg, hsl(${ h ? h : '0'} 100% ${ h ? '50%' : '0%'} / ${ o ? o : '.12'}) 1px, transparent 1px); /*background-position: -1px -1px, -1px -1px;*/ background-size: ${ s ? `${s}px ${s}px, ${s}px ${s}px` : '24px 24px, 24px 24px'}; box-shadow: inset 0 0 0 1px hsl(${ h ? h : '0'} 100% ${ h ? '50%' : '0%'} / ${ o ? o : '.12'}); content: ''; position: absolute; top: 0; bottom: 0; left: 0; right: 0; width: 100%; z-index: 10000; }\n.${CSS.escape(sel)} > [class*="${CSS.escape('b:')}"] { border-color: red !important; border-radius: 0 !important; }\n.${CSS.escape(sel)} > :not([class*="${CSS.escape('b:')}"]) { border-radius: 0 !important; box-shadow: inset 0 0 0 1px red !important; }${ r ? `\n.${CSS.escape(sel)}:after { background-image: linear-gradient(hsl(0 100% 0% / 1) 1px, transparent 1px), linear-gradient(90deg, hsl(0 100% 0% / 1) 1px, transparent 1px), linear-gradient(90deg, hsl(0 100% 0% / 1) 1px, transparent 1px), linear-gradient(-90deg, hsl(0 100% 0% / 1) 1px, transparent 1px); background-position: left bottom; background-repeat-y: no-repeat; background-size: ${ s ? `${s/2}px 6px, ${s/2}px 6px, ${s}px 11px, 100% 11px` : '6px 6px, 6px 6px, 12px 11px, 100% 11px'}; content: attr(data-width); display: block; font-size: 10px; line-height: 1; padding-bottom: 16px; position: absolute; top: 0; left: 0; text-align: center; transform: translateY(-125%); width: 100%; }` : '' }` )], ], shortcuts: [ // you could still have object style /*{ 'forum-nav-bar': '!bg:cyan', },*/ // dynamic shortcuts //[/^btn-(.*)$/, ([, c]) => `bg-${c}-400 text-${c}-100 py-2 px-4 rounded-lg`], ], variants: [ // Allows targeting child/sub elements of the element the util is applied to // Any valid combinator (or none) are supported, just add the child selector between pipes `|` // and add the util after, give that class to the wrapping element of whatever should be targeted // this helps a lot for use-cases where every child (imagine a ul>li structure) should get the // same styles, with regular utils, every li has to have all util classes, which is very // redundant and one of the major downsides of utility-based CSS approaches. With the help of this // variant this is a thing of the past as only one class has to be defined on the parent targeting // any and all decendent nodes with the appropriate selector. // `|>li|bg:red` => `.\|\>li\|bg\:red > li { background-color: red; }` { name: 'combinators', match: (matcher) => { const rx = /^\|(?<combinator>[>+~])?(?<selector>\S+)\|(?<util>\S+)$/; if ( !rx.test(matcher) ) { return matcher; } const { groups: { combinator, selector, util } } = matcher.match(rx); // the combinator is optional, but makes no sense to continue without selector or util if ( !selector || !util ) { return matcher; } //console.log('Found combinator match', `(selector) ${combinator} ${selector}`, util); return { matcher: util, selector: (s) => `${s} ${combinator || ''} ${selector}`, } }, multipass: false, //order: -1, }, // Targets basically every advanced CSS selector and pesudo content if they are prefixed with a colon `:` // this is very powerful as it allows targeting stuff like `:not(:last-child)` purely through CSS // classes, like `.b:b1/red::not(:last-child)` or `.t:bold::after` the double colon separator is needed // so we can actually do stuff like `:not(:last-child)`, which wouldn't work with a single colon (or put // differently: I'm too dumb to figure out the regex to do that!) // TODO: Figure out why those don't work in conjunction with combinator variants { name: 'pseudo', match: (matcher, ctx) => { const rx = /^[^:|]+:[^:]+(?<pseudoclass>:\S+)$/; //if ( !/:(:.+)$/.test(matcher) ) { return matcher; } if ( !rx.test(matcher) ) { return matcher; } const { groups: { pseudoclass } } = matcher.match(rx); // You can define any custom pseudo-classes and their selector interpolations here const custom = function(pc, s) { return { ':hocus': `${s}:hover, ${s}:focus`, ':hocus-within': `${s}:hover, ${s}:focus-within`, }[pc]; }; //console.log('Pseudoclass match found', matcher, pseudoclass); return { // slice pseudo-class and pass to the next variants and rules matcher: (pseudoclass ? matcher.slice(0, -(pseudoclass.length)) : matcher), selector: (s) => (pseudoclass ? custom(pseudoclass, s) || `${s}${pseudoclass}` : s), } }, // doesn't really work yet, probably my fault (regex?), not that important, specify two classes meanwhile multipass: false, //order: -1, }, // Converts `©(width>500px)` to `@container(width>500px)` to be dealt with by the atrule variant // => meh: I prefer the shortcut atrules like @c, @s, @l... /*{ name: '©rules', match: (matcher, ctx) => { if ( !matcher.includes('©') ) { return matcher; } return { matcher: matcher.replace(/©/g, '@container'), } }, multipass: false, },*/ // @rules { name: '@rules', match: (matcher, ctx) => { const rx = /^\S+:\S+(?<atrule>@[^:]+)/; if ( !rx.test(matcher) ) { return matcher; } const { groups: { atrule } } = matcher.match(rx); /** /* We want to support a variety of @rules, not all make sense to be specified via classes /* but the goal is to support: /* - pre-configured theme breakpoints which are mapped to @media, they are simply a string /* with the exact syntax of a regular CSS media query, this allows for complex breakpoints /* with logical operators etc. without creating a massive parsing overhead here. /* - on-the-fly evaluated breakpoints to be specified like CSS 4 range queries: /* `@(<|<=|=|>=|>)<number><unit>(width|height)(<|<=|=|>=|>)<number><unit>` supporting `width` and `height` /* There's a limit on how far I think it makes sense to go with supporting the offical spec /* it would get highly complex to implement all of it, so for now as we want to translate /* those expressions into the wider supported min-<prop> max-<prop> syntax, once browser support /* for range syntax is not that recent anymore, it should be easy to simply evaluate and forward /* complex range syntax queries with multiple conditions and operators. An implementation detail /* worth noting is the use of `=` (which usually makes 0 sense for media queries, who wants to /* specify an exact pixel value where styles apply?) to communicate values for props, so for example /* you'd specify the orientation feature (normally `@media (orientation: landscape)`) like /* `@orientation=landscape` or `@media(orientation=landscape)` or `@(orientation=landscape) (all valid) /* - @supports(display=grid) => https://css-tricks.com/how-supports-works/ /* - @layer(name), /* - @container((<|<=|=|>=|>)<number><unit>(width|height)(<|<=|=|>=|>)<number><unit>), /* => if none of those 3 keywords are found, @media, is assumed by default, as values can /* contain colons (:) we 'escape' those within the CSS class names with `=` as the colon /* is already used for pseudo classes this goes also for stuff like `@supports(selector(=last-child))` /* and is then translated to `@supports (selector(:last-child))` /*/ function transform(atrule) { //console.log('transform', atrule, ctx.theme.breakpoints[atrule], ctx.theme); // Check theme config for matching breakpoint (with or without the @) and return that early if ( ctx.theme.breakpoints[atrule] || ctx.theme.breakpoints[atrule.substring(1)] ) { return `@media ${(ctx.theme.breakpoints[atrule] || ctx.theme.breakpoints[atrule.substring(1)])}`; } let { groups: { at, rule } } = atrule.match(/^@(?<at>media|c|container|l|layer|s|supports)?(?<rule>\S+)$/); // Map @rule shortcuts like @c, @l, @s, those are different from theme breakpoints as they have a value! // => @l(<layer-name>), @s(display=grid), @c(width>618px) at = { c: 'container', l: 'layer', s: 'supports', }[at] || at; const operators = { '&&': () => ') and (', '||': () => '), (', '!': () => '), not all and (', '<': (p, inv) => (!inv ? `max-${p}: ` : `min-${p}: `), '<=': (p, inv) => (!inv ? `max-${p}: ` : `min-${p}: `), '=': () => ':', '>=': (p, inv) => (!inv ? `min-${p}: ` : `max-${p}: `), '>': (p, inv) => (!inv ? `min-${p}: ` : `max-${p}: `), }; let invert = false; let property = 'width'; // Unwarp @rule from parenthesis if it comes in wrapped rule = (rule.startsWith('(') ? rule.substring(1, rule.length-1) : rule); // Deal with simple values with no operators like @320px, prefix so it transforms to `max-width` // this allows something like `bg:red@320` to translate to `@media (max-width: 320px)` rule = rule.replace(/^(?<v>[\d.]+)(?<u>[a-z%]{1,4})?$/, (m, v, u) => `<${v}${u || 'px'}`); // Deal with range syntax, anything else won't be matched, transform into CSS3 conditions // don't check for proper units here, stop being lazy and write them... rule = rule.replace(/(?<left>[^<>=\s]+)?(?<oleft><=|>=|=|<|>)?(?<prop>width|height)(?<oright><=|>=|=|<|>)(?<right>[^<>=\s]+)/, (match, left, oleft, prop, oright, right) => { invert = !!oleft; property = prop; return `${oleft||''}${left||''}${oleft ? '&&' : ''}${oright}${right}`; }); // Use `.split()` with a RegEx and a capturing group to include the separators const parts = rule.split(/([^<>=&|!]+)?(&&|\|\||!|<=|>=|=|<|>)([^<>=&|!]+)/).filter(x => x); const result = parts.reduce((r, v, i, arr) => { if ( operators[v] ) { r.push(operators[v](property, invert)); // Only applies to range syntax, we have to invert the first operator, then reset the var invert = false; } else { // Resolve custom CSS properties/variables, we can't use them in @rules // a bit like https://github.com/WolfgangKluge/postcss-media-variables if ( v.startsWith('--') ) { v = getComputedStyle(document.documentElement).getPropertyValue(v).trim() || `${v}__var-undefined`; } // Cast unitless numeric values to pixels r.push((/^[\d\.]+$/.test(v) ? `${v}px` : v)); } // Add closing parenthesis if we reached the end (i == arr.length-1 && !(at || '').includes('layer')) && r.push(')'); return r; }, ['@', (at || 'media'), `${!(at || '').includes('layer') ? ' (' : ' '}`]).join(''); //console.log(result); return result; } return { // slice @rule and pass to the next variants and rules matcher: matcher.replace(atrule, ''), //selector: (s) => s, handle: (input, next) => { //console.log('input', input, 'next', next); return next({ ...input, parent: `${input.parent ? `${input.parent} $$ ` : ''}${transform(atrule)}`, }) }, } }, multipass: true, }, // Adds !important to a rules resulting CSS if the class is prefixed with !, e.g. `!t:red` { name: 'important', match: (matcher, ctx) => { if ( !matcher.startsWith('!') ) { return matcher; } return { matcher: matcher.slice(1), // body is an array of tuples like `[ [<prop>, <value>], [<prop>, <value>] ]` body: (body) => { body.forEach(([prop, val], i, arr) => { arr[i] = [prop, ( val ? val += ' !important' : val )]; }); return body; }, } }, multipass: false, }, ], preflights: [ { layer: 'recss', //getCSS: async () => (await fetch('my-style.css')).text(), // `:` (colon) needs double escaping when used in template literal! e.g. `<foo>\\:<bar>` getCSS: ({ theme }) => ` /** /* Little pseudo content helpers. /*/ [data-before]:before { content: attr(data-before); } [data-after]:after { content: attr(data-after); } [data-class]:after { content: attr(class); } /** /* Yes, invalid attribute, but no browser cares, useful for easily showing/hiding entire blocks /* based on FreeMarker/JS conditions. Use like <div class="..." if="{somebooleanexpression?c}">. /*/ [if="false"] { display: none !important; } /** /* Use for elements that should only be visible when handled by JavaScript, it's the JS code's /* responsibility to remove this class once it has done whatever it's doing. Useful for /* pre-rendering markup in FreeMarker and then progressively enhance it with JS. /*/ .js--only { display: none !important; } /** TODO: IMPLEMENT THESE DYNAMICALLY! **/ /** /* LAYOUT, DISPLAY & POSITIONING UTILS /* 1. Completely remove from the flow but leave available to screen readers. /* 2. Fix for Firefox bug: an image styled 'max-width:100%' within an /* inline-block will display at its default size, and not limit its width to /* 100% of an ancestral container. /* 3. The space content is one way to avoid an Opera bug when the /* 'contenteditable' attribute is included anywhere else in the document. /* Otherwise it causes space to appear at the top and bottom of the /* element. /* 4. The use of 'table' rather than 'block' is only necessary if using /* ':before' to contain the top-margins of child elements. /* 5. Make sure fixed elements are promoted into a new layer, for performance /* reasons. /* 6. Element will be absolutely centered inside the nearest relatively-positioned ancestor. /* 7. Element will be centered horizontally regardless of width. Setting 'transform: perspective(1px)' /* prevents element from being blurry if positioned on a "half-pixel", alternatively, setting /* 'transform-style: preserve-3d;' on the parent element has the same effect! /* 8. Element will be centered vertically regardless of height. Setting 'transform: perspective(1px)' /* prevents element from being blurry if positioned on a "half-pixel", alternatively, setting /* 'transform-style: preserve-3d;' on the parent element has the same effect! /* 9. Fix for Chrome 44 bug. https://code.google.com/p/chromium/issues/detail?id=506893 /* 10. Setting percentage height is rather rare, not worth the bloat of all utility classes /*/ .d\\:ib-fix { font-size: 0; line-height: 0; } .d\\:ib-fix > *, .d\\:ib-fix *:before, .d\\:ib-fix *:after { font-size: initial; line-height: initial; vertical-align: middle; } .d\\:f-col-r { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -webkit-flex-direction: column-reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } .d\\:f-row-r { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -webkit-flex-direction: row-reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } .d\\:f-wrap-r { -webkit-flex-wrap: wrap-reverse; -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } .d\\:fc-items-start { -webkit-box-align: start; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; } .d\\:fc-items-end { -webkit-box-align: end; -webkit-align-items: flex-end; -ms-flex-align: end; align-items: flex-end; } .d\\:fc-items-baseline { -webkit-box-align: baseline; -webkit-align-items: baseline; -ms-flex-align: baseline; align-items: baseline; } .d\\:fc-items-stretch { -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } .d\\:fc-justify-start { -webkit-box-pack: start; -webkit-justify-content: flex-start; -ms-flex-pack: start; justify-content: flex-start; } .d\\:fc-justify-end { -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; } .d\\:fc-justify-around { -webkit-justify-content: space-around; -ms-flex-pack: distribute; justify-content: space-around; } .d\\:fc-justify-evenly { -webkit-box-pack: space-evenly; -webkit-justify-content: space-evenly; -ms-flex-pack: space-evenly; justify-content: space-evenly; /* not supported in Edge! */ } .d\\:fc-content-start { -webkit-align-content: flex-start; -ms-flex-line-pack: start; align-content: flex-start; } .d\\:fc-content-end { -webkit-align-content: flex-end; -ms-flex-line-pack: end; align-content: flex-end; } .d\\:fc-content-center { -webkit-align-content: center; -ms-flex-line-pack: center; align-content: center; } .d\\:fc-content-between { -webkit-align-content: space-between; -ms-flex-line-pack: justify; align-content: space-between; } .d\\:fc-content-around { -webkit-align-content: space-around; -ms-flex-line-pack: distribute; align-content: space-around; } .d\\:fc-content-stretch { -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-content: stretch; } .d\\:fi-self-start { -webkit-align-self: flex-start; -ms-flex-item-align: start; align-self: flex-start; } .d\\:fi-self-end { -webkit-align-self: flex-end; -ms-flex-item-align: end; align-self: flex-end; } .d\\:fi-self-center { -webkit-align-self: center; -ms-flex-item-align: center; align-self: center; } .d\\:fi-self-baseline { -webkit-align-self: baseline; -ms-flex-item-align: baseline; align-self: baseline; } .d\\:fi-self-stretch { -webkit-align-self: stretch; -ms-flex-item-align: stretch; align-self: stretch; } .d\\:fi-grow { -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; min-width: 0; /* 9 */ min-height: 0; /* 9 */ } .d\\:table { display: table; } .d\\:table-collapse { border-collapse: collapse; } .d\\:table-separate { border-collapse: separate; } .d\\:table-spacing { border-spacing: 1px; } .d\\:td { display: table-cell; } .d\\:tr { display: table-row; } .d\\:left { float: left; } .d\\:right { float: right; } .d\\:clear:before, .d\\:clear:after { content: " "; /* 3 */ display: table; /* 4 */ } .d\\:clear:after { clear: both; } .d\\:valign-baseline { vertical-align: baseline; } .d\\:valign-top { vertical-align: top; } .d\\:valign-middle { vertical-align: middle; } .d\\:valign-bottom { vertical-align: bottom; } .d\\:valign-all-baseline > * { vertical-align: baseline; } .d\\:valign-all-top > * { vertical-align: top; } .d\\:valign-all-middle > * { vertical-align: middle; } .d\\:valign-all-bottom > * { vertical-align: bottom; } /** /* Skeleton loader styles /* @credits: https://css-tricks.com/a-bare-bones-approach-to-versatile-and-reusable-skeleton-loaders/ /* Multi-row skeleton loaders can be added by adding <br/> elements assuming that all /* child (content) elements get replaced with actual content once loading is done. /*/ .is--loading, .is--loading * { pointer-events: none; user-select: none; cursor: default; } .is--loading .sk\\:el { animation: 2s sk\\:progress linear infinite; background-size: 200% 100%; /* for loading animation */ background: var(--c-skeleton-bg); background: var(--c-skeleton-gradient); border-color: rgba(0,0,0,0) !important; border-radius: 2px; color: rgba(0,0,0,0) !important; } .is--loading .sk\\:el:is(.sk\\:static) { animation: none !important; background: var(--c-skeleton-bg); } @media (prefers-reduced-motion) { .is--loading .sk\\:el { animation: none !important; background: var(--c-skeleton-bg); } } /* Make sure all child elements are hidden, but preserve their dimensions and layout */ .is--loading .sk\\:el * { visibility: hidden; } /** * Make sure that an element has at least a whitespace character as a child so it displays * properly. This is useful when no text placeholder is present (element is empty). */ .is--loading .sk\\:el:empty:after, .is--loading .sk\\:el *:empty:after { content: ' '; /* Can't use 00a0 (octal escape sequences) in template literls! */ } @keyframes sk\\:progress { to { background-position-x: -200%; } } ` }, ], /*transformers: [ { name: 'my-transformer', enforce: 'pre', // enforce before other transformers idFilter(id) { // only transform .tsx and .jsx files //return id.match(/\.[tj]sx$/) //console.log('transformer: idFilter', id); return true; }, async transform(code, id, { uno }) { // code is a MagicString instance //console.log('transformer: transform()', code, id, uno); }, } ],*/ /* preprocess: (t) => { // for example prefix all classes with ! which makes them !important (bad idea!) if (t.includes('!')) return t; return `!${t}`; }, */ // convert pixels to rem for all utils postprocess: (util, ...args) => { if ( !window || !window.__unocss?.theme?.px2rem ) { return } const px = /(-?[\.\d]+)px/g; const reminpx = parseFloat(getComputedStyle(document.documentElement).fontSize); util.entries.forEach((i) => { const value = i[1]; if ( typeof value === 'string' && px.test(value) ) { i[1] = value.replace(px, (_, v) => `${v / reminpx}rem`); } }); }, /*extractors: [ // This is the default split extractor of UnoCSS, comment out what is below if you encounter // issues and try this one, it's very crude... but works with more or less false positives // depending on the markup your are working with { name: '@unocss/core/extractor-split', order: 0, extract({ code }) { const defaultSplitRE = /[\\:]?[\s'"`;{}]+/g; function splitCode(code) { return code.split(defaultSplitRE); }; console.log('split extractor code', typeof code, code.split(defaultSplitRE)); return splitCode(code) }, }, { name: 're:css', order: 0, extract({ code }) { const classes = []; for ([_, q, c] of code.matchAll(/(?:class\s*?=\s*?)(["'])((?:(?=(?:\\)*)\\.|.)*?)\1/gi)) { classes.push(...c.split(/\s+/g)); } console.log('re:css extractor classes', classes); return classes; }, } ],*/ // disable the default extractor //extractorDefault: false, // override the default extractor // This one WILL FAIL if you do not quote your class attribute values (as you should anyways)! // But this extractor is 5-10x faster than the default extractor extractorDefault: { name: 're:css', order: 0, extract({ code }) { //const start = performance.now(); const classes = []; for (match of code.matchAll(/(?:class\s*?=\s*?)(["'])((?:(?=(?:\\)*)\\.|.)*?)\1/gi)) { // we use the default splitter RegEx, but only to split class attribute values, nothing else // it properly deals with inline riot `<template>` tags that contain unparsed expressions // `{ <expression> }` which cause problems with a 'simple' whitespace splitter... classes.push(...match[2].split(/[\\:]?[\s'"`;{}]+/g)); } //console.log('re:css extractor classes', classes); //console.log(`custom extractor done in ${performance.now()-start}ms`); return classes; }, }, // This is actually the default extractor, takes 60ms+ for a large HTML document /*extractorDefault: { name: '@unocss/core/extractor-split', order: 0, extract({ code }) { //const start = performance.now(); const defaultSplitRE = /[\\:]?[\s'"`;{}]+/g; function splitCode(code) { return code.split(defaultSplitRE); }; const tokens = splitCode(code); //console.log(`default extractor done in ${performance.now()-start}ms`); return tokens; }, },*/ runtime: { inject: (styleElement) => document.head.append(styleElement), observer: { target: () => document.querySelector('.lia-page'), attributeFilter: ['class'], }, //inspect: (el) => { console.log(el); if ( /\S+:\S+/gi.test(el.getAttribute('class') || '') ) { console.log('uno inspect', el, el.classList, el.classList.matchAll(/\S+:\S+/gi)); } return true; }, ready: (ctx) => { //console.log('uno ready?', `${performance.now()-window.$start}ms`, ctx); // we can't pass the inspect callback directly via runtime config? why? ctx.inspect((el) => { if ( /\S+:\S+/gi.test(el.getAttribute('class') || '') ) { [...el.classList].filter((x) => /\S+:\S+/i.test(x)).forEach((v) => { window.$stats[v] = (window.$stats[v] || 0) + 1; }); } // need to return true from inspector callback return true; }); // need to return true from ready return true; }, //configResolved: (config, defaults) => { console.log('uno config resolved, modify it?', `${performance.now()-window.$start}ms`, config, defaults); }, }, }; </script> <script src="https://community.hubspot.com/html/@BAA2A9A38B8DF4DC5426B3D61241E9AB/assets/_core.libs.min.js" type="text/javascript"></script> <script src="https://community.hubspot.com/html/@B5A089E4D43239236FCE70D92814096D/assets/_core.global.min.js" type="text/javascript"></script> <script src="https://community.hubspot.com/html/@F771820AC374A1A1436A3D822BE61093/assets/_cmp.min.js" type="text/javascript"></script> <link rel="icon" href="https://community.hubspot.com/html/@46292D292824DF071B6641C2DA6FDD8E/assets/favicon.png"> <!--[if IE]><link rel="shortcut icon" href="https://community.hubspot.com/html/@46292D292824DF071B6641C2DA6FDD8E/assets/favicon.png"><![endif]--> <meta class="swiftype" name="doc-type" data-type="string" content="Community"> <script data-external-hs-domain="true" data-gtm-id="GTM-M3KWR2J" src="https://www.hubspot.com/wt-assets/static-files/compliance/index.js" defer nonce></script> <script type="text/javascript" src="/t5/scripts/A8A4D60844A7A24245ECDC960EA81DEE/lia-scripts-head-min.js"></script><script language="javascript" type="text/javascript"> <!-- LITHIUM.PrefetchData = {"Components":{"grouphubs.widget.my-groups-panel-list":{"instances":[{"userScope":{"result":{"data":{"size":1,"list_item_type":"user","type":"users","items":[{"id":"169781","type":"user","login":"kennedyp"}]},"successful":true}},"groupHubs":{"call":{"query":{"nodes":{"limit":5,"fields":["id","title","view_href","avatar","user_context.can_update_node","user_context.is_member","membership_type","memberships.count(*)"],"constraints":[{"user.id":"169781"},{"node_type":"grouphub"}],"sorts":["message_activity.core_property_change_time desc"]}}},"result":{"data":{"size":3,"list_item_type":"node","type":"nodes","items":[{"id":"grouphub:study-group-inbound","avatar":{"medium_href":"/t5/image/serverpage/image-id/45260i54F3605EC818A699/image-size/medium?v=v2&px=400","small_href":"/t5/image/serverpage/image-id/45260i54F3605EC818A699/image-size/small?v=v2&px=200","large_href":"/t5/image/serverpage/image-id/45260i54F3605EC818A699/image-size/large?v=v2&px=999","tiny_href":"/t5/image/serverpage/image-id/45260i54F3605EC818A699/image-size/tiny?v=v2&px=100","type":"avatar"},"type":"node","title":"Inbound","view_href":"/t5/Inbound/gh-p/study-group-inbound","user_context":{"can_update_node":false,"is_member":false,"type":"node_user_context"},"membership_type":"open","memberships":{"count":12126}},{"id":"grouphub:Women_In_Tech","avatar":{"medium_href":"/t5/image/serverpage/image-id/35492i55896808D811C855/image-size/medium?v=v2&px=400","small_href":"/t5/image/serverpage/image-id/35492i55896808D811C855/image-size/small?v=v2&px=200","large_href":"/t5/image/serverpage/image-id/35492i55896808D811C855/image-size/large?v=v2&px=999","tiny_href":"/t5/image/serverpage/image-id/35492i55896808D811C855/image-size/tiny?v=v2&px=100","type":"avatar"},"type":"node","title":"Women in Tech","view_href":"/t5/Women-in-Tech/gh-p/Women_In_Tech","user_context":{"can_update_node":false,"is_member":false,"type":"node_user_context"},"membership_type":"open","memberships":{"count":734}},{"id":"grouphub:studentspot","avatar":{"medium_href":"/t5/image/serverpage/image-id/66276i43039547FC51BCB3/image-size/medium?v=v2&px=400","small_href":"/t5/image/serverpage/image-id/66276i43039547FC51BCB3/image-size/small?v=v2&px=200","large_href":"/t5/image/serverpage/image-id/66276i43039547FC51BCB3/image-size/large?v=v2&px=999","tiny_href":"/t5/image/serverpage/image-id/66276i43039547FC51BCB3/image-size/tiny?v=v2&px=100","type":"avatar"},"type":"node","title":"StudentSpot","view_href":"/t5/StudentSpot/gh-p/studentspot","user_context":{"can_update_node":false,"is_member":false,"type":"node_user_context"},"membership_type":"open","memberships":{"count":274}}]},"successful":true}}}]}},"commonResults":{}}; ;(function(){var en = function(n, ord ) { var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2); if (ord) return (n10 == 1 && n100 != 11) ? 'one' : (n10 == 2 && n100 != 12) ? 'two' : (n10 == 3 && n100 != 13) ? 'few' : 'other'; return (n == 1 && v0) ? 'one' : 'other'; }; var number = function (value, name, offset) { if (!offset) return value; if (isNaN(value)) throw new Error("Can't apply offset:" + offset + ' to argument `' + name + '` with non-numerical value ' + JSON.stringify(value) + '.'); return value - offset; }; var plural = function (value, offset, lcfunc, data, isOrdinal) { if ({}.hasOwnProperty.call(data, value)) return data[value]; if (offset) value -= offset; var key = lcfunc(value, isOrdinal); return key in data ? data[key] : data.other; }; var fmt = { prop: function (value, lc, param) { return value[param]; } }; LITHIUM.TextData = { li: { nodes: { NodeTitle: { title: function(d) { return d.title; } }, NodeAvatar: { alt: function(d) { return d.title; } } }, common: { Panel: { viewAll: function(d) { return "See all"; } } }, grouphubs: { GroupHubsPanelList: { title: { node: { "default": function(d) { return "Group Hubs"; }, top: function(d) { return "Group Hubs"; }, leaf: function(d) { return "Group Hubs in " + fmt.prop(d.node, "en", (" title").trim()); } }, user: { them: function(d) { return "Group Hubs for " + fmt.prop(d.user, "en", (" login").trim()); }, my: function(d) { return "My groups"; } } } } }, memberships: { MembershipCount: { iconCount: function(d) { return d.count; }, count: function(d) { return plural(d.count, 0, en, { one: "1 member", other: number(d.count, "count") + " members" }); } } } } };LITHIUM.Limuirs = LITHIUM.Limuirs || {}; LITHIUM.Limuirs.logLevel = "error"; LITHIUM.Limuirs.getChunkURL = function(){ return "https:\u002F\u002Flimuirs-assets.lithium.com\u002Fassets\u002F"}; LITHIUM.Limuirs.preloadPaths = [];;LITHIUM.release = "24.8"})(); LITHIUM.DEBUG = false; LITHIUM.CommunityJsonObject = { "Validation" : { "image.description" : { "min" : 0, "max" : 1000, "isoneof" : [ ], "type" : "string" }, "tkb.toc_maximum_heading_level" : { "min" : 1, "max" : 6, "isoneof" : [ ], "type" : "integer" }, "tkb.toc_heading_list_style" : { "min" : 0, "max" : 50, "isoneof" : [ "disc", "circle", "square", "none" ], "type" : "string" }, "blog.toc_maximum_heading_level" : { "min" : 1, "max" : 6, "isoneof" : [ ], "type" : "integer" }, "tkb.toc_heading_indent" : { "min" : 5, "max" : 50, "isoneof" : [ ], "type" : "integer" }, "blog.toc_heading_indent" : { "min" : 5, "max" : 50, "isoneof" : [ ], "type" : "integer" }, "blog.toc_heading_list_style" : { "min" : 0, "max" : 50, "isoneof" : [ "disc", "circle", "square", "none" ], "type" : "string" } }, "User" : { "settings" : { "imageupload.legal_file_extensions" : "*.jpg;*.JPG;*.jpeg;*.JPEG;*.gif;*.GIF;*.png;*.PNG", "config.enable_avatar" : true, "integratedprofile.show_klout_score" : true, "layout.sort_view_by_last_post_date" : false, "layout.friendly_dates_enabled" : true, "profileplus.allow.anonymous.scorebox" : false, "tkb.message_sort_default" : "topicPublishDate", "layout.format_pattern_date" : "MMM d, yyyy", "config.require_search_before_post" : "off", "isUserLinked" : false, "integratedprofile.cta_add_topics_dismissal_timestamp" : -1, "layout.message_body_image_max_size" : 1000, "profileplus.everyone" : false, "integratedprofile.cta_connect_wide_dismissal_timestamp" : -1, "blog.toc_maximum_heading_level" : "", "integratedprofile.hide_social_networks" : false, "blog.toc_heading_indent" : "", "contest.entries_per_page_num" : 20, "layout.messages_per_page_linear" : 12, "integratedprofile.cta_manage_topics_dismissal_timestamp" : -1, "profile.shared_profile_test_group" : false, "integratedprofile.cta_personalized_feed_dismissal_timestamp" : -1, "integratedprofile.curated_feed_size" : 10, "contest.one_kudo_per_contest" : false, "integratedprofile.enable_social_networks" : false, "integratedprofile.my_interests_dismissal_timestamp" : -1, "profile.language" : "en", "layout.friendly_dates_max_age_days" : 31, "layout.threading_order" : "thread_descending", "blog.toc_heading_list_style" : "disc", "useRecService" : false, "layout.module_welcome" : "", "imageupload.max_uploaded_images_per_upload" : 100, "imageupload.max_uploaded_images_per_user" : 6000, "integratedprofile.connect_mode" : "", "tkb.toc_maximum_heading_level" : "", "tkb.toc_heading_list_style" : "disc", "sharedprofile.show_hovercard_score" : true, "config.search_before_post_scope" : "container", "tkb.toc_heading_indent" : "", "p13n.cta.recommendations_feed_dismissal_timestamp" : -1, "imageupload.max_file_size" : 3072, "layout.show_batch_checkboxes" : false, "integratedprofile.cta_connect_slim_dismissal_timestamp" : -1 }, "isAnonymous" : true, "policies" : { "image-upload.process-and-remove-exif-metadata" : false }, "registered" : false, "emailRef" : "", "id" : -1, "login" : "Anonymous" }, "Server" : { "communityPrefix" : "/mjmao93648", "nodeChangeTimeStamp" : 1732487684998, "tapestryPrefix" : "/t5", "deviceMode" : "DESKTOP", "responsiveDeviceMode" : "DESKTOP", "membershipChangeTimeStamp" : "0", "version" : "24.8", "branch" : "24.8-release", "showTextKeys" : false }, "Config" : { "phase" : "prod", "integratedprofile.cta.reprompt.delay" : 30, "profileplus.tracking" : { "profileplus.tracking.enable" : false, "profileplus.tracking.click.enable" : false, "profileplus.tracking.impression.enable" : false }, "app.revision" : "2410251442-s96644fcabc-b95", "navigation.manager.community.structure.limit" : "1000" }, "Activity" : { "Results" : [ { "name" : "UserUpdated", "user" : { "uid" : -1, "login" : "Anonymous" } } ] }, "NodeContainer" : { "viewHref" : "https://community.hubspot.com/t5/Top/ct-p/top", "description" : "", "id" : "top", "shortTitle" : "Top", "title" : "Top", "nodeType" : "category" }, "Page" : { "skins" : [ "hubspot", "responsive_peak" ], "authUrls" : { "loginUrl" : "https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fv2%2Fviewprofilepage%2Fuser-id%2F169781", "loginUrlNotRegistered" : "https://app.hubspot.com/khoros/integration/jwt/authenticate?redirectreason=notregistered&referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fv2%2Fviewprofilepage%2Fuser-id%2F169781", "loginUrlNotRegisteredDestTpl" : "https://app.hubspot.com/khoros/integration/jwt/authenticate?redirectreason=notregistered&referer=%7B%7BdestUrl%7D%7D" }, "name" : "ViewProfilePage", "rtl" : false, "object" : { "viewHref" : "/t5/user/viewprofilepage/user-id/169781", "id" : 169781, "page" : "ViewProfilePage", "type" : "User", "login" : "kennedyp" } }, "WebTracking" : { "Activities" : { }, "path" : "Community:HubSpot Community" }, "Feedback" : { "targeted" : { } }, "Seo" : { "markerEscaping" : { "pathElement" : { "prefix" : "@", "match" : "^[0-9][0-9]$" }, "enabled" : false } }, "TopLevelNode" : { "viewHref" : "https://community.hubspot.com/", "description" : "Welcome to the HubSpot Community! Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot.", "id" : "mjmao93648", "shortTitle" : "HubSpot Community", "title" : "HubSpot Community", "nodeType" : "Community" }, "Community" : { "viewHref" : "https://community.hubspot.com/", "integratedprofile.lang_code" : "en", "integratedprofile.country_code" : "US", "id" : "mjmao93648", "shortTitle" : "HubSpot Community", "title" : "HubSpot Community" }, "CoreNode" : { "viewHref" : "https://community.hubspot.com/", "settings" : { }, "description" : "Welcome to the HubSpot Community! Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot.", "id" : "mjmao93648", "shortTitle" : "HubSpot Community", "title" : "HubSpot Community", "nodeType" : "Community", "ancestors" : [ ] } }; LITHIUM.Components.RENDER_URL = "/t5/util/componentrenderpage/component-id/#{component-id}?render_behavior=raw"; LITHIUM.Components.ORIGINAL_PAGE_NAME = 'user/v2/ViewProfilePage'; LITHIUM.Components.ORIGINAL_PAGE_ID = 'ViewProfilePage'; LITHIUM.Components.ORIGINAL_PAGE_CONTEXT = 'k_MD7aghixq3zVLIPIEXpm0H-dzGug_XTZqHtLhOKXkRlsokKPppwoaxjk78Y_tbdHyRW2Z4Do6A69Y4TAitp10NZf_cLacqFvz8xb4cNuClbyGFWdopdHMC4XOwwjUtEgP1os_y7bSevLrNich2mjWpRd9RCveS_ixzMPJ7cKqY9cJy5iO_oYKrf7Puzq7xsSx41fCJES1FYI_-GlTI6AO-uO9mUFugyG3z7W1bNWvQ7iXbKNZBKIW4PMRML9rPYWEULAXNQCPScOsRlBDTCi0n1-82QylXjhUC0kNjtha3hcc21Cwv-lAPnve_rjkX'; LITHIUM.Css = { "BASE_DEFERRED_IMAGE" : "lia-deferred-image", "BASE_BUTTON" : "lia-button", "BASE_SPOILER_CONTAINER" : "lia-spoiler-container", "BASE_TABS_INACTIVE" : "lia-tabs-inactive", "BASE_TABS_ACTIVE" : "lia-tabs-active", "BASE_AJAX_REMOVE_HIGHLIGHT" : "lia-ajax-remove-highlight", "BASE_FEEDBACK_SCROLL_TO" : "lia-feedback-scroll-to", "BASE_FORM_FIELD_VALIDATING" : "lia-form-field-validating", "BASE_FORM_ERROR_TEXT" : "lia-form-error-text", "BASE_FEEDBACK_INLINE_ALERT" : "lia-panel-feedback-inline-alert", "BASE_BUTTON_OVERLAY" : "lia-button-overlay", "BASE_TABS_STANDARD" : "lia-tabs-standard", "BASE_AJAX_INDETERMINATE_LOADER_BAR" : "lia-ajax-indeterminate-loader-bar", "BASE_AJAX_SUCCESS_HIGHLIGHT" : "lia-ajax-success-highlight", "BASE_CONTENT" : "lia-content", "BASE_JS_HIDDEN" : "lia-js-hidden", "BASE_AJAX_LOADER_CONTENT_OVERLAY" : "lia-ajax-loader-content-overlay", "BASE_FORM_FIELD_SUCCESS" : "lia-form-field-success", "BASE_FORM_WARNING_TEXT" : "lia-form-warning-text", "BASE_FORM_FIELDSET_CONTENT_WRAPPER" : "lia-form-fieldset-content-wrapper", "BASE_AJAX_LOADER_OVERLAY_TYPE" : "lia-ajax-overlay-loader", "BASE_FORM_FIELD_ERROR" : "lia-form-field-error", "BASE_SPOILER_CONTENT" : "lia-spoiler-content", "BASE_FORM_SUBMITTING" : "lia-form-submitting", "BASE_EFFECT_HIGHLIGHT_START" : "lia-effect-highlight-start", "BASE_FORM_FIELD_ERROR_NO_FOCUS" : "lia-form-field-error-no-focus", "BASE_EFFECT_HIGHLIGHT_END" : "lia-effect-highlight-end", "BASE_SPOILER_LINK" : "lia-spoiler-link", "FACEBOOK_LOGOUT" : "lia-component-users-action-logout", "BASE_DISABLED" : "lia-link-disabled", "FACEBOOK_SWITCH_USER" : "lia-component-admin-action-switch-user", "BASE_FORM_FIELD_WARNING" : "lia-form-field-warning", "BASE_AJAX_LOADER_FEEDBACK" : "lia-ajax-loader-feedback", "BASE_AJAX_LOADER_OVERLAY" : "lia-ajax-loader-overlay", "BASE_LAZY_LOAD" : "lia-lazy-load" }; LITHIUM.noConflict = true; LITHIUM.useCheckOnline = false; LITHIUM.RenderedScripts = [ "Cache.js", "SearchForm.js", "Video.js", "jquery.clone-position-1.0.js", "AutoComplete.js", "Lithium.js", "limuirs-24_7-main.138f37e85bead07e28fd.js", "PolyfillsAll.js", "jquery.ui.draggable.js", "Text.js", "LiModernizr.js", "Link.js", "PartialRenderProxy.js", "jquery.ui.dialog.js", "jquery.ui.mouse.js", "Auth.js", "jquery.blockui.js", "jquery.effects.core.js", "jquery.effects.slide.js", "CustomEvent.js", "ActiveCast3.js", "jquery.hoverIntent-r6.js", "jquery.placeholder-2.0.7.js", "NoConflict.js", "jquery.autocomplete.js", "jquery.json-2.6.0.js", "ElementQueries.js", "AjaxFeedback.js", "SearchAutoCompleteToggle.js", "UserBadges.js", "jquery.iframe-transport.js", "jquery.viewport-1.0.js", "PolyfillsOld.js", "ElementMethods.js", "jquery.tmpl-1.1.1.js", "Sandbox.js", "InformationBox.js", "ForceLithiumJQuery.js", "jquery.tools.tooltip-1.2.6.js", "ResizeSensor.js", "jquery.ui.resizable.js", "Namespace.js", "limuirs-24_7-vendors~main.5ef86aa8c72fe4cbb8d6.js", "Events.js", "jquery.fileupload.js", "Globals.js", "Throttle.js", "Components.js", "jquery.scrollTo.js", "prism.js", "jquery.iframe-shim-1.0.js", "jquery.appear-1.1.1.js", "jquery.css-data-1.0.js", "SpoilerToggle.js", "jquery.ui.core.js", "MessageListActual.js", "jquery.function-utils-1.0.js", "Loader.js", "jquery.ui.position.js", "json2.js", "Forms.js", "jquery.lithium-selector-extensions.js", "DeferredImages.js", "AjaxSupport.js", "HelpIcon.js", "Placeholder.js", "DataHandler.js", "jquery.position-toggle-1.0.js", "jquery.delayToggle-1.0.js", "jquery.js", "jquery.ajax-cache-response-1.0.js", "Tooltip.js", "jquery.ui.widget.js" ];// --> </script><script type="text/javascript" src="/t5/scripts/D60EB96AE5FF670ED274F16ABB044ABD/lia-scripts-head-min.js"></script></head> <body class="lia-user-status-anonymous ViewProfilePage lia-body" id="lia-body"> <div id="4AC-186-2" class="ServiceNodeInfoHeader"> </div> <div class="lia-page"> <center> <noscript class=" " id="hubspot" data-page="ViewProfilePage" data-style="none" data-rootid="mjmao93648" data-roottype="community" data-topid="mjmao93648" data-nodeid="mjmao93648" data-nodetype="community" data-nodelang="en" data-userlang="en" data-skin="hubspot"> <p>JavaScript must be installed and enabled to use these boards.<p> Your browser appears to have JavaScript disabled or does not support JavaScript. Please refer to your browser's help file to determine how to enable JavaScript.</p> </noscript> <svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <symbol id="i:hubspot" viewBox="0 0 32 32"> <path d="M23.7 10.92V7.36a2.74 2.74 0 0 0 1.59-2.48V4.8a2.75 2.75 0 0 0-2.74-2.74h-.08a2.75 2.75 0 0 0-2.74 2.74v.08c0 1.1.64 2.03 1.56 2.47h.02v3.57a7.7 7.7 0 0 0-3.71 1.64l.01-.01-9.78-7.62a3.12 3.12 0 1 0-1.45 1.9h-.01l9.62 7.49a7.75 7.75 0 0 0 .14 8.81l-.02-.03-2.93 2.94a2.54 2.54 0 1 0 1.81 2.43c0-.27-.04-.52-.12-.75v.01l2.9-2.9a7.8 7.8 0 1 0 5.98-13.91h-.04zm-1.2 11.72a3.55 3.55 0 1 1 .01 0z" /> </symbol> <symbol id="i:link" viewBox="0 0 20 20"> <path d="M7.859 14.691l-0.81 0.805c-0.701 0.695-1.843 0.695-2.545 0-0.336-0.334-0.521-0.779-0.521-1.252s0.186-0.916 0.521-1.252l2.98-2.955c0.617-0.613 1.779-1.515 2.626-0.675 0.389 0.386 1.016 0.384 1.403-0.005 0.385-0.389 0.383-1.017-0.006-1.402-1.438-1.428-3.566-1.164-5.419 0.675l-2.98 2.956c-0.715 0.709-1.108 1.654-1.108 2.658 0 1.006 0.394 1.949 1.108 2.658 0.736 0.73 1.702 1.096 2.669 1.096s1.934-0.365 2.669-1.096l0.811-0.805c0.389-0.385 0.391-1.012 0.005-1.4s-1.014-0.39-1.403-0.006zM16.891 3.207c-1.547-1.534-3.709-1.617-5.139-0.197l-1.009 1.002c-0.389 0.386-0.392 1.013-0.006 1.401 0.386 0.389 1.013 0.391 1.402 0.005l1.010-1.001c0.74-0.736 1.711-0.431 2.346 0.197 0.336 0.335 0.522 0.779 0.522 1.252s-0.186 0.917-0.522 1.251l-3.18 3.154c-1.454 1.441-2.136 0.766-2.427 0.477-0.389-0.386-1.016-0.383-1.401 0.005s-0.384 1.017 0.005 1.401c0.668 0.662 1.43 0.99 2.228 0.99 0.977 0 2.010-0.492 2.993-1.467l3.18-3.153c0.712-0.71 1.107-1.654 1.107-2.658s-0.395-1.949-1.109-2.659z"></path> </symbol> <symbol id="i:linkedin" viewBox="0 0 20 20"> <path d="M10 0.4c-5.302 0-9.6 4.298-9.6 9.6s4.298 9.6 9.6 9.6 9.6-4.298 9.6-9.6-4.298-9.6-9.6-9.6zM7.65 13.979h-1.944v-6.256h1.944v6.256zM6.666 6.955c-0.614 0-1.011-0.435-1.011-0.973 0-0.549 0.409-0.971 1.036-0.971s1.011 0.422 1.023 0.971c0 0.538-0.396 0.973-1.048 0.973zM14.75 13.979h-1.944v-3.467c0-0.807-0.282-1.355-0.985-1.355-0.537 0-0.856 0.371-0.997 0.728-0.052 0.127-0.065 0.307-0.065 0.486v3.607h-1.945v-4.26c0-0.781-0.025-1.434-0.051-1.996h1.689l0.089 0.869h0.039c0.256-0.408 0.883-1.010 1.932-1.010 1.279 0 2.238 0.857 2.238 2.699v3.699z"></path> </symbol> <symbol id="i:xcom" viewBox="0 0 32 32"> <path d="M24.325 3h4.411l-9.636 11.013 11.336 14.987h-8.876l-6.952-9.089-7.955 9.089h-4.413l10.307-11.78-10.875-14.22h9.101l6.284 8.308zM22.777 26.36h2.444l-15.776-20.859h-2.623z"></path> </symbol> <symbol id="i:connectcom" viewBox="0 0 24 24"> <path fill="#192733" style="fill: var(--color1, #192733)" d="M17.585 5.753c-1.941 0-3.675 0.886-4.822 2.273l0.005 0.005c-0.025 0.030-0.052 0.060-0.075 0.092l-3.988 5.512c-0.608 0.75-1.335 1.171-2.291 1.171-1.549 0-2.806-1.258-2.806-2.806s1.258-2.806 2.806-2.806c1.082 0 1.953 0.603 2.519 1.492l2.261-2.712c-1.146-1.36-2.861-2.221-4.78-2.221-3.452 0.003-6.248 2.799-6.248 6.248s2.797 6.248 6.248 6.248c1.941 0 3.675-0.886 4.822-2.273l-0.005-0.005c0.067-0.067 0.132-0.136 0.189-0.216l4.023-5.559c0.579-0.642 1.263-1 2.142-1 1.549 0 2.806 1.258 2.806 2.806s-1.258 2.806-2.806 2.806c-1.082 0-1.953-0.603-2.519-1.492l-2.261 2.712c1.146 1.36 2.861 2.221 4.78 2.221 3.452 0 6.248-2.797 6.248-6.248s-2.797-6.248-6.248-6.248z"></path> <path fill="#ff5c35" style="fill: var(--color2, #ff5c35)" d="M17.585 10.15c-0.747 0-1.389 0.446-1.682 1.085h-7.737c-0.293-0.64-0.935-1.085-1.682-1.085-1.020 0-1.849 0.829-1.849 1.849s0.829 1.849 1.849 1.849c0.747 0 1.389-0.446 1.682-1.085h7.737c0.293 0.64 0.935 1.085 1.682 1.085 1.020 0 1.849-0.829 1.849-1.849s-0.829-1.849-1.849-1.849z"></path> </symbol> <symbol id="i:globe" viewBox="0 0 24 24"> <path d="M16.951 11c-0.214-2.69-1.102-5.353-2.674-7.71 1.57 0.409 2.973 1.232 4.087 2.346 1.408 1.408 2.351 3.278 2.581 5.364zM14.279 20.709c1.483-2.226 2.437-4.853 2.669-7.709h3.997c-0.23 2.086-1.173 3.956-2.581 5.364-1.113 1.113-2.516 1.936-4.085 2.345zM7.049 13c0.214 2.69 1.102 5.353 2.674 7.71-1.57-0.409-2.973-1.232-4.087-2.346-1.408-1.408-2.351-3.278-2.581-5.364zM9.721 3.291c-1.482 2.226-2.436 4.853-2.669 7.709h-3.997c0.23-2.086 1.173-3.956 2.581-5.364 1.114-1.113 2.516-1.936 4.085-2.345zM12.004 1c0 0 0 0 0 0-3.044 0.001-5.794 1.233-7.782 3.222-1.99 1.989-3.222 4.741-3.222 7.778s1.232 5.789 3.222 7.778c1.988 1.989 4.738 3.221 7.774 3.222 0 0 0 0 0 0 3.044-0.001 5.793-1.233 7.782-3.222 1.99-1.989 3.222-4.741 3.222-7.778s-1.232-5.789-3.222-7.778c-1.988-1.989-4.738-3.221-7.774-3.222zM14.946 13c-0.252 2.788-1.316 5.36-2.945 7.451-1.729-2.221-2.706-4.818-2.945-7.451zM11.999 3.549c1.729 2.221 2.706 4.818 2.945 7.451h-5.89c0.252-2.788 1.316-5.36 2.945-7.451z"></path> </symbol> </defs> </svg> <style class="core-cmp-icons/core" type="text/css"> .i { display: inline-block; fill: currentColor; height: 1rem; width: 1rem; stroke-width: 0; stroke: currentColor; } /* Single-colored icons can be modified like so but this usually happens directly where they are used: */ /* .i\:<name> { font-size: 32px; color: red; } */ </style> <style class="cmp-global-styles/core" type="text/css"> /** /* TODO: Potentially move to skin or leave here? Might be easier to find changes done by us this way? /*/ /* Center category banner card icons (often the HTMl is CC23 based), for example Advocacy */ #lia-body .custom-home-banner-section__cards .card-item__icon { align-self: center; } /* Fix unstyled layout for NotifyModeratorPage quilt */ .lia-quilt-notify-moderator-page > .lia-quilt-row-header .lia-page-header, .lia-quilt-notify-moderator-page > .lia-quilt-row-main { margin: 0 auto; max-width: 1236px; padding-left: 15px; padding-right: 15px; } /* KBCOM-2818: Add node description the hacky way */ .CategoryPage .custom-v2-banner__wrapper .page-title-wrapper:after, .ForumPage .custom-v2-banner__wrapper .page-title-wrapper:after, .GroupHubPage .custom-v2-banner__wrapper .page-title-wrapper:after { content: "Welcome to the HubSpot Community! Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot."; display: block; margin-bottom: -24px; } /* KBCOM-2802: Fix BlogDashboardPage filter alignment issue */ .BlogDashboardPage .dashboard-wrapper { margin: 0 auto; max-width: 1236px; } .BlogDashboardPage .dashboard-wrapper .lia-node-selector-dropdown { left: auto !important; right: 0; } .BlogDashboardPage .dashboard-wrapper .lia-component-blog-dashboard-tabs ul.lia-tabs-standard, .BlogDashboardPage .dashboard-wrapper .lia-component-blog-widget-dashboard-tabs ul.lia-tabs-standard { padding-left: 0 !important; } /* KBCOM-2831: Remove advanced search options toggle on SearchPage */ .SearchPage .lia-advanced-search-toggle { display: none; } /* re-css cloaking class to hide components relying on dynamically generated classes to only show when they are ready */ [un-cloak] { display: none; } /** /* Custom styles for core feedback elements. Those are usually only used to provide info and hints /* to priviledged roles when issues occur within custom components, so they are not part of the /* regular community theme. /*/ .admininfo { --b-radius: 3px; --H: 0; --S: 0%; --L: 41%; --A: 1; background: hsla(var(--H), var(--S), var(--L), var(--A)); border-radius: var(--b-radius); color: white; display: flex; align-items: center; gap: 24px; /*filter: grayscale(1);*/ font-size: 12px; line-height: 1.25; margin: 24px 0; padding: 24px; transition: all 236ms ease; } .admininfo .checkmark { display: none; } .admininfo__title { display: block; font-weight: bold; font-size: 125%; } .admininfo code { background: white; border-radius: var(--b-radius); color: hsla(var(--H), var(--S), var(--L), 1); display: inline-block; font-size: 90%; font-weight: bold; padding: 2px 4px; } .admininfo a { color: white; display: inline-block; position: relative; } .admininfo a:hover, .admininfo a:focus { text-decoration: none; } .admininfo a:before, .admininfo a:after { border-bottom: 1px dotted white; content: ''; position: absolute; left: 0; right: 0; bottom: -1px; transition: all 382ms ease; } .admininfo a:after { border-bottom: 1px solid white; max-width: 0; width: 0; right: auto; } .admininfo a:hover:after, .admininfo a:focus:after { max-width: 100%; width: 1200px; } .admininfo a:active:after { transform: scaleX(1.0618); } .admininfo.is--error { --S: 100%; --L: 41%; background: hsla(var(--H), var(--S), var(--L), var(--A)); filter: grayscale(0); } .admininfo.is--error .checkmark { display: block; } .admininfo.is--error code { background: #ff4444; color: white; } .admininfo.is--success { --H: 135; --S: 100%; background: hsla(var(--H), var(--S), var(--L), var(--A)); filter: grayscale(0); } .admininfo.is--success code { background: #00c851; color: white; } .checkmark { flex-shrink: 0; /* prevents shrinking under 48px! */ min-width: 48px; width: 48px; height: 48px; border-radius: 50%; display: block; stroke-width: 3px; stroke: white; stroke-miterlimit: 10; } .checkmark_circle_error { stroke-dasharray: 166; stroke-dashoffset: 166; stroke-width: 5px; stroke-miterlimit: 10; stroke: #ff4444; animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards; } .checkmark.error { box-shadow: inset 0px 0px 0px #ff4444; animation: fillerror 0.4s ease-in-out 0.4s forwards, scale 0.3s ease-in-out 0.9s both; } .checkmark_check { transform-origin: 50% 50%; stroke-dasharray: 48; stroke-dashoffset: 48; animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.9s forwards; } .progress { position: absolute; top: 5%; left: 5%; stroke: black; transform: rotate(-90deg); } .progress.progress--thin { left: auto; right: 5%; } .progress circle { stroke-dasharray: 130; stroke-dashoffset: 130; animation: dash 1.5s infinite; } @keyframes dash { 50% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -130; } } @keyframes stroke { 100% { stroke-dashoffset: 0; } } @keyframes scale { 0%, 100% { transform: none; } 50% { transform: scale3d(1.1, 1.1, 1); } } @keyframes fillerror { 100% { box-shadow: inset 0px 0px 0px 75px #ff4444; } } </style><style class="cmp-global-search-external/core" type="text/css"> @media (min-width: 799px) { .lia-search-input-message + .lia-autocomplete-container { width: 200% !important; max-width: 768px !important; } } .lia-search-input-message + .lia-autocomplete-container .lia-autocomplete-content { display: flex; margin: 0; } @media (max-width: 799px) { .mobile-header form.SerachForm [name="messageSearchField"] + .lia-autocomplete-container > ul:first-of-type { max-height: 210px !important; } /*.mobile-header form.SerachForm [name="messageSearchField"] + .lia-autocomplete-container .collapse-results.fa-chevron-down + ul { max-height: 36px !important; overflow: hidden !important; }*/ .lia-search-input-message + .lia-autocomplete-container .lia-autocomplete-content { flex-direction: column; } } .lia-search-input-message + .lia-autocomplete-container .lia-autocomplete-content>ul>li { padding: 8px 15px 8px 15px; } /* default tag styles */ .SearchPage .search-external-link:after, .is--tag { background-color: var(--color-calypso-light); border: 1px solid transparent; border-radius: 2px; color: var(--color-link-hover); display: inline-block; font-size: 12px; font-weight: 600; line-height: 22px; padding: 0 8px; position: relative; vertical-align: baseline; } .SearchPage .search-external-link:after { line-height: 16px; margin-left: 6px; } /* Override styles form the native Khoros skin... */ .SearchPage .lia-tabs-standard .lia-tabs:first-child { padding-left: 0; } .SearchPage .lia-tabs-standard-wrapper>.lia-tabs-standard { padding: 0 15px !important; /* This one is especially stubborn for some reason I can't explain! */ } </style> <script> // glowingblue: really? xhr.open()? when 5 lines up you were aware you have jQuery? oh boy... function followunfollow(id,value,currentUser,top){ const xhttp = new XMLHttpRequest(); xhttp.onload = function() { if (value=='Unfollow') { document.getElementById("tunfollow-"+id+"").style.display = 'none'; document.getElementById("tfollow-"+id+"").style.display = 'flex'; } if (value=='follow') { document.getElementById("tunfollow-"+id+"").style.display = 'flex'; document.getElementById("tfollow-"+id+"").style.display = 'none'; } } xhttp.open("GET", "/plugins/custom/hubspot/hubspot/follow-unfollow-hover-card-button?id="+id+"&val="+value+"&currentUser="+currentUser+"",true);xhttp.send(); } </script> <div class="MinimumWidthContainer"> <div class="min-width-wrapper"> <div class="min-width"> <div class="lia-content"> <div class="lia-browser-support-alert"> <div class="lia-browser-support-alert-text"> We no longer support Internet Explorer v10 and older, or you have compatibility view enabled. Disable Compatibility view, upgrade to a newer version, or use a different browser. </div> <div class="lia-browser-support-alert-close"> <a class="lia-link-navigation lia-link-ticket-post-action" data-lia-action-token="YPMyNzGvTKnIebna_bK_3GG7--hoX7dKKbdfH3qF9Nc." rel="nofollow" id="dismissAlert" href="https://community.hubspot.com/t5/user/v2/viewprofilepage.liabase.basebody.browsersupportalert.dismissalert:dismissalert?t:ac=user-id/169781"><span class="lia-img-close-small lia-fa-close lia-fa-small lia-fa" title="Dismiss this alert" alt="Dismiss this alert" aria-label="Dismiss this alert" role="img" id="display"></span></a> </div> </div> <div class="lia-quilt lia-quilt-view-profile-page-v2 lia-quilt-layout-two-column-main-side lia-top-quilt lia-anonymous-viewing-other"> <div class="lia-quilt-row lia-quilt-row-header"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-header"> <div class="lia-quilt-column-alley lia-quilt-column-alley-single"> <div class="lia-quilt lia-quilt-header lia-quilt-layout-one-column lia-component-quilt-header"> <div class="lia-quilt-row lia-quilt-row-header"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-header"> <div class="lia-quilt-column-alley lia-quilt-column-alley-single"> <!-- hs.custom.responsive-header --> <style> #lia-body .mobile-header .navbar .menu .menu-item.active.has-collapsible .menu-child{ display: flex; flex-direction: column; } .jp-resources-list{ display:flex; flex-direction:column; } .jp-resources-list>li.menu-child-item.jp-class-HubSpot.Community.Blog{ order:1; } .header-dropdown-menu .lia-header-nav-component-widget .private-notes-link:before { content: "Message"; } #lia-body .mobile-header .navbar .menu-wrapper.offcanvas::before {width: 0px;} .profile-menu-dropdown{display: none !important;} .pagination-recent-post.pagination a#jp-previous:after { content: "Prev"; } .pagination-recent-post.pagination a#jp-next:before { content: "Next"; } #lia-body .MessageView.lia-message-view-idea-message-item .lia-quilt-idea-message-item .lia-message-footer-action .lia-link-navigation.lia-message-comment-post:after { content: "0 Comment"; } </style> <div class="header mobile-header"> <nav class="navbar"> <span class="open-menu"> <img src="https://community.hubspot.com/html/@3A38E73C772F7CCD402C2EC02A244F14/assets/Hamburger-Nav.svg"> </span> <span class="hubspot-mobile-logo-wrapper"> <a href="/"> <img src="https://community.hubspot.com/html/@813D252A70F0A7024C8EA3BB1B8B9CFD/assets/sticky-logo.png"> </a> </span> <div class="menu-wrapper"> <div class="menu-block"> <span class="close-menu"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"> <path fill="#252a32" fill-rule="evenodd" d="M17.778.808l1.414 1.414L11.414 10l7.778 7.778-1.414 1.414L10 11.414l-7.778 7.778-1.414-1.414L8.586 10 .808 2.222 2.222.808 10 8.586 17.778.808z" /> </svg> </span> </div> <ul class="menu"> <li class="menu-item has-collapsible"> <a><span></span>Discussions</a> <ul class="menu-child"> <li class="menu-child-item"> <a href="/t5/CRM-Sales-Hub/ct-p/sales" class="nav-dropdown-link nav-discussions-crm"> CRM & Sales </a> </li> <li class="menu-child-item"> <a href="/t5/Marketing-Hub/ct-p/marketing" class="nav-dropdown-link nav-discussions-mktg"> Marketing & Content </a> </li> <li class="menu-child-item"> <a href="/t5/Service-Hub/ct-p/service_hub" class="nav-dropdown-link nav-discussions-svc"> Customer Success & Service </a> </li> <li class="menu-child-item"> <a href="/t5/Operations/ct-p/Operations" class="nav-dropdown-link nav-discussions-ops"> RevOps & Operations </a> </li> <li class="menu-child-item"> <a href="/t5/Commerce/ct-p/commerce" class="nav-dropdown-link nav-discussions-commerce "> Commerce </a> </li> <li class="menu-child-item"> <a href="/t5/HubSpot-Developers/ct-p/developers" class="nav-dropdown-link nav-discussions-developers "> Developers </a> </li> <li class="menu-child-item"> <a href="https://community.hubspot.com/t5/Getting-Started-on-the-Community/How-to-join-the-Solutions-Partner-Program/ba-p/400205" class="nav-dropdown-link nav-partners "> Partners </a> </li> <li class="menu-child-item"> <a href="/t5/HubSpot-Ideas/idb-p/HubSpot_Ideas" class="nav-dropdown-link nav-discussions-ideas"> Ideas </a> </li> </ul> </li> <li class="menu-item has-collapsible"> <a><span></span>Academy</a> <ul class="menu-child"> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/courses" class="nav-dropdown-link nav-external-link" target="_blank"> Courses </a> </li> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/certification-overview" class="nav-dropdown-link nav-external-link" target="_blank"> Certifications </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/academy/bootcamps/home" class="nav-dropdown-link nav-external-link" target="_blank"> Bootcamps </a> </li> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/learning-paths" class="nav-dropdown-link nav-external-link " target="_blank"> Learning Paths </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/HubSpot-Academy-Support/bd-p/certifications_help" class="nav-dropdown-link"> Academy Support </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Study-Groups/ct-p/study-groups" class="nav-dropdown-link"> Study Groups </a> </li> </ul> </li> <li class="menu-item has-collapsible"> <a><span></span>Resources</a> <ul class="menu-child jp-resources-list"> <li class="menu-child-item "> <a href="/t5/Getting-Started/ct-p/getting_started" class="nav-dropdown-link nav-discussions-gs"> Getting Started </a> </li> <li class="nav-dropdown-item"> <a href="https://help.hubspot.com/" class="nav-dropdown-link nav-external-link" target="_blank"> Help Center </a> </li> <li class="nav-dropdown-item"> <a href="https://knowledge.hubspot.com/" class="nav-dropdown-link nav-external-link" target="_blank"> Knowledge Base </a> </li> <li class="nav-dropdown-item"> <a href="https://developers.hubspot.com/docs/api/overview" class="nav-dropdown-link nav-external-link" target="_blank"> API Documentation </a> </li> <li class="nav-dropdown-item"> <a href="https://developers.hubspot.com/docs/cms" class="nav-dropdown-link nav-external-link" target="_blank"> CMS Documentation </a> </li> <li class="menu-child-item "> <a href="/t5/News-Networking-Events/ct-p/communityboard" class="nav-dropdown-link nav-news"> News </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=webinars&order_by=last_updated" class="nav-dropdown-link nav-resource-blog-Webinars "> Webinars </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=releases-updates" class="nav-dropdown-link nav-resource-blog-Releases and Updates "> Releases and Updates </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=hubspot-community-blog" class="nav-dropdown-link nav-resource-blog-HubSpot Community Blog "> Community Blog </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=sales-hub-community-perspectives" class="nav-dropdown-link nav-resource-blog-Sales Hub Community Perspectives "> Sales Hub Community Perspectives </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=workflows_library" class="nav-dropdown-link nav-resource-blog-Workflows Library "> Workflows Library </a> </li> <li class="menu-child-item "> <a href="/t5/Resources/ct-p/resources?node_id=ai-library" class="nav-dropdown-link nav-resource-blog-Breeze Library "> Breeze Library </a> </li> </ul> </li> <li class="menu-item has-collapsible"> <a href="#"><span></span>Events</a> <ul class="menu-child"> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Ask-Me-Anything-and-Panel/bd-p/ama_discussions" class="nav-dropdown-link "> AMA </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Community-Led-Events/bd-p/adapt" class="nav-dropdown-link "> Community Led Events </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/resources/webinar" class="nav-dropdown-link nav-external-link " target="_blank"> Webinars </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/hubspot-user-groups" class="nav-dropdown-link nav-external-link " target="_blank"> HUGS </a> </li> </ul> </li> <li class="menu-item has-collapsible"> <a><span></span>Advocacy</a> <ul class="menu-child"> <li class="menu-child-item"> <a href='/t5/Advocacy/ct-p/advocacy' class="nav-dropdown-link nav-hubFans-program "> Community Champions Program </a> </li> <li class="menu-child-item"> <a href="/t5/Advocates-Blog/bg-p/advocates-blog" class="nav-dropdown-link nav-adovcates-blog "> Champions Blog </a> </li> </ul> </li> </ul> <div class="lang-picker-wrapper"> <div class="lang-picker-container"> <a id="current-language" class="current-language"> <span class="lang-picker-globe-icon"></span> English </a> <div id="lang-picker-global" class="nav-popover lang-picker"> <div class="nav-popover-arrow" style="border-top-color: transparent; border-left-color: transparent; width: 20px; height: 20px; transform: rotate(-135deg); top: -10px; right: calc(50% - 48px);"></div> <ul id="lang-picker-dropdown" class="nav-dropdown-list"> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=en" class="nav-dropdown-link" data-lang="en"> <li class="nav-dropdown-item"> English </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=es" class="nav-dropdown-link" data-lang="es"> <li class="nav-dropdown-item"> Español </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=pt-br" class="nav-dropdown-link" data-lang="pt-br"> <li class="nav-dropdown-item"> Português </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=fr" class="nav-dropdown-link" data-lang="fr"> <li class="nav-dropdown-item"> Français </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=de" class="nav-dropdown-link" data-lang="de"> <li class="nav-dropdown-item"> Deutsch </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=ja" class="nav-dropdown-link" data-lang="ja"> <li class="nav-dropdown-item"> 日本語 </li> </a> </ul> </div> </div></div> </div> <div class="header-right-col"> <div class="search_icon" onclick="myFunction()"> <img src="/html/assets/search-icon-grey.svg?version=preview" alt=""> </div> <div class="wrapper-search" style="display:none"> <div id='lia-searchformV32_23f26f3944c942' class='SearchForm lia-search-form-wrapper lia-mode-default lia-component-common-widget-search-form'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23f26f3944c942'></div> </div> <div id='searchautocompletetoggle_23f26f3944c942'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23f26f3944c942_0'></div> </div> <form enctype='multipart/form-data' class='lia-form lia-form-inline SearchForm' action='https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.form.form' method='post' id='form_23f26f3944c942' name='form_23f26f3944c942'><div class='t-invisible'><input value='user-id/169781' name='t:ac' type='hidden'></input><input value='search/contributions/page' name='t:cp' type='hidden'></input><input value='ASg-o3tFF0OMKs5_jKDr-OXI5ITa3oRe2NFZsLG8pxNtr2q1LAGoAwYVce4p5CAS7R6EUrP7OHPauqAKQoDO4LyWwOQOhMK2iSN0geUi-pT_LK9bAKkd1z-r4Ft1rTd2gYpT1dP3P76ii5j8ddpZrT5ffr-djFrDqj1CP-QllgAZBeVuhmgaaHRa67HTDDkX0Fdus9uTbPUw9yGGyuO_Noex7D9-nXtNIGPNYuYeWBwoTGTNl0IH79zQWA6GEXyl2W5JnjP3LdRcw9-DfE6_kfA9a24n1CJeN649HsREz6-PrPLPUcYChmwwbRUi176ZhjUHEYmOYG5h1287kYyoajBDL9oiMf0mpxTYxWoCgr3tdx_JkHoBJhWyHP_iCO_Kk6HTRhZ2SblJo-jkcHW1a_zHOgQaGCOCz4P5USYGT83FgpBJivTJTcEVWpcGZlvZPiE2T_JVeuKXf6r_wItNXQ3Jx_xGHvy1QgiOddyi2nfqKQHKNLwzP8wCTZclfeXVomrJEYus8loHRDLk3OEU-N6WZ9lo1ba8MTLH8bTM4nKuKn3n10jOUi9UIQ2jFlVOZuibdWsNiYPCSsWDxwhwx1S4H4T9NivAjAdNZ-lYTejOQCslmtUzvshJjPLhIvBx0d2stCXNdF2czEFtNM9Bk-xtig5lwNO-x9aIxvwcrdGDLDO5GqwGCOlWqt678PFAP3okQj0KcPimnvFLmgakh827lFgR5b3zrr_39D8qk_TtTkLX3Rjf-okCeHl-STUxlLFic06J-XZCJ86sIXLNh-i136ajn40XFTbfft-6Om2MYGmG-ls3eJwzVddyTThN_pc4GeBd-O-_cK4fwPO5fkd1guCDdvtY0LNeoEzyv7o_3uKRhhPfiBjqwjUyeSdlYUoXSm0OYlAj45dVR2Sjphb--VvCIWHBmOE7yw0ZqNhGp2GPfSotxuBgfwaoLBCuR7P58M6KF29cjCEQU4TQKX-lnd1UJnaQ14gJzVERnnaZKlhho2eA9rjFD7iH_xaB97Oe44WGhezQ78rgq98O6oQHWuVr5pjkgW_P65ImU_fBfp1Wl71ZbF3t7_tR-u-pnvkLvIhM8MHoNsRfs-Aw11gAJsYwxGvyzcMWJQOvFjsVCYWfgf05VIlY6D6VN3q0w7EYrXwkGOigRAtTM4QjdHqgM87JwYtrk1ed1mSMs5s.' name='lia-form-context' type='hidden'></input><input value='ViewProfilePage:user-id/169781:searchformv32.form:' name='liaFormContentKey' type='hidden'></input><input value='yDZKQFNI0U/sd9X4pbUemqvQQZw=:H4sIAAAAAAAAALWSzUrDQBSFr4Wuigiib6DbiajdqAhFUISqweBaZibTNJpk4sxNEzc+ik8gvkQX7nwHH8CtKxfmzxJbwaTUVZhzw/nOmblP79COD+FAC6r40OAyQOWyCF0ZaMOkjtgrJgOp/NHONtER810sP9nfIkGtoCuVQ2hI+VAQpKHQqO67hEslPJcRRrUgPZaKlOOxKzx7wxIYhZtX487b+stnC5b60MnZ0junvkBY7d/QETU8GjiGlUYKnP0kRFguwEcFeAHBe02Dm0pyobWV+Wid0sbP9u7g4/G1BZCE8QWc1U3kpzapWoqZ+S+SvoMHgPQ+ypGVj/IoC2dlqHZ8CWZdV7xljUqszZa43voPYNHkFE7qGkdaqKrl1Pm7wEqmV59gcYjGkQOJP25h6jyJnOlzRv4DUURusIWhknbEsWo5K002vhzNufG1WHmDLwdzh8gDBQAA' name='t:formdata' type='hidden'></input></div> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='feedback_23f26f3944c942'></div> </div> <input value='Z34wjlPwDRmvOGuuugkVVtO0QSxlLEg3p_-G5I7SkXw.' name='lia-action-token' type='hidden'></input> <input value='form_23f26f3944c942' id='form_UIDform_23f26f3944c942' name='form_UID' type='hidden'></input> <input value='' id='form_instance_keyform_23f26f3944c942' name='form_instance_key' type='hidden'></input> <span class='lia-search-input-wrapper'> <span class='lia-search-input-field'> <span class='lia-button-wrapper lia-button-wrapper-secondary lia-button-wrapper-searchForm-action'><input value='searchForm' name='submitContextX' type='hidden'></input><input class='lia-button lia-button-secondary lia-button-searchForm-action' value='Search' id='submitContext_23f26f3944c942' name='submitContext' type='submit'></input></span> <input placeholder='Search the Community' aria-label='Search' title='Search' class='lia-form-type-text lia-autocomplete-input search-input lia-search-input-message' value='' id='messageSearchField_23f26f3944c942_0' name='messageSearchField' type='text'></input> <input placeholder='Search the Community' aria-label='Search' title='Search' class='lia-form-type-text lia-autocomplete-input search-input lia-search-input-tkb-article lia-js-hidden' value='' id='messageSearchField_23f26f3944c942_1' name='messageSearchField_0' type='text'></input> <input ng-non-bindable='' title='Enter a user name or rank' class='lia-form-type-text UserSearchField lia-search-input-user search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a user name or rank' value='' id='userSearchField_23f26f3944c942' name='userSearchField' type='text'></input> <input placeholder='Enter a keyword to search within the private messages' title='Enter a search word' class='lia-form-type-text NoteSearchField lia-search-input-note search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a search word' value='' id='noteSearchField_23f26f3944c942_0' name='noteSearchField' type='text'></input> <input title='Enter a search word' class='lia-form-type-text ProductSearchField lia-search-input-product search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a search word' value='' id='productSearchField_23f26f3944c942' name='productSearchField' type='text'></input> <input class='lia-as-search-action-id' name='as-search-action-id' type='hidden'></input> </span> </span> <span class='lia-cancel-search'>cancel</span> </form> <div class='search-autocomplete-toggle-link lia-js-hidden'> <span> <a class='lia-link-navigation auto-complete-toggle-on lia-link-ticket-post-action lia-component-search-action-enable-auto-complete' data-lia-action-token='QzB8zv0sEINTNWgYTo8QD8CaKm9O0KU0CuPDMe_QXp8.' rel='nofollow' id='enableAutoComplete_23f26f3944c942' href='https://community.hubspot.com/t5/user/v2/viewprofilepage.enableautocomplete:enableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions'>Turn on suggestions</a> <span class='HelpIcon'> <a class='lia-link-navigation help-icon lia-tooltip-trigger' role='button' aria-label='Help Icon' id='link_23f26f3944c942' href='#'><span class='lia-img-icon-help lia-fa-icon lia-fa-help lia-fa' alt='Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.' aria-label='Help Icon' role='img' id='display_23f26f3944c942'></span></a><div role='alertdialog' class='lia-content lia-tooltip-pos-bottom-left lia-panel-tooltip-wrapper' id='link_23f26f3944c942_0-tooltip-element'><div class='lia-tooltip-arrow'></div><div class='lia-panel-tooltip'><div class='content'>Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.</div></div></div> </span> </span> </div> </div> <div class='spell-check-showing-result'> Showing results for <span class='lia-link-navigation show-results-for-link lia-link-disabled' aria-disabled='true' id='showingResult_23f26f3944c942'></span> </div> <div> <span class='spell-check-search-instead'> Search instead for <a class='lia-link-navigation search-instead-for-link' rel='nofollow' id='searchInstead_23f26f3944c942' href='#'></a> </span> </div> <div class='spell-check-do-you-mean lia-component-search-widget-spellcheck'> Did you mean: <a class='lia-link-navigation do-you-mean-link' rel='nofollow' id='doYouMean_23f26f3944c942' href='#'></a> </div> </div> </div> <script> function myFunction() { var x = document.getElementsByClassName("wrapper-search")[0]; if (x.style.display === "none") { x.style.display = "block"; } else { x.style.display = "none"; } } </script><div class="search-icon-plus-top"> <button class="lia-button search-toggle-action-icon-plus"><img src='https://community.hubspot.com/html/@5320E40129AA1377479EABCA2009B53A/assets/Start-dicsucssion.svg' alt=""><i class="lia-fa lia-fa-caret-down"></i></button> <div class="plus-bar-main-content" style="display: none;"> <ul id="plus-bar-top-main"> <li class="plus-bar"> <a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fforums%2Fpostpage%2Fcategory-id%2Fhubspot_community_en%2Fchoose-node%2Ftrue" class="white-btn transpaent"><i><img src="https://community.hubspot.com/html/@38F5B2AB35958F39F47C2BFFE5486135/assets/Edit.svg"></i> Create post</a> </li> <li class="plus-bar"><a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fforums%2Fpostpage%2Fboard-id%2FHubSpot_Ideas"><i><img src="https://community.hubspot.com/html/@C71A42AEB76B8A3A82335DA9F5B9C717/assets/lightbulb.svg"></i> Submit Idea</a></li> </ul> </div> </div> <div class="login-container"> <a class='lia-link-navigation login-link lia-authentication-link lia-component-users-action-login' rel='nofollow' id='loginPageV2_23f26f3b7214cb' href='https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F169781'>Log in</a> </div> </div> </nav> </div> <script> const openMenu = document.querySelector(".open-menu"); const closeMenu = document.querySelector(".close-menu"); const menuWrapper = document.querySelector(".menu-wrapper"); const hasCollapsible = document.querySelectorAll(".has-collapsible"); // Sidenav Toggle openMenu.addEventListener("click", function () { menuWrapper.classList.add("offcanvas"); }); closeMenu.addEventListener("click", function () { menuWrapper.classList.remove("offcanvas"); }); // Collapsible Menu hasCollapsible.forEach(function (collapsible) { collapsible.addEventListener("click", function () { collapsible.classList.toggle("active"); // Close Other Collapsible hasCollapsible.forEach(function (otherCollapsible) { if (otherCollapsible !== collapsible) { otherCollapsible.classList.remove("active"); } }); }); }); </script> </div> </div> </div><div class="lia-quilt-row lia-quilt-row-main"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-main-content"> <div class="lia-quilt-column-alley lia-quilt-column-alley-single"> <script> // Inline click listeners function getHubSpotClickListener(btnId) { if(btnId == "get-hubspot-free-v2"){ if (document.getElementById('get-hubspot-v2')) { document.getElementById('get-hubspot-v2').classList.toggle("show"); } } } function langPickerClickListener() { if (document.getElementById('lang-picker-global')) { document.getElementById('lang-picker-global').classList.toggle("show"); } } </script> <style> /* Text customization for CSS generated content */ .lia-list-row-thread-solved:after { content: "Solved"!important; text-transform: uppercase; } .SearchPage .lia-replies-toggle-link:before { content: "Replies"!important; } label.lia-form-label:after { // content: "(required)"; margin-left: 4px; } .lia-form-board-entry label.lia-form-label:after, .lia-form-subject-entry label.lia-form-label:after, .lia-form-body-entry label.lia-form-label:after, .lia-form-labels-entry label.lia-form-label:after { content: "(required)"; } .lia-form-login-entry .lia-form-input-wrapper:before { content: "Username*"!important; } .lia-form-profile-first-name-entry:before { content: "First name*"!important; } .lia-form-profile-last-name-entry:before { content: "Last name*"!important; } .lia-note-unread:after { content: "Unread"; text-transform: uppercase; } .EditPage label.lia-form-label:after, .EditPage .lia-form-label.lia-fieldset-title:after, .EditPage .lia-component-tkb-article-editor-form .lia-form-label.lia-form-compare-title:after, .lia-component-tkb-article-editor-form .EditPage .lia-form-label.lia-form-compare-title:after, .EditPage .lia-component-tkb-article-editor-form .lia-form-label.lia-revision-info-title:after, .lia-component-tkb-article-editor-form .EditPage .lia-form-label.lia-revision-info-title:after, .EditPage .lia-component-tkb-article-editor-form .lia-form-label.lia-related-messages-title:after, .lia-component-tkb-article-editor-form .EditPage .lia-form-label.lia-related-messages-title:after, .ReplyPage label.lia-form-label:after, .ReplyPage .lia-form-label.lia-fieldset-title:after, .ReplyPage .lia-component-tkb-article-editor-form .lia-form-label.lia-form-compare-title:after, .lia-component-tkb-article-editor-form .ReplyPage .lia-form-label.lia-form-compare-title:after, .ReplyPage .lia-component-tkb-article-editor-form .lia-form-label.lia-revision-info-title:after, .lia-component-tkb-article-editor-form .ReplyPage .lia-form-label.lia-revision-info-title:after, .ReplyPage .lia-component-tkb-article-editor-form .lia-form-label.lia-related-messages-title:after, .lia-component-tkb-article-editor-form .ReplyPage .lia-form-label.lia-related-messages-title:after, .PostPage label.lia-form-label:after, .PostPage .lia-form-label.lia-fieldset-title:after, .PostPage .lia-component-tkb-article-editor-form .lia-form-label.lia-form-compare-title:after, .lia-component-tkb-article-editor-form .PostPage .lia-form-label.lia-form-compare-title:after, .PostPage .lia-component-tkb-article-editor-form .lia-form-label.lia-revision-info-title:after, .lia-component-tkb-article-editor-form .PostPage .lia-form-label.lia-revision-info-title:after, .PostPage .lia-component-tkb-article-editor-form .lia-form-label.lia-related-messages-title:after, .lia-component-tkb-article-editor-form .PostPage .lia-form-label.lia-related-messages-title:after, .MyProfilePage label.lia-form-label:after, .MyProfilePage .lia-form-label.lia-fieldset-title:after, .MyProfilePage .lia-component-tkb-article-editor-form .lia-form-label.lia-form-compare-title:after, .lia-component-tkb-article-editor-form .MyProfilePage .lia-form-label.lia-form-compare-title:after, .MyProfilePage .lia-component-tkb-article-editor-form .lia-form-label.lia-revision-info-title:after, .lia-component-tkb-article-editor-form .MyProfilePage .lia-form-label.lia-revision-info-title:after, .MyProfilePage .lia-component-tkb-article-editor-form .lia-form-label.lia-related-messages-title:after, .lia-component-tkb-article-editor-form .MyProfilePage .lia-form-label.lia-related-messages-title:after, .KudosMessagePage label.lia-form-label:after, .KudosMessagePage .lia-form-label.lia-fieldset-title:after, .KudosMessagePage .lia-component-tkb-article-editor-form .lia-form-label.lia-form-compare-title:after, .lia-component-tkb-article-editor-form .KudosMessagePage .lia-form-label.lia-form-compare-title:after, .KudosMessagePage .lia-component-tkb-article-editor-form .lia-form-label.lia-revision-info-title:after, .lia-component-tkb-article-editor-form .KudosMessagePage .lia-form-label.lia-revision-info-title:after, .KudosMessagePage .lia-component-tkb-article-editor-form .lia-form-label.lia-related-messages-title:after, .lia-component-tkb-article-editor-form .KudosMessagePage .lia-form-label.lia-related-messages-title:after { // content: "(required)"; } .ForumPage span.in-english:after, .CategoryPage span.in-english:after { content: "EN"; } a.in-english:after { content: "EN"; } .nav-menu a.in-english:after { content: "EN"; } .header-search-wrapper{margin-right:5px;margin-top: 33px;} #lia-body .nav-wrapper .header-search-wrapper .lia-search-form-wrapper{position: relative;left: 0px;border:none;box-shadow:none;max-width: 460px;width: 100%;} #lia-body .nav-wrapper .header-search-wrapper .lia-button-searchForm-action{top:8px !important;position: absolute;right: 6px;min-width: auto !important; background-color: transparent !important;border: none !important;max-height: 18px !important; background-size:contain !important;background-image: url(/html/assets/search-icon.svg)} #lia-body .nav-wrapper .header-search-wrapper .search-input::-webkit-input-placeholder{font-size: 16px !important;} #lia-body .nav-wrapper .header-search-wrapper .search-input{height: 32px !important;padding: 10px !important;padding-right: 50px !important;border: 1px solid #CBD6E2 !important;box-shadow: none !important;background-color: white !important;font-size: 16px !important; border-radius: 3px !important;color: #33475B !important;line-height:24px !important;} #lia-body .header-search-wrapper .lia-search-input-wrapper {width: 460px !important;} #lia-body .nav-wrapper .header-search-wrapper .lia-search-input-wrapper input:focus {box-shadow: 0 0 4px 1px rgb(255 255 255 / 30%), 0 0 0 1px #fff!important;} .header-search-wrapper .lia-search-granularity-wrapper .lia-search-form-granularity{display:none !important;} .header-search-wrapper .lia-search-granularity-wrapper:before{display:none !important;} @media only screen and (max-width: 992px) { #lia-body .nav-wrapper .header-search-wrapper .lia-search-form-wrapper{z-index: 1;} } @media only screen and (max-width: 767px) { .user-nav-bar{ position: relative; height: 100%; } #lia-body .header-search-wrapper .lia-search-input-wrapper{ width: 100% !important; max-width: none;} #lia-body .nav-wrapper .header-search-wrapper .lia-search-form-wrapper{padding: 10px 24px !important; background: #fff;border: 1px solid #eee2e2;margin-left: 4px;max-width:none !important;width: 100%;} #lia-body .nav-wrapper .header-search-wrapper .lia-button-searchForm-action {right:20px;top:17px !important;} .header-search-wrapper{display:none; position: absolute; top: 121px; width: 100% !important;} #lia-body .nav-wrapper .header-search-wrapper .lia-search-input-message{ margin-right:-7px;} #lia-body .nav-wrapper .header-search-wrapper .lia-search-form-wrapper::before{position: absolute; top: -6px;content: "";right: 67px;width: 10px;height: 10px;background-color: white;transform: rotate(45deg);border-top: 1px solid #eee2e2; border-left: 1px solid #eee2e2;} } </style> <section class="community-header-nav v2"> <div class="nav-wrapper"> <div class="user-nav-bar"> <nav class="nav-menu template-centered template-section"> <div class="lang-picker-wrapper"> <div class="lang-picker-container"> <a id="current-language" class="current-language"> <span class="lang-picker-globe-icon"></span> English </a> <div id="lang-picker-global" class="nav-popover lang-picker"> <div class="nav-popover-arrow" style="border-top-color: transparent; border-left-color: transparent; width: 20px; height: 20px; transform: rotate(-135deg); top: -10px; right: calc(50% - 48px);"></div> <ul id="lang-picker-dropdown" class="nav-dropdown-list"> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=en" class="nav-dropdown-link" data-lang="en"> <li class="nav-dropdown-item"> English </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=es" class="nav-dropdown-link" data-lang="es"> <li class="nav-dropdown-item"> Español </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=pt-br" class="nav-dropdown-link" data-lang="pt-br"> <li class="nav-dropdown-item"> Português </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=fr" class="nav-dropdown-link" data-lang="fr"> <li class="nav-dropdown-item"> Français </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=de" class="nav-dropdown-link" data-lang="de"> <li class="nav-dropdown-item"> Deutsch </li> </a> <a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781?profile.language=ja" class="nav-dropdown-link" data-lang="ja"> <li class="nav-dropdown-item"> 日本語 </li> </a> </ul> </div> </div></div> <div class="header-search-wrapper"> <div id='lia-searchformV32_23f26f3b976736' class='SearchForm lia-search-form-wrapper lia-mode-default lia-component-common-widget-search-form'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23f26f3b976736'></div> </div> <div id='searchautocompletetoggle_23f26f3b976736'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23f26f3b976736_0'></div> </div> <form enctype='multipart/form-data' class='lia-form lia-form-inline SearchForm' action='https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.form.form' method='post' id='form_23f26f3b976736' name='form_23f26f3b976736'><div class='t-invisible'><input value='user-id/169781' name='t:ac' type='hidden'></input><input value='search/contributions/page' name='t:cp' type='hidden'></input><input value='19tWR_RQUJU0ur3e3Kng3PPxgc-WfaYiUMae9QOTx78K7qjzGWcWY3V5Dftiaaf0o7Xg-uMgY7yRsZEvmMZohp3SheECcfl-HIh-YPuo95-O6U85Vv_yv9iYLa8FV6FL9ztQgrQYEr124XwANGLSIWOfqkaSQKnZ_CUtz5tTm6_eOiptSmdPNNmpMonYEiF6c25Rs7kM6JAM5bbEQMHLsbIuw_pymosAoyZ8eDV44xBWnm2SSOzdQcFbVsPsP74j3wylYoohKC3h7BxPP76sYPDbQzWua9QE3QpcQepYzHxfIY3UI_HAio6ZXJ0e2al9XDHzcyqCArqguzA_wNpmlxnhXEFSPc-MAozlZBWSD_ytSE5gHhPazcpDMwKskoFVVz39t4afjTg8JHfVwlMhv07xpHQY7y7q5XrCj3CJ29xD5fq2ObpXptwzO8GPX4fvsHJ5t2BaYa533-GWMyPDBgiFlEo_xFHu_xp71xbCsmjndzyMXNu21zYPVgi2IOhsUe93B6aMqpJmL48b7n5YtNNfmGBATg9zh5KQOAa1XAhVLvBM2EEXlHykvD3VoXhQ_0JXSPYQDD3242WYCgBfL1w-y6gsbft7hJhsiFLjwsWHhervJNG8PeljQUQ3bllWvTJq4xTyYQfZQdiR20CwDP5GcBaY47xeFrnmc1EBHnDw8gtRRdTDdHEvCf3ZCpWZPP8vpLA7XmdcTkkLnO6mhE5UqpJ-1syjpHFer6BOkz2OXGorYX-beIff_1T9hpfNVzShq9-I4ZpLq7dsCYH4O3JeqCJ2Jjom4xPoalkYwTcml2B9p7pN7hYceLvhh1B9ISDNUs7VdJHJJ-lD4KSXwre1R7C-bx5zVAQkmkcIH4R5_B_e2z94_vs1MM8AzCkg5F64sEBGLbiuxJtspjKk05DNfilealjWKglAYdVlpjBGG61dpvQMKwAN2IYNWLkqwqwElXJwLRuGnYMV6V2CvtSm48MN3N3tK1h4sGgVwxUgqF4I66J2qPctxjkkBjCVPok7tMjeI_xdg-BLfsnA_wcEmVyoK4TlcLaoFWb5v0lrdBvlWUlw5rN5qCAwrXXVpViS0UKMnc-iwh4cCmb7hO1MKBwjQIYfSPE974CTQmMpFiN6Uk8MmMKAlQIISQRZTIxMlozzpY-XYMESqOXcuvAFunZf8k4EHKaN3YJPgQiyGD0IZtGdDHaZXAm5nRtgfytOZLRVBZaXm6Gar8ler1fTCcc0n8bdAyOlJwO53yQ.' name='lia-form-context' type='hidden'></input><input value='ViewProfilePage:user-id/169781:searchformv32.form:' name='liaFormContentKey' type='hidden'></input><input value='5DI9GWMef1Esyz275vuiiOExwpQ=:H4sIAAAAAAAAALVSTU7CQBR+krAixkj0BrptjcpCMSbERGKCSmxcm+kwlGrbqTOvFDYexRMYL8HCnXfwAG5dubDtFKxgYgu4mrzvm3w/M+/pHcphHQ4kI4L2dMo9FLYZoM09qbeJxQ4V0+XC7e/tamqyBPEChwgbh1JAjQtLIz6hPaYh8ZlEMaxplAvm2KZmEsm0hhmBhOKpzZzOlsEw8LevR5W3zZfPEqy0oJIYc+eCuAyh2rolfaI7xLN0I8rjWfWBj7CuzJvf5osmbxRN3hacMimNwHRtKSOr0XNnv/vx+FoCGPjhMRzljhNLYHrEt9kA5T08ACCsKvREoYuqxqLl8BLO84q4UcMITcG49y/QOGs1pYyESl5p6V6qwRW086rinVmoxMZsiZud/zBUTc6gmVc4kExkJafmcYG1GM9+wfIsCkf2OP54hal5EjnG54z8h0XhjfcF7wQUs5Kz0GTjU2rOjc/llTT4Au07pDOcBQAA' name='t:formdata' type='hidden'></input></div> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='feedback_23f26f3b976736'></div> </div> <input value='2TfTsYn4J7OX3Qi7rEuu4JwLHEVLIvbwbh1jHemY7FQ.' name='lia-action-token' type='hidden'></input> <input value='form_23f26f3b976736' id='form_UIDform_23f26f3b976736' name='form_UID' type='hidden'></input> <input value='' id='form_instance_keyform_23f26f3b976736' name='form_instance_key' type='hidden'></input> <span class='lia-search-granularity-wrapper'> <select title='Search Granularity' class='lia-search-form-granularity search-granularity' aria-label='Search Granularity' id='searchGranularity_23f26f3b976736' name='searchGranularity'><option title='All Results' selected='selected' value='mjmao93648|community'>All Results</option><option title='kennedyp' value='169781|authorMessages'>kennedyp</option><option title='Users' value='user|user'>Users</option></select> </span> <span class='lia-search-input-wrapper'> <span class='lia-search-input-field'> <span class='lia-button-wrapper lia-button-wrapper-secondary lia-button-wrapper-searchForm-action'><input value='searchForm' name='submitContextX' type='hidden'></input><input class='lia-button lia-button-secondary lia-button-searchForm-action' value='Search' id='submitContext_23f26f3b976736' name='submitContext' type='submit'></input></span> <input placeholder='Search the Community' aria-label='Search' title='Search' class='lia-form-type-text lia-autocomplete-input search-input lia-search-input-message' value='' id='messageSearchField_23f26f3b976736_0' name='messageSearchField' type='text'></input> <input placeholder='Search the Community' aria-label='Search' title='Search' class='lia-form-type-text lia-autocomplete-input search-input lia-search-input-tkb-article lia-js-hidden' value='' id='messageSearchField_23f26f3b976736_1' name='messageSearchField_0' type='text'></input> <input ng-non-bindable='' title='Enter a user name or rank' class='lia-form-type-text UserSearchField lia-search-input-user search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a user name or rank' value='' id='userSearchField_23f26f3b976736' name='userSearchField' type='text'></input> <input placeholder='Enter a keyword to search within the private messages' title='Enter a search word' class='lia-form-type-text NoteSearchField lia-search-input-note search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a search word' value='' id='noteSearchField_23f26f3b976736_0' name='noteSearchField' type='text'></input> <input title='Enter a search word' class='lia-form-type-text ProductSearchField lia-search-input-product search-input lia-js-hidden lia-autocomplete-input' aria-label='Enter a search word' value='' id='productSearchField_23f26f3b976736' name='productSearchField' type='text'></input> <input class='lia-as-search-action-id' name='as-search-action-id' type='hidden'></input> </span> </span> <span class='lia-cancel-search'>cancel</span> </form> <div class='search-autocomplete-toggle-link lia-js-hidden'> <span> <a class='lia-link-navigation auto-complete-toggle-on lia-link-ticket-post-action lia-component-search-action-enable-auto-complete' data-lia-action-token='zKax2M9kBFyu_kX_Slix2QBdSCbDNQq7kWSS1NzfC88.' rel='nofollow' id='enableAutoComplete_23f26f3b976736' href='https://community.hubspot.com/t5/user/v2/viewprofilepage.enableautocomplete:enableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions'>Turn on suggestions</a> <span class='HelpIcon'> <a class='lia-link-navigation help-icon lia-tooltip-trigger' role='button' aria-label='Help Icon' id='link_23f26f3b976736' href='#'><span class='lia-img-icon-help lia-fa-icon lia-fa-help lia-fa' alt='Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.' aria-label='Help Icon' role='img' id='display_23f26f3b976736'></span></a><div role='alertdialog' class='lia-content lia-tooltip-pos-bottom-left lia-panel-tooltip-wrapper' id='link_23f26f3b976736_0-tooltip-element'><div class='lia-tooltip-arrow'></div><div class='lia-panel-tooltip'><div class='content'>Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.</div></div></div> </span> </span> </div> </div> <div class='spell-check-showing-result'> Showing results for <span class='lia-link-navigation show-results-for-link lia-link-disabled' aria-disabled='true' id='showingResult_23f26f3b976736'></span> </div> <div> <span class='spell-check-search-instead'> Search instead for <a class='lia-link-navigation search-instead-for-link' rel='nofollow' id='searchInstead_23f26f3b976736' href='#'></a> </span> </div> <div class='spell-check-do-you-mean lia-component-search-widget-spellcheck'> Did you mean: <a class='lia-link-navigation do-you-mean-link' rel='nofollow' id='doYouMean_23f26f3b976736' href='#'></a> </div> </div> </div> <div class="user-nav-options"> <!-- community-header-nav-upper-v2 --> <style>.get-hubspot .nav-popover-arrow {border-top-color: transparent; border-left-color: transparent; width: 20px; height: 20px; transform: rotate(-135deg); top: -10px; display: block;}</style> <div class="search-icon-plus-top"> <button class="lia-button search-toggle-action-icon-plus"><img src='https://community.hubspot.com/html/@5320E40129AA1377479EABCA2009B53A/assets/Start-dicsucssion.svg' alt=""><i class="lia-fa lia-fa-caret-down"></i></button> <div class="plus-bar-main-content" style="display: none;"> <ul id="plus-bar-top-main"> <li class="plus-bar"> <a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fforums%2Fpostpage%2Fcategory-id%2Fhubspot_community_en%2Fchoose-node%2Ftrue" class="white-btn transpaent"><i><img src="https://community.hubspot.com/html/@38F5B2AB35958F39F47C2BFFE5486135/assets/Edit.svg"></i> Create post</a> </li> <li class="plus-bar"><a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fforums%2Fpostpage%2Fboard-id%2FHubSpot_Ideas"><i><img src="https://community.hubspot.com/html/@C71A42AEB76B8A3A82335DA9F5B9C717/assets/lightbulb.svg"></i> Submit Idea</a></li> </ul> </div> </div> <div class="login-container"> <a class='lia-link-navigation login-link lia-authentication-link lia-component-users-action-login' rel='nofollow' id='loginPageV2_23f26f3cc9a1ab' href='https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F169781'>Log in</a> <a id="get-hubspot-free" class="btn btn-sm button-tertiary get-hubspot">Get HubSpot free</a> <div id="get-hubspot" class="get-hubspot"> <div class="nav-popover-arrow"></div> <ul id="get-hs-dropdown" class="nav-dropdown-list"> <li class="nav-dropdown-item nav-crm"> <a href="https://www.hubspot.com/products/get-started" class="nav-dropdown-link nav-crm"> Get a HubSpot CRM account </a> </li> <li class="nav-dropdown-item nav-dev"> <a href="https://developers.hubspot.com/" class="nav-dropdown-link nav-dev"> Get a HubSpot developer account </a> </li> </ul> </div> </div> </div> </nav> </div> <div class="forum-nav-bar"> <nav class="nav-menu template-centered template-section"> <a href="/" id="nav-logo" class="en nav-wordmark"> <img src='https://community.hubspot.com/html/@60C06466C7735C4373198758B1428669/assets/Community-logo-new.svg' alt="hubspot" class="nav-wordmark"> <span class="page-title nav-wordmark">Community</span> </a> <a href="/" id="nav-logo-v2" class="en nav-wordmark v2"> <img src='https://community.hubspot.com/html/@813D252A70F0A7024C8EA3BB1B8B9CFD/assets/sticky-logo.png' alt="hubspot" class="nav-wordmark"> </a> <style> .jp-resources-list{ display:flex; flex-direction:column; } .jp-resources-list>li.nav-dropdown-item.jp-class-HubSpot.Community.Blog{ order:1; } .nav-external-link::after { background: none; content: "\f08e"; font-family: "FontAwesome"; margin-left: 5px; font-size: 14px; } </style> <div class="nav-group-wrapper"> <div class="nav-menu"> <ul class="nav-group-primary"> <li class="nav-group-item nav-group-item-has-dropdown"> <div class="nav-link-wrapper"> <a class="nav-link forum"> <span class="nav-link-label"> Discussions </span> <span class="dropdown-caret"></span> </a> <div class="nav-popover-arrow"></div> <div class="nav-popover forum"> <ul class="nav-dropdown-list"> <li class="nav-dropdown-item"> <a href="/t5/CRM-Sales-Hub/ct-p/sales" class="nav-dropdown-link nav-discussions-crm"> CRM & Sales </a> </li> <li class="nav-dropdown-item"> <a href="/t5/Marketing-Hub/ct-p/marketing" class="nav-dropdown-link nav-discussions-mktg"> Marketing & Content </a> </li> <li class="nav-dropdown-item"> <a href="/t5/Service-Hub/ct-p/service_hub" class="nav-dropdown-link nav-discussions-svc"> Customer Success & Service </a> </li> <li class="nav-dropdown-item"> <a href="/t5/Operations/ct-p/Operations" class="nav-dropdown-link nav-discussions-ops"> RevOps & Operations </a> </li> <li class="nav-dropdown-item"> <a href="/t5/Commerce/ct-p/commerce" class="nav-dropdown-link nav-discussions-commerce "> Commerce </a> </li> <li class="nav-dropdown-item"> <a href="/t5/HubSpot-Developers/ct-p/developers" class="nav-dropdown-link nav-discussions-developers "> Developers </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Getting-Started-on-the-Community/How-to-join-the-Solutions-Partner-Program/ba-p/400205" class="nav-dropdown-link nav-partners "> Partners </a> </li> <li class="nav-dropdown-item"> <a href="/t5/HubSpot-Ideas/idb-p/HubSpot_Ideas" class="nav-dropdown-link nav-discussions-ideas"> Ideas </a> </li> </ul> </div> </div> </li> <li class="nav-group-item nav-group-item-has-dropdown secondInList"> <div class="nav-link-wrapper"> <a class="nav-link forum"> <span class="nav-link-label"> Academy </span> <span class="dropdown-caret"></span> </a> <div class="nav-popover-arrow"></div> <div class="nav-popover forum"> <ul class="nav-dropdown-list"> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/courses" class="nav-dropdown-link nav-external-link" target="_blank"> Courses </a> </li> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/certification-overview" class="nav-dropdown-link nav-external-link" target="_blank"> Certifications </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/academy/bootcamps/home" class="nav-dropdown-link nav-external-link" target="_blank"> Bootcamps </a> </li> <li class="nav-dropdown-item"> <a href="https://academy.hubspot.com/learning-paths" class="nav-dropdown-link nav-external-link" target="_blank"> Learning Paths </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/HubSpot-Academy-Support/bd-p/certifications_help" class="nav-dropdown-link "> Academy Support </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Study-Groups/ct-p/study-groups" class="nav-dropdown-link"> Study Groups </a> </li> </ul> </div> </div> </li> <li class="nav-group-item"> <div class="nav-link-wrapper"> <a class="nav-link nav-resources"> <span class="nav-link-label"> Resources </span> <span class="dropdown-caret"></span> </a> <div class="nav-popover-arrow"></div> <div class="nav-popover forum"> <ul class="nav-dropdown-list jp-resources-list"> <li class="nav-dropdown-item "> <a href="/t5/Getting-Started/ct-p/getting_started" class="nav-dropdown-link nav-discussions-gs"> Getting Started </a> </li> <li class="nav-dropdown-item"> <a href="https://help.hubspot.com/" class="nav-dropdown-link nav-external-link" target="_blank"> Help Center </a> </li> <li class="nav-dropdown-item"> <a href="https://knowledge.hubspot.com/" class="nav-dropdown-link nav-external-link" target="_blank"> Knowledge Base </a> </li> <li class="nav-dropdown-item"> <a href="https://developers.hubspot.com/docs/api/overview" class="nav-dropdown-link nav-external-link" target="_blank"> API Documentation </a> </li> <li class="nav-dropdown-item"> <a href="https://developers.hubspot.com/docs/cms" class="nav-dropdown-link nav-external-link" target="_blank"> CMS Documentation </a> </li> <li class="nav-dropdown-item "> <a href="/t5/News-Networking-Events/ct-p/communityboard" class="nav-dropdown-link nav-news"> News </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=webinars&order_by=last_updated" class="nav-dropdown-link nav-resource-blog-Webinars "> Webinars </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=releases-updates" class="nav-dropdown-link nav-resource-blog-Releases and Updates "> Releases and Updates </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=hubspot-community-blog" class="nav-dropdown-link nav-resource-blog-HubSpot Community Blog "> Community Blog </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=sales-hub-community-perspectives" class="nav-dropdown-link nav-resource-blog-Sales Hub Community Perspectives "> Sales Hub Community Perspectives </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=workflows_library" class="nav-dropdown-link nav-resource-blog-Workflows Library "> Workflows Library </a> </li> <li class="nav-dropdown-item "> <a href="/t5/Resources/ct-p/resources?node_id=ai-library" class="nav-dropdown-link nav-resource-blog-Breeze Library "> Breeze Library </a> </li> </ul> </div> </div> </li> <li class="nav-group-item nav-group-item-has-dropdown"> <div class="nav-link-wrapper"> <a class="nav-link forum"> <span class="nav-link-label"> Events </span> <span class="dropdown-caret"></span> </a> <div class="nav-popover-arrow"></div> <div class="nav-popover forum"> <ul class="nav-dropdown-list"> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Ask-Me-Anything-and-Panel/bd-p/ama_discussions" class="nav-dropdown-link "> AMA </a> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/Community-Led-Events/bd-p/adapt" class="nav-dropdown-link "> Community Led Events </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/resources/webinar" class="nav-dropdown-link nav-external-link" target="_blank"> Webinars </a> </li> <li class="nav-dropdown-item"> <a href="https://www.hubspot.com/hubspot-user-groups" class="nav-dropdown-link nav-external-link" target="_blank"> HUGS </a> </li> </ul> </div> </div> </li> <li class="nav-group-item nav-group-item-has-dropdown"> <div class="nav-link-wrapper"> <a class="nav-link forum"> <span class="nav-link-label"> Advocacy </span> <span class="dropdown-caret"></span> </a> <div class="nav-popover-arrow"></div> <div class="nav-popover forum"> <ul class="nav-dropdown-list"> <li class="nav-dropdown-item"> <a href='/t5/Advocacy/ct-p/advocacy' class="nav-dropdown-link nav-hubFans-program "> Community Champions Program </a> </li> <li class="nav-dropdown-item"> <a href="/t5/Advocates-Blog/bg-p/advocates-blog" class="nav-dropdown-link nav-adovcates-blog "> Champions Blog </a> </li> </ul> </div> </div> </li> </ul> <div class="login-container v2"> <style> #lia-body .community-header-nav.v2 .forum-nav-bar.ch-sticky .nav-menu > ul.nav-group-primary { transform: none !important; } #lia-body .community-header-nav.v2 .forum-nav-bar.ch-sticky .login-container.v2 { display: flex; align-items: center; gap: 12px; padding-left: 0; width: 116px; } #lia-body.lia-user-status-anonymous .community-header-nav.v2 .forum-nav-bar.ch-sticky .login-container.v2 { gap: 0; width: 24px; } .ch-sticky .nav-menu { gap: 36px; } .custom-search-focus { background-image: url('https://community.hubspot.com/html/@C11BAC294B66222FECF4AA35890ACE71/assets/search-icon.svg'); background-repeat: no-repeat; background-size: contain; cursor: pointer; margin: 0; width: 24px; height: 24px; } </style> <label class="custom-search-focus"></label> <div class="nav-link-wrapper custom-user-menu-v2"> <div class="nav-popover-arrow"></div> <div class="nav-popover profile first"> <div class="header-dropdown-menu"> <div class="user-heading">Anonymous</div> <ul class="header-tab-nav"> <li><span id="profile" class="active">Profile</span></li> </ul> <div class="header-tab-nav-content"> <div id="profile-list-wrapper"> <div class="nav-link-wrapper"> <a href="https://app.hubspot.com/l/reports-dashboard/" class="text-link my-account nav-hubspot-account" target="_blank"> Go to my HubSpot Account </a> </div> <ul class="nav-dropdown-list"> <li class="nav-dropdown-item"> <a href="/t5/user/viewprofilepage/user-id/-1" class="nav-dropdown-link nav-account-profile-2"> My Profile </a> </li> <li class="nav-dropdown-item"> <a href="/t5/user/myprofilepage/tab/personal-profile" class="nav-dropdown-link nav-account-user-settings"> Settings </a> </li> <li class="nav-dropdown-item"> </li> <li class="nav-dropdown-item"> <a href="https://community.hubspot.com/t5/community/page.logoutpage?t:cp=authentication/contributions/unticketedauthenticationactions&dest_url=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fv2%2Fviewprofilepage%2Fuser-id%2F169781&lia-action-token=vDT37dZJ6kvKVKP560soPLtqC_xfVb0KuEyR2AgcUww.&lia-action-token-id=logoff" class="nav-dropdown-link nav-account-sign-out"> Sign out </a> </li> </ul> </div> <div id="admin-list-wrapper" style="display: none;"> <div class="admin-menu-list"> </div> </div> </div> </div> </div> </div> </div> </div> </div> </nav> </div> </div> </section> </div> </div> </div><div class="lia-quilt-row lia-quilt-row-footer"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-footer lia-mark-empty"> </div> </div> </div> <!-- homepage-search-hero-v2 --> <section class="custom-hero-banner-v2 page-full-width" style="background-image:url('https://community.hubspot.com/html/@CD1DC873BBF045E1B8173D31F4DE4841/assets/sales-BannerImg.png');"> <div class="custom-v2-banner"> <div class="custom-v2-banner__announcement"><div class="custom-announcement__title"> <div class="custom-v2-banner__announcement"> <div class="community-banner-message transparent"> <div class="banner-message-inner-wrapper"> <div class="hsg-promo-bar__wrapper" data-background="gradient4"> <div class="hsg-page-width-normal" data-background-image="circle2"> <div class="hsg-promo-bar" id="promo-bar"> </div> </div> </div> </div> </div> </div> <style> .lia-page { overflow-x: hidden; } .custom-v2-banner__announcement:not(:has(.transparent)) { margin-bottom: -20px; transform: translateY(-20px); } .CommunityPage .custom-v2-banner__announcement:not(:has(.transparent)) { margin-bottom: -72px; transform: translateY(-72px); } #promo-bar { background: #c1eaeb; border: 0; padding: 16px 0; position: relative; } #promo-bar:before, #promo-bar:after { background: #c1eaeb; content: ''; width: 300%; position: absolute; top: 0; bottom: 0; left: 0; } #promo-bar:before { transform: translateX(-100%); } #promo-bar:after { left: auto; right: 0; transform: translateX(100%); } #promo-bar .banner-message-text { display: flex; gap: 16px; align-items: center; justify-content: center; font-size: 16px !important; font-weight: 300; line-height: 1.75; text-align: center; } #promo-bar .banner-message-text .banner-cta-link { margin: 0; text-align: center; } @media only screen and (max-width: 767px) { #promo-bar .banner-message-wrapper:not(:first-child) { display: none; } #promo-bar .banner-message-text { flex-direction: column; gap: 8px; } #promo-bar .banner-message-text .banner-cta-link { min-width: 50%; } } @media only screen and (max-width: 1023px) { .custom-v2-banner__announcement:not(:has(.transparent)) { margin-bottom: -30px !important; transform: translateY(-30px) !important; } } </style> <script> const userLang = 'en'; const injectContent = (results) => { let promoBar = document.querySelector('#promo-bar'); // glowingblue: Remove stage test announcements if there are real ones if ( results.length ) { promoBar.replaceChildren(); } results.forEach(result => { if (result.message.length) { if (document.querySelector('.community-banner-message.transparent')) { document.querySelector('.community-banner-message.transparent').classList.remove('transparent'); } let messageWrapper = document.createElement('div'); messageWrapper.classList.add('banner-message-wrapper'); let message = document.createElement('p'); message.classList.add("banner-message-text"); message.innerHTML = result.message.trim(); promoBar.appendChild(messageWrapper); messageWrapper.appendChild(message) if (result.ctaLink.length && result.ctaText.length) { let ctaWrapper = document.createElement('div'); let cta = document.createElement('a'); ctaWrapper.classList.add('banner-cta-link'); cta.classList.add('banner-cta-link'); cta.setAttribute('href',result.ctaLink.trim()); cta.innerHTML = result.ctaText.trim(); message.appendChild(cta); } } else { return; } }) } const processBanner = (results) => { if (sessionStorage.getItem("HSCommunityAnnouncements")) { let data = JSON.parse(sessionStorage.getItem('HSCommunityAnnouncements')); if (data.lang === userLang) { data.messages = results; sessionStorage.setItem("HSCommunityAnnouncements", JSON.stringify(data)); return; } else { injectContent(results); data.lang = userLang; data.messages = results; sessionStorage.setItem("HSCommunityAnnouncements", JSON.stringify(data)); } } else { injectContent(results); messageData = { lang: userLang, messages: results } sessionStorage.setItem("HSCommunityAnnouncements", JSON.stringify(messageData)); } } if (sessionStorage.getItem('HSCommunityAnnouncements')) { let data = JSON.parse(sessionStorage.getItem('HSCommunityAnnouncements')); if (userLang === data.lang) { injectContent(data.messages); } } </script> <script src="https://script.google.com/a/hubspot.com/macros/s/AKfycbySpmgzEjP1bEnrLD5xM_fdMOxDn47Kt9buJO9HtQER7pZmhu0/exec?lang=en&callback=processBanner" async> </script> </div></div> <div class="custom-v2-banner__wrapper"> <div class="page-breadcrumb-wrapper"> <div aria-label="breadcrumbs" role="navigation" class="BreadCrumb crumb-line lia-breadcrumb lia-component-common-widget-breadcrumb"> <ul role="list" class="lia-list-standard-inline"> <li class="lia-breadcrumb-node crumb"> <a href="/" class="crumb-community lia-breadcrumb-community lia-breadcrumb-forum community-home">HubSpot Community</a> </li> <li class="lia-breadcrumb-node crumb final-crumb"> <span>About kennedyp</span> </li> </ul> </div> </div> <div class="page-title-wrapper"> <h2 class="page-title">Profile</h2> </div> </div> </div> <section class="profile-info-flex"> <div class="profile-info-wrapper page-width-body"> <div class="profile-info-column user-basics"> <div class="profile-wrapper"><div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar lia-component-user-avatar"> <span class="UserAvatar lia-link-navigation lia-link-disabled" aria-disabled="true" target="_self" id="link_1_23f26f43475d37"><img class="lia-user-avatar-profile" title="kennedyp" alt="kennedyp" id="display_23f26f43475d37" src="/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"/> </span> </div> </div> <div class="text-details pos:r w:100%"> <span class="name"><div class="lia-user-name lia-component-users-widget-profile-user-name"> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="color:#30465c" target="_self" aria-label="View Profile of kennedyp" itemprop="url" id="link_23f26f435b54a5" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781"><span class="login-bold">kennedyp</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Community Manager" alt="Community Manager" id="display_0_23f26f435b54a5" src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"/> </span> </div> <span class="proNoun"> </span> </span> <span class="rank community-manager"> <div class="otb-rank-title"><div class="lia-user-rank lia-component-user-rank"> Community Manager </div></div> <span class="rank-icon"><img src='https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png'></span> </span> <div class="lia-user-registration-date lia-component-registration-date"> <span class="lia-registration-date-title">Member since </span><span class="DateTime lia-component-common-widget-date"> <span class="local-date">‎Feb 25, 2021</span> </span> </div> <div class="profile-info-column user-contact !d:f !d:f(row) !d:ai(center) !d:jc(start) d:gap12"> <a class="btn btn-sm button-primary" href="/t5/notes/privatenotespage/tab/compose/note-to-user-id/169781">Message</a> <ul class="cmp-profile-socials d:f d:gap12 m:0" data-cmp="cmp.profile.socials" un-cloak> </ul> </div> </div> </div> <ul class="profile-info-column user-stats"> <li class="stat"> <h6>solutions</h6> <span>68</span> </li> <li class="stat"> <h6>Replies</h6> <span>573</span> </li> <li class="stat"> <h6>Upvotes Given</h6> <span>521</span> </li> <li class="stat2"> </li> <li class="stat"> <h6>Upvotes Received</h6> <span>185</span> </li> <li class="stat"> <h6>Ideas</h6> <span>0</span> </li> </ul> </div> <div class="profile-about-wrapper page-width-body"> <div class="profile-about-column"> <h2>about</h2> <div class="bio"> <h5> </h5> <h5> </h5> <h5></h5> <p>Please <a href='https://app.hubspot.com/khoros/integration/staging/jwt/authenticate?referer=https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781'>sign in to the Community</a> or <a href='https://www.hubspot.com/products/get-started'>get HubSpot free</a> to view user profiles</p> </div> </div> <div class="profile-friend-column"> <h2>Following</h2> <div class="friends"> <ul class="friend-list"> <li class="friend"> <a href="/t5/user/viewprofilepage/user-id/9374"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/107287i4AC562C3ED7606EC/image-dimensions/150x150/image-coordinates/57%2C51%2C1029%2C1023?v=v2" /></a> <a href="/t5/user/viewprofilepage/user-id/9374">trevordjones</a> <span class="online-status offline "></span> </li> <li class="friend"> <a href="/t5/user/viewprofilepage/user-id/15"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/1930i41077A0D49B7BB43/image-dimensions/150x150/image-coordinates/25%2C0%2C411%2C386?v=v2" /></a> <a href="/t5/user/viewprofilepage/user-id/15">sjudson</a> <span class="online-status offline "></span> </li> <li class="friend"> <a href="/t5/user/viewprofilepage/user-id/513582"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/98878i663D0A9F2F64F4F0/image-dimensions/150x150/image-coordinates/0%2C4%2C1238%2C1242?v=v2" /></a> <a href="/t5/user/viewprofilepage/user-id/513582">Shadab_Khan</a> <span class="online-status offline "></span> </li> </ul> </div> </div> </div> </section> </section> <script type="application/ld+json" data-cmp="cmp.seo.profile.structured-data"> { "@context": "https://schema.org", "@type": "ProfilePage", "dateCreated": "Thu Feb 25 15:28:31 PST 2021", "mainEntity": { "@type": "Person", "name": "Kennedy Poirier", "alternateName": "kennedyp", "identifier": "169781", "agentInteractionStatistic": [ { "@type": "InteractionCounter", "interactionType": "https://schema.org/FollowAction", "userInteractionCount": 15 }, { "@type": "InteractionCounter", "interactionType": "https://schema.org/LikeAction", "userInteractionCount": 522 }, { "@type": "InteractionCounter", "interactionType": "https://schema.org/WriteAction", "userInteractionCount": 583 } ], "description": "Community Manager", "image": "https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150?v=v2" } } </script> </div> </div> </div><div class="lia-quilt-row lia-quilt-row-main"> <div class="lia-quilt-column lia-quilt-column-18 lia-quilt-column-left lia-quilt-column-main-content"> <div class="lia-quilt-column-alley lia-quilt-column-alley-left"> <style> .wrapper{ margin:12px 12px 53px 0px; } .wrapper .real-name-of-the-use, .wrapper .bio-goes-here-lorem { color: #33475B; letter-spacing: 0; } .wrapper .real-name-of-the-use{ font-size: 26px; line-height: 36px; margin-bottom: 18px; font-weight: bold; } .wrapper .bio-goes-here-lorem{ font-size: 14px; line-height: 24px; font-weight: 500; } .cta { color: #00A4BD; font-size: 12px; font-weight: 600; letter-spacing: 0; line-height: 12px; } </style> <div class="wrapper"> <h2 class="real-name-of-the-use">Kennedy Poirier</h2> <p>This user hasn't created a bio yet. <a href='/t5/user/myprofilepage/tab/personal-profile%3Apersonal-info'>Edit your own profile here!</a></p> </div> <style> .ViewProfilePage .lia-list-row-thread-solved:after { text-transform: uppercase; content: 'Solved'!important; } .ViewProfilePage section.posts-feed-flex .lia-table-head .deleteIconColumn:after { content: 'delete'!important; } .ViewProfilePage section.posts-feed-flex .lia-table-head .autosaveSubjectColumn:after { content: 'subject'!important; } .no-content, .bookmark-empty-state { display: none; } .lia-panel .lia-view-all .lia-link-navigation:before{ content: 'View all'!important; } </style> <section class="posts-feed-flex profile-page-heading"> <div class="post-feed-wrapper page-width-body"> <h2>Activity</h2> <div class="tabs"> <div class="tabs-list-wrapper"> <nav class="tabs-list"> <button type="button" class="tab button-unstyled tab-active" id="latest"> recent <span class="tab-indicator"></span> </button> <button type="button" class="tab button-unstyled" id="top"> Most Upvotes <span class="tab-indicator"></span> </button> <button type="button" class="tab button-unstyled" id="solutions"> Accepted Solutions <span class="tab-indicator"></span> </button> </nav> </div> <select class="profilepage_select"> <option value="latest" selected >recent</option> <option value="top" >Most Upvotes</option> <option value="solutions" >Accepted Solutions</option> <option value="drafts" >Drafts</option> <option value="subscription" >Subscriptions</option> <option value="bookmarks" >Bookmarks</option> </select> </div> <div class="feeds"> <div id="latest-content" class="tab-content"> <!-- --> <div class="category-filtered-posts-wrapper"> <div class="message-list-wrapper"> <div id="msgList"> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/861352"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/861352" class="author-name-link"><span class="author-username">JPaulo1</span></a> on <span class="msg-created-date"> November 22, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/APIs-Integrations/I-would-like-to-know-if-a-webhook-request-always-involves-a/td-p/1073927"> I would like to know if a webhook request always involves a unique objectId </a> </div> <div class="msg-body"> I would like to know if a webhook request always involves a single unique objectId, or if a single request can include events with different objectIds, such as two different actions like contract creation and company creation. </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FAPIs-Integrations%2FI-would-like-to-know-if-a-webhook-request-always-involves-a%2Ftd-p%2F1073927%23reply">Reply</a> <a href="/t5/APIs-Integrations/I-would-like-to-know-if-a-webhook-request-always-involves-a/td-p/1073927" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/APIs-Integrations/bd-p/integrations/label-name/Webhooks" class="blue-btn msg-btn">Webhooks</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">2</span> <span title="Total posts">Replies</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 22, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hi @JPaulo1 ! Welcome to the Community-- happy to have you here Thanks for the response @zach_threadint ! I'd like to invite @CateDua...<a href="/t5/APIs-Integrations/I-would-like-to-know-if-a-webhook-request-always-involves-a/td-p/1073927" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/859980"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/859980" class="author-name-link"><span class="author-username">TRichartz5</span></a> on <span class="msg-created-date"> November 20, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/Sales-Integrations/sync-to-Salesforce/td-p/1072299"> sync to Salesforce </a> </div> <div class="msg-body"> We previously had a paid HubSpot subscription with an integration to Salesforce, but after canceling our subscription, we’re now using a free HubSpot account. We understand that with a free account, syncing with Salesforce should no longer be possib <a href="/t5/Sales-Integrations/sync-to-Salesforce/td-p/1072299" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FSales-Integrations%2Fsync-to-Salesforce%2Ftd-p%2F1072299%23reply">Reply</a> <a href="/t5/Sales-Integrations/sync-to-Salesforce/td-p/1072299" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/Sales-Integrations/bd-p/sales_integrations/label-name/FREE" class="blue-btn msg-btn">FREE</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">3</span> <span title="Total posts">Replies</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 22, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hey @TRichartz5 ! So glad you found that. Those scopes would all pertain to the HubSpot connection, so your Salesforce data should not be affected! ...<a href="/t5/Sales-Integrations/sync-to-Salesforce/td-p/1072299" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/861312"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/861312" class="author-name-link"><span class="author-username">Ldennis23</span></a> on <span class="msg-created-date"> November 22, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/Outlook-Sales-Extension/Emails-not-sending-Sending-Limit/td-p/1073870"> Emails not sending/Sending Limit? </a> </div> <div class="msg-body"> All my emails sent through HubSpot are undeliverable. Sales told me it's because I've exceeded some kind of email limit. Is this a new rule in Outlook? I've never heard of daily limits before, and I've sent over 50 emails a day using other CRM plat <a href="/t5/Outlook-Sales-Extension/Emails-not-sending-Sending-Limit/td-p/1073870" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FOutlook-Sales-Extension%2FEmails-not-sending-Sending-Limit%2Ftd-p%2F1073870%23reply">Reply</a> <a href="/t5/Outlook-Sales-Extension/Emails-not-sending-Sending-Limit/td-p/1073870" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/Outlook-Sales-Extension/bd-p/sales_outlook/label-name/Connected%20Email" class="blue-btn msg-btn">Connected Email</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">1</span> <span title="Total posts">Reply</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 22, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hey @Ldennis23 ! Thanks for your post. HubSpot does have rolling daily sending limits for connected emails based on email provider limits...<a href="/t5/Outlook-Sales-Extension/Emails-not-sending-Sending-Limit/td-p/1073870" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/845082"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/845082" class="author-name-link"><span class="author-username">JBLacey</span></a> on <span class="msg-created-date"> November 22, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/Reporting-Analytics/Do-campaign-start-and-end-date-affect-attribution/td-p/1073857"> Do campaign start and end date affect attribution? </a> </div> <div class="msg-body"> I haven't found a clear answer to this. I recently switched from Pardot, and there I know the dates affect reporting and attribution. Say for example, we are aponsoring a conference July 14-16 so I put July 14 as the start and July 16 as the end, th <a href="/t5/Reporting-Analytics/Do-campaign-start-and-end-date-affect-attribution/td-p/1073857" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FReporting-Analytics%2FDo-campaign-start-and-end-date-affect-attribution%2Ftd-p%2F1073857%23reply">Reply</a> <a href="/t5/Reporting-Analytics/Do-campaign-start-and-end-date-affect-attribution/td-p/1073857" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/Reporting-Analytics/bd-p/marketing_reports/label-name/Campaigns" class="blue-btn msg-btn">Campaigns</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">1</span> <span title="Total posts">Reply</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 22, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hi @JBLacey ! Welcome to the Community-- happy to have you here This is a great question. The attribution Knowledge Base article does note...<a href="/t5/Reporting-Analytics/Do-campaign-start-and-end-date-affect-attribution/td-p/1073857" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/861300"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/861300" class="author-name-link"><span class="author-username">ToddCMO</span></a> on <span class="msg-created-date"> November 22, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/Blog-Website-Page-Publishing/Four-Column-Card-Grid-images-do-not-show-on-website/td-p/1073855"> Four Column Card Grid images do not show on website </a> </div> <div class="msg-body"> I added a Four Column Card Grid to my website but the images I uploaded do not show in the Preview. Why? And how can this be fixed? </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FBlog-Website-Page-Publishing%2FFour-Column-Card-Grid-images-do-not-show-on-website%2Ftd-p%2F1073855%23reply">Reply</a> <a href="/t5/Blog-Website-Page-Publishing/Four-Column-Card-Grid-images-do-not-show-on-website/td-p/1073855" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/Blog-Website-Page-Publishing/bd-p/content_publishing/label-name/Website%20Pages" class="blue-btn msg-btn">Website Pages</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">1</span> <span title="Total posts">Reply</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 22, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hi @ToddCMO ! Welcome to the Community-- happy to have you here Are these images showing on your live site but not in the HubSpot preview?...<a href="/t5/Blog-Website-Page-Publishing/Four-Column-Card-Grid-images-do-not-show-on-website/td-p/1073855" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="solved-img">Solved</div> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/612154"><img src="https://community.hubspot.com/t5/image/serverpage/avatar-name/Avatar-Hubspot/avatar-theme/candy/avatar-collection/1_Hubspot/avatar-display-size/profile/version/2?xdesc=1.0"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/612154" class="author-name-link"><span class="author-username">genevagordon</span></a> on <span class="msg-created-date"> November 21, 2024 </span> </div> <div class="msg-subject msg-solved"> <a href="/t5/Sales-Hub-Tools/Send-meeting-information-to-redirected-page/td-p/1073307"> Send meeting information to redirected page </a> </div> <div class="msg-body"> Is there any way to pass meeting information to a redirected custom confirmation page? Right now if we have enabled a custom redirect, it just shows this simple confirmation icon for 3s and then redirects to our url with a bunch of tracking paramete <a href="/t5/Sales-Hub-Tools/Send-meeting-information-to-redirected-page/td-p/1073307" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FSales-Hub-Tools%2FSend-meeting-information-to-redirected-page%2Ftd-p%2F1073307%23reply">Reply</a> <a href="/t5/Sales-Hub-Tools/Send-meeting-information-to-redirected-page/td-p/1073307" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/Sales-Hub-Tools/bd-p/sales_tools/label-name/Meetings" class="blue-btn msg-btn">Meetings</a> <a href="/t5/Sales-Hub-Tools/bd-p/sales_tools/label-name/Sales" class="blue-btn msg-btn">Sales</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">0</span> <span title="Upvotes">upvote</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">2</span> <span title="Total posts">Replies</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> November 21, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hi @genevagordon ! Thanks for this post I understand you are hoping to populate meeting details on your custom confirmation page and bypass...<a href="/t5/Sales-Hub-Tools/Send-meeting-information-to-redirected-page/td-p/1073307" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="view-all-button-container"> <a class="btn btn-sm button-tertiary" href="/t5/forums/recentpostspage/post-type/message/user-id/169781">View latest posts</a> </div> <div class="pagination_div pagination-recent-post pagination" data-count="577"> <div class="holder recent_holder"></div> </div> </div> <div class="ajax-loading" style="display: none;"><img src="/html/assets/ajax-loadergif.gif"></div> </div> </div> </div> <div id="top-content" class="tab-content hidden"> <!-- --> <div class="category-filtered-posts-wrapper"> <div class="message-list-wrapper"> <div id="msgList"> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> on <span class="msg-created-date"> November 06, 2023 </span> </div> <div class="msg-subject "> <a href="/t5/Workflows-Library/Capture-Individual-Form-Submission-Dates-on-Contacts/ba-p/875566"> Capture Individual Form Submission Dates on Contacts </a> </div> <div class="msg-body"> HubSpot Forms are a valuable tool to collect new leads and grow your business. There are many ways to analyze form submission data to evaluate the performance of your lead capture. That said, some users seek more advanced date-based reporting <a href="/t5/Workflows-Library/Capture-Individual-Form-Submission-Dates-on-Contacts/ba-p/875566" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FWorkflows-Library%2FCapture-Individual-Form-Submission-Dates-on-Contacts%2Fba-p%2F875566%23reply">Reply</a> <a href="/t5/Workflows-Library/Capture-Individual-Form-Submission-Dates-on-Contacts/ba-p/875566" class="blog-btn msg-btn"> Blog </a> <a href="/t5/Workflows-Library/bg-p/workflows_library/label-name/CRM" class="blue-btn msg-btn">CRM</a> <a href="/t5/Workflows-Library/bg-p/workflows_library/label-name/Enterprise" class="blue-btn msg-btn">Enterprise</a> <a href="/t5/Workflows-Library/bg-p/workflows_library/label-name/Forms" class="blue-btn msg-btn">Forms</a> <a href="/t5/Workflows-Library/bg-p/workflows_library/label-name/Professional" class="blue-btn msg-btn">Professional</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">7</span> <span title="Upvotes">upvotes</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">6</span> <span title="Total posts">Replies</span> </div> </div> </div> </div> </div> </div> </div> <div class="reply-box"> <div class="latest-reply-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="reply-msg-box"> <div class="reply-top-row"> <div class="reply-top-left-col"> <div class="name-rank-box"> </div> <div class="name-rank-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> <span class="rank-img-box"> <img src="https://cdn2.hubspot.net/hubfs/98485/community.hubspot.com/Community%20Manager%20Icon.png"> </span> </div> <div class="rank-role-box"> <span class="role-name">Community Manager</span> </div> </div> <div class="reply-top-right-col"> <div class="reply-option"> <span class="three-dots-option"></span> <div class="option-dropdown"></div> </div> <div class="reply-posted-time"> <span class="msg-created-date"> February 26, 2024 </span> </div> </div> </div> <div class="reply-bottom-row"> <div class="msg-body reply-msg-body"> Hey @HuyDTran -- thanks for your support! From what I understand, the current workaround to capture the dates for multiple forms would requi...<a href="/t5/Workflows-Library/Capture-Individual-Form-Submission-Dates-on-Contacts/ba-p/875566" class="msg-read-more">read more</a> </div> </div> </div> </div> </div> </div> <div class="custom-MessageView"> <div class="first-message-box message-box"> <a class="author-img-floated" href="/t5/user/viewprofilepage/user-id/169781"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/119922i02EE10C06691E6AA/image-dimensions/150x150/image-coordinates/0%2C25%2C1072%2C1097?v=v2"> </a> <div class="first-message-box__wrapper"> <div class="first-message-layout"> <div class="message-info-wrap"> <div class="author-info-box"> <a href="/t5/user/viewprofilepage/user-id/169781" class="author-name-link"><span class="author-username">kennedyp</span></a> on <span class="msg-created-date"> August 12, 2024 </span> </div> <div class="msg-subject "> <a href="/t5/INBOUND-Topics-Trends/INBOUND-2024-Tickets/td-p/1025827"> INBOUND 2024 Tickets </a> </div> <div class="msg-body">   Hey HubSpot Community! We hope you are getting as excited as we are for INBOUND 2024! This three-day conference will showcase the latest in marketing, sales, and AI with keynotes, breakouts, HubSpot demos, workshops, networking, <a href="/t5/INBOUND-Topics-Trends/INBOUND-2024-Tickets/td-p/1025827" class="msg-read-more">read more</a> </div> <div class="message-stats-wrapper Profile-box"> <div class="button-box"> <a class="btn-reply" href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2FINBOUND-Topics-Trends%2FINBOUND-2024-Tickets%2Ftd-p%2F1025827%23reply">Reply</a> <a href="/t5/INBOUND-Topics-Trends/INBOUND-2024-Tickets/td-p/1025827" class="discussion-btn msg-btn"> Discussion </a> <a href="/t5/INBOUND-Topics-Trends/bd-p/INBOUND-general-discussions/label-name/Inbound%202024" class="blue-btn msg-btn">Inbound 2024</a> <a href="/t5/INBOUND-Topics-Trends/bd-p/INBOUND-general-discussions/label-name/Inbound%20ticket" class="blue-btn msg-btn">Inbound ticket</a> </div> <div class="msg-stats"> <div class="upvotes-box stat-box"> <span class="upvote-icon stat-icon" title="Upvotes"></span> <span class="upvote-count stat-count" title="Upvotes">4</span> <span title="Upvotes">upvotes</span> </div> <div class="stat-box"> <span class="reply-icon stat-icon" title="Total posts"></span> <span class="reply-count stat-count" title="Total posts">343</span> <span title="Total posts">Replies</span> </div> </div> </div> </div> </div> </div> </div> </div> <div class="view-all-button-container"> <a class="btn btn-sm button-tertiary" href="/t5/forums/recentpostspage/post-type/message/user-id/169781">View latest posts</a> </div> <div class="pagination_div pagination-recent-post pagination" data-count="577"> <div class="holder recent_holder"></div> </div> </div> <div class="ajax-loading" style="display: none;"><img src="/html/assets/ajax-loadergif.gif"></div> </div> </div> </div> <div id="solutions-content" class="tab-content hidden"><div class="lia-panel lia-panel-standard MessageListTaplet Chrome lia-component-users-widget-my-accepted-solutions"><div class="lia-decoration-border"><div class="lia-decoration-border-top"><div> </div></div><div class="lia-decoration-border-content"><div><div class="lia-panel-heading-bar-wrapper"><div class="lia-panel-heading-bar"><span aria-level="3" role="heading" class="lia-panel-heading-bar-title">My Accepted Solutions</span></div></div><div class="lia-panel-content-wrapper"><div class="lia-panel-content"> <div class="SimpleMessageList"> <div id="messageList_23f26f708a5ac4" class="MessageList lia-component-forums-widget-message-list"> <span id="message-listmessageList_23f26f708a5ac4"> </span> <div class="t-data-grid single-message-list" id="grid_23f26f708a5ac4"> <table role="presentation" class="lia-list-wide"> <thead class="lia-table-head" id="columns_23f26f708a5ac4"><tr><th scope="col" class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text t-first"> Subject </th><th scope="col" class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> Views </th><th scope="col" class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date t-last"> Posted </th></tr></thead> <tbody> <tr class="lia-list-row lia-row-odd lia-list-row-thread-solved lia-js-data-messageUid-1072667 lia-js-data-messageRevision-1 t-first"><td class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text"> <div class="MessageSubjectCell"> <div class="MessageSubject"> <div class="MessageSubjectIcons "> <a class="lia-link-navigation verified-icon" id="link_23f26f708a5ac4" href="/t5/Beta-Discussions-and-Feedback/Create-Notes-from-Workflow-BETA-not-available-for-Custom-Objects/m-p/1072667#M247"><span class="lia-img-message-type-solution lia-fa-message lia-fa-type lia-fa-solution lia-fa" title="Solution" alt="Solution" aria-label="Solution" role="img" id="display_23f26f708a5ac4"></span></a> <h2 class="message-subject"> <span class="lia-message-unread lia-message-unread-windows"> <a class="page-link lia-link-navigation lia-custom-event" id="link_2_23f26f708a5ac4" href="/t5/Beta-Discussions-and-Feedback/Create-Notes-from-Workflow-BETA-not-available-for-Custom-Objects/m-p/1072667#M247"> Re: [Create Notes from Workflow BETA] - not available for Custom Objects </a> </span> </h2> </div> <div class="message-subject-board"> <a class="local-time xsmall-text lia-link-navigation" id="link_1_23f26f708a5ac4" href="/t5/Beta-Discussions-and-Feedback/bd-p/beta_discussions">Beta Discussions and Feedback</a> </div> </div> </div> </td><td class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> 26 </td><td class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date"> <span class="DateTime"> <span title="‎Nov 20, 2024 3:59 PM" class="local-friendly-date"> Wednesday </span> </span> </td></tr><tr class="lia-list-row lia-row-even lia-list-row-thread-solved lia-js-data-messageUid-1072585 lia-js-data-messageRevision-1"><td class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text"> <div class="MessageSubjectCell"> <div class="MessageSubject"> <div class="MessageSubjectIcons "> <a class="lia-link-navigation verified-icon" id="link_23f26f708a5ac4_0" href="/t5/CRM/Meeting-scheduler-and-the-new-Appointment-object/m-p/1072585#M153391"><span class="lia-img-message-type-solution lia-fa-message lia-fa-type lia-fa-solution lia-fa" title="Solution" alt="Solution" aria-label="Solution" role="img" id="display_23f26f708a5ac4_0"></span></a> <h2 class="message-subject"> <span class="lia-message-unread lia-message-unread-windows"> <a class="page-link lia-link-navigation lia-custom-event" id="link_2_23f26f708a5ac4_0" href="/t5/CRM/Meeting-scheduler-and-the-new-Appointment-object/m-p/1072585#M153391"> Re: Meeting scheduler and the new Appointment object </a> </span> </h2> <span class="lia-img-message-has-image lia-fa-message lia-fa-has lia-fa-image lia-fa" title="Contains an image" alt="Message contains an image" aria-label="Contains an image" role="img" id="display_3_23f26f708a5ac4"></span> </div> <div class="message-subject-board"> <a class="local-time xsmall-text lia-link-navigation" id="link_1_23f26f708a5ac4_0" href="/t5/CRM/bd-p/sales_CRM">CRM</a> </div> </div> </div> </td><td class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> 163 </td><td class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date"> <span class="DateTime"> <span title="‎Nov 20, 2024 2:19 PM" class="local-friendly-date"> Wednesday </span> </span> </td></tr><tr class="lia-list-row lia-row-odd lia-list-row-thread-solved lia-js-data-messageUid-1072560 lia-js-data-messageRevision-1"><td class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text"> <div class="MessageSubjectCell"> <div class="MessageSubject"> <div class="MessageSubjectIcons "> <a class="lia-link-navigation verified-icon" id="link_23f26f708a5ac4_1" href="/t5/Account-Settings/quot-We-weren-t-able-to-verify-your-identity-quot/m-p/1072560#M33762"><span class="lia-img-message-type-solution lia-fa-message lia-fa-type lia-fa-solution lia-fa" title="Solution" alt="Solution" aria-label="Solution" role="img" id="display_23f26f708a5ac4_1"></span></a> <h2 class="message-subject"> <span class="lia-message-unread lia-message-unread-windows"> <a class="page-link lia-link-navigation lia-custom-event" id="link_2_23f26f708a5ac4_1" href="/t5/Account-Settings/quot-We-weren-t-able-to-verify-your-identity-quot/m-p/1072560#M33762"> Re: "We weren’t able to verify your identity" </a> </span> </h2> <span class="lia-img-message-has-url lia-fa-message lia-fa-has lia-fa-url lia-fa" title="Contains a hyperlink" alt="Message contains a hyperlink" aria-label="Contains a hyperlink" role="img" id="display_2_23f26f708a5ac4"></span> </div> <div class="message-subject-board"> <a class="local-time xsmall-text lia-link-navigation" id="link_1_23f26f708a5ac4_1" href="/t5/Account-Settings/bd-p/sales_account">Account & Settings</a> </div> </div> </div> </td><td class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> 60 </td><td class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date"> <span class="DateTime"> <span title="‎Nov 20, 2024 1:40 PM" class="local-friendly-date"> Wednesday </span> </span> </td></tr><tr class="lia-list-row lia-row-even lia-list-row-thread-solved lia-js-data-messageUid-1069718 lia-js-data-messageRevision-1"><td class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text"> <div class="MessageSubjectCell"> <div class="MessageSubject"> <div class="MessageSubjectIcons "> <a class="lia-link-navigation verified-icon" id="link_23f26f708a5ac4_2" href="/t5/Sales-Integrations/How-to-set-up-uni-directional-Sync-for-contacts-from-Salesforce/m-p/1069718#M7620"><span class="lia-img-message-type-solution lia-fa-message lia-fa-type lia-fa-solution lia-fa" title="Solution" alt="Solution" aria-label="Solution" role="img" id="display_23f26f708a5ac4_2"></span></a> <h2 class="message-subject"> <span class="lia-message-unread lia-message-unread-windows"> <a class="page-link lia-link-navigation lia-custom-event" id="link_2_23f26f708a5ac4_2" href="/t5/Sales-Integrations/How-to-set-up-uni-directional-Sync-for-contacts-from-Salesforce/m-p/1069718#M7620"> Re: How to set up uni-directional Sync for contacts from Salesforce to Hubspot </a> </span> </h2> <span class="lia-img-message-has-url lia-fa-message lia-fa-has lia-fa-url lia-fa" title="Contains a hyperlink" alt="Message contains a hyperlink" aria-label="Contains a hyperlink" role="img" id="display_2_23f26f708a5ac4_0"></span> </div> <div class="message-subject-board"> <a class="local-time xsmall-text lia-link-navigation" id="link_1_23f26f708a5ac4_2" href="/t5/Sales-Integrations/bd-p/sales_integrations">Sales Integrations</a> </div> </div> </div> </td><td class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> 78 </td><td class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date"> <span class="DateTime"> <span title="‎Nov 14, 2024 12:37 PM" class="local-friendly-date"> a week ago </span> </span> </td></tr><tr class="lia-list-row lia-row-odd lia-list-row-thread-solved lia-js-data-messageUid-1068543 lia-js-data-messageRevision-1 t-last"><td class="messageSubjectColumn lia-data-cell-primary lia-data-cell-text"> <div class="MessageSubjectCell"> <div class="MessageSubject"> <div class="MessageSubjectIcons "> <a class="lia-link-navigation verified-icon" id="link_23f26f708a5ac4_3" href="/t5/Social-Tools/Whatsapp-Conversation-Limit/m-p/1068543#M2531"><span class="lia-img-message-type-solution lia-fa-message lia-fa-type lia-fa-solution lia-fa" title="Solution" alt="Solution" aria-label="Solution" role="img" id="display_23f26f708a5ac4_3"></span></a> <h2 class="message-subject"> <span class="lia-message-unread lia-message-unread-windows"> <a class="page-link lia-link-navigation lia-custom-event" id="link_2_23f26f708a5ac4_3" href="/t5/Social-Tools/Whatsapp-Conversation-Limit/m-p/1068543#M2531"> Re: Whatsapp Conversation Limit </a> </span> </h2> <span class="lia-img-message-has-url lia-fa-message lia-fa-has lia-fa-url lia-fa" title="Contains a hyperlink" alt="Message contains a hyperlink" aria-label="Contains a hyperlink" role="img" id="display_2_23f26f708a5ac4_1"></span> </div> <div class="message-subject-board"> <a class="local-time xsmall-text lia-link-navigation" id="link_1_23f26f708a5ac4_3" href="/t5/Social-Tools/bd-p/marketing_social">Social Tools</a> </div> </div> </div> </td><td class="viewsCountColumn lia-data-cell-secondary lia-data-cell-integer"> 145 </td><td class="messagePostDateColumn lia-data-cell-secondary lia-data-cell-date"> <span class="DateTime"> <span title="‎Nov 12, 2024 1:51 PM" class="local-friendly-date"> 2 weeks ago </span> </span> </td></tr> </tbody> </table> </div> </div> </div> <div class="lia-view-all"><a class="lia-link-navigation" rel="nofollow" id="link_23f26f708a5ac4_4" href="/t5/forums/recentpostspage/post-type/message/is-solution/true/user-id/169781">View all badges</a></div> </div></div></div></div><div class="lia-decoration-border-bottom"><div> </div></div></div></div></div> <div id="drafts-content" class="tab-content hidden"></div> <div id="subscription-content" class="tab-content hidden"> <div class="view-all-button-container manage-subs"> <a class="btn btn-sm button-tertiary" href="/t5/user/myprofilepage/tab/user-subscriptions">Manage all subscriptions</a> </div> </div> <div id="bookmarks-content" class="tab-content hidden bookmark_div"></div> <section class="bookmark-empty-state"> <div class="no-content-message-div"> <div class="image-div"> <img src="/html/assets/Empty_Status.svg" alt=""> </div> <span class="there-arent-any-pos"> Bookmark content for quick access to your most helpful threads.</a> </span> <div class="button-wrapper"> <a class="button-primary custom-btn" href="https://community.hubspot.com/">Get started</a> </div> </div> </section> <section class="no-content"> <div class="no-content-message-div"> <div class="image-div"> <img src="/html/assets/Empty_Status.svg" alt=""> </div> <span class="there-arent-any-pos"> Everyone has to start somewhere! Why not kick-start your journey by telling us a bit more about your interests. </span> <div class="button-wrapper"> <a class="button-primary custom-btn" href="https://community.hubspot.com/t5/Welcome-and-Introductions/Welcome-to-the-Community-introduce-yourself/td-p/79">Introduce yourself</a> </div> </div> </section> </div> </section> </div> </div><div class="lia-quilt-column lia-quilt-column-06 lia-quilt-column-right lia-quilt-column-side-content"> <div class="lia-quilt-column-alley lia-quilt-column-alley-right"> <style class="cmp-global-json-content/core" type="text/css"> /** /* Custom styles for core feedback elements. Those are usually only used to provide info and hints /* to priviledged roles when issues occur within custom components, so they are not part of the /* regular community theme. /*/ .feedback { --b-radius: var(--ui--b-radius, 3px); --H: 0; --S: 0%; --S-light: 0%; --L: 41%; --L-light: 55%; --A: 1; --A-light: 1; --H-step: 45; --H-offset: 0; background: hsla(var(--H), var(--S), var(--L), var(--A)); border-radius: var(--b-radius); color: hsla(var(--H), var(--S), 100%, var(--A)); display: flex; align-items: center; gap: 24px; font-size: 12px; line-height: 1.25; margin: 24px 0; padding: 24px; position: relative; transition: background-color 236ms ease; } .feedback__trigger { display: none; } .feedback__trigger:not(:checked) + .feedback { display: none; } .feedback__close { color: inherit; cursor: pointer; display: inline-block; font-size: 24px !important; font-weight: 300 !important; line-height: 12px !important; margin: 0 !important; padding: 0 !important; position: absolute; top: 12px; right: 12px; width: 12px; } .feedback__close:before { border-radius: 50%; content: ''; position: absolute; top: 0; left: 0; transform: translate(calc(-100%/3), calc(-100%/3)); width: 300%; height: 300%; } .feedback__title { display: block; font-weight: bold; font-size: 125%; } .feedback a { color: inherit !important; display: inline-block; position: relative; } .feedback a:hover, .feedback a:focus { text-decoration: none !important; } .feedback a:not(.button):before, .feedback a:not(.button):after { border-bottom: 1px dotted white; content: ''; position: absolute; left: 0; right: 0; bottom: -1px; transition: all 382ms ease-out; } .feedback a:not(.button):after { border-bottom: 1px solid white; max-width: 0; width: 0; right: auto; } .feedback a:not(.button):hover:after, .feedback a:not(.button):focus:after { max-width: 100%; width: 1200px; } .feedback a:not(.button):active:after { transform: scaleX(1.0618); } .feedback a.button, .feedback button, .feedback input[type="button"] { background: transparent; border: 0 !important; border-radius: var(--b-radius); box-shadow: inset 0 0 0 1px currentColor; color: inherit !important; cursor: pointer; font-size: inherit !important; line-height: 24px !important; padding: 0 12px !important; transition: background-color 236ms ease-out; } .feedback button:hover, .feedback a.button:hover, .feedback button:focus, .feedback a.button:focus, .feedback code { background: hsla(var(--H), var(--S-light), var(--L-light), var(--A-light)); } .feedback code { border-radius: var(--b-radius); color: inherit; display: inline-block; font-size: 90%; font-weight: bold; padding: 2px 4px; } .feedback.is--error { --H: calc(0 * var(--H-step) + var(--H-offset)); --S: 100%; --S-light:100%; --L-light: 63%; } /*.feedback.is--error button:hover, .feedback.is--error a.button:hover, .feedback.is--error button:focus, .feedback.is--error a.button:focus, .feedback.is--error code { --L: 63%; background: hsla(var(--H), var(--S), var(--L), var(--A)); }*/ .feedback.is--warning { --H: calc(1 * var(--H-step) + var(--H-offset)); --S: 100%; --S-light: 75%; --L-light: 55%; } /*.feedback.is--warning button:hover, .feedback.is--warning a.button:hover, .feedback.is--warning button:focus, .feedback.is--warning a.button:focus, .feedback.is--warning code { --S: 75%; --L: 55%; background: hsla(var(--H), var(--S), var(--L), var(--A)); }*/ .feedback.is--success { --H: calc(3 * var(--H-step) + var(--H-offset)); --S: 100%; --L: 31%; background: hsla(var(--H), var(--S), var(--L), var(--A)); } .feedback.is--success button:hover, .feedback.is--success a.button:hover, .feedback.is--success button:focus, .feedback.is--success a.button:focus, .feedback.is--success code { --L: 50%; --S: 50%; background: hsla(var(--H), var(--S), var(--L), var(--A)); } .feedback.is--info { --H-offset: 22; --H: calc(4 * var(--H-step) + var(--H-offset)); --S: 69%; background: hsla(var(--H), var(--S), var(--L), var(--A)); } .feedback.is--info button:hover, .feedback.is--info a.button:hover, .feedback.is--info button:focus, .feedback.is--info a.button:focus, .feedback.is--info code { --S: 75%; --L: 55%; background: hsla(var(--H), var(--S), var(--L), var(--A)); } .feedback__icon { --feedback-icon: 48px; border-radius: 50%; display: block; flex-shrink: 0; stroke-miterlimit: 10; stroke-width: 3px; stroke: white; width: var(--feedback-icon); height: var(--feedback-icon); /* margin: 10% auto; */ } .is--success .feedback__icon { animation: fillsuccess 4s ease-in-out 4s forwards, scale 3s ease-in-out 9s both; box-shadow: inset 0px 0px 0px #00c851; } .is--error .feedback__icon { animation: fillerror 4s ease-in-out 4s forwards, scale 3s ease-in-out 9s both; box-shadow: inset 0px 0px 0px #ff4444; } .feedback__icon-circle { animation: stroke 6s cubic-bezier(0.65, 0, 0.45, 1) forwards; stroke-dasharray: 166; stroke-dashoffset: 166; stroke-miterlimit: 10; stroke-width: 5px; } .is--success .feedback__icon-circle { stroke: #00c851; } .is--error .feedback__icon-circle { stroke: #ff4444; } .feedback__icon-path { animation: stroke 3s cubic-bezier(0.65, 0, 0.45, 1) 9s forwards; display: none; stroke-dasharray: 48; stroke-dashoffset: 48; transform-origin: 50% 50%; } .feedback__icon-path--i { display: block; font-size: calc(var(--feedback-icon)*0.6); line-height: var(--feedback-icon);; text-align: center; width: 100%; height: 100%; } .feedback__icon-path--i i { display: inline-block; font-style: normal; font-weight: bold; transform: translate3d(0, -300%, 0); transform-origin: 50% 100% 0; } .feedback__icon:hover .feedback__icon-path--i i { position: relative; animation: fall-in-bounce 1618ms, sway 3618ms 1600ms; /*animation-name: fall-in-bounce, sway; animation-delay: 0, 1618ms; animation-duration: 1618ms, 3618ms;*/ transform: translate3d(0, 0, 0); } .feedback__icon:hover .feedback__icon-path--i i:after { animation: fall-in 618ms ease-in, bounce 618ms infinite alternate cubic-bezier(0, 0, 0.1, 1); animation-delay: 5236ms, 5854ms; content: '.'; line-height: 1; position: absolute; left: 0; bottom: .93em; transform: translateY(-100%); } @keyframes fall-in { from { transform: translateY(-100%); } to { transform: translateY(0.1em); } } @keyframes bounce { from { transform: translateY(0.1em); } to { transform: translateY(-0.2em); } } @keyframes fall-in-bounce { from { transform: translate3d(0, -300%, 0); animation-timing-function: ease-in; opacity: 0; } 40% { transform: translate3d(0, 6%, 0); animation-timing-function: ease-out; opacity: 1; } 55% { transform: translate3d(0, -12%, 0); animation-timing-function: ease-in; } 75% { transform: translate3d(0, 2%, 0); animation-timing-function: ease-out; } 90% { transform: translate3d(0, -6%, 0); animation-timing-function: ease-in; } to { transform: translate3d(0, 0, 0); animation-timing-function: ease-out; } } @keyframes sway { from { transform: rotate(0); animation-timing-function: ease-out; } 10% { transform: rotate(5deg); animation-timing-function: ease-out; } 30% { transform: rotate(-4deg); animation-timing-function: ease-out; } 50% { transform: rotate(3deg); animation-timing-function: ease-out; } 70% { transform: rotate(-2deg); animation-timing-function: ease-out; } 90% { transform: rotate(1deg); animation-timing-function: ease-out; } to { transform: rotate(0); animation-timing-function: ease-out; } } .is--success .feedback__icon-path--check { display: block; } .is--error .feedback__icon-path--cross { display: block; } .is--error .feedback__icon-path--i { display: block; } @keyframes stroke { 100% { stroke-dashoffset: 0; } } @keyframes scale { 0%, 100% { transform: none; } 50% { transform: scale3d(1.1, 1.1, 1); } } @keyframes fillsuccess { 100% { box-shadow: inset 0px 0px 0px 75px #00c851; } } @keyframes fillerror { 100% { box-shadow: inset 0px 0px 0px 75px #ff4444; } } </style> <div class="dashboard-following"> <div class="dashboard-following__header"> <h4>Following</h4> </div> <div class="dashboard-following__body"> <ul> <li> <div class="dashboard-following__body--left"> <a href="/t5/user/viewprofilepage/user-id/9374" class="user-avatar"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/107287i4AC562C3ED7606EC/image-dimensions/150x150/image-coordinates/57%2C51%2C1029%2C1023?v=v2" alt="avatar"> </a> <a href="/t5/user/viewprofilepage/user-id/9374" class="user-name">trevordjones</a> </div> <div class="dashboard-following__body--right"> <span class="user-rank">Recognized Expert | Diamond Partner</span> </div> </li> <li> <div class="dashboard-following__body--left"> <a href="/t5/user/viewprofilepage/user-id/15" class="user-avatar"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/1930i41077A0D49B7BB43/image-dimensions/150x150/image-coordinates/25%2C0%2C411%2C386?v=v2" alt="avatar"> </a> <a href="/t5/user/viewprofilepage/user-id/15" class="user-name">sjudson</a> </div> <div class="dashboard-following__body--right"> <span class="user-rank">HubSpot Product Team</span> </div> </li> <li> <div class="dashboard-following__body--left"> <a href="/t5/user/viewprofilepage/user-id/513582" class="user-avatar"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/98878i663D0A9F2F64F4F0/image-dimensions/150x150/image-coordinates/0%2C4%2C1238%2C1242?v=v2" alt="avatar"> </a> <a href="/t5/user/viewprofilepage/user-id/513582" class="user-name">Shadab_Khan</a> </div> <div class="dashboard-following__body--right"> <span class="user-rank">Key Advisor | Elite Partner</span> </div> </li> <li> <div class="dashboard-following__body--left"> <a href="/t5/user/viewprofilepage/user-id/133120" class="user-avatar"> <img src="https://community.hubspot.com/t5/image/serverpage/image-id/33171iF3BE35799FEB876F/image-dimensions/150x150/image-coordinates/0%2C0%2C442%2C442?v=v2" alt="avatar"> </a> <a href="/t5/user/viewprofilepage/user-id/133120" class="user-name">MatthiasWeber</a> </div> <div class="dashboard-following__body--right"> <span class="user-rank">Guide | Partner</span> </div> </li> </ul> </div> <div class="dashboard-following__footer"> <a href="/t5/custom/page/page-id/myDashboardList?tabid=following&amp;user=169781" class="view-all">View all</a> </div> </div> <div class="community_badges_wrapper"> <div class="lia-panel-heading-bar-wrappers"> <div class="lia-panel-heading-bar"> <span aria-level="4" role="heading" class="lia-panel-heading-bar-title"> Community Badges </span> <div class="lia-user-badge-list lia-component-badges-widget-user-badges-list" id="userBadgesListV11_23f26f83ba2bd9"> <div class="lia-quilt-row lia-quilt-row-standard lia-badge-list"> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9"><img title="Thankful (500)" alt="Thankful (500)" tabindex="0" id="display_23f26f83ba2bd9" src="/html/@F6BFC6FF7CE06FAB622F9F2AB242FAEA/badge_icons/Give%20Kudos%20_5.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_0"><img title="Responder (500)" alt="Responder (500)" tabindex="0" id="display_23f26f83ba2bd9_0" src="/html/@06315A3C7A458449A30D7F6A2EB7FD2F/badge_icons/Reply%20Topic_7.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_1"><img title="Problem Solver (50)" alt="Problem Solver (50)" tabindex="0" id="display_23f26f83ba2bd9_1" src="/html/@AAF68F6E1242B91C48FC6B870B6289A5/badge_icons/Accepted%20Solution_4.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_2"><img title="Helpful (100)" alt="Helpful (100)" tabindex="0" id="display_23f26f83ba2bd9_2" src="/html/@B787AA0682656AE392CF60CFC84F9824/badge_icons/Get%20Kudos_2.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_3"><img title="Problem Solver (25)" alt="Problem Solver (25)" tabindex="0" id="display_23f26f83ba2bd9_3" src="/html/@C75D1B85745AF8274F0879D532B23D4F/badge_icons/Accepted%20Solution_3.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_4"><img title="Thankful (250)" alt="Thankful (250)" tabindex="0" id="display_23f26f83ba2bd9_4" src="/html/@692FE67FC2A40F5C42944697938024F5/badge_icons/Give%20Kudos%20_4.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_5"><img title="Responder (250)" alt="Responder (250)" tabindex="0" id="display_23f26f83ba2bd9_5" src="/html/@606B12DD090207D5CAEF2BECDF5149A6/badge_icons/Reply%20Topic_6.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_6"><img title="Helpful (50)" alt="Helpful (50)" tabindex="0" id="display_23f26f83ba2bd9_6" src="/html/@B4EB096BE8D61B3C6038420CE7A5966A/badge_icons/Get%20Kudos%20_2.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_7"><img title="Problem Solver (10)" alt="Problem Solver (10)" tabindex="0" id="display_23f26f83ba2bd9_7" src="/html/@616427A94316B6C45B02499C6AC237BE/badge_icons/Accepted%20Solution_2.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_8"><img title="Blogger" alt="Blogger" tabindex="0" id="display_23f26f83ba2bd9_8" src="/html/@516D3E864D44ADD94869407F5890D718/badge_icons/Publish%20A%20Blog.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_9"><img title="Responder (100)" alt="Responder (100)" tabindex="0" id="display_23f26f83ba2bd9_9" src="/html/@DA3ABC4424529987E078BFAE59C5CAD0/badge_icons/Reply%20Topic_5.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_10"><img title="Thankful (100)" alt="Thankful (100)" tabindex="0" id="display_23f26f83ba2bd9_10" src="/html/@626BA233A5DDDE3F2BA04F46A3B37ED2/badge_icons/Give%20Kudos%20_3.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_11"><img title="Helpful (25)" alt="Helpful (25)" tabindex="0" id="display_23f26f83ba2bd9_11" src="/html/@DBDC1A1EE02ACDD35B712BB1E2FA6E1E/badge_icons/Get%20Kudos%20_1.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_12"><img title="Responder (50)" alt="Responder (50)" tabindex="0" id="display_23f26f83ba2bd9_12" src="/html/@74FBEF7E8744D9455EB778EC56988630/badge_icons/Reply%20Topic_4.png"></span> <div class="lia-user-badge-attributes"> </div> </div> <div class="lia-user-badge-display"> <span class="lia-link-navigation lia-link-disabled" aria-disabled="true" id="link_23f26f83ba2bd9_13"><img title="Problem Solver (5)" alt="Problem Solver (5)" tabindex="0" id="display_23f26f83ba2bd9_13" src="/html/@09DA689795EE0B244FB9016197F60D3F/badge_icons/Accepted%20Solution_1.png"></span> <div class="lia-user-badge-attributes"> </div> </div> </div> <div class="lia-view-all"><a class="lia-link-navigation" id="link_23f26f83ba2bd9_14" href="/t5/badges/userbadgespage/user-id/169781/page/1">View all badges</a></div> </div> </div> </div> </div> <!-- no badges --> <div class="community_badges_wrapper community_groups_wrapper"> <div class="lia-panel-heading-bar-wrappers"> <div class="lia-panel-heading-bar"> <h4>My Groups</h4> <li:grouphubs-group-hubs-panel-list class="lia-limuirs-comp lia-component-grouphubs-widget-my-groups-panel-list" data-lia-limuirs-comp="{&quot;mode&quot;:&quot;DEFAULT&quot;,&quot;componentId&quot;:&quot;grouphubs.widget.my-groups-panel-list&quot;,&quot;id&quot;:&quot;grouphubs.widget.my-groups-panel-list&quot;,&quot;skipInlineEditMode&quot;:&quot;true&quot;,&quot;path&quot;:&quot;limuirs\u002Fcomponents\u002Fgrouphubs\u002FGroupHubsPanelList&quot;,&quot;alias&quot;:&quot;grouphubs.widget.my-groups-panel-list&quot;,&quot;instance&quot;:0,&quot;fqPath&quot;:&quot;0\u002Flimuirs\u002Fcomponents\u002Fgrouphubs\u002FGroupHubsPanelList&quot;}"><div class="lia-panel lia-panel-standard lia-group-hubs-panel-list"><div class="lia-decoration-border"><div class="lia-decoration-border-top"><div></div></div><div class="lia-decoration-border-content"><div><div class="lia-panel-heading-bar-wrapper"><div class="lia-panel-heading-bar"><span role="heading" aria-level="4" class="lia-panel-heading-bar-title">Group Hubs for kennedyp</span></div></div><div class="lia-panel-content-wrapper"><div class="lia-panel-content"><ul><li><div class="lia-group-hubs-panel-list-item"><a href="/t5/Inbound/gh-p/study-group-inbound" class="lia-node-avatar-wrapper"><img class="lia-node-avatar" src="/t5/image/serverpage/image-id/45260i54F3605EC818A699/image-size/tiny/crop-image/true?v=v2&amp;px=100" alt="Inbound"></a><h4 class="lia-node-title"><a class="lia-link-navigation" href="/t5/Inbound/gh-p/study-group-inbound">Inbound<!-- --> </a></h4><div class="lia-membership-count"><i class="lia-fa lia-fa-user "></i>12126</div></div></li><li><div class="lia-group-hubs-panel-list-item"><a href="/t5/Women-in-Tech/gh-p/Women_In_Tech" class="lia-node-avatar-wrapper"><img class="lia-node-avatar" src="/t5/image/serverpage/image-id/35492i55896808D811C855/image-size/tiny/crop-image/true?v=v2&amp;px=100" alt="Women in Tech"></a><h4 class="lia-node-title"><a class="lia-link-navigation" href="/t5/Women-in-Tech/gh-p/Women_In_Tech">Women in Tech<!-- --> </a></h4><div class="lia-membership-count"><i class="lia-fa lia-fa-user "></i>734</div></div></li><li><div class="lia-group-hubs-panel-list-item"><a href="/t5/StudentSpot/gh-p/studentspot" class="lia-node-avatar-wrapper"><img class="lia-node-avatar" src="/t5/image/serverpage/image-id/66276i43039547FC51BCB3/image-size/tiny/crop-image/true?v=v2&amp;px=100" alt="StudentSpot"></a><h4 class="lia-node-title"><a class="lia-link-navigation" href="/t5/StudentSpot/gh-p/studentspot">StudentSpot<!-- --> </a></h4><div class="lia-membership-count"><i class="lia-fa lia-fa-user "></i>274</div></div></li></ul><div class="lia-view-all"><a class="lia-link-navigation view-all-link" href="/t5/grouphubs/page/user-id/169781">See all</a></div></div></div></div></div><div class="lia-decoration-border-bottom"><div></div></div></div></div></li:grouphubs-group-hubs-panel-list> </div> </div> </div> </div> </div> </div><div class="lia-quilt-row lia-quilt-row-footer"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-footer"> <div class="lia-quilt-column-alley lia-quilt-column-alley-single"> <div class="lia-quilt lia-quilt-footer lia-quilt-layout-one-column lia-component-quilt-footer"> <div class="lia-quilt-row lia-quilt-row-header"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-header lia-mark-empty"> </div> </div><div class="lia-quilt-row lia-quilt-row-main"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-main-content"> <div class="lia-quilt-column-alley lia-quilt-column-alley-single"> <!-- FOOTER START --> <footer class="community-footer"> <div class="community-footer-wrapper template-centered"> <div class="footer-responsive"> <div class="contain-top"> <div class="col"> <h5>HubSpot</h5> <ul> <li><a href="https://www.hubspot.com/">Home</a></li> <li><a href="https://help.hubspot.com/">Help</a></li> <li><a href="https://academy.hubspot.com/">Academy</a></li> <li><a href="https://knowledge.hubspot.com/">Knowledge Base</a></li> <li><a href="https://ecosystem.hubspot.com/marketplace/solutions">Solutions Directory</a></li> <li><a href="https://blog.hubspot.com/">Blog</a></li> </ul> </div> <div class="col"> <h5>Get involved</h5> <ul> <li><a href= "https://offers.hubspot.com/community-champions" class="">Community Champions</a> </li> <li><a href= "https://www.hubspot.com/hubspot-user-groups" class="">HubSpot User Groups</a></li> <li><a href= "https://www.hubspot.com/partners/solutions" class="">Solutions Partner Program</a></li> <li><a href= "https://www.hubspot.com/community-newsletter" class="">Community Newsletter</a></li> </ul> </div> <div class="col"> <h5>Community</h5> <ul> <li><a href= "https://community.hubspot.com/t5/CRM-Sales-Hub/ct-p/sales">CRM & Sales</a></li> <li><a href= "https://community.hubspot.com/t5/Marketing-Hub/ct-p/marketing">Marketing</a></li> <li><a href= "https://community.hubspot.com/t5/Service-Hub/ct-p/service_hub">Service</a></li> <li><a href= "https://community.hubspot.com/t5/RevOps-Operations/ct-p/Operations">RevOps & Operations</a></li> <li><a href= "https://community.hubspot.com/t5/HubSpot-Developers/ct-p/developers" class="">Developers</a></li> <li><a href= "https://community.hubspot.com/t5/Getting-Started-on-the-Community/How-to-join-the-Solutions-Partner-Program/ba-p/400205" class="">Partners</a></li> </ul> </div> <div class="col"> <ul> <li><a href= "https://community.hubspot.com/t5/Academy/ct-p/academy">Academy</a></li> <li><a href= "https://community.hubspot.com/t5/Groups/ct-p/groups">Groups</a></li> <li><a href= "/t5/Advocacy/ct-p/advocacy" class="">Advocacy</a></li> <li><a href= "/t5/HubSpot-Ideas/idb-p/HubSpot_Ideas" class="">Ideas</a></li> </ul> </div> </div> <div class="contain-top-two"> <hr class="seperator"> </div> </div> <div class="footer-main-two"> <div class="footer-copywrite-container"> <a href="/" id="footer-logo" class="footer-wordmark"> <img src="https://community.hubspot.com/html/@B5A74D0D426EC31D4B4C76F0526FF1E5/assets/HS_Logo_Wordmark-White.svg" alt="hubspot"> </a> <span id="copywrite">Copyright © 2024 HubSpot, Inc.</span> </div> <div class="footer-links-container"> <ul class="footer-link"> <li> <a href="https://legal.hubspot.com/privacy-policy" class="footer-terms">Privacy Policy</a> </li> <li> <a href="https://legal.hubspot.com/community-tou" class="footer-privacy">Community Terms of Use</a> </li> <li> <a href="https://community.hubspot.com/t5/Getting-Started-on-the-Community/HubSpot-Community-Guidelines/ba-p/384050" class="footer-guidelines">Community Guidelines</a> </li> <li> <a href="https://status.hubspot.com/" class="footer-status">Status</a> </li> <li> <a href="https://legal.hubspot.com/digital-services-act" class="footer-dsa">DSA Statement</a> </li> <li class="hs-footer-cookie-settings footer-cookie-settings" hidden> <a href=""></a> </li> </ul> </div> </div> </div> </footer> </div> </div> </div><div class="lia-quilt-row lia-quilt-row-footer"> <div class="lia-quilt-column lia-quilt-column-24 lia-quilt-column-single lia-quilt-column-common-footer lia-mark-empty"> </div> </div> </div> <script> document.addEventListener("DOMContentLoaded", function() { const userIcons = new Set([ ...document.querySelectorAll('.UserName[class*="Provider"]'), ...document.querySelectorAll('.UserName[class*="Partner"]') ]) userIcons.forEach(user => { let userMeta = user.childNodes; let imgWrapper = document.createElement('span'); imgWrapper.classList.add('img-wrapper'); let rank; user.appendChild(imgWrapper); for (i=userMeta.length;i>=0;i--) { try { if (userMeta[i].classList.contains('lia-user-rank') || userMeta[i].classList.contains('user-role')) { rank = userMeta[i]; rank.parentNode.removeChild(rank); } } catch(error) { } try { if (userMeta[i].classList.contains('lia-user-rank-icon')) { imgWrapper.appendChild(userMeta[i]); user.appendChild(rank); } } catch(error) { } } }); }); </script> </div> </div> </div> </div> </div> </div> </div> </div> <script> document.cookie = "Crowdvocate_user_ck=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; //console.log('deleting cookie for logged out case'); //console.log('is cookie set? '+ getCookie('Crowdvocate_user_ck')); </script> <!-- Start HubSpot SDK Script --> <script>window.acsdk = {cid: 'WSUMXJFRDOXTWNTJ8YKRUEQ6GHNP1UAUP9EA', params: {popupDelay: 10, position: [0, 20, 50, 0, 0]}};</script> <script>(function(){var w=window;var ac=w.Crowdvocate;if(typeof ac==="function"){ac('init',w.acsdk);}else{var a=function(){a.c(arguments)};a.q=[];a.c=function(args){a.q.push(args)};w.Crowdvocate=a;} var s=document.createElement('script');s.type='text/javascript';s.async=true;s.src='https://d29zub39v1xeg4.cloudfront.net/api/v1/sdk.js';var x=document.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);})();</script> <!-- End of HubSpot SDK Script --> </center> </div> <script type="text/javascript"> new Image().src = ["/","b","e","a","c","o","n","/","1","2","5","4","7","5","6","8","5","9","0","9","_","1","7","3","2","4","8","8","6","2","0","0","3","7",".","g","i","f"].join(""); </script> <script type="text/javascript" src="/t5/scripts/C1D0FDEB5D557CE5FA1EFA105E95A13F/lia-scripts-common-min.js"></script><script type="text/javascript" src="https://limuirs-assets.lithium.com/assets/limuirs-24_7-vendors~main.5ef86aa8c72fe4cbb8d6.js"></script><script type="text/javascript" src="https://limuirs-assets.lithium.com/assets/limuirs-24_7-main.138f37e85bead07e28fd.js"></script><script type="text/javascript" src="/t5/scripts/7F4FD3DECB488FD6A8C686616842875E/lia-scripts-body-min.js"></script><script language="javascript" type="text/javascript"> <!-- LITHIUM.Sandbox.restore(); LITHIUM.jQuery.fn.cssData.defaults = {"dataPrefix":"lia-js-data","pairDelimeter":"-","prefixDelimeter":"-"}; (function($){ jQuery(document).on('click', '#hs-eu-confirmation-button', function() { location.reload(); }); })(LITHIUM.jQuery); LITHIUM.CommunityJsonObject.User.policies['forums.action.message-view.batch-messages.allow'] = false; // <script> // $ prefix because 'core' is kinda prone to conflict with other stuff that might define a variable 'core' // If there are issues with markup_output (on communities that have auto-escaping enabled!) you can add // ?no_esc behind the expression, but can't have that conditionally there because FreeMarker will just throw // an exception just because the built in is in the code if auto-escaping is disabled... var $core = { config: {"initialized" : true,"debug" : false,"devmode" : false,"env" : "prod","context" : "component","versions" : {"lithium" : 24.8,"freemarker" : "2.3.26-incubating","core.component" : 23.8,"core.users" : 23.5,"core" : 23.4},"file" : "core.cmp.noscript","output" : "undefined","locale" : {"charset" : "UTF-8","timezone" : "US/Eastern","format" : {"date" : "MMM d, yyyy","time" : "h:mm a","full" : "MMM d, yyyy h:mm a","relative" : true,"cutoff" : 31}},"lang" : "en","langs" : ["en","de","es","fr","ja","pt-br"],"node" : {"id" : "mjmao93648","uid" : 1,"lang" : "en","type" : "community","style" : "none","url" : "https://community.hubspot.com/","quilt" : "ViewProfilePage","skin" : "hubspot","path" : "\/","top" : "\/categories/id/mjmao93648","settings_key" : "config_node"},"user" : {"admin" : false,"mod" : false,"auth" : false,"device" : "desktop","lang" : "en","registered" : false,"roles" : [],"show_text_keys" : false,"settings_key" : "config_user","settings" : {}},"cache" : {"cached" : false,"type" : "","map" : {},"config" : {"usercache" : {"inactiveTime" : 1200000,"maximumTime" : 7200000,"maxSize" : 10000},"appcache" : {"inactiveTime" : 1200000,"maximumTime" : 7200000,"maxSize" : 10000}}},"modules" : {"api" : false,"community" : false,"component" : true,"nodes" : false,"templates" : false,"users" : true},"ids" : {"lang" : {"en" : "hubspot_community_en","de" : "hubspot_community_de","es" : "hubspot_community_es","fr" : "hubspot_community_fr","ja" : "hubspot_community_jp","pt" : "hubspot_community_pt"}},"request" : {"get" : "","uri" : "https://community.hubspot.com/t5/user/viewprofilepage/user-id/169781","type" : "GET","https" : true,"options" : {},"context" : "","endpoint" : "https://community.hubspot.com/mjmao93648/plugins/custom/hubspot/hubspot/controller"},"rest_default_version" : 1,"rest_base_url_v1" : "https://community.hubspot.com/restapi/v1","rest_base_url_v2" : "https://community.hubspot.com/api/2.0/search?q=","stats" : {"requests" : 0,"total" : 0,"calls" : []},"logs" : []}, properties: {}, // Add a reference to the global (native) JS object for the community, can be useful lithium: LITHIUM?.CommunityJsonObject, version: LITHIUM?.CommunityJsonObject?.Config?.['app.revision'] || null }; // </script> // <script> just for inline syntax-highlighting... ;(function($){ var tools = { admin: { /** /* By default admin/studio pages all have the same page title "Community Settings" which is extermely unhelpful /* when having 10 admin/studio tabs open and then having to cycle through them just to find the right one. /* This fix makes admin/studio tabs more distinguishable. /*/ fixStudioTitle: function() { var title = [ $('.lia-bizapps-tab-studio-tab-group .lia-tabs-active').text(), $('.lia-bizapps-tab-community-tab-group .lia-tabs-active').text(), $('.lia-bizapps-page-title-community').text(), ]; $('title').text(title.join(' - ')); }, /** /* In multi-language communities it can be difficult to distinguish nodes if they all have the same title but another language. /* Usually we add a language suffix to the node ID but that is not easily visible in the community structure. Thea idea of this /* enhancement is to extract that language suffix from the node id and add it to the node title within the community structure. /*/ structureAddLanguageFromID: function(validLanguages = ['de', 'fr', 'it', 'jp', 'en', 'es', 'pt']) { $('.lia-component-admin-widget-node-editor-tree .lia-list-tree-toggle-node').each(function() { var $listitem = $(this); var $title = $listitem.find('span.lia-node-display-node-title:first'); var id = $listitem.find('.manage-node-link').first().prop('href').split('_').pop().trim().toLowerCase(); // console.log(id); if ( validLanguages.includes(id) ) { $title.text($title.text() + ' (' + id.toUpperCase() + ')'); } }); }, }, dom: function() { /** /* Just a little clutter saver for components. We should specify both aria-label and title /* attributes, but doing so can lead to very messy markup. As aria-label is more important /* devs have the option of simply adding an empty title attribute as well to elements which /* have aria-label already. This little tool will look for those and simply copy the aria-label /* text over to the title attribute so mouse-users can also get the hints as tooltips. /*/ $('[aria-label][title=""]').each(function() { $(this).attr('title', $(this).attr('aria-label')); }); /** /* A 'big-target' implementation I came up with using a `data-target` attribute on the actual link. /*/ (function(attr = 'data-target') { document.querySelectorAll(`a[${attr}]`).forEach(link => { //console.log('handling big target', link); let trigger = link.parentNode; const selector = link?.getAttribute(attr); while (trigger && trigger !== document) { if ( (!selector || trigger.matches(selector)) && !trigger.matches('[data-edit], [data-bind]') ) { trigger.style.cursor = 'pointer'; trigger.querySelectorAll('a').forEach(link => link.addEventListener('click', (e) => e.stopPropagation())); trigger.addEventListener('click', (e) => (!window.getSelection().toString() && link.click())); break; } trigger = trigger.parentNode; } }); })(); }, installKV: function() { /** /* Creates a Deno KV-like key-value store based on the localStorage with optional /* (custom) versioning and migration support. /* This has passed basic testing, although not how well it aligns with what native Deno KV does itself! /* The API is the same minus atomic operations and some options like consistency level, Kv64 etc. /* that don't really make sense or are insanely complex or impossible to do with a synchronous /* API like localStorage is. The KV's methods are all fake async (because Deno KV's api is async) /* so code written and used in the browser with this implementation should (hopefully) work /* with Deno KV on the server side as well (muuuch more testing needed to confirm that)! /* Aside from `open()` and `close()` this implementation should cover the entire Deno KV API. /* /* There are of course differences in behavior you should be aware of if you build any logic around it: /* - The most important limitation with this mock is that if you use non-serializable values in /* in your keys (your values too!), it will not behave like you expect! Stick to types that can be /* serialized to JSON... localStorage can only handle strings, that's the reason. /* - With Deno KV you can pass an `expireIn` option, but the values you receive from `get()` /* will never return that expiration date. This implementation returns that information /* when querying a key, e.g. besides value and versionstamp you get back `expires` /* if a TTL was provided when setting the value, it's going to be an ISO timestamp otherwise `null`. /* - `list()` is an AsyncIterator like it is with Deno KV, but it does NOT support cursor, consistency /* or batchSize options, you can pass them to the method, but nothing will happen. /* Deno KV `list()` requires a selector, this implementation does not, it will simply list all /* KV entries if you don't provide a selector. /* Furthermore this `list()` implementation supports an optional 3rd argument which is a function /* passed to `Array.filter()` that allows filtering the returned entries further after selector(s) /* and options have been applied. /* - This implementation does support some features Deno KV does not have: /* 1. You can optionally provide a `version` function that will replace how the versionstamp is created /* and thus provide your own versioning implementation. /* 2. You can also provide a `migrate` function that will be applied if an outdated versionstamp /* is encountered, you can for example extend the old value with some new ones keeping what was stored. /* 3. There is a `toJSON()` method you can use to serialize either the entire KV store or a portion of it. /* It supports the same arguments as `list()` (because it internally calls it) and has options to /* serialize prettified JSON or streamable (individual store entries separated by newlines) JSON. /* /* @param {Function} [version] - Optional function to generate a versionstamp for stored values. /* @param {Function} [migrate] - Optional function to migrate outdated values. It is called with the key and outdated value and should return the migrated value. /* @param {String} [prefix='kv:'] - Prefix to be used for keys in localStorage, helping in namespacing and avoiding key conflicts. /* /* @returns {Object} - An object providing the mocked Deno KV API methods to interact with the store. /*/ Object.defineProperties(window, { $kv: { value: function(version, migrate, prefix = 'kv:') { const $hash = typeof version === 'function' ? version : ( $hash || ((v) => 1) ); const _queueListeners = new Set(); const _key = { inrange: (key, start, end) => { const orderedKey = _key.order(key); const orderedStart = start ? _key.order(start) : null; const orderedEnd = end ? _key.order(end) : null; if (orderedStart && orderedKey < orderedStart) { return false; } if (orderedEnd && orderedKey >= orderedEnd) { return false; } return true; }, order: (key) => key.slice().sort((a, b) => { const order = ['Uint8Array', 'string', 'number', 'bigint', 'boolean']; const typeA = typeof a, typeB = typeof b; return typeA === typeB ? (typeA === 'number' ? a - b : String(a).localeCompare(String(b))) : order.indexOf(typeA) - order.indexOf(typeB); }), // Check how Deno KV actually does it here: // https://github.com/denoland/deno/blob/main/ext/kv/codec.rs serialize: (key) => prefix + JSON.stringify(key), deserialize: (serializedKey) => JSON.parse(serializedKey.substring(prefix.length)) }; return { /** /* Retrieves a value from the store by its key(s). /* /* @param {Array} key - The keys array to look up the value. /* /* @returns {Promise<Object>} - An object containing the key, its associated value, versionstamp and expiry. /*/ get: async (key) => { const serializedKey = _key.serialize(key); const data = JSON.parse(localStorage.getItem(serializedKey)); if ( !data || (data.expires && new Date(data.expires) < new Date()) ) { data && data.expires && localStorage.removeItem(serializedKey); return { key, value: null, versionstamp: null }; } if ( data.versionstamp !== $hash(data.value) ) { if (typeof migrate === 'function') { data.value = await migrate(key, data.value); data.versionstamp = $hash(data.value); localStorage.setItem(serializedKey, JSON.stringify(data)); } else { console.warn('Outdated value found but no migration function defined!', key, data); } } return { key, ...data }; }, /** /* Retrieves multiple values from the store based on the provided keys. /* /* @param {Array<Array>} keys - An array of key arrays to look up the values. /* /* @returns {Promise<Array<Object>>} - An array of objects. /*/ getMany: async function(keys) { return Promise.all(keys.map(key => this.get(key))) }, /** /* Stores a value in the store with the given key. /* /* @param {Array} key - The key array to associate with the value. /* @param {*} value - The value to be stored. /* @param {Object} [options] - Optional parameters for storing the value. `expireIn` sets the expiration time in milliseconds. /* /* @returns {Promise<Object>} - An object indicating the success and the versionstamp of the stored value. /*/ set: async (key, value, options = {}) => { const data = { value, versionstamp: $hash(value), expires: options.expireIn ? new Date(Date.now() + options.expireIn).toISOString() : null }; localStorage.setItem(_key.serialize(key), JSON.stringify(data)); return { ok: true, versionstamp: data.versionstamp }; }, /** /* Removes a value from the store by its key. /* /* @param {Array} key - The key array of the value to be removed. /*/ delete: async (key) => localStorage.removeItem(_key.serialize(key)), /** /* Iterates over values in the store based on the provided selector prefix, range, or limited by options. /* This method provides an AsyncIterator to be used with `for await...of` loops. /* This `list()` implementation supports an additional third argument which is NOT STANDARD for Deno KV. /* It's a function that allows for further filtering the results before yielding them within the iterator. /* /* The selector can either be a prefix selector or a range selector: /* - A prefix selector selects all keys that start with the given prefix (optionally starting at a given key). /* - A range selector selects all keys that are lexicographically between the given start and end keys. /* /* @param {Object} [selector] - The selection criteria. NOT STANDARD: Can be undefined here, not with Deno KV! /* @property {Array} [selector.prefix] - Defines the prefix for filtering the results. /* @property {Array} [selector.start] - The starting key for range selection. /* @property {Array} [selector.end] - The ending key for range selection. /* @param {Object} [options] - Optional parameters for the listing. /* @property {number} [options.limit] - Limits the number of results. /* @property {boolean} [options.reverse] - If true, reverses the order of results. /* @param {Function} [fn] - NON STANDARD: Optional filter function to filter entries before yielding. /* /* @returns {AsyncIterator} - An AsyncIterator yielding store entries. /*/ list: async function*(selector, options = {}, fn) { const keys = options?.reverse ? Object.keys(localStorage).reverse() : Object.keys(localStorage); for (const serializedKey of keys) { // Limit results based on `options.limit` if ( options?.limit !== undefined && options.limit <= 0 ) { break; } if ( !serializedKey.startsWith(prefix) ) { continue; }; const deserializedKey = _key.deserialize(serializedKey); if ( selector?.prefix && !deserializedKey.slice(0, selector.prefix.length).every((part, index) => part === selector.prefix[index]) ) { continue; } if ( selector?.start && !_key.inrange(deserializedKey, selector?.start, selector?.end) ) { continue; } const entry = await this.get(deserializedKey); if ( entry.value !== null ) { if ( !fn || (typeof fn === 'function' && fn(entry)) ) { if ( options?.limit !== undefined ) { options.limit--; } yield entry; } } } }, /** /* Adds a value into a mock database queue to be delivered to queue listeners. /* This method simulates the behavior of the Deno KV's enqueue(). /* `keysIfUndelivered` option is not supported, you can pass it, but won't have an effect. /* /* @param {*} value - The value to be enqueued. /* @param {Object} [options] - Optional settings for the enqueue operation. /* @param {number} [options.delay] - Delays the delivery of the value by the specified number of milliseconds. /* @returns {Promise<Object>} - An object indicating the success of the enqueue operation. /*/ enqueue: async (value, options) => { for (const fn of _queueListeners) { options?.delay && await (new Promise(res => setTimeout(res, options.delay))); await fn(value); }; }, /** /* Listens for queue values to be delivered from the mock database queue. /* This method simulates the behavior of the Deno KV's listenQueue(). /* /* @param {Function} handler - A callback function that gets when a new value is dequeued. /* /* @returns {Promise<void>} /*/ listenQueue: async (handler) => { if ( !_queueListeners.has(handler) ) { _queueListeners.add(handler) } }, /** /* Provides a mock for Deno Kv's AtomicOperation API with the same chainable methods, but /* they do nothing...you can provide a function to each of the mock methods that receives /* `this`, just return it again, otherwise you break the chaining! Something like this: /* /* @example /* ``` /* (await $core.kv().atomic()).check((t) => (console.log('check'), t)).min((t) => (console.log('min'), t)).commit(); /* // you can pass an arbitrary number of additional args to those mock functions, like /* (await $core.kv().atomic()).check((t, arg1, arg2) => (console.log('check', arg1, arg2), t), 'foo', 'var') /* ``` /* /* @returns {Promise<*>} - A mocked API of Deno KV's atomic(). /*/ atomic: async () => { console.warn('Atomic ops are not supported!'); return [ 'check', 'commit', 'delete', 'enqueue', 'max', 'min', 'mutate', 'set', 'sum' ].reduce((r, m) => (r[m] = function(fn, ...args) { return typeof fn === 'function' ? fn(this, ...args) : this }, r), Object.create(null)); }, /** /* NON STANDARD: Serializes the store to a JSON string. /* The methods first 3 arguments match the ones from list() (see there for details). /* /* @param {Object} selector - The selection criteria for list(). /* @param {Object} [options] - Optional parameters for list(). /* @param {Function} [fn] - Optional filter function list(). /* @param {Number|String} [pretty] - JSON.stringify() space param to prettify output. Defatuls to tab indent. /* @param {Boolean} [streamable] - If the output should be stringified entries separated by newlilnes. /* /* @returns {String} - A JSON string representation of the KV store. /*/ toJSON: async function(selector, options, fn, pretty = '\t', streamable = false) { let data = []; for await (const entry of this.list(selector, options, fn)) { data.push(entry); } data = data.reduce((r, entry) => { if ( streamable ) { r.push(JSON.stringify({ key: entry.key, value: entry.value })); return r; } return r[JSON.stringify(entry.key)] = entry.value, r; }, streamable ? [] : {}); return streamable ? data.join('\n') : JSON.stringify(data, null, pretty); }, }; }, configurable: false, // Cannot be deleted enumerable: false, // Will not show up in loops writable: false, // Cannot be overwritten } }); }, logMeIn: function() { /** /* Enable 'magic' redirect to login page when "logmein" is typed into the void =) /* just for convenience and speed, only useful on stage if SSO is activated for the community /*/ var keycodes = { logmein: [76, 79, 71, 77, 69, 73, 78], listudio: [], liadmin: [] }; var neededkeys = [76, 79, 71, 77, 69, 73, 78]; var watching = false; var count = 0; $(document).keydown(function(e) { var key = e.keyCode; // console.log(key); // Set start to true only if the first key in the sequence is pressed if ( !watching ) { if ( key == neededkeys[0] ) { watching = true; } } // If watching, pay attention to key presses, looking for right sequence. if ( watching ) { // console.log('watching: ' + key); if ( neededkeys[count] == key ) { // We're good so far. count++; } else { // Oops, not the right sequence, lets restart from the top. watching = false; count = 0; return; } if ( count == neededkeys.length ) { // We made it! Execute whatever should happen when entering the right sequence window.location.replace('/t5/user/userloginpage'); // Reset the conditions so that someone can do it all again. watching = false; count = 0; return; } } else { // Oops. watching = false; count = 0; return; } }); }, scssCompile: function() { /** /* Handle on-the-fly SCSS compilation if any text/scss inline style tags are found /* This primarily useful for development, no style/scss tags should remain when on /* production as they would still be compiled. /*/ if ( $('style[type$="scss"]').length ) { $('style[type$="scss"]').each(function() { var $el = $(this); var scss = $el.text(); $core?.config?.user?.admin && console.log('Found inline SCSS style tag, compiling to CSS...', $el); $.ajax({ type: 'POST', url: 'https://www.sassmeister.com/app/lib/compile', data: { input: scss, compiler: 'lib', syntax: 'scss', original_syntax: 'scss', output_style: 'nested' }, contentType: 'multipart/form-data', dataType: 'json' }) .done(function(response) { $core?.config?.user?.admin && console.log('core.cmp.tools: SCSS successfully compiled, injecting usable CSS...'); // inject compiled CSS into irignal source tag $el.text(response.css).attr('type', 'text/css'); }) .fail(function(err) { console.log('fail', err); }); }); } }, redirect: function() { /** /* Handles redirects specified in `cmp.global.scripts` (or elsewhere) and added to /* the `$core.redirects` object. /*/ if ( $core?.redirects ) { Object.entries($core.redirects).some(function([source, target]) { if ( window.location.pathname === source ) { $core?.config?.user?.admin && console.log('core.cmp.tools: redirect match found, redirecting...'); window.location.href = target; // Stop the .some() iteration by returning true return true; } }); } }, fixAmp: function() { /** /* Handle &amp ampersand bug in SEO field of ArticleEditorPage (TKB, more?) /* & gets replace with &amp; but each time the article is saved again, the & of the &amp; /* gets encoded again, resulting in repeated and invalid encoding, e.g. &amp;amp;amp;amp;amp; /*/ $('.lia-form-message-seo-description-input, .lia-form-message-seo-title-input').each(function() { $(this).val($(this).val().replaceAll(/amp;/gm, '')); }); }, fixTOC: function() { /** /* Adds 'is--toc' class to `ul`-tag of BlogArticle TOC (table of contents, an Angular component by Khoros) /* as it natively does not have any identifier, making it tricky to target with CSS /* WebKit browsers remove list semantics when list-style-type is none, we fix that with `role=list` /*/ $('.BlogArticlePage a[href*="#toc-hId"]').first().parents('ul').addClass('is--toc').attr('role', 'list'); }, inview: function() { // Make sure nothing explodes in older browsers that do not support IntersectionObserver if ( 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype ) { // Minimal polyfill for Edge 15's lack of `isIntersecting` // See: https://github.com/w3c/IntersectionObserver/issues/211 if ( !('isIntersecting' in window.IntersectionObserverEntry.prototype) ) { Object.defineProperty(window.IntersectionObserverEntry.prototype, 'isIntersecting', { get: function () { return this.intersectionRatio > 0; } }); } // Set up the intersection observer const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { // If the element is fully in view, trigger custom event with jQuery if (entry.isIntersecting && entry.intersectionRatio === 1) { entry.target.dataset.inview = 'true'; $(document).trigger('inview', [entry.target]); } else if ( entry.target.dataset.inview !== 'false' ) { entry.target.dataset.inview = 'false'; } }); }, { threshold: 1 }); // Observe each element with attribute `data-inview` $('[data-inview]').each(function() { observer.observe(this); }); } }, XHRmiddleware: function() { // Middleware storage const middlewares = []; // Override the XMLHttpRequest constructor (breaks Khoros for some reason...) /* const xhrRequest = XMLHttpRequest; XMLHttpRequest = function() { const xhr = new xhrRequest(); return new Proxy(xhr, { get: function(target, prop) { // You can intercept specific properties here and provide custom values if ( prop === 'responseText' ) { let value = target[prop]; middlewares.forEach((fn) => { if ( !fn.on || fn.on.includes('response') { // make sure in case the dev forgets to return from the fn() // we still return a value, it's gonna be the unmodified one // but better than nothing... value = fn(target, 'response', value) || value; } }); return value; } // For all other properties, return the original value return target[prop]; }, set: function(target, prop, value) { target[prop] = value; return true; } }); }; XMLHttpRequest.prototype = xhrRequest.prototype; */ // Save original XHR methods const xhrSend = XMLHttpRequest.prototype.send; const xhrOpen = XMLHttpRequest.prototype.open; // Define the .$use() method using Object.defineProperty Object.defineProperty(XMLHttpRequest, '$use', { value: function(fn) { if ( typeof fn === 'function' ) { middlewares.push(fn); } }, writable: false, // Cannot be overwritten enumerable: false, // Will not show up in loops configurable: false, // Cannot be deleted }); // Override the .open() method XMLHttpRequest.prototype.open = function(method, url, async, user, password) { middlewares.forEach((fn) => { !fn.on || fn.on.includes('open') ? fn(this, 'open', method, url, async, user, password) : null; }); xhrOpen.apply(this, arguments); }; // Override the .send() method XMLHttpRequest.prototype.send = function(body) { const xhr = this; middlewares.forEach((fn) => { !fn.on || fn.on.includes('send') ? fn(xhr, 'send', body) : null; }); // Attach an event listener for the 'readystatechange' event xhr.addEventListener('readystatechange', function() { if ( xhr.readyState === XMLHttpRequest.DONE ) { middlewares.forEach((fn) => { !fn.on || fn.on.includes('done') ? fn(xhr, 'done') : null; }); } }); xhrSend.apply(this, arguments); }; // Example middleware /* XMLHttpRequest.$use((xhr, event, ...args) => { if ( event === 'open' ) { // args is going to be [method, url, async, user, password] if defined console.log('Request created:', xhr, ...args); } else if ( event === 'send' ) { // args is going to be [body] the optional payload/body of the request console.log('Request payload is about to be sent:', args[0]); } else if ( event === 'response' ) { // args is going to be [responseText]! console.log('Can modify response text!, xhr); // IMPORTANT: Return something from here, // otherwise response is gonna be returned umodified to the caller! return xhr; } else if ( event === 'done' ) { console.log('Response received:', xhr.responseText); } }); */ }, }; // Install tools before extending core in case we need access to globally defined tools! Object.entries(tools).map(function([name, fn]) { if ( typeof fn === 'function' ) { $core?.config?.user?.admin && console.info(`core.cmp.tools: Running tools.${name}()`); fn(); } }); // Extend $core base variable with additional capabilities if ( $core ) { /** /* Helper method to automatically proxy requests to external URL's through the proxy endpoint /* when they can't be requested directly via JS due to CORS restrictions. /*/ $core.compile = async function(mount) { console.time("compiling"); // start benchmark // Handles inline tag definitions and script-linked tags with `[type="riot/tag"]`. // Why not go with the native riot.compile()? a) we want to check first if we actually // need to fetch the tag file or not (if it wasn't modified) and b) we want to compile // tags that are defined inline as well and cache the compiled result of both for faster // loading during prototyping. const usedb = true; const db = localStorage; const sources = Array.prototype.slice.call(document.querySelectorAll('script[type*="riot"]')).concat(Array.prototype.slice.call(document.querySelectorAll('template[type*="riot"]'))); const tags = await Promise.all( sources.map(async (el, i, arr) => { //console.log('compiling from:', el.hasAttribute('data-src') ? 'File' : 'Inline Template'); let cached; let hash; let tag; let response; if (el.hasAttribute('data-src')) { try { if ( usedb ) { // first we want to check the last modified header! // if the dev server is not running this will fail and enter catch block response = await fetch(el.getAttribute('data-src'), { // credentials: 'include', method: 'HEAD', }); // typecast to string for localStorage (keys are strings!) hash = `riot:${await $hash(el.getAttribute('data-src') + new Date(response.headers.get('last-modified')).getTime())}`; cached = db.getItem(hash); } if ( !cached ) { console.log('compile(): No cached version found, fetching source!'); response = await fetch(el.getAttribute('data-src'), { // credentials: 'include', method: 'GET', }); data = await response.json(); response = { headers: [...response.headers].reduce((acc, header) => { return { ...acc, [header[0]]: header[1] }; }, {}), status: response.status, data: data, }; //console.log('response', response); // add attribute data-scoped="false" to the include script tag to turn off CSS scoping //tag = riot.compileFromString(response.data, { scopedCss: !['false', '0', 0].includes(el.getAttribute('data-scoped')) }).code; // TODO: Dev server should return compiled code exactly as riot.deno.dev does tag = response.data?.code; //console.log('tag', tag); if ( tag && usedb ) { db.setItem(hash, tag); } console.log(`compile(): No cached tag found, file-tag compiled ${usedb ? `and cached with hash ${hash}`: ''}`); } else { tag = cached; console.log(`compile(): Found cached version of file-tag!`); } } catch (ex) { // We do not get details for net errors (e.g. if the server is down), so we try to // isolate those because they don't have a message (well, which ones do? custom ones?) if ( !ex.data?.message ) { console.warn(`compile(): fetching ${el.getAttribute('data-src')} failed: Dev server is not reachable...`); } else { console.error(ex); } } } // if `tag` is still undefined, we can assume the dev server wasn't running, // so we compile the content locally if ( !tag ) { // `.innerHTML` returns the 'fixed' (browser interpreted) HTML, but it will encode // `&` to `&amp;`, also within riot expressions, which of course messes up the compiler // so once we have the tag html, we need to get those encoded ampersands back to normal tag = el.innerHTML.replace(/&amp;/g, '&').trim(); if ( !tag ) { console.warn(`compile(): Tried to get content from inline tag, but was empty, did you forget to paste the component code in?`, el); return; } hash = `riot:${await $hash(tag)}`; cached = db.getItem(hash); if ( !cached ) { // add attribute data-scoped="false" to the template tag to turn off CSS scoping //tag = riot.compileFromString(tag, { scopedCss: !['false', '0', 0].includes(el.getAttribute('data-scoped')) }).code; try { response = await (await fetch('https://riot.deno.dev', { method: 'POST', body: JSON.stringify({ markup: encodeURIComponent(tag), versionstamp: await $hash(tag), key: await $hash(window.location.origin), }), headers: { 'content-type': 'application/json', }, })).json(); if ( response.code ) { tag = response.code; } else { console.error('compile(): Faulty response', response); if ( response.payload ) { console.warn('compile(): Creating downloadable file from failed component markup!'); const file = new File([response.payload.markup], 'failed.tag.html', { type: 'text/plain', }); const fr = new FileReader(); fr.onload = function(e) { const link = `<span class="compile-error">Download uncompilable markup: <a href="${URL.createObjectURL(file)}" download="${file.name}">${file.name}</a></span>`; $('body').append(link); } fr.readAsText(file); } throw new Error('compile(): response.code could not be found!', { cause: 'fetch()', message: response }); } } catch(ex) { console.error(ex); tag = null; } if ( tag && usedb ) { db.setItem(hash, tag); } console.log(`compile(): No cached tag found, inline-tag compiled ${usedb ? `and cached with hash ${hash}` : ''}`); } else { tag = cached; console.log(`compile(): Found cached version of inline-tag!`); } } try { const { groups: { name = null } } = tag.match(/^riot\.register\(['"](?<name>[^\s'"]+)/) || { groups: {} }; console.log('compile(): tag name = ', name); //riot.inject(tag, name, `./${name}.html`); // yeahyeah, eval is evil, but we are the author of the code, so nothing to worry... } catch (ex) { console.error('compile(): Something went wrong with tag name extraction', ex); } try { eval(tag); } catch(ex) { console.error(`compile(): Something went wrong with tag evaluation for '${name}'`, ex); } return { name: name, hash: hash, cached: cached !== null, code: tag }; }) ); console.log('compile(): result', tags); console.timeEnd('compiling'); // end benchmark // optionally mount tag(s) via the compile() method if ( typeof mount === 'string' ) { console.time('mounting'); riot.mount(mount); console.timeEnd('mounting'); } else if ( typeof mount === 'boolean' ) { // auto-mount all top level components, but not the nested ones, they will be handled by the parent riot.mount('[is]:not([is] [is])'); } }; /** /* Helper method to automatically proxy requests to external URL's through the proxy endpoint /* when they can't be requested directly via JS due to CORS restrictions. /* /* @usage `$core.fetch('<url>', {<fetch.options>});` /* /* @param {string} url - The URL to fetch via proxy. /* @param {object} options - An optional fetch options object. /* /* @returns {any} - The proxied response data. /*/ $core.fetch = function(url = '', options = {}, cache) { return fetch(`${$core.config.request.endpoint}?get=proxy&url=${encodeURIComponent(url)}${cache ? '&cache=' + cache : ''}`, options); }; $core.fmt = { /** /* Adapted from https://github.com/lukeed/tinydate/blob/master/src/index.js /* /* @usage `$core.fmt.date('Current time: [{HH}:{mm}:{ss}]')(new Date())` /* /* @param str - Output string with placeholders /* @param custom - Custom formatter functions for placeholders (optional) /* /* @return - Returns a rendering function that will optionally accept a date value as its only argument. /*/ date: function(str, custom) { const RGX = /([^{]*?)\w(?=\})/g; const MAP = { YYYY: 'getFullYear', YY: 'getYear', MM: function (d) { return d.getMonth() + 1; }, DD: 'getDate', HH: 'getHours', mm: 'getMinutes', ss: 'getSeconds', fff: 'getMilliseconds' }; let parts=[], offset=0; str.replace(RGX, function(key, _, idx) { // save preceding string parts.push(str.substring(offset, idx - 1)); offset = idx += key.length + 1; // save function parts.push(custom && custom[key] || function(d) { return ('00' + (typeof MAP[key] === 'string' ? d[MAP[key]]() : MAP[key](d))).slice(-key.length); }); }); if ( offset !== str.length ) { parts.push(str.substring(offset)); } return function(arg) { var out='', i=0, d=arg||new Date(); for (; i<parts.length; i++) { out += (typeof parts[i]==='string') ? parts[i] : parts[i](d); } return out; }; }, }; /** /* Map (now) global $hash function to $core namespace for backwards compatibility. /*/ $core.hash = $hash; /** /* Dynamically imports and appends scripts to the DOM. /* Offers extended functionality such as manual deferral, error handling, and initialization tasks. /* /* @usage /* ``` /* $import('path/to/script.js').then(() => { /* console.log('All scripts loaded!'); /* }).catch(error => { /* console.error('Error loading script:', error); /* }); /* ``` /* /* @param {String|Array|HTMLElement|NodeList} input - Path(s) to the script(s) to be imported, or DOM nodes. /* @param {Object} [options] - Optional configuration object. /* @param {Function} [options.on] - Function to manually handle the script injection. /* @param {Function} [options.init] - Function to run initial tasks, e.g. for setup purposes. /* @param {HTMLElement} [options.target=document.body] - DOM element to which the script will be appended. /* @param {Object} [options.attributes={}] - Additional attributes to set on the script element. /* /* @returns {Promise} - Resolves when all scripts are loaded; rejects on any error. /*/ $core.import = function(scripts, { on, init, target = document.body, attributes = {} } = {}) { // Ensure scripts is always an array and not an already loaded script scripts = [].concat(scripts).filter(s => !(s?.src || document.querySelector(`script[src="${s}"]`))); typeof init === 'function' && init(scripts); return Promise.all(scripts.map(script => new Promise((resolve, reject) => { let el = script instanceof HTMLElement ? script : document.createElement('script'); if ( el.tagName === 'SCRIPT' ) { if ( el.querySelector(':is(script[data-src])') ) { el.src = el.getAttribute('data-src'); } else { Object.entries({ ...attributes, src: script }).forEach(([attr, val]) => el.setAttribute(attr, val)); } } else { return reject({ message: `Invalid input!`, data: script }); } el.addEventListener('load', resolve(el, script)); el.addEventListener('error', (e) => reject({ message: `Failed to load script: ${e.target.src}`, event: e })); // If an `on` function is provided, pass the script element to it for manual deferring typeof on === 'function' ? on(el, script) : target.appendChild(el); }))); }; /** /* Map global KV localStorage wrapper to $core for convenience. /*/ $core.kv = $kv; /** /* Proximity sensor helper method. Triggers a callback function when the mouse is within /* a certain distance of the given element. With the optional `check` flag set to `true` the method /* will check if the target element is reachable by the user, e.g. not hidden or obstructed by /* other elements. These checks are off by default, as they will be triggered with every tracked /* mousemove event which can potentially cause performance issues. If the target element is initially /* hidden, consider binding `$near()` AFTER the element has become visible! /* /* @usage /* ``` /* $near('.my-button', 50, (el, threshold) => { /* console.log(`Mouse is within ${threshold}px of ${el}`); /* }); /* ``` /* /* @param {Element|String} el - The target DOM element or a selector string to identify the element. /* @param {Number} threshold - The proximity threshold (in pixels) at which the callback will be invoked. /* @param {Function} fn - The callback function to be invoked when the mouse is within the defined distance of the target. /* @param {Boolean} [once=false] - If true, the callback will be triggered only once. /* @param {Boolean} [check=false] - If true, performs enhanced checks on the element that it's visible and not obstructed. /* /* @returns {void} /*/ $core.near = function(el, threshold, fn, { once = false, check = false } = {}) { el = typeof el === 'string' ? document.querySelector(el) : el; if ( !el || typeof fn !== 'function' || typeof threshold !== 'number' ) { return; } // make sure element exists let run = false; let within = false; // flag to track if the mouse is inside the proximity zone const proximity = function(target, x, y) { const { left, right, top, bottom } = target.getBoundingClientRect(); if ( check ) { const style = window.getComputedStyle(target); if ( Object.entries({ display: 'none', visibility: 'hidden', opacity: '0' }).some(([p, v]) => style[p] === v) ) { return false; } } return x > left - threshold && x < right + threshold && y > top - threshold && y < bottom + threshold; }; const handler = (e) => { if (run) { return; } run = true; window.requestAnimationFrame(() => { const near = proximity(el, e.clientX, e.clientY); if ( near && !within ) { within = true; fn(el, threshold); once && window.removeEventListener('mousemove', handler); } else if ( !near && within ) { within = false; } run = false; }); }; window.addEventListener('mousemove', handler); // return a function that removes the event listener when called return () => window.removeEventListener('mousemove', handler); }; /** /* This is the velocity parser implemented in core ported over to javascript, but it's only /* half useful, as it can't interpolate native strings, so this is only used to interpret /* non-interpolated velocity expressions. /*/ $core.parseVelocity = function(key = '', value = '', args = []) { // Cast all placeholder values to strings, otherwise the parser can't deal with the args args = args.map(String); const scopes_map = { component: $core.config.file, device: $core.config.user.device, page: $core.config.node.quilt, place: $core.config.node.type, lang: $core.config.user.lang, }; const scopes_matches = [...key.matchAll(/@([^@\r\n\t\f\v ]+)/gm)] || []; const scopes = scopes_matches.reduce((obj, match) => { const scope = match[1].split(':')[0]; const value = match[1].split(':')[1]; if ( scopes_map[scope] ) { obj[scope] = (scopes_map[scope] == value); } return obj; }, {}); const choice = (expr = '', args = []) => { const value = args[parseInt(expr.split(',').shift())]; const choices = expr.split('choice,').pop().trim(); return choices.match(`(${value})#(.*?)\\|`)?.[2] || choices.split('|').pop().replace(/\d+</, '') }; const rx = /\$\{(?<i>.+?)\}|\{(?<p>[0-5])\}|\{(?<e>[^\$]+?)\}/gm; let result = value; let cnt = 1; // Enter recursion to resolve nested velocity variables/placeholders/expressions // Have a safety max recursion depth to avoid unintentional memory leaks / endless loops while ( result.match(rx) ) { result = $core.parseVelocity(key, value.replace(rx, (match, i, p, e) => (i ? $core.str(i, null, ...args) : ( p ? args[parseInt(p)] || '' : choice(e, args) ))), args).value; if ( cnt >= 10 ) { console.warn('$core.parseVelocity(): Recursion depth of 10 exceeded!', result, result.match(rx), !!result.match(rx)); break; } cnt++; } return { key: key, value: result, scopes: scopes }; }; $core.serializeForm = function(form, json = false, filter, reducer) { // The shortest way of getting an object from FormData is // `Object.fromEntries((new FormData(form)).entries())` // but it will not handle select multiple inputs as the entries will have the same key // and therefore only the last selected option is returned as a value, to aggregate such array-like fields // properly, we have to manually loop over the fields after destructuring them into tuples (array of arrays) // this can also handle any type of field as an array of values by specifying the name with `[]` at the end // and furthermore it is also possible to directly aggregate form inputs into objects by specifying the name // attribute like so `name='object\{key}'`. NOTE: I'm not sure if the backslash escape is needed outside of // frameworks that interpolate expressions with `{`. Check the admin component for a practial example. const data = [...(new FormData(form)).entries()].reduce(reducer ? reducer : (obj, [name, value]) => { value = name.startsWith('(bool)') ? ({ '0': false, 'false': false, '1': true, 'true': true})[value.trim()] : value; const key = name.replace(/{(.+)}/gm, '').replace('(bool)', ''); value = obj[key] && key.endsWith('[]') ? [...obj[key], value] : ( key.endsWith('[]') ? [value] : ( /{(.+)}/gm.test(name) ? { ...obj[key], [[...name.matchAll(/{(?<k>.+)}/gm)][0]?.groups?.k]: value } : value ) ); if ( typeof filter === 'function' && filter(key, value) ) { obj[key] = value; } else { obj[key] = value; } return obj; }, {}); // Return a JSON string if requested return json ? JSON.stringify(data) : data; }; $core.obj = { /** /* Filters properties defined in props from the input object (non-destructive). /* /* @param {object} obj - The input object to operate on. /* @param {array} props - The properties to skip from the input object if present. /* /* @returns {object} - A new object without the properties defined in props. /*/ skip: function(obj = {}, props = []) { return Object.entries(obj).reduce((acc, [prop, value]) => { return props.includes(prop) ? acc : ((acc[prop] = value), acc); }, {}); }, /** /* Include only properties defined in props from the input object (non-destructive). /* Basically the opposite of _.skip(). /* /* @param {object} obj - The input object to operate on. /* @param {array} props - The properties to include from the input object if present. /* /* @returns {object} - A new object with only the properties defined in props. /*/ only: function(obj = {}, props = []) { return Object.entries(obj).reduce((acc, [prop, value]) => { return !props.includes(prop) ? acc : ((acc[prop] = value), acc); }, {}); }, }; $core.str = function(_key = null, _default = null, ...placeholders) { if ( !_key ) { return _default; } if ( _key && !_default ) { _default = _key; } // Try a direct lookup first, should work in most cases, except when there are @scopes // that weren't specified in the key let key = _key; let value = $core.properties[`${key}@component:${$core.config.file}`]; if ( !value ) { // Try to find a matching key by checking all properties //console.log(`$core.str(): could not find matching string for key ${key}: ${key}@component:${$core.config.file}, trying all properties:`, Object.entries($core.properties).find(([k, v]) => (k.includes(`@component:${$core.config.file}`) && k.split('@component').pop().includes(key)))); [ key, value ] = Object.entries($core.properties).find(([k, v]) => (k.includes(`@component:${$core.config.file}`) && k.split('@component').pop().includes(key))) || []; // console.log(`$core.str(): Could not find string by direct key (${_key}) lookup, find result:`, key, value); } /*else { console.log(`$core.str(): found matching string for key ${key}: ${key}@component:${$core.config.file}`); }*/ if ( value ) { return $core.parseVelocity(`${key}@component:${$core.config.file}`, value, placeholders).value; } else { return $core.parseVelocity(`${_key}@component:${$core.config.file}`, _default, placeholders).value; } }; /** /* Helper method facilitating watching for a DOM element for mutations and run a callback on them. /* /* @usage /* ``` /* $core.watch('<selector>', (mutation) => { /* console.log('I am here!', mutation); /* }, <optional:options>, <optional.immediate>); /* ``` /* /* @param {string} selector - The CSS selector to watch for mutations. /* @param {function} fn - The callback function to call when the element is mutated. /* @param {object} options - An optional options object for `.observe(<target>, <option>)` /* @param {boolean} immediate - If the callback function should be executed immediately once if the element is present. /*/ $core.watch = (selector, fn, options = {}, immediate = false) => { // Make sure nothing explodes in older browsers that do not support MutationObserver if ( window.MutationObserver ) { // Look for watched selector matching elements already present in the DOM, // and execute the callback on them immediately. if ( immediate && document.querySelector(selector) ) { [document.querySelector(selector)].forEach(fn); } options = { attributes: true, // can't have it set by default, otherwise not all attributes are monitored //attributeFilter: ['style', 'class'], attributeOldValue: false, characterData: false, characterDataOldValue: false, childList: false, subtree: false, ...(options || {}) } // One might have to do `const target = el.target as HTMLElement;` // within the callback to get an actual HTMLElement with its expected methods return (new MutationObserver(mutations => mutations.forEach(fn))) .observe(document.querySelector(selector), options); } }; /** /* Helper method facilitating waiting for a DOM element to appear and execute a callback. /* /* @usage `$core.when('<selector>', (el) => { console.log('I am here!', el); }, <optional:targetNode>);` /* /* @param {string} selector - The CSS selector to wait for. /* @param {function} fn - The callback function to call when the element appears. /* @param {HTMLElement} watch - The node to watch for mutations within. /*/ $core.when = (selector, fn, watch = document.body, existing = false) => { // Check if we even have a valid node to watch, otherwise MutationObserver will throw! watch = typeof watch === 'string' ? document.querySelector(watch) : watch; if ( !(watch instanceof Node) ) { console.warn(`$core.when(): 'watch' param wasn't a Node, aborting!`, watch); return; } // Make sure nothing explodes in older browsers that do not support MutationObserver if ( window.MutationObserver ) { // Look for watched selector matching elements already present in the DOM, // and execute the callback on them immediately. if ( document.querySelectorAll(selector).length ) { document.querySelectorAll(selector).forEach(fn); } return (new MutationObserver(mutations => [...mutations] .flatMap((mutation) => [...mutation.addedNodes]) .filter((node) => node.matches && node.matches(selector)) .forEach(fn))) .observe(watch, { childList: true, subtree: true }); } }; } // Initialize Tools // Make sure jQuery is available in some form which is not always the case in admin/studio and // several tools rely on it. if ( $ ) { // Tools only useful in admin/studio if ( $('body').is('.BizAppsPage') ) { Object.entries(tools.admin).map(function([name, fn]) { if ( typeof fn === 'function' ) { $core?.config?.user?.admin && console.info(`core.cmp.tools: Running tools.admin.${name}()`); fn(); } else { $core?.config?.user?.admin && console.warn(`core.cmp.tools: fn was not a function!`, fn); } }); } } // Bootstrap riot with global stuff if present // TODO: Maybe this should be its own component, separated from general purpose tools code! if ( window.riot ) { /** /* Stateless minimal router. /* Being stateless is a feature! The real "state" (i.e., the current route, history, etc.) /* is managed by the browser itself through the History API and the current URL. /* The router's job is to react to changes in that state and inform the rest of the app /* (via events or other mechanisms) about those changes. /*/ riot.$router = function(base = '/', options = {}) { const routes = []; base = '/' + base.replace(/^\/|\/$/g, ''); function parseRoute(route) { const { k, r } = route.split('/').reduce(({ k, r }, segment, i) => { switch (segment[0]) { case '*': return { k: k.concat('*'), r: r + '/(?<wild>.*)' }; case ':': const { key, con, ext, opt } = segment.match(/^:(?<key>[^\s(.?]+)(?:\((?<con>[\S]+?)\)(?![\)]))?(?<opt>\?)?\.?\(?(?<ext>[a-z0-9|]+)?\)?$/i)?.groups || {}; const p = `(?<${key}>${(con || '[^/]+?')}${ext ? `\\.(?<ext>${ext})` : ''})`; return key ? { k: k.concat(key), r: r + (opt ? `(?:/${p})?` : `/${p}`), } : { k, r }; default: console.log('parse index', i, Boolean(i)); return segment ? { k, r: r + `/(?<type${i > 1 ? i : ''}>${segment})` } : { k, r }; } }, { k: [], r: '' }); return { keys: k, pattern: new RegExp('^' + r + '/?$','i'), }; } function on(route, handler) { const { keys, pattern } = parseRoute(route); routes.push({ keys, pattern, handler }); } function navigate(path, replace) { const url = new URL(path, location.origin); const { keys, pattern } = parse(url.pathname); const match = routes.find(r => pattern.test(r.path)); if (match) { const match = match.pattern.exec(path); history[replace ? 'replaceState' : 'pushState'](null, null, path); $trigger('route', { url, match, keys, pattern }); } else if (options['404']) { options['404'](path); } } document.addEventListener('click', e => { const href = e.target.closest('a') && e.target.getAttribute('href'); const skip = [ !href, href.startsWith('#'), href.startsWith('javascript:'), !href.startsWith(base), e.defaultPrevented, e.button !== 0, e.metaKey, e.ctrlKey, e.shiftKey, e.altKey, ...(options.skipConditions || []) ]; if ( skip.some((con) => (typeof con === 'function' ? con(e) : Boolean(con))) ) { return; } e.preventDefault(); navigate(href); }); return { on, navigate }; }; riot.install((cmp) => { /** /* Override native riot `tag.$` (we don't touch `tag.$$`) with a much more powerful /* jQuery like API (it's not complete of course, but very mighty for 3.6KB code)! /* Of course we could also just map an already present jQuery instance to `tag.$`! /* This doesn't work, riot component internals are frozen! /*/ /*delete cmp.$; cmp['$'] = (selector) => { console.log('overwritten tag.$', selector); return cmp.$(selector); }; */ /** /* Install global event bus proxy methods. We do not want these methods scoped to every /* component individually but for them to be the same for all components so they can talk /* to each other on a global scope. /* /* - `on` will automatically bind `this` within the event listener function to the component. /*/ // This auto binding magic creates trouble, bind the component to the handler yourself if needed! //cmp['on'] = (e, fn, once) => $on(e, fn.bind(cmp), once); cmp['on'] = $on; cmp['off'] = $off; cmp['trigger'] = $trigger; /** /* Look for 'magic' listener methods defined within the component and automatically /* create a listener for them. These methods need to be named in a particular way: /* `$on<event.name.capitalized>` (note the $ prefix!), e.g. for an event type 'results' /* the component method needs to be defined as `$onResults: (e, results) => {}`, /* This approach is eliminating the need to define an explicit event listener somewhere /* within a regular lifecycle method with `tag.on('results', (e, results) => {})`... /* Using this automatic approach eliminates the need to remove any explicitely defined /* event listeners when a component is unmounted, as this is done automatically through /* the proxied lifecycle methods below. /* /* @note Be aware that if you need `this` bound to the component within your listeners /* (regardless if automatic or explicitely defined) you need to use the `function` way /* e.g. `$onResults: funciton(e, results) {}`! /* /* As this will create a listener for all events for every component, it's not enabled /* by default, only when the component has a property `events: true`. This way 'dumb' /* components do not get useless listeners registered which have to be processed! /*/ // We can either override riot.$trigger or 'properly' use the event bus, I'm not sure what // is actually better, both approaches seem to work just fine, when overriding obviously // there are no listeners stored and thus don't need to be removed when unmounting, resulting // in slightly less bootstrap code, but other than that it somehow feels wrong to me... /* if ( cmp.events ) { const $trigger = riot.$trigger; riot.$trigger = async (e, ...args) => { const $ = `$on${e.replace(/\b\w/, (c) => c.toUpperCase())}`; if ( cmp[$] ) { console.log(`automatically triggering ${$} listener for ${cmp.name}`); await cmp[$](e, ...args); } await $trigger(e, ...args); }; } */ let off = null; if ( cmp.events ) { // `on` will return a function to remove that listener, we store it for auto-cleanup off = cmp.on('*', async function(e, ...args) { const $ = `$on${e.replace(/\b\w/, (c) => c.toUpperCase())}`; if ( cmp[$] ) { await cmp[$](e, ...args); } }); } /** /* Proxy component lifecycle methods to give some debug info in debug mode /* allows us to define debugging stuff once here instead of having to repeat /* the same lengthy logging code in every single component increasing bundle /* size for nothing... we can reference component.name safely as it is part of the /* default component implementation along with .css and .template even though those /* properties are not enumerable (e.g. shown when console.log(component)) /* to proxy lifecycle methods simply set the .proxy property within the component to true /* if undefined or false nothing will be done here (e.g. the proxy is opt-in) /*/ ['onBeforeMount', 'onMounted', 'onBeforeUnmount'].forEach((method) => { // make a reference to the original lifecycle method and bind the component to it const org = cmp[method].bind(cmp); // add proxy method calling the original after it has done global stuff cmp[method] = (props, state) => { org(cmp.props, cmp.state); // automatically removes auto-event listeners when the component is unmounted if ( method === 'onBeforeUnmount' && off ) { off(); } cmp.debug && console.log(`${cmp.name}.${method}()`, cmp.props, cmp.state, method === 'onBeforeMount' ? cmp : null); }; }); //console.log('riot.install() done!', cmp); }); } if ( $core?.config?.env?.includes('stage') && $core?.config?.user?.roles?.includes('GlowingBlue') && document.querySelectorAll('[type*="riot"]').length && window.riot ) { // Live compiling riot components in dev mode console.log('core.cmp.tools: Compiling and registering riot components'); (async () => { await $core.compile(); // Useful for profiling component performance if ( window.performance ) { window.start = performance.now(); } if ( $trigger ) { await $trigger('compiled'); } })(); } else { // directly trigger on prod as components are pre-compiled! (async () => { if ( $trigger ) { // Wait for next event loop, if not it can cause strange non-mounting issues // due to no work having to be done (e.g. compile event basically happening immediately) // and the order in which @liaAddScript adds the JS from various places within the codebase await $wait(0); await $trigger('compiled'); } })(); } //document.addEventListener('DOMContentLoaded', function() {}); })(LITHIUM?.jQuery || jQuery); // Pull in global jQuery reference // </script> // <script> just for inline syntax-highlighting... ;(function($){ // This is very much work in progress, but a synk object should generally look something like: /* { type: <event-type-synk-understands>, event: { data: {}, source: { node: {}, page: {}, user: {}, }, verified: <bool>, } } */ // Store objectified request payloads for later use let payload = {}; // Define which particular routes we want to forward to synk. Those can be from forms, // links etc. // The key is the form action or link (partial) to check for when a request comes in // the value is an object with an `only`array for filtering the payload and a `type` // function to dynamically determine the type of event that is witnessed. // TODO: Look into those `t:cp=solutions/contributions/acceptedsolutionsactions` // identifiers that most actions seem to have, maybe they are an easier to detect way // of what to track on different pages, then the changing URL's which force us to use // URL partials... => unfortunately not, the identifier stays the same, the action is // still burried in the URL, like `markmessageasacceptedsolutionsecondarybutton` (WTF?): // t5/forums/v5/forumtopicpage.markmessageasacceptedsolutionsecondarybutton/message-uid/1844 const synk = { // AJAX: inline reply form submit 'inlinemessagereplyeditor.form.form.form.form': { only: [ 'attachment-key', 'liaFormContentKey', 'mediaSnippetUrl', 'multipleUpload', 'parentMessageRef', 't:ac', 'tags_', 'tinyMceEditor', ], type: (xhr) => 'post-reply', }, // AJAX: Kudos button 'kudosbuttonv2.kudoentity': { only: ['triggerEvent', 'parameterOverrides'], type: (xhr) => (xhr.responseURL.includes('revoke-kudos/true') ? 'dislike' : 'like'), }, // not AJAX: Report content to moderator 'notifymoderatorform.form.form.form': { type: (xhr) => 'mod-check', }, // subscribe: /t5/forums/v5/forumtopicpage.__addmessageuseremailsubscription__/message-uid/1841?t:cp=subscriptions/contributions/messageactions // post-mute: /t5/forums/v5/forumtopicpage.__addmessageusermute__/message-uid/1924?t:cp=subscriptions/contributions/messageactions // bookmark: /t5/forums/v5/forumtopicpage.__addmessageuserbookmark__/message-uid/1924?t:cp=subscriptions/contributions/messageactions // post-edit: /t5/forums/v5/forumtopicpage.__editmessageinline:editmessage__/message-uid/1924?t:cp=boards/contributions/messageactions // post-delete: /t5/forums/v5/forumtopicpage.__deletemessage:deletemessage__/message-uid/1924?t:cp=boards/contributions/messageactions // post-move: /t5/forums/v5/forumtopicpage.__movemessage:movemessage__/message-uid/1924?t:cp=boards/contributions/messageactions // post-solved: /t5/forums/v5/forumtopicpage.__markmessageasacceptedsolutionsecondarybutton__/message-uid/1844?t:cp=solutions/contributions/acceptedsolutionsactions // post-unsolved: /t5/forums/v5/forumtopicpage.__unmarkmessageasacceptedsolution__/message-uid/1844?t:cp=solutions/contributions/acceptedsolutionsactions }; $on('xhr', (e, state, xhr, ...args) => { //console.log('$onXHR', e, xhr, args); if ( state === 'open' ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Request created', args, xhr); } else if ( state === 'send' ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Request payload is about to be sent', Object.fromEntries([...(new URLSearchParams(args[0]))])); payload = args[0] ? Object.fromEntries([...(new URLSearchParams(args[0]))]) : {}; } else if ( state === 'response' ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Can modify response text!', args, xhr); // important to return modified response from here return args[0]; } else if ( state === 'done' ) { if ( !Object.entries(synk).some(([urlpartial, obj]) => xhr.responseURL.includes(urlpartial)) ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: skipping response processing for', xhr.responseURL); return; } $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Response received', xhr.responseText); try { // Do some basic sanity checks before attempting to parse JSON... // Usually Khoros XHR responses are a huge object that is then processed and // injected into the current page via some very convoluted logic, most of the data // is irrelevant for us and is contained within a response's `components` property let data = xhr.responseText.trim().startsWith('{') ? $core.obj.skip(JSON.parse(xhr.responseText)?.response, ['components']) : xhr.responseText; if ( typeof data !== 'string' ) { const { only, type } = (Object.entries(synk).find(([urlpartial, obj]) => xhr.responseURL.includes(urlpartial))[1] || {}); data = { type: type(xhr) || 'undefined', event: { url: xhr.responseURL, timestamp: (new Date()).toISOString(), // the result of the event data: data, // the initiator of the event source: { payload: $core.obj.only(payload, (only || [])), user: $core.obj.skip(LITHIUM.CommunityJsonObject.User, ['settings', 'policies', 'emailRef']), node: LITHIUM.CommunityJsonObject.CoreNode, page: { ...($core.obj.only(LITHIUM.CommunityJsonObject.Page, ['object'])?.object || {}), }, } } }; $trigger('synk', data); //console.log('Forwarded data:', data); } else { $core.config.devmode === 'xhr' && console.warn('XHRmiddlaware: Respsone was a string, skipping sync!'); } } catch(ex) { console.error(ex); } } }); // The above handles (old school) AJAX requests, but we also need to deal with actions/events // that occur the even old-schooler way through regular link clicks that reload the page. // To do that we attach a global link listener. I believe to catch Khoros related events we // can safely filter links by `data-lia-action-token` as all the relevant action links seem // to have such an attribute! // TODO: Make sure we do not somehow also track links that are handled by AJAX requests and // thus would be double-synk'ed... document.querySelectorAll('a[data-lia-action-token]').forEach((el) => { el.addEventListener('click', (e) => { if ( e.target.getAttribute('href') ) { const url = new URL(e.target.getAttribute('href')); const params = Object.fromEntries([...url.searchParams]); $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Action link params', url, params, e.target.getAttribute('href')); } else { $core.config.devmode === 'xhr' && console.warn('XHRmiddlaware: Action link did not have a href attribute?', e.target, e); } if ( $core.config.user.roles.includes('GlowingBlue') ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Preventing action from glowingblue user'); //e.preventDefault(); } // We would then build a synk object and trigger a 'synk' event through the global // event bus... // TODO: We also need to think about what happens if an action fails, maybe storing // potential sync objects in localStorage with an attribute of `verified: false` // would be a good idea, for AJAX requests we can more or less reliably track // if an action was successful, as the returned objects contain a property `state` // (NOT `status`, that one is always 'success'!) that will indicate any errors... }); }); // There are also forms that are not handled via AJAX! It seems this listener does not // conflict with the AJAX ones, as those are implemented by Khoros earlier on, so this // listener is never called for AJAX handled forms, which is good because then we don't // have to deal with duplicate synk events... document.querySelectorAll('input[name="lia-action-token"]').forEach((el) => { $(el).parents('form')[0].addEventListener('submit', (e) => { if ( $core.config.user.roles.includes('GlowingBlue') ) { $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Preventing form submission from glowingblue user'); //e.preventDefault(); $core.config.devmode === 'xhr' && console.log('XHRmiddlaware: Serialized form', $core.serializeForm(e.target)); } else { //return true; } }); }); $on('synk', (e, data) => { console.log('$onSynk', e, data); // TODO: make sure there is no issue with aborted requests due to page reloading // if it is, we might have to store the synk objects in local storage before sending // out the request and then when we get a successful response, delete them and on page // load check if we have any leftover items to synk and process those again... // TODO: Push requests through proxy (not sure if it already handles POST requests!) // So we do not leak any user IP information to a third party which Deno deploy is... fetch('https://synk.deno.dev', {method:'POST', body: JSON.stringify(data)}); }); // Simply trigger a custom xhr event and handle whatever logic in the event listener! XMLHttpRequest.$use((xhr, state, ...args) => { $trigger('xhr', state, xhr, ...args); }); })(LITHIUM.jQuery); // Pull in global jQuery reference // </script> // <script> just for inline syntax-highlighting... ;(function($){ /** /* Deals with any redirects that need to be handled in JavaScript for some reason, like 404 Pages /* as we don't get the request in page.init and therefore can't do a redirect form there. /* /* You only need to specify the project specific redirects by adding them to the global `$core` /* object. The actual redirect logic will be dealt with from `core.cmp.tools` via `tools.redirect()`, /* just make sure you don't change the property name as the tool method looks for `$core.redirects`. /* /* @usage: Just define key/value paris where key = url.pathname and value the url to redirect to /*/ $core.redirects = { //'/some/path/to/redirect': 'https://www.community.tld/some/url/to/target/redirect/to', }; /** /* @issue KBCOM-2655 /* /* Immediately makes the inline 'Reply' button disabled on click instead of waiting for TinyMCE /* to be initialized as OOB does. /*/ $('.lia-component-messages-widget-reply-inline-button .lia-button.lia-action-reply').each(function() { $(this).on('click', function(e) { $(this).attr('disabled', true) }); }); })(LITHIUM.jQuery); // Pull in global jQuery reference // </script> // <script> ;(function() { if ( $core ) { $core.config.file = 'cmp.global.search-external'; $core.properties = { ...($core.properties || {}), ...{"general.in@component:cmp.global.search-external" : "in","general.of@component:cmp.global.search-external" : "of","general.from-community@component:cmp.global.search-external" : "From the Community","title@component:cmp.global.search-external" : "Additional Resources","filter.title.resources@component:cmp.global.search-external" : "Included Resources","filter.title.languages@component:cmp.global.search-external" : "Languages","filter.knowledge@component:cmp.global.search-external" : "Knowledge Base","filter.academy@component:cmp.global.search-external" : "Academy","filter.cms@component:cmp.global.search-external" : "CMS Documentation","filter.api@component:cmp.global.search-external" : "API Documentation","filter.customer@component:cmp.global.search-external" : "Customer Blog","filter.language.en@component:cmp.global.search-external" : "en","filter.language.de@component:cmp.global.search-external" : "de","filter.language.es@component:cmp.global.search-external" : "es","filter.language.fr@component:cmp.global.search-external" : "fr","filter.language.pt@component:cmp.global.search-external" : "pt","filter.language.ja@component:cmp.global.search-external" : "ja","result@component:cmp.global.search-external" : "result","results@component:cmp.global.search-external" : "results","results.in@component:cmp.global.search-external" : "{0} {0,choice,0#${results}|1#${result}|1<${results}} ${general.in} {1}","results.none.title@component:cmp.global.search-external" : "No results for \"{0}\"","results.none.text@component:cmp.global.search-external" : "Try a different search term or use the filter on the left to search other resources.","paging.prev@component:cmp.global.search-external" : "Prev","paging.next@component:cmp.global.search-external" : "Next"}, }; } })(); // </script> // <script> just for inline syntax-highlighting... ;(function($){ const params = (new URL(window.location.href)).searchParams; // Allows testing production behavior on stage by switching env temporarily via URL param if ( $core.config.env.includes('stage') && params.get('test') ) { $core.config.env = 'prod'; console.warn('Production test mode enabled', $core.config); } // The most primitive in-memory cache you can imagine // it will hold HubSpot search API results as long as the page is not reloaded, this // speeds things up drastically when using typeahead (e.g. reacting to every key stroke) $core.cache = new Map(); const title = $core.str('title', 'Additional Resources'); /** /* Searchbar autosuggest integration. /* We can't really use riot here as we are hacking into the existing Khoros auto-suggest dropdown /* Furthermore the SearchForm is added twice to every page due to how the mobile header was done. /* It's entirely separate from the desktop header and therefore needs to be targetet properly when /* it's visible from 1024px down. /*/ const search_form = window.innerWidth <= 1024 ? '.mobile-header form.SearchForm' : '.community-header-nav .SearchForm'; $core.when(`${search_form} [name="messageSearchField"] + .lia-autocomplete-container .lia-autocomplete-content`, function(el) { $(`${search_form} [name="messageSearchField"]`).each(function() { var $input = $(this); var $results = $input.find('+ .lia-autocomplete-container .lia-autocomplete-content'); /** /* The autosuggest-dropdown closes on a click anywhere besides it's own results, /* not too crazy of an issue, but it annoys me and I can't fix it, don't know what /* triggers which event, tried to find out but without success... /*/ /*if ( window.innerWidth <= 1024 ) { $results .prepend(`<i class="collapse-results lia-autocomplete-no-event-item lia-fa fa-chevron-up p:x15 p:y11 pos:a pos:r0 pos:t0"></i>`) .find('.collapse-results') .on('click', function(e) { e.stopPropagation(); console.log($(this).siblings('ul:first'), $(this)); $(this).toggleClass('fa-chevron-up fa-chevron-down'); }); }*/ if ( !$results.find('ul.custom-external-results').length ) { $results.append(`<ul class="custom-external-results d:f(row/0/1/100%) h:max300 d:scroll(y) t:/12//400 d:b:before p:y8:before p:l15:before pos:r t:ucase:before" aria-label="${title}" data-before="${title}"></ul>`); } var $container = $results.find('ul.custom-external-results'); $input.on('input', async function(e) { // TODO: find out what is the URL param to limit `limit=` does not work... // => well, it seems the autocomplete endpoint of the HubSpot search API does not // support any kind of configuration at all. If it's needed, we might have to switch // to the slower 'full' API endpoint var url = `https://wtcfns.hubspot.com/wt-api/search/autocomplete?queryString=${$input.val()}&language=${$core.config.user.lang}&limit=5`; //console.log($input.val()); if ( !$core.cache.has(url) ) { $core.cache.set(url, (await (await $core.fetch(url)).json())); } //console.log($core.cache.get(url)); // HubSpot search API will return `{'message': 'Missing search key'}` when the `queryString` is empty // in that case, markup will be `undefined`, which we have to catch, otherwise it's going to be // an empty array if there are really no results for a search term. var markup = $core.cache.get(url).data?.searchResults?.results?.reduce((r, v) => { // As limit doesn't work on the autocomplete endpoint we have to limit the auto-suggest results like this //if ( r.length < 3) { r.push(` <li class="lia-autocomplete-node-item lia-autocomplete-custom-item"> <a class="lia-link-navigation board-icon" tabindex="-1" href="#"> <span class="custom-img-icon-help lia-fa-icon lia-fa-question lia-fa bg:--color-lorax t:--color-olaf" title="${v.resource}" aria-label="${v.resource}" role="img"></span> </a> <a class="lia-link-navigation lia-js-autocomplete-list-item-link lia-autocomplete-message-list-item-link" tabindex="-1" href="${v.url}" target="_blank"> ${v.title.replace('hs-search-highlight hs-highlight-title', 'lia-search-match-lithium')} </a> <div class="lia-autocomplete-suggestion-additional-details lia-component-nodes-widget-auto-complete-node-list-item"> <span class="lia-autocomplete-suggestion-board-title t:caps">${v.resource}</span> </div> </li> `); //} return r; }, []); if ( markup === []._ ) { return; } if ( markup.length ) { $container.html(markup.join('\n')); } else { $container.html(`<div class="pos:center t:center t:/14">${$core.str('results.none.title', null, $input.val())}</div>`); } }); }); }, document.querySelector(`${search_form}`)); /** /* Global SearchPage integration of external search. /* Adds a new tab to the search sections area and additionally a filter-like fake dropdown /* That additional filter was removed again via KBCOM-2830! /* that triggers a click on the new tab, it's just for more visual exposure as we worry the /* new tab might be too unassuming and might be overlooked. /*/ if ( $core.config.node.quilt.includes('SearchPage') ) { let results = null; const getResults = async function(query, resources = ['knowledge', 'academy', /*'customer', 'api', 'cms'*/], language = ($core?.config?.user?.lang || 'en'), page = 1, limit = 10, offset = 0, padding = 2) { offset = (page-1) * limit; // HubSpot search API resources are targeted with `contentKey: api, cms, knowledge, academy, customer` const url = `https://wtcfns.hubspot.com/wt-api/search?queryString=${query}&limit=${limit}&offset=${offset}&page=${page}&language=${language}&contentKey=${resources.join(',')}`; //console.log(url); if ( !$core.cache.has(url) ) { $core.cache.set(url, (await (await $core.fetch(url, null, 'appcache')).json())); } // once received, the HubSpot search API results are agumented with custom stuff // that helps rendering things like pagination etc. let results = Object.entries(($core.cache.get(url)?.data?.searchResults || {})).reduce((r, [k, v]) => ({ ...r, [k]: v }), { active: resources, lang: language, query: query, url: url }); // calculate the total amount of pages first and set it to minimum 1 const pages = Array.from({length: Math.max(Math.ceil(results.total/limit), 1)}, (el, i) => i+1); // calculate collection object const collection = { limit: limit, offset: offset, total: results.total, paging: { page: page, pages: pages.length, // TODO: there are still some issues with this, it does work for page // 1, but for the last page, only 3 (instead of 5) pages are returned display: pages.length ? (() => { const num = (padding * 2) + 1; const i = pages.indexOf(page); const from = Math.max(i - Math.floor(num / 2), 0); const to = Math.min(from + num - 1, pages.length - 1); return pages.slice(from, to + 1); })() : [], // number of page-links left and right of current page padding: padding, // rendering helpers is_first: page === 1, is_last: page == pages.length } }; results = { ...results, collection: collection }; //console.log('response', res); //console.log('results', results); // Trigger a custom jQuery event globally that we can hook into from any other code $(document).trigger('results', results); // We can trigger the custom global riot event-bus from outside of components as it // is attached to the global riot object! if ( $trigger ) { $trigger('results', results); } return results; }; const injectSearchExternal = function() { // Inject our custom tab const $tab = $('.lia-search-tab-bar .lia-component-search-tabs .lia-tabs-standard').append(` <li role="presentation" class="search-external-tab lia-tabs lia-tabs-inactive is--custom"> <span><a class="search-external-link lia-link-navigation tab-link" role="tab" aria-selected="false" tabindex="0" href="${window.location.href}">${title}</a></span> </li> `).find('.search-external-tab'); // Removed via KBCOM-2830 /* const $filter = $(` <div class="lia-form-fieldset-wrapper lia-component-search-widget-external is--custom"> <a href="${window.location.href}" class="lia-common-dropdown-toggle" role="button">${title}</a> </div> `).insertBefore('.lia-component-quilt-search-page-thread-filters .lia-component-search-widget-location-filter'); */ // Handle clicks on our new tab: // The idea is to basically wipe the existing content from the page and mount the // custom search component instead. Any click on the regular tabs will behave like always and // trigger a page reload which will show the original content again $tab.find('.search-external-link').on('click', function(e) { e.preventDefault(); // Handle active state of tabs $(this).parents('.search-external-tab').addClass('lia-tabs-active').siblings('.lia-tabs-active').removeClass('lia-tabs-active'); // Then we clean out some of the content of the current page and make it look like a tab switch $('.lia-search-tab-bar .lia-component-search-widget-advanced-search-toggle, .lia-search-tab-bar .lia-component-search-actions, .lia-search-results .search-result-sorting').remove(); $('.lia-search-results .search-result-count').remove(); // Once cleaned up we inject the base tag and mount our custom component $('.lia-quilt-column-main-content .lia-quilt-row-main').empty().append('<div is="search-external" class="p:x15" data-cmp="cmp.global.search-external"></div>'); riot.mount('[is]:not([is] [is])', { title: title, results: results, params: params, getResults: getResults }); }); // The fake injected filter simply triggers the tab, it's meant to provide greater exposure (visually) // $filter.on('click', function(e) { // e.preventDefault(); // $tab.find('.search-external-link').trigger('click'); // }); }; // Pre-fetch results, why wait as we already know the query here, this also allows to inject a variety of // dynamic information into the native search results content and agument it with external results data // this also deals with the fact that the search page is an Angular component that reloads dynamically // when doing certain things... (not tab switching though) $core.when('.lia-message-search-container', async (el) => { if ( !document.querySelector('.lia-tabs.external-tab') ) { //console.log('Injecting external search!'); injectSearchExternal(); results = await getResults(params.get('q')); } }); // Attach custom event listener here to deal with non-component DOM updates as I don't want to handle those // within the custom component but also be updated if something changes there... $(document).on('results', function(e, results) { //console.log('onResults', e, results); if ( $('.lia-search-results .search-result-count').text().trim().length ) { $('.lia-search-results .search-result-count').attr('data-after', $core.str('general.from-community')); } // Update the tab tag $('.search-external-link').attr('data-after', (results?.total || 0)); }); } // Re-set production test mode if ( $core.config.env.match('stage') && params.get('test') ) { $core.config.env = 'stage'; } })(LITHIUM.jQuery); // Pull in global jQuery reference // </script> (function($) { //START END-USER CONFIGURATION //------------------------------ //selectors for hover card triggers var allHoverCardTriggers = '.author-name-link,.friend-list .friend a,.username a,.avatar,.user-avatar,.author-img, .authors a, .messageauthorusername a, a.lia-user-name-link, .js-latest-post-by-from a, .user-online-list li a, a.UserAvatar, .customUsersOnline a, #authors a,.dashboard-followers a.user-name, .dashboard-following a.user-name,.author-login-wrapper a, .hb-leaderboard a, .author-img-floated'; // Forward calling page's URL params to endpoint URL as well, helps with testing! var params = (new URL(location.href)).searchParams; var userApiUrl = '/plugins/custom/hubspot/hubspot/hovercardendpoint?' + ((params.set('user_id', '') == []._) && params.toString()); if($('.hover-card-container').length<1){ $('body').append('<div class="hover-card-container"></div>'); } var cardWrapper = $('.hover-card-container'); var error = false; var thisUserID = ''; var thisUserLogin = ''; var userLink =''; var cardTimer; var leaveTimer; function mouseenter(Elem) { var thisEl = Elem; cardTimer = setTimeout(function(){ var docWidth = $(document).width(); var rightSide = false; var userLink = thisEl.attr('href'); if($('.ViewProfilePage').length && $('img.lia-user-avatar-profile',thisEl).length){thisUserID = '169781';} else if(thisEl.attr('href')=='#' || thisEl.attr('href')=='' || !userLink.match('viewprofilepage')){ return false;} else{ var thisLen = (userLink).split('/'); thisUserID = (thisLen)[thisLen.length-1]; } var thisCard = $('.profileCard[data-user='+thisUserID+']',cardWrapper); var cardId = 'userProfileCard-'+ thisUserID; var addAttr = thisEl.attr('aria-describedby',cardId); var thisElTopOffset = Math.round(thisEl.offset().top+(thisEl.height()/2)+30); var thisElbottomoffset = "auto"; var className = ""; var winHeight = $(window).height(); var elOffset = thisEl.offset(); var scrollTop = $(window).scrollTop(); var elementOffset = thisEl.offset().top; var distanceTop = (elementOffset - scrollTop); var distanceBottom = (winHeight + scrollTop) - (elOffset.top + thisEl.outerHeight(true)); var distanceLeft = Math.round(thisEl.offset().left); var bodyHight = $('body').height(); var topParam = ''; var bottomparam = ''; var position = ''; var className = 'topArrow'; cardId if(distanceBottom < 300 ){ if(distanceLeft < 59){ thisCard.removeClass('bottomArrow'); var className = 'leftArrow'; var distanceLeft = (distanceLeft)+(39); var thisElTopOffset = (thisElTopOffset)-(150); }else{ var thisElTopOffset = (thisElTopOffset)-(301); var className = 'bottomArrow'; thisCard.removeClass('topArrow'); thisCard.removeClass('leftArrow'); var distanceLeft = (distanceLeft)-(45); } } else{ if(distanceLeft < 59){ thisCard.removeClass('topArrow'); var className = 'leftArrow'; var distanceLeft = (distanceLeft)+(39); var thisElTopOffset = (thisElTopOffset)-(150); }else{ thisCard.removeClass('leftArrow'); thisCard.removeClass('bottomArrow').addClass('topArrow'); var distanceLeft = (distanceLeft)-(45); } } if(thisCard.length && $('.profileCard[data-user='+thisUserID+'] .preloader',cardWrapper).length<1){ $('.profileCard',cardWrapper).hide(); thisCard.addClass(className); rightSide?thisCard.addClass('rightArrow'):thisCard.removeClass('rightArrow'); thisCard.delay(0).css({'top':(thisElTopOffset),'left':distanceLeft,'bottom':thisElbottomoffset}).fadeIn(); } else { var ajaxReturn = ''; //just in case thisCard.remove(); //hover card wrapper markup var rightArrowClass = rightSide?'rightArrow':''; if(thisElTopOffset != "auto"){ topParam = 'px'; } if(thisElbottomoffset != "auto"){ bottomparam = 'px'; } var profileCardHtml = '<div id="'+cardId+'" role="tooltip" class="AllCard profileCard '+rightArrowClass+' '+className+'"style="display:block;top:'+thisElTopOffset+topParam+';left:'+distanceLeft+'px;bottom:'+thisElbottomoffset+bottomparam+';" data-user="'+thisUserID+'"></div>'; $.when( //get the background $.ajax({ type: 'GET', url: userApiUrl+thisUserID, dataType: 'html', success: function(data) { $('.profileCard',cardWrapper).hide(); ajaxReturn = data; } }) ) .done(function(){ cardWrapper.append(profileCardHtml); $('.profileCard[data-user='+thisUserID+']',cardWrapper).eq(0).empty().html(ajaxReturn); if($('.profileCard[data-user='+thisUserID+'] .preloader',cardWrapper).length){ $('.profileCard[data-user='+thisUserID+'] .preloader',cardWrapper).parents('div.profileCard').remove(); } }) .fail(function(){ //uh oh - bail out! $('.profileCard',cardWrapper).hide(); }); } }, 360); } function mouseleave(e) { clearTimeout(cardTimer); // glowingblue: When the user leaves the hovercard trigger, wait because the leaving could be // to interact with the hovercard, if we don't wait it will just disappear...because // we left the trigger, right...so we'll have another handler that check if the mouse is // over the hovercard and if so clears this timer, so the card doesn't close here leaveTimer = setTimeout(function() { if ($('.profileCard[data-user="'+thisUserID+'"]',cardWrapper).length) { $('.profileCard[data-user="'+thisUserID+'"]',cardWrapper).fadeOut('fast'); } else { $(".profileCard").fadeOut('fast'); } }, 2400); } $(document).on("mouseenter focusin", allHoverCardTriggers, function(event) { if(!($(this).parents().hasClass('custom-header'))&& !($(this).parents().hasClass('green-wrap'))){ (leaveTimer !== []._) && clearTimeout(leaveTimer); mouseenter($(this)); event.stopPropagation(); } }); $(document).on("mouseleave focusout", allHoverCardTriggers, function(event) { (leaveTimer !== []._) && clearTimeout(leaveTimer); mouseleave(event); event.stopPropagation(); }); // glowingblue: Add handlers for when the users interacts with the hovercard, no closing! $('.hover-card-container').on('mouseenter', function(e) { (leaveTimer !== []._) && clearTimeout(leaveTimer); }); $('.hover-card-container').on('mouseleave', function(e) { (leaveTimer !== []._) && clearTimeout(leaveTimer); if ( $(e.target).is('.profileCard[style*="block"]') ) { leaveTimer = setTimeout(function() { $(e.target).fadeOut('fast'); }, 2400); } }); // glowingblue: add one global root level click handler to also close any visible hovercards // if the user taps/clicks outside the hovercard $(document).on('mousedown', function(e) { if ( !$(e.target).parents('.hover-card-container').length ) { (leaveTimer != []._) && clearTimeout(leaveTimer); $('.hover-card-container .profileCard[style*="block"]').each(function() { $(this).fadeOut('fast'); }); } }); })(LITHIUM.jQuery); (function($) { $(document).ready(function(){ var originalElementId; function setCookie(cname,cvalue,exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays*24*60*60*1000)); var expires = "expires=" + d.toGMTString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } function getCookie(cname) { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } function errorDisplay(errType) { var errAuth = '<div class="InfoMessage lia-panel-feedback-inline-alert not-exists">To access this area of the Community, please <a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F169781%3Frefresh%3Dtrue">log in</a>. </div>'; var errAuthLogin = '<div class="InfoMessage lia-panel-feedback-inline-alert not-exists">To access the HubSpot Community Champions program and complete opportunities, confirm you are logged in by <a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F169781%3Frefresh%3Dtrue">clicking here</a>. Find additional troubleshooting steps <a href="/t5/Getting-Started-on-the-Community/Accessing-HubFans-Program-and-Challenges/ba-p/632594">here</a> </div>'; var noBadges = '<div class="InfoMessage lia-panel-feedback-inline-alert not-exists">To start earning advocacy badges, go to the Community Champions advocacy program page and start completing opportunities.</div>'; var noAsks = '<div class="InfoMessage lia-panel-feedback-inline-alert not-exists">Hooray! You\'ve completed all available opportunities. Check back in for more opportunities next month.</div>'; if(errType=='errAuth'){ $('.lia-quilt-category-page-hubstars .lia-quilt-column.lia-quilt-column-06.lia-quilt-column-right.lia-quilt-column-side-content').hide(); $('.asks-filters-wrap').html(errAuth); $('.advocacy-badgeslist').html(errAuth); $('.ViewProfilePage .rank-badge-border-box .lia-user-badge-list').html(errAuth); } else if(errType=='errAuthLogin') { $('.lia-quilt-category-page-hubstars .lia-quilt-column.lia-quilt-column-06.lia-quilt-column-right.lia-quilt-column-side-content').hide(); $('.asks-filters-wrap').html(errAuthLogin); $('.advocacy-badgeslist').html(errAuthLogin); $('.ViewProfilePage .rank-badge-border-box .lia-user-badge-list').html(errAuthLogin); } else if(errType=='noAsks'){ $('.asks-lists').addClass('no-asks').html(noAsks); }else if(errType=='noBadges'){ $('.advocacy-badgeslist').html(noBadges); $('.ViewProfilePage .rank-badge-border-box .lia-user-badge-list').html(noBadges); } } function parseJwt (token) { var base64Url = token.split('.')[1]; var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); return JSON.parse(jsonPayload); }; var pagename = 'ViewProfilePage'; if(pagename == 'AdvocacyBadges'){ $('#lia-body').addClass('UserBadgesPage'); } setCookie("advocacyToken", "", 0); errorDisplay('errAuth'); }); })(LITHIUM.jQuery); (function($) { <!-- Expire all cookies --> document.cookie = "advocacyToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; document.cookie = "Crowdvocate_jwt_token=; domain=.hubspot.com; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; document.cookie = "Crowdvocate_user_ck=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; })(LITHIUM.jQuery); (function($) { document.addEventListener('gdpr.allow', function() { if (document.querySelector('.lia-cookie-banner-alert-accept a')) { document.querySelector('.lia-cookie-banner-alert-accept a').click(); } }); })(LITHIUM.jQuery); LITHIUM.Link({"linkSelector":"a.lia-link-ticket-post-action"}); ;(function($){ var langMap = { 'en':'hubspot_community_en', 'es':'hubspot_community_es', 'fr':'hubspot_community_fr', 'ja':'hubspot_community_jp', 'pt-br':'hubspot_community_pt', 'de':'hubspot_community_de' } var nodeType = "community"; var langScope = langMap['en']; var isSearchPage = jQuery('body').hasClass('SearchPage'); var isIdeasLandingPage = jQuery('body').hasClass('ideaslandingpage'); if (nodeType === "community" && !isSearchPage && !isIdeasLandingPage) { var inputFormFilter = '<input name="filter" value="location" type="hidden">'; var inputFormLocation = '<input name="location" value="category:' + langScope + '" type="hidden">'; $('form.SearchForm').append(inputFormFilter).append(inputFormLocation); } else if (nodeType === "community" && isIdeasLandingPage) { var searchUrl = "/t5/forums/searchpage/tab/message?filter=location&location=idea-board:HubSpot_Ideas&collapse_discussion=true"; var query = jQuery('.SearchForm .lia-search-input-message').val(); jQuery(document).on('submit', 'form.SearchForm', function(e) { e.preventDefault(); var newQ = "&q=" + document.querySelector('.SearchForm .lia-search-input-wrapper input.search-input').value; window.location = window.location.origin + searchUrl + newQ; }) } })(LITHIUM.jQuery) LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3944c942","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3944c942_0","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3944c942_1","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3944c942_2","feedbackSelector":".InfoMessage"}); LITHIUM.AjaxFeedback(".lia-inline-ajax-feedback", "LITHIUM:hideAjaxFeedback", ".lia-inline-ajax-feedback-persist"); LITHIUM.Placeholder(); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.defaultAjaxFeedbackHtml = \"<div class=\\\"lia-inline-ajax-feedback lia-component-common-widget-ajax-feedback\\\">\\n\\t\\t\\t<div class=\\\"AjaxFeedback\\\" id=\\\"ajaxFeedback_23f26f399071f0\\\"><\\/div>\\n\\t\\t\\t\\n\\t\\n\\n\\t\\n\\n\\t\\t<\\/div>\";LITHIUM.AjaxSupport.defaultAjaxErrorHtml = \"<span id=\\\"feedback-errorfeedback_23f26f39c3b969\\\"> <\\/span>\\n\\n\\t\\n\\t\\t<div class=\\\"InfoMessage lia-panel-feedback-inline-alert lia-component-common-widget-feedback\\\" id=\\\"feedback_23f26f39c3b969\\\">\\n\\t\\t\\t<div role=\\\"alert\\\" class=\\\"lia-text\\\">\\n\\t\\t\\t\\t\\n\\n\\t\\t\\t\\t\\n\\t\\t\\t\\t\\t<p ng-non-bindable=\\\"\\\" tabindex=\\\"0\\\">\\n\\t\\t\\t\\t\\t\\tSorry, unable to complete the action you requested.\\n\\t\\t\\t\\t\\t<\\/p>\\n\\t\\t\\t\\t\\n\\n\\t\\t\\t\\t\\n\\n\\t\\t\\t\\t\\n\\n\\t\\t\\t\\t\\n\\t\\t\\t<\\/div>\\n\\n\\t\\t\\t\\n\\t\\t<\\/div>\";LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3981cb13', 'disableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'ckAEva7m4i5rL-tGHoQtuHD_-7OBRyeLLUjv-RslW4Q.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"WLO3_2mqcdWUXCCieEIVeARtnSEuLhHr9zBsufXz6lI.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3981cb13\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23f26f3944c942_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.messagesearchfield.messagesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3a3f5e80', 'disableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'E59cocp_2Iwp-Q_uz1QitMGNeKQ_Nx9zkie2OHD4eKI.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"OrCvw358yIkrT19inLc7pQDgmkKKQ5Yshg9RO8Io_iM.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3a3f5e80\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23f26f3944c942_1","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.tkbmessagesearchfield.messagesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching for users...","emptyText":"No Matches","successText":"Users found:","defaultText":"Enter a user name or rank","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3a7ab3fd', 'disableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'B38gZcs5Ra8u9hjSoO0Ov7Wh0cBSQ2mTWFwV4qXl9GA.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"LZIIAiDTh-uGGRrTzrqnVRG459DVD_xsLBJRea44hC4.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3a7ab3fd\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#userSearchField_23f26f3944c942","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.usersearchfield.usersearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport({"ajaxOptionsParam":{"event":"LITHIUM:userExistsQuery","parameters":{"javascript.ignore_combine_and_minify":"true"}},"tokenId":"ajax","elementSelector":"#userSearchField_23f26f3944c942","action":"userExistsQuery","feedbackSelector":"#ajaxfeedback_23f26f3944c942_0","url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.usersearchfield:userexistsquery?t:ac=user-id/169781&t:cp=search/contributions/page","ajaxErrorEventName":"LITHIUM:ajaxError","token":"3Ekt_4mtxVqzd8BAWuDbnV1erSlQH0iPGRFDg5ohd2k."}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3ab3a502', 'disableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'MCEDIxGB5GEvecLbkK30L-tktiglZDtQIeoDxXCCs-M.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"hAW0E_HiqgORyHIiJUOr0YTZGNAgtDBC-ecWUnoj5gU.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3ab3a502\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#noteSearchField_23f26f3944c942_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.notesearchfield.notesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3b00f6a8', 'disableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'y4RFWqIi01f9mGd4H-vFL-Ral6ribfCg272obSct5Ac.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"CSaiVIzfj6ENaHv-dWeRMpi7Gtip4LhwnmyVL-DiKh8.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3b00f6a8\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#productSearchField_23f26f3944c942","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.productsearchfield.productsearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport.fromLink('#enableAutoComplete_23f26f3944c942', 'enableAutoComplete', '#ajaxfeedback_23f26f3944c942_0', 'LITHIUM:ajaxError', {}, 'pIW_1hHDoIbtcHD7oFpzdT5flXPjZjTWg1kaGPp4vb0.', 'ajax'); LITHIUM.Tooltip({"bodySelector":"body#lia-body","delay":30,"enableOnClickForTrigger":false,"predelay":10,"triggerSelector":"#link_23f26f3944c942","tooltipContentSelector":"#link_23f26f3944c942_0-tooltip-element .content","position":["bottom","left"],"tooltipElementSelector":"#link_23f26f3944c942_0-tooltip-element","events":{"def":"focus mouseover keydown,blur mouseout keydown"},"hideOnLeave":true}); LITHIUM.HelpIcon({"selectors":{"helpIconSelector":".help-icon .lia-img-icon-help"}}); LITHIUM.SearchAutoCompleteToggle({"containerSelector":"#searchautocompletetoggle_23f26f3944c942","enableAutoCompleteSelector":".search-autocomplete-toggle-link","enableAutocompleteSuccessEvent":"LITHIUM:ajaxSuccess:enableAutoComplete","disableAutoCompleteSelector":".lia-autocomplete-toggle-off","disableAutocompleteSuccessEvent":"LITHIUM:ajaxSuccess:disableAutoComplete","autoCompleteSelector":".lia-autocomplete-input"}); LITHIUM.SearchForm({"asSearchActionIdSelector":".lia-as-search-action-id","useAutoComplete":true,"selectSelector":".lia-search-form-granularity","useClearSearchButton":false,"buttonSelector":".lia-button-searchForm-action","asSearchActionIdParamName":"as-search-action-id","formSelector":"#lia-searchformV32_23f26f3944c942","asSearchActionIdHeaderKey":"X-LI-AS-Search-Action-Id","inputSelector":"#messageSearchField_23f26f3944c942_0:not(.lia-js-hidden)","clearSearchButtonSelector":null}); LITHIUM.Form.resetFieldForFocusFound(); (function($) { document.querySelector('a.login-link').classList.add('homepage-nav-login'); })(LITHIUM.jQuery); ;(function($){ $(document).ready(function() { $(".custom-user-menu-v2 .nav-link").click(function(e) { e.preventDefault(); $(".nav-popover.profile").toggleClass('show'); }); $(".search-toggle-action-icon-plus").on("click",function(e){ e.preventDefault(); $(this).parent().find(".plus-bar-main-content").toggle(); }); //User Avatar $('.header-tab-nav li span').click(function() { $('.header-tab-nav li span').removeClass("active"); if(this.id == 'profile'){ $('span#profile').addClass("active"); $('.header-tab-nav-content > div#profile-list-wrapper').show(); $('.header-tab-nav-content > div#admin-list-wrapper').hide(); $('.header-tab-nav-content > div#profile-list-wrapper').removeClass('profile-menu-dropdown'); } if(this.id == 'admin'){ $('span#admin').addClass("active"); $('.header-tab-nav-content > div#profile-list-wrapper').hide(); $('.header-tab-nav-content > div#admin-list-wrapper').show(); $('.header-tab-nav-content > div#profile-list-wrapper').addClass('profile-menu-dropdown'); } var indexer = $(this).index(); //gets the current index of (this) which is #header-tab-nav li $('.header-tab-nav-content > div:eq(' + indexer + ')').fadeIn(); //uses whatever index the link has to open the corresponding box }); $(this).mouseup(function (e){ var customButton = $('.nav-popover.profile'); if(!$('.custom-menu-caret').is(e.target) && $('.custom-menu-caret').has(e.target).length === 0){ if(!customButton.is(e.target) && customButton.has(e.target).length === 0){ if (!$('.custom-user-menu-v2 > .nav-link').is(e.target) && $('.custom-user-menu-v2 > .nav-link').has(e.target).length === 0) { customButton.removeClass('show'); } } } var menuWrapper = $('.menu-wrapper'); if(!menuWrapper.is(e.target) && menuWrapper.has(e.target).length === 0){ if (!$('.menu').is(e.target) && $('.menu').has(e.target).length === 0) { menuWrapper.removeClass('offcanvas'); } } var container = $(".plus-bar-main-content"); var customButton = $(".search-toggle-action-icon-plus"); if (!customButton.is(e.target) && customButton.has(e.target).length === 0) { container.hide(); } if(!$('.lang-picker-wrapper').is(e.target) && $('.lang-picker-wrapper').has(e.target).length === 0){ if (!$('.lang-picker').is(e.target) && $('.lang-picker').has(e.target).length === 0) { $('.lang-picker').removeClass('show'); } } }); //SCROLL JS $(window).scroll(function(e) { e.preventDefault(); if($('.nav-popover.profile').hasClass("show")){ if ($(this).scrollTop() > 0) { $('.nav-popover.profile').removeClass("show"); } else { $('.nav-popover.profile').addClass("show"); } } if($('.nav-popover.get-hubspot').hasClass("show")){ if ($(this).scrollTop() > 0) { $('.nav-popover.get-hubspot').removeClass("show"); } else { $('.nav-popover.get-hubspot').addClass("show"); } } if ($(this).scrollTop() > 0) { $('.search-input.lia-search-input-message').blur(); $('.plus-bar-main-content').hide(); } }); }); jQuery('.lang-picker-wrapper').click(function(){ jQuery(".lang-picker").toggleClass('show'); }); jQuery('.lia-cat-sub-editor-modal .lia-ui-modal-footer .lia-button-Submit-action').live('click',function(){ setTimeout( function() { location.reload(true); },1000); }); })(LITHIUM.jQuery); ;(function($){ var langMap = { 'en':'hubspot_community_en', 'es':'hubspot_community_es', 'fr':'hubspot_community_fr', 'ja':'hubspot_community_jp', 'pt-br':'hubspot_community_pt', 'de':'hubspot_community_de' } var nodeType = "community"; var langScope = langMap['en']; var isSearchPage = jQuery('body').hasClass('SearchPage'); var isIdeasLandingPage = jQuery('body').hasClass('ideaslandingpage'); if (nodeType === "community" && !isSearchPage && !isIdeasLandingPage) { var inputFormFilter = '<input name="filter" value="location" type="hidden">'; var inputFormLocation = '<input name="location" value="category:' + langScope + '" type="hidden">'; $('form.SearchForm').append(inputFormFilter).append(inputFormLocation); } else if (nodeType === "community" && isIdeasLandingPage) { var searchUrl = "/t5/forums/searchpage/tab/message?filter=location&location=idea-board:HubSpot_Ideas&collapse_discussion=true"; var query = jQuery('.SearchForm .lia-search-input-message').val(); jQuery(document).on('submit', 'form.SearchForm', function(e) { e.preventDefault(); var newQ = "&q=" + document.querySelector('.SearchForm .lia-search-input-wrapper input.search-input').value; window.location = window.location.origin + searchUrl + newQ; }) } })(LITHIUM.jQuery) LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3b976736","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3b976736_0","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3b976736_1","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23f26f3b976736_2","feedbackSelector":".InfoMessage"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3bc44ff4', 'disableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, 'f9Idu9XCzkvnyNDkRdYHrmxxsFyRK3sZGi_PCweNYqY.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"Hvw2LcCPtybK2T5ufecrhnADViGFrkUso61sWy-klok.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3bc44ff4\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23f26f3b976736_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.messagesearchfield.messagesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3bfdcdea', 'disableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, 'pu9AVpTp4hantmZ2j2wvopxU0mnYSv8ljA-kAlcY1H4.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"76eas2uYxZ576ohmi-bnEYMGApMLXKkSGSd0vtWoQQY.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3bfdcdea\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23f26f3b976736_1","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.tkbmessagesearchfield.messagesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching for users...","emptyText":"No Matches","successText":"Users found:","defaultText":"Enter a user name or rank","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3c269b8d', 'disableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, '0rdPTVQP7R_Jw3isDG2Kk_6S944NyMYwF8TJKMZIBuc.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"N96CHK5vLX89NR4k2PgUPP8rM2dAvPzgtx8wv7JDprw.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3c269b8d\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#userSearchField_23f26f3b976736","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.usersearchfield.usersearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport({"ajaxOptionsParam":{"event":"LITHIUM:userExistsQuery","parameters":{"javascript.ignore_combine_and_minify":"true"}},"tokenId":"ajax","elementSelector":"#userSearchField_23f26f3b976736","action":"userExistsQuery","feedbackSelector":"#ajaxfeedback_23f26f3b976736_0","url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.usersearchfield:userexistsquery?t:ac=user-id/169781&t:cp=search/contributions/page","ajaxErrorEventName":"LITHIUM:ajaxError","token":"Oiw3I5NxrVlMVHMgTgCyxazgSYJ5qHJ6OSD-Z6A5z8E."}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3c50b6c0', 'disableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, 'VwSCrUuJIjiHBWLSJr8A6x2nKdGKqqT7oKPOBY8Xh_c.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"HEost6NdSiZyObjJ8vq1vrw0rBjQk41jHB5YRtHhn6g.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3c50b6c0\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#noteSearchField_23f26f3b976736_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.notesearchfield.notesearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('<form>',{method:'POST',action:$link.attr('href'),enctype:'multipart/form-data'});var $ticket=$('<input>',{type:'hidden',name:'lia-action-token',value:token});$form.append($ticket);$(document.body).append($form);$form.submit();$doc.trigger('click');}}}\nif($doc.data('lia-link-action-handler')===undefined){$doc.data('lia-link-action-handler',true);$doc.on('click.link-action',params.linkSelector,handler);$.fn.on=$.wrap($.fn.on,function(proceed){var ret=proceed.apply(this,$.makeArray(arguments).slice(1));if(this.is(document)){$doc.off('click.link-action',params.linkSelector,handler);proceed.call(this,'click.link-action',params.linkSelector,handler);}\nreturn ret;});}}})(LITHIUM.jQuery);\r\n\nLITHIUM.Link({\n \"linkSelector\" : \"a.lia-link-ticket-post-action\"\n});LITHIUM.AjaxSupport.fromLink('#disableAutoComplete_23f26f3c79ac5a', 'disableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, '3OmWB4kF0poXMiL3_Fal0ahmXihRi2lpgB74l1yHSdY.', 'ajax');","content":"<a class=\"lia-link-navigation lia-autocomplete-toggle-off lia-link-ticket-post-action lia-component-search-action-disable-auto-complete\" data-lia-action-token=\"ssWKct6Yp1pSay4sMOUAxODU51YorVPVRtPmPZ0HnUg.\" rel=\"nofollow\" id=\"disableAutoComplete_23f26f3c79ac5a\" href=\"https://community.hubspot.com/t5/user/v2/viewprofilepage.disableautocomplete:disableautocomplete?t:ac=user-id/169781&amp;t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#productSearchField_23f26f3b976736","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.searchformv32.productsearchfield.productsearchfield:autocomplete?t:ac=user-id/169781&t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport.fromLink('#enableAutoComplete_23f26f3b976736', 'enableAutoComplete', '#ajaxfeedback_23f26f3b976736_0', 'LITHIUM:ajaxError', {}, '_zZZwZI2GI6QPXg_CHQvI647au30L3f62wc5agzFcmI.', 'ajax'); LITHIUM.Tooltip({"bodySelector":"body#lia-body","delay":30,"enableOnClickForTrigger":false,"predelay":10,"triggerSelector":"#link_23f26f3b976736","tooltipContentSelector":"#link_23f26f3b976736_0-tooltip-element .content","position":["bottom","left"],"tooltipElementSelector":"#link_23f26f3b976736_0-tooltip-element","events":{"def":"focus mouseover keydown,blur mouseout keydown"},"hideOnLeave":true}); LITHIUM.HelpIcon({"selectors":{"helpIconSelector":".help-icon .lia-img-icon-help"}}); LITHIUM.SearchAutoCompleteToggle({"containerSelector":"#searchautocompletetoggle_23f26f3b976736","enableAutoCompleteSelector":".search-autocomplete-toggle-link","enableAutocompleteSuccessEvent":"LITHIUM:ajaxSuccess:enableAutoComplete","disableAutoCompleteSelector":".lia-autocomplete-toggle-off","disableAutocompleteSuccessEvent":"LITHIUM:ajaxSuccess:disableAutoComplete","autoCompleteSelector":".lia-autocomplete-input"}); LITHIUM.SearchForm({"asSearchActionIdSelector":".lia-as-search-action-id","useAutoComplete":true,"selectSelector":".lia-search-form-granularity","useClearSearchButton":false,"buttonSelector":".lia-button-searchForm-action","asSearchActionIdParamName":"as-search-action-id","formSelector":"#lia-searchformV32_23f26f3b976736","nodesModel":{"169781|authorMessages":{"title":"Search posts by kennedyp","inputSelector":".lia-search-input-message"},"user|user":{"title":"Users","inputSelector":".lia-search-input-user"},"mjmao93648|community":{"title":"Search Community: HubSpot Community","inputSelector":".lia-search-input-message"}},"asSearchActionIdHeaderKey":"X-LI-AS-Search-Action-Id","inputSelector":"#messageSearchField_23f26f3b976736_0:not(.lia-js-hidden)","clearSearchButtonSelector":null}); (function($) { document.querySelector('a.login-link').classList.add('homepage-nav-login'); })(LITHIUM.jQuery); (function($) { if ( $('.lia-notification-feed-page-link').length ) { $('.lia-notification-feed-page-link').addClass('nav-notifs'); } if ( $('.private-notes-link').length ) { $('.private-notes-link').addClass('nav-mail'); } })(LITHIUM.jQuery); ;(function($){ $('.custom-search-focus').on('click', function() { $('.lia-search-input-message').focus(); }); })(LITHIUM.jQuery); // Pull in global jQuery reference if (document.querySelectorAll('.lia-component-admin-widget-moderation-manager')[0]) { document.querySelectorAll('.lia-component-admin-widget-moderation-manager')[0].href = "/t5/bizapps/page/tab/community%3Amoderation?filter=includeForums&sort_by=-topicPostDate&include_forums=true&collapse_discussion=true" } ;(function($) { $("#get-hubspot-free").click(function(){ $("#get-hubspot").toggleClass("show"); }); // Closes dropdown boxes when clicking outside of the box // click listener applied inline, function in script tag window.onclick = function(e) { if (e.target?.matches && !e.target?.matches('#get-hubspot-free')) { if (document.getElementById("get-hubspot")) { if (document.getElementById("get-hubspot").classList.contains('show')) { document.getElementById("get-hubspot").classList.remove('show'); } } } if (e.target?.matches && !e.target?.matches('#current-language')) { if (document.getElementById("lang-picker-global")) { if (document.getElementById("lang-picker-global").classList.contains('show')) { document.getElementById("lang-picker-global").classList.remove('show'); } } } }; $(window).scroll(function(){ if ($(this).scrollTop() > 65) { $('.forum-nav-bar').addClass('ch-sticky'); $('.community-header-nav').addClass('ch-space'); } else { $('.forum-nav-bar').removeClass('ch-sticky'); $('.community-header-nav').removeClass('ch-space');; } }); $('span.custom-menu-caret').on('click',function(){ $(this).siblings('.nav-popover.profile').toggleClass('show'); }); })(LITHIUM.jQuery); (function($) { $(document).ready(function(){ try{ var PostURl = '' if('community'==='board'){ var PostURl = '/t5/forums/postpage/board-id/mjmao93648' }else if('community'==='category') { var PostURl = '/t5/forums/postpage/category-id/mjmao93648'} var searchSelector = $('.custom-search-wrapper .lia-autocomplete-container .lia-autocomplete-footer'); var data = "<div class='search-post-buttons'><span>Can't find what you're looking for?</span> <a href='"+PostURl+"' class='btn btn-sm button-primary'><span>Ask A Question</span></a> </div>"; $(data).insertAfter(searchSelector[0]); }catch(err){ console.log(err); } }); })(LITHIUM.jQuery); (function($) { $(document).ready(function(){ $('.lia-quilt-idea-exchange-page-filtered-v2 .custom-v2-banner .search-input, .lia-quilt-idea-page-filtered .custom-v2-banner .search-input').attr('placeholder', 'Search for Ideas'); }); })(LITHIUM.jQuery); (function($){ var ssoId = 12916311; if(ssoId != undefined || ssoId != null){ $.ajax({ url: "/mjmao93648/plugins/custom/hubspot/hubspot/fetch-api-data", data:{ssoId:ssoId}, success: function(data){ var data = JSON.parse(data); var pageUserId = 169781; var userId = -1; var hubspotUsername = "kennedyp"; var username = data.username; var { searchParams } = new URL(location.href); if(data.hasOwnProperty('username') || searchParams.get('test') === 'connect'){ if ( !data.hasOwnProperty('username') ) { var username = 'testing'; } $('.profile-info-column.user-contact .cmp-profile-socials').prepend("<li><a href='https://connect.com/member/"+username+"' target='_blank' title='"+hubspotUsername+" Connect.com Profile'><svg class='i'><use xlink:href='#i:connectcom'></use></svg></a></li>"); } else{ if(pageUserId==userId){ $('.profile-info-column.user-contact .cmp-profile-socials').prepend("<li class='claim p:t2 pos:a pos:l0 tr:translateY(100%)'><a href='https://app.hubspot.com/signup-hubspot/connect?uuid=bfe1a14c-248d-4e2c-b184-02582d5acab0&step=landing_page' target='_blank'>Claim your Connect.com profile</a></li>"); } } } }); } })(LITHIUM.jQuery); LITHIUM.CustomEvent('.lia-custom-event', 'click'); LITHIUM.MessageListActual({"selectors":{"emptyCellClass":".lia-component-common-column-empty-cell","listRowSelector":".lia-list-row","triangleTopClass":".triangletop"},"misc":{"unsolvedStatus":"unsolved","solvedStatus":"solved","threadUnreadClass":"lia-list-row-thread-unread","escalatedStatus":"escalated","threadClass":"lia-list-row-thread","threadSolvedClass":"lia-list-row-thread-solved","readonlyStatus":"readonly","unreadStatus":"unread","threadEscalatedClass":"lia-list-row-thread-escalated","threadReadOnlyClass":"lia-list-row-thread-readonly"}}); ;(function($){ $( ".tab" ).click(function() { let selectedPostType = $(this).attr('id').toLowerCase(); $(this).siblings("button").removeClass("tab-active"); $(this).addClass("tab-active"); componentChange(selectedPostType) }); $('.profilepage_select').on('change', function() { let selectedPostType = $(this).val(); componentChange(selectedPostType); }); function componentChange(selectedPostType){ let selectedPostID = "#" + selectedPostType + "-content"; console.log(selectedPostID) /* Transition between forum feeds */ $(selectedPostID).removeClass("hidden"); $(selectedPostID).siblings("div").addClass("hidden"); if($(selectedPostID).text().length==0) { $("body#lia-body.ViewProfilePage .no-content").css("display", "block"); $("body#lia-body.ViewProfilePage .bookmark-empty-state").css("display", "none"); } else { $("body#lia-body.ViewProfilePage .no-content").css("display", "none"); $("body#lia-body.ViewProfilePage .bookmark-empty-state").css("display", "none"); } console.log(selectedPostID == "#bookmarks-content") if(selectedPostID == "#bookmarks-content" && $("#bookmarks-content").text().length == 0){ console.log("IF Bookmark") $("body#lia-body.ViewProfilePage .bookmark-empty-state").css("display", "block"); $("body#lia-body.ViewProfilePage .no-content").css("display", "none"); } } })(LITHIUM.jQuery); LITHIUM.UserBadges({"viewAllSelector":"lia-view-all","userBadgesComponentSelector":"lia-user-badge-list","badgeListSelector":"lia-badge-list","badgeDisplaySelector":"lia-user-badge-display"}); ;(function ($) { if ($(window).width() <= 991) { $(".community-footer .col:nth-child(3)").on('click',function(){ if ($(this).hasClass('active')) { $(this).removeClass('active'); $(this).children("ul").hide(); $(this).children("h5").removeClass("addedClass"); } else { $(".community-footer .col").removeClass('active'); $('.community-footer .col ul').hide(); $(this).addClass('active'); $(this).children("ul").show(); $('.community-footer .col').children("h5").removeClass("addedClass"); $(this).children("h5").addClass("addedClass"); } if ( $(".community-footer .col:nth-child(4) ul").hasClass('custom-footer-res')) { $(".community-footer .col:nth-child(4) ul").removeClass('custom-footer-res'); } else { $(".community-footer .col:nth-child(4) ul").addClass('custom-footer-res'); } }) $('.community-footer .col:nth-child(1)').on("click", function () { if ($(this).hasClass('active')) { $(this).removeClass('active'); $(this).children("ul").hide(); $(this).children("h5").removeClass("addedClass"); } else { $(".community-footer .col").removeClass('active'); $('.community-footer .col ul').hide(); $(this).addClass('active'); $(this).children("ul").show(); $('.community-footer .col').children("h5").removeClass("addedClass"); $(this).children("h5").addClass("addedClass"); } if ( $(".community-footer .col:nth-child(4) ul").hasClass('custom-footer-res')) { $(".community-footer .col:nth-child(4) ul").removeClass('custom-footer-res'); } }) $('.community-footer .col:nth-child(2)').on("click", function () { if ($(this).hasClass('active')) { $(this).removeClass('active'); $(this).children("ul").hide(); $(this).children("h5").removeClass("addedClass"); } else { $(".community-footer .col").removeClass('active'); $('.community-footer .col ul').hide(); $(this).addClass('active'); $(this).children("ul").show(); $('.community-footer .col').children("h5").removeClass("addedClass"); $(this).children("h5").addClass("addedClass"); } if ( $(".community-footer .col:nth-child(4) ul").hasClass('custom-footer-res')) { $(".community-footer .col:nth-child(4) ul").removeClass('custom-footer-res'); } }) } })(LITHIUM.jQuery); LITHIUM.PartialRenderProxy({"limuirsComponentRenderedEvent":"LITHIUM:limuirsComponentRendered","relayEvent":"LITHIUM:partialRenderProxyRelay","listenerEvent":"LITHIUM:partialRenderProxy"}); LITHIUM.AjaxSupport({"ajaxOptionsParam":{"event":"LITHIUM:partialRenderProxyRelay","parameters":{"javascript.ignore_combine_and_minify":"true"}},"tokenId":"ajax","elementSelector":document,"action":"partialRenderProxyRelay","feedbackSelector":false,"url":"https://community.hubspot.com/t5/user/v2/viewprofilepage.liabase.basebody.partialrenderproxy:partialrenderproxyrelay?t:ac=user-id/169781","ajaxErrorEventName":"LITHIUM:ajaxError","token":"cWsd85AYIJUIHOjUZHOUmGKyw98TtkDvugG9P47_LvA."}); LITHIUM.Auth.API_URL = "/t5/util/authcheckpage"; LITHIUM.Auth.LOGIN_URL_TMPL = "https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2FREPLACE_TEXT"; LITHIUM.Auth.KEEP_ALIVE_URL = "/t5/status/blankpage?keepalive"; LITHIUM.Auth.KEEP_ALIVE_TIME = 300000; LITHIUM.Auth.CHECK_SESSION_TOKEN = 'kj2_w5gmiw-6rmAFIDILiit0N1sDcnNuPRlbn0D-7Gw.'; LITHIUM.AjaxSupport.useTickets = false; LITHIUM.Cache.CustomEvent.set([{"elementId":"link_2_23f26f708a5ac4","stopTriggerEvent":false,"fireEvent":"LITHIUM:selectMessage","triggerEvent":"click","eventContext":{"message":1072667}},{"elementId":"link_2_23f26f708a5ac4_0","stopTriggerEvent":false,"fireEvent":"LITHIUM:selectMessage","triggerEvent":"click","eventContext":{"message":1072585}},{"elementId":"link_2_23f26f708a5ac4_1","stopTriggerEvent":false,"fireEvent":"LITHIUM:selectMessage","triggerEvent":"click","eventContext":{"message":1072560}},{"elementId":"link_2_23f26f708a5ac4_2","stopTriggerEvent":false,"fireEvent":"LITHIUM:selectMessage","triggerEvent":"click","eventContext":{"message":1069718}},{"elementId":"link_2_23f26f708a5ac4_3","stopTriggerEvent":false,"fireEvent":"LITHIUM:selectMessage","triggerEvent":"click","eventContext":{"message":1068543}}]); LITHIUM.Loader.runJsAttached(); // --> </script></body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10