CINXE.COM
HubSpot Community
<!DOCTYPE html><html prefix="og: http://ogp.me/ns#" dir="ltr" lang="en" class="no-js"> <head> <title> HubSpot Community </title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <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/" rel="canonical"/> <meta content="community.hubspot.com" property="og:site_name"/><meta content="website" property="og:type"/><meta content="https://community.hubspot.com/" property="og:url"/><meta content="Welcome to the HubSpot Community! Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot." property="og:description"/><meta content="HubSpot Community" property="og:title"/> <link class="lia-link-navigation hidden live-links" title="New board topics in HubSpot Community" type="application/rss+xml" rel="alternate" id="link" href="/mjmao93648/rss/Community?interaction.style=forum"></link> <link class="lia-link-navigation hidden live-links" title="All board posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_0" href="/mjmao93648/rss/Community?interaction.style=forum&feeds.replies=true"></link> <link class="lia-link-navigation hidden live-links" title="New blog blog posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_1" href="/mjmao93648/rss/Community?interaction.style=blog"></link> <link class="lia-link-navigation hidden live-links" title="All blog posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_2" href="/mjmao93648/rss/Community?interaction.style=blog&feeds.replies=true"></link> <link class="lia-link-navigation hidden live-links" title="New idea exchange ideas in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_3" href="/mjmao93648/rss/Community?interaction.style=idea"></link> <link class="lia-link-navigation hidden live-links" title="All idea exchange posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_4" href="/mjmao93648/rss/Community?interaction.style=idea&feeds.replies=true"></link> <link class="lia-link-navigation hidden live-links" title="New Products" type="application/rss+xml" rel="alternate" id="link_5" href="/mjmao93648/rss/Community?interaction.style=review"></link> <link class="lia-link-navigation hidden live-links" title="All Reviews and Comments" type="application/rss+xml" rel="alternate" id="link_6" href="/mjmao93648/rss/Community?interaction.style=review&feeds.replies=true"></link> <link class="lia-link-navigation hidden live-links" title="rss.livelink.threads-in-node@place:occasion" type="application/rss+xml" rel="alternate" id="link_7" href="/mjmao93648/rss/Community?interaction.style=occasion"></link> <link class="lia-link-navigation hidden live-links" title="rss.livelink.posts-in-node@place:occasion" type="application/rss+xml" rel="alternate" id="link_8" href="/mjmao93648/rss/Community?interaction.style=occasion&feeds.replies=true"></link> <link class="lia-link-navigation hidden live-links" title="New media posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_9" href="/mjmao93648/rss/Community?interaction.style=media"></link> <link class="lia-link-navigation hidden live-links" title="All media posts in HubSpot Community" type="application/rss+xml" rel="alternate" id="link_10" href="/mjmao93648/rss/Community?interaction.style=media&feeds.replies=true"></link> <link href="/skins/6566941/cf3d09a7a299b32f8a1bbd2f844fb61f/hubspot.css" rel="stylesheet" type="text/css"/> <!-- Twitter Card metadata: For Japanese Blog Articles only --> <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":{},"commonResults":{}}; 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" : 1732400385156, "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%2F", "loginUrlNotRegistered" : "https://app.hubspot.com/khoros/integration/jwt/authenticate?redirectreason=notregistered&referer=https%3A%2F%2Fcommunity.hubspot.com%2F", "loginUrlNotRegisteredDestTpl" : "https://app.hubspot.com/khoros/integration/jwt/authenticate?redirectreason=notregistered&referer=%7B%7BdestUrl%7D%7D" }, "name" : "CommunityPage", "rtl" : false, "object" : { "viewHref" : "/", "id" : "mjmao93648", "page" : "CommunityPage", "type" : "Community" } }, "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 = 'community/Page'; LITHIUM.Components.ORIGINAL_PAGE_ID = 'CommunityPage'; LITHIUM.Components.ORIGINAL_PAGE_CONTEXT = '7lMjdYDiV7k28uqNPmnB3nvxObNQiWlr6xTHlSSAizIZvVPI_ifv1YtcGVbFSGpgHmfA-XW0vdo04PnbML74akumWhzEBupppIZXiP-Ycq9iCteNv8w430mDJ8fxDTp8CEB2M5EUmb00GccKQgvAO4j5cvhK1KPSviofeb1WgUR2HGBEvJumtCmvl9eB2-y3WaKdPUAkLH3gLmsFgND2W5InHZ25rL3A6uKLMGKZyfjG1r1rz3syGuwTAQbJm--e0LFTNWcnOTFX4-LZCkWukAwteI6w2wUNsFVuBHX8tbA.'; 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 = [ "ActiveCast3.js", "jquery.ui.draggable.js", "SearchAutoCompleteToggle.js", "jquery.js", "LiModernizr.js", "Video.js", "DeferredImages.js", "ElementMethods.js", "Components.js", "jquery.ui.widget.js", "jquery.clone-position-1.0.js", "jquery.iframe-shim-1.0.js", "Text.js", "UserListActual.js", "Link.js", "jquery.ui.mouse.js", "PolyfillsOld.js", "jquery.fileupload.js", "Throttle.js", "Loader.js", "jquery.ui.dialog.js", "Globals.js", "Sandbox.js", "jquery.tmpl-1.1.1.js", "jquery.iframe-transport.js", "jquery.tools.tooltip-1.2.6.js", "jquery.hoverIntent-r6.js", "PartialRenderProxy.js", "jquery.effects.core.js", "Tooltip.js", "json2.js", "jquery.json-2.6.0.js", "PolyfillsAll.js", "Auth.js", "jquery.position-toggle-1.0.js", "jquery.function-utils-1.0.js", "ElementQueries.js", "jquery.viewport-1.0.js", "prism.js", "jquery.lithium-selector-extensions.js", "NoConflict.js", "jquery.blockui.js", "SpoilerToggle.js", "jquery.css-data-1.0.js", "jquery.effects.slide.js", "jquery.placeholder-2.0.7.js", "jquery.appear-1.1.1.js", "jquery.ui.position.js", "jquery.ui.core.js", "Cache.js", "AjaxFeedback.js", "InformationBox.js", "ForceLithiumJQuery.js", "HelpIcon.js", "jquery.delayToggle-1.0.js", "Events.js", "AjaxSupport.js", "SearchForm.js", "Forms.js", "jquery.ajax-cache-response-1.0.js", "DataHandler.js", "jquery.autocomplete.js", "Namespace.js", "Lithium.js", "ResizeSensor.js", "Placeholder.js", "jquery.scrollTo.js", "AutoComplete.js", "jquery.ui.resizable.js" ];// --> </script><script type="text/javascript" src="/t5/scripts/D60EB96AE5FF670ED274F16ABB044ABD/lia-scripts-head-min.js"></script></head> <body class="lia-user-status-anonymous CommunityPage lia-body" id="lia-body"> <div id="C04-182-4" class="ServiceNodeInfoHeader"> </div> <div class="lia-page"> <center> <noscript class=" " id="hubspot" data-page="CommunityPage" 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+"¤tUser="+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="cdpQEE7QEkXlZ0ytYpBrvo4tNFbU77n_FJGFY3Z9b4A." rel="nofollow" id="dismissAlert" href="https://community.hubspot.com/t5/community/page.liabase.basebody.browsersupportalert.dismissalert:dismissalert"><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-community-page-v2 lia-quilt-layout-one-column lia-top-quilt"> <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/?profile.language=en" class="nav-dropdown-link" data-lang="en"> <li class="nav-dropdown-item"> English </li> </a> <a href="https://community.hubspot.com/?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/?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/?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/?profile.language=de" class="nav-dropdown-link" data-lang="de"> <li class="nav-dropdown-item"> Deutsch </li> </a> <a href="https://community.hubspot.com/?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_23a256cd58077e' 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_23a256cd58077e'></div> </div> <div id='searchautocompletetoggle_23a256cd58077e'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23a256cd58077e_0'></div> </div> <form enctype='multipart/form-data' class='lia-form lia-form-inline SearchForm' action='https://community.hubspot.com/t5/community/page.searchformv32.form.form' method='post' id='form_23a256cd58077e' name='form_23a256cd58077e'><div class='t-invisible'><input value='search/contributions/page' name='t:cp' type='hidden'></input><input value='zOBj4mbbv-raXIQqoiwENnX4O747TG_GXo0tE-KgB12fS_OKtnrIa5iOL5AxKSBU4jULvF__VGDPZWQRjX1YnTP8fZHpf34Fp_qwiffXU3SeC5uKAdNm0KsTBMXpmUstZKubAeDF66WCH0yli3zb5Q5sgNmPF5AQ8YTmyuGxbPXfpAnYDWPvOp9qza3e1CZOGrlAuqNuDbBEELr3URQGtH3EL4lGLfEiR6dgctO3GPBQKw6We-ot-2z6EQXErdD5PvaM-kfsuarp265-GXFql7q2thzUeK-OokWQc-SJ_6pCFgbqzwzxY5HNumM5Af5IXa9uLyO5_3XdHzBZV0UmSCUsNDEsk37q-NX_20PSQw9TE5AutJelGPROYO0spwScct10ltBuiAZE3ld8mmDkFAn16sDOqVaonHVO_sjgeyMt3-MeIj2hEl-RlnQJP6Pv5f_R5YMNMd-4APRXPhkwdE2GkXH8qCbHCqGWIsn5apyQ5zXoNuzpNM9KhjjuB5dFPl3dpS14WC64UQxFnS6QRYwt_OFOZrz5yVlP7AfmmGgRaN7lEX_8_a6BT6WKa9sH5CN6wFwDGXy7Tj-4szdBNo3u3K168EpYt8zi4HYJvJLqXOmRQeLZ89Fu-bAVsmI_tTJLpBz53-Y0Ra3gPoRYL0YQiIAZprsWsE3AdBtML7PsCrBFSUXY05xyVKe4zzPewQ0I_7DrbKEy1BbZPEPSsMGDfiKMffV-EaK7bEuU-zj8eNpxT9d3VUCgFOIk5dHcnzLRC8KBQGjJ8ZFmsEXQejNpUqZjSv99MM0HX0ILQSXeUbROiiszvbZcXlXLXG5Wy109PPHTwdj2lW15pZG9J-TI_BYv4RsfxUo1RgEa9hfTzW_QfNxWg-aNkB7ZiNoHdtYIuZvkfiAKmncRHHQesm1C9_mXP7F4DWb63f9WUTy722y5MrhQb7PaQucmh-jP7HvEeTsbgZduxstDvtFlMORTT684O46CqebEC5H0rPPCOCrVFtTqHpYXmx6RPbGjx5GvedzzH9ES--a5ogdROaZaoet-5Z-Dz0_ZUyjOy6aaNAd5a0SxDEMoctk6kEnWHEOPyibAno8rhnNBafycdQdwqmIIc4Vcj-7qywsu9iZqX67Hkyu1fK7h5cgkTbJNspN0AbLcQy4vULEQe6kX_pHw-TKs7hdXPjfupUKoQc0.' name='lia-form-context' type='hidden'></input><input value='CommunityPage::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_23a256cd58077e'></div> </div> <input value='x9w-ntY8m_tVtBCS11KaOmA-RKx88H_cD3s6B9WbOo0.' name='lia-action-token' type='hidden'></input> <input value='form_23a256cd58077e' id='form_UIDform_23a256cd58077e' name='form_UID' type='hidden'></input> <input value='' id='form_instance_keyform_23a256cd58077e' 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_23a256cd58077e' 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_23a256cd58077e_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_23a256cd58077e_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_23a256cd58077e' 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_23a256cd58077e_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_23a256cd58077e' 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='RYwnsc2bzB75thBQTgQsYw35ZdJPf5q_nuo8W4Z_6F4.' rel='nofollow' id='enableAutoComplete_23a256cd58077e' href='https://community.hubspot.com/t5/community/page.enableautocomplete:enableautocomplete?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_23a256cd58077e' 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_23a256cd58077e'></span></a><div role='alertdialog' class='lia-content lia-tooltip-pos-bottom-left lia-panel-tooltip-wrapper' id='link_23a256cd58077e_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_23a256cd58077e'></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_23a256cd58077e' 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_23a256cd58077e' 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_23a256d01e5937' href='https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2F'>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/?profile.language=en" class="nav-dropdown-link" data-lang="en"> <li class="nav-dropdown-item"> English </li> </a> <a href="https://community.hubspot.com/?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/?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/?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/?profile.language=de" class="nav-dropdown-link" data-lang="de"> <li class="nav-dropdown-item"> Deutsch </li> </a> <a href="https://community.hubspot.com/?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_23a256d048ec69' 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_23a256d048ec69'></div> </div> <div id='searchautocompletetoggle_23a256d048ec69'> <div class='lia-inline-ajax-feedback'> <div class='AjaxFeedback' id='ajaxfeedback_23a256d048ec69_0'></div> </div> <form enctype='multipart/form-data' class='lia-form lia-form-inline SearchForm' action='https://community.hubspot.com/t5/community/page.searchformv32.form.form' method='post' id='form_23a256d048ec69' name='form_23a256d048ec69'><div class='t-invisible'><input value='search/contributions/page' name='t:cp' type='hidden'></input><input value='Tf60qciyI-ywfTa6Tzj_wFPQW4s9GG7C0YqLstqAjGYW8uBLBzmIfq5CmuQ-CQZCJm6SYI6WIZM6XHkBQuD0BEnbJ4sr6KJS8gY6cAXhs6yfXg2s3SV6FACSmmyzIa10U8EPe8kQuh94BFJbzzAj-65C7_tSoBKNdA2k2mqY5v575onbgxcQnVGDSYUIKRxDb7o62kar9OWK4wPRQaM05PPDFNMLuOC_zWLr4PMZfNkfnubyXmiaaCHnjpbWhTL8WVg94ID7rrCPdOVGAOU95y3sSU-Ud_MdNbmAXi6JtqdWbsNwB68_6aWi3veXG8S9roOXFu5PgumcJlAPkXbROhbw2psokz5T7fhDOu6r6_YIpVJsaK7NCN8uqdO8Zd9xsBIEDg-8ssAGpEwmmU0piWtfwFYsELW6jMWgkXg---t8Megr1MG3MbWAtl5kcaOxaCbpK_OXgdhdgBrmBaRS_5BdXP1RHABp51Z2qyCEhiD8LeJfLu72SRwpsJdACPv903RxiBS64PSrYR1Z4fkjXf06LGwXOPOEOPlNBhqqVnbrBZCPEw2yA85mbSZ380H2Ffe_fYI46MDVioZxcGrRYZYV3qi-ckFviuuzHyTveCxwFlvVYDhZO4rziFwvVXnyx8ySpYYfRFlL3cae15cMiqSJLFA9bOPaU3nVR8jcArG6ruuhWkD7AJFUoTdTaHt40k7qz8IKJtVCcAFZTfE6PgmFsllWgD7qAsTLoMUlUcfIt8ZlNvX7iSFJGYfw2LECuuRmHcDOGDLKCVx3AA82jeHD2pWq4Ho16_ovezHPMGqZ6c_tOVu0o4_5vJPA9OEHqNezCLcf2yz-CWx2fXVbg4WBvhLNLosNpwadTZAwkghREgyNexSBSMmzNXV30ea6bNjXc69lrlPy1Hs_JALI9dYDzSPUF_PVQ94fRXlrcnL9l8jqvcxUgZXd2p_jtyZ3yX1anN-V6Y0TPapVrb0HwL7MxkHKWb_p3I08AT_GmQhdaO-xIki67yNjjx1z51NzEa7xlgPp6K-dmeGpC4Lucr7Aj-zvmHeZfuDy4Zhn1N4b0k3w07zatTm3jkHv-Upvh1uQnuE2MOYRZDkR51JamffwiOWp7r-hQk5xKIurn-Z8KV9igtwpO8mWLyURtYvj2nPtbXQI9eq8HuwpWbtSTdgYgLmcG4p0m_z93Q_ZqwIwNrZPgTUQfxNckpFYIuKOo0jHrTYAS_cl75QkbmcgE16GUDrNCTxFwH6mZN6m1E8.' name='lia-form-context' type='hidden'></input><input value='CommunityPage::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_23a256d048ec69'></div> </div> <input value='d997BjRYL3j0w6DD8tQWDs1RPefaGQMNho4srguhIhA.' name='lia-action-token' type='hidden'></input> <input value='form_23a256d048ec69' id='form_UIDform_23a256d048ec69' name='form_UID' type='hidden'></input> <input value='' id='form_instance_keyform_23a256d048ec69' 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_23a256d048ec69' name='searchGranularity'><option title='All Results' selected='selected' value='mjmao93648|community'>All Results</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_23a256d048ec69' 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_23a256d048ec69_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_23a256d048ec69_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_23a256d048ec69' 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_23a256d048ec69_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_23a256d048ec69' 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='pwXlHKRJS7w0KCOJLlhE0pYCOLZaXSz4-dlTb32CJhA.' rel='nofollow' id='enableAutoComplete_23a256d048ec69' href='https://community.hubspot.com/t5/community/page.enableautocomplete:enableautocomplete?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_23a256d048ec69' 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_23a256d048ec69'></span></a><div role='alertdialog' class='lia-content lia-tooltip-pos-bottom-left lia-panel-tooltip-wrapper' id='link_23a256d048ec69_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_23a256d048ec69'></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_23a256d048ec69' 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_23a256d048ec69' 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_23a256d285e05a' href='https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2F'>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%2F&lia-action-token=uzohzCOSEMku6YvgCpQVf-c7bZJsDjW27g1bGPPr81A.&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> </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"> <!-- homepage-search-hero-v2 --> <section class="homepage-hero v2 page-full-width guest-banner-wrapper" style="background-image:url('/html/assets/bg.community_page.png');" > <div class="custom-homepage-banner guest-mode"> <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><!-- [{"subject": "Product Manager Interview - Guided Selling in Sales Hub","body": " Excited to discuss AI-powered Guided Selling in Sales Hub! In just 8 minutes, I sit down with Brigitte Gabrus , Director of Product Management at HubSpot , to learn how Sales Hub transforms sales teams with tools designed to prioritize, personalize, and drive productivity. Here’","href": "/t5/Releases-and-Updates/Product-Manager-Interview-Guided-Selling-in-Sales-Hub/ba-p/1072777"},{"subject": "Google Tag Manager on HubSpot Landing Pages","body": " Hi, So we have multiple HubSpot landing pages where paid campaigns are run by agencies through their ad accounts. I want to install the Tag Manager on these landing pages which are hosted in HubSpot. So do I need to create multiple tag Manager account for each of the landing pages? How shou","href": "/t5/Blog-Website-Page-Publishing/Google-Tag-Manager-on-HubSpot-Landing-Pages/td-p/1073145"},{"subject": "[Webinar] From Insights to Growth: Using Product Data to Fuel Smarter Marketing","body": " Ready to unlock marketing growth with product qualified leads (PQLs)? Elevate your marketing game with insights from the best in the industry. Our Insights to Growth webinar, brought to you by Amplitude, HubSpot, and Aptitude 8, is tailor-made for marketers looking to superch","href": "/t5/Tips-Tricks-Best-Practices/Webinar-From-Insights-to-Growth-Using-Product-Data-to-Fuel/td-p/1068684"},{"subject": "[Live Webinar] Maximising Your Lead Gen Strategy with TikTok Ads and HubSpot","body": " Learn how to turn tuned-in audiences into high-value customers thanks to HubSpot's TikTok Ads Integration. In our upcoming webinar, you'll hear from HubSpot's Head of Marketing for Northern Europe, Laura Lane and TikTok's Nicholas O'Connor, Product Partnerships, on what's possible with Ti","href": "/t5/Community-Led-Events/Live-Webinar-Maximising-Your-Lead-Gen-Strategy-with-TikTok-Ads/td-p/1073368"}] --> <div class="custom-homepage-banner__wrapper"> <div class="custom-homepage-banner__header"> <div class="custom-homepage-banner__title"> <h2>Welcome to the HubSpot Community</h2> <p>Connect with peers, maximize your HubSpot knowledge, and learn how to grow better with HubSpot.</p> <a class="btn banner-action-btn" href="https://community.hubspot.com/t5/Getting-Started-on-the-Community/Getting-Started-on-the-Community/ba-p/404339">Get Started</a> </div> </div> <div class="custom-homepage-banner__body"> <div class="banner-grid-container"> <a href="/t5/Releases-and-Updates/Product-Manager-Interview-Guided-Selling-in-Sales-Hub/ba-p/1072777"class="item1"> <h4> Product Manager Interview - Guided Selling in Sales Hub </h4> <p> Excited to discuss AI-powered Guided Selling in Sales Hub! In just 8 minutes, I sit down with Brigitte Gabrus , D... </p> </a> <a href="/t5/Blog-Website-Page-Publishing/Google-Tag-Manager-on-HubSpot-Landing-Pages/td-p/1073145"class="item2"> <h4> Google Tag Manager on HubSpot Landing Pages </h4> <p> Hi, So we have multiple HubSpot landing pages where paid campaigns are run ... </p> </a> <a href="/t5/Tips-Tricks-Best-Practices/Webinar-From-Insights-to-Growth-Using-Product-Data-to-Fuel/td-p/1068684"class="item3"> <h4> [Webinar] From Insights to Growth: Using Product Data to Fuel Smarter Marketing </h4> <p> Ready to unlock marketing growth with product qualified leads (PQLs)? Elevate your marketing game wi... </p> </a> <a href="/t5/Community-Led-Events/Live-Webinar-Maximising-Your-Lead-Gen-Strategy-with-TikTok-Ads/td-p/1073368"class="item4"> <h4> [Live Webinar] Maximising Your Lead Gen Strategy with TikTok Ads and HubSpot </h4> <p> Learn how to turn tuned-in audiences into high-value customers thanks to HubSpot's TikTok Ads Integration. In our upcoming webinar, you'll hear from HubSpot's Head of Marketing for Northern Europe, Laura Lane ... </p> </a> </div> </div> </div> <style> .item1 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%204%20(490%20x%201166)/1.png") ; } .item2 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20Hot%202%20(346%20x%20326)/3.png") ; } .item3 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%203%20(224%20x%20290)/5.png") ; } .item4 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%201%20(312%20x%20327)/1.png") ; } @media(max-width:767px){ .item1 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%204%20(490%20x%201166)/Mobile%20Images/1.png") ; } .item2 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20Hot%202%20(346%20x%20326)/Mobile/3.png") ; } .item3 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%203%20(224%20x%20290)/Mobile/5.png") ; } .item4 { background-image : url("https://www.hubspot.com/hubfs/%5BHubSpot_Community%5D_/2024/Whats%20hot%201%20(312%20x%20327)/Mobile/1.png") ; } } </style> </div> </section> <div class="custom-tabs-with-accordian custom-top-community-conversations custom-home-banner-section wrapper-container"> <div class="custom-home-banner-section__header"><h2>Top Community Conversations</h2><p>Check out this round up of popular discussions from around the Community.</p> </div> <div id="top-conversations-tabs" class="custom-dashboard-tabs desktop-view"> <ul class="custom-dashboard-tabs__list"> <li class="custom-dashboard-tabs__item active " data-tab-id="tab-1" title="CRM & Sales Hub"> <a href="#">CRM & Sales Hub</a> </li> <li class="custom-dashboard-tabs__item " data-tab-id="tab-2" title="Marketing & Content"> <a href="#">Marketing & Content</a> </li> <li class="custom-dashboard-tabs__item " data-tab-id="tab-3" title="Customer Success & Service"> <a href="#">Customer Success & Service</a> </li> <li class="custom-dashboard-tabs__item " data-tab-id="tab-4" title="RevOps & Operations Hub"> <a href="#">RevOps & Operations Hub</a> </li> <li class="custom-dashboard-tabs__item " data-tab-id="tab-5" title="HubSpot Developers"> <a href="#">HubSpot Developers</a> </li> <li class="custom-dashboard-tabs__item " data-tab-id="tab-6" title="INBOUND"> <a href="#">INBOUND</a> </li> </ul> </div> <div id="top-conversations-tabs" class="custom-dashboard-tabs mobile-view"> <select class="custom-dashboard-tabs__list"> <option class="custom-dashboard-tabs__item" data-tab-id="tab-1"><a href="#" title="CRM & Sales Hub">CRM & Sales Hub</a></option> <option class="custom-dashboard-tabs__item" data-tab-id="tab-2"><a href="#" title="Marketing & Content">Marketing & Content</a></option> <option class="custom-dashboard-tabs__item" data-tab-id="tab-3"><a href="#" title="Customer Success & Service">Customer Success & Service</a></option> <option class="custom-dashboard-tabs__item" data-tab-id="tab-4"><a href="#" title="RevOps & Operations Hub">RevOps & Operations Hub</a></option> <option class="custom-dashboard-tabs__item" data-tab-id="tab-5"><a href="#" title="HubSpot Developers">HubSpot Developers</a></option> <option class="custom-dashboard-tabs__item" data-tab-id="tab-6"><a href="#" title="INBOUND">INBOUND</a></option> </select> </div> <div class="custom-dashboard-tabs__content top-conversations-tabs__content"> <div class="custom-dashboard-tab-body active tab-1"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CRM/Meeting-scheduler-and-the-new-Appointment-object/td-p/1072160">Meeting scheduler and the new Appointment object</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Is there any information on how the new "Appointments" object works together with the Meeting Scheduler? We want to keep track of our meetings (mainly digital ones), and we often use the built-in Meeting Scheduler for this purpose. I’m trying t ...<a href="/t5/CRM/Meeting-scheduler-and-the-new-Appointment-object/td-p/1072160" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CRM/Excluding-contact-if-one-property-is-equal-to-another/td-p/1071711">Excluding contact if one property is equal to another</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello, I can't seem to find a way to exclude contacts if the value in one property is equal to another property's value (in this case, if the company name is equal to the contact last name). Is there any way to do this action? ...<a href="/t5/CRM/Excluding-contact-if-one-property-is-equal-to-another/td-p/1071711" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CRM/Dynamic-Meeting-Schedule-Page-depending-on-Contact-Owner-on/td-p/1071677">Dynamic Meeting Schedule Page depending on Contact Owner on Website</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi everyone, a tricky question! I have tried to plan the process/logic to create a meeting router on our website but I am stuck at one specific point. The logic is that there is a Hubspot form embedded on the website where a lead can fill in conta ...<a href="/t5/CRM/Dynamic-Meeting-Schedule-Page-depending-on-Contact-Owner-on/td-p/1071677" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CRM/Exporting-Contacts-with-Internal-Values-from-Dropdowns/td-p/1071241">Exporting Contacts with Internal Values from Dropdowns</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Is there currently a way to export the internal value of an enummeration field on a contact instead of the display value? For some of our ennumeration fields we are using IDs for the internal value and would like to export them as opposed to the di ...<a href="/t5/CRM/Exporting-Contacts-with-Internal-Values-from-Dropdowns/td-p/1071241" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Sales-Integrations/Connect-more-than-one-whatsapp-business-to-hubspot/td-p/1070113">Connect more than one whatsapp business to hubspot</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Can I connect multipe whatsapp business accounts to my hubspot account so that the sales team can have their own whatsapp number? ...<a href="/t5/Sales-Integrations/Connect-more-than-one-whatsapp-business-to-hubspot/td-p/1070113" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/CRM-Sales-Hub/ct-p/sales">Go to CRM & Sales Hub </a> </div> </div> <div class="custom-dashboard-tab-body tab-2"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tips-Tricks-Best-Practices/How-to-create-an-Active-list-amp-Workflow-to-send-Birthday/td-p/1073846">How to create an Active list & Workflow to send Birthday Emails to Members</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello, I hope someone can assist me with this. I'm trying to create an Active List to send Birthday Emails in HubSpot, but I'm having difficulty retrieving members' birthdays. HubSpot appears to only consider the current year (2024), meaning that n ...<a href="/t5/Tips-Tricks-Best-Practices/How-to-create-an-Active-list-amp-Workflow-to-send-Birthday/td-p/1073846" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tips-Tricks-Best-Practices/Recommendations-for-social-media-and-website-integration-for/td-p/1073022">Recommendations for social media and website integration for HubSpot</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi HubSpot Community, I’m Oliver Jones, and I’m currently exploring ways to improve our digital marketing efforts. I’d love to tap into the collective knowledge of this community to get your recommendations and insights! Managing Social Media ...<a href="/t5/Tips-Tricks-Best-Practices/Recommendations-for-social-media-and-website-integration-for/td-p/1073022" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Lists-Lead-Scoring-Workflows/How-to-Prevent-Workflow-Un-enrollment-When-Merging-Tickets/td-p/1072107">How to Prevent Workflow Un-enrollment When Merging Tickets</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi everyone, I am Emmanuel Katto from Dubai, working with a workflow where a ticket property is incremented by 1, and then the ticket is either delayed for 12 hours and moved to a specific phase or, if the ticket property reaches a certain value, ...<a href="/t5/Lists-Lead-Scoring-Workflows/How-to-Prevent-Workflow-Un-enrollment-When-Merging-Tickets/td-p/1072107" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Email-Marketing-Tool/Social-Icons-Dark-Mode/td-p/1071796">Social Icons - Dark Mode</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Does anyone know why the social icons, despite being images, are inverting colour from white to black in dark mode clients? I understand dark mode will invert the colours of HTML sections, but I don't understand why this would also happen to the so ...<a href="/t5/Email-Marketing-Tool/Social-Icons-Dark-Mode/td-p/1071796" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tips-Tricks-Best-Practices/Newbie-Marketer-Any-tips-and-tricks-for-creating-engaging/td-p/1070585">Newbie Marketer: Any tips and tricks for creating engaging content?</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> My name is Kathy. I own a small herbal store. I have a website also. I need to learn to create engaging content to help promote my brand and increase online and in store sales. Check me out at www.2harvesttx.com . ...<a href="/t5/Tips-Tricks-Best-Practices/Newbie-Marketer-Any-tips-and-tricks-for-creating-engaging/td-p/1070585" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/Marketing-Content/ct-p/marketing">Go to Marketing & Content </a> </div> </div> <div class="custom-dashboard-tab-body tab-3"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tickets-Conversations/want-to-disable-the-sound-when-I-send-a-chat-message/td-p/1073506">want to disable the sound when I send a chat message</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello! I want to disable the sound that plays when I reply to a chat message. I still want all the other sounds for notifications, but this sound is not a notification; it just plays when I send a message. And it's annoying! ...<a href="/t5/Tickets-Conversations/want-to-disable-the-sound-when-I-send-a-chat-message/td-p/1073506" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tickets-Conversations/Unable-to-comment-in-Help-Desk-when-there-is-No-Conversation/td-p/1071955">Unable to comment in Help Desk when there is No Conversation History</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello, A client is having an issue inside help desk where they are unable to create an internal comment when there is no conversation history associated to the ticket. For example, a support team member recieves a call and creates a ticket in He ...<a href="/t5/Tickets-Conversations/Unable-to-comment-in-Help-Desk-when-there-is-No-Conversation/td-p/1071955" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tickets-Conversations/Uneven-Chat-Distribution/td-p/1071323">Uneven Chat Distribution</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Would love some help with our chat not evenly distributing to team members. We recently switched from Intercom and with Hubspot chat it seems to like to settle on one person as the main one to get chats throughout the day rather then round-robining ...<a href="/t5/Tickets-Conversations/Uneven-Chat-Distribution/td-p/1071323" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tickets-Conversations/Create-support-ticket-using-a-json-attachment-from-inbound-email/td-p/1070770">Create support ticket using a json attachment from inbound email</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi all TL;DR -------- Receive an email that contains a json file into a HubSpot inbox and have HubSpot parse the json, strip out the context and customer account, and then create subsequent help desk ticket? -------- We are lookin ...<a href="/t5/Tickets-Conversations/Create-support-ticket-using-a-json-attachment-from-inbound-email/td-p/1070770" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/Tips-Tricks-Best-Practices/AI-Chatbot/td-p/1070563">AI Chatbot</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello, I'm trying to add an AI Chatbot as shows in this video: https://www.youtube.com/watch?v=y12uwgcXDyE However, I can't see "AI Chatbot" under the Automation menu. Is it limited to paid plans? In the video, it seems as an option for the fre ...<a href="/t5/Tips-Tricks-Best-Practices/AI-Chatbot/td-p/1070563" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/Customer-Success-Service/ct-p/service_hub">Go to Customer Success & Service </a> </div> </div> <div class="custom-dashboard-tab-body tab-4"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/128172-RevOps-Discussions/Tracking-Funnel-Conversions-from-Stage-to-Stage/td-p/1071719">Tracking Funnel Conversions from Stage to Stage</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi HS Community, We are having an issue in my company. We have lifecycle stages on our company level that are: Target -> Prospect -> Awareness -> MQA -> SAA > SQA -> Customer. We also have certain workflows that set some compan ...<a href="/t5/128172-RevOps-Discussions/Tracking-Funnel-Conversions-from-Stage-to-Stage/td-p/1071719" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/9881-Operations-Hub/Conflicting-IP-Addresses-in-DNS-Configuration-for-Domain/td-p/1071562">Conflicting IP Addresses in DNS Configuration for Domain Connection</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi Community, I’m experiencing issues with connecting my domain to HubSpot. I keep receiving error messages during the DNS verification process. The information displayed in HubSpot seems to be contradictory: The "Required Data" column spe ...<a href="/t5/9881-Operations-Hub/Conflicting-IP-Addresses-in-DNS-Configuration-for-Domain/td-p/1071562" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/9881-Operations-Hub/How-to-do-a-API-Call-Out-from-Hubspot/td-p/1069533">How to do a API Call Out from Hubspot</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi Team, I want to make a api call out from hubspot. Can i known what is the best practice to make a API callout and what all resource i would be requiring to setup a callout Thanks! ...<a href="/t5/9881-Operations-Hub/How-to-do-a-API-Call-Out-from-Hubspot/td-p/1069533" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/9881-Operations-Hub/Re-enrollment-trigger-not-working/td-p/1068978">Re-enrollment trigger not working</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello everyone, I have created a workflow that works as following: subscription criterias are "Status" = Not answering and "Contact owner" = Sales1 Then i create a task for Sales1 to call the lead with deadline the same day The problem aris ...<a href="/t5/9881-Operations-Hub/Re-enrollment-trigger-not-working/td-p/1068978" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/128172-RevOps-Discussions/Live-User-Data-from-our-Application/td-p/1067116">Live User Data from our Application</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hey everyone, we have our in-house app which syncs with Amplitude app, and display our user data in live environment with all the events and actions users are doing in the app. I am looking at moving that user data into HubSpot, which currently is b ...<a href="/t5/128172-RevOps-Discussions/Live-User-Data-from-our-Application/td-p/1067116" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/RevOps-Operations-Hub/ct-p/Operations">Go to RevOps & Operations Hub </a> </div> </div> <div class="custom-dashboard-tab-body tab-5"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/APIs-Integrations/How-to-locate-recordId-for-Properties-via-API/td-p/1063501">How to locate recordId for Properties via API</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> I'm trying to PATCH (Update) a Property I went here first: https://developers.hubspot.com/beta-docs/guides/api/crm/properties for guidance. Then I pulled down a list of properties: GET https://api.hubapi.com/crm/v3/properties/deal ...<a href="/t5/APIs-Integrations/How-to-locate-recordId-for-Properties-via-API/td-p/1063501" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/APIs-Integrations/Custom-validation-amp-prevent-form-submision/td-p/882793">Custom validation & prevent form submision</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hello all, Just found a solution for doing a basic validation on fields before submit on a hubspot form. Conditions: Raw html form Include jquery Code <script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/embed/v2.js"& ...<a href="/t5/APIs-Integrations/Custom-validation-amp-prevent-form-submision/td-p/882793" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CMS-Development/Custom-Equation-Amount-Time-Between/td-p/850063">Custom Equation: Amount / Time Between</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hi, Part of our business is selling services. I'd like to create a new Deal Property that gets its value from a calculation. In two deal properties we set 'Start Date' and 'End Date' . This allows for a quick (and correct) return for 'Proje ...<a href="/t5/CMS-Development/Custom-Equation-Amount-Time-Between/td-p/850063" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/APIs-Integrations/Migration-guide-Migrate-an-API-key-integration-to-a-private-app/td-p/666309">Migration guide — Migrate an API key integration to a private app</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Hey, HubSpot Developers! There are plenty of questions about the upcoming sunset and how to migrate your apps using API keys to use Private Apps. Luckily, our amazing team put together this guide, Migrate an API key integration to a private a ...<a href="/t5/APIs-Integrations/Migration-guide-Migrate-an-API-key-integration-to-a-private-app/td-p/666309" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/CMS-Development/HubDB-vs-Custom-Object-Best-scenario-option/td-p/637789">HubDB vs Custom Object - Best scenario option?</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> I wanted to get some opinions on which direction to go based on a custom project scenario we have. First, a little background... Phase 1 - Created a dashboard webpage that is set to "private - registration required" and displays data from a ...<a href="/t5/CMS-Development/HubDB-vs-Custom-Object-Best-scenario-option/td-p/637789" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/HubSpot-Developers/ct-p/developers">Go to HubSpot Developers </a> </div> </div> <div class="custom-dashboard-tab-body tab-6"> <ul class="custom-dashboard-accordian-list"> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/INBOUND-Topics-Trends/Game-of-Emails-Compliance-is-Coming-Inbound24-Recap/td-p/1044902">Game of Emails: Compliance is Coming – Inbound24 Recap</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Game of Emails: Compliance is Coming – Inbound24 Recap Welcome, noble Lords and Ladies, to the realm of Inbound24, where we explored the treacherous lands of email deliverability and compliance. We hope you found our journey into making these d ...<a href="/t5/INBOUND-Topics-Trends/Game-of-Emails-Compliance-is-Coming-Inbound24-Recap/td-p/1044902" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/INBOUND-Topics-Trends/INBOUND-24-Recap-Day-2-amp-3/td-p/1043990">INBOUND 24 Recap Day 2 & 3</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Welcome to your Recap of INBOUND 2024! Here’s a summary of Thursday and Friday’s highlights and key takeaways from the sessions and events we enjoyed. Keynote Highlights Thursday, Sept 19 - The Power of Unified data, Product led g ...<a href="/t5/INBOUND-Topics-Trends/INBOUND-24-Recap-Day-2-amp-3/td-p/1043990" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/INBOUND-Topics-Trends/Topic-Ideas-for-Project-Managers/td-p/1043817">Topic Ideas for Project Managers</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> This year, our partner agency brought some folks from our project management team - and I was so curious to see if anyone found any good sessions for this kind of role or if they had any ideas of sessions or activities for future years that would ap ...<a href="/t5/INBOUND-Topics-Trends/Topic-Ideas-for-Project-Managers/td-p/1043817" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/INBOUND-Topics-Trends/Post-Show-Follow-Up/td-p/1043765">Post-Show Follow-Up</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> With so many learnings, I'm wondering how everyone takes their insights back to their teams (especially partner agencies). Anyone got any good tips or have done something that worked well in the past? ...<a href="/t5/INBOUND-Topics-Trends/Post-Show-Follow-Up/td-p/1043765" class="read-more">read more </a> </li> </ul> </li> <li class="custom-dashboard-accordian-item off"> <div class="custom-dashboard-accordian-item__header"> <span class="toggle-action on"><i class="fa fa-chevron-down nav-item-icon"></i></span> <h4 class="custom-dashboard-accordian-item__title"> <a class="con-link on" href="/t5/INBOUND-Topics-Trends/INBOUND-Recap-Day-1/td-p/1042871">INBOUND Recap Day 1</a> </h4> </div> <ul class="custom-dashboard-accordian-posts-list"> <li class="custom-dashboard-accordian-post-item scroll-class"> Welcome to your Daily Recap of INBOUND 2024! Here’s a summary of today’s highlights and key takeaways from the sessions and events we enjoyed. Keynote Highlights HubSpot Spotlight: The future of growth lies in the synergy of ...<a href="/t5/INBOUND-Topics-Trends/INBOUND-Recap-Day-1/td-p/1042871" class="read-more">read more </a> </li> </ul> </li> </ul> <div class="custom-home-banner-section__see-all"><a href="/t5/INBOUND/ct-p/INBOUND">Go to INBOUND </a> </div> </div> </div> </div> <!-- 2 --> <style> #lia-body .featured-places-tabs__content .wrapper-container{ padding-left:0px; padding-right:0px; } </style> <div class="custom-home-banner-section custom-featured-boards"> <div class="custom-home-banner-section__wrapper wrapper-container"> <div class="custom-home-banner-section__header"> <h2>Featured Places</h2> <p>Discover different forums, blogs, and groups from around the Community</p> </div> <div class="custom-home-banner-section__cards"> <a class="card-item" href="/t5/Reporting-Analytics/bd-p/marketing_reports"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/104028iB5B55C79F4227C75/image-size/tiny?v=v2&px=100"></div> <h4 class="card-item__head">Reporting & Analytics</h4> <p class="card-item__desc"> Web analytics, keywords, competitors and ads.... </p> <div class="card-item__stats"> <span title="Total posts">5233</span> </div> </a> <a class="card-item" href="/t5/Sales-Email/bd-p/crm_setup"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/104012i6DF9C70BEF688DE6/image-size/tiny?v=v2&px=100"></div> <h4 class="card-item__head">Sales Email</h4> <p class="card-item__desc"> IMAP setup, sequences, templates and tracking... </p> <div class="card-item__stats"> <span title="Total posts">4674</span> </div> </a> <a class="card-item" href="/t5/Tickets-Conversations/bd-p/service_conversations_tickets"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/103984i95A29559BF037413/image-size/tiny?v=v2&px=100"></div> <h4 class="card-item__head">Tickets & Conversations</h4> <p class="card-item__desc"> Helpdesk and customer ticket management. </p> <div class="card-item__stats"> <span title="Total posts">5093</span> </div> </a> <a class="card-item" href="/t5/APIs-Integrations/bd-p/integrations"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/104044i354A6A29E2FA2F18/image-size/tiny?v=v2&px=100"></div> <h4 class="card-item__head">APIs & Integrations</h4> <p class="card-item__desc"> Ask questions and find answers about HubSpot'... </p> <div class="card-item__stats"> <span title="Total posts">40776</span> </div> </a> <a class="card-item" href="/t5/9881-Operations-Hub/bd-p/Operations_Hub"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/104049i2055972D27E26755/image-size/tiny?v=v2&px=100"></div> <h4 class="card-item__head">⚙ Operations Hub</h4> <p class="card-item__desc"> Ask and answer questions about HubSpot’s Oper... </p> <div class="card-item__stats"> <span title="Total posts">2463</span> </div> </a> </div> </div> </div> <!-- 2 --> <div class="custom-home-banner-section custom-featured-groups"> <div class="custom-home-banner-section__wrapper wrapper-container"> <div class="custom-home-banner-section__header"> <h2>Featured Groups</h2> <p>Join groups to share and connect with likeminded HubSpot community members.</p> </div> <!-- ******* --> <div class="custom-home-banner-section__cards"> <a class="card-item" href = "/t5/Email-Marketing/gh-p/study-group-email-marketing"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/45267i73B4F4FC33179E59/image-size/medium?v=v2&px=400"></div> <h4 class="card-item__head">Email Marketing</h4> <p class="card-item__desc"> A place for email marketers to share ideas, l... </p> <div class="card-item__stats"> <span class="member-count" title="Group Members">8305</span> <span class="topic-count" title="Total posts">167</span> </div></a> <a class="card-item" href = "/t5/Inbound-Sales/gh-p/study-group-inbound-sales"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/45264i8254200E8FB43D60/image-size/medium?v=v2&px=400"></div> <h4 class="card-item__head">Inbound Sales</h4> <p class="card-item__desc"> A place for sales professionals to share idea... </p> <div class="card-item__stats"> <span class="member-count" title="Group Members">9284</span> <span class="topic-count" title="Total posts">127</span> </div></a> <a class="card-item" href = "/t5/Video-Marketing/gh-p/study-group-video-marketing"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/45266iDC5E2F0CF9DD20F9/image-size/medium?v=v2&px=400"></div> <h4 class="card-item__head">Video Marketing</h4> <p class="card-item__desc"> A place for video marketers to share ideas, l... </p> <div class="card-item__stats"> <span class="member-count" title="Group Members">2174</span> <span class="topic-count" title="Total posts">84</span> </div></a> <a class="card-item" href = "/t5/Social-Media/gh-p/study-group-social-media"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/45259iD4639CEF68A7ECBF/image-size/medium?v=v2&px=400"></div> <h4 class="card-item__head">Social Media</h4> <p class="card-item__desc"> A place for social media professionals to sha... </p> <div class="card-item__stats"> <span class="member-count" title="Group Members">12617</span> <span class="topic-count" title="Total posts">238</span> </div></a> <a class="card-item" href = "/t5/Content-Hub-for-Marketers/gh-p/study-group-cms-for-marketers"> <div class="card-item__icon"><img src="/t5/image/serverpage/image-id/65296i8B0E110F9F912056/image-size/medium?v=v2&px=400"></div> <h4 class="card-item__head">Content Hub for Marketers</h4> <p class="card-item__desc"> A place for marketers interested in Content H... </p> <div class="card-item__stats"> <span class="member-count" title="Group Members">1310</span> <span class="topic-count" title="Total posts">30</span> </div></a> </div> <div class="custom-home-banner-section__see-all"> <a href="/t5/Groups/ct-p/groups">See all</a> </div> </div> </div> <div class="custom-home-banner-section v2 custom-live-events"> <div class="custom-home-banner-section__wrapper"> <style> .events-section-main{ min-height: 228px; } .events-section-top{ display: flex; justify-content: space-between; box-shadow: 0 1px 5px 0 rgba(45,62,80,0.12); border-radius: 3px; } .events-section-heading{ background:#fff; padding: 30px 30px 30px 42px; width: calc(100% - 350px); display: inline-block; border-top-left-radius: 3px; border-bottom-left-radius: 3px; } .events-section-heading h1{ color: #33475B; font-size: 32px; font-weight: bold; letter-spacing: 0; line-height: 44px; } .events-section-heading p{ color: #33475B; font-size: 18px; letter-spacing: 0; line-height: 32px; } .events-section-tab{ display: inline-block; width: 100%; max-width: 350px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .events-section-tab img{ width:100%; height: 100%; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .learn-more-btn{ border-radius: 3px; background-color: #FF7A59; color: #FFFFFF; font-size: 16px; font-weight: 600; letter-spacing: 0; line-height: 28px; padding: 8px 30px; } .learn-more-btn:hover{ text-decoration: none; color:#fff;} @media only screen and (max-width: 767px) { #lia-body .wrapper-container.events-section-bottom{ display:block; } .events-section-top{ display:flex; flex-flow:column; } .events-section-heading{ width:100%; } .events-section-tab{ order:-1;} } </style> <div class="events-section-main wrapper-container events-section-bottom"> <div class="events-section-top"> <div class="events-section-heading"> <h1>Live Events</h1> <p>Browse upcoming events hosted by HubSpot. With topics ranging from product deep dives to networking opportunities, there is an event to help you grow your skills and continue growing with HubSpot.</p> <a target="_blank" href="https://events.hubspot.com/" class="btn btn-primary learn-more-btn"><span>See Events</span></a> </div> <div class="events-section-tab"> <img src="https://community.hubspot.com/html/@A696DCC46884337F9626CCB93F41EBF5/assets/hubspot-live-event.png" alt=""> </div> </div> </div> </div> </div> <style> #lia-body .champion-box .recentContributorLeaderboard { box-shadow: none; margin: 0; } #lia-body .champion-box .recentContributorLeaderboard ul li:nth-child(n+6) { display: inherit; } </style> <div class="spotlight-wrapper"> <div class="template-centered"> <div class="spotlight-row"> <div class="spotlight-left-col"> <h4>Community Spotlight</h4> <div class="customer-box"> <div class="customer-img-box"> <a href = "/t5/Advocates-Blog/Meet-the-Champion-of-the-Month-November-2024/ba-p/1063777"><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Profile Background Option 6 (1).png" style="width: 200px;"><img src="https://community.hubspot.com/t5/image/serverpage/image-id/130751iA78F59A707CC783F/image-size/small?v=v2&px=200" role="button" title="Profile Background Option 6 (1).png" alt="Profile Background Option 6 (1).png" /></span><a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/91608">@alyssamwilie</a> </P></a> </div> <div class="customer-info-box"> <div class="customer-head">Meet the Champion of the Month: November 2024</div> <div class="customer-description"> Hello Community! I am thrilled to announce that our Champion of the Month for November 2024 is: Alyssa Wilie @alyssamwilie Let's give Alyssa a big round of applause for her contributions in the HubSpot Community What makes Alyssa our Community Champion of the Month? Alyssa is a front end developer at LyntonWe... </div> <div class="cta-box"> <a href="/t5/Advocates-Blog/Meet-the-Champion-of-the-Month-November-2024/ba-p/1063777">Read More</a> <a href="https://offers.hubspot.com/community-champions" target="_blank">Become a Champion</a> </div> </div> </div> </div> <div class="spotlight-right-col"> <div class="champion-box"> <style> div.top-solutions .UserAvatar {display: inline-block;} </style> <section class="advisor-features"> <div class="advisor-features-container page-width-body center-block"> <div class="card-container"> <div class="top-advisors top-solutions"> <h2>Community Leaderboard</h2> <div class="top-solutions-column-header"> <h6>Username</h6> <h6 class="count-column">upvotes</h6> </div> <div class="top-solution-column-content" id="top-solution-container"> <ul id="top-solutions" class="top-kudos-authors top-solutions"> <li id="top-author"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/111325"> <img class="lia-user-avatar-message" title="karstenkoehler" alt="karstenkoehler" id="display" src="https://community.hubspot.com/t5/image/serverpage/image-id/94937i541C6229094CA9C4/image-dimensions/150x150/image-coordinates/0%2C0%2C400%2C400?v=v2"> </a> </div> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/111325"> <span class="login-bold">karstenkoehler</span> </a> </span> <span class="base-count-number">341</span> </li> <li id="top-author"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/141"> <img class="lia-user-avatar-message" title="TomM2" alt="TomM2" id="display" src="https://community.hubspot.com/t5/image/serverpage/image-id/85434iB7C5AAFDC991A8DE/image-dimensions/150x150/image-coordinates/0%2C210%2C1231%2C1441?v=v2"> </a> </div> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/141"> <span class="login-bold">TomM2</span> </a> </span> <span class="base-count-number">62</span> </li> <li id="top-author"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/123775"> <img class="lia-user-avatar-message" title="danmoyle" alt="danmoyle" id="display" src="https://community.hubspot.com/t5/image/serverpage/image-id/78949i1FED35B475025824/image-dimensions/150x150/image-coordinates/80%2C0%2C720%2C640?v=v2"> </a> </div> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/123775"> <span class="login-bold">danmoyle</span> </a> </span> <span class="base-count-number">36</span> </li> <li id="top-author"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/363300"> <img class="lia-user-avatar-message" title="Jnix284" alt="Jnix284" id="display" src="https://community.hubspot.com/t5/image/serverpage/image-id/63384iEE9BBBF006FB1880/image-dimensions/150x150/image-coordinates/0%2C0%2C400%2C400?v=v2"> </a> </div> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/363300"> <span class="login-bold">Jnix284</span> </a> </span> <span class="base-count-number">32</span> </li> <li id="top-author"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/198917"> <img class="lia-user-avatar-message" title="ScottPennwood" alt="ScottPennwood" id="display" src="https://community.hubspot.com/t5/image/serverpage/image-id/129844i0CD60111956E3002/image-dimensions/150x150/image-coordinates/0%2C0%2C200%2C200?v=v2"> </a> </div> <span class="UserName lia-user-name lia-user-rank-Community-Manager"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_6" href="/t5/user/viewprofilepage/user-id/198917"> <span class="login-bold">ScottPennwood</span> </a> </span> <span class="base-count-number">27</span> </li> </ul> </div> <div class="view-all-wrapper"><a class="view-all microcopy top-solutions-view-all" href="/t5/kudos/leaderboardpage/category-id/hubspot_community_en/timerange/one_month">View all</a></div> </div> </div> </div> </section> <!-- <div class="lia-panel lia-panel-standard recentContributorLeaderboard"> <!-- <div class="lia-panel-content-wrapper"> <div class="lia-panel-content-box"> <div class="lia-panel lia-panel-standard KudoedAuthorsLeaderboardTaplet Chrome lia-component-kudos-widget-authors-leaderboard"><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">Top Contributors</span></div></div><div class="lia-panel-content-wrapper"><div class="lia-panel-content"> <div class="UserList lia-component-users-widget-user-list"> <span id="user-listuserList_23a256eb3c55fb"> </span> <div class="t-data-grid" id="grid_23a256eb3c55fb"> <table role="presentation" class="lia-list-slim"> <thead class="lia-table-head" id="columns_23a256eb3c55fb"><tr><th scope="col" class="userColumn lia-data-cell-primary lia-data-cell-text t-first"> <span class="lia-view-filter lia-link-disabled" aria-disabled="true" id="link_23a256eb3c55fb">User</span> </th><th scope="col" class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer t-last"> Count </th></tr></thead> <tbody> <tr class="lia-list-row lia-row-odd t-first"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-111325 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb" href="/t5/user/viewprofilepage/user-id/111325"><img class="lia-user-avatar-message" title="karstenkoehler" alt="karstenkoehler" id="display_23a256eb3c55fb" src="/t5/image/serverpage/image-id/94937i541C6229094CA9C4/image-dimensions/38x38/image-coordinates/0%2C0%2C400%2C400?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Hall-of-Famer---Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of karstenkoehler" itemprop="url" id="link_23a256eb3c55fb_0" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/111325"><span class="">karstenkoehler</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Hall of Famer | Partner" alt="Hall of Famer | Partner" id="display_0_23a256eb3c55fb" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/partner-color.svg"/> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 107"> 107 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-833252 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_0" href="/t5/user/viewprofilepage/user-id/833252"><img class="lia-user-avatar-message" title="nlakeland" alt="nlakeland" id="display_23a256eb3c55fb_0" 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> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Member"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of nlakeland" itemprop="url" id="link_23a256eb3c55fb_1" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/833252"><span class="">nlakeland</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 45"> 45 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-141 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_1" href="/t5/user/viewprofilepage/user-id/141"><img class="lia-user-avatar-message" title="TomM2" alt="TomM2" id="display_23a256eb3c55fb_1" src="/t5/image/serverpage/image-id/85434iB7C5AAFDC991A8DE/image-dimensions/38x38/image-coordinates/0%2C204%2C1232%2C1436?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Thought-Leader---Platinum-Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of TomM2" itemprop="url" id="link_23a256eb3c55fb_2" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/141"><span class="">TomM2</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Thought Leader | Platinum Partner" alt="Thought Leader | Platinum Partner" id="display_0_23a256eb3c55fb_0" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/platinum-color.svg"/> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 19"> 19 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-858848 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_2" href="/t5/user/viewprofilepage/user-id/858848"><img class="lia-user-avatar-message" title="jolliapalmon" alt="jolliapalmon" id="display_23a256eb3c55fb_2" 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> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Member"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of jolliapalmon" itemprop="url" id="link_23a256eb3c55fb_3" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/858848"><span class="">jolliapalmon</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 11"> 11 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-53340 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_3" href="/t5/user/viewprofilepage/user-id/53340"><img class="lia-user-avatar-message" title="jforte" alt="jforte" id="display_23a256eb3c55fb_3" src="/t5/image/serverpage/image-id/61195i3146EA87F3BF6BBC/image-dimensions/38x38?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Top-Contributor"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of jforte" itemprop="url" id="link_23a256eb3c55fb_4" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/53340"><span class="">jforte</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 11"> 11 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-4892 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_4" href="/t5/user/viewprofilepage/user-id/4892"><img class="lia-user-avatar-message" title="VagaeNatus" alt="VagaeNatus" id="display_23a256eb3c55fb_4" src="/t5/image/serverpage/avatar-name/monkey/avatar-theme/candy/avatar-collection/animals/avatar-display-size/message/version/2?xdesc=1.0"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Key-Advisor"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of VagaeNatus" itemprop="url" id="link_23a256eb3c55fb_5" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/4892"><span class="">VagaeNatus</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 11"> 11 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-73173 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_5" href="/t5/user/viewprofilepage/user-id/73173"><img class="lia-user-avatar-message" title="franksteiner79" alt="franksteiner79" id="display_23a256eb3c55fb_5" src="/t5/image/serverpage/image-id/17243i5D1F32478A522B19/image-dimensions/38x38/image-coordinates/320%2C0%2C1600%2C1280?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Key-Advisor"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of franksteiner79" itemprop="url" id="link_23a256eb3c55fb_6" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/73173"><span class="">franksteiner79</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 10"> 10 </td></tr><tr class="lia-list-row lia-row-even t-last"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-294724 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256eb3c55fb_6" href="/t5/user/viewprofilepage/user-id/294724"><img class="lia-user-avatar-message" title="HNewman" alt="HNewman" id="display_23a256eb3c55fb_6" src="/t5/image/serverpage/image-id/112615i29C6360E39A63241/image-dimensions/38x38/image-coordinates/0%2C0%2C648%2C648?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Member"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of HNewman" itemprop="url" id="link_23a256eb3c55fb_7" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/294724"><span class="">HNewman</span></a> </span> </div> </div> </div> </td><td class="kudosCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of kudos: 9"> 9 </td></tr> </tbody> </table> </div> </div> <div class="lia-view-all"><a class="lia-link-navigation view-all-link" id="link_23a256eb3c55fb_8" href="/t5/forums/kudosleaderboardpage/timerange/one_week/page/1/tab/authors">View All</a></div> </div></div></div></div><div class="lia-decoration-border-bottom"><div> </div></div></div></div><div class="lia-panel lia-panel-standard AcceptedSolutionsLeaderboardTaplet Chrome lia-component-solutions-widget-accepted-solutions-leaderboard-taplet"><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">Top Solution Authors</span></div></div><div class="lia-panel-content-wrapper"><div class="lia-panel-content"> <div class="UserList lia-component-users-widget-user-list"> <span id="user-listuserList_23a256ed805d31"> </span> <div class="t-data-grid" id="grid_23a256ed805d31"> <table role="presentation" class="lia-list-slim"> <thead class="lia-table-head" id="columns_23a256ed805d31"><tr><th scope="col" class="userColumn lia-data-cell-primary lia-data-cell-text t-first"> <span class="lia-view-filter lia-link-disabled" aria-disabled="true" id="link_23a256ed805d31">User</span> </th><th scope="col" class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer t-last"> Solutions </th></tr></thead> <tbody> <tr class="lia-list-row lia-row-odd t-first"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-111325 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31" href="/t5/user/viewprofilepage/user-id/111325"><img class="lia-user-avatar-message" title="karstenkoehler" alt="karstenkoehler" id="display_23a256ed805d31" src="/t5/image/serverpage/image-id/94937i541C6229094CA9C4/image-dimensions/38x38/image-coordinates/0%2C0%2C400%2C400?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Hall-of-Famer---Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of karstenkoehler" itemprop="url" id="link_23a256ed805d31_0" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/111325"><span class="">karstenkoehler</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Hall of Famer | Partner" alt="Hall of Famer | Partner" id="display_0_23a256ed805d31" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/partner-color.svg"/> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 2,006"> 2006 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-363300 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_0" href="/t5/user/viewprofilepage/user-id/363300"><img class="lia-user-avatar-message" title="Jnix284" alt="Jnix284" id="display_23a256ed805d31_0" src="/t5/image/serverpage/image-id/63384iEE9BBBF006FB1880/image-dimensions/38x38/image-coordinates/0%2C0%2C400%2C400?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Hall-of-Famer"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of Jnix284" itemprop="url" id="link_23a256ed805d31_1" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/363300"><span class="">Jnix284</span></a> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 713"> 713 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-640 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_1" href="/t5/user/viewprofilepage/user-id/640"><img class="lia-user-avatar-message" title="Lucila-Andimol" alt="Lucila-Andimol" id="display_23a256ed805d31_1" src="/t5/image/serverpage/image-id/51165iEDCBDF635B851437/image-dimensions/38x38/image-coordinates/769%2C0%2C3086%2C2317?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Thought-Leader---Platinum-Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of Lucila-Andimol" itemprop="url" id="link_23a256ed805d31_2" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/640"><span class="">Lucila-Andimol</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Thought Leader | Platinum Partner" alt="Thought Leader | Platinum Partner" id="display_0_23a256ed805d31_0" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/platinum-color.svg"/> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 275"> 275 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-141 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_2" href="/t5/user/viewprofilepage/user-id/141"><img class="lia-user-avatar-message" title="TomM2" alt="TomM2" id="display_23a256ed805d31_2" src="/t5/image/serverpage/image-id/85434iB7C5AAFDC991A8DE/image-dimensions/38x38/image-coordinates/0%2C204%2C1232%2C1436?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Thought-Leader---Platinum-Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of TomM2" itemprop="url" id="link_23a256ed805d31_3" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/141"><span class="">TomM2</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Thought Leader | Platinum Partner" alt="Thought Leader | Platinum Partner" id="display_0_23a256ed805d31_1" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/platinum-color.svg"/> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 230"> 230 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-87451 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_3" href="/t5/user/viewprofilepage/user-id/87451"><img class="lia-user-avatar-message" title="Ben_M" alt="Ben_M" id="display_23a256ed805d31_3" src="/t5/image/serverpage/avatar-name/suv/avatar-theme/candy/avatar-collection/transit/avatar-display-size/message/version/2?xdesc=1.0"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Key-Advisor"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of Ben_M" itemprop="url" id="link_23a256ed805d31_4" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/87451"><span class="">Ben_M</span></a> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 170"> 170 </td></tr><tr class="lia-list-row lia-row-even"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-781425 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_4" href="/t5/user/viewprofilepage/user-id/781425"><img class="lia-user-avatar-message" title="Gaurav_Aggarwal" alt="Gaurav_Aggarwal" id="display_23a256ed805d31_4" src="/t5/image/serverpage/image-id/121558iBA0D96D36ABB09C4/image-dimensions/38x38/image-coordinates/0%2C0%2C340%2C340?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Key-Advisor---Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of Gaurav_Aggarwal" itemprop="url" id="link_23a256ed805d31_5" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/781425"><span class="">Gaurav_Aggarwal</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Key Advisor | Partner" alt="Key Advisor | Partner" id="display_0_23a256ed805d31_2" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/partner-color.svg"/> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 152"> 152 </td></tr><tr class="lia-list-row lia-row-odd"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-73173 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_5" href="/t5/user/viewprofilepage/user-id/73173"><img class="lia-user-avatar-message" title="franksteiner79" alt="franksteiner79" id="display_23a256ed805d31_5" src="/t5/image/serverpage/image-id/17243i5D1F32478A522B19/image-dimensions/38x38/image-coordinates/320%2C0%2C1600%2C1280?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Key-Advisor"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of franksteiner79" itemprop="url" id="link_23a256ed805d31_6" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/73173"><span class="">franksteiner79</span></a> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 144"> 144 </td></tr><tr class="lia-list-row lia-row-even t-last"><td class="userColumn lia-data-cell-primary lia-data-cell-text"> <div class="UserProfileSummary lia-user-item lia-js-data-userId-346639 lia-user-info-group"> <div class="UserAvatar lia-user-avatar lia-component-common-widget-user-avatar"> <a class="UserAvatar lia-link-navigation" tabindex="-1" target="_self" id="link_1_23a256ed805d31_6" href="/t5/user/viewprofilepage/user-id/346639"><img class="lia-user-avatar-message" title="GRajput" alt="GRajput" id="display_23a256ed805d31_6" src="/t5/image/serverpage/image-id/94960iD89D3FDEC001CBFE/image-dimensions/38x38/image-coordinates/96%2C48%2C1010%2C961?v=v2"/> </a> </div> <div class="lia-user-attributes"> <div class="lia-user-name"> <span class="UserName lia-user-name lia-user-rank-Recognized-Expert---Platinum-Partner"> <a class="lia-link-navigation lia-page-link lia-user-name-link" style="" target="_self" aria-label="View Profile of GRajput" itemprop="url" id="link_23a256ed805d31_7" href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/346639"><span class="">GRajput</span></a> <img class="lia-user-rank-icon lia-user-rank-icon-right" title="Recognized Expert | Platinum Partner" alt="Recognized Expert | Platinum Partner" id="display_0_23a256ed805d31_3" src="https://cdn2.hubspot.net/hubfs/53/Partners%20Redesign%202019/Agencies/tag-icons/platinum-color.svg"/> </span> </div> </div> </div> </td><td class="acceptedSolutionsCountColumn lia-data-cell-tertiary lia-data-cell-integer" aria-label="Number of accepted solutions: 130"> 130 </td></tr> </tbody> </table> </div> </div> <div class="lia-view-all"><a class="lia-link-navigation" id="link_23a256ed805d31_8" href="/t5/solutions/acceptedsolutionsleaderboardpage/node-display-id/community%3Amjmao93648/timerange/six_months">View All</a></div> </div></div></div></div><div class="lia-decoration-border-bottom"><div> </div></div></div></div> </div> </div> --> </div> </div> </div> </div> </div> </div> <div class="custom-home-banner-section v3 custom-get-experience"> <div class="custom-home-banner-section__wrapper wrapper-container"> <div class="custom-home-banner-section__header"> <h2>Get the full HubSpot Community experience</h2> </div> <div class="custom-home-banner-section__cards"> <div class="card-item"> <div class="card-item__icon"><img src="/html/assets/announcement.svg"></div> <a href = " /t5/Advocacy/ct-p/advocacy "><h4 class="card-item__head">Community Champions</h4></a> <p class="card-item__desc">Receive points and badges as a Community Champion for sharing your love and knowledge of HubSpot. <span><a href="/t5/Advocacy/ct-p/advocacy">Learn More</a></span> </p> </div> <div class="card-item"> <div class="card-item__icon"><img src="/html/assets/Solutions Directory.svg"></div> <a href = " https://www.hubspot.com/partners/solutions "><h4 class="card-item__head">Solutions Partner Program</h4></a> <p class="card-item__desc">Interested in teaming up with HubSpot? Find the partner program that's right for you. <span><a href=" https://www.hubspot.com/partners/solutions ">Learn More</a></span> </p> </div> <div class="card-item"> <div class="card-item__icon"><img src="/html/assets/People (1).svg"></div> <a href = " https://www.hubspot.com/hubspot-user-groups "><h4 class="card-item__head">User Groups</h4></a> <p class="card-item__desc">Sign up to learn when your local HubSpot User Group (HUG) is planning their next meet-up. <span><a href=" https://www.hubspot.com/hubspot-user-groups ">Learn More</a></span> </p> </div> <div class="card-item"> <div class="card-item__icon"><img src="/html/assets/Availability.svg"></div> <a href = ""><h4 class="card-item__head">Conversations</h4></a> <p class="card-item__desc">Sign in to join strategy and product focused conversations with your peers.</p> </div> </div> <div class="custom-home-banner-section__join-btn"> <a href="https://app.hubspot.com/khoros/integration/jwt/authenticate?referer=https%3A%2F%2Fcommunity.hubspot.com%2F">Join the Community</a> </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"> <!-- 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> </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","3","8","0","4","0","7","9","6","4","_","1","7","3","2","4","0","0","5","2","6","5","0","6",".","g","i","f"].join(""); </script> <script type="text/javascript" src="/t5/scripts/C1D0FDEB5D557CE5FA1EFA105E95A13F/lia-scripts-common-min.js"></script><script type="text/javascript" src="/t5/scripts/B1F5AEA9EDE360355D99A0C0F7016E3B/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" : "CommunityPage","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/","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 & ampersand bug in SEO field of ArticleEditorPage (TKB, more?) /* & gets replace with & but each time the article is saved again, the & of the & /* gets encoded again, resulting in repeated and invalid encoding, e.g. &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 `&`, 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(/&/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 = '';} 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($) { <!-- 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_23a256cd58077e","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256cd58077e_0","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256cd58077e_1","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256cd58077e_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_23a256cdf55f66\\\"><\\/div>\\n\\t\\t\\t\\n\\t\\n\\n\\t\\n\\n\\t\\t<\\/div>\";LITHIUM.AjaxSupport.defaultAjaxErrorHtml = \"<span id=\\\"feedback-errorfeedback_23a256ce44b510\\\"> <\\/span>\\n\\n\\t\\n\\t\\t<div class=\\\"InfoMessage lia-panel-feedback-inline-alert lia-component-common-widget-feedback\\\" id=\\\"feedback_23a256ce44b510\\\">\\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_23a256cdbd56c5', 'disableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, 'GeMGz_PWSFjXU8GROpkU-pd4YwJShRSzSvnSViGPQK8.', '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=\"H4XTkNu6GpzuM2pLQ46L6RoZuGirtb06yR_A0xcn9qw.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256cdbd56c5\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23a256cd58077e_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.messagesearchfield.messagesearchfield:autocomplete?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_23a256ce8ff053', 'disableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, 'Is9X5S-c2hrT3XvX1td9KIruyHqNAfAt5FsGhZsBHPw.', '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=\"xJ62i45pxFevAzH0JTQG1Qk6_-F8WKe3Qg2hA9NYWEY.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256ce8ff053\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23a256cd58077e_1","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.tkbmessagesearchfield.messagesearchfield:autocomplete?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_23a256cf127379', 'disableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, 'c5YEQSV4DF2sYEvQqGRWXD7WXPJY7RrtuZ7-Fz91vE0.', '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=\"YO6oDR_J5lHCoBAU6kF58mr2wCCjqRX1TOrA3CjLIzs.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256cf127379\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#userSearchField_23a256cd58077e","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.usersearchfield.usersearchfield:autocomplete?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_23a256cd58077e","action":"userExistsQuery","feedbackSelector":"#ajaxfeedback_23a256cd58077e_0","url":"https://community.hubspot.com/t5/community/page.searchformv32.usersearchfield:userexistsquery?t:cp=search/contributions/page","ajaxErrorEventName":"LITHIUM:ajaxError","token":"qxqZdH4TEDfXaWyCJ07tf0jqlQNK-GM-icvD2qYMy4E."}); 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_23a256cf8d7938', 'disableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, 'hkdnXZAldmUna0kM0h1ihzq9A85G0HBr2Bq6e0n2G6Q.', '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=\"sTGQpOH445-yvCpxEM9CRasJVmq5ypxdrT0ujHsCO2c.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256cf8d7938\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#noteSearchField_23a256cd58077e_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.notesearchfield.notesearchfield:autocomplete?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_23a256cfb31eaf', 'disableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, '_1ZF-quZG2P5lR3_yRRSA7z_8_tJmrZ4Gibfwv7kN8U.', '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=\"p-gOHO7TIG-vK-Wwui7pw8HLb1e9LQL3jbly6OXIiUw.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256cfb31eaf\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#productSearchField_23a256cd58077e","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.productsearchfield.productsearchfield:autocomplete?t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport.fromLink('#enableAutoComplete_23a256cd58077e', 'enableAutoComplete', '#ajaxfeedback_23a256cd58077e_0', 'LITHIUM:ajaxError', {}, 'QY-u4GqDF2miV_5P9_9w1lpqx8fxq45EmGgSmcGolH8.', 'ajax'); LITHIUM.Tooltip({"bodySelector":"body#lia-body","delay":30,"enableOnClickForTrigger":false,"predelay":10,"triggerSelector":"#link_23a256cd58077e","tooltipContentSelector":"#link_23a256cd58077e_0-tooltip-element .content","position":["bottom","left"],"tooltipElementSelector":"#link_23a256cd58077e_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_23a256cd58077e","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_23a256cd58077e","asSearchActionIdHeaderKey":"X-LI-AS-Search-Action-Id","inputSelector":"#messageSearchField_23a256cd58077e_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_23a256d048ec69","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256d048ec69_0","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256d048ec69_1","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_23a256d048ec69_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_23a256d07c3fc0', 'disableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 'UZ-T1SSAed0Sbd-_hv95Amwsr9b_X1O1LgZhMrSDhp4.', '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=\"KUURi4iNG3AcS2ALVR3Jsmugy_S26wuaUqbz4kHnp-g.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256d07c3fc0\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23a256d048ec69_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.messagesearchfield.messagesearchfield:autocomplete?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_23a256d0a299ff', 'disableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 's6ZHTg7bHEg00UtYR8emNjAoerfFERhBuSYokSGyVQY.', '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=\"8KbqiWSlyC9NL7D3nTCggytXZZ6BKnQWf0RARuaGu0s.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256d0a299ff\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":3},"inputSelector":"#messageSearchField_23a256d048ec69_1","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.tkbmessagesearchfield.messagesearchfield:autocomplete?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_23a256d0fb7ecd', 'disableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 'iv65Xv5aaEv0cVha6eu4JE8OlSj2-5BS2KBabXq47MQ.', '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=\"dE1nSXTqcZvj98hhk8aYQWHTt7k_OlLdCKnpwb3vJLg.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256d0fb7ecd\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#userSearchField_23a256d048ec69","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.usersearchfield.usersearchfield:autocomplete?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_23a256d048ec69","action":"userExistsQuery","feedbackSelector":"#ajaxfeedback_23a256d048ec69_0","url":"https://community.hubspot.com/t5/community/page.searchformv32.usersearchfield:userexistsquery?t:cp=search/contributions/page","ajaxErrorEventName":"LITHIUM:ajaxError","token":"TvxOiT7s7Upj5cnazXjhrqnZRBTvqAl0b6u96T6MfNk."}); 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_23a256d14e3e89', 'disableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 'kEsXE2JnVsAFB8HOLribKd3MjWP-kkb2VgbKfqNRadE.', '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=\"w9QBKZEwZHDQFxr5XHwJeZuzh3ZSNmYXCB8qTIXDWo4.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256d14e3e89\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#noteSearchField_23a256d048ec69_0","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.notesearchfield.notesearchfield:autocomplete?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_23a256d1e51fa9', 'disableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 'XhJJQrTg-sIXW5U9gNrL0OeiAXoppBclB9QJaHnMf3Y.', '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=\"QE2lRJ-KyRcRjwBNhoM9eD4NfhQspfvfAdJQFJ_dDzE.\" rel=\"nofollow\" id=\"disableAutoComplete_23a256d1e51fa9\" href=\"https://community.hubspot.com/t5/community/page.disableautocomplete:disableautocomplete?t:cp=action/contributions/searchactions\">Turn off suggestions<\/a>"}],"prefixTriggerTextLength":0},"inputSelector":"#productSearchField_23a256d048ec69","redirectToItemLink":false,"url":"https://community.hubspot.com/t5/community/page.searchformv32.productsearchfield.productsearchfield:autocomplete?t:cp=search/contributions/page","resizeImageEvent":"LITHIUM:renderImages"}); LITHIUM.AjaxSupport.fromLink('#enableAutoComplete_23a256d048ec69', 'enableAutoComplete', '#ajaxfeedback_23a256d048ec69_0', 'LITHIUM:ajaxError', {}, 'G8wKZL4TMBz91_p_pxWUWodxHZcm4LP-BOOVk9F3Q5s.', 'ajax'); LITHIUM.Tooltip({"bodySelector":"body#lia-body","delay":30,"enableOnClickForTrigger":false,"predelay":10,"triggerSelector":"#link_23a256d048ec69","tooltipContentSelector":"#link_23a256d048ec69_0-tooltip-element .content","position":["bottom","left"],"tooltipElementSelector":"#link_23a256d048ec69_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_23a256d048ec69","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_23a256d048ec69","nodesModel":{"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_23a256d048ec69_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($) { $('#loggedInUser').append('Anonymous'); })(LITHIUM.jQuery); ;(function($) { // Custom Tabs Accordian JS var tab = $('.custom-dashboard-tabs__list .custom-dashboard-tabs__item'); var mobileTab = $('.custom-dashboard-tabs__list'); tab.on('click',function(e){ e.preventDefault(); $(this).addClass('active').siblings().removeClass('active'); var selectedTab = $(this).attr('data-tab-id'); var tabsType = $(this).closest('.custom-dashboard-tabs').attr('id'); loadTabContent(selectedTab,tabsType); }); mobileTab.on('change',function(){ var selectedMobileTab= $(this).find(":selected").attr('data-tab-id'); var selectedMobileTabType= $(this).closest(".custom-dashboard-tabs").attr('id'); loadTabContent(selectedMobileTab,selectedMobileTabType); }); function loadTabContent(selectedTab,tabType){ var tabContent = $('.'+tabType+'__content'+' '+'.custom-dashboard-tab-body'); tabContent.removeClass('active'); $('.'+selectedTab).addClass('active'); }; jQuery('.custom-dashboard-accordian-item').click(function(e){ if (!$('.read-more').is(e.target) && $('.read-more').has(e.target).length === 0){ e.preventDefault(); jQuery(this).toggleClass('active').toggleClass('off').siblings().removeClass('active').addClass('off'); jQuery('.off > .custom-dashboard-accordian-item__header > .toggle-action').addClass('on'); jQuery('.active > .custom-dashboard-accordian-item__header > .toggle-action').removeClass('on'); jQuery('.off > .custom-dashboard-accordian-posts-list').slideUp(); jQuery('.active > .custom-dashboard-accordian-posts-list').slideDown(); } }); // Custom Tabs Accordian JS })(LITHIUM.jQuery); LITHIUM.UserListActual({"acceptedSolutionsColumnSelector":".UserList .lia-list-row .acceptedSolutionsCountColumn","kudosColumnSelector":".UserList .lia-list-row .kudosCountColumn"}); ;(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/community/page.liabase.basebody.partialrenderproxy:partialrenderproxyrelay","ajaxErrorEventName":"LITHIUM:ajaxError","token":"a06-Zo-CFj6Worgm7KHNzw-LEz87X-HqZUg9eNk2I8Q."}); 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 = 'xt6aTRXWbQFTOBy7Alxrh8EvIxln-39fzbHXF-9teEc.'; LITHIUM.AjaxSupport.useTickets = false; LITHIUM.Loader.runJsAttached(); // --> </script></body> </html>